Merge "Disable opening expanded QS from the lockscreen on large screen devices" into sc-v2-dev
diff --git a/core/api/current.txt b/core/api/current.txt
index 9a16390..e884b9e 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -1298,7 +1298,7 @@
field public static final int shortcutLongLabel = 16844074; // 0x101052a
field public static final int shortcutShortLabel = 16844073; // 0x1010529
field public static final int shouldDisableView = 16843246; // 0x10101ee
- field public static final int shouldUseDefaultDeviceStateChangeTransition;
+ field public static final int shouldUseDefaultDisplayStateChangeTransition;
field public static final int showAsAction = 16843481; // 0x10102d9
field public static final int showDefault = 16843258; // 0x10101fa
field public static final int showDividers = 16843561; // 0x1010329
@@ -6927,7 +6927,7 @@
method public android.graphics.drawable.Drawable loadIcon(android.content.pm.PackageManager);
method public CharSequence loadLabel(android.content.pm.PackageManager);
method public android.graphics.drawable.Drawable loadThumbnail(android.content.pm.PackageManager);
- method public boolean shouldUseDefaultDeviceStateChangeTransition();
+ method public boolean shouldUseDefaultDisplayStateChangeTransition();
method public boolean supportsMultipleDisplays();
method public void writeToParcel(android.os.Parcel, int);
field @NonNull public static final android.os.Parcelable.Creator<android.app.WallpaperInfo> CREATOR;
@@ -17949,6 +17949,7 @@
field @NonNull public static final android.hardware.camera2.CameraCharacteristics.Key<int[]> EDGE_AVAILABLE_EDGE_MODES;
field @NonNull public static final android.hardware.camera2.CameraCharacteristics.Key<java.lang.Boolean> FLASH_INFO_AVAILABLE;
field @NonNull public static final android.hardware.camera2.CameraCharacteristics.Key<int[]> HOT_PIXEL_AVAILABLE_HOT_PIXEL_MODES;
+ field @NonNull public static final android.hardware.camera2.CameraCharacteristics.Key<android.hardware.camera2.params.DeviceStateOrientationMap> INFO_DEVICE_STATE_ORIENTATION_MAP;
field @NonNull public static final android.hardware.camera2.CameraCharacteristics.Key<java.lang.Integer> INFO_SUPPORTED_HARDWARE_LEVEL;
field @NonNull public static final android.hardware.camera2.CameraCharacteristics.Key<java.lang.String> INFO_VERSION;
field @NonNull public static final android.hardware.camera2.CameraCharacteristics.Key<android.util.Size[]> JPEG_AVAILABLE_THUMBNAIL_SIZES;
@@ -18643,6 +18644,12 @@
method public android.util.Rational getElement(int, int);
}
+ public final class DeviceStateOrientationMap {
+ method public int getSensorOrientation(long);
+ field public static final long FOLDED = 4L; // 0x4L
+ field public static final long NORMAL = 0L; // 0x0L
+ }
+
public final class ExtensionSessionConfiguration {
ctor public ExtensionSessionConfiguration(int, @NonNull java.util.List<android.hardware.camera2.params.OutputConfiguration>, @NonNull java.util.concurrent.Executor, @NonNull android.hardware.camera2.CameraExtensionSession.StateCallback);
method @NonNull public java.util.concurrent.Executor getExecutor();
diff --git a/core/api/test-current.txt b/core/api/test-current.txt
index d484100..ceab056 100644
--- a/core/api/test-current.txt
+++ b/core/api/test-current.txt
@@ -448,6 +448,7 @@
method @RequiresPermission("android.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS") public void resetDefaultCrossProfileIntentFilters(int);
method @RequiresPermission(allOf={"android.permission.MANAGE_DEVICE_ADMINS", android.Manifest.permission.INTERACT_ACROSS_USERS_FULL}) public void setActiveAdmin(@NonNull android.content.ComponentName, boolean, int);
method @RequiresPermission("android.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS") public boolean setDeviceOwner(@NonNull android.content.ComponentName, @Nullable String, int);
+ method @RequiresPermission("android.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS") public boolean setDeviceOwnerOnly(@NonNull android.content.ComponentName, @Nullable String, int);
method @RequiresPermission("android.permission.MANAGE_DEVICE_ADMINS") public void setNextOperationSafety(int, int);
field public static final String ACTION_DATA_SHARING_RESTRICTION_APPLIED = "android.app.action.DATA_SHARING_RESTRICTION_APPLIED";
field public static final String ACTION_DEVICE_POLICY_CONSTANTS_CHANGED = "android.app.action.DEVICE_POLICY_CONSTANTS_CHANGED";
@@ -3190,13 +3191,6 @@
method @Nullable public android.view.View getBrandingView();
}
- public final class StartingWindowInfo implements android.os.Parcelable {
- ctor public StartingWindowInfo();
- method public int describeContents();
- method public void writeToParcel(@NonNull android.os.Parcel, int);
- field @NonNull public static final android.os.Parcelable.Creator<android.window.StartingWindowInfo> CREATOR;
- }
-
public final class TaskAppearedInfo implements android.os.Parcelable {
ctor public TaskAppearedInfo(@NonNull android.app.ActivityManager.RunningTaskInfo, @NonNull android.view.SurfaceControl);
method public int describeContents();
@@ -3263,7 +3257,6 @@
public class TaskOrganizer extends android.window.WindowOrganizer {
ctor public TaskOrganizer();
- method @BinderThread public void addStartingWindow(@NonNull android.window.StartingWindowInfo, @NonNull android.os.IBinder);
method @BinderThread public void copySplashScreenView(int);
method @Nullable @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_TASKS) public void createRootTask(int, int, @Nullable android.os.IBinder);
method @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_TASKS) public boolean deleteRootTask(@NonNull android.window.WindowContainerToken);
@@ -3276,7 +3269,6 @@
method @BinderThread public void onTaskInfoChanged(@NonNull android.app.ActivityManager.RunningTaskInfo);
method @BinderThread public void onTaskVanished(@NonNull android.app.ActivityManager.RunningTaskInfo);
method @CallSuper @NonNull @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_TASKS) public java.util.List<android.window.TaskAppearedInfo> registerOrganizer();
- method @BinderThread public void removeStartingWindow(int, @Nullable android.view.SurfaceControl, @Nullable android.graphics.Rect, boolean);
method @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_TASKS) public void setInterceptBackPressedOnTaskRoot(@NonNull android.window.WindowContainerToken, boolean);
method @CallSuper @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_TASKS) public void unregisterOrganizer();
}
diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java
index 12025f9..22091fc 100644
--- a/core/java/android/app/Activity.java
+++ b/core/java/android/app/Activity.java
@@ -2507,12 +2507,11 @@
*
* <p>To get the voice interactor you need to call {@link #getVoiceInteractor()}
* which would return non <code>null</code> only if there is an ongoing voice
- * interaction session. You an also detect when the voice interactor is no
+ * interaction session. You can also detect when the voice interactor is no
* longer valid because the voice interaction session that is backing is finished
* by calling {@link VoiceInteractor#registerOnDestroyedCallback(Executor, Runnable)}.
*
- * <p>This method will be called only after {@link #onStart()} is being called and
- * before {@link #onStop()} is being called.
+ * <p>This method will be called only after {@link #onStart()} and before {@link #onStop()}.
*
* <p>You should pass to the callback the currently supported direct actions which
* cannot be <code>null</code> or contain <code>null</code> elements.
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index 80554d7..d0680f8 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -526,9 +526,6 @@
// A reusable token for other purposes, e.g. content capture, translation. It shouldn't be
// used without security checks
public IBinder shareableActivityToken;
- // The token of the initial TaskFragment that embedded this activity. Do not rely on it
- // after creation because the activity could be reparented.
- @Nullable public IBinder mInitialTaskFragmentToken;
int ident;
@UnsupportedAppUsage
Intent intent;
@@ -622,8 +619,7 @@
List<ReferrerIntent> pendingNewIntents, ActivityOptions activityOptions,
boolean isForward, ProfilerInfo profilerInfo, ClientTransactionHandler client,
IBinder assistToken, FixedRotationAdjustments fixedRotationAdjustments,
- IBinder shareableActivityToken, boolean launchedFromBubble,
- IBinder initialTaskFragmentToken) {
+ IBinder shareableActivityToken, boolean launchedFromBubble) {
this.token = token;
this.assistToken = assistToken;
this.shareableActivityToken = shareableActivityToken;
@@ -645,7 +641,6 @@
mActivityOptions = activityOptions;
mPendingFixedRotationAdjustments = fixedRotationAdjustments;
mLaunchedFromBubble = launchedFromBubble;
- mInitialTaskFragmentToken = initialTaskFragmentToken;
init();
}
diff --git a/core/java/android/app/ActivityTransitionCoordinator.java b/core/java/android/app/ActivityTransitionCoordinator.java
index 4b87a64..f5b3b40 100644
--- a/core/java/android/app/ActivityTransitionCoordinator.java
+++ b/core/java/android/app/ActivityTransitionCoordinator.java
@@ -871,6 +871,7 @@
if (view.isAttachedToWindow()) {
tempMatrix.reset();
mSharedElementParentMatrices.get(i).invert(tempMatrix);
+ decor.transformMatrixToLocal(tempMatrix);
GhostView.addGhost(view, decor, tempMatrix);
ViewGroup parent = (ViewGroup) view.getParent();
if (moveWithParent && !isInTransitionGroup(parent, decor)) {
diff --git a/core/java/android/app/DirectAction.java b/core/java/android/app/DirectAction.java
index b0ed490..ac3868b 100644
--- a/core/java/android/app/DirectAction.java
+++ b/core/java/android/app/DirectAction.java
@@ -22,14 +22,13 @@
import android.os.IBinder;
import android.os.Parcel;
import android.os.Parcelable;
-import android.view.accessibility.AccessibilityNodeInfo;
import com.android.internal.util.Preconditions;
import java.util.Objects;
/**
- * Represents a abstract action that can be perform on this app. This are requested from
+ * Represents an abstract action that can be perform on this app. This are requested from
* outside the app's UI (eg by SystemUI or assistant). The semantics of these actions are
* not specified by the OS. This allows open-ended and scalable approach for defining how
* an app interacts with components that expose alternative interaction models to the user
diff --git a/core/java/android/app/WallpaperInfo.java b/core/java/android/app/WallpaperInfo.java
index c552cb6..a969b10 100644
--- a/core/java/android/app/WallpaperInfo.java
+++ b/core/java/android/app/WallpaperInfo.java
@@ -81,7 +81,7 @@
final int mContextDescriptionResource;
final boolean mShowMetadataInPreview;
final boolean mSupportsAmbientMode;
- final boolean mShouldUseDefaultDeviceStateChangeTransition;
+ final boolean mShouldUseDefaultDisplayStateChangeTransition;
final String mSettingsSliceUri;
final boolean mSupportMultipleDisplays;
@@ -146,9 +146,9 @@
mSupportsAmbientMode = sa.getBoolean(
com.android.internal.R.styleable.Wallpaper_supportsAmbientMode,
false);
- mShouldUseDefaultDeviceStateChangeTransition = sa.getBoolean(
+ mShouldUseDefaultDisplayStateChangeTransition = sa.getBoolean(
com.android.internal.R.styleable
- .Wallpaper_shouldUseDefaultDeviceStateChangeTransition, true);
+ .Wallpaper_shouldUseDefaultDisplayStateChangeTransition, true);
mSettingsSliceUri = sa.getString(
com.android.internal.R.styleable.Wallpaper_settingsSliceUri);
mSupportMultipleDisplays = sa.getBoolean(
@@ -175,7 +175,7 @@
mSupportsAmbientMode = source.readInt() != 0;
mSettingsSliceUri = source.readString();
mSupportMultipleDisplays = source.readInt() != 0;
- mShouldUseDefaultDeviceStateChangeTransition = source.readInt() != 0;
+ mShouldUseDefaultDisplayStateChangeTransition = source.readInt() != 0;
mService = ResolveInfo.CREATOR.createFromParcel(source);
}
@@ -400,24 +400,24 @@
/**
* Returns whether this wallpaper should receive default zooming updates when the device
- * changes its state (e.g. when folding or unfolding a foldable device).
+ * changes its display state (e.g. when folding or unfolding a foldable device).
* If set to false the wallpaper will not receive zoom events when changing the device state,
* so it can implement its own transition instead.
* <p>
* This corresponds to the value {@link
- * android.R.styleable#Wallpaper_shouldUseDefaultDeviceStateChangeTransition} in the
+ * android.R.styleable#Wallpaper_shouldUseDefaultDisplayStateChangeTransition} in the
* XML description of the wallpaper.
* <p>
* The default value is {@code true}.
*
- * @see android.R.styleable#Wallpaper_shouldUseDefaultDeviceStateChangeTransition
+ * @see android.R.styleable#Wallpaper_shouldUseDefaultDisplayStateChangeTransition
* @return {@code true} if wallpaper should receive default device state change
* transition updates
*
- * @attr ref android.R.styleable#Wallpaper_shouldUseDefaultDeviceStateChangeTransition
+ * @attr ref android.R.styleable#Wallpaper_shouldUseDefaultDisplayStateChangeTransition
*/
- public boolean shouldUseDefaultDeviceStateChangeTransition() {
- return mShouldUseDefaultDeviceStateChangeTransition;
+ public boolean shouldUseDefaultDisplayStateChangeTransition() {
+ return mShouldUseDefaultDisplayStateChangeTransition;
}
public void dump(Printer pw, String prefix) {
@@ -450,7 +450,7 @@
dest.writeInt(mSupportsAmbientMode ? 1 : 0);
dest.writeString(mSettingsSliceUri);
dest.writeInt(mSupportMultipleDisplays ? 1 : 0);
- dest.writeInt(mShouldUseDefaultDeviceStateChangeTransition ? 1 : 0);
+ dest.writeInt(mShouldUseDefaultDisplayStateChangeTransition ? 1 : 0);
mService.writeToParcel(dest, flags);
}
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index 12444ab..5b01a7d 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -7726,27 +7726,64 @@
}
/**
- * @hide
- * Sets the given package as the device owner. The package must already be installed. There
- * must not already be a device owner.
- * Only apps with the MANAGE_PROFILE_AND_DEVICE_OWNERS permission and the shell uid can call
- * this method.
- * Calling this after the setup phase of the primary user has completed is allowed only if
- * the caller is the shell uid, and there are no additional users and no accounts.
+ * Sets the given package as the device owner.
+ *
+ * <p>Preconditions:
+ * <ul>
+ * <li>The package must already be installed.
+ * <li>There must not already be a device owner.
+ * <li>Only apps with the {@code MANAGE_PROFILE_AND_DEVICE_OWNERS} permission or the
+ * {@link Process#SHELL_UID Shell UID} can call this method.
+ * </ul>
+ *
+ * <p>Calling this after the setup phase of the device owner user has completed is allowed only
+ * if the caller is the {@link Process#SHELL_UID Shell UID}, and there are no additional users
+ * (except when the device runs on headless system user mode, in which case it could have exact
+ * one extra user, which is the current user - the device owner will be set in the
+ * {@link UserHandle#SYSTEM system} user and a profile owner will be set in the current user)
+ * and no accounts.
+ *
* @param who the component name to be registered as device owner.
* @param ownerName the human readable name of the institution that owns this device.
* @param userId ID of the user on which the device owner runs.
+ *
* @return whether the package was successfully registered as the device owner.
- * @throws IllegalArgumentException if the package name is null or invalid
+ *
+ * @throws IllegalArgumentException if the package name is {@code null} or invalid.
* @throws IllegalStateException If the preconditions mentioned are not met.
+ *
+ * @hide
*/
@TestApi
@RequiresPermission(android.Manifest.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS)
- public boolean setDeviceOwner(
+ public boolean setDeviceOwner(@NonNull ComponentName who, @Nullable String ownerName,
+ @UserIdInt int userId) {
+ if (mService != null) {
+ try {
+ return mService.setDeviceOwner(who, ownerName, userId,
+ /* setProfileOwnerOnCurrentUserIfNecessary= */ true);
+ } catch (RemoteException re) {
+ throw re.rethrowFromSystemServer();
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Same as {@link #setDeviceOwner(ComponentName, String, int)}, but without setting the profile
+ * owner on current user when running on headless system user mode - should be used only by
+ * testing infra.
+ *
+ * @hide
+ */
+ @TestApi
+ @RequiresPermission(android.Manifest.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS)
+ public boolean setDeviceOwnerOnly(
@NonNull ComponentName who, @Nullable String ownerName, @UserIdInt int userId) {
if (mService != null) {
try {
- return mService.setDeviceOwner(who, ownerName, userId);
+ return mService.setDeviceOwner(who, ownerName, userId,
+ /* setProfileOwnerOnCurrentUserIfNecessary= */ false);
} catch (RemoteException re) {
throw re.rethrowFromSystemServer();
}
@@ -9734,6 +9771,27 @@
}
/**
+ * @param userId The user for whom to retrieve information.
+ * @param restriction The restriction enforced by admin. It could be any user restriction or
+ * policy like {@link DevicePolicyManager#POLICY_DISABLE_CAMERA} and
+ * {@link DevicePolicyManager#POLICY_DISABLE_SCREEN_CAPTURE}.
+ * @return Details of admin and user which enforced the restriction for the userId. If
+ * restriction is null, profile owner for the user or device owner info is returned.
+ * @hide
+ */
+ public @Nullable Bundle getEnforcingAdminAndUserDetails(int userId,
+ @Nullable String restriction) {
+ if (mService != null) {
+ try {
+ return mService.getEnforcingAdminAndUserDetails(userId, restriction);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+ return null;
+ }
+
+ /**
* Hide or unhide packages. When a package is hidden it is unavailable for use, but the data and
* actual package file remain. This function can be called by a device owner, profile owner, or
* by a delegate given the {@link #DELEGATION_PACKAGE_ACCESS} scope via
diff --git a/core/java/android/app/admin/IDevicePolicyManager.aidl b/core/java/android/app/admin/IDevicePolicyManager.aidl
index b1364b5..1c9187d 100644
--- a/core/java/android/app/admin/IDevicePolicyManager.aidl
+++ b/core/java/android/app/admin/IDevicePolicyManager.aidl
@@ -159,7 +159,7 @@
void reportKeyguardDismissed(int userHandle);
void reportKeyguardSecured(int userHandle);
- boolean setDeviceOwner(in ComponentName who, String ownerName, int userId);
+ boolean setDeviceOwner(in ComponentName who, String ownerName, int userId, boolean setProfileOwnerOnCurrentUserIfNecessary);
ComponentName getDeviceOwnerComponent(boolean callingUserOnly);
boolean hasDeviceOwner();
String getDeviceOwnerName();
@@ -251,6 +251,7 @@
boolean isNotificationListenerServicePermitted(in String packageName, int userId);
Intent createAdminSupportIntent(in String restriction);
+ Bundle getEnforcingAdminAndUserDetails(int userId,String restriction);
boolean setApplicationHidden(in ComponentName admin, in String callerPackage, in String packageName, boolean hidden, boolean parent);
boolean isApplicationHidden(in ComponentName admin, in String callerPackage, in String packageName, boolean parent);
diff --git a/core/java/android/app/servertransaction/ActivityResultItem.java b/core/java/android/app/servertransaction/ActivityResultItem.java
index e059f17..27d104b 100644
--- a/core/java/android/app/servertransaction/ActivityResultItem.java
+++ b/core/java/android/app/servertransaction/ActivityResultItem.java
@@ -16,6 +16,8 @@
package android.app.servertransaction;
+import static android.app.servertransaction.ActivityLifecycleItem.ON_RESUME;
+import static android.app.servertransaction.ActivityLifecycleItem.UNDEFINED;
import static android.os.Trace.TRACE_TAG_ACTIVITY_MANAGER;
import android.annotation.NonNull;
@@ -23,6 +25,9 @@
import android.app.ActivityThread.ActivityClientRecord;
import android.app.ClientTransactionHandler;
import android.app.ResultInfo;
+import android.app.compat.CompatChanges;
+import android.compat.annotation.ChangeId;
+import android.compat.annotation.EnabledAfter;
import android.compat.annotation.UnsupportedAppUsage;
import android.os.Build;
import android.os.Parcel;
@@ -41,11 +46,19 @@
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
private List<ResultInfo> mResultInfoList;
- /* TODO(b/78294732)
+ /**
+ * Correct the lifecycle of activity result after {@link android.os.Build.VERSION_CODES#S} to
+ * guarantee that an activity gets activity result just before resume.
+ */
+ @ChangeId
+ @EnabledAfter(targetSdkVersion = Build.VERSION_CODES.S)
+ public static final long CALL_ACTIVITY_RESULT_BEFORE_RESUME = 78294732L;
+
@Override
public int getPostExecutionState() {
- return ON_RESUME;
- }*/
+ return CompatChanges.isChangeEnabled(CALL_ACTIVITY_RESULT_BEFORE_RESUME)
+ ? ON_RESUME : UNDEFINED;
+ }
@Override
public void execute(ClientTransactionHandler client, ActivityClientRecord r,
diff --git a/core/java/android/app/servertransaction/LaunchActivityItem.java b/core/java/android/app/servertransaction/LaunchActivityItem.java
index 37cbccb..34e4fcd 100644
--- a/core/java/android/app/servertransaction/LaunchActivityItem.java
+++ b/core/java/android/app/servertransaction/LaunchActivityItem.java
@@ -73,7 +73,6 @@
private IBinder mAssistToken;
private IBinder mShareableActivityToken;
private boolean mLaunchedFromBubble;
- private IBinder mTaskFragmentToken;
/**
* It is only non-null if the process is the first time to launch activity. It is only an
* optimization for quick look up of the interface so the field is ignored for comparison.
@@ -87,7 +86,7 @@
mOverrideConfig, mCompatInfo, mReferrer, mVoiceInteractor, mState, mPersistentState,
mPendingResults, mPendingNewIntents, mActivityOptions, mIsForward, mProfilerInfo,
client, mAssistToken, mFixedRotationAdjustments, mShareableActivityToken,
- mLaunchedFromBubble, mTaskFragmentToken);
+ mLaunchedFromBubble);
client.addLaunchingActivity(token, r);
client.updateProcessState(mProcState, false);
client.updatePendingConfiguration(mCurConfig);
@@ -125,7 +124,7 @@
boolean isForward, ProfilerInfo profilerInfo, IBinder assistToken,
IActivityClientController activityClientController,
FixedRotationAdjustments fixedRotationAdjustments, IBinder shareableActivityToken,
- boolean launchedFromBubble, IBinder taskFragmentToken) {
+ boolean launchedFromBubble) {
LaunchActivityItem instance = ObjectPool.obtain(LaunchActivityItem.class);
if (instance == null) {
instance = new LaunchActivityItem();
@@ -134,7 +133,7 @@
voiceInteractor, procState, state, persistentState, pendingResults,
pendingNewIntents, activityOptions, isForward, profilerInfo, assistToken,
activityClientController, fixedRotationAdjustments, shareableActivityToken,
- launchedFromBubble, taskFragmentToken);
+ launchedFromBubble);
return instance;
}
@@ -142,7 +141,7 @@
@Override
public void recycle() {
setValues(this, null, 0, null, null, null, null, null, null, 0, null, null, null, null,
- null, false, null, null, null, null, null, false, null);
+ null, false, null, null, null, null, null, false);
ObjectPool.recycle(this);
}
@@ -173,7 +172,6 @@
dest.writeTypedObject(mFixedRotationAdjustments, flags);
dest.writeStrongBinder(mShareableActivityToken);
dest.writeBoolean(mLaunchedFromBubble);
- dest.writeStrongBinder(mTaskFragmentToken);
}
/** Read from Parcel. */
@@ -192,8 +190,7 @@
in.readStrongBinder(),
IActivityClientController.Stub.asInterface(in.readStrongBinder()),
in.readTypedObject(FixedRotationAdjustments.CREATOR), in.readStrongBinder(),
- in.readBoolean(),
- in.readStrongBinder());
+ in.readBoolean());
}
public static final @NonNull Creator<LaunchActivityItem> CREATOR =
@@ -232,8 +229,7 @@
&& Objects.equals(mProfilerInfo, other.mProfilerInfo)
&& Objects.equals(mAssistToken, other.mAssistToken)
&& Objects.equals(mFixedRotationAdjustments, other.mFixedRotationAdjustments)
- && Objects.equals(mShareableActivityToken, other.mShareableActivityToken)
- && Objects.equals(mTaskFragmentToken, other.mTaskFragmentToken);
+ && Objects.equals(mShareableActivityToken, other.mShareableActivityToken);
}
@Override
@@ -256,7 +252,6 @@
result = 31 * result + Objects.hashCode(mAssistToken);
result = 31 * result + Objects.hashCode(mFixedRotationAdjustments);
result = 31 * result + Objects.hashCode(mShareableActivityToken);
- result = 31 * result + Objects.hashCode(mTaskFragmentToken);
return result;
}
@@ -306,7 +301,7 @@
ActivityOptions activityOptions, boolean isForward, ProfilerInfo profilerInfo,
IBinder assistToken, IActivityClientController activityClientController,
FixedRotationAdjustments fixedRotationAdjustments, IBinder shareableActivityToken,
- boolean launchedFromBubble, IBinder taskFragmentToken) {
+ boolean launchedFromBubble) {
instance.mIntent = intent;
instance.mIdent = ident;
instance.mInfo = info;
@@ -328,6 +323,5 @@
instance.mFixedRotationAdjustments = fixedRotationAdjustments;
instance.mShareableActivityToken = shareableActivityToken;
instance.mLaunchedFromBubble = launchedFromBubble;
- instance.mTaskFragmentToken = taskFragmentToken;
}
}
diff --git a/core/java/android/appwidget/AppWidgetHostView.java b/core/java/android/appwidget/AppWidgetHostView.java
index 8aa2785..dd147cc 100644
--- a/core/java/android/appwidget/AppWidgetHostView.java
+++ b/core/java/android/appwidget/AppWidgetHostView.java
@@ -552,6 +552,10 @@
inflateAsync(rvToApply);
return;
}
+ // Prepare a local reference to the remote Context so we're ready to
+ // inflate any requested LayoutParams.
+ mRemoteContext = getRemoteContext();
+
int layoutId = rvToApply.getLayoutId();
if (rvToApply.canRecycleView(mView)) {
try {
@@ -727,6 +731,9 @@
} catch (NameNotFoundException e) {
Log.e(TAG, "Package name " + mInfo.providerInfo.packageName + " not found");
return mContext;
+ } catch (NullPointerException e) {
+ Log.e(TAG, "Error trying to create the remote context.", e);
+ return mContext;
}
}
diff --git a/core/java/android/hardware/camera2/CameraCharacteristics.java b/core/java/android/hardware/camera2/CameraCharacteristics.java
index e0138c5..ddac22c 100644
--- a/core/java/android/hardware/camera2/CameraCharacteristics.java
+++ b/core/java/android/hardware/camera2/CameraCharacteristics.java
@@ -22,15 +22,18 @@
import android.hardware.camera2.impl.CameraMetadataNative;
import android.hardware.camera2.impl.PublicKey;
import android.hardware.camera2.impl.SyntheticKey;
+import android.hardware.camera2.params.DeviceStateOrientationMap;
import android.hardware.camera2.params.RecommendedStreamConfigurationMap;
import android.hardware.camera2.params.SessionConfiguration;
import android.hardware.camera2.utils.TypeReference;
import android.os.Build;
+import android.util.Log;
import android.util.Rational;
+import com.android.internal.annotations.GuardedBy;
+
import java.util.ArrayList;
import java.util.Collections;
-import java.util.HashSet;
import java.util.List;
import java.util.Set;
@@ -202,8 +205,25 @@
private List<CaptureResult.Key<?>> mAvailableResultKeys;
private ArrayList<RecommendedStreamConfigurationMap> mRecommendedConfigurations;
+ private final Object mLock = new Object();
+ @GuardedBy("mLock")
+ private boolean mFoldedDeviceState;
+
+ private final CameraManager.DeviceStateListener mFoldStateListener =
+ new CameraManager.DeviceStateListener() {
+ @Override
+ public final void onDeviceStateChanged(boolean folded) {
+ synchronized (mLock) {
+ mFoldedDeviceState = folded;
+ }
+ }};
+
+ private static final String TAG = "CameraCharacteristics";
+
/**
* Takes ownership of the passed-in properties object
+ *
+ * @param properties Camera properties.
* @hide
*/
public CameraCharacteristics(CameraMetadataNative properties) {
@@ -220,6 +240,41 @@
}
/**
+ * Return the device state listener for this Camera characteristics instance
+ */
+ CameraManager.DeviceStateListener getDeviceStateListener() { return mFoldStateListener; }
+
+ /**
+ * Overrides the property value
+ *
+ * <p>Check whether a given property value needs to be overridden in some specific
+ * case.</p>
+ *
+ * @param key The characteristics field to override.
+ * @return The value of overridden property, or {@code null} if the property doesn't need an
+ * override.
+ */
+ @Nullable
+ private <T> T overrideProperty(Key<T> key) {
+ if (CameraCharacteristics.SENSOR_ORIENTATION.equals(key) && (mFoldStateListener != null) &&
+ (mProperties.get(CameraCharacteristics.INFO_DEVICE_STATE_ORIENTATIONS) != null)) {
+ DeviceStateOrientationMap deviceStateOrientationMap =
+ mProperties.get(CameraCharacteristics.INFO_DEVICE_STATE_ORIENTATION_MAP);
+ synchronized (mLock) {
+ Integer ret = deviceStateOrientationMap.getSensorOrientation(mFoldedDeviceState ?
+ DeviceStateOrientationMap.FOLDED : DeviceStateOrientationMap.NORMAL);
+ if (ret >= 0) {
+ return (T) ret;
+ } else {
+ Log.w(TAG, "No valid device state to orientation mapping! Using default!");
+ }
+ }
+ }
+
+ return null;
+ }
+
+ /**
* Get a camera characteristics field value.
*
* <p>The field definitions can be
@@ -235,7 +290,8 @@
*/
@Nullable
public <T> T get(Key<T> key) {
- return mProperties.get(key);
+ T propertyOverride = overrideProperty(key);
+ return (propertyOverride != null) ? propertyOverride : mProperties.get(key);
}
/**
@@ -3993,11 +4049,26 @@
* upright on the device screen in its native orientation.</p>
* <p>Also defines the direction of rolling shutter readout, which is from top to bottom in
* the sensor's coordinate system.</p>
+ * <p>Starting with Android API level 32, camera clients that query the orientation via
+ * {@link android.hardware.camera2.CameraCharacteristics#get } on foldable devices which
+ * include logical cameras can receive a value that can dynamically change depending on the
+ * device/fold state.
+ * Clients are advised to not cache or store the orientation value of such logical sensors.
+ * In case repeated queries to CameraCharacteristics are not preferred, then clients can
+ * also access the entire mapping from device state to sensor orientation in
+ * {@link android.hardware.camera2.params.DeviceStateOrientationMap }.
+ * Do note that a dynamically changing sensor orientation value in camera characteristics
+ * will not be the best way to establish the orientation per frame. Clients that want to
+ * know the sensor orientation of a particular captured frame should query the
+ * {@link CaptureResult#LOGICAL_MULTI_CAMERA_ACTIVE_PHYSICAL_ID android.logicalMultiCamera.activePhysicalId} from the corresponding capture result and
+ * check the respective physical camera orientation.</p>
* <p><b>Units</b>: Degrees of clockwise rotation; always a multiple of
* 90</p>
* <p><b>Range of valid values:</b><br>
* 0, 90, 180, 270</p>
* <p>This key is available on all devices.</p>
+ *
+ * @see CaptureResult#LOGICAL_MULTI_CAMERA_ACTIVE_PHYSICAL_ID
*/
@PublicKey
@NonNull
@@ -4307,6 +4378,46 @@
new Key<String>("android.info.version", String.class);
/**
+ * <p>This lists the mapping between a device folding state and
+ * specific camera sensor orientation for logical cameras on a foldable device.</p>
+ * <p>Logical cameras on foldable devices can support sensors with different orientation
+ * values. The orientation value may need to change depending on the specific folding
+ * state. Information about the mapping between the device folding state and the
+ * sensor orientation can be obtained in
+ * {@link android.hardware.camera2.params.DeviceStateOrientationMap }.
+ * Device state orientation maps are optional and maybe present on devices that support
+ * {@link CaptureRequest#SCALER_ROTATE_AND_CROP android.scaler.rotateAndCrop}.</p>
+ * <p><b>Optional</b> - The value for this key may be {@code null} on some devices.</p>
+ * <p><b>Limited capability</b> -
+ * Present on all camera devices that report being at least {@link CameraCharacteristics#INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED HARDWARE_LEVEL_LIMITED} devices in the
+ * {@link CameraCharacteristics#INFO_SUPPORTED_HARDWARE_LEVEL android.info.supportedHardwareLevel} key</p>
+ *
+ * @see CameraCharacteristics#INFO_SUPPORTED_HARDWARE_LEVEL
+ * @see CaptureRequest#SCALER_ROTATE_AND_CROP
+ */
+ @PublicKey
+ @NonNull
+ @SyntheticKey
+ public static final Key<android.hardware.camera2.params.DeviceStateOrientationMap> INFO_DEVICE_STATE_ORIENTATION_MAP =
+ new Key<android.hardware.camera2.params.DeviceStateOrientationMap>("android.info.deviceStateOrientationMap", android.hardware.camera2.params.DeviceStateOrientationMap.class);
+
+ /**
+ * <p>HAL must populate the array with
+ * (hardware::camera::provider::V2_5::DeviceState, sensorOrientation) pairs for each
+ * supported device state bitwise combination.</p>
+ * <p><b>Units</b>: (device fold state, sensor orientation) x n</p>
+ * <p><b>Optional</b> - The value for this key may be {@code null} on some devices.</p>
+ * <p><b>Limited capability</b> -
+ * Present on all camera devices that report being at least {@link CameraCharacteristics#INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED HARDWARE_LEVEL_LIMITED} devices in the
+ * {@link CameraCharacteristics#INFO_SUPPORTED_HARDWARE_LEVEL android.info.supportedHardwareLevel} key</p>
+ *
+ * @see CameraCharacteristics#INFO_SUPPORTED_HARDWARE_LEVEL
+ * @hide
+ */
+ public static final Key<long[]> INFO_DEVICE_STATE_ORIENTATIONS =
+ new Key<long[]>("android.info.deviceStateOrientations", long[].class);
+
+ /**
* <p>The maximum number of frames that can occur after a request
* (different than the previous) has been submitted, and before the
* result's state becomes synchronized.</p>
diff --git a/core/java/android/hardware/camera2/CameraManager.java b/core/java/android/hardware/camera2/CameraManager.java
index 5833b3d..9bb901f 100644
--- a/core/java/android/hardware/camera2/CameraManager.java
+++ b/core/java/android/hardware/camera2/CameraManager.java
@@ -30,15 +30,19 @@
import android.hardware.camera2.impl.CameraDeviceImpl;
import android.hardware.camera2.impl.CameraInjectionSessionImpl;
import android.hardware.camera2.impl.CameraMetadataNative;
+import android.hardware.camera2.params.DeviceStateOrientationMap;
import android.hardware.camera2.params.ExtensionSessionConfiguration;
import android.hardware.camera2.params.SessionConfiguration;
import android.hardware.camera2.params.StreamConfiguration;
import android.hardware.camera2.utils.CameraIdAndSessionConfiguration;
import android.hardware.camera2.utils.ConcurrentCameraIdCombination;
+import android.hardware.devicestate.DeviceStateManager;
import android.hardware.display.DisplayManager;
import android.os.Binder;
import android.os.DeadObjectException;
import android.os.Handler;
+import android.os.HandlerExecutor;
+import android.os.HandlerThread;
import android.os.IBinder;
import android.os.RemoteException;
import android.os.ServiceManager;
@@ -50,6 +54,10 @@
import android.util.Size;
import android.view.Display;
+import com.android.internal.annotations.GuardedBy;
+import com.android.internal.util.ArrayUtils;
+
+import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Comparator;
@@ -97,6 +105,80 @@
synchronized(mLock) {
mContext = context;
}
+
+ mHandlerThread = new HandlerThread(TAG);
+ mHandlerThread.start();
+ mHandler = new Handler(mHandlerThread.getLooper());
+ mFoldStateListener = new FoldStateListener(context);
+ context.getSystemService(DeviceStateManager.class)
+ .registerCallback(new HandlerExecutor(mHandler), mFoldStateListener);
+ }
+
+ private HandlerThread mHandlerThread;
+ private Handler mHandler;
+ private FoldStateListener mFoldStateListener;
+ @GuardedBy("mLock")
+ private ArrayList<WeakReference<DeviceStateListener>> mDeviceStateListeners = new ArrayList<>();
+ private boolean mFoldedDeviceState;
+
+ /**
+ * @hide
+ */
+ public interface DeviceStateListener {
+ void onDeviceStateChanged(boolean folded);
+ }
+
+ private final class FoldStateListener implements DeviceStateManager.DeviceStateCallback {
+ private final int[] mFoldedDeviceStates;
+
+ public FoldStateListener(Context context) {
+ mFoldedDeviceStates = context.getResources().getIntArray(
+ com.android.internal.R.array.config_foldedDeviceStates);
+ }
+
+ private void handleStateChange(int state) {
+ boolean folded = ArrayUtils.contains(mFoldedDeviceStates, state);
+ synchronized (mLock) {
+ mFoldedDeviceState = folded;
+ ArrayList<WeakReference<DeviceStateListener>> invalidListeners = new ArrayList<>();
+ for (WeakReference<DeviceStateListener> listener : mDeviceStateListeners) {
+ DeviceStateListener callback = listener.get();
+ if (callback != null) {
+ callback.onDeviceStateChanged(folded);
+ } else {
+ invalidListeners.add(listener);
+ }
+ }
+ if (!invalidListeners.isEmpty()) {
+ mDeviceStateListeners.removeAll(invalidListeners);
+ }
+ }
+ }
+
+ @Override
+ public final void onBaseStateChanged(int state) {
+ handleStateChange(state);
+ }
+
+ @Override
+ public final void onStateChanged(int state) {
+ handleStateChange(state);
+ }
+ }
+
+ /**
+ * Register a {@link CameraCharacteristics} device state listener
+ *
+ * @param chars Camera characteristics that need to receive device state updates
+ *
+ * @hide
+ */
+ public void registerDeviceStateListener(@NonNull CameraCharacteristics chars) {
+ synchronized (mLock) {
+ DeviceStateListener listener = chars.getDeviceStateListener();
+ listener.onDeviceStateChanged(mFoldedDeviceState);
+ mDeviceStateListeners.add(new WeakReference<>(listener));
+ }
}
/**
@@ -504,6 +586,7 @@
"Camera service is currently unavailable", e);
}
}
+ registerDeviceStateListener(characteristics);
return characteristics;
}
@@ -1327,8 +1410,7 @@
private ICameraService mCameraService;
// Singleton, don't allow construction
- private CameraManagerGlobal() {
- }
+ private CameraManagerGlobal() { }
public static final boolean sCameraServiceDisabled =
SystemProperties.getBoolean("config.disable_cameraservice", false);
diff --git a/core/java/android/hardware/camera2/impl/CameraMetadataNative.java b/core/java/android/hardware/camera2/impl/CameraMetadataNative.java
index 196134b..3745022 100644
--- a/core/java/android/hardware/camera2/impl/CameraMetadataNative.java
+++ b/core/java/android/hardware/camera2/impl/CameraMetadataNative.java
@@ -50,6 +50,7 @@
import android.hardware.camera2.marshal.impl.MarshalQueryableStreamConfigurationDuration;
import android.hardware.camera2.marshal.impl.MarshalQueryableString;
import android.hardware.camera2.params.Capability;
+import android.hardware.camera2.params.DeviceStateOrientationMap;
import android.hardware.camera2.params.Face;
import android.hardware.camera2.params.HighSpeedVideoConfiguration;
import android.hardware.camera2.params.LensShadingMap;
@@ -754,7 +755,7 @@
});
sGetCommandMap.put(
CaptureResult.STATISTICS_LENS_SHADING_CORRECTION_MAP.getNativeKey(),
- new GetCommand() {
+ new GetCommand() {
@Override
@SuppressWarnings("unchecked")
public <T> T getValue(CameraMetadataNative metadata, Key<T> key) {
@@ -762,6 +763,15 @@
}
});
sGetCommandMap.put(
+ CameraCharacteristics.INFO_DEVICE_STATE_ORIENTATION_MAP.getNativeKey(),
+ new GetCommand() {
+ @Override
+ @SuppressWarnings("unchecked")
+ public <T> T getValue(CameraMetadataNative metadata, Key<T> key) {
+ return (T) metadata.getDeviceStateOrientationMap();
+ }
+ });
+ sGetCommandMap.put(
CaptureResult.STATISTICS_OIS_SAMPLES.getNativeKey(),
new GetCommand() {
@Override
@@ -994,6 +1004,18 @@
return map;
}
+ private DeviceStateOrientationMap getDeviceStateOrientationMap() {
+ long[] mapArray = getBase(CameraCharacteristics.INFO_DEVICE_STATE_ORIENTATIONS);
+
+ // Do not warn if map is null while s is not. This is valid.
+ if (mapArray == null) {
+ return null;
+ }
+
+ DeviceStateOrientationMap map = new DeviceStateOrientationMap(mapArray);
+ return map;
+ }
+
private Location getGpsLocation() {
String processingMethod = get(CaptureResult.JPEG_GPS_PROCESSING_METHOD);
double[] coords = get(CaptureResult.JPEG_GPS_COORDINATES);
diff --git a/core/java/android/hardware/camera2/params/DeviceStateOrientationMap.java b/core/java/android/hardware/camera2/params/DeviceStateOrientationMap.java
new file mode 100644
index 0000000..3907f04
--- /dev/null
+++ b/core/java/android/hardware/camera2/params/DeviceStateOrientationMap.java
@@ -0,0 +1,155 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.camera2.params;
+
+import android.annotation.LongDef;
+import android.hardware.camera2.CameraCharacteristics;
+import android.hardware.camera2.utils.HashCodeHelpers;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.Objects;
+
+/**
+ * Immutable class that maps the device fold state to sensor orientation.
+ *
+ * <p>Some {@link CameraCharacteristics#REQUEST_AVAILABLE_CAPABILITIES_LOGICAL_MULTI_CAMERA logical}
+ * cameras on foldables can include physical sensors with different sensor orientation
+ * values. As a result, the values of the logical camera device can potentially change depending
+ * on the device fold state.</p>
+ *
+ * <p>The device fold state to sensor orientation map will contain information about the
+ * respective logical camera sensor orientation given a device state. Clients
+ * can query the mapping for all possible supported folded states.
+ *
+ * @see CameraCharacteristics#SENSOR_ORIENTATION
+ */
+public final class DeviceStateOrientationMap {
+ /**
+ * Needs to be kept in sync with the HIDL/AIDL DeviceState
+ */
+
+ /**
+ * The device is in its normal physical configuration. This is the default if the
+ * device does not support multiple different states.
+ */
+ public static final long NORMAL = 0;
+
+ /**
+ * The device is folded. If not set, the device is unfolded or does not
+ * support folding.
+ *
+ * The exact point when this status change happens during the folding
+ * operation is device-specific.
+ */
+ public static final long FOLDED = 1 << 2;
+
+ /** @hide */
+ @Retention(RetentionPolicy.SOURCE)
+ @LongDef(prefix = {"DEVICE_STATE"}, value =
+ {NORMAL,
+ FOLDED })
+ public @interface DeviceState {};
+
+ private final HashMap<Long, Integer> mDeviceStateOrientationMap = new HashMap<>();
+
+ /**
+ * Create a new immutable DeviceStateOrientationMap instance.
+ *
+ * <p>This constructor takes over the array; do not write to the array afterwards.</p>
+ *
+ * @param elements
+ * An array of elements describing the map
+ *
+ * @throws IllegalArgumentException
+ * if the {@code elements} array length is invalid, not divisible by 2 or contains
+ * invalid element values
+ * @throws NullPointerException
+ * if {@code elements} is {@code null}
+ *
+ * @hide
+ */
+ public DeviceStateOrientationMap(final long[] elements) {
+ mElements = Objects.requireNonNull(elements, "elements must not be null");
+ if ((elements.length % 2) != 0) {
+ throw new IllegalArgumentException("Device state orientation map length " +
+ elements.length + " is not even!");
+ }
+
+ for (int i = 0; i < elements.length; i += 2) {
+ if ((elements[i+1] % 90) != 0) {
+ throw new IllegalArgumentException("Sensor orientation not divisible by 90: " +
+ elements[i+1]);
+ }
+
+ mDeviceStateOrientationMap.put(elements[i], Math.toIntExact(elements[i + 1]));
+ }
+ }
+
+ /**
+ * Return the logical camera sensor orientation given a specific device fold state.
+ *
+ * @param deviceState Device fold state
+ *
+ * @return Valid {@link android.hardware.camera2.CameraCharacteristics#SENSOR_ORIENTATION} for
+ * any supported device fold state
+ *
+ * @throws IllegalArgumentException if the given device state is invalid
+ */
+ public int getSensorOrientation(@DeviceState long deviceState) {
+ if (!mDeviceStateOrientationMap.containsKey(deviceState)) {
+ throw new IllegalArgumentException("Invalid device state: " + deviceState);
+ }
+
+ return mDeviceStateOrientationMap.get(deviceState);
+ }
+
+ /**
+ * Check if this DeviceStateOrientationMap is equal to another DeviceStateOrientationMap.
+ *
+ * <p>Two device state orientation maps are equal if and only if all of their elements are
+ * {@link Object#equals equal}.</p>
+ *
+ * @return {@code true} if the objects were equal, {@code false} otherwise
+ */
+ @Override
+ public boolean equals(final Object obj) {
+ if (obj == null) {
+ return false;
+ }
+ if (this == obj) {
+ return true;
+ }
+ if (obj instanceof DeviceStateOrientationMap) {
+ final DeviceStateOrientationMap other = (DeviceStateOrientationMap) obj;
+ return Arrays.equals(mElements, other.mElements);
+ }
+ return false;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public int hashCode() {
+ return HashCodeHelpers.hashCodeGeneric(mElements);
+ }
+
+ private final long[] mElements;
+}
diff --git a/core/java/android/hardware/display/AmbientDisplayConfiguration.java b/core/java/android/hardware/display/AmbientDisplayConfiguration.java
index a1f7aa1..f5b2ac5 100644
--- a/core/java/android/hardware/display/AmbientDisplayConfiguration.java
+++ b/core/java/android/hardware/display/AmbientDisplayConfiguration.java
@@ -22,6 +22,9 @@
import android.os.SystemProperties;
import android.provider.Settings;
import android.text.TextUtils;
+import android.util.Log;
+
+import java.util.Arrays;
import com.android.internal.R;
@@ -32,7 +35,7 @@
*/
@TestApi
public class AmbientDisplayConfiguration {
-
+ private static final String TAG = "AmbientDisplayConfig";
private final Context mContext;
private final boolean mAlwaysOnByDefault;
@@ -141,11 +144,20 @@
}
/** {@hide} */
- public String tapSensorType() {
+ private String tapSensorType() {
return mContext.getResources().getString(R.string.config_dozeTapSensorType);
}
/** {@hide} */
+ public String tapSensorType(int posture) {
+ return getSensorFromPostureMapping(
+ mContext.getResources().getStringArray(R.array.config_dozeTapSensorPostureMapping),
+ tapSensorType(),
+ posture
+ );
+ }
+
+ /** {@hide} */
public String longPressSensorType() {
return mContext.getResources().getString(R.string.config_dozeLongPressSensorType);
}
@@ -241,4 +253,20 @@
private boolean boolSetting(String name, int user, int def) {
return Settings.Secure.getIntForUser(mContext.getContentResolver(), name, def, user) != 0;
}
+
+ /** {@hide} */
+ public static String getSensorFromPostureMapping(
+ String[] postureMapping,
+ String defaultValue,
+ int posture) {
+ String sensorType = defaultValue;
+ if (postureMapping != null && posture < postureMapping.length) {
+ sensorType = postureMapping[posture];
+ } else {
+ Log.e(TAG, "Unsupported doze posture " + posture
+ + " postureMapping=" + Arrays.toString(postureMapping));
+ }
+
+ return TextUtils.isEmpty(sensorType) ? defaultValue : sensorType;
+ }
}
diff --git a/core/java/android/hardware/display/BrightnessInfo.java b/core/java/android/hardware/display/BrightnessInfo.java
index c5d37c2..0dc8f92 100644
--- a/core/java/android/hardware/display/BrightnessInfo.java
+++ b/core/java/android/hardware/display/BrightnessInfo.java
@@ -60,12 +60,18 @@
/** Brightness */
public final float brightness;
+ /** Brightness after {@link DisplayPowerController} adjustments */
+ public final float adjustedBrightness;
+
/** Current minimum supported brightness. */
public final float brightnessMinimum;
/** Current maximum supported brightness. */
public final float brightnessMaximum;
+ /** Brightness values greater than this point are only used in High Brightness Mode. */
+ public final float highBrightnessTransitionPoint;
+
/**
* Current state of high brightness mode.
* Can be any of HIGH_BRIGHTNESS_MODE_* values.
@@ -73,11 +79,20 @@
public final int highBrightnessMode;
public BrightnessInfo(float brightness, float brightnessMinimum, float brightnessMaximum,
- @HighBrightnessMode int highBrightnessMode) {
+ @HighBrightnessMode int highBrightnessMode, float highBrightnessTransitionPoint) {
+ this(brightness, brightness, brightnessMinimum, brightnessMaximum, highBrightnessMode,
+ highBrightnessTransitionPoint);
+ }
+
+ public BrightnessInfo(float brightness, float adjustedBrightness, float brightnessMinimum,
+ float brightnessMaximum, @HighBrightnessMode int highBrightnessMode,
+ float highBrightnessTransitionPoint) {
this.brightness = brightness;
+ this.adjustedBrightness = adjustedBrightness;
this.brightnessMinimum = brightnessMinimum;
this.brightnessMaximum = brightnessMaximum;
this.highBrightnessMode = highBrightnessMode;
+ this.highBrightnessTransitionPoint = highBrightnessTransitionPoint;
}
/**
@@ -103,9 +118,11 @@
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeFloat(brightness);
+ dest.writeFloat(adjustedBrightness);
dest.writeFloat(brightnessMinimum);
dest.writeFloat(brightnessMaximum);
dest.writeInt(highBrightnessMode);
+ dest.writeFloat(highBrightnessTransitionPoint);
}
public static final @android.annotation.NonNull Creator<BrightnessInfo> CREATOR =
@@ -123,9 +140,11 @@
private BrightnessInfo(Parcel source) {
brightness = source.readFloat();
+ adjustedBrightness = source.readFloat();
brightnessMinimum = source.readFloat();
brightnessMaximum = source.readFloat();
highBrightnessMode = source.readInt();
+ highBrightnessTransitionPoint = source.readFloat();
}
}
diff --git a/core/java/android/text/TextLine.java b/core/java/android/text/TextLine.java
index 1f11d10..1a7ec7f 100644
--- a/core/java/android/text/TextLine.java
+++ b/core/java/android/text/TextLine.java
@@ -782,7 +782,7 @@
int spanStart = runStart;
int spanLimit;
- if (mSpanned == null) {
+ if (mSpanned == null || runStart == runLimit) {
spanLimit = runLimit;
} else {
int target = after ? offset + 1 : offset;
diff --git a/core/java/android/view/SurfaceControl.java b/core/java/android/view/SurfaceControl.java
index 5b8dc40..c786f0f 100644
--- a/core/java/android/view/SurfaceControl.java
+++ b/core/java/android/view/SurfaceControl.java
@@ -23,6 +23,7 @@
import static android.graphics.Matrix.MTRANS_X;
import static android.graphics.Matrix.MTRANS_Y;
import static android.view.SurfaceControlProto.HASH_CODE;
+import static android.view.SurfaceControlProto.LAYER_ID;
import static android.view.SurfaceControlProto.NAME;
import android.annotation.FloatRange;
@@ -242,6 +243,7 @@
private static native int nativeGetGPUContextPriority();
private static native void nativeSetTransformHint(long nativeObject, int transformHint);
private static native int nativeGetTransformHint(long nativeObject);
+ private static native int nativeGetLayerId(long nativeObject);
@Nullable
@GuardedBy("mLock")
@@ -357,8 +359,6 @@
@GuardedBy("mLock")
private int mHeight;
- private int mTransformHint;
-
private WeakReference<View> mLocalOwnerView;
static GlobalTransactionWrapper sGlobalTransaction;
@@ -1541,6 +1541,7 @@
final long token = proto.start(fieldId);
proto.write(HASH_CODE, System.identityHashCode(this));
proto.write(NAME, mName);
+ proto.write(LAYER_ID, getLayerId());
proto.end(token);
}
@@ -3675,4 +3676,15 @@
public void setTransformHint(@Surface.Rotation int transformHint) {
nativeSetTransformHint(mNativeObject, transformHint);
}
+
+ /**
+ * @hide
+ */
+ public int getLayerId() {
+ if (mNativeObject != 0) {
+ return nativeGetLayerId(mNativeObject);
+ }
+
+ return -1;
+ }
}
diff --git a/core/java/android/window/ITaskOrganizer.aidl b/core/java/android/window/ITaskOrganizer.aidl
index 69bc1b5..fd86769 100644
--- a/core/java/android/window/ITaskOrganizer.aidl
+++ b/core/java/android/window/ITaskOrganizer.aidl
@@ -20,6 +20,7 @@
import android.app.ActivityManager;
import android.graphics.Rect;
import android.window.StartingWindowInfo;
+import android.window.StartingWindowRemovalInfo;
import android.window.WindowContainerToken;
/**
@@ -39,12 +40,9 @@
/**
* Called when the Task want to remove the starting window.
- * @param leash A persistent leash for the top window in this task.
- * @param frame Window frame of the top window.
- * @param playRevealAnimation Play vanish animation.
+ * @param removalInfo The information used to remove the starting window.
*/
- void removeStartingWindow(int taskId, in SurfaceControl leash, in Rect frame,
- in boolean playRevealAnimation);
+ void removeStartingWindow(in StartingWindowRemovalInfo removalInfo);
/**
* Called when the Task want to copy the splash screen.
diff --git a/core/java/android/window/StartingWindowInfo.java b/core/java/android/window/StartingWindowInfo.java
index 10d21a0..5950e9f 100644
--- a/core/java/android/window/StartingWindowInfo.java
+++ b/core/java/android/window/StartingWindowInfo.java
@@ -19,7 +19,6 @@
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
-import android.annotation.TestApi;
import android.app.ActivityManager;
import android.app.TaskInfo;
import android.content.pm.ActivityInfo;
@@ -34,7 +33,6 @@
* start in the system.
* @hide
*/
-@TestApi
public final class StartingWindowInfo implements Parcelable {
/**
* Prefer nothing or not care the type of starting window.
diff --git a/core/java/android/window/StartingWindowRemovalInfo.aidl b/core/java/android/window/StartingWindowRemovalInfo.aidl
new file mode 100644
index 0000000..8e4ac04
--- /dev/null
+++ b/core/java/android/window/StartingWindowRemovalInfo.aidl
@@ -0,0 +1,20 @@
+/**
+ * Copyright (c) 2021, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.window;
+
+/** @hide */
+parcelable StartingWindowRemovalInfo;
\ No newline at end of file
diff --git a/core/java/android/window/StartingWindowRemovalInfo.java b/core/java/android/window/StartingWindowRemovalInfo.java
new file mode 100644
index 0000000..573db0d
--- /dev/null
+++ b/core/java/android/window/StartingWindowRemovalInfo.java
@@ -0,0 +1,111 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.window;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.graphics.Rect;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.view.SurfaceControl;
+
+/**
+ * Information when removing a starting window of a particular task.
+ * @hide
+ */
+public final class StartingWindowRemovalInfo implements Parcelable {
+
+ /**
+ * The identifier of a task.
+ * @hide
+ */
+ public int taskId;
+
+ /**
+ * The animation container layer of the top activity.
+ * @hide
+ */
+ @Nullable
+ public SurfaceControl windowAnimationLeash;
+
+ /**
+ * The main window frame for the window of the top activity.
+ * @hide
+ */
+ @Nullable
+ public Rect mainFrame;
+
+ /**
+ * Whether need to play reveal animation.
+ * @hide
+ */
+ public boolean playRevealAnimation;
+
+ /**
+ * Whether need to defer removing the starting window for IME.
+ * @hide
+ */
+ public boolean deferRemoveForIme;
+
+ public StartingWindowRemovalInfo() {
+
+ }
+
+ private StartingWindowRemovalInfo(@NonNull Parcel source) {
+ readFromParcel(source);
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ void readFromParcel(@NonNull Parcel source) {
+ taskId = source.readInt();
+ windowAnimationLeash = source.readTypedObject(SurfaceControl.CREATOR);
+ mainFrame = source.readTypedObject(Rect.CREATOR);
+ playRevealAnimation = source.readBoolean();
+ deferRemoveForIme = source.readBoolean();
+ }
+
+ @Override
+ public void writeToParcel(@NonNull Parcel dest, int flags) {
+ dest.writeInt(taskId);
+ dest.writeTypedObject(windowAnimationLeash, flags);
+ dest.writeTypedObject(mainFrame, flags);
+ dest.writeBoolean(playRevealAnimation);
+ dest.writeBoolean(deferRemoveForIme);
+ }
+
+ @Override
+ public String toString() {
+ return "StartingWindowRemovalInfo{taskId=" + taskId
+ + " frame=" + mainFrame
+ + " playRevealAnimation=" + playRevealAnimation
+ + " deferRemoveForIme=" + deferRemoveForIme + "}";
+ }
+
+ public static final @android.annotation.NonNull Creator<StartingWindowRemovalInfo> CREATOR =
+ new Creator<StartingWindowRemovalInfo>() {
+ public StartingWindowRemovalInfo createFromParcel(@NonNull Parcel source) {
+ return new StartingWindowRemovalInfo(source);
+ }
+ public StartingWindowRemovalInfo[] newArray(int size) {
+ return new StartingWindowRemovalInfo[size];
+ }
+ };
+}
diff --git a/core/java/android/window/TaskOrganizer.java b/core/java/android/window/TaskOrganizer.java
index 845c13d..27c7d315 100644
--- a/core/java/android/window/TaskOrganizer.java
+++ b/core/java/android/window/TaskOrganizer.java
@@ -24,7 +24,6 @@
import android.annotation.SuppressLint;
import android.annotation.TestApi;
import android.app.ActivityManager;
-import android.graphics.Rect;
import android.os.IBinder;
import android.os.RemoteException;
import android.view.SurfaceControl;
@@ -94,6 +93,7 @@
* @param info The information about the Task that's available
* @param appToken Token of the application being started.
* context to for resources
+ * @hide
*/
@BinderThread
public void addStartingWindow(@NonNull StartingWindowInfo info,
@@ -101,14 +101,11 @@
/**
* Called when the Task want to remove the starting window.
- * @param leash A persistent leash for the top window in this task. Release it once exit
- * animation has finished.
- * @param frame Window frame of the top window.
- * @param playRevealAnimation Play vanish animation.
+ * @param removalInfo The information used to remove the starting window.
+ * @hide
*/
@BinderThread
- public void removeStartingWindow(int taskId, @Nullable SurfaceControl leash,
- @Nullable Rect frame, boolean playRevealAnimation) {}
+ public void removeStartingWindow(@NonNull StartingWindowRemovalInfo removalInfo) {}
/**
* Called when the Task want to copy the splash screen.
@@ -257,10 +254,8 @@
}
@Override
- public void removeStartingWindow(int taskId, SurfaceControl leash, Rect frame,
- boolean playRevealAnimation) {
- mExecutor.execute(() -> TaskOrganizer.this.removeStartingWindow(taskId, leash, frame,
- playRevealAnimation));
+ public void removeStartingWindow(StartingWindowRemovalInfo removalInfo) {
+ mExecutor.execute(() -> TaskOrganizer.this.removeStartingWindow(removalInfo));
}
@Override
diff --git a/core/java/com/android/internal/os/AmbientDisplayPowerCalculator.java b/core/java/com/android/internal/os/AmbientDisplayPowerCalculator.java
index 0307268..93baa19 100644
--- a/core/java/com/android/internal/os/AmbientDisplayPowerCalculator.java
+++ b/core/java/com/android/internal/os/AmbientDisplayPowerCalculator.java
@@ -16,6 +16,8 @@
package com.android.internal.os;
+import static com.android.internal.os.PowerProfile.POWER_GROUP_DISPLAY_AMBIENT;
+
import android.os.BatteryConsumer;
import android.os.BatteryStats;
import android.os.BatteryUsageStats;
@@ -32,8 +34,9 @@
private final UsageBasedPowerEstimator mPowerEstimator;
public AmbientDisplayPowerCalculator(PowerProfile powerProfile) {
+ // TODO(b/200239964): update to support multidisplay.
mPowerEstimator = new UsageBasedPowerEstimator(
- powerProfile.getAveragePower(PowerProfile.POWER_AMBIENT_DISPLAY));
+ powerProfile.getAveragePowerForOrdinal(POWER_GROUP_DISPLAY_AMBIENT, 0));
}
/**
diff --git a/core/java/com/android/internal/os/PowerProfile.java b/core/java/com/android/internal/os/PowerProfile.java
index add2304..4d19b35 100644
--- a/core/java/com/android/internal/os/PowerProfile.java
+++ b/core/java/com/android/internal/os/PowerProfile.java
@@ -17,10 +17,12 @@
package com.android.internal.os;
+import android.annotation.StringDef;
import android.compat.annotation.UnsupportedAppUsage;
import android.content.Context;
import android.content.res.Resources;
import android.content.res.XmlResourceParser;
+import android.util.Slog;
import android.util.proto.ProtoOutputStream;
import com.android.internal.annotations.VisibleForTesting;
@@ -30,6 +32,8 @@
import org.xmlpull.v1.XmlPullParserException;
import java.io.IOException;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
import java.util.HashMap;
@@ -40,6 +44,8 @@
*/
public class PowerProfile {
+ public static final String TAG = "PowerProfile";
+
/*
* POWER_CPU_SUSPEND: Power consumption when CPU is in power collapse mode.
* POWER_CPU_IDLE: Power consumption when CPU is awake (when a wake lock is held). This should
@@ -145,12 +151,18 @@
/**
* Power consumption when screen is in doze/ambient/always-on mode, including backlight power.
+ *
+ * @deprecated Use {@link #POWER_GROUP_DISPLAY_AMBIENT} instead.
*/
+ @Deprecated
public static final String POWER_AMBIENT_DISPLAY = "ambient.on";
/**
* Power consumption when screen is on, not including the backlight power.
+ *
+ * @deprecated Use {@link #POWER_GROUP_DISPLAY_SCREEN_ON} instead.
*/
+ @Deprecated
@UnsupportedAppUsage
public static final String POWER_SCREEN_ON = "screen.on";
@@ -175,7 +187,10 @@
/**
* Power consumption at full backlight brightness. If the backlight is at
* 50% brightness, then this should be multiplied by 0.5
+ *
+ * @deprecated Use {@link #POWER_GROUP_DISPLAY_SCREEN_FULL} instead.
*/
+ @Deprecated
@UnsupportedAppUsage
public static final String POWER_SCREEN_FULL = "screen.full";
@@ -221,6 +236,29 @@
public static final String POWER_BATTERY_CAPACITY = "battery.capacity";
/**
+ * Power consumption when a screen is in doze/ambient/always-on mode, including backlight power.
+ */
+ public static final String POWER_GROUP_DISPLAY_AMBIENT = "ambient.on.display";
+
+ /**
+ * Power consumption when a screen is on, not including the backlight power.
+ */
+ public static final String POWER_GROUP_DISPLAY_SCREEN_ON = "screen.on.display";
+
+ /**
+ * Power consumption of a screen at full backlight brightness.
+ */
+ public static final String POWER_GROUP_DISPLAY_SCREEN_FULL = "screen.full.display";
+
+ @StringDef(prefix = { "POWER_GROUP_" }, value = {
+ POWER_GROUP_DISPLAY_AMBIENT,
+ POWER_GROUP_DISPLAY_SCREEN_ON,
+ POWER_GROUP_DISPLAY_SCREEN_FULL,
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface PowerGroup {}
+
+ /**
* A map from Power Use Item to its power consumption.
*/
static final HashMap<String, Double> sPowerItemMap = new HashMap<>();
@@ -255,6 +293,7 @@
readPowerValuesFromXml(context, forTest);
}
initCpuClusters();
+ initDisplays();
}
}
@@ -424,6 +463,58 @@
return 0;
}
+ private int mNumDisplays;
+
+ private void initDisplays() {
+ // Figure out how many displays are listed in the power profile.
+ mNumDisplays = 0;
+ while (!Double.isNaN(
+ getAveragePowerForOrdinal(POWER_GROUP_DISPLAY_AMBIENT, mNumDisplays, Double.NaN))
+ || !Double.isNaN(
+ getAveragePowerForOrdinal(POWER_GROUP_DISPLAY_SCREEN_ON, mNumDisplays, Double.NaN))
+ || !Double.isNaN(
+ getAveragePowerForOrdinal(POWER_GROUP_DISPLAY_SCREEN_FULL, mNumDisplays,
+ Double.NaN))) {
+ mNumDisplays++;
+ }
+
+ // Handle legacy display power constants.
+ final Double deprecatedAmbientDisplay = sPowerItemMap.get(POWER_AMBIENT_DISPLAY);
+ boolean legacy = false;
+ if (deprecatedAmbientDisplay != null && mNumDisplays == 0) {
+ final String key = getOrdinalPowerType(POWER_GROUP_DISPLAY_AMBIENT, 0);
+ Slog.w(TAG, POWER_AMBIENT_DISPLAY + " is deprecated! Use " + key + " instead.");
+ sPowerItemMap.put(key, deprecatedAmbientDisplay);
+ legacy = true;
+ }
+
+ final Double deprecatedScreenOn = sPowerItemMap.get(POWER_SCREEN_ON);
+ if (deprecatedScreenOn != null && mNumDisplays == 0) {
+ final String key = getOrdinalPowerType(POWER_GROUP_DISPLAY_SCREEN_ON, 0);
+ Slog.w(TAG, POWER_SCREEN_ON + " is deprecated! Use " + key + " instead.");
+ sPowerItemMap.put(key, deprecatedScreenOn);
+ legacy = true;
+ }
+
+ final Double deprecatedScreenFull = sPowerItemMap.get(POWER_SCREEN_FULL);
+ if (deprecatedScreenFull != null && mNumDisplays == 0) {
+ final String key = getOrdinalPowerType(POWER_GROUP_DISPLAY_SCREEN_FULL, 0);
+ Slog.w(TAG, POWER_SCREEN_FULL + " is deprecated! Use " + key + " instead.");
+ sPowerItemMap.put(key, deprecatedScreenFull);
+ legacy = true;
+ }
+ if (legacy) {
+ mNumDisplays = 1;
+ }
+ }
+
+ /**
+ * Returns the number built in displays on the device as defined in the power_profile.xml.
+ */
+ public int getNumDisplays() {
+ return mNumDisplays;
+ }
+
/**
* Returns the number of memory bandwidth buckets defined in power_profile.xml, or a
* default value if the subsystem has no recorded value.
@@ -496,6 +587,32 @@
}
/**
+ * Returns the average current in mA consumed by an ordinaled subsystem, or the given
+ * default value if the subsystem has no recorded value.
+ *
+ * @param group the subsystem {@link PowerGroup}.
+ * @param ordinal which entity in the {@link PowerGroup}.
+ * @param defaultValue the value to return if the subsystem has no recorded value.
+ * @return the average current in milliAmps.
+ */
+ public double getAveragePowerForOrdinal(@PowerGroup String group, int ordinal,
+ double defaultValue) {
+ final String type = getOrdinalPowerType(group, ordinal);
+ return getAveragePowerOrDefault(type, defaultValue);
+ }
+
+ /**
+ * Returns the average current in mA consumed by an ordinaled subsystem.
+ *
+ * @param group the subsystem {@link PowerGroup}.
+ * @param ordinal which entity in the {@link PowerGroup}.
+ * @return the average current in milliAmps.
+ */
+ public double getAveragePowerForOrdinal(@PowerGroup String group, int ordinal) {
+ return getAveragePowerForOrdinal(group, ordinal, 0);
+ }
+
+ /**
* Returns the battery capacity, if available, in milli Amp Hours. If not available,
* it returns zero.
*
@@ -682,4 +799,9 @@
}
}
}
+
+ // Creates the key for an ordinaled power constant from the group and ordinal.
+ private static String getOrdinalPowerType(@PowerGroup String group, int ordinal) {
+ return group + ordinal;
+ }
}
diff --git a/core/java/com/android/internal/os/ScreenPowerCalculator.java b/core/java/com/android/internal/os/ScreenPowerCalculator.java
index 1b3bc23..72ad4e7 100644
--- a/core/java/com/android/internal/os/ScreenPowerCalculator.java
+++ b/core/java/com/android/internal/os/ScreenPowerCalculator.java
@@ -16,6 +16,9 @@
package com.android.internal.os;
+import static com.android.internal.os.PowerProfile.POWER_GROUP_DISPLAY_SCREEN_FULL;
+import static com.android.internal.os.PowerProfile.POWER_GROUP_DISPLAY_SCREEN_ON;
+
import android.os.BatteryConsumer;
import android.os.BatteryStats;
import android.os.BatteryUsageStats;
@@ -50,10 +53,11 @@
}
public ScreenPowerCalculator(PowerProfile powerProfile) {
+ // TODO(b/200239964): update to support multidisplay.
mScreenOnPowerEstimator = new UsageBasedPowerEstimator(
- powerProfile.getAveragePower(PowerProfile.POWER_SCREEN_ON));
+ powerProfile.getAveragePowerForOrdinal(POWER_GROUP_DISPLAY_SCREEN_ON, 0));
mScreenFullPowerEstimator = new UsageBasedPowerEstimator(
- powerProfile.getAveragePower(PowerProfile.POWER_SCREEN_FULL));
+ powerProfile.getAveragePowerForOrdinal(POWER_GROUP_DISPLAY_SCREEN_FULL, 0));
}
@Override
diff --git a/core/jni/android_view_SurfaceControl.cpp b/core/jni/android_view_SurfaceControl.cpp
index 5ce43df..65ff7c7 100644
--- a/core/jni/android_view_SurfaceControl.cpp
+++ b/core/jni/android_view_SurfaceControl.cpp
@@ -1827,6 +1827,12 @@
return toRotationInt(ui::Transform::toRotation((transformHintRotationFlags)));
}
+static jint nativeGetLayerId(JNIEnv* env, jclass clazz, jlong nativeSurfaceControl) {
+ sp<SurfaceControl> surface(reinterpret_cast<SurfaceControl*>(nativeSurfaceControl));
+
+ return surface->getLayerId();
+}
+
// ----------------------------------------------------------------------------
static const JNINativeMethod sSurfaceControlMethods[] = {
@@ -2026,6 +2032,8 @@
(void*)nativeSetTrustedOverlay },
{"nativeSetDropInputMode", "(JJI)V",
(void*)nativeSetDropInputMode },
+ {"nativeGetLayerId", "(J)I",
+ (void*)nativeGetLayerId },
// clang-format on
};
diff --git a/core/proto/android/server/windowmanagerservice.proto b/core/proto/android/server/windowmanagerservice.proto
index 0121bff..4af9d75 100644
--- a/core/proto/android/server/windowmanagerservice.proto
+++ b/core/proto/android/server/windowmanagerservice.proto
@@ -480,6 +480,7 @@
optional SurfaceAnimatorProto surface_animator = 4;
repeated WindowContainerChildProto children = 5;
optional IdentifierProto identifier = 6;
+ optional .android.view.SurfaceControlProto surface_control = 7;
}
/* represents a generic child of a WindowContainer */
diff --git a/core/proto/android/view/surfacecontrol.proto b/core/proto/android/view/surfacecontrol.proto
index cbb243b..5a5f035 100644
--- a/core/proto/android/view/surfacecontrol.proto
+++ b/core/proto/android/view/surfacecontrol.proto
@@ -29,4 +29,5 @@
optional int32 hash_code = 1;
optional string name = 2 [ (android.privacy).dest = DEST_EXPLICIT ];
+ optional int32 layerId = 3;
}
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 3f530fe..1255dad 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -248,6 +248,8 @@
android:name="com.android.bluetooth.BluetoothMapContentObserver.action.MESSAGE_DELIVERY" />
<protected-broadcast
android:name="android.bluetooth.pan.profile.action.CONNECTION_STATE_CHANGED" />
+ <protected-broadcast android:name="android.bluetooth.action.LE_AUDIO_CONNECTION_STATE_CHANGED" />
+ <protected-broadcast android:name="android.bluetooth.action.LE_AUDIO_ACTIVE_DEVICE_CHANGED" />
<protected-broadcast
android:name="android.bluetooth.action.TETHERING_STATE_CHANGED" />
<protected-broadcast android:name="android.bluetooth.pbap.profile.action.CONNECTION_STATE_CHANGED" />
diff --git a/core/res/res/anim-ldrtl/task_close_exit.xml b/core/res/res/anim-ldrtl/task_close_exit.xml
index 76fbdff..0887019 100644
--- a/core/res/res/anim-ldrtl/task_close_exit.xml
+++ b/core/res/res/anim-ldrtl/task_close_exit.xml
@@ -28,9 +28,4 @@
android:startOffset="0"
android:duration="500"/>
- <!-- This is needed to keep the animation running while task_open_enter completes -->
- <alpha
- android:fromAlpha="1.0"
- android:toAlpha="1.0"
- android:duration="600"/>
-</set>
\ No newline at end of file
+</set>
diff --git a/core/res/res/anim-ldrtl/task_open_exit.xml b/core/res/res/anim-ldrtl/task_open_exit.xml
index beb6fca..88cdcce 100644
--- a/core/res/res/anim-ldrtl/task_open_exit.xml
+++ b/core/res/res/anim-ldrtl/task_open_exit.xml
@@ -28,9 +28,4 @@
android:startOffset="0"
android:duration="500"/>
- <!-- This is needed to keep the animation running while task_open_enter completes -->
- <alpha
- android:fromAlpha="1.0"
- android:toAlpha="1.0"
- android:duration="600"/>
-</set>
\ No newline at end of file
+</set>
diff --git a/core/res/res/anim/task_close_exit.xml b/core/res/res/anim/task_close_exit.xml
index 736f3f2..3a8dd93 100644
--- a/core/res/res/anim/task_close_exit.xml
+++ b/core/res/res/anim/task_close_exit.xml
@@ -30,9 +30,4 @@
android:startOffset="0"
android:duration="500"/>
- <!-- This is needed to keep the animation running while task_open_enter completes -->
- <alpha
- android:fromAlpha="1.0"
- android:toAlpha="1.0"
- android:duration="600"/>
-</set>
\ No newline at end of file
+</set>
diff --git a/core/res/res/anim/task_open_exit.xml b/core/res/res/anim/task_open_exit.xml
index d170317..21fec7f 100644
--- a/core/res/res/anim/task_open_exit.xml
+++ b/core/res/res/anim/task_open_exit.xml
@@ -30,9 +30,4 @@
android:startOffset="0"
android:duration="500"/>
- <!-- This is needed to keep the animation running while task_open_enter completes -->
- <alpha
- android:fromAlpha="1.0"
- android:toAlpha="1.0"
- android:duration="600"/>
-</set>
\ No newline at end of file
+</set>
diff --git a/core/res/res/values-bg/strings.xml b/core/res/res/values-bg/strings.xml
index 01ebe06..cdd27d4 100644
--- a/core/res/res/values-bg/strings.xml
+++ b/core/res/res/values-bg/strings.xml
@@ -2097,7 +2097,7 @@
<string name="notification_feedback_indicator_promoted" msgid="9030204303764698640">"Това известие бе класирано по-високо. Докоснете, за да изпратите отзиви."</string>
<string name="notification_feedback_indicator_demoted" msgid="8880309924296450875">"Това известие бе класирано по-ниско. Докоснете, за да изпратите отзиви."</string>
<string name="nas_upgrade_notification_title" msgid="8436359459300146555">"Подобрени известия"</string>
- <string name="nas_upgrade_notification_content" msgid="5157550369837103337">"Предложените действия и отговори вече се предоставят от функцията за подобрени известия. Адаптивните известия за Android вече не се поддържат."</string>
+ <string name="nas_upgrade_notification_content" msgid="5157550369837103337">"Предложените действия и отговори вече се предоставят от функцията за подобрени известия. Адаптивните известия за Android вече не се поддържат."</string>
<string name="nas_upgrade_notification_enable_action" msgid="3046406808378726874">"OK"</string>
<string name="nas_upgrade_notification_disable_action" msgid="3794833210043497982">"Изключване"</string>
<string name="nas_upgrade_notification_learn_more_action" msgid="7011130656195423947">"Научете повече"</string>
diff --git a/core/res/res/values-mcc334-mnc020-af/strings.xml b/core/res/res/values-mcc334-mnc020-af/strings.xml
new file mode 100644
index 0000000..c9177f3
--- /dev/null
+++ b/core/res/res/values-mcc334-mnc020-af/strings.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+** Copyright 2020, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="config_pdp_reject_dialog_title" msgid="41208110171880430"></string>
+ <string name="config_pdp_reject_user_authentication_failed" msgid="4683454131283459978">"STAWINGMISLUKKING -29-"</string>
+ <string name="config_pdp_reject_service_not_subscribed" msgid="9021140729932308119">"NIE INGETEKEN OP DIENS NIE -33-"</string>
+ <string name="config_pdp_reject_multi_conn_to_same_pdn_not_allowed" msgid="3838388706348367865">"Veelvuldige PDN-verbindings vir \'n bepaalde APN word nie toegelaat nie -55-"</string>
+</resources>
diff --git a/core/res/res/values-mcc334-mnc020-am/strings.xml b/core/res/res/values-mcc334-mnc020-am/strings.xml
new file mode 100644
index 0000000..609fcf9
--- /dev/null
+++ b/core/res/res/values-mcc334-mnc020-am/strings.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+** Copyright 2020, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="config_pdp_reject_dialog_title" msgid="41208110171880430"></string>
+ <string name="config_pdp_reject_user_authentication_failed" msgid="4683454131283459978">"የማረጋገጫ አለመሳካት -29-"</string>
+ <string name="config_pdp_reject_service_not_subscribed" msgid="9021140729932308119">"ለአገልግሎት ደንበኝነት አልተመዘገበም -33-"</string>
+ <string name="config_pdp_reject_multi_conn_to_same_pdn_not_allowed" msgid="3838388706348367865">"ለተሰጠው ኤፒኤን ብዙ የፒዲኤን ግንኙነቶች አይፈቀዱም -55-"</string>
+</resources>
diff --git a/core/res/res/values-mcc334-mnc020-ar/strings.xml b/core/res/res/values-mcc334-mnc020-ar/strings.xml
new file mode 100644
index 0000000..ac7dec0
--- /dev/null
+++ b/core/res/res/values-mcc334-mnc020-ar/strings.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+** Copyright 2020, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="config_pdp_reject_dialog_title" msgid="41208110171880430"></string>
+ <string name="config_pdp_reject_user_authentication_failed" msgid="4683454131283459978">"تعذّرت المصادقة -29-"</string>
+ <string name="config_pdp_reject_service_not_subscribed" msgid="9021140729932308119">"غير مشترك في الخدمة -33-"</string>
+ <string name="config_pdp_reject_multi_conn_to_same_pdn_not_allowed" msgid="3838388706348367865">"غير مسموح باتصالات PDN متعددة لاسم نقطة وصول محددة (APN) -55-"</string>
+</resources>
diff --git a/core/res/res/values-mcc334-mnc020-as/strings.xml b/core/res/res/values-mcc334-mnc020-as/strings.xml
new file mode 100644
index 0000000..43f5fb1
--- /dev/null
+++ b/core/res/res/values-mcc334-mnc020-as/strings.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+** Copyright 2020, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="config_pdp_reject_dialog_title" msgid="41208110171880430"></string>
+ <string name="config_pdp_reject_user_authentication_failed" msgid="4683454131283459978">"বিশ্বাসযোগ্যতা প্ৰমাণীকৰণ বিফল হৈছে -29-"</string>
+ <string name="config_pdp_reject_service_not_subscribed" msgid="9021140729932308119">"সেৱাটো ছাবস্ক্ৰাইব কৰা নাই -33-"</string>
+ <string name="config_pdp_reject_multi_conn_to_same_pdn_not_allowed" msgid="3838388706348367865">"এটা APNৰ বাবে একাধিক PDN সংযোগৰ অনুমতি নাই -55-"</string>
+</resources>
diff --git a/core/res/res/values-mcc334-mnc020-az/strings.xml b/core/res/res/values-mcc334-mnc020-az/strings.xml
new file mode 100644
index 0000000..83b210d
--- /dev/null
+++ b/core/res/res/values-mcc334-mnc020-az/strings.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+** Copyright 2020, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="config_pdp_reject_dialog_title" msgid="41208110171880430"></string>
+ <string name="config_pdp_reject_user_authentication_failed" msgid="4683454131283459978">"DOĞRULAMA XƏTASI -29-"</string>
+ <string name="config_pdp_reject_service_not_subscribed" msgid="9021140729932308119">"XİDMƏTƏ ABUNƏ OLUNMAYIB -33-"</string>
+ <string name="config_pdp_reject_multi_conn_to_same_pdn_not_allowed" msgid="3838388706348367865">"Verilmiş APN üçün birdən çox PDN bağlantısına icazə verilmir -55-"</string>
+</resources>
diff --git a/core/res/res/values-mcc334-mnc020-b+sr+Latn/strings.xml b/core/res/res/values-mcc334-mnc020-b+sr+Latn/strings.xml
new file mode 100644
index 0000000..b27e485
--- /dev/null
+++ b/core/res/res/values-mcc334-mnc020-b+sr+Latn/strings.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+** Copyright 2020, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="config_pdp_reject_dialog_title" msgid="41208110171880430"></string>
+ <string name="config_pdp_reject_user_authentication_failed" msgid="4683454131283459978">"POTVRDA IDENTITETA NIJE USPELA -29-"</string>
+ <string name="config_pdp_reject_service_not_subscribed" msgid="9021140729932308119">"NISTE PRETPLAĆENI NA USLUGU -33-"</string>
+ <string name="config_pdp_reject_multi_conn_to_same_pdn_not_allowed" msgid="3838388706348367865">"Nije dozvoljeno više PDN veza za određeni APN -55-"</string>
+</resources>
diff --git a/core/res/res/values-mcc334-mnc020-be/strings.xml b/core/res/res/values-mcc334-mnc020-be/strings.xml
new file mode 100644
index 0000000..3ee9e8f
--- /dev/null
+++ b/core/res/res/values-mcc334-mnc020-be/strings.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+** Copyright 2020, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="config_pdp_reject_dialog_title" msgid="41208110171880430"></string>
+ <string name="config_pdp_reject_user_authentication_failed" msgid="4683454131283459978">"ЗБОЙ АЎТЭНТЫФІКАЦЫІ -29-"</string>
+ <string name="config_pdp_reject_service_not_subscribed" msgid="9021140729932308119">"У ВАС НЯМА ПАДПІСКІ НА СЭРВІС -33-"</string>
+ <string name="config_pdp_reject_multi_conn_to_same_pdn_not_allowed" msgid="3838388706348367865">"Да гэтага APN нельга падключаць некалькі сетак PDN -55-"</string>
+</resources>
diff --git a/core/res/res/values-mcc334-mnc020-bg/strings.xml b/core/res/res/values-mcc334-mnc020-bg/strings.xml
new file mode 100644
index 0000000..257cf58
--- /dev/null
+++ b/core/res/res/values-mcc334-mnc020-bg/strings.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+** Copyright 2020, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="config_pdp_reject_dialog_title" msgid="41208110171880430"></string>
+ <string name="config_pdp_reject_user_authentication_failed" msgid="4683454131283459978">"НЕУСПЕШНО УДОСТОВЕРЯВАНЕ -29-"</string>
+ <string name="config_pdp_reject_service_not_subscribed" msgid="9021140729932308119">"НЯМА АБОНАМЕНТ ЗА УСЛУГАТА -33-"</string>
+ <string name="config_pdp_reject_multi_conn_to_same_pdn_not_allowed" msgid="3838388706348367865">"Не е разрешено да има повече от една PDN връзка за дадено име на точката за достъп (APN) -55-"</string>
+</resources>
diff --git a/core/res/res/values-mcc334-mnc020-bn/strings.xml b/core/res/res/values-mcc334-mnc020-bn/strings.xml
new file mode 100644
index 0000000..f58aea7
--- /dev/null
+++ b/core/res/res/values-mcc334-mnc020-bn/strings.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+** Copyright 2020, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="config_pdp_reject_dialog_title" msgid="41208110171880430"></string>
+ <string name="config_pdp_reject_user_authentication_failed" msgid="4683454131283459978">"যাচাই করা যায়নি -29-"</string>
+ <string name="config_pdp_reject_service_not_subscribed" msgid="9021140729932308119">"পরিষেবা সাবস্ক্রাইব করা হয়নি -33-"</string>
+ <string name="config_pdp_reject_multi_conn_to_same_pdn_not_allowed" msgid="3838388706348367865">"নির্দিষ্ট কোনও APN-এর জন্য একাধিক PDN কানেকশন অনুমোদিত নয় -55-"</string>
+</resources>
diff --git a/core/res/res/values-mcc334-mnc020-bs/strings.xml b/core/res/res/values-mcc334-mnc020-bs/strings.xml
new file mode 100644
index 0000000..4bf1b8f
--- /dev/null
+++ b/core/res/res/values-mcc334-mnc020-bs/strings.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+** Copyright 2020, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="config_pdp_reject_dialog_title" msgid="41208110171880430"></string>
+ <string name="config_pdp_reject_user_authentication_failed" msgid="4683454131283459978">"AUTENTIFIKACIJA NIJE USPJELA -29-"</string>
+ <string name="config_pdp_reject_service_not_subscribed" msgid="9021140729932308119">"NISTE PRETPLAĆENI NA USLUGU -33-"</string>
+ <string name="config_pdp_reject_multi_conn_to_same_pdn_not_allowed" msgid="3838388706348367865">"Više PDN veza za određeni APN nije dozvoljeno -55-"</string>
+</resources>
diff --git a/core/res/res/values-mcc334-mnc020-ca/strings.xml b/core/res/res/values-mcc334-mnc020-ca/strings.xml
new file mode 100644
index 0000000..0ad1f1c
--- /dev/null
+++ b/core/res/res/values-mcc334-mnc020-ca/strings.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+** Copyright 2020, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="config_pdp_reject_dialog_title" msgid="41208110171880430"></string>
+ <string name="config_pdp_reject_user_authentication_failed" msgid="4683454131283459978">"ERROR D\'AUTENTICACIÓ -29-"</string>
+ <string name="config_pdp_reject_service_not_subscribed" msgid="9021140729932308119">"NO ESTÀS SUBSCRIT AL SERVEI -33-"</string>
+ <string name="config_pdp_reject_multi_conn_to_same_pdn_not_allowed" msgid="3838388706348367865">"No es permeten diverses connexions PDN per a un APN determinat -55-"</string>
+</resources>
diff --git a/core/res/res/values-mcc334-mnc020-cs/strings.xml b/core/res/res/values-mcc334-mnc020-cs/strings.xml
new file mode 100644
index 0000000..cfa1a44
--- /dev/null
+++ b/core/res/res/values-mcc334-mnc020-cs/strings.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+** Copyright 2020, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="config_pdp_reject_dialog_title" msgid="41208110171880430"></string>
+ <string name="config_pdp_reject_user_authentication_failed" msgid="4683454131283459978">"SELHÁNÍ OVĚŘENÍ -29-"</string>
+ <string name="config_pdp_reject_service_not_subscribed" msgid="9021140729932308119">"NEJSTE PŘIHLÁŠENI K ODBĚRU SLUŽBY -33-"</string>
+ <string name="config_pdp_reject_multi_conn_to_same_pdn_not_allowed" msgid="3838388706348367865">"U jednoho APN není povoleno více než jedno připojení PDN -55-"</string>
+</resources>
diff --git a/core/res/res/values-mcc334-mnc020-da/strings.xml b/core/res/res/values-mcc334-mnc020-da/strings.xml
new file mode 100644
index 0000000..7d349cf
--- /dev/null
+++ b/core/res/res/values-mcc334-mnc020-da/strings.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+** Copyright 2020, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="config_pdp_reject_dialog_title" msgid="41208110171880430"></string>
+ <string name="config_pdp_reject_user_authentication_failed" msgid="4683454131283459978">"GODKENDELSESFEJL -29-"</string>
+ <string name="config_pdp_reject_service_not_subscribed" msgid="9021140729932308119">"IKKE TILMELDT TJENESTEN -33-"</string>
+ <string name="config_pdp_reject_multi_conn_to_same_pdn_not_allowed" msgid="3838388706348367865">"Flere PDN-forbindelser for en given APN er ikke tilladt -55-"</string>
+</resources>
diff --git a/core/res/res/values-mcc334-mnc020-de/strings.xml b/core/res/res/values-mcc334-mnc020-de/strings.xml
new file mode 100644
index 0000000..084bf46
--- /dev/null
+++ b/core/res/res/values-mcc334-mnc020-de/strings.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+** Copyright 2020, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="config_pdp_reject_dialog_title" msgid="41208110171880430"></string>
+ <string name="config_pdp_reject_user_authentication_failed" msgid="4683454131283459978">"AUTHENTIFIZIERUNGSFEHLER -29-"</string>
+ <string name="config_pdp_reject_service_not_subscribed" msgid="9021140729932308119">"DIENST NICHT ABONNIERT -33-"</string>
+ <string name="config_pdp_reject_multi_conn_to_same_pdn_not_allowed" msgid="3838388706348367865">"Mehrere PDN-Verbindungen für einen bestimmten APN sind nicht zulässig -55-"</string>
+</resources>
diff --git a/core/res/res/values-mcc334-mnc020-el/strings.xml b/core/res/res/values-mcc334-mnc020-el/strings.xml
new file mode 100644
index 0000000..27c5f95
--- /dev/null
+++ b/core/res/res/values-mcc334-mnc020-el/strings.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+** Copyright 2020, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="config_pdp_reject_dialog_title" msgid="41208110171880430"></string>
+ <string name="config_pdp_reject_user_authentication_failed" msgid="4683454131283459978">"ΑΠΟΤΥΧΙΑ ΕΛΕΓΧΟΥ ΤΑΥΤΟΤΗΤΑΣ -29-"</string>
+ <string name="config_pdp_reject_service_not_subscribed" msgid="9021140729932308119">"ΔΕΝ ΕΓΙΝΕ ΕΓΓΡΑΦΗ ΣΤΗΝ ΥΠΗΡΕΣΙΑ -33-"</string>
+ <string name="config_pdp_reject_multi_conn_to_same_pdn_not_allowed" msgid="3838388706348367865">"Δεν επιτρέπονται πολλές συνδέσεις PDN για ένα δεδομένο APN -55-"</string>
+</resources>
diff --git a/core/res/res/values-mcc334-mnc020-en-rAU/strings.xml b/core/res/res/values-mcc334-mnc020-en-rAU/strings.xml
new file mode 100644
index 0000000..6dce345
--- /dev/null
+++ b/core/res/res/values-mcc334-mnc020-en-rAU/strings.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+** Copyright 2020, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="config_pdp_reject_dialog_title" msgid="41208110171880430"></string>
+ <string name="config_pdp_reject_user_authentication_failed" msgid="4683454131283459978">"AUTHENTICATION FAILURE -29-"</string>
+ <string name="config_pdp_reject_service_not_subscribed" msgid="9021140729932308119">"NOT SUBSCRIBED TO SERVICE -33-"</string>
+ <string name="config_pdp_reject_multi_conn_to_same_pdn_not_allowed" msgid="3838388706348367865">"Multiple PDN connections for a given APN not allowed -55-"</string>
+</resources>
diff --git a/core/res/res/values-mcc334-mnc020-en-rCA/strings.xml b/core/res/res/values-mcc334-mnc020-en-rCA/strings.xml
new file mode 100644
index 0000000..6dce345
--- /dev/null
+++ b/core/res/res/values-mcc334-mnc020-en-rCA/strings.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+** Copyright 2020, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="config_pdp_reject_dialog_title" msgid="41208110171880430"></string>
+ <string name="config_pdp_reject_user_authentication_failed" msgid="4683454131283459978">"AUTHENTICATION FAILURE -29-"</string>
+ <string name="config_pdp_reject_service_not_subscribed" msgid="9021140729932308119">"NOT SUBSCRIBED TO SERVICE -33-"</string>
+ <string name="config_pdp_reject_multi_conn_to_same_pdn_not_allowed" msgid="3838388706348367865">"Multiple PDN connections for a given APN not allowed -55-"</string>
+</resources>
diff --git a/core/res/res/values-mcc334-mnc020-en-rGB/strings.xml b/core/res/res/values-mcc334-mnc020-en-rGB/strings.xml
new file mode 100644
index 0000000..6dce345
--- /dev/null
+++ b/core/res/res/values-mcc334-mnc020-en-rGB/strings.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+** Copyright 2020, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="config_pdp_reject_dialog_title" msgid="41208110171880430"></string>
+ <string name="config_pdp_reject_user_authentication_failed" msgid="4683454131283459978">"AUTHENTICATION FAILURE -29-"</string>
+ <string name="config_pdp_reject_service_not_subscribed" msgid="9021140729932308119">"NOT SUBSCRIBED TO SERVICE -33-"</string>
+ <string name="config_pdp_reject_multi_conn_to_same_pdn_not_allowed" msgid="3838388706348367865">"Multiple PDN connections for a given APN not allowed -55-"</string>
+</resources>
diff --git a/core/res/res/values-mcc334-mnc020-en-rIN/strings.xml b/core/res/res/values-mcc334-mnc020-en-rIN/strings.xml
new file mode 100644
index 0000000..6dce345
--- /dev/null
+++ b/core/res/res/values-mcc334-mnc020-en-rIN/strings.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+** Copyright 2020, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="config_pdp_reject_dialog_title" msgid="41208110171880430"></string>
+ <string name="config_pdp_reject_user_authentication_failed" msgid="4683454131283459978">"AUTHENTICATION FAILURE -29-"</string>
+ <string name="config_pdp_reject_service_not_subscribed" msgid="9021140729932308119">"NOT SUBSCRIBED TO SERVICE -33-"</string>
+ <string name="config_pdp_reject_multi_conn_to_same_pdn_not_allowed" msgid="3838388706348367865">"Multiple PDN connections for a given APN not allowed -55-"</string>
+</resources>
diff --git a/core/res/res/values-mcc334-mnc020-en-rXC/strings.xml b/core/res/res/values-mcc334-mnc020-en-rXC/strings.xml
new file mode 100644
index 0000000..ec8f4ab
--- /dev/null
+++ b/core/res/res/values-mcc334-mnc020-en-rXC/strings.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+** Copyright 2020, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="config_pdp_reject_dialog_title" msgid="41208110171880430"></string>
+ <string name="config_pdp_reject_user_authentication_failed" msgid="4683454131283459978">"AUTHENTICATION FAILURE -29-"</string>
+ <string name="config_pdp_reject_service_not_subscribed" msgid="9021140729932308119">"NOT SUBSCRIBED TO SERVICE -33-"</string>
+ <string name="config_pdp_reject_multi_conn_to_same_pdn_not_allowed" msgid="3838388706348367865">"Multiple PDN connections for a given APN not allowed -55-"</string>
+</resources>
diff --git a/core/res/res/values-mcc334-mnc020-es-rUS/strings.xml b/core/res/res/values-mcc334-mnc020-es-rUS/strings.xml
new file mode 100644
index 0000000..8cb181d
--- /dev/null
+++ b/core/res/res/values-mcc334-mnc020-es-rUS/strings.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+** Copyright 2020, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="config_pdp_reject_dialog_title" msgid="41208110171880430"></string>
+ <string name="config_pdp_reject_user_authentication_failed" msgid="4683454131283459978">"FALLO EN LA AUTENTICACIÓN -29-"</string>
+ <string name="config_pdp_reject_service_not_subscribed" msgid="9021140729932308119">"NO TE SUSCRIBISTE AL SERVICIO -33-"</string>
+ <string name="config_pdp_reject_multi_conn_to_same_pdn_not_allowed" msgid="3838388706348367865">"No se permiten varias conexiones PDN para un APN determinado -55-"</string>
+</resources>
diff --git a/core/res/res/values-mcc334-mnc020-es/strings.xml b/core/res/res/values-mcc334-mnc020-es/strings.xml
new file mode 100644
index 0000000..0744699
--- /dev/null
+++ b/core/res/res/values-mcc334-mnc020-es/strings.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+** Copyright 2020, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="config_pdp_reject_dialog_title" msgid="41208110171880430"></string>
+ <string name="config_pdp_reject_user_authentication_failed" msgid="4683454131283459978">"ERROR DE AUTENTICACIÓN -29-"</string>
+ <string name="config_pdp_reject_service_not_subscribed" msgid="9021140729932308119">"NO SUSCRITO AL SERVICIO -33-"</string>
+ <string name="config_pdp_reject_multi_conn_to_same_pdn_not_allowed" msgid="3838388706348367865">"No se permiten varias conexiones PDN de un APN concreto -55-"</string>
+</resources>
diff --git a/core/res/res/values-mcc334-mnc020-et/strings.xml b/core/res/res/values-mcc334-mnc020-et/strings.xml
new file mode 100644
index 0000000..a7e9e16
--- /dev/null
+++ b/core/res/res/values-mcc334-mnc020-et/strings.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+** Copyright 2020, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="config_pdp_reject_dialog_title" msgid="41208110171880430"></string>
+ <string name="config_pdp_reject_user_authentication_failed" msgid="4683454131283459978">"AUTENTIMINE EBAÕNNESTUS -29-"</string>
+ <string name="config_pdp_reject_service_not_subscribed" msgid="9021140729932308119">"TEENUST POLE TELLITUD -33-"</string>
+ <string name="config_pdp_reject_multi_conn_to_same_pdn_not_allowed" msgid="3838388706348367865">"Antud APN-i puhul pole mitu PDN-ühendust lubatud -55-"</string>
+</resources>
diff --git a/core/res/res/values-mcc334-mnc020-eu/strings.xml b/core/res/res/values-mcc334-mnc020-eu/strings.xml
new file mode 100644
index 0000000..2d05b3f
--- /dev/null
+++ b/core/res/res/values-mcc334-mnc020-eu/strings.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+** Copyright 2020, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="config_pdp_reject_dialog_title" msgid="41208110171880430"></string>
+ <string name="config_pdp_reject_user_authentication_failed" msgid="4683454131283459978">"EZIN IZAN DA AUTENTIFIKATU -29-"</string>
+ <string name="config_pdp_reject_service_not_subscribed" msgid="9021140729932308119">"EZ DAGO HARPIDETUTA ZERBITZURA -33-"</string>
+ <string name="config_pdp_reject_multi_conn_to_same_pdn_not_allowed" msgid="3838388706348367865">"Ezin da konektatu APN jakin bat PDN batera baino gehiagotara -55-"</string>
+</resources>
diff --git a/core/res/res/values-mcc334-mnc020-fa/strings.xml b/core/res/res/values-mcc334-mnc020-fa/strings.xml
new file mode 100644
index 0000000..8bf364f
--- /dev/null
+++ b/core/res/res/values-mcc334-mnc020-fa/strings.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+** Copyright 2020, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="config_pdp_reject_dialog_title" msgid="41208110171880430"></string>
+ <string name="config_pdp_reject_user_authentication_failed" msgid="4683454131283459978">"اصالتسنجی انجام نشد -۲۹-"</string>
+ <string name="config_pdp_reject_service_not_subscribed" msgid="9021140729932308119">"مشترک سرویس نیستید -۳۳-"</string>
+ <string name="config_pdp_reject_multi_conn_to_same_pdn_not_allowed" msgid="3838388706348367865">"تعدادی از اتصالهای PDN مربوط به APN تعیینشده مجاز نیست -۵۵-"</string>
+</resources>
diff --git a/core/res/res/values-mcc334-mnc020-fi/strings.xml b/core/res/res/values-mcc334-mnc020-fi/strings.xml
new file mode 100644
index 0000000..f8d01ba
--- /dev/null
+++ b/core/res/res/values-mcc334-mnc020-fi/strings.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+** Copyright 2020, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="config_pdp_reject_dialog_title" msgid="41208110171880430"></string>
+ <string name="config_pdp_reject_user_authentication_failed" msgid="4683454131283459978">"TODENNUS EPÄONNISTUI -29-"</string>
+ <string name="config_pdp_reject_service_not_subscribed" msgid="9021140729932308119">"PALVELUA EI TILATTU -33-"</string>
+ <string name="config_pdp_reject_multi_conn_to_same_pdn_not_allowed" msgid="3838388706348367865">"Useat yhteydet eivät ole sallittuja yhdelle APN:lle -55-"</string>
+</resources>
diff --git a/core/res/res/values-mcc334-mnc020-fr-rCA/strings.xml b/core/res/res/values-mcc334-mnc020-fr-rCA/strings.xml
new file mode 100644
index 0000000..19794da
--- /dev/null
+++ b/core/res/res/values-mcc334-mnc020-fr-rCA/strings.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+** Copyright 2020, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="config_pdp_reject_dialog_title" msgid="41208110171880430"></string>
+ <string name="config_pdp_reject_user_authentication_failed" msgid="4683454131283459978">"ÉCHEC DE L\'AUTHENTIFICATION -29-"</string>
+ <string name="config_pdp_reject_service_not_subscribed" msgid="9021140729932308119">"NON ABONNÉ AU SERVICE -33-"</string>
+ <string name="config_pdp_reject_multi_conn_to_same_pdn_not_allowed" msgid="3838388706348367865">"Connexions PDN multiples interdites pour un APN donné -55-"</string>
+</resources>
diff --git a/core/res/res/values-mcc334-mnc020-fr/strings.xml b/core/res/res/values-mcc334-mnc020-fr/strings.xml
new file mode 100644
index 0000000..6eaa7cd
--- /dev/null
+++ b/core/res/res/values-mcc334-mnc020-fr/strings.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+** Copyright 2020, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="config_pdp_reject_dialog_title" msgid="41208110171880430"></string>
+ <string name="config_pdp_reject_user_authentication_failed" msgid="4683454131283459978">"ÉCHEC DE L\'AUTHENTIFICATION -29-"</string>
+ <string name="config_pdp_reject_service_not_subscribed" msgid="9021140729932308119">"NON INSCRIT AU SERVICE -33-"</string>
+ <string name="config_pdp_reject_multi_conn_to_same_pdn_not_allowed" msgid="3838388706348367865">"Vous n\'êtes pas autorisé à avoir plusieurs connexions PDN pour un APN donné -55-"</string>
+</resources>
diff --git a/core/res/res/values-mcc334-mnc020-gl/strings.xml b/core/res/res/values-mcc334-mnc020-gl/strings.xml
new file mode 100644
index 0000000..49baed9
--- /dev/null
+++ b/core/res/res/values-mcc334-mnc020-gl/strings.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+** Copyright 2020, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="config_pdp_reject_dialog_title" msgid="41208110171880430"></string>
+ <string name="config_pdp_reject_user_authentication_failed" msgid="4683454131283459978">"ERRO DE AUTENTICACIÓN -29-"</string>
+ <string name="config_pdp_reject_service_not_subscribed" msgid="9021140729932308119">"SEN SUBSCRICIÓN AO SERVIZO -33-"</string>
+ <string name="config_pdp_reject_multi_conn_to_same_pdn_not_allowed" msgid="3838388706348367865">"Non está permitido establecer varias conexións de tipo PDN para un determinado APN -55-"</string>
+</resources>
diff --git a/core/res/res/values-mcc334-mnc020-gu/strings.xml b/core/res/res/values-mcc334-mnc020-gu/strings.xml
new file mode 100644
index 0000000..5faef6f
--- /dev/null
+++ b/core/res/res/values-mcc334-mnc020-gu/strings.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+** Copyright 2020, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="config_pdp_reject_dialog_title" msgid="41208110171880430"></string>
+ <string name="config_pdp_reject_user_authentication_failed" msgid="4683454131283459978">"પ્રમાણીકરણ નિષ્ફળ થયું -29-"</string>
+ <string name="config_pdp_reject_service_not_subscribed" msgid="9021140729932308119">"સેવા સબ્સ્ક્રાઇબ કરી નથી -33-"</string>
+ <string name="config_pdp_reject_multi_conn_to_same_pdn_not_allowed" msgid="3838388706348367865">"આપેલા APN માટે એક કરતાં વધારે PDN કનેક્શનની મંજૂરી નથી -55-"</string>
+</resources>
diff --git a/core/res/res/values-mcc334-mnc020-hi/strings.xml b/core/res/res/values-mcc334-mnc020-hi/strings.xml
new file mode 100644
index 0000000..8a11053
--- /dev/null
+++ b/core/res/res/values-mcc334-mnc020-hi/strings.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+** Copyright 2020, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="config_pdp_reject_dialog_title" msgid="41208110171880430"></string>
+ <string name="config_pdp_reject_user_authentication_failed" msgid="4683454131283459978">"पुष्टि नहीं की जा सकी -29-"</string>
+ <string name="config_pdp_reject_service_not_subscribed" msgid="9021140729932308119">"सेवा की सदस्यता नहीं ली गई है -33-"</string>
+ <string name="config_pdp_reject_multi_conn_to_same_pdn_not_allowed" msgid="3838388706348367865">"किसी भी एपीएन के लिए एक से ज़्यादा पीडीएन कनेक्शन की अनुमति नहीं है -55-"</string>
+</resources>
diff --git a/core/res/res/values-mcc334-mnc020-hr/strings.xml b/core/res/res/values-mcc334-mnc020-hr/strings.xml
new file mode 100644
index 0000000..ff9b427
--- /dev/null
+++ b/core/res/res/values-mcc334-mnc020-hr/strings.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+** Copyright 2020, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="config_pdp_reject_dialog_title" msgid="41208110171880430"></string>
+ <string name="config_pdp_reject_user_authentication_failed" msgid="4683454131283459978">"AUTENTIFIKACIJA NIJE USPJELA -29-"</string>
+ <string name="config_pdp_reject_service_not_subscribed" msgid="9021140729932308119">"NISTE PRETPLAĆENI NA USLUGU -33-"</string>
+ <string name="config_pdp_reject_multi_conn_to_same_pdn_not_allowed" msgid="3838388706348367865">"Više PDN veza za određeni APN nije dopušteno -55-"</string>
+</resources>
diff --git a/core/res/res/values-mcc334-mnc020-hu/strings.xml b/core/res/res/values-mcc334-mnc020-hu/strings.xml
new file mode 100644
index 0000000..42776c1
--- /dev/null
+++ b/core/res/res/values-mcc334-mnc020-hu/strings.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+** Copyright 2020, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="config_pdp_reject_dialog_title" msgid="41208110171880430"></string>
+ <string name="config_pdp_reject_user_authentication_failed" msgid="4683454131283459978">"SIKERTELEN HITELESÍTÉS -29-"</string>
+ <string name="config_pdp_reject_service_not_subscribed" msgid="9021140729932308119">"NINCS ELŐFIZETÉSE A SZOLGÁLTATÁSRA -33-"</string>
+ <string name="config_pdp_reject_multi_conn_to_same_pdn_not_allowed" msgid="3838388706348367865">"Nem engedélyezett több PDN-kapcsolat egy adott APN-nél -55-"</string>
+</resources>
diff --git a/core/res/res/values-mcc334-mnc020-hy/strings.xml b/core/res/res/values-mcc334-mnc020-hy/strings.xml
new file mode 100644
index 0000000..6633bb3
--- /dev/null
+++ b/core/res/res/values-mcc334-mnc020-hy/strings.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+** Copyright 2020, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="config_pdp_reject_dialog_title" msgid="41208110171880430"></string>
+ <string name="config_pdp_reject_user_authentication_failed" msgid="4683454131283459978">"ՆՈՒՅՆԱԿԱՆԱՑՄԱՆ ՍԽԱԼ -29-"</string>
+ <string name="config_pdp_reject_service_not_subscribed" msgid="9021140729932308119">"ԴՈՒՔ ԲԱԺԱՆՈՐԴԱԳՐՎԱԾ ՉԵՔ ԱՅՍ ԾԱՌԱՅՈՒԹՅԱՆԸ -33-"</string>
+ <string name="config_pdp_reject_multi_conn_to_same_pdn_not_allowed" msgid="3838388706348367865">"Այս APN-ով չի թույլատրվում միանալ տվյալների փոխանցման մեկից ավել բաց ցանցի -55-"</string>
+</resources>
diff --git a/core/res/res/values-mcc334-mnc020-in/strings.xml b/core/res/res/values-mcc334-mnc020-in/strings.xml
new file mode 100644
index 0000000..79a9dfd
--- /dev/null
+++ b/core/res/res/values-mcc334-mnc020-in/strings.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+** Copyright 2020, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="config_pdp_reject_dialog_title" msgid="41208110171880430"></string>
+ <string name="config_pdp_reject_user_authentication_failed" msgid="4683454131283459978">"KEGAGALAN AUTENTIKASI -29-"</string>
+ <string name="config_pdp_reject_service_not_subscribed" msgid="9021140729932308119">"TIDAK BERLANGGANAN KE LAYANAN -33-"</string>
+ <string name="config_pdp_reject_multi_conn_to_same_pdn_not_allowed" msgid="3838388706348367865">"Beberapa koneksi PDN untuk APN tertentu tidak diizinkan -55-"</string>
+</resources>
diff --git a/core/res/res/values-mcc334-mnc020-is/strings.xml b/core/res/res/values-mcc334-mnc020-is/strings.xml
new file mode 100644
index 0000000..c9049e8
--- /dev/null
+++ b/core/res/res/values-mcc334-mnc020-is/strings.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+** Copyright 2020, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="config_pdp_reject_dialog_title" msgid="41208110171880430"></string>
+ <string name="config_pdp_reject_user_authentication_failed" msgid="4683454131283459978">"AUÐKENNING MISTÓKST -29-"</string>
+ <string name="config_pdp_reject_service_not_subscribed" msgid="9021140729932308119">"EKKI ÁSKRIFANDI AÐ ÞJÓNUSTU -33-"</string>
+ <string name="config_pdp_reject_multi_conn_to_same_pdn_not_allowed" msgid="3838388706348367865">"Margar PDN-tengingar fyrir tiltekinn aðgangsstað eru ekki leyfðar -55-"</string>
+</resources>
diff --git a/core/res/res/values-mcc334-mnc020-it/strings.xml b/core/res/res/values-mcc334-mnc020-it/strings.xml
new file mode 100644
index 0000000..add9b0d
--- /dev/null
+++ b/core/res/res/values-mcc334-mnc020-it/strings.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+** Copyright 2020, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="config_pdp_reject_dialog_title" msgid="41208110171880430"></string>
+ <string name="config_pdp_reject_user_authentication_failed" msgid="4683454131283459978">"ERRORE DI AUTENTICAZIONE -29-"</string>
+ <string name="config_pdp_reject_service_not_subscribed" msgid="9021140729932308119">"SOTTOSCRIZIONE AL SERVIZIO NON EFFETTUATA -33-"</string>
+ <string name="config_pdp_reject_multi_conn_to_same_pdn_not_allowed" msgid="3838388706348367865">"Più connessioni PDN per un dato APN non consentite -55-"</string>
+</resources>
diff --git a/core/res/res/values-mcc334-mnc020-iw/strings.xml b/core/res/res/values-mcc334-mnc020-iw/strings.xml
new file mode 100644
index 0000000..b8b1ab5
--- /dev/null
+++ b/core/res/res/values-mcc334-mnc020-iw/strings.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+** Copyright 2020, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="config_pdp_reject_dialog_title" msgid="41208110171880430"></string>
+ <string name="config_pdp_reject_user_authentication_failed" msgid="4683454131283459978">"האימות נכשל -29-"</string>
+ <string name="config_pdp_reject_service_not_subscribed" msgid="9021140729932308119">"לא נרשמת כמנוי לשירות -33-"</string>
+ <string name="config_pdp_reject_multi_conn_to_same_pdn_not_allowed" msgid="3838388706348367865">"אין הרשאה למספר חיבורי PDN ל-APN מסוים -55-"</string>
+</resources>
diff --git a/core/res/res/values-mcc334-mnc020-ja/strings.xml b/core/res/res/values-mcc334-mnc020-ja/strings.xml
new file mode 100644
index 0000000..ec543fa
--- /dev/null
+++ b/core/res/res/values-mcc334-mnc020-ja/strings.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+** Copyright 2020, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="config_pdp_reject_dialog_title" msgid="41208110171880430"></string>
+ <string name="config_pdp_reject_user_authentication_failed" msgid="4683454131283459978">"認証エラーが発生しました -29-"</string>
+ <string name="config_pdp_reject_service_not_subscribed" msgid="9021140729932308119">"サービスに登録していません -33-"</string>
+ <string name="config_pdp_reject_multi_conn_to_same_pdn_not_allowed" msgid="3838388706348367865">"特定の APN に対する複数の PDN 接続は許可されていません -55-"</string>
+</resources>
diff --git a/core/res/res/values-mcc334-mnc020-ka/strings.xml b/core/res/res/values-mcc334-mnc020-ka/strings.xml
new file mode 100644
index 0000000..e55057b
--- /dev/null
+++ b/core/res/res/values-mcc334-mnc020-ka/strings.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+** Copyright 2020, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="config_pdp_reject_dialog_title" msgid="41208110171880430"></string>
+ <string name="config_pdp_reject_user_authentication_failed" msgid="4683454131283459978">"ავტორიზაციის შეცდომა -29-"</string>
+ <string name="config_pdp_reject_service_not_subscribed" msgid="9021140729932308119">"გამოწერილი არ გაქვთ სერვისი -33-"</string>
+ <string name="config_pdp_reject_multi_conn_to_same_pdn_not_allowed" msgid="3838388706348367865">"მრავალი PDN კავშირი მოცემული APN-ისთვის დაუშვებელია -55-"</string>
+</resources>
diff --git a/core/res/res/values-mcc334-mnc020-kk/strings.xml b/core/res/res/values-mcc334-mnc020-kk/strings.xml
new file mode 100644
index 0000000..f19d2c7
--- /dev/null
+++ b/core/res/res/values-mcc334-mnc020-kk/strings.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+** Copyright 2020, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="config_pdp_reject_dialog_title" msgid="41208110171880430"></string>
+ <string name="config_pdp_reject_user_authentication_failed" msgid="4683454131283459978">"АУТЕНТИФИКАЦИЯЛАУ ҚАТЕСІ -29-"</string>
+ <string name="config_pdp_reject_service_not_subscribed" msgid="9021140729932308119">"ҚЫЗМЕТКЕ ЖАЗЫЛМАҒАН -33-"</string>
+ <string name="config_pdp_reject_multi_conn_to_same_pdn_not_allowed" msgid="3838388706348367865">"Бұл APN үшін бірнеше PDN-ге қосылуға рұқсат берілмеген -55-"</string>
+</resources>
diff --git a/core/res/res/values-mcc334-mnc020-km/strings.xml b/core/res/res/values-mcc334-mnc020-km/strings.xml
new file mode 100644
index 0000000..08e1a0d
--- /dev/null
+++ b/core/res/res/values-mcc334-mnc020-km/strings.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+** Copyright 2020, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="config_pdp_reject_dialog_title" msgid="41208110171880430"></string>
+ <string name="config_pdp_reject_user_authentication_failed" msgid="4683454131283459978">"មិនអាចផ្ទៀងផ្ទាត់បានទេ -29-"</string>
+ <string name="config_pdp_reject_service_not_subscribed" msgid="9021140729932308119">"មិនបានជាវសេវាកម្មទេ -33-"</string>
+ <string name="config_pdp_reject_multi_conn_to_same_pdn_not_allowed" msgid="3838388706348367865">"ការតភ្ជាប់ PDN ច្រើនសម្រាប់ APN ដែលបានផ្ដល់មិនត្រូវបានអនុញ្ញាតទេ -55-"</string>
+</resources>
diff --git a/core/res/res/values-mcc334-mnc020-kn/strings.xml b/core/res/res/values-mcc334-mnc020-kn/strings.xml
new file mode 100644
index 0000000..e5780b3
--- /dev/null
+++ b/core/res/res/values-mcc334-mnc020-kn/strings.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+** Copyright 2020, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="config_pdp_reject_dialog_title" msgid="41208110171880430"></string>
+ <string name="config_pdp_reject_user_authentication_failed" msgid="4683454131283459978">"ದೃಢೀಕರಣ ವಿಫಲಗೊಂಡಿದೆ -29-"</string>
+ <string name="config_pdp_reject_service_not_subscribed" msgid="9021140729932308119">"ಸೇವೆಗೆ ಸಬ್ಸ್ಕ್ರೈಬ್ ಮಾಡಿಲ್ಲ -33-"</string>
+ <string name="config_pdp_reject_multi_conn_to_same_pdn_not_allowed" msgid="3838388706348367865">"ನೀಡಲಾದ APN ಗಾಗಿ ಬಹು PDN ಸಂಪರ್ಕಗಳನ್ನು ಅನುಮತಿಸಲಾಗುವುದಿಲ್ಲ -55-"</string>
+</resources>
diff --git a/core/res/res/values-mcc334-mnc020-ko/strings.xml b/core/res/res/values-mcc334-mnc020-ko/strings.xml
new file mode 100644
index 0000000..444d050
--- /dev/null
+++ b/core/res/res/values-mcc334-mnc020-ko/strings.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+** Copyright 2020, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="config_pdp_reject_dialog_title" msgid="41208110171880430"></string>
+ <string name="config_pdp_reject_user_authentication_failed" msgid="4683454131283459978">"인증할 수 없음 -29-"</string>
+ <string name="config_pdp_reject_service_not_subscribed" msgid="9021140729932308119">"서비스 구독 중이 아님 -33-"</string>
+ <string name="config_pdp_reject_multi_conn_to_same_pdn_not_allowed" msgid="3838388706348367865">"지정된 APN에 여러 PDN을 연결할 수 없음 -55-"</string>
+</resources>
diff --git a/core/res/res/values-mcc334-mnc020-ky/strings.xml b/core/res/res/values-mcc334-mnc020-ky/strings.xml
new file mode 100644
index 0000000..240c6de
--- /dev/null
+++ b/core/res/res/values-mcc334-mnc020-ky/strings.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+** Copyright 2020, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="config_pdp_reject_dialog_title" msgid="41208110171880430"></string>
+ <string name="config_pdp_reject_user_authentication_failed" msgid="4683454131283459978">"АНЫКТЫГЫ ТЕКШЕРИЛГЕН ЖОК -29-"</string>
+ <string name="config_pdp_reject_service_not_subscribed" msgid="9021140729932308119">"КЫЗМАТКА ЖАЗЫЛГАН ЭМЕССИЗ -33-"</string>
+ <string name="config_pdp_reject_multi_conn_to_same_pdn_not_allowed" msgid="3838388706348367865">"Бул APN менен бир нече PDN\'ге туташууга болбойт -55-"</string>
+</resources>
diff --git a/core/res/res/values-mcc334-mnc020-lo/strings.xml b/core/res/res/values-mcc334-mnc020-lo/strings.xml
new file mode 100644
index 0000000..89eb9fc
--- /dev/null
+++ b/core/res/res/values-mcc334-mnc020-lo/strings.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+** Copyright 2020, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="config_pdp_reject_dialog_title" msgid="41208110171880430"></string>
+ <string name="config_pdp_reject_user_authentication_failed" msgid="4683454131283459978">"ການພິສູດຢືນຢັນບໍ່ສຳເລັດ -29-"</string>
+ <string name="config_pdp_reject_service_not_subscribed" msgid="9021140729932308119">"ບໍ່ໄດ້ສະໝັກໃຊ້ບໍລິການ -33-"</string>
+ <string name="config_pdp_reject_multi_conn_to_same_pdn_not_allowed" msgid="3838388706348367865">"ບໍ່ອະນຸຍາດການເຊື່ອມຕໍ່ PDN ຫຼາຍອັນສຳລັບ APN ທີ່ລະບຸ -55-"</string>
+</resources>
diff --git a/core/res/res/values-mcc334-mnc020-lt/strings.xml b/core/res/res/values-mcc334-mnc020-lt/strings.xml
new file mode 100644
index 0000000..9157b8d
--- /dev/null
+++ b/core/res/res/values-mcc334-mnc020-lt/strings.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+** Copyright 2020, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="config_pdp_reject_dialog_title" msgid="41208110171880430"></string>
+ <string name="config_pdp_reject_user_authentication_failed" msgid="4683454131283459978">"AUTENTIFIKAVIMO TRIKTIS -29-"</string>
+ <string name="config_pdp_reject_service_not_subscribed" msgid="9021140729932308119">"PASLAUGA NEPRENUMERUOJAMA -33-"</string>
+ <string name="config_pdp_reject_multi_conn_to_same_pdn_not_allowed" msgid="3838388706348367865">"Kelių PDN ryšiai su APN neleidžiami -55-"</string>
+</resources>
diff --git a/core/res/res/values-mcc334-mnc020-lv/strings.xml b/core/res/res/values-mcc334-mnc020-lv/strings.xml
new file mode 100644
index 0000000..33af863
--- /dev/null
+++ b/core/res/res/values-mcc334-mnc020-lv/strings.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+** Copyright 2020, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="config_pdp_reject_dialog_title" msgid="41208110171880430"></string>
+ <string name="config_pdp_reject_user_authentication_failed" msgid="4683454131283459978">"AUTENTIFIKĀCIJAS KĻŪME -29-"</string>
+ <string name="config_pdp_reject_service_not_subscribed" msgid="9021140729932308119">"PAKALPOJUMS NAV ABONĒTS -33-"</string>
+ <string name="config_pdp_reject_multi_conn_to_same_pdn_not_allowed" msgid="3838388706348367865">"Norādītajam APN nav atļauti vairāki PDN savienojumi -55-"</string>
+</resources>
diff --git a/core/res/res/values-mcc334-mnc020-mk/strings.xml b/core/res/res/values-mcc334-mnc020-mk/strings.xml
new file mode 100644
index 0000000..5b6b621
--- /dev/null
+++ b/core/res/res/values-mcc334-mnc020-mk/strings.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+** Copyright 2020, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="config_pdp_reject_dialog_title" msgid="41208110171880430"></string>
+ <string name="config_pdp_reject_user_authentication_failed" msgid="4683454131283459978">"НЕУСПЕШНА ПРОВЕРКА -29-"</string>
+ <string name="config_pdp_reject_service_not_subscribed" msgid="9021140729932308119">"НЕ СТЕ ПРЕТПЛАТЕНИ НА УСЛУГАТА -33-"</string>
+ <string name="config_pdp_reject_multi_conn_to_same_pdn_not_allowed" msgid="3838388706348367865">"Не се дозволени повеќекратни PDN-врски за дадена APN -55-"</string>
+</resources>
diff --git a/core/res/res/values-mcc334-mnc020-ml/strings.xml b/core/res/res/values-mcc334-mnc020-ml/strings.xml
new file mode 100644
index 0000000..d56b4c8
--- /dev/null
+++ b/core/res/res/values-mcc334-mnc020-ml/strings.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+** Copyright 2020, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="config_pdp_reject_dialog_title" msgid="41208110171880430"></string>
+ <string name="config_pdp_reject_user_authentication_failed" msgid="4683454131283459978">"പരിശോധിച്ചുറപ്പിക്കൽ പരാജയം -29-"</string>
+ <string name="config_pdp_reject_service_not_subscribed" msgid="9021140729932308119">"സേവനം സബ്സ്ക്രൈബ് ചെയ്തിട്ടില്ല -33-"</string>
+ <string name="config_pdp_reject_multi_conn_to_same_pdn_not_allowed" msgid="3838388706348367865">"നൽകിയിരിക്കുന്ന ഒരു APN-ൽ ഒന്നിലധികം PDN കണക്ഷനുകൾ അനുവദനീയമല്ല -55-"</string>
+</resources>
diff --git a/core/res/res/values-mcc334-mnc020-mn/strings.xml b/core/res/res/values-mcc334-mnc020-mn/strings.xml
new file mode 100644
index 0000000..9eb4753
--- /dev/null
+++ b/core/res/res/values-mcc334-mnc020-mn/strings.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+** Copyright 2020, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="config_pdp_reject_dialog_title" msgid="41208110171880430"></string>
+ <string name="config_pdp_reject_user_authentication_failed" msgid="4683454131283459978">"БАТАЛГААЖУУЛЖ ЧАДСАНГҮЙ -29-"</string>
+ <string name="config_pdp_reject_service_not_subscribed" msgid="9021140729932308119">"ҮЙЛЧИЛГЭЭГ ЗАХИАЛААГҮЙ БАЙНА -33-"</string>
+ <string name="config_pdp_reject_multi_conn_to_same_pdn_not_allowed" msgid="3838388706348367865">"Өгсөн APN-д хэд хэдэн PDN холболтыг зөвшөөрдөггүй -55-"</string>
+</resources>
diff --git a/core/res/res/values-mcc334-mnc020-mr/strings.xml b/core/res/res/values-mcc334-mnc020-mr/strings.xml
new file mode 100644
index 0000000..7adf666
--- /dev/null
+++ b/core/res/res/values-mcc334-mnc020-mr/strings.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+** Copyright 2020, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="config_pdp_reject_dialog_title" msgid="41208110171880430"></string>
+ <string name="config_pdp_reject_user_authentication_failed" msgid="4683454131283459978">"ऑथेंटिकेशन यशस्वी झाले नाही -29-"</string>
+ <string name="config_pdp_reject_service_not_subscribed" msgid="9021140729932308119">"सेवेचे सदस्यत्व घेतलेले नाही -33-"</string>
+ <string name="config_pdp_reject_multi_conn_to_same_pdn_not_allowed" msgid="3838388706348367865">"दिलेल्या APN साठी एकपेक्षा जास्त PDN कनेक्शनची अनुमती नाही -55-"</string>
+</resources>
diff --git a/core/res/res/values-mcc334-mnc020-ms/strings.xml b/core/res/res/values-mcc334-mnc020-ms/strings.xml
new file mode 100644
index 0000000..cbae67e
--- /dev/null
+++ b/core/res/res/values-mcc334-mnc020-ms/strings.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+** Copyright 2020, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="config_pdp_reject_dialog_title" msgid="41208110171880430"></string>
+ <string name="config_pdp_reject_user_authentication_failed" msgid="4683454131283459978">"KEGAGALAN PENGESAHAN -29-"</string>
+ <string name="config_pdp_reject_service_not_subscribed" msgid="9021140729932308119">"TIDAK MELANGGAN PERKHIDMATAN -33-"</string>
+ <string name="config_pdp_reject_multi_conn_to_same_pdn_not_allowed" msgid="3838388706348367865">"Sambungan berbilang PDN untuk APN yang diberikan tidak dibenarkan -55-"</string>
+</resources>
diff --git a/core/res/res/values-mcc334-mnc020-my/strings.xml b/core/res/res/values-mcc334-mnc020-my/strings.xml
new file mode 100644
index 0000000..f1e7d6e
--- /dev/null
+++ b/core/res/res/values-mcc334-mnc020-my/strings.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+** Copyright 2020, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="config_pdp_reject_dialog_title" msgid="41208110171880430"></string>
+ <string name="config_pdp_reject_user_authentication_failed" msgid="4683454131283459978">"အထောက်အထားစိစစ်ခြင်း မအောင်မြင်ပါ -29-"</string>
+ <string name="config_pdp_reject_service_not_subscribed" msgid="9021140729932308119">"ဝန်ဆောင်မှုကို စာရင်းသွင်းမထားပါ -33-"</string>
+ <string name="config_pdp_reject_multi_conn_to_same_pdn_not_allowed" msgid="3838388706348367865">"ပေးထားသော APN အတွက် PDN ချိတ်ဆက်မှုအများအပြားကို ခွင့်မပြုပါ -55-"</string>
+</resources>
diff --git a/core/res/res/values-mcc334-mnc020-nb/strings.xml b/core/res/res/values-mcc334-mnc020-nb/strings.xml
new file mode 100644
index 0000000..623f2d9
--- /dev/null
+++ b/core/res/res/values-mcc334-mnc020-nb/strings.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+** Copyright 2020, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="config_pdp_reject_dialog_title" msgid="41208110171880430"></string>
+ <string name="config_pdp_reject_user_authentication_failed" msgid="4683454131283459978">"AUTENTISERINGSFEIL -29-"</string>
+ <string name="config_pdp_reject_service_not_subscribed" msgid="9021140729932308119">"ABONNERER IKKE PÅ TJENESTEN -33-"</string>
+ <string name="config_pdp_reject_multi_conn_to_same_pdn_not_allowed" msgid="3838388706348367865">"Flere PDN-tilkoblinger for et gitt APN er ikke tillatt -55-"</string>
+</resources>
diff --git a/core/res/res/values-mcc334-mnc020-ne/strings.xml b/core/res/res/values-mcc334-mnc020-ne/strings.xml
new file mode 100644
index 0000000..fd69276
--- /dev/null
+++ b/core/res/res/values-mcc334-mnc020-ne/strings.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+** Copyright 2020, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="config_pdp_reject_dialog_title" msgid="41208110171880430"></string>
+ <string name="config_pdp_reject_user_authentication_failed" msgid="4683454131283459978">"पुष्टि गर्न सकिएन -२९-"</string>
+ <string name="config_pdp_reject_service_not_subscribed" msgid="9021140729932308119">"सेवाको सदस्यता लिइएको छैन -३३-"</string>
+ <string name="config_pdp_reject_multi_conn_to_same_pdn_not_allowed" msgid="3838388706348367865">"कुनै निश्चित APN का हकमा एकभन्दा बढी PDN कनेक्सन गर्ने अनुमति दिइएको छैन -५५-"</string>
+</resources>
diff --git a/core/res/res/values-mcc334-mnc020-nl/strings.xml b/core/res/res/values-mcc334-mnc020-nl/strings.xml
new file mode 100644
index 0000000..40cb2fa
--- /dev/null
+++ b/core/res/res/values-mcc334-mnc020-nl/strings.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+** Copyright 2020, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="config_pdp_reject_dialog_title" msgid="41208110171880430"></string>
+ <string name="config_pdp_reject_user_authentication_failed" msgid="4683454131283459978">"VERIFICATIE MISLUKT -29-"</string>
+ <string name="config_pdp_reject_service_not_subscribed" msgid="9021140729932308119">"NIET GEABONNEERD OP SERVICE -33-"</string>
+ <string name="config_pdp_reject_multi_conn_to_same_pdn_not_allowed" msgid="3838388706348367865">"Meerdere PDN-verbindingen voor een bepaalde APN niet toegestaan -55-"</string>
+</resources>
diff --git a/core/res/res/values-mcc334-mnc020-or/strings.xml b/core/res/res/values-mcc334-mnc020-or/strings.xml
new file mode 100644
index 0000000..49810d5
--- /dev/null
+++ b/core/res/res/values-mcc334-mnc020-or/strings.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+** Copyright 2020, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="config_pdp_reject_dialog_title" msgid="41208110171880430"></string>
+ <string name="config_pdp_reject_user_authentication_failed" msgid="4683454131283459978">"ପ୍ରାମାଣିକତା ବିଫଳ ହୋଇଛି -29-"</string>
+ <string name="config_pdp_reject_service_not_subscribed" msgid="9021140729932308119">"ସେବା ପାଇଁ ସଦସ୍ୟତା ନିଆଯାଇନାହିଁ -33-"</string>
+ <string name="config_pdp_reject_multi_conn_to_same_pdn_not_allowed" msgid="3838388706348367865">"ଦିଆଯାଇଥିବା ଏକ APN ପାଇଁ ଏକାଧିକ PDN ସଂଯୋଗକୁ ଅନୁମତି ଦିଆଯାଇନାହିଁ -55-"</string>
+</resources>
diff --git a/core/res/res/values-mcc334-mnc020-pa/strings.xml b/core/res/res/values-mcc334-mnc020-pa/strings.xml
new file mode 100644
index 0000000..3cf6bc8
--- /dev/null
+++ b/core/res/res/values-mcc334-mnc020-pa/strings.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+** Copyright 2020, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="config_pdp_reject_dialog_title" msgid="41208110171880430"></string>
+ <string name="config_pdp_reject_user_authentication_failed" msgid="4683454131283459978">"ਪ੍ਰਮਾਣੀਕਰਨ ਅਸਫਲ -29-"</string>
+ <string name="config_pdp_reject_service_not_subscribed" msgid="9021140729932308119">"ਸੇਵਾ ਦੀ ਗਾਹਕੀ ਨਹੀਂ ਲਈ -33-"</string>
+ <string name="config_pdp_reject_multi_conn_to_same_pdn_not_allowed" msgid="3838388706348367865">"ਕਿਸੇ ਵੀ APN ਲਈ ਇੱਕ ਤੋਂ ਵੱਧ PDN ਕਨੈਕਸ਼ਨਾਂ ਦੀ ਆਗਿਆ ਨਹੀਂ ਹੈ -55-"</string>
+</resources>
diff --git a/core/res/res/values-mcc334-mnc020-pl/strings.xml b/core/res/res/values-mcc334-mnc020-pl/strings.xml
new file mode 100644
index 0000000..9c5fd78
--- /dev/null
+++ b/core/res/res/values-mcc334-mnc020-pl/strings.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+** Copyright 2020, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="config_pdp_reject_dialog_title" msgid="41208110171880430"></string>
+ <string name="config_pdp_reject_user_authentication_failed" msgid="4683454131283459978">"BŁĄD UWIERZYTELNIANIA -29-"</string>
+ <string name="config_pdp_reject_service_not_subscribed" msgid="9021140729932308119">"USŁUGA NIESUBSKRYBOWANA -33-"</string>
+ <string name="config_pdp_reject_multi_conn_to_same_pdn_not_allowed" msgid="3838388706348367865">"Wielokrotne połączenia PDN w danym APN nie są dozwolone -55-"</string>
+</resources>
diff --git a/core/res/res/values-mcc334-mnc020-pt-rBR/strings.xml b/core/res/res/values-mcc334-mnc020-pt-rBR/strings.xml
new file mode 100644
index 0000000..1292978
--- /dev/null
+++ b/core/res/res/values-mcc334-mnc020-pt-rBR/strings.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+** Copyright 2020, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="config_pdp_reject_dialog_title" msgid="41208110171880430"></string>
+ <string name="config_pdp_reject_user_authentication_failed" msgid="4683454131283459978">"FALHA NA AUTENTICAÇÃO -29-"</string>
+ <string name="config_pdp_reject_service_not_subscribed" msgid="9021140729932308119">"A CONTA NÃO ESTÁ INSCRITA NO SERVIÇO -33-"</string>
+ <string name="config_pdp_reject_multi_conn_to_same_pdn_not_allowed" msgid="3838388706348367865">"Não é permitido ter várias conexões PDN para um determinado APN -55-"</string>
+</resources>
diff --git a/core/res/res/values-mcc334-mnc020-pt-rPT/strings.xml b/core/res/res/values-mcc334-mnc020-pt-rPT/strings.xml
new file mode 100644
index 0000000..68248f3
--- /dev/null
+++ b/core/res/res/values-mcc334-mnc020-pt-rPT/strings.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+** Copyright 2020, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="config_pdp_reject_dialog_title" msgid="41208110171880430"></string>
+ <string name="config_pdp_reject_user_authentication_failed" msgid="4683454131283459978">"FALHA NA AUTENTICAÇÃO -29-"</string>
+ <string name="config_pdp_reject_service_not_subscribed" msgid="9021140729932308119">"NÃO SUBSCREVEU O SERVIÇO -33-"</string>
+ <string name="config_pdp_reject_multi_conn_to_same_pdn_not_allowed" msgid="3838388706348367865">"Não são permitidas várias ligações PDN para um determinado APN -55-"</string>
+</resources>
diff --git a/core/res/res/values-mcc334-mnc020-pt/strings.xml b/core/res/res/values-mcc334-mnc020-pt/strings.xml
new file mode 100644
index 0000000..1292978
--- /dev/null
+++ b/core/res/res/values-mcc334-mnc020-pt/strings.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+** Copyright 2020, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="config_pdp_reject_dialog_title" msgid="41208110171880430"></string>
+ <string name="config_pdp_reject_user_authentication_failed" msgid="4683454131283459978">"FALHA NA AUTENTICAÇÃO -29-"</string>
+ <string name="config_pdp_reject_service_not_subscribed" msgid="9021140729932308119">"A CONTA NÃO ESTÁ INSCRITA NO SERVIÇO -33-"</string>
+ <string name="config_pdp_reject_multi_conn_to_same_pdn_not_allowed" msgid="3838388706348367865">"Não é permitido ter várias conexões PDN para um determinado APN -55-"</string>
+</resources>
diff --git a/core/res/res/values-mcc334-mnc020-ro/strings.xml b/core/res/res/values-mcc334-mnc020-ro/strings.xml
new file mode 100644
index 0000000..92564e9
--- /dev/null
+++ b/core/res/res/values-mcc334-mnc020-ro/strings.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+** Copyright 2020, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="config_pdp_reject_dialog_title" msgid="41208110171880430"></string>
+ <string name="config_pdp_reject_user_authentication_failed" msgid="4683454131283459978">"EROARE DE AUTENTIFICARE -29-"</string>
+ <string name="config_pdp_reject_service_not_subscribed" msgid="9021140729932308119">"FĂRĂ ABONAMENT LA SERVICIU -33-"</string>
+ <string name="config_pdp_reject_multi_conn_to_same_pdn_not_allowed" msgid="3838388706348367865">"Nu se permit mai multe conexiuni PDN pentru un anumit APN -55-"</string>
+</resources>
diff --git a/core/res/res/values-mcc334-mnc020-ru/strings.xml b/core/res/res/values-mcc334-mnc020-ru/strings.xml
new file mode 100644
index 0000000..f4647ba
--- /dev/null
+++ b/core/res/res/values-mcc334-mnc020-ru/strings.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+** Copyright 2020, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="config_pdp_reject_dialog_title" msgid="41208110171880430"></string>
+ <string name="config_pdp_reject_user_authentication_failed" msgid="4683454131283459978">"ОШИБКА АУТЕНТИФИКАЦИИ -29-"</string>
+ <string name="config_pdp_reject_service_not_subscribed" msgid="9021140729932308119">"НЕТ ПОДПИСКИ НА СЕРВИС -33-"</string>
+ <string name="config_pdp_reject_multi_conn_to_same_pdn_not_allowed" msgid="3838388706348367865">"Указанная точка доступа не поддерживает несколько PDN-соединений -55-"</string>
+</resources>
diff --git a/core/res/res/values-mcc334-mnc020-si/strings.xml b/core/res/res/values-mcc334-mnc020-si/strings.xml
new file mode 100644
index 0000000..cb83975
--- /dev/null
+++ b/core/res/res/values-mcc334-mnc020-si/strings.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+** Copyright 2020, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="config_pdp_reject_dialog_title" msgid="41208110171880430"></string>
+ <string name="config_pdp_reject_user_authentication_failed" msgid="4683454131283459978">"සත්යාපනය අසාර්ථකයි -29-"</string>
+ <string name="config_pdp_reject_service_not_subscribed" msgid="9021140729932308119">"සේවාවකට ග්රාහක වී නැත -33-"</string>
+ <string name="config_pdp_reject_multi_conn_to_same_pdn_not_allowed" msgid="3838388706348367865">"දී ඇති APN එකක් සඳහා බහුවිධ PDN සම්බන්ධතා ඉඩ නොදේ -55-"</string>
+</resources>
diff --git a/core/res/res/values-mcc334-mnc020-sk/strings.xml b/core/res/res/values-mcc334-mnc020-sk/strings.xml
new file mode 100644
index 0000000..77efe30
--- /dev/null
+++ b/core/res/res/values-mcc334-mnc020-sk/strings.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+** Copyright 2020, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="config_pdp_reject_dialog_title" msgid="41208110171880430"></string>
+ <string name="config_pdp_reject_user_authentication_failed" msgid="4683454131283459978">"NEPODARILO SA OVERIŤ -29-"</string>
+ <string name="config_pdp_reject_service_not_subscribed" msgid="9021140729932308119">"SLUŽBU NEODOBERÁTE -33-"</string>
+ <string name="config_pdp_reject_multi_conn_to_same_pdn_not_allowed" msgid="3838388706348367865">"Pre daný názov prístupového bodu (APN) nie je povolených viacero pripojení PDN -55-"</string>
+</resources>
diff --git a/core/res/res/values-mcc334-mnc020-sl/strings.xml b/core/res/res/values-mcc334-mnc020-sl/strings.xml
new file mode 100644
index 0000000..118c3dd
--- /dev/null
+++ b/core/res/res/values-mcc334-mnc020-sl/strings.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+** Copyright 2020, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="config_pdp_reject_dialog_title" msgid="41208110171880430"></string>
+ <string name="config_pdp_reject_user_authentication_failed" msgid="4683454131283459978">"NAPAKA PRI PREVERJANJU PRISTNOSTI. -29-"</string>
+ <string name="config_pdp_reject_service_not_subscribed" msgid="9021140729932308119">"NISTE NAROČENI NA STORITEV. -33-"</string>
+ <string name="config_pdp_reject_multi_conn_to_same_pdn_not_allowed" msgid="3838388706348367865">"Za dano ime dostopne točke (APN) ni dovoljenih več povezav PDN. -55-"</string>
+</resources>
diff --git a/core/res/res/values-mcc334-mnc020-sq/strings.xml b/core/res/res/values-mcc334-mnc020-sq/strings.xml
new file mode 100644
index 0000000..315a8ab
--- /dev/null
+++ b/core/res/res/values-mcc334-mnc020-sq/strings.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+** Copyright 2020, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="config_pdp_reject_dialog_title" msgid="41208110171880430"></string>
+ <string name="config_pdp_reject_user_authentication_failed" msgid="4683454131283459978">"DËSHTIM NË VERIFIKIM -29-"</string>
+ <string name="config_pdp_reject_service_not_subscribed" msgid="9021140729932308119">"NUK JE ABONUAR NË SHËRBIM -33-"</string>
+ <string name="config_pdp_reject_multi_conn_to_same_pdn_not_allowed" msgid="3838388706348367865">"Disa lidhje PDN për një APN të caktuar nuk lejohen -55-"</string>
+</resources>
diff --git a/core/res/res/values-mcc334-mnc020-sr/strings.xml b/core/res/res/values-mcc334-mnc020-sr/strings.xml
new file mode 100644
index 0000000..ddf0ce1
--- /dev/null
+++ b/core/res/res/values-mcc334-mnc020-sr/strings.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+** Copyright 2020, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="config_pdp_reject_dialog_title" msgid="41208110171880430"></string>
+ <string name="config_pdp_reject_user_authentication_failed" msgid="4683454131283459978">"ПОТВРДА ИДЕНТИТЕТА НИЈЕ УСПЕЛА -29-"</string>
+ <string name="config_pdp_reject_service_not_subscribed" msgid="9021140729932308119">"НИСТЕ ПРЕТПЛАЋЕНИ НА УСЛУГУ -33-"</string>
+ <string name="config_pdp_reject_multi_conn_to_same_pdn_not_allowed" msgid="3838388706348367865">"Није дозвољено више PDN веза за одређени APN -55-"</string>
+</resources>
diff --git a/core/res/res/values-mcc334-mnc020-sv/strings.xml b/core/res/res/values-mcc334-mnc020-sv/strings.xml
new file mode 100644
index 0000000..75bc642
--- /dev/null
+++ b/core/res/res/values-mcc334-mnc020-sv/strings.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+** Copyright 2020, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="config_pdp_reject_dialog_title" msgid="41208110171880430"></string>
+ <string name="config_pdp_reject_user_authentication_failed" msgid="4683454131283459978">"AUTENTISERINGSFEL -29-"</string>
+ <string name="config_pdp_reject_service_not_subscribed" msgid="9021140729932308119">"PRENUMERERAR INTE PÅ TJÄNSTEN -33-"</string>
+ <string name="config_pdp_reject_multi_conn_to_same_pdn_not_allowed" msgid="3838388706348367865">"Flera PDN-anslutningar för samma APN tillåts inte -55-"</string>
+</resources>
diff --git a/core/res/res/values-mcc334-mnc020-sw/strings.xml b/core/res/res/values-mcc334-mnc020-sw/strings.xml
new file mode 100644
index 0000000..275f628
--- /dev/null
+++ b/core/res/res/values-mcc334-mnc020-sw/strings.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+** Copyright 2020, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="config_pdp_reject_dialog_title" msgid="41208110171880430"></string>
+ <string name="config_pdp_reject_user_authentication_failed" msgid="4683454131283459978">"IMESHINDWA KUTHIBITISHA -29-"</string>
+ <string name="config_pdp_reject_service_not_subscribed" msgid="9021140729932308119">"HUJAJISAJILI KWENYE HUDUMA -33-"</string>
+ <string name="config_pdp_reject_multi_conn_to_same_pdn_not_allowed" msgid="3838388706348367865">"Miunganisho mingi ya PDN kwenye APN husika hairuhusiwi -55-"</string>
+</resources>
diff --git a/core/res/res/values-mcc334-mnc020-ta/strings.xml b/core/res/res/values-mcc334-mnc020-ta/strings.xml
new file mode 100644
index 0000000..699ea71
--- /dev/null
+++ b/core/res/res/values-mcc334-mnc020-ta/strings.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+** Copyright 2020, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="config_pdp_reject_dialog_title" msgid="41208110171880430"></string>
+ <string name="config_pdp_reject_user_authentication_failed" msgid="4683454131283459978">"அங்கீகரிக்க முடியவில்லை -29-"</string>
+ <string name="config_pdp_reject_service_not_subscribed" msgid="9021140729932308119">"சேவைக்குச் சந்தா பெறவில்லை -33-"</string>
+ <string name="config_pdp_reject_multi_conn_to_same_pdn_not_allowed" msgid="3838388706348367865">"ஒரு APNக்குப் பல PDN இணைப்புகள் அனுமதிக்கப்படாது -55-"</string>
+</resources>
diff --git a/core/res/res/values-mcc334-mnc020-te/strings.xml b/core/res/res/values-mcc334-mnc020-te/strings.xml
new file mode 100644
index 0000000..eee21aa
--- /dev/null
+++ b/core/res/res/values-mcc334-mnc020-te/strings.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+** Copyright 2020, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="config_pdp_reject_dialog_title" msgid="41208110171880430"></string>
+ <string name="config_pdp_reject_user_authentication_failed" msgid="4683454131283459978">"ప్రామాణీకరణ వైఫల్యం -29-"</string>
+ <string name="config_pdp_reject_service_not_subscribed" msgid="9021140729932308119">"ఈ సర్వీస్ కోసం సబ్స్క్రయిబ్ చేసుకోలేదు -33-"</string>
+ <string name="config_pdp_reject_multi_conn_to_same_pdn_not_allowed" msgid="3838388706348367865">"ఈ APNలో బహుళ PDN కనెక్షన్లు అనుమతించబడవు -55-"</string>
+</resources>
diff --git a/core/res/res/values-mcc334-mnc020-th/strings.xml b/core/res/res/values-mcc334-mnc020-th/strings.xml
new file mode 100644
index 0000000..e4e62f0
--- /dev/null
+++ b/core/res/res/values-mcc334-mnc020-th/strings.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+** Copyright 2020, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="config_pdp_reject_dialog_title" msgid="41208110171880430"></string>
+ <string name="config_pdp_reject_user_authentication_failed" msgid="4683454131283459978">"การตรวจสอบสิทธิ์ล้มเหลว -29-"</string>
+ <string name="config_pdp_reject_service_not_subscribed" msgid="9021140729932308119">"ไม่ได้สมัครใช้บริการ -33-"</string>
+ <string name="config_pdp_reject_multi_conn_to_same_pdn_not_allowed" msgid="3838388706348367865">"ไม่อนุญาตการเชื่อมต่อ PDN หลายรายการสำหรับ APN ที่กำหนด -55-"</string>
+</resources>
diff --git a/core/res/res/values-mcc334-mnc020-tl/strings.xml b/core/res/res/values-mcc334-mnc020-tl/strings.xml
new file mode 100644
index 0000000..e05ef9b
--- /dev/null
+++ b/core/res/res/values-mcc334-mnc020-tl/strings.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+** Copyright 2020, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="config_pdp_reject_dialog_title" msgid="41208110171880430"></string>
+ <string name="config_pdp_reject_user_authentication_failed" msgid="4683454131283459978">"HINDI NA-AUTHENTICATE -29-"</string>
+ <string name="config_pdp_reject_service_not_subscribed" msgid="9021140729932308119">"HINDI NAKA-SUBSCRIBE SA SERBISYO -33-"</string>
+ <string name="config_pdp_reject_multi_conn_to_same_pdn_not_allowed" msgid="3838388706348367865">"Hindi pinapayagan ang maraming PDN na koneksyon para sa isang partikular na APN -55-"</string>
+</resources>
diff --git a/core/res/res/values-mcc334-mnc020-tr/strings.xml b/core/res/res/values-mcc334-mnc020-tr/strings.xml
new file mode 100644
index 0000000..7c83aee
--- /dev/null
+++ b/core/res/res/values-mcc334-mnc020-tr/strings.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+** Copyright 2020, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="config_pdp_reject_dialog_title" msgid="41208110171880430"></string>
+ <string name="config_pdp_reject_user_authentication_failed" msgid="4683454131283459978">"KİMLİK DOĞRULAMA HATASI -29-"</string>
+ <string name="config_pdp_reject_service_not_subscribed" msgid="9021140729932308119">"HİZMETE ABONE OLUNMADI -33-"</string>
+ <string name="config_pdp_reject_multi_conn_to_same_pdn_not_allowed" msgid="3838388706348367865">"Belirli bir APN için birden fazla PDN bağlantısına izin verilmez -55-"</string>
+</resources>
diff --git a/core/res/res/values-mcc334-mnc020-uk/strings.xml b/core/res/res/values-mcc334-mnc020-uk/strings.xml
new file mode 100644
index 0000000..dc18a3e
--- /dev/null
+++ b/core/res/res/values-mcc334-mnc020-uk/strings.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+** Copyright 2020, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="config_pdp_reject_dialog_title" msgid="41208110171880430"></string>
+ <string name="config_pdp_reject_user_authentication_failed" msgid="4683454131283459978">"ПОМИЛКА АВТЕНТИФІКАЦІЇ -29-"</string>
+ <string name="config_pdp_reject_service_not_subscribed" msgid="9021140729932308119">"НЕМАЄ ПІДПИСКИ НА СЕРВІС -33-"</string>
+ <string name="config_pdp_reject_multi_conn_to_same_pdn_not_allowed" msgid="3838388706348367865">"З цієї APN не можна підключатися до кількох відкритих мереж -55-"</string>
+</resources>
diff --git a/core/res/res/values-mcc334-mnc020-ur/strings.xml b/core/res/res/values-mcc334-mnc020-ur/strings.xml
new file mode 100644
index 0000000..bab5589
--- /dev/null
+++ b/core/res/res/values-mcc334-mnc020-ur/strings.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+** Copyright 2020, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="config_pdp_reject_dialog_title" msgid="41208110171880430"></string>
+ <string name="config_pdp_reject_user_authentication_failed" msgid="4683454131283459978">"توثیق کی ناکامی -29-"</string>
+ <string name="config_pdp_reject_service_not_subscribed" msgid="9021140729932308119">"سروس کو سبسکرائب نہیں کیا -33-"</string>
+ <string name="config_pdp_reject_multi_conn_to_same_pdn_not_allowed" msgid="3838388706348367865">"ایک دیئے گئے APN کے لیے متعدد PDN کنکشنز کی اجازت نہیں ہے -55-"</string>
+</resources>
diff --git a/core/res/res/values-mcc334-mnc020-uz/strings.xml b/core/res/res/values-mcc334-mnc020-uz/strings.xml
new file mode 100644
index 0000000..321aff2
--- /dev/null
+++ b/core/res/res/values-mcc334-mnc020-uz/strings.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+** Copyright 2020, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="config_pdp_reject_dialog_title" msgid="41208110171880430"></string>
+ <string name="config_pdp_reject_user_authentication_failed" msgid="4683454131283459978">"HAQIQIYLIK TEKSHIRUVIDA XATO -29-"</string>
+ <string name="config_pdp_reject_service_not_subscribed" msgid="9021140729932308119">"XIZMATGA OBUNA QILINMAGAN -33-"</string>
+ <string name="config_pdp_reject_multi_conn_to_same_pdn_not_allowed" msgid="3838388706348367865">"APN uchun bir nechta PDN ulanishiga ruxsat berilmagan -55-"</string>
+</resources>
diff --git a/core/res/res/values-mcc334-mnc020-vi/strings.xml b/core/res/res/values-mcc334-mnc020-vi/strings.xml
new file mode 100644
index 0000000..bb77bb52
--- /dev/null
+++ b/core/res/res/values-mcc334-mnc020-vi/strings.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+** Copyright 2020, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="config_pdp_reject_dialog_title" msgid="41208110171880430"></string>
+ <string name="config_pdp_reject_user_authentication_failed" msgid="4683454131283459978">"KHÔNG XÁC THỰC ĐƯỢC -29-"</string>
+ <string name="config_pdp_reject_service_not_subscribed" msgid="9021140729932308119">"CHƯA ĐĂNG KÝ DỊCH VỤ -33-"</string>
+ <string name="config_pdp_reject_multi_conn_to_same_pdn_not_allowed" msgid="3838388706348367865">"Không cho phép có nhiều kết nối PDN với một APN đã cho -55-"</string>
+</resources>
diff --git a/core/res/res/values-mcc334-mnc020-zh-rCN/strings.xml b/core/res/res/values-mcc334-mnc020-zh-rCN/strings.xml
new file mode 100644
index 0000000..a8fbe74c
--- /dev/null
+++ b/core/res/res/values-mcc334-mnc020-zh-rCN/strings.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+** Copyright 2020, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="config_pdp_reject_dialog_title" msgid="41208110171880430"></string>
+ <string name="config_pdp_reject_user_authentication_failed" msgid="4683454131283459978">"身份验证失败 -29-"</string>
+ <string name="config_pdp_reject_service_not_subscribed" msgid="9021140729932308119">"未订阅服务 -33-"</string>
+ <string name="config_pdp_reject_multi_conn_to_same_pdn_not_allowed" msgid="3838388706348367865">"指定的 APN 不能有多个 PDN 连接 -55-"</string>
+</resources>
diff --git a/core/res/res/values-mcc334-mnc020-zh-rHK/strings.xml b/core/res/res/values-mcc334-mnc020-zh-rHK/strings.xml
new file mode 100644
index 0000000..af20578
--- /dev/null
+++ b/core/res/res/values-mcc334-mnc020-zh-rHK/strings.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+** Copyright 2020, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="config_pdp_reject_dialog_title" msgid="41208110171880430"></string>
+ <string name="config_pdp_reject_user_authentication_failed" msgid="4683454131283459978">"認證錯誤 -29-"</string>
+ <string name="config_pdp_reject_service_not_subscribed" msgid="9021140729932308119">"未訂閱服務 -33-"</string>
+ <string name="config_pdp_reject_multi_conn_to_same_pdn_not_allowed" msgid="3838388706348367865">"指定的 APN 不允許有多個 PDN 連線 -55-"</string>
+</resources>
diff --git a/core/res/res/values-mcc334-mnc020-zh-rTW/strings.xml b/core/res/res/values-mcc334-mnc020-zh-rTW/strings.xml
new file mode 100644
index 0000000..2d74d5a
--- /dev/null
+++ b/core/res/res/values-mcc334-mnc020-zh-rTW/strings.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+** Copyright 2020, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="config_pdp_reject_dialog_title" msgid="41208110171880430"></string>
+ <string name="config_pdp_reject_user_authentication_failed" msgid="4683454131283459978">"驗證失敗 -29-"</string>
+ <string name="config_pdp_reject_service_not_subscribed" msgid="9021140729932308119">"未訂閱服務 -33-"</string>
+ <string name="config_pdp_reject_multi_conn_to_same_pdn_not_allowed" msgid="3838388706348367865">"指定的 APN 不允許有多個 PDN 連線 -55-"</string>
+</resources>
diff --git a/core/res/res/values-mcc334-mnc020-zu/strings.xml b/core/res/res/values-mcc334-mnc020-zu/strings.xml
new file mode 100644
index 0000000..ef0f3cb
--- /dev/null
+++ b/core/res/res/values-mcc334-mnc020-zu/strings.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+** Copyright 2020, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="config_pdp_reject_dialog_title" msgid="41208110171880430"></string>
+ <string name="config_pdp_reject_user_authentication_failed" msgid="4683454131283459978">"UKWEHLULEKA KOKUFAKAZELA UBUQINISO -29-"</string>
+ <string name="config_pdp_reject_service_not_subscribed" msgid="9021140729932308119">"AWUBHALISELE ISEVISI -33-"</string>
+ <string name="config_pdp_reject_multi_conn_to_same_pdn_not_allowed" msgid="3838388706348367865">"Uxhumano lwe-PDN oluningi lwe-APN enikeziwe aluvunyelwe -55-"</string>
+</resources>
diff --git a/core/res/res/values-mr/strings.xml b/core/res/res/values-mr/strings.xml
index eeb2b57..4bb06bb 100644
--- a/core/res/res/values-mr/strings.xml
+++ b/core/res/res/values-mr/strings.xml
@@ -1040,7 +1040,7 @@
<string name="menu_space_shortcut_label" msgid="5949311515646872071">"स्पेस"</string>
<string name="menu_enter_shortcut_label" msgid="6709499510082897320">"एंटर"</string>
<string name="menu_delete_shortcut_label" msgid="4365787714477739080">"हटवा"</string>
- <string name="search_go" msgid="2141477624421347086">"Search"</string>
+ <string name="search_go" msgid="2141477624421347086">"शोध"</string>
<string name="search_hint" msgid="455364685740251925">"शोधा…"</string>
<string name="searchview_description_search" msgid="1045552007537359343">"शोधा"</string>
<string name="searchview_description_query" msgid="7430242366971716338">"शोध क्वेरी"</string>
diff --git a/core/res/res/values-te/strings.xml b/core/res/res/values-te/strings.xml
index a75ef3d..0cadb45 100644
--- a/core/res/res/values-te/strings.xml
+++ b/core/res/res/values-te/strings.xml
@@ -124,7 +124,7 @@
<string name="roamingTextSearching" msgid="5323235489657753486">"సేవ కోసం శోధిస్తోంది"</string>
<string name="wfcRegErrorTitle" msgid="3193072971584858020">"Wi‑Fi కాలింగ్ని సెటప్ చేయడం సాధ్యపడలేదు"</string>
<string-array name="wfcOperatorErrorAlertMessages">
- <item msgid="468830943567116703">"Wi-Fiతో కాల్స్ను చేయడానికి మరియు మెసేజ్లను పంపించడానికి, మొదట ఈ సేవను సెటప్ చేయాల్సిందిగా మీ క్యారియర్కి చెప్పండి. ఆ తర్వాత సెట్టింగ్ల నుండి Wi-Fi కాలింగ్ని మళ్లీ ఆన్ చేయండి. (లోపం కోడ్: <xliff:g id="CODE">%1$s</xliff:g>)"</item>
+ <item msgid="468830943567116703">"Wi-Fiతో కాల్స్ను చేయడానికి, మెసేజ్లను పంపించడానికి, ముందుగా ఈ సర్వీస్ను సెటప్ చేయాల్సిందిగా మీ క్యారియర్ను అడగండి. ఆ తర్వాత సెట్టింగ్ల నుండి Wi-Fi కాలింగ్ను మళ్లీ ఆన్ చేయండి. (ఎర్రర్ కోడ్: <xliff:g id="CODE">%1$s</xliff:g>)"</item>
</string-array>
<string-array name="wfcOperatorErrorNotificationMessages">
<item msgid="4795145070505729156">"మీ క్యారియర్తో Wi‑Fi కాలింగ్ని నమోదు చేయడంలో సమస్య: <xliff:g id="CODE">%1$s</xliff:g>"</item>
@@ -306,10 +306,10 @@
<string name="permgroupdesc_contacts" msgid="9163927941244182567">"మీ కాంటాక్ట్లను యాక్సెస్ చేయడానికి"</string>
<string name="permgrouplab_location" msgid="1858277002233964394">"లొకేషన్"</string>
<string name="permgroupdesc_location" msgid="1995955142118450685">"ఈ పరికర లొకేషన్ను యాక్సెస్ చేయడానికి"</string>
- <string name="permgrouplab_calendar" msgid="6426860926123033230">"Calendar"</string>
+ <string name="permgrouplab_calendar" msgid="6426860926123033230">"క్యాలెండర్"</string>
<string name="permgroupdesc_calendar" msgid="6762751063361489379">"మీ క్యాలెండర్ను యాక్సెస్ చేయడానికి"</string>
<string name="permgrouplab_sms" msgid="795737735126084874">"SMS"</string>
- <string name="permgroupdesc_sms" msgid="5726462398070064542">"SMS మెసేజ్లను పంపడం మరియు వీక్షించడం"</string>
+ <string name="permgroupdesc_sms" msgid="5726462398070064542">"SMS మెసేజ్లను పంపడం, వీక్షించడం"</string>
<string name="permgrouplab_storage" msgid="1938416135375282333">"ఫైల్స్, మీడియా"</string>
<string name="permgroupdesc_storage" msgid="6351503740613026600">"మీ పరికరంలోని ఫోటోలు, మీడియా మరియు ఫైళ్లను యాక్సెస్ చేయడానికి"</string>
<string name="permgrouplab_microphone" msgid="2480597427667420076">"మైక్రోఫోన్"</string>
@@ -356,10 +356,10 @@
<string name="permdesc_processOutgoingCalls" msgid="7833149750590606334">"కాల్ను వేరే నంబర్కు దారి మళ్లించే లేదా మొత్తంగా కాల్ను ఆపివేసే ఎంపిక సహాయంతో అవుట్గోయింగ్ కాల్ సమయంలో డయల్ చేయబడుతున్న నంబర్ను చూడటానికి యాప్ను అనుమతిస్తుంది."</string>
<string name="permlab_answerPhoneCalls" msgid="4131324833663725855">"ఫోన్ కాల్స్కు సమాధానమివ్వు"</string>
<string name="permdesc_answerPhoneCalls" msgid="894386681983116838">"ఇన్కమింగ్ ఫోన్ కాల్స్కు సమాధానమివ్వడానికి యాప్ను అనుమతిస్తుంది."</string>
- <string name="permlab_receiveSms" msgid="505961632050451881">"వచన మెసేజ్లను (SMS) స్వీకరించడం"</string>
- <string name="permdesc_receiveSms" msgid="1797345626687832285">"SMS మెసేజ్లను స్వీకరించడానికి మరియు ప్రాసెస్ చేయడానికి యాప్ను అనుమతిస్తుంది. యాప్ మీ డివైజ్కు పంపబడిన మెసేజ్లను మీకు చూపకుండానే పర్యవేక్షించగలదని లేదా తొలగించగలదని దీని అర్థం."</string>
- <string name="permlab_receiveMms" msgid="4000650116674380275">"వచన మెసేజ్లను (MMS) స్వీకరించడం"</string>
- <string name="permdesc_receiveMms" msgid="958102423732219710">"MMS మెసేజ్లను స్వీకరించడానికి మరియు ప్రాసెస్ చేయడానికి యాప్ను అనుమతిస్తుంది. యాప్ మీ డివైజ్కు పంపబడిన మెసేజ్లను మీకు చూపకుండానే పర్యవేక్షించగలదని లేదా తొలగించగలదని దీని అర్థం."</string>
+ <string name="permlab_receiveSms" msgid="505961632050451881">"టెక్స్ట్ మెసేజ్లను (SMS) స్వీకరించడం"</string>
+ <string name="permdesc_receiveSms" msgid="1797345626687832285">"SMS మెసేజ్లను స్వీకరించడానికి, ప్రాసెస్ చేయడానికి యాప్ను అనుమతిస్తుంది. మీ డివైజ్కు వచ్చిన మెసేజ్లను మీకు చూపకుండానే యాప్ పర్యవేక్షించగలదని లేదా తొలగించగలదని దీని అర్థం."</string>
+ <string name="permlab_receiveMms" msgid="4000650116674380275">"టెక్స్ట్ మెసేజ్లను (MMS) స్వీకరించడం"</string>
+ <string name="permdesc_receiveMms" msgid="958102423732219710">"MMS మెసేజ్లను స్వీకరించడానికి, ప్రాసెస్ చేయడానికి యాప్ను అనుమతిస్తుంది. మీ డివైజ్కు వచ్చిన మెసేజ్లను మీకు చూపకుండానే యాప్ పర్యవేక్షించగలదని లేదా తొలగించగలదని దీని అర్థం."</string>
<string name="permlab_bindCellBroadcastService" msgid="586746677002040651">"సెల్ ప్రసార మెసేజ్లను ఫార్వర్డ్ చేయడం"</string>
<string name="permdesc_bindCellBroadcastService" msgid="6540910200973641606">"సెల్ ప్రసార మెసేజ్లను స్వీకరించినప్పుడు, వాటిని ఫార్వర్డ్ చేయడానికి సెల్ ప్రసార మాడ్యూల్కు కట్టుబడి ఉండేందుకు యాప్ను అనుమతిస్తుంది. ఎమర్జెన్సీ పరిస్థితుల గురించి మిమ్మల్ని హెచ్చరించడానికి కొన్ని లొకేషన్లలో సెల్ ప్రసార అలర్ట్లు డెలివరీ చేయబడతాయి. ఎమర్జెన్సీ సెల్ ప్రసార అలర్ట్ను స్వీకరించినప్పుడు హానికరమైన యాప్లు మీ పరికరం పనితీరుకు లేదా నిర్వహణకు ఆటంకం కలిగించే అవకాశం ఉంది."</string>
<string name="permlab_manageOngoingCalls" msgid="281244770664231782">"కొనసాగుతున్న కాల్స్ను మేనేజ్ చేయి"</string>
@@ -368,14 +368,14 @@
<string name="permdesc_readCellBroadcasts" msgid="672513437331980168">"మీ పరికరం స్వీకరించిన సెల్ ప్రసార మెసేజ్లను చదవడానికి యాప్ను అనుమతిస్తుంది. ఎమర్జెన్సీ పరిస్థితుల గురించి మిమ్మల్ని హెచ్చరించడానికి కొన్ని లొకేషన్లలో సెల్ ప్రసార అలర్ట్లు డెలివరీ చేయబడతాయి. ఎమర్జెన్సీ సెల్ ప్రసార అలర్ట్ను స్వీకరించినప్పుడు హానికరమైన యాప్లు మీ పరికరం పనితీరుకు లేదా నిర్వహణకు ఆటంకం కలిగించే అవకాశం ఉంది."</string>
<string name="permlab_subscribedFeedsRead" msgid="217624769238425461">"చందా చేయబడిన ఫీడ్లను చదవడం"</string>
<string name="permdesc_subscribedFeedsRead" msgid="6911349196661811865">"ప్రస్తుతం సమకాలీకరించిన ఫీడ్ల గురించి వివరాలను పొందడానికి యాప్ను అనుమతిస్తుంది."</string>
- <string name="permlab_sendSms" msgid="7757368721742014252">"SMS మెసేజ్లను పంపడం మరియు వీక్షించడం"</string>
+ <string name="permlab_sendSms" msgid="7757368721742014252">"SMS మెసేజ్లను పంపడం, వీక్షించడం"</string>
<string name="permdesc_sendSms" msgid="6757089798435130769">"SMS మెసేజ్లు పంపడానికి యాప్ను అనుమతిస్తుంది. దీని వలన ఊహించని ఛార్జీలు విధించబడవచ్చు. హానికరమైన యాప్లు మీ నిర్ధారణ లేకుండానే మెసేజ్లను పంపడం ద్వారా మీకు డబ్బు ఖర్చయ్యేలా చేయవచ్చు."</string>
- <string name="permlab_readSms" msgid="5164176626258800297">"మీ వచన మెసేజ్లు (SMS లేదా MMS) చదవడం"</string>
- <string name="permdesc_readSms" product="tablet" msgid="7912990447198112829">"ఈ యాప్ మీ టాబ్లెట్లో నిల్వ చేసిన అన్ని SMS (వచన) మెసేజ్లను చదవగలదు."</string>
- <string name="permdesc_readSms" product="tv" msgid="3054753345758011986">"ఈ యాప్ మీ Android TV పరికరంలో నిల్వ అయిన SMS (వచనం) సందేశాలన్నింటినీ చదవగలదు."</string>
- <string name="permdesc_readSms" product="default" msgid="774753371111699782">"ఈ యాప్ మీ ఫోన్లో నిల్వ చేసిన అన్ని SMS (వచన) మెసేజ్లను చదవగలదు."</string>
- <string name="permlab_receiveWapPush" msgid="4223747702856929056">"వచన మెసేజ్లను (WAP) స్వీకరించడం"</string>
- <string name="permdesc_receiveWapPush" msgid="1638677888301778457">"WAP మెసేజ్లను స్వీకరించడానికి మరియు ప్రాసెస్ చేయడానికి యాప్ను అనుమతిస్తుంది. ఈ అనుమతి మీకు పంపబడిన మెసేజ్లను మీకు చూపకుండానే పర్యవేక్షించగల లేదా తొలగించగల సామర్థ్యాన్ని కలిగి ఉంటుంది."</string>
+ <string name="permlab_readSms" msgid="5164176626258800297">"మీ టెక్స్ట్ మెసేజ్లు (SMS లేదా MMS) చదవడం"</string>
+ <string name="permdesc_readSms" product="tablet" msgid="7912990447198112829">"ఈ యాప్ మీ టాబ్లెట్లో స్టోర్ చేసిన అన్ని SMS (టెక్స్ట్) మెసేజ్లను చదవగలదు."</string>
+ <string name="permdesc_readSms" product="tv" msgid="3054753345758011986">"ఈ యాప్ మీ Android TV పరికరంలో స్టోర్ అయిన SMS (టెక్స్ట్) మెసేజ్లు అన్నింటిని చదవగలదు."</string>
+ <string name="permdesc_readSms" product="default" msgid="774753371111699782">"ఈ యాప్ మీ ఫోన్లో నిల్వ చేసిన అన్ని SMS (టెక్స్ట్) మెసేజ్లను చదవగలదు."</string>
+ <string name="permlab_receiveWapPush" msgid="4223747702856929056">"టెక్స్ట్ మెసేజ్లను (WAP) స్వీకరించడం"</string>
+ <string name="permdesc_receiveWapPush" msgid="1638677888301778457">"WAP మెసేజ్లను స్వీకరించడానికి, ప్రాసెస్ చేయడానికి యాప్ను అనుమతిస్తుంది. ఈ అనుమతి మీకు వచ్చిన మెసేజ్లను మీకు చూపకుండానే పర్యవేక్షించగల లేదా తొలగించగల సామర్థ్యాన్ని కలిగి ఉంటుంది."</string>
<string name="permlab_getTasks" msgid="7460048811831750262">"అమలవుతున్న యాప్లను పునరుద్ధరించడం"</string>
<string name="permdesc_getTasks" msgid="7388138607018233726">"ప్రస్తుతం మరియు ఇటీవల అమలవుతున్న విధుల గురించి వివరణాత్మక సమాచారాన్ని తిరిగి పొందడానికి యాప్ను అనుమతిస్తుంది. ఇది పరికరంలో ఉపయోగించబడిన యాప్ల గురించి సమాచారాన్ని కనుగొనడానికి యాప్ను అనుమతించవచ్చు."</string>
<string name="permlab_manageProfileAndDeviceOwners" msgid="639849495253987493">"ప్రొఫైల్ మరియు పరికర యజమానులను నిర్వహించడం"</string>
@@ -431,9 +431,9 @@
<string name="permdesc_readCalendar" product="tv" msgid="5811726712981647628">"ఈ యాప్ మీ Android TV పరికరంలో నిల్వ చేసిన క్యాలెండర్ ఈవెంట్లన్నీ చదవగలదు, మీ క్యాలెండర్ డేటాను షేర్ చేయగలదు లేదా సేవ్ చేయగలదు."</string>
<string name="permdesc_readCalendar" product="default" msgid="9118823807655829957">"ఈ యాప్ మీ ఫోన్లో నిల్వ చేసిన క్యాలెండర్ ఈవెంట్లన్నీ చదవగలదు మరియు మీ క్యాలెండర్ డేటాను షేర్ చేయగలదు లేదా సేవ్ చేయగలదు."</string>
<string name="permlab_writeCalendar" msgid="6422137308329578076">"యజమానికి తెలియకుండానే క్యాలెండర్ ఈవెంట్లను జోడించి లేదా సవరించి, అతిథులకు ఇమెయిల్ పంపడం"</string>
- <string name="permdesc_writeCalendar" product="tablet" msgid="8722230940717092850">"ఈ యాప్ మీ టాబ్లెట్లో క్యాలెండర్ ఈవెంట్లను జోడించగలదు, తీసివేయగలదు లేదా మార్చగలదు. ఈ యాప్ క్యాలెండర్ యజమానుల నుండి వచ్చినట్లుగా మెసేజ్లను పంపగలదు లేదా ఈవెంట్లను వాటి యజమానులకు తెలియకుండానే మార్చగలదు."</string>
- <string name="permdesc_writeCalendar" product="tv" msgid="951246749004952706">"ఈ యాప్ మీ Android TV పరికరంలో క్యాలెండర్ ఈవెంట్లను జోడించగలదు, తీసివేయగలదు లేదా మార్చగలదు. ఈ యాప్ క్యాలెండర్ యజమానుల నుండి వచ్చినట్లుగా మెసేజ్లను పంపగలదు లేదా ఈవెంట్లను వాటి యజమానులకు తెలియకుండానే మార్చగలదు."</string>
- <string name="permdesc_writeCalendar" product="default" msgid="5416380074475634233">"ఈ యాప్ మీ ఫోన్లో క్యాలెండర్ ఈవెంట్లను జోడించగలదు, తీసివేయగలదు లేదా మార్చగలదు. ఈ యాప్ క్యాలెండర్ యజమానుల నుండి వచ్చినట్లుగా మెసేజ్లను పంపగలదు లేదా ఈవెంట్లను వాటి యజమానులకు తెలియకుండానే మార్చగలదు."</string>
+ <string name="permdesc_writeCalendar" product="tablet" msgid="8722230940717092850">"ఈ యాప్ మీ టాబ్లెట్లో క్యాలెండర్ ఈవెంట్లను జోడించగలదు, తీసివేయగలదు లేదా మార్చగలదు. ఈ యాప్ క్యాలెండర్ ఓనర్ల నుండి వచ్చినట్లుగా మెసేజ్లను పంపగలదు లేదా ఈవెంట్లను వాటి ఓనర్లకు తెలియకుండానే మార్చగలదు."</string>
+ <string name="permdesc_writeCalendar" product="tv" msgid="951246749004952706">"ఈ యాప్ మీ Android TV పరికరంలో క్యాలెండర్ ఈవెంట్లను జోడించగలదు, తీసివేయగలదు లేదా మార్చగలదు. ఈ యాప్ క్యాలెండర్ ఓనర్ల నుండి వచ్చినట్లుగా మెసేజ్లను పంపగలదు లేదా ఈవెంట్లను వాటి ఓనర్లకు తెలియకుండానే మార్చగలదు."</string>
+ <string name="permdesc_writeCalendar" product="default" msgid="5416380074475634233">"ఈ యాప్ మీ ఫోన్లో క్యాలెండర్ ఈవెంట్లను జోడించగలదు, తీసివేయగలదు లేదా మార్చగలదు. ఈ యాప్ క్యాలెండర్ ఓనర్ల నుండి వచ్చినట్లుగా మెసేజ్లను పంపగలదు, లేదా ఈవెంట్లను వాటి ఓనర్లకు తెలియకుండానే మార్చగలదు."</string>
<string name="permlab_accessLocationExtraCommands" msgid="5162339812057983988">"అదనపు లొకేషన్ ప్రొవైడర్ కమాండ్లను యాక్సెస్ చేయడం"</string>
<string name="permdesc_accessLocationExtraCommands" msgid="355369611979907967">"అదనపు లొకేషన్ ప్రొవైడర్ కమాండ్లను యాక్సెస్ చేయడానికి యాప్ను అనుమతిస్తుంది. ఇది GPS లేదా ఇతర లొకేషన్ సోర్స్ల నిర్వహణలో యాప్ ప్రమేయం ఉండేలా అనుమతించవచ్చు."</string>
<string name="permlab_accessFineLocation" msgid="6426318438195622966">"స్క్రీన్పై ఉన్నప్పుడు మాత్రమే ఖచ్చితమైన లొకేషన్ను యాక్సెస్ చేయండి"</string>
@@ -817,7 +817,7 @@
<string name="phoneTypeTtyTdd" msgid="532038552105328779">"TTY TDD"</string>
<string name="phoneTypeWorkMobile" msgid="7522314392003565121">"కార్యాలయ మొబైల్"</string>
<string name="phoneTypeWorkPager" msgid="3748332310638505234">"కార్యాలయ పేజర్"</string>
- <string name="phoneTypeAssistant" msgid="757550783842231039">"Assistant"</string>
+ <string name="phoneTypeAssistant" msgid="757550783842231039">"అసిస్టెంట్"</string>
<string name="phoneTypeMms" msgid="1799747455131365989">"MMS"</string>
<string name="eventTypeCustom" msgid="3257367158986466481">"అనుకూలం"</string>
<string name="eventTypeBirthday" msgid="7770026752793912283">"పుట్టినరోజు"</string>
@@ -850,7 +850,7 @@
<string name="orgTypeOther" msgid="5450675258408005553">"ఇతరం"</string>
<string name="orgTypeCustom" msgid="1126322047677329218">"అనుకూలం"</string>
<string name="relationTypeCustom" msgid="282938315217441351">"అనుకూలం"</string>
- <string name="relationTypeAssistant" msgid="4057605157116589315">"Assistant"</string>
+ <string name="relationTypeAssistant" msgid="4057605157116589315">"అసిస్టెంట్"</string>
<string name="relationTypeBrother" msgid="7141662427379247820">"సోదరుడు"</string>
<string name="relationTypeChild" msgid="9076258911292693601">"బిడ్డ"</string>
<string name="relationTypeDomesticPartner" msgid="7825306887697559238">"జీవిత భాగస్వామి"</string>
@@ -1014,7 +1014,7 @@
<string name="permlab_setAlarm" msgid="1158001610254173567">"అలారం సెట్ చేయడం"</string>
<string name="permdesc_setAlarm" msgid="2185033720060109640">"ఇన్స్టాల్ చేయబడిన అలారం గడియారం యాప్లో అలారంను సెట్ చేయడానికి యాప్ను అనుమతిస్తుంది. కొన్ని అలారం గల గడియారం యాప్లు ఈ ఫీచర్ను అమలు చేయకపోవచ్చు."</string>
<string name="permlab_addVoicemail" msgid="4770245808840814471">"వాయిస్ మెయిల్ను జోడించడం"</string>
- <string name="permdesc_addVoicemail" msgid="5470312139820074324">"మీ వాయిస్ మెయిల్ ఇన్బాక్స్కి మెసేజ్లను జోడించడానికి యాప్ను అనుమతిస్తుంది."</string>
+ <string name="permdesc_addVoicemail" msgid="5470312139820074324">"మీ వాయిస్ మెయిల్ ఇన్బాక్స్కు మెసేజ్లను జోడించడానికి యాప్ను అనుమతిస్తుంది."</string>
<string name="permlab_writeGeolocationPermissions" msgid="8605631647492879449">"బ్రౌజర్ భౌగోళిక స్థానం అనుమతులను సవరించడం"</string>
<string name="permdesc_writeGeolocationPermissions" msgid="5817346421222227772">"బ్రౌజర్ యొక్క భౌగోళిక లొకేషన్ అనుమతులను సవరించడానికి యాప్ను అనుమతిస్తుంది. హానికరమైన యాప్లు ఏకపక్ష వెబ్ సైట్లకు లొకేషన్ సమాచారాన్ని అనుమతించడానికి దీన్ని ఉపయోగించవచ్చు."</string>
<string name="save_password_message" msgid="2146409467245462965">"మీరు బ్రౌజర్ ఈ పాస్వర్డ్ను గుర్తుపెట్టుకోవాలని కోరుకుంటున్నారా?"</string>
@@ -2010,7 +2010,7 @@
<string name="app_category_image" msgid="7307840291864213007">"ఫోటోలు, ఇమేజ్లు"</string>
<string name="app_category_social" msgid="2278269325488344054">"సామాజికం & కమ్యూనికేషన్"</string>
<string name="app_category_news" msgid="1172762719574964544">"వార్తలు & వార్తాపత్రికలు"</string>
- <string name="app_category_maps" msgid="6395725487922533156">"Maps & నావిగేషన్"</string>
+ <string name="app_category_maps" msgid="6395725487922533156">"మ్యాప్స్ & నావిగేషన్"</string>
<string name="app_category_productivity" msgid="1844422703029557883">"ఉత్పాదకత"</string>
<string name="app_category_accessibility" msgid="6643521607848547683">"యాక్సెసిబిలిటీ"</string>
<string name="device_storage_monitor_notification_channel" msgid="5164244565844470758">"పరికర నిల్వ"</string>
@@ -2055,7 +2055,7 @@
<string name="etws_primary_default_message_earthquake" msgid="8401079517718280669">"ప్రశాంతంగా ఉండండి మరియు దగ్గర్లో తలదాచుకోండి."</string>
<string name="etws_primary_default_message_tsunami" msgid="5828171463387976279">"వెంటనే తీర ప్రాంతాలు మరియు నదీ పరీవాహక ప్రాంతాలను ఖాళీ చేసి మెట్ట ప్రాంతాలకు తరలి వెళ్లండి."</string>
<string name="etws_primary_default_message_earthquake_and_tsunami" msgid="4888224011071875068">"ప్రశాంతంగా ఉండండి మరియు దగ్గర్లో తలదాచుకోండి."</string>
- <string name="etws_primary_default_message_test" msgid="4583367373909549421">"అత్యవసర మెసేజ్ల పరీక్ష"</string>
+ <string name="etws_primary_default_message_test" msgid="4583367373909549421">"అత్యవసర మెసేజ్ల టెస్ట్"</string>
<string name="notification_reply_button_accessibility" msgid="5235776156579456126">"రిప్లయి పంపండి"</string>
<string name="etws_primary_default_message_others" msgid="7958161706019130739"></string>
<string name="mmcc_authentication_reject" msgid="4891965994643876369">"వాయిస్ కోసం SIM అనుమతించబడదు"</string>
@@ -2101,7 +2101,7 @@
<string name="nas_upgrade_notification_enable_action" msgid="3046406808378726874">"సరే"</string>
<string name="nas_upgrade_notification_disable_action" msgid="3794833210043497982">"ఆఫ్ చేయండి"</string>
<string name="nas_upgrade_notification_learn_more_action" msgid="7011130656195423947">"మరింత తెలుసుకోండి"</string>
- <string name="nas_upgrade_notification_learn_more_content" msgid="3735480566983530650">"Android 12లో Android అనుకూల నోటిఫికేషన్లను, మెరుగైన నోటిఫికేషన్లు భర్తీ చేశాయి. సూచించిన చర్యలు, రిప్లయిలను ఈ ఫీచర్ చూపించి, మీ నోటిఫికేషన్లను ఆర్గనైజ్ చేస్తుంది.\n\nకాంటాక్ట్ పేర్లు, మెసేజ్లు లాంటి వ్యక్తిగత సమాచారంతో సహా నోటిఫికేషన్ కంటెంట్ను మెరుగైన నోటిఫికేషన్లు యాక్సెస్ చేయవచ్చు. ఫోన్ కాల్స్కు సమాధానమివ్వడం, \'అంతరాయం కలిగించవద్దు\' ఆప్షన్ను కంట్రోల్ చేయడం లాంటి నోటిఫికేషన్లను విస్మరించడం లేదా ప్రతిస్పందించడం కూడా ఈ ఫీచర్ చేయగలదు."</string>
+ <string name="nas_upgrade_notification_learn_more_content" msgid="3735480566983530650">"Android 12లో Android అనుకూల నోటిఫికేషన్లను, మెరుగైన నోటిఫికేషన్లు రీప్లేస్ చేశాయి. ఈ ఫీచర్, సూచించిన చర్యలను, రిప్లయిలను చూపించి, మీ నోటిఫికేషన్లను ఆర్గనైజ్ చేస్తుంది.\n\nకాంటాక్ట్ పేర్లు, మెసేజ్లు లాంటి వ్యక్తిగత సమాచారంతో పాటు నోటిఫికేషన్ కంటెంట్ను మెరుగైన నోటిఫికేషన్లు యాక్సెస్ చేస్తాయి. ఫోన్ కాల్స్కు సమాధానమివ్వడం, \'అంతరాయం కలిగించవద్దు\' ఆప్షన్ను కంట్రోల్ చేయడం వంటి నోటిఫికేషన్లను విస్మరించడం లేదా వాటికి ప్రతిస్పందించడం కూడా ఈ ఫీచర్ చేయగలదు."</string>
<string name="dynamic_mode_notification_channel_name" msgid="2986926422100223328">"రొటీన్ మోడ్ సమాచార నోటిఫికేషన్"</string>
<string name="dynamic_mode_notification_title" msgid="9205715501274608016">"మామూలుగా ఛార్జ్ చేసేలోపు బ్యాటరీ ఖాళీ కావచ్చు"</string>
<string name="dynamic_mode_notification_summary" msgid="4141614604437372157">"బ్యాటరీ జీవితకాలాన్ని పెంచడానికి బ్యాటరీ సేవర్ యాక్టివేట్ చేయబడింది"</string>
diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml
index 5339640..77820d1 100644
--- a/core/res/res/values/attrs.xml
+++ b/core/res/res/values/attrs.xml
@@ -8372,8 +8372,8 @@
<attr name="supportsAmbientMode" format="boolean" />
<!-- Indicates that this wallpaper service should receive zoom transition updates when
- changing the device state (e.g. when folding or unfolding a foldable device).
- When this value is set to true
+ changing the display state of the device (e.g. when folding or unfolding
+ a foldable device). When this value is set to true
{@link android.service.wallpaper.WallpaperService.Engine} could receive zoom updates
before or after changing the device state. Wallpapers receive zoom updates using
{@link android.service.wallpaper.WallpaperService.Engine#onZoomChanged(float)} and
@@ -8381,8 +8381,8 @@
{@link android.service.wallpaper.WallpaperService.Engine} is created and not destroyed.
Default value is true.
Corresponds to
- {@link android.app.WallpaperInfo#shouldUseDefaultDeviceStateChangeTransition()} -->
- <attr name="shouldUseDefaultDeviceStateChangeTransition" format="boolean" />
+ {@link android.app.WallpaperInfo#shouldUseDefaultDisplayStateChangeTransition()} -->
+ <attr name="shouldUseDefaultDisplayStateChangeTransition" format="boolean" />
<!-- Uri that specifies a settings Slice for this wallpaper. -->
<attr name="settingsSliceUri" format="string"/>
diff --git a/core/res/res/values/colors.xml b/core/res/res/values/colors.xml
index b191584..59d6005 100644
--- a/core/res/res/values/colors.xml
+++ b/core/res/res/values/colors.xml
@@ -246,37 +246,37 @@
<!-- Lightest shade of the accent color used by the system. White.
This value can be overlaid at runtime by OverlayManager RROs. -->
<color name="system_accent1_0">#ffffff</color>
- <!-- Shade of the accent system color at 99% lightness.
+ <!-- Shade of the accent system color at 99% perceptual luminance (L* in L*a*b* color space).
This value can be overlaid at runtime by OverlayManager RROs. -->
<color name="system_accent1_10">#F9FCFF</color>
- <!-- Shade of the accent system color at 95% lightness.
+ <!-- Shade of the accent system color at 95% perceptual luminance (L* in L*a*b* color space).
This value can be overlaid at runtime by OverlayManager RROs. -->
<color name="system_accent1_50">#E0F3FF</color>
- <!-- Shade of the accent system color at 90% lightness.
+ <!-- Shade of the accent system color at 90% perceptual luminance (L* in L*a*b* color space).
This value can be overlaid at runtime by OverlayManager RROs. -->
<color name="system_accent1_100">#C1E8FF</color>
- <!-- Shade of the accent system color at 80% lightness.
+ <!-- Shade of the accent system color at 80% perceptual luminance (L* in L*a*b* color space).
This value can be overlaid at runtime by OverlayManager RROs. -->
<color name="system_accent1_200">#76D1FF</color>
- <!-- Shade of the accent system color at 70% lightness.
+ <!-- Shade of the accent system color at 70% perceptual luminance (L* in L*a*b* color space).
This value can be overlaid at runtime by OverlayManager RROs. -->
<color name="system_accent1_300">#4BB6E8</color>
- <!-- Shade of the accent system color at 60% lightness.
+ <!-- Shade of the accent system color at 60% perceptual luminance (L* in L*a*b* color space).
This value can be overlaid at runtime by OverlayManager RROs. -->
<color name="system_accent1_400">#219BCC</color>
- <!-- Shade of the accent system color at 49% lightness.
+ <!-- Shade of the accent system color at 49.6% perceptual luminance (L* in L*a*b* color space).
This value can be overlaid at runtime by OverlayManager RROs. -->
<color name="system_accent1_500">#007FAC</color>
- <!-- Shade of the accent system color at 40% lightness.
+ <!-- Shade of the accent system color at 40% perceptual luminance (L* in L*a*b* color space).
This value can be overlaid at runtime by OverlayManager RROs. -->
<color name="system_accent1_600">#00668B</color>
- <!-- Shade of the accent system color at 30% lightness.
+ <!-- Shade of the accent system color at 30% perceptual luminance (L* in L*a*b* color space).
This value can be overlaid at runtime by OverlayManager RROs. -->
<color name="system_accent1_700">#004C69</color>
- <!-- Shade of the accent system color at 20% lightness.
+ <!-- Shade of the accent system color at 20% perceptual luminance (L* in L*a*b* color space).
This value can be overlaid at runtime by OverlayManager RROs. -->
<color name="system_accent1_800">#003549</color>
- <!-- Shade of the accent system color at 10% lightness.
+ <!-- Shade of the accent system color at 10% perceptual luminance (L* in L*a*b* color space).
This value can be overlaid at runtime by OverlayManager RROs. -->
<color name="system_accent1_900">#001E2C</color>
<!-- Darkest shade of the accent color used by the system. Black.
@@ -286,37 +286,37 @@
<!-- Lightest shade of the secondary accent color used by the system. White.
This value can be overlaid at runtime by OverlayManager RROs. -->
<color name="system_accent2_0">#ffffff</color>
- <!-- Shade of the secondary accent system color at 99% lightness.
+ <!-- Shade of the secondary accent system color at 99% perceptual luminance (L* in L*a*b* color space).
This value can be overlaid at runtime by OverlayManager RROs. -->
<color name="system_accent2_10">#F9FCFF</color>
- <!-- Shade of the secondary accent system color at 95% lightness.
+ <!-- Shade of the secondary accent system color at 95% perceptual luminance (L* in L*a*b* color space).
This value can be overlaid at runtime by OverlayManager RROs. -->
<color name="system_accent2_50">#E0F3FF</color>
- <!-- Shade of the secondary accent system color at 90% lightness.
+ <!-- Shade of the secondary accent system color at 90% perceptual luminance (L* in L*a*b* color space).
This value can be overlaid at runtime by OverlayManager RROs. -->
<color name="system_accent2_100">#D1E5F4</color>
- <!-- Shade of the secondary accent system color at 80% lightness.
+ <!-- Shade of the secondary accent system color at 80% perceptual luminance (L* in L*a*b* color space).
This value can be overlaid at runtime by OverlayManager RROs. -->
<color name="system_accent2_200">#B5CAD7</color>
- <!-- Shade of the secondary accent system color at 70% lightness.
+ <!-- Shade of the secondary accent system color at 70% perceptual luminance (L* in L*a*b* color space).
This value can be overlaid at runtime by OverlayManager RROs. -->
<color name="system_accent2_300">#9AAEBB</color>
- <!-- Shade of the secondary accent system color at 60% lightness.
+ <!-- Shade of the secondary accent system color at 60% perceptual luminance (L* in L*a*b* color space).
This value can be overlaid at runtime by OverlayManager RROs. -->
<color name="system_accent2_400">#8094A0</color>
- <!-- Shade of the secondary accent system color at 49% lightness.
+ <!-- Shade of the secondary accent system color at 49.6% perceptual luminance (L* in L*a*b* color space).
This value can be overlaid at runtime by OverlayManager RROs. -->
<color name="system_accent2_500">#657985</color>
- <!-- Shade of the secondary accent system color at 40% lightness.
+ <!-- Shade of the secondary accent system color at 40% perceptual luminance (L* in L*a*b* color space).
This value can be overlaid at runtime by OverlayManager RROs. -->
<color name="system_accent2_600">#4E616C</color>
- <!-- Shade of the secondary accent system color at 30% lightness.
+ <!-- Shade of the secondary accent system color at 30% perceptual luminance (L* in L*a*b* color space).
This value can be overlaid at runtime by OverlayManager RROs. -->
<color name="system_accent2_700">#374955</color>
- <!-- Shade of the secondary accent system color at 20% lightness.
+ <!-- Shade of the secondary accent system color at 20% perceptual luminance (L* in L*a*b* color space).
This value can be overlaid at runtime by OverlayManager RROs. -->
<color name="system_accent2_800">#20333D</color>
- <!-- Shade of the secondary accent system color at 10% lightness.
+ <!-- Shade of the secondary accent system color at 10% perceptual luminance (L* in L*a*b* color space).
This value can be overlaid at runtime by OverlayManager RROs. -->
<color name="system_accent2_900">#091E28</color>
<!-- Darkest shade of the secondary accent color used by the system. Black.
@@ -326,37 +326,37 @@
<!-- Lightest shade of the tertiary accent color used by the system. White.
This value can be overlaid at runtime by OverlayManager RROs. -->
<color name="system_accent3_0">#ffffff</color>
- <!-- Shade of the tertiary accent system color at 99% lightness.
+ <!-- Shade of the tertiary accent system color at 99% perceptual luminance (L* in L*a*b* color space).
This value can be overlaid at runtime by OverlayManager RROs. -->
<color name="system_accent3_10">#FFFBFF</color>
- <!-- Shade of the tertiary accent system color at 95% lightness.
+ <!-- Shade of the tertiary accent system color at 95% perceptual luminance (L* in L*a*b* color space).
This value can be overlaid at runtime by OverlayManager RROs. -->
<color name="system_accent3_50">#F5EEFF</color>
- <!-- Shade of the tertiary accent system color at 90% lightness.
+ <!-- Shade of the tertiary accent system color at 90% perceptual luminance (L* in L*a*b* color space).
This value can be overlaid at runtime by OverlayManager RROs. -->
<color name="system_accent3_100">#E6DEFF</color>
- <!-- Shade of the tertiary accent system color at 80% lightness.
+ <!-- Shade of the tertiary accent system color at 80% perceptual luminance (L* in L*a*b* color space).
This value can be overlaid at runtime by OverlayManager RROs. -->
<color name="system_accent3_200">#CAC1EA</color>
- <!-- Shade of the tertiary accent system color at 70% lightness.
+ <!-- Shade of the tertiary accent system color at 70% perceptual luminance (L* in L*a*b* color space).
This value can be overlaid at runtime by OverlayManager RROs. -->
<color name="system_accent3_300">#AEA6CE</color>
- <!-- Shade of the tertiary accent system color at 60% lightness.
+ <!-- Shade of the tertiary accent system color at 60% perceptual luminance (L* in L*a*b* color space).
This value can be overlaid at runtime by OverlayManager RROs. -->
<color name="system_accent3_400">#938CB1</color>
- <!-- Shade of the tertiary accent system color at 49% lightness.
+ <!-- Shade of the tertiary accent system color at 49% perceptual luminance (L* in L*a*b* color space).
This value can be overlaid at runtime by OverlayManager RROs. -->
<color name="system_accent3_500">#787296</color>
- <!-- Shade of the tertiary accent system color at 40% lightness.
+ <!-- Shade of the tertiary accent system color at 40% perceptual luminance (L* in L*a*b* color space).
This value can be overlaid at runtime by OverlayManager RROs. -->
<color name="system_accent3_600">#605A7C</color>
- <!-- Shade of the tertiary accent system color at 30% lightness.
+ <!-- Shade of the tertiary accent system color at 30% perceptual luminance (L* in L*a*b* color space).
This value can be overlaid at runtime by OverlayManager RROs. -->
<color name="system_accent3_700">#484264</color>
- <!-- Shade of the tertiary accent system color at 20% lightness.
+ <!-- Shade of the tertiary accent system color at 20% perceptual luminance (L* in L*a*b* color space).
This value can be overlaid at runtime by OverlayManager RROs. -->
<color name="system_accent3_800">#322C4C</color>
- <!-- Shade of the tertiary accent system color at 10% lightness.
+ <!-- Shade of the tertiary accent system color at 10% perceptual luminance (L* in L*a*b* color space).
This value can be overlaid at runtime by OverlayManager RROs. -->
<color name="system_accent3_900">#1D1736</color>
<!-- Darkest shade of the tertiary accent color used by the system. Black.
@@ -366,37 +366,37 @@
<!-- Lightest shade of the neutral color used by the system. White.
This value can be overlaid at runtime by OverlayManager RROs. -->
<color name="system_neutral1_0">#ffffff</color>
- <!-- Shade of the neutral system color at 99% lightness.
+ <!-- Shade of the neutral system color at 99% perceptual luminance (L* in L*a*b* color space).
This value can be overlaid at runtime by OverlayManager RROs. -->
<color name="system_neutral1_10">#FCFCFF</color>
- <!-- Shade of the neutral system color at 95% lightness.
+ <!-- Shade of the neutral system color at 95% perceptual luminance (L* in L*a*b* color space).
This value can be overlaid at runtime by OverlayManager RROs. -->
<color name="system_neutral1_50">#F0F0F3</color>
- <!-- Shade of the neutral system color at 90% lightness.
+ <!-- Shade of the neutral system color at 90% perceptual luminance (L* in L*a*b* color space).
This value can be overlaid at runtime by OverlayManager RROs. -->
<color name="system_neutral1_100">#E1E3E5</color>
- <!-- Shade of the neutral system color at 80% lightness.
+ <!-- Shade of the neutral system color at 80% perceptual luminance (L* in L*a*b* color space).
This value can be overlaid at runtime by OverlayManager RROs. -->
<color name="system_neutral1_200">#C5C7C9</color>
- <!-- Shade of the neutral system color at 70% lightness.
+ <!-- Shade of the neutral system color at 70% perceptual luminance (L* in L*a*b* color space).
This value can be overlaid at runtime by OverlayManager RROs. -->
<color name="system_neutral1_300">#AAABAE</color>
- <!-- Shade of the neutral system color at 60% lightness.
+ <!-- Shade of the neutral system color at 60% perceptual luminance (L* in L*a*b* color space).
This value can be overlaid at runtime by OverlayManager RROs. -->
<color name="system_neutral1_400">#8F9193</color>
- <!-- Shade of the neutral system color at 49% lightness.
+ <!-- Shade of the neutral system color at 49% perceptual luminance (L* in L*a*b* color space).
This value can be overlaid at runtime by OverlayManager RROs. -->
<color name="system_neutral1_500">#747679</color>
- <!-- Shade of the neutral system color at 40% lightness.
+ <!-- Shade of the neutral system color at 40% perceptual luminance (L* in L*a*b* color space).
This value can be overlaid at runtime by OverlayManager RROs. -->
<color name="system_neutral1_600">#5C5F61</color>
- <!-- Shade of the neutral system color at 30% lightness.
+ <!-- Shade of the neutral system color at 30% perceptual luminance (L* in L*a*b* color space).
This value can be overlaid at runtime by OverlayManager RROs. -->
<color name="system_neutral1_700">#454749</color>
- <!-- Shade of the neutral system color at 20% lightness.
+ <!-- Shade of the neutral system color at 20% perceptual luminance (L* in L*a*b* color space).
This value can be overlaid at runtime by OverlayManager RROs. -->
<color name="system_neutral1_800">#2E3133</color>
- <!-- Shade of the neutral system color at 10% lightness.
+ <!-- Shade of the neutral system color at 10% perceptual luminance (L* in L*a*b* color space).
This value can be overlaid at runtime by OverlayManager RROs. -->
<color name="system_neutral1_900">#191C1E</color>
<!-- Darkest shade of the neutral color used by the system. Black.
@@ -406,37 +406,37 @@
<!-- Lightest shade of the secondary neutral color used by the system. White.
This value can be overlaid at runtime by OverlayManager RROs. -->
<color name="system_neutral2_0">#ffffff</color>
- <!-- Shade of the secondary neutral system color at 99% lightness.
+ <!-- Shade of the secondary neutral system color at 99% perceptual luminance (L* in L*a*b* color space).
This value can be overlaid at runtime by OverlayManager RROs. -->
<color name="system_neutral2_10">#F9FCFF</color>
- <!-- Shade of the secondary neutral system color at 95% lightness.
+ <!-- Shade of the secondary neutral system color at 95% perceptual luminance (L* in L*a*b* color space).
This value can be overlaid at runtime by OverlayManager RROs. -->
<color name="system_neutral2_50">#EBF1F8</color>
- <!-- Shade of the secondary neutral system color at 90% lightness.
+ <!-- Shade of the secondary neutral system color at 90% perceptual luminance (L* in L*a*b* color space).
This value can be overlaid at runtime by OverlayManager RROs. -->
<color name="system_neutral2_100">#DCE3E9</color>
- <!-- Shade of the secondary neutral system color at 80% lightness.
+ <!-- Shade of the secondary neutral system color at 80% perceptual luminance (L* in L*a*b* color space).
This value can be overlaid at runtime by OverlayManager RROs. -->
<color name="system_neutral2_200">#C0C7CD</color>
- <!-- Shade of the secondary neutral system color at 70% lightness.
+ <!-- Shade of the secondary neutral system color at 70% perceptual luminance (L* in L*a*b* color space).
This value can be overlaid at runtime by OverlayManager RROs. -->
<color name="system_neutral2_300">#A5ACB2</color>
- <!-- Shade of the secondary neutral system color at 60% lightness.
+ <!-- Shade of the secondary neutral system color at 60% perceptual luminance (L* in L*a*b* color space).
This value can be overlaid at runtime by OverlayManager RROs. -->
<color name="system_neutral2_400">#8A9297</color>
- <!-- Shade of the secondary neutral system color at 49% lightness.
+ <!-- Shade of the secondary neutral system color at 49% perceptual luminance (L* in L*a*b* color space).
This value can be overlaid at runtime by OverlayManager RROs. -->
<color name="system_neutral2_500">#70777C</color>
- <!-- Shade of the secondary neutral system color at 40% lightness.
+ <!-- Shade of the secondary neutral system color at 40% perceptual luminance (L* in L*a*b* color space).
This value can be overlaid at runtime by OverlayManager RROs. -->
<color name="system_neutral2_600">#585F65</color>
- <!-- Shade of the secondary neutral system color at 30% lightness.
+ <!-- Shade of the secondary neutral system color at 30% perceptual luminance (L* in L*a*b* color space).
This value can be overlaid at runtime by OverlayManager RROs. -->
<color name="system_neutral2_700">#40484D</color>
- <!-- Shade of the secondary neutral system color at 20% lightness.
+ <!-- Shade of the secondary neutral system color at 20% perceptual luminance (L* in L*a*b* color space).
This value can be overlaid at runtime by OverlayManager RROs. -->
<color name="system_neutral2_800">#2A3136</color>
- <!-- Shade of the secondary neutral system color at 10% lightness.
+ <!-- Shade of the secondary neutral system color at 10% perceptual luminance (L* in L*a*b* color space).
This value can be overlaid at runtime by OverlayManager RROs. -->
<color name="system_neutral2_900">#161C20</color>
<!-- Darkest shade of the secondary neutral color used by the system. Black.
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 219d065..ddc9f02 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -2323,6 +2323,15 @@
<!-- Type of the tap sensor. Empty if tap is not supported. -->
<string name="config_dozeTapSensorType" translatable="false"></string>
+ <!-- Type of the ambient tap sensor per device posture (defined by WM Jetpack posture).
+ Unspecified values use config_dozeTapSensor -->
+ <string-array name="config_dozeTapSensorPostureMapping" translatable="false">
+ <item></item> <!-- UNKNOWN -->
+ <item></item> <!-- CLOSED -->
+ <item></item> <!-- HALF_OPENED -->
+ <item></item> <!-- OPENED -->
+ </string-array>
+
<!-- Type of the long press sensor. Empty if long press is not supported. -->
<string name="config_dozeLongPressSensorType" translatable="false"></string>
@@ -4915,16 +4924,15 @@
device orientation. -->
<bool name="config_letterboxIsReachabilityEnabled">false</bool>
- <!-- Default horizonal position of a center of the letterboxed app window when reachability is
- enabled and an app is fullscreen in landscape device orientation.
- 0 corresponds to the left side of the screen and 1 to the right side. If given value < 0.0
- or > 1, it is ignored and right positionis used (1.0). The position multiplier is changed
- to a symmetrical value computed as (1 - current multiplier) after each double tap in the
- letterbox area. -->
- <item name="config_letterboxDefaultPositionMultiplierForReachability"
- format="float" type="dimen">
- 0.9
- </item>
+ <!-- Default horizonal position of the letterboxed app window when reachability is
+ enabled and an app is fullscreen in landscape device orientation. When reachability is
+ enabled, the position can change between left, center and right. This config defines the
+ default one:
+ - Option 0 - Left.
+ - Option 1 - Center.
+ - Option 2 - Right.
+ If given value is outside of this range, the option 1 (center) is assummed. -->
+ <integer name="config_letterboxDefaultPositionForReachability">1</integer>
<!-- If true, hide the display cutout with display area -->
<bool name="config_hideDisplayCutoutWithDisplayArea">false</bool>
diff --git a/core/res/res/values/dimens.xml b/core/res/res/values/dimens.xml
index 9ed4e65..ab923d0 100644
--- a/core/res/res/values/dimens.xml
+++ b/core/res/res/values/dimens.xml
@@ -60,6 +60,9 @@
<!-- How much we expand the touchable region of the status bar below the notch to catch touches
that just start below the notch. -->
<dimen name="display_cutout_touchable_region_size">12dp</dimen>
+ <!-- The default margin used in immersive mode to capture the start of a swipe gesture from the
+ edge of the screen to show the system bars. -->
+ <dimen name="system_gestures_start_threshold">24dp</dimen>
<!-- Height of the bottom navigation bar frame; this is different than navigation_bar_height
where that is the height reported to all the other windows to resize themselves around the
diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml
index e17daf0..462b188 100644
--- a/core/res/res/values/public.xml
+++ b/core/res/res/values/public.xml
@@ -3221,7 +3221,7 @@
<eat-comment />
<staging-public-group type="attr" first-id="0x01ff0000">
- <public name="shouldUseDefaultDeviceStateChangeTransition" />
+ <public name="shouldUseDefaultDisplayStateChangeTransition" />
</staging-public-group>
<staging-public-group type="id" first-id="0x01fe0000">
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 4e8d915..758990d 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -1745,6 +1745,7 @@
<java-symbol type="dimen" name="taskbar_frame_height" />
<java-symbol type="dimen" name="status_bar_height" />
<java-symbol type="dimen" name="display_cutout_touchable_region_size" />
+ <java-symbol type="dimen" name="system_gestures_start_threshold" />
<java-symbol type="dimen" name="quick_qs_offset_height" />
<java-symbol type="drawable" name="ic_jog_dial_sound_off" />
<java-symbol type="drawable" name="ic_jog_dial_sound_on" />
@@ -3255,6 +3256,7 @@
<java-symbol type="string" name="config_dozeDoubleTapSensorType" />
<java-symbol type="string" name="config_dozeTapSensorType" />
+ <java-symbol type="array" name="config_dozeTapSensorPostureMapping" />
<java-symbol type="bool" name="config_dozePulsePickup" />
<!-- Used for MimeIconUtils. -->
@@ -4262,7 +4264,7 @@
<java-symbol type="color" name="config_letterboxBackgroundColor" />
<java-symbol type="dimen" name="config_letterboxHorizontalPositionMultiplier" />
<java-symbol type="bool" name="config_letterboxIsReachabilityEnabled" />
- <java-symbol type="dimen" name="config_letterboxDefaultPositionMultiplierForReachability" />
+ <java-symbol type="integer" name="config_letterboxDefaultPositionForReachability" />
<java-symbol type="bool" name="config_hideDisplayCutoutWithDisplayArea" />
diff --git a/core/res/res/xml/power_profile.xml b/core/res/res/xml/power_profile.xml
index 166edca..d310736 100644
--- a/core/res/res/xml/power_profile.xml
+++ b/core/res/res/xml/power_profile.xml
@@ -27,9 +27,33 @@
are totally dependent on the platform and can vary
significantly, so should be measured on the shipping platform
with a power meter. -->
- <item name="ambient.on">0.1</item> <!-- ~100mA -->
- <item name="screen.on">0.1</item> <!-- ~100mA -->
- <item name="screen.full">0.1</item> <!-- ~100mA -->
+
+ <!-- Display related values. -->
+ <!-- Average battery current draw of display0 while in ambient mode, including backlight.
+ There must be one of these for each display, labeled:
+ ambient.on.display0, ambient.on.display1, etc...
+
+ Each display suffix number should match it's ordinal in its display device config.
+ -->
+ <item name="ambient.on.display0">0.1</item> <!-- ~100mA -->
+ <!-- Average battery current draw of display0 while on without backlight.
+ There must be one of these for each display, labeled:
+ screen.on.display0, screen.on.display1, etc...
+
+ Each display suffix number should match it's ordinal in its display device config.
+ -->
+ <item name="screen.on.display0">0.1</item> <!-- ~100mA -->
+ <!-- Average battery current draw of the backlight at full brightness.
+ The full current draw of display N at full brightness should be the sum of screen.on.displayN
+ and screen.full.displayN
+
+ There must be one of these for each display, labeled:
+ screen.full.display0, screen.full.display1, etc...
+
+ Each display suffix number should match it's ordinal in its display device config.
+ -->
+ <item name="screen.full.display0">0.1</item> <!-- ~100mA -->
+
<item name="bluetooth.active">0.1</item> <!-- Bluetooth data transfer, ~10mA -->
<item name="bluetooth.on">0.1</item> <!-- Bluetooth on & connectable, but not connected, ~0.1mA -->
<item name="wifi.on">0.1</item> <!-- ~3mA -->
diff --git a/core/tests/coretests/src/android/app/servertransaction/ObjectPoolTests.java b/core/tests/coretests/src/android/app/servertransaction/ObjectPoolTests.java
index 50639be..3e261a7 100644
--- a/core/tests/coretests/src/android/app/servertransaction/ObjectPoolTests.java
+++ b/core/tests/coretests/src/android/app/servertransaction/ObjectPoolTests.java
@@ -157,7 +157,7 @@
.setPendingResults(resultInfoList()).setPendingNewIntents(referrerIntentList())
.setIsForward(true).setAssistToken(assistToken)
.setShareableActivityToken(shareableActivityToken)
- .setTaskFragmentToken(new Binder()).build();
+ .build();
LaunchActivityItem emptyItem = new LaunchActivityItemBuilder().build();
LaunchActivityItem item = itemSupplier.get();
diff --git a/core/tests/coretests/src/android/app/servertransaction/TestUtils.java b/core/tests/coretests/src/android/app/servertransaction/TestUtils.java
index 1173c92..75da0bf 100644
--- a/core/tests/coretests/src/android/app/servertransaction/TestUtils.java
+++ b/core/tests/coretests/src/android/app/servertransaction/TestUtils.java
@@ -112,7 +112,6 @@
private IBinder mShareableActivityToken;
private FixedRotationAdjustments mFixedRotationAdjustments;
private boolean mLaunchedFromBubble;
- private IBinder mTaskFragmentToken;
LaunchActivityItemBuilder setIntent(Intent intent) {
mIntent = intent;
@@ -214,18 +213,13 @@
return this;
}
- LaunchActivityItemBuilder setTaskFragmentToken(IBinder taskFragmentToken) {
- mTaskFragmentToken = taskFragmentToken;
- return this;
- }
-
LaunchActivityItem build() {
return LaunchActivityItem.obtain(mIntent, mIdent, mInfo,
mCurConfig, mOverrideConfig, mCompatInfo, mReferrer, mVoiceInteractor,
mProcState, mState, mPersistentState, mPendingResults, mPendingNewIntents,
mActivityOptions, mIsForward, mProfilerInfo, mAssistToken,
null /* activityClientController */, mFixedRotationAdjustments,
- mShareableActivityToken, mLaunchedFromBubble, mTaskFragmentToken);
+ mShareableActivityToken, mLaunchedFromBubble);
}
}
}
diff --git a/core/tests/coretests/src/android/app/servertransaction/TransactionParcelTests.java b/core/tests/coretests/src/android/app/servertransaction/TransactionParcelTests.java
index 98c9afd..df0c64c 100644
--- a/core/tests/coretests/src/android/app/servertransaction/TransactionParcelTests.java
+++ b/core/tests/coretests/src/android/app/servertransaction/TransactionParcelTests.java
@@ -209,7 +209,6 @@
.setPendingNewIntents(referrerIntentList()).setIsForward(true)
.setAssistToken(new Binder()).setFixedRotationAdjustments(fixedRotationAdjustments)
.setShareableActivityToken(new Binder())
- .setTaskFragmentToken(new Binder())
.build();
writeAndPrepareForReading(item);
diff --git a/core/tests/coretests/src/com/android/internal/os/AmbientDisplayPowerCalculatorTest.java b/core/tests/coretests/src/com/android/internal/os/AmbientDisplayPowerCalculatorTest.java
index 79f7a5c..d76037e 100644
--- a/core/tests/coretests/src/com/android/internal/os/AmbientDisplayPowerCalculatorTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/AmbientDisplayPowerCalculatorTest.java
@@ -16,6 +16,8 @@
package com.android.internal.os;
+import static com.android.internal.os.PowerProfile.POWER_GROUP_DISPLAY_AMBIENT;
+
import static com.google.common.truth.Truth.assertThat;
import android.os.BatteryConsumer;
@@ -36,7 +38,7 @@
@Rule
public final BatteryUsageStatsRule mStatsRule = new BatteryUsageStatsRule()
- .setAveragePower(PowerProfile.POWER_AMBIENT_DISPLAY, 10.0);
+ .setAveragePowerForOrdinal(POWER_GROUP_DISPLAY_AMBIENT, 0, 10.0);
@Test
public void testMeasuredEnergyBasedModel() {
diff --git a/core/tests/coretests/src/com/android/internal/os/BatteryUsageStatsRule.java b/core/tests/coretests/src/com/android/internal/os/BatteryUsageStatsRule.java
index 083090c..ab38f01 100644
--- a/core/tests/coretests/src/com/android/internal/os/BatteryUsageStatsRule.java
+++ b/core/tests/coretests/src/com/android/internal/os/BatteryUsageStatsRule.java
@@ -110,6 +110,14 @@
return this;
}
+ public BatteryUsageStatsRule setAveragePowerForOrdinal(String group, int ordinal,
+ double value) {
+ when(mPowerProfile.getAveragePowerForOrdinal(group, ordinal)).thenReturn(value);
+ when(mPowerProfile.getAveragePowerForOrdinal(eq(group), eq(ordinal),
+ anyDouble())).thenReturn(value);
+ return this;
+ }
+
/** Call only after setting the power profile information. */
public BatteryUsageStatsRule initMeasuredEnergyStatsLocked() {
return initMeasuredEnergyStatsLocked(new String[0]);
diff --git a/core/tests/coretests/src/com/android/internal/os/PowerProfileTest.java b/core/tests/coretests/src/com/android/internal/os/PowerProfileTest.java
index 5862368..88ee405 100644
--- a/core/tests/coretests/src/com/android/internal/os/PowerProfileTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/PowerProfileTest.java
@@ -17,6 +17,10 @@
package com.android.internal.os;
+import static com.android.internal.os.PowerProfile.POWER_GROUP_DISPLAY_AMBIENT;
+import static com.android.internal.os.PowerProfile.POWER_GROUP_DISPLAY_SCREEN_FULL;
+import static com.android.internal.os.PowerProfile.POWER_GROUP_DISPLAY_SCREEN_ON;
+
import androidx.test.InstrumentationRegistry;
import androidx.test.filters.SmallTest;
@@ -53,7 +57,12 @@
assertEquals(4, mProfile.getNumSpeedStepsInCpuCluster(1));
assertEquals(60.0, mProfile.getAveragePowerForCpuCore(1, 3));
assertEquals(3000.0, mProfile.getBatteryCapacity());
- assertEquals(0.5, mProfile.getAveragePower(PowerProfile.POWER_AMBIENT_DISPLAY));
+ assertEquals(0.5,
+ mProfile.getAveragePowerForOrdinal(POWER_GROUP_DISPLAY_AMBIENT, 0));
+ assertEquals(100.0,
+ mProfile.getAveragePowerForOrdinal(POWER_GROUP_DISPLAY_SCREEN_ON, 0));
+ assertEquals(800.0,
+ mProfile.getAveragePowerForOrdinal(POWER_GROUP_DISPLAY_SCREEN_FULL, 0));
assertEquals(100.0, mProfile.getAveragePower(PowerProfile.POWER_AUDIO));
assertEquals(150.0, mProfile.getAveragePower(PowerProfile.POWER_VIDEO));
}
diff --git a/core/tests/coretests/src/com/android/internal/os/ScreenPowerCalculatorTest.java b/core/tests/coretests/src/com/android/internal/os/ScreenPowerCalculatorTest.java
index c695fc9..50e0a15 100644
--- a/core/tests/coretests/src/com/android/internal/os/ScreenPowerCalculatorTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/ScreenPowerCalculatorTest.java
@@ -16,6 +16,9 @@
package com.android.internal.os;
+import static com.android.internal.os.PowerProfile.POWER_GROUP_DISPLAY_SCREEN_FULL;
+import static com.android.internal.os.PowerProfile.POWER_GROUP_DISPLAY_SCREEN_ON;
+
import static com.google.common.truth.Truth.assertThat;
import android.app.ActivityManager;
@@ -42,8 +45,8 @@
@Rule
public final BatteryUsageStatsRule mStatsRule = new BatteryUsageStatsRule()
- .setAveragePower(PowerProfile.POWER_SCREEN_ON, 36.0)
- .setAveragePower(PowerProfile.POWER_SCREEN_FULL, 48.0);
+ .setAveragePowerForOrdinal(POWER_GROUP_DISPLAY_SCREEN_ON, 0, 36.0)
+ .setAveragePowerForOrdinal(POWER_GROUP_DISPLAY_SCREEN_FULL, 0, 48.0);
@Test
public void testMeasuredEnergyBasedModel() {
diff --git a/core/tests/mockingcoretests/src/android/app/activity/ActivityThreadClientTest.java b/core/tests/mockingcoretests/src/android/app/activity/ActivityThreadClientTest.java
index 516a5d2..269d842 100644
--- a/core/tests/mockingcoretests/src/android/app/activity/ActivityThreadClientTest.java
+++ b/core/tests/mockingcoretests/src/android/app/activity/ActivityThreadClientTest.java
@@ -299,7 +299,7 @@
null /* activityOptions */, true /* isForward */, null /* profilerInfo */,
mThread /* client */, null /* asssitToken */,
null /* fixedRotationAdjustments */, null /* shareableActivityToken */,
- false /* launchedFromBubble */, null /* taskfragmentToken */);
+ false /* launchedFromBubble */);
}
@Override
diff --git a/data/etc/car/com.android.car.carlauncher.xml b/data/etc/car/com.android.car.carlauncher.xml
index 33f885a..53d02a4 100644
--- a/data/etc/car/com.android.car.carlauncher.xml
+++ b/data/etc/car/com.android.car.carlauncher.xml
@@ -24,5 +24,6 @@
<permission name="android.permission.MEDIA_CONTENT_CONTROL"/>
<permission name="android.permission.PACKAGE_USAGE_STATS"/>
<permission name="android.car.permission.ACCESS_CAR_PROJECTION_STATUS"/>
+ <permission name="android.car.permission.CONTROL_CAR_APP_LAUNCH"/>
</privapp-permissions>
</permissions>
diff --git a/data/etc/car/com.google.android.car.kitchensink.xml b/data/etc/car/com.google.android.car.kitchensink.xml
index 8705067..ab162dd5 100644
--- a/data/etc/car/com.google.android.car.kitchensink.xml
+++ b/data/etc/car/com.google.android.car.kitchensink.xml
@@ -74,6 +74,8 @@
<permission name="android.car.permission.CAR_TEST_SERVICE"/>
<permission name="android.car.permission.CAR_UX_RESTRICTIONS_CONFIGURATION"/>
<permission name="android.car.permission.CAR_VENDOR_EXTENSION"/>
+ <!-- use for AndroidCarApiTest -->
+ <permission name="android.car.permission.CONTROL_CAR_APP_LAUNCH"/>
<permission name="android.car.permission.CONTROL_CAR_CLIMATE"/>
<permission name="android.car.permission.CONTROL_CAR_DOORS"/>
<permission name="android.car.permission.CONTROL_CAR_EXTERIOR_LIGHTS"/>
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/WindowExtensionsImpl.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/WindowExtensionsImpl.java
index 990d7b6..bdf703c 100644
--- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/WindowExtensionsImpl.java
+++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/WindowExtensionsImpl.java
@@ -39,11 +39,12 @@
return 1;
}
- @Override
- public boolean isWindowLayoutComponentAvailable() {
- return true;
- }
-
+ /**
+ * Returns a reference implementation of {@link WindowLayoutComponent} if available,
+ * {@code null} otherwise. The implementation must match the API level reported in
+ * {@link WindowExtensions#getWindowLayoutComponent()}.
+ * @return {@link WindowLayoutComponent} OEM implementation
+ */
@Override
public WindowLayoutComponent getWindowLayoutComponent() {
if (mWindowLayoutComponent == null) {
@@ -58,24 +59,10 @@
}
/**
- * Returns {@code true} if {@link ActivityEmbeddingComponent} is present on the device,
- * {@code false} otherwise. If the component is not available the developer will receive a
- * single callback with empty data or default values where possible.
- */
- @Override
- public boolean isEmbeddingComponentAvailable() {
- return true;
- }
-
- /**
- * Returns the OEM implementation of {@link ActivityEmbeddingComponent} if it is supported on
- * the device. The implementation must match the API level reported in
- * {@link androidx.window.extensions.WindowExtensions}. An
- * {@link UnsupportedOperationException} will be thrown if the device does not support
- * Activity Embedding. Use
- * {@link WindowExtensions#isEmbeddingComponentAvailable()} to determine if
- * {@link ActivityEmbeddingComponent} is present.
- * @return the OEM implementation of {@link ActivityEmbeddingComponent}
+ * Returns a reference implementation of {@link ActivityEmbeddingComponent} if available,
+ * {@code null} otherwise. The implementation must match the API level reported in
+ * {@link WindowExtensions#getWindowLayoutComponent()}.
+ * @return {@link ActivityEmbeddingComponent} OEM implementation.
*/
@NonNull
public ActivityEmbeddingComponent getActivityEmbeddingComponent() {
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java
index e1c8b11..42b4380 100644
--- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java
+++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java
@@ -172,7 +172,7 @@
void handleActivityCreated(@NonNull Activity launchedActivity) {
final List<EmbeddingRule> splitRules = getSplitRules();
final TaskFragmentContainer currentContainer = getContainerWithActivity(
- launchedActivity.getActivityToken(), launchedActivity);
+ launchedActivity.getActivityToken());
// Check if the activity is configured to always be expanded.
if (shouldExpand(launchedActivity, null, splitRules)) {
@@ -262,29 +262,9 @@
*/
@Nullable
TaskFragmentContainer getContainerWithActivity(@NonNull IBinder activityToken) {
- return getContainerWithActivity(activityToken, null /* activityToAdd */);
- }
-
- /**
- * This method can only be called from {@link #onActivityCreated(Activity)}, use
- * {@link #getContainerWithActivity(IBinder) } otherwise.
- *
- * Returns a container that this activity is registered with. The activity could be created
- * before the container appeared, adding the activity to the container if so.
- */
- @Nullable
- private TaskFragmentContainer getContainerWithActivity(@NonNull IBinder activityToken,
- Activity activityToAdd) {
- final IBinder taskFragmentToken = ActivityThread.currentActivityThread().getActivityClient(
- activityToken).mInitialTaskFragmentToken;
for (TaskFragmentContainer container : mContainers) {
if (container.hasActivity(activityToken)) {
return container;
- } else if (container.getTaskFragmentToken().equals(taskFragmentToken)) {
- if (activityToAdd != null) {
- container.addPendingAppearedActivity(activityToAdd);
- }
- return container;
}
}
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitPresenter.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitPresenter.java
index 25292b9..81be21c 100644
--- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitPresenter.java
+++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitPresenter.java
@@ -252,7 +252,8 @@
// Getting the parent bounds using the updated container - it will have the recent value.
final Rect parentBounds = getParentContainerBounds(updatedContainer);
final SplitRule rule = splitContainer.getSplitRule();
- final Activity activity = splitContainer.getPrimaryContainer().getTopNonFinishingActivity();
+ final TaskFragmentContainer primaryContainer = splitContainer.getPrimaryContainer();
+ final Activity activity = primaryContainer.getTopNonFinishingActivity();
if (activity == null) {
return;
}
@@ -264,10 +265,12 @@
// If the task fragments are not registered yet, the positions will be updated after they
// are created again.
- resizeTaskFragmentIfRegistered(wct, splitContainer.getPrimaryContainer(),
- primaryRectBounds);
- resizeTaskFragmentIfRegistered(wct, splitContainer.getSecondaryContainer(),
- secondaryRectBounds);
+ resizeTaskFragmentIfRegistered(wct, primaryContainer, primaryRectBounds);
+ final TaskFragmentContainer secondaryContainer = splitContainer.getSecondaryContainer();
+ resizeTaskFragmentIfRegistered(wct, secondaryContainer, secondaryRectBounds);
+
+ setAdjacentTaskFragments(wct, primaryContainer.getTaskFragmentToken(),
+ secondaryContainer.getTaskFragmentToken(), rule);
}
/**
diff --git a/libs/WindowManager/Jetpack/window-extensions-release.aar b/libs/WindowManager/Jetpack/window-extensions-release.aar
index 42e829e..4f36c9c 100644
--- a/libs/WindowManager/Jetpack/window-extensions-release.aar
+++ b/libs/WindowManager/Jetpack/window-extensions-release.aar
Binary files differ
diff --git a/libs/WindowManager/Shell/Android.bp b/libs/WindowManager/Shell/Android.bp
index 9aaef3b..3ba1a34 100644
--- a/libs/WindowManager/Shell/Android.bp
+++ b/libs/WindowManager/Shell/Android.bp
@@ -39,6 +39,14 @@
}
filegroup {
+ name: "wm_shell_util-sources",
+ srcs: [
+ "src/com/android/wm/shell/util/**/*.java",
+ ],
+ path: "src",
+}
+
+filegroup {
name: "wm_shell-aidls",
srcs: [
"src/**/*.aidl",
diff --git a/libs/WindowManager/Shell/res/color/unfold_transition_background.xml b/libs/WindowManager/Shell/res/color/unfold_transition_background.xml
new file mode 100644
index 0000000..63289a3
--- /dev/null
+++ b/libs/WindowManager/Shell/res/color/unfold_transition_background.xml
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2021 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+ <!-- Matches taskbar color -->
+ <item android:color="@android:color/system_neutral2_500" android:lStar="35" />
+</selector>
diff --git a/libs/WindowManager/Shell/res/layout/split_outline.xml b/libs/WindowManager/Shell/res/layout/split_outline.xml
index 4e2a77f..13a30f5 100644
--- a/libs/WindowManager/Shell/res/layout/split_outline.xml
+++ b/libs/WindowManager/Shell/res/layout/split_outline.xml
@@ -13,7 +13,7 @@
See the License for the specific language governing permissions and
limitations under the License.
-->
-<com.android.wm.shell.splitscreen.OutlineRoot
+<FrameLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
@@ -23,4 +23,4 @@
android:layout_height="match_parent"
android:layout_width="match_parent" />
-</com.android.wm.shell.splitscreen.OutlineRoot>
+</FrameLayout>
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/FullscreenTaskListener.java b/libs/WindowManager/Shell/src/com/android/wm/shell/FullscreenTaskListener.java
deleted file mode 100644
index 006730d..0000000
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/FullscreenTaskListener.java
+++ /dev/null
@@ -1,128 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.wm.shell;
-
-import static com.android.wm.shell.ShellTaskOrganizer.TASK_LISTENER_TYPE_FULLSCREEN;
-import static com.android.wm.shell.ShellTaskOrganizer.taskListenerTypeToString;
-
-import android.app.ActivityManager;
-import android.graphics.Point;
-import android.util.Slog;
-import android.util.SparseArray;
-import android.view.SurfaceControl;
-
-import androidx.annotation.NonNull;
-
-import com.android.internal.protolog.common.ProtoLog;
-import com.android.wm.shell.common.SyncTransactionQueue;
-import com.android.wm.shell.protolog.ShellProtoLogGroup;
-import com.android.wm.shell.transition.Transitions;
-
-import java.io.PrintWriter;
-
-/**
- * Organizes tasks presented in {@link android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN}.
- */
-public class FullscreenTaskListener implements ShellTaskOrganizer.TaskListener {
- private static final String TAG = "FullscreenTaskListener";
-
- private final SyncTransactionQueue mSyncQueue;
-
- private final SparseArray<TaskData> mDataByTaskId = new SparseArray<>();
-
- public FullscreenTaskListener(SyncTransactionQueue syncQueue) {
- mSyncQueue = syncQueue;
- }
-
- @Override
- public void onTaskAppeared(ActivityManager.RunningTaskInfo taskInfo, SurfaceControl leash) {
- if (mDataByTaskId.get(taskInfo.taskId) != null) {
- throw new IllegalStateException("Task appeared more than once: #" + taskInfo.taskId);
- }
- ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TASK_ORG, "Fullscreen Task Appeared: #%d",
- taskInfo.taskId);
- final Point positionInParent = taskInfo.positionInParent;
- mDataByTaskId.put(taskInfo.taskId, new TaskData(leash, positionInParent));
- if (Transitions.ENABLE_SHELL_TRANSITIONS) return;
- mSyncQueue.runInSync(t -> {
- // Reset several properties back to fullscreen (PiP, for example, leaves all these
- // properties in a bad state).
- t.setWindowCrop(leash, null);
- t.setPosition(leash, positionInParent.x, positionInParent.y);
- t.setAlpha(leash, 1f);
- t.setMatrix(leash, 1, 0, 0, 1);
- t.show(leash);
- });
- }
-
- @Override
- public void onTaskInfoChanged(ActivityManager.RunningTaskInfo taskInfo) {
- if (Transitions.ENABLE_SHELL_TRANSITIONS) return;
- final TaskData data = mDataByTaskId.get(taskInfo.taskId);
- final Point positionInParent = taskInfo.positionInParent;
- if (!positionInParent.equals(data.positionInParent)) {
- data.positionInParent.set(positionInParent.x, positionInParent.y);
- mSyncQueue.runInSync(t -> {
- t.setPosition(data.surface, positionInParent.x, positionInParent.y);
- });
- }
- }
-
- @Override
- public void onTaskVanished(ActivityManager.RunningTaskInfo taskInfo) {
- if (mDataByTaskId.get(taskInfo.taskId) == null) {
- Slog.e(TAG, "Task already vanished: #" + taskInfo.taskId);
- return;
- }
- mDataByTaskId.remove(taskInfo.taskId);
- ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TASK_ORG, "Fullscreen Task Vanished: #%d",
- taskInfo.taskId);
- }
-
- @Override
- public void attachChildSurfaceToTask(int taskId, SurfaceControl.Builder b) {
- if (!mDataByTaskId.contains(taskId)) {
- throw new IllegalArgumentException("There is no surface for taskId=" + taskId);
- }
- b.setParent(mDataByTaskId.get(taskId).surface);
- }
-
- @Override
- public void dump(@NonNull PrintWriter pw, String prefix) {
- final String innerPrefix = prefix + " ";
- pw.println(prefix + this);
- pw.println(innerPrefix + mDataByTaskId.size() + " Tasks");
- }
-
- @Override
- public String toString() {
- return TAG + ":" + taskListenerTypeToString(TASK_LISTENER_TYPE_FULLSCREEN);
- }
-
- /**
- * Per-task data for each managed task.
- */
- private static class TaskData {
- public final SurfaceControl surface;
- public final Point positionInParent;
-
- public TaskData(SurfaceControl surface, Point positionInParent) {
- this.surface = surface;
- this.positionInParent = positionInParent;
- }
- }
-}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/ShellInitImpl.java b/libs/WindowManager/Shell/src/com/android/wm/shell/ShellInitImpl.java
index df4f238..fa58fcd 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/ShellInitImpl.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/ShellInitImpl.java
@@ -27,6 +27,8 @@
import com.android.wm.shell.common.annotations.ExternalThread;
import com.android.wm.shell.draganddrop.DragAndDropController;
import com.android.wm.shell.freeform.FreeformTaskListener;
+import com.android.wm.shell.fullscreen.FullscreenTaskListener;
+import com.android.wm.shell.fullscreen.FullscreenUnfoldController;
import com.android.wm.shell.legacysplitscreen.LegacySplitScreenController;
import com.android.wm.shell.pip.phone.PipTouchHandler;
import com.android.wm.shell.splitscreen.SplitScreenController;
@@ -52,6 +54,7 @@
private final Optional<AppPairsController> mAppPairsOptional;
private final Optional<PipTouchHandler> mPipTouchHandlerOptional;
private final FullscreenTaskListener mFullscreenTaskListener;
+ private final Optional<FullscreenUnfoldController> mFullscreenUnfoldController;
private final Optional<FreeformTaskListener> mFreeformTaskListenerOptional;
private final ShellExecutor mMainExecutor;
private final Transitions mTransitions;
@@ -71,6 +74,7 @@
Optional<AppPairsController> appPairsOptional,
Optional<PipTouchHandler> pipTouchHandlerOptional,
FullscreenTaskListener fullscreenTaskListener,
+ Optional<FullscreenUnfoldController> fullscreenUnfoldTransitionController,
Optional<Optional<FreeformTaskListener>> freeformTaskListenerOptional,
Transitions transitions,
StartingWindowController startingWindow,
@@ -86,6 +90,7 @@
mAppPairsOptional = appPairsOptional;
mFullscreenTaskListener = fullscreenTaskListener;
mPipTouchHandlerOptional = pipTouchHandlerOptional;
+ mFullscreenUnfoldController = fullscreenUnfoldTransitionController;
mFreeformTaskListenerOptional = freeformTaskListenerOptional.flatMap(f -> f);
mTransitions = transitions;
mMainExecutor = mainExecutor;
@@ -128,6 +133,8 @@
mFreeformTaskListenerOptional.ifPresent(f ->
mShellTaskOrganizer.addListenerForType(
f, ShellTaskOrganizer.TASK_LISTENER_TYPE_FREEFORM));
+
+ mFullscreenUnfoldController.ifPresent(FullscreenUnfoldController::init);
}
@ExternalThread
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/ShellTaskOrganizer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/ShellTaskOrganizer.java
index d326534..020ecb7 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/ShellTaskOrganizer.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/ShellTaskOrganizer.java
@@ -42,6 +42,7 @@
import android.view.SurfaceControl;
import android.window.ITaskOrganizerController;
import android.window.StartingWindowInfo;
+import android.window.StartingWindowRemovalInfo;
import android.window.TaskAppearedInfo;
import android.window.TaskOrganizer;
@@ -233,14 +234,14 @@
+ " already exists");
}
mTaskListeners.put(listenerType, listener);
+ }
- // Notify the listener of all existing tasks with the given type.
- for (int i = mTasks.size() - 1; i >= 0; --i) {
- final TaskAppearedInfo data = mTasks.valueAt(i);
- final TaskListener taskListener = getTaskListener(data.getTaskInfo());
- if (taskListener != listener) continue;
- listener.onTaskAppeared(data.getTaskInfo(), data.getLeash());
- }
+ // Notify the listener of all existing tasks with the given type.
+ for (int i = mTasks.size() - 1; i >= 0; --i) {
+ final TaskAppearedInfo data = mTasks.valueAt(i);
+ final TaskListener taskListener = getTaskListener(data.getTaskInfo());
+ if (taskListener != listener) continue;
+ listener.onTaskAppeared(data.getTaskInfo(), data.getLeash());
}
}
}
@@ -266,8 +267,12 @@
tasks.add(data);
}
- // Remove listener
- mTaskListeners.removeAt(index);
+ // Remove listener, there can be the multiple occurrences, so search the whole list.
+ for (int i = mTaskListeners.size() - 1; i >= 0; --i) {
+ if (mTaskListeners.valueAt(i) == listener) {
+ mTaskListeners.removeAt(i);
+ }
+ }
// Associate tasks with new listeners if needed.
for (int i = tasks.size() - 1; i >= 0; --i) {
@@ -318,10 +323,9 @@
}
@Override
- public void removeStartingWindow(int taskId, SurfaceControl leash, Rect frame,
- boolean playRevealAnimation) {
+ public void removeStartingWindow(StartingWindowRemovalInfo removalInfo) {
if (mStartingWindow != null) {
- mStartingWindow.removeStartingWindow(taskId, leash, frame, playRevealAnimation);
+ mStartingWindow.removeStartingWindow(removalInfo);
}
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/apppairs/AppPair.java b/libs/WindowManager/Shell/src/com/android/wm/shell/apppairs/AppPair.java
index c2cb72a..10d7725 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/apppairs/AppPair.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/apppairs/AppPair.java
@@ -27,7 +27,6 @@
import static com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_TASK_ORG;
import android.app.ActivityManager;
-import android.graphics.Rect;
import android.view.SurfaceControl;
import android.view.SurfaceSession;
import android.window.WindowContainerToken;
@@ -40,6 +39,7 @@
import com.android.wm.shell.ShellTaskOrganizer;
import com.android.wm.shell.common.DisplayController;
import com.android.wm.shell.common.DisplayImeController;
+import com.android.wm.shell.common.DisplayInsetsController;
import com.android.wm.shell.common.SurfaceUtils;
import com.android.wm.shell.common.SyncTransactionQueue;
import com.android.wm.shell.common.split.SplitLayout;
@@ -69,6 +69,7 @@
private final SyncTransactionQueue mSyncQueue;
private final DisplayController mDisplayController;
private final DisplayImeController mDisplayImeController;
+ private final DisplayInsetsController mDisplayInsetsController;
private SplitLayout mSplitLayout;
private final SplitWindowManager.ParentContainerCallbacks mParentContainerCallbacks =
@@ -80,7 +81,12 @@
@Override
public void onLeashReady(SurfaceControl leash) {
- mSyncQueue.runInSync(t -> t.show(leash));
+ mSyncQueue.runInSync(t -> t
+ .show(leash)
+ .setLayer(leash, SPLIT_DIVIDER_LAYER)
+ .setPosition(leash,
+ mSplitLayout.getDividerBounds().left,
+ mSplitLayout.getDividerBounds().top));
}
};
@@ -89,6 +95,7 @@
mSyncQueue = controller.getSyncTransactionQueue();
mDisplayController = controller.getDisplayController();
mDisplayImeController = controller.getDisplayImeController();
+ mDisplayInsetsController = controller.getDisplayInsetsController();
}
int getRootTaskId() {
@@ -125,6 +132,7 @@
mDisplayController.getDisplayContext(mRootTaskInfo.displayId),
mRootTaskInfo.configuration, this /* layoutChangeListener */,
mParentContainerCallbacks, mDisplayImeController, mController.getTaskOrganizer());
+ mDisplayInsetsController.addInsetsChangedListener(mRootTaskInfo.displayId, mSplitLayout);
final WindowContainerToken token1 = task1.token;
final WindowContainerToken token2 = task2.token;
@@ -190,22 +198,17 @@
if (mTaskLeash1 == null || mTaskLeash2 == null) return;
mSplitLayout.init();
- final SurfaceControl dividerLeash = mSplitLayout.getDividerLeash();
- final Rect dividerBounds = mSplitLayout.getDividerBounds();
- // TODO: Is there more we need to do here?
- mSyncQueue.runInSync(t -> {
- t.setLayer(dividerLeash, SPLIT_DIVIDER_LAYER)
- .setPosition(mTaskLeash1, mTaskInfo1.positionInParent.x,
- mTaskInfo1.positionInParent.y)
- .setPosition(mTaskLeash2, mTaskInfo2.positionInParent.x,
- mTaskInfo2.positionInParent.y)
- .setPosition(dividerLeash, dividerBounds.left, dividerBounds.top)
- .show(dividerLeash)
- .show(mRootTaskLeash)
- .show(mTaskLeash1)
- .show(mTaskLeash2);
- });
+ mSyncQueue.runInSync(t -> t
+ .show(mRootTaskLeash)
+ .show(mTaskLeash1)
+ .show(mTaskLeash2)
+ .setPosition(mTaskLeash1,
+ mTaskInfo1.positionInParent.x,
+ mTaskInfo1.positionInParent.y)
+ .setPosition(mTaskLeash2,
+ mTaskInfo2.positionInParent.x,
+ mTaskInfo2.positionInParent.y));
}
@Override
@@ -227,10 +230,9 @@
}
mRootTaskInfo = taskInfo;
- if (mSplitLayout != null) {
- if (mSplitLayout.updateConfiguration(mRootTaskInfo.configuration)) {
- onLayoutChanged(mSplitLayout);
- }
+ if (mSplitLayout != null
+ && mSplitLayout.updateConfiguration(mRootTaskInfo.configuration)) {
+ onLayoutChanged(mSplitLayout);
}
} else if (taskInfo.taskId == getTaskId1()) {
mTaskInfo1 = taskInfo;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/apppairs/AppPairsController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/apppairs/AppPairsController.java
index b159333..53234ab 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/apppairs/AppPairsController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/apppairs/AppPairsController.java
@@ -29,6 +29,7 @@
import com.android.wm.shell.ShellTaskOrganizer;
import com.android.wm.shell.common.DisplayController;
import com.android.wm.shell.common.DisplayImeController;
+import com.android.wm.shell.common.DisplayInsetsController;
import com.android.wm.shell.common.ShellExecutor;
import com.android.wm.shell.common.SyncTransactionQueue;
@@ -50,14 +51,17 @@
private final SparseArray<AppPair> mActiveAppPairs = new SparseArray<>();
private final DisplayController mDisplayController;
private final DisplayImeController mDisplayImeController;
+ private final DisplayInsetsController mDisplayInsetsController;
public AppPairsController(ShellTaskOrganizer organizer, SyncTransactionQueue syncQueue,
DisplayController displayController, ShellExecutor mainExecutor,
- DisplayImeController displayImeController) {
+ DisplayImeController displayImeController,
+ DisplayInsetsController displayInsetsController) {
mTaskOrganizer = organizer;
mSyncQueue = syncQueue;
mDisplayController = displayController;
mDisplayImeController = displayImeController;
+ mDisplayInsetsController = displayInsetsController;
mMainExecutor = mainExecutor;
}
@@ -148,6 +152,10 @@
return mDisplayImeController;
}
+ DisplayInsetsController getDisplayInsetsController() {
+ return mDisplayInsetsController;
+ }
+
public void dump(@NonNull PrintWriter pw, String prefix) {
final String innerPrefix = prefix + " ";
final String childPrefix = innerPrefix + " ";
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleData.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleData.java
index b48bda3..bef26bf 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleData.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleData.java
@@ -558,6 +558,8 @@
}
Bubble bubbleToRemove = mBubbles.get(indexToRemove);
bubbleToRemove.stopInflation();
+ overflowBubble(reason, bubbleToRemove);
+
if (mBubbles.size() == 1) {
if (hasOverflowBubbles() && (mPositioner.showingInTaskbar() || isExpanded())) {
// No more active bubbles but we have stuff in the overflow -- select that view
@@ -581,8 +583,6 @@
mStateChange.orderChanged |= repackAll();
}
- overflowBubble(reason, bubbleToRemove);
-
// Note: If mBubbles.isEmpty(), then mSelectedBubble is now null.
if (Objects.equals(mSelectedBubble, bubbleToRemove)) {
// Move selection to the new bubble at the same position.
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java
index 1c308a3..596a2f4 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java
@@ -189,24 +189,28 @@
final int rotation = configuration.windowConfiguration.getRotation();
final Rect rootBounds = configuration.windowConfiguration.getBounds();
final int orientation = configuration.orientation;
- if (rotation != mRotation || !mRootBounds.equals(rootBounds)
- || orientation != mOrientation) {
- mContext = mContext.createConfigurationContext(configuration);
- mSplitWindowManager.setConfiguration(configuration);
- mOrientation = orientation;
- mTempRect.set(mRootBounds);
- mRootBounds.set(rootBounds);
- mDividerSnapAlgorithm = getSnapAlgorithm(mContext, mRootBounds);
- initDividerPosition(mTempRect);
- affectsLayout = true;
+
+ if (mOrientation == orientation
+ && rotation == mRotation
+ && mRootBounds.equals(rootBounds)) {
+ return false;
}
+ mContext = mContext.createConfigurationContext(configuration);
+ mSplitWindowManager.setConfiguration(configuration);
+ mOrientation = orientation;
+ mTempRect.set(mRootBounds);
+ mRootBounds.set(rootBounds);
+ mRotation = rotation;
+ mDividerSnapAlgorithm = getSnapAlgorithm(mContext, mRootBounds);
+ initDividerPosition(mTempRect);
+
if (mInitialized) {
release();
init();
}
- return affectsLayout;
+ return true;
}
private void initDividerPosition(Rect oldBounds) {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/fullscreen/FullscreenTaskListener.java b/libs/WindowManager/Shell/src/com/android/wm/shell/fullscreen/FullscreenTaskListener.java
new file mode 100644
index 0000000..3f17f2b
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/fullscreen/FullscreenTaskListener.java
@@ -0,0 +1,207 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell.fullscreen;
+
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
+
+import static com.android.wm.shell.ShellTaskOrganizer.TASK_LISTENER_TYPE_FULLSCREEN;
+import static com.android.wm.shell.ShellTaskOrganizer.taskListenerTypeToString;
+
+import android.app.ActivityManager.RunningTaskInfo;
+import android.app.TaskInfo;
+import android.graphics.Point;
+import android.util.Slog;
+import android.util.SparseArray;
+import android.util.SparseBooleanArray;
+import android.view.SurfaceControl;
+
+import androidx.annotation.NonNull;
+
+import com.android.internal.protolog.common.ProtoLog;
+import com.android.wm.shell.ShellTaskOrganizer;
+import com.android.wm.shell.common.SyncTransactionQueue;
+import com.android.wm.shell.protolog.ShellProtoLogGroup;
+import com.android.wm.shell.transition.Transitions;
+
+import java.io.PrintWriter;
+import java.util.Optional;
+
+/**
+ * Organizes tasks presented in {@link android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN}.
+ */
+public class FullscreenTaskListener implements ShellTaskOrganizer.TaskListener {
+ private static final String TAG = "FullscreenTaskListener";
+
+ private final SyncTransactionQueue mSyncQueue;
+
+ private final SparseArray<TaskData> mDataByTaskId = new SparseArray<>();
+ private final AnimatableTasksListener mAnimatableTasksListener = new AnimatableTasksListener();
+ private final FullscreenUnfoldController mFullscreenUnfoldController;
+
+ public FullscreenTaskListener(SyncTransactionQueue syncQueue,
+ Optional<FullscreenUnfoldController> unfoldController) {
+ mSyncQueue = syncQueue;
+ mFullscreenUnfoldController = unfoldController.orElse(null);
+ }
+
+ @Override
+ public void onTaskAppeared(RunningTaskInfo taskInfo, SurfaceControl leash) {
+ if (mDataByTaskId.get(taskInfo.taskId) != null) {
+ throw new IllegalStateException("Task appeared more than once: #" + taskInfo.taskId);
+ }
+ ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TASK_ORG, "Fullscreen Task Appeared: #%d",
+ taskInfo.taskId);
+ final Point positionInParent = taskInfo.positionInParent;
+ mDataByTaskId.put(taskInfo.taskId, new TaskData(leash, positionInParent));
+ if (Transitions.ENABLE_SHELL_TRANSITIONS) return;
+ mSyncQueue.runInSync(t -> {
+ // Reset several properties back to fullscreen (PiP, for example, leaves all these
+ // properties in a bad state).
+ t.setWindowCrop(leash, null);
+ t.setPosition(leash, positionInParent.x, positionInParent.y);
+ t.setAlpha(leash, 1f);
+ t.setMatrix(leash, 1, 0, 0, 1);
+ t.show(leash);
+ });
+
+ mAnimatableTasksListener.onTaskAppeared(taskInfo);
+ }
+
+ @Override
+ public void onTaskInfoChanged(RunningTaskInfo taskInfo) {
+ if (Transitions.ENABLE_SHELL_TRANSITIONS) return;
+
+ mAnimatableTasksListener.onTaskInfoChanged(taskInfo);
+
+ final TaskData data = mDataByTaskId.get(taskInfo.taskId);
+ final Point positionInParent = taskInfo.positionInParent;
+ if (!positionInParent.equals(data.positionInParent)) {
+ data.positionInParent.set(positionInParent.x, positionInParent.y);
+ mSyncQueue.runInSync(t -> {
+ t.setPosition(data.surface, positionInParent.x, positionInParent.y);
+ });
+ }
+ }
+
+ @Override
+ public void onTaskVanished(RunningTaskInfo taskInfo) {
+ if (mDataByTaskId.get(taskInfo.taskId) == null) {
+ Slog.e(TAG, "Task already vanished: #" + taskInfo.taskId);
+ return;
+ }
+
+ mAnimatableTasksListener.onTaskVanished(taskInfo);
+ mDataByTaskId.remove(taskInfo.taskId);
+
+ ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TASK_ORG, "Fullscreen Task Vanished: #%d",
+ taskInfo.taskId);
+ }
+
+ @Override
+ public void attachChildSurfaceToTask(int taskId, SurfaceControl.Builder b) {
+ if (!mDataByTaskId.contains(taskId)) {
+ throw new IllegalArgumentException("There is no surface for taskId=" + taskId);
+ }
+ b.setParent(mDataByTaskId.get(taskId).surface);
+ }
+
+ @Override
+ public void dump(@NonNull PrintWriter pw, String prefix) {
+ final String innerPrefix = prefix + " ";
+ pw.println(prefix + this);
+ pw.println(innerPrefix + mDataByTaskId.size() + " Tasks");
+ }
+
+ @Override
+ public String toString() {
+ return TAG + ":" + taskListenerTypeToString(TASK_LISTENER_TYPE_FULLSCREEN);
+ }
+
+ /**
+ * Per-task data for each managed task.
+ */
+ private static class TaskData {
+ public final SurfaceControl surface;
+ public final Point positionInParent;
+
+ public TaskData(SurfaceControl surface, Point positionInParent) {
+ this.surface = surface;
+ this.positionInParent = positionInParent;
+ }
+ }
+
+ class AnimatableTasksListener {
+ private final SparseBooleanArray mTaskIds = new SparseBooleanArray();
+
+ public void onTaskAppeared(RunningTaskInfo taskInfo) {
+ final boolean isApplicable = isAnimatable(taskInfo);
+ if (isApplicable) {
+ mTaskIds.put(taskInfo.taskId, true);
+
+ if (mFullscreenUnfoldController != null) {
+ SurfaceControl leash = mDataByTaskId.get(taskInfo.taskId).surface;
+ mFullscreenUnfoldController.onTaskAppeared(taskInfo, leash);
+ }
+ }
+ }
+
+ public void onTaskInfoChanged(RunningTaskInfo taskInfo) {
+ final boolean isCurrentlyApplicable = mTaskIds.get(taskInfo.taskId);
+ final boolean isApplicable = isAnimatable(taskInfo);
+
+ if (isCurrentlyApplicable) {
+ if (isApplicable) {
+ // Still applicable, send update
+ if (mFullscreenUnfoldController != null) {
+ mFullscreenUnfoldController.onTaskInfoChanged(taskInfo);
+ }
+ } else {
+ // Became inapplicable
+ if (mFullscreenUnfoldController != null) {
+ mFullscreenUnfoldController.onTaskVanished(taskInfo);
+ }
+ mTaskIds.put(taskInfo.taskId, false);
+ }
+ } else {
+ if (isApplicable) {
+ // Became applicable
+ mTaskIds.put(taskInfo.taskId, true);
+
+ if (mFullscreenUnfoldController != null) {
+ SurfaceControl leash = mDataByTaskId.get(taskInfo.taskId).surface;
+ mFullscreenUnfoldController.onTaskAppeared(taskInfo, leash);
+ }
+ }
+ }
+ }
+
+ public void onTaskVanished(RunningTaskInfo taskInfo) {
+ final boolean isCurrentlyApplicable = mTaskIds.get(taskInfo.taskId);
+ if (isCurrentlyApplicable && mFullscreenUnfoldController != null) {
+ mFullscreenUnfoldController.onTaskVanished(taskInfo);
+ }
+ mTaskIds.put(taskInfo.taskId, false);
+ }
+
+ private boolean isAnimatable(TaskInfo taskInfo) {
+ // Filter all visible tasks that are not launcher tasks
+ // We do not animate launcher as it handles the animation by itself
+ return taskInfo != null && taskInfo.isVisible && taskInfo.getConfiguration()
+ .windowConfiguration.getActivityType() != ACTIVITY_TYPE_HOME;
+ }
+ }
+}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/fullscreen/FullscreenUnfoldController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/fullscreen/FullscreenUnfoldController.java
new file mode 100644
index 0000000..fc1b704
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/fullscreen/FullscreenUnfoldController.java
@@ -0,0 +1,225 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell.fullscreen;
+
+import static android.util.MathUtils.lerp;
+import static android.view.Display.DEFAULT_DISPLAY;
+
+import android.animation.RectEvaluator;
+import android.animation.TypeEvaluator;
+import android.annotation.NonNull;
+import android.app.ActivityManager;
+import android.app.TaskInfo;
+import android.content.Context;
+import android.graphics.Matrix;
+import android.graphics.Rect;
+import android.util.SparseArray;
+import android.view.InsetsSource;
+import android.view.InsetsState;
+import android.view.SurfaceControl;
+
+import com.android.internal.policy.ScreenDecorationsUtils;
+import com.android.wm.shell.common.DisplayInsetsController;
+import com.android.wm.shell.common.DisplayInsetsController.OnInsetsChangedListener;
+import com.android.wm.shell.unfold.ShellUnfoldProgressProvider;
+import com.android.wm.shell.unfold.ShellUnfoldProgressProvider.UnfoldListener;
+import com.android.wm.shell.unfold.UnfoldBackgroundController;
+
+import java.util.concurrent.Executor;
+
+/**
+ * Controls full screen app unfold transition: animating cropping window and scaling when
+ * folding or unfolding a foldable device.
+ */
+public final class FullscreenUnfoldController implements UnfoldListener,
+ OnInsetsChangedListener {
+
+ private static final float[] FLOAT_9 = new float[9];
+ private static final TypeEvaluator<Rect> RECT_EVALUATOR = new RectEvaluator(new Rect());
+
+ private static final float HORIZONTAL_START_MARGIN = 0.08f;
+ private static final float VERTICAL_START_MARGIN = 0.03f;
+ private static final float END_SCALE = 1f;
+ private static final float START_SCALE = END_SCALE - VERTICAL_START_MARGIN * 2;
+
+ private final Executor mExecutor;
+ private final ShellUnfoldProgressProvider mProgressProvider;
+ private final DisplayInsetsController mDisplayInsetsController;
+
+ private final SparseArray<AnimationContext> mAnimationContextByTaskId = new SparseArray<>();
+ private final UnfoldBackgroundController mBackgroundController;
+
+ private InsetsSource mTaskbarInsetsSource;
+
+ private final float mWindowCornerRadiusPx;
+ private final float mExpandedTaskBarHeight;
+
+ private final SurfaceControl.Transaction mTransaction = new SurfaceControl.Transaction();
+
+ public FullscreenUnfoldController(
+ @NonNull Context context,
+ @NonNull Executor executor,
+ @NonNull UnfoldBackgroundController backgroundController,
+ @NonNull ShellUnfoldProgressProvider progressProvider,
+ @NonNull DisplayInsetsController displayInsetsController
+ ) {
+ mExecutor = executor;
+ mProgressProvider = progressProvider;
+ mDisplayInsetsController = displayInsetsController;
+ mWindowCornerRadiusPx = ScreenDecorationsUtils.getWindowCornerRadius(context);
+ mExpandedTaskBarHeight = context.getResources().getDimensionPixelSize(
+ com.android.internal.R.dimen.taskbar_frame_height);
+ mBackgroundController = backgroundController;
+ }
+
+ /**
+ * Initializes the controller
+ */
+ public void init() {
+ mProgressProvider.addListener(mExecutor, this);
+ mDisplayInsetsController.addInsetsChangedListener(DEFAULT_DISPLAY, this);
+ }
+
+ @Override
+ public void onStateChangeProgress(float progress) {
+ if (mAnimationContextByTaskId.size() == 0) return;
+
+ mBackgroundController.ensureBackground(mTransaction);
+
+ for (int i = mAnimationContextByTaskId.size() - 1; i >= 0; i--) {
+ final AnimationContext context = mAnimationContextByTaskId.valueAt(i);
+
+ context.mCurrentCropRect.set(RECT_EVALUATOR
+ .evaluate(progress, context.mStartCropRect, context.mEndCropRect));
+
+ float scale = lerp(START_SCALE, END_SCALE, progress);
+ context.mMatrix.setScale(scale, scale, context.mCurrentCropRect.exactCenterX(),
+ context.mCurrentCropRect.exactCenterY());
+
+ mTransaction.setWindowCrop(context.mLeash, context.mCurrentCropRect)
+ .setMatrix(context.mLeash, context.mMatrix, FLOAT_9)
+ .setCornerRadius(context.mLeash, mWindowCornerRadiusPx);
+ }
+
+ mTransaction.apply();
+ }
+
+ @Override
+ public void onStateChangeFinished() {
+ for (int i = mAnimationContextByTaskId.size() - 1; i >= 0; i--) {
+ final AnimationContext context = mAnimationContextByTaskId.valueAt(i);
+ resetSurface(context);
+ }
+
+ mBackgroundController.removeBackground(mTransaction);
+ mTransaction.apply();
+ }
+
+ @Override
+ public void insetsChanged(InsetsState insetsState) {
+ mTaskbarInsetsSource = insetsState.getSource(InsetsState.ITYPE_EXTRA_NAVIGATION_BAR);
+ for (int i = mAnimationContextByTaskId.size() - 1; i >= 0; i--) {
+ AnimationContext context = mAnimationContextByTaskId.valueAt(i);
+ context.update(mTaskbarInsetsSource, context.mTaskInfo);
+ }
+ }
+
+ /**
+ * Called when a new matching task appeared
+ */
+ public void onTaskAppeared(ActivityManager.RunningTaskInfo taskInfo, SurfaceControl leash) {
+ AnimationContext animationContext = new AnimationContext(leash, mTaskbarInsetsSource,
+ taskInfo);
+ mAnimationContextByTaskId.put(taskInfo.taskId, animationContext);
+ }
+
+ /**
+ * Called when matching task changed
+ */
+ public void onTaskInfoChanged(ActivityManager.RunningTaskInfo taskInfo) {
+ AnimationContext animationContext = mAnimationContextByTaskId.get(taskInfo.taskId);
+ if (animationContext != null) {
+ animationContext.update(mTaskbarInsetsSource, taskInfo);
+ }
+ }
+
+ /**
+ * Called when matching task vanished
+ */
+ public void onTaskVanished(ActivityManager.RunningTaskInfo taskInfo) {
+ AnimationContext animationContext = mAnimationContextByTaskId.get(taskInfo.taskId);
+ if (animationContext != null) {
+ resetSurface(animationContext);
+ mAnimationContextByTaskId.remove(taskInfo.taskId);
+ }
+
+ if (mAnimationContextByTaskId.size() == 0) {
+ mBackgroundController.removeBackground(mTransaction);
+ }
+
+ mTransaction.apply();
+ }
+
+ private void resetSurface(AnimationContext context) {
+ mTransaction
+ .setWindowCrop(context.mLeash, null)
+ .setCornerRadius(context.mLeash, 0.0F)
+ .setMatrix(context.mLeash, 1.0F, 0.0F, 0.0F, 1.0F)
+ .setPosition(context.mLeash,
+ (float) context.mTaskInfo.positionInParent.x,
+ (float) context.mTaskInfo.positionInParent.y);
+ }
+
+ private class AnimationContext {
+ final SurfaceControl mLeash;
+ final Rect mStartCropRect = new Rect();
+ final Rect mEndCropRect = new Rect();
+ final Rect mCurrentCropRect = new Rect();
+ final Matrix mMatrix = new Matrix();
+
+ TaskInfo mTaskInfo;
+
+ private AnimationContext(SurfaceControl leash,
+ InsetsSource taskBarInsetsSource,
+ TaskInfo taskInfo) {
+ this.mLeash = leash;
+ update(taskBarInsetsSource, taskInfo);
+ }
+
+ private void update(InsetsSource taskBarInsetsSource, TaskInfo taskInfo) {
+ mTaskInfo = taskInfo;
+ mStartCropRect.set(mTaskInfo.getConfiguration().windowConfiguration.getBounds());
+
+ if (taskBarInsetsSource != null) {
+ // Only insets the cropping window with task bar when it's expanded
+ if (taskBarInsetsSource.getFrame().height() >= mExpandedTaskBarHeight) {
+ mStartCropRect.inset(taskBarInsetsSource
+ .calculateVisibleInsets(mStartCropRect));
+ }
+ }
+
+ mEndCropRect.set(mStartCropRect);
+
+ int horizontalMargin = (int) (mEndCropRect.width() * HORIZONTAL_START_MARGIN);
+ mStartCropRect.left = mEndCropRect.left + horizontalMargin;
+ mStartCropRect.right = mEndCropRect.right - horizontalMargin;
+ int verticalMargin = (int) (mEndCropRect.height() * VERTICAL_START_MARGIN);
+ mStartCropRect.top = mEndCropRect.top + verticalMargin;
+ mStartCropRect.bottom = mEndCropRect.bottom - verticalMargin;
+ }
+ }
+}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/MainStage.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/MainStage.java
index d0998eb..7f82ebd 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/MainStage.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/MainStage.java
@@ -18,6 +18,7 @@
import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW;
+import android.annotation.Nullable;
import android.graphics.Rect;
import android.view.SurfaceSession;
import android.window.WindowContainerToken;
@@ -38,8 +39,10 @@
MainStage(ShellTaskOrganizer taskOrganizer, int displayId,
StageListenerCallbacks callbacks, SyncTransactionQueue syncQueue,
- SurfaceSession surfaceSession) {
- super(taskOrganizer, displayId, callbacks, syncQueue, surfaceSession);
+ SurfaceSession surfaceSession,
+ @Nullable StageTaskUnfoldController stageTaskUnfoldController) {
+ super(taskOrganizer, displayId, callbacks, syncQueue, surfaceSession,
+ stageTaskUnfoldController);
}
boolean isActive() {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/OutlineManager.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/OutlineManager.java
index 0b763f2..a459c8d 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/OutlineManager.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/OutlineManager.java
@@ -22,19 +22,23 @@
import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_TRUSTED_OVERLAY;
import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;
+import android.annotation.Nullable;
import android.content.Context;
import android.content.res.Configuration;
import android.graphics.PixelFormat;
import android.graphics.Rect;
import android.os.Binder;
import android.view.IWindow;
+import android.view.InsetsSource;
+import android.view.InsetsState;
import android.view.LayoutInflater;
import android.view.SurfaceControl;
import android.view.SurfaceControlViewHost;
-import android.view.WindowInsets;
+import android.view.View;
+import android.view.ViewGroup;
import android.view.WindowManager;
-import android.view.WindowMetrics;
import android.view.WindowlessWindowManager;
+import android.widget.FrameLayout;
import com.android.wm.shell.R;
@@ -45,17 +49,22 @@
class OutlineManager extends WindowlessWindowManager {
private static final String WINDOW_NAME = "SplitOutlineLayer";
private final Context mContext;
- private final Rect mOutlineBounds = new Rect();
- private final Rect mTmpBounds = new Rect();
+ private final Rect mRootBounds = new Rect();
+ private final Rect mTempRect = new Rect();
+ private final Rect mLastOutlineBounds = new Rect();
+ private final InsetsState mInsetsState = new InsetsState();
+ private final int mExpandedTaskBarHeight;
+ private OutlineView mOutlineView;
private SurfaceControlViewHost mViewHost;
private SurfaceControl mHostLeash;
private SurfaceControl mLeash;
- private int mOutlineColor;
OutlineManager(Context context, Configuration configuration) {
super(configuration, null /* rootSurface */, null /* hostInputToken */);
mContext = context.createWindowContext(context.getDisplay(), TYPE_APPLICATION_OVERLAY,
null /* options */);
+ mExpandedTaskBarHeight = mContext.getResources().getDimensionPixelSize(
+ com.android.internal.R.dimen.taskbar_frame_height);
}
@Override
@@ -63,65 +72,110 @@
b.setParent(mHostLeash);
}
- boolean drawOutlineBounds(Rect rootBounds) {
- if (mLeash == null || mViewHost == null) return false;
-
- computeOutlineBounds(mContext, rootBounds, mTmpBounds);
- if (mOutlineBounds.equals(mTmpBounds)) {
- return false;
- }
- mOutlineBounds.set(mTmpBounds);
-
- ((OutlineRoot) mViewHost.getView()).updateOutlineBounds(mOutlineBounds, mOutlineColor);
- final WindowManager.LayoutParams lp =
- (WindowManager.LayoutParams) mViewHost.getView().getLayoutParams();
- lp.width = rootBounds.width();
- lp.height = rootBounds.height();
- mViewHost.relayout(lp);
-
- return true;
- }
-
- void inflate(SurfaceControl.Transaction t, SurfaceControl hostLeash, int color) {
+ void inflate(SurfaceControl rootLeash, Rect rootBounds) {
if (mLeash != null || mViewHost != null) return;
- mHostLeash = hostLeash;
- mOutlineColor = color;
+ mHostLeash = rootLeash;
+ mRootBounds.set(rootBounds);
mViewHost = new SurfaceControlViewHost(mContext, mContext.getDisplay(), this);
- final OutlineRoot rootView = (OutlineRoot) LayoutInflater.from(mContext)
+
+ final FrameLayout rootLayout = (FrameLayout) LayoutInflater.from(mContext)
.inflate(R.layout.split_outline, null);
+ mOutlineView = rootLayout.findViewById(R.id.split_outline);
final WindowManager.LayoutParams lp = new WindowManager.LayoutParams(
0 /* width */, 0 /* height */, TYPE_APPLICATION_OVERLAY,
FLAG_NOT_FOCUSABLE | FLAG_NOT_TOUCHABLE, PixelFormat.TRANSLUCENT);
+ lp.width = mRootBounds.width();
+ lp.height = mRootBounds.height();
lp.token = new Binder();
lp.setTitle(WINDOW_NAME);
lp.privateFlags |= PRIVATE_FLAG_NO_MOVE_ANIMATION | PRIVATE_FLAG_TRUSTED_OVERLAY;
// TODO(b/189839391): Set INPUT_FEATURE_NO_INPUT_CHANNEL after WM supports
// TRUSTED_OVERLAY for windowless window without input channel.
- mViewHost.setView(rootView, lp);
+ mViewHost.setView(rootLayout, lp);
mLeash = getSurfaceControl(mViewHost.getWindowToken());
- t.setLayer(mLeash, Integer.MAX_VALUE);
+
+ drawOutline();
}
void release() {
if (mViewHost != null) {
mViewHost.release();
+ mViewHost = null;
+ }
+ mRootBounds.setEmpty();
+ mLastOutlineBounds.setEmpty();
+ mOutlineView = null;
+ mHostLeash = null;
+ mLeash = null;
+ }
+
+ @Nullable
+ SurfaceControl getOutlineLeash() {
+ return mLeash;
+ }
+
+ void setVisibility(boolean visible) {
+ if (mOutlineView != null) {
+ mOutlineView.setVisibility(visible ? View.VISIBLE : View.INVISIBLE);
}
}
- private static void computeOutlineBounds(Context context, Rect rootBounds, Rect outBounds) {
- computeDisplayStableBounds(context, outBounds);
- outBounds.intersect(rootBounds);
+ void setRootBounds(Rect rootBounds) {
+ if (mViewHost == null || mViewHost.getView() == null) {
+ return;
+ }
+
+ if (!mRootBounds.equals(rootBounds)) {
+ WindowManager.LayoutParams lp =
+ (WindowManager.LayoutParams) mViewHost.getView().getLayoutParams();
+ lp.width = rootBounds.width();
+ lp.height = rootBounds.height();
+ mViewHost.relayout(lp);
+ mRootBounds.set(rootBounds);
+ drawOutline();
+ }
+ }
+
+ void onInsetsChanged(InsetsState insetsState) {
+ if (!mInsetsState.equals(insetsState)) {
+ mInsetsState.set(insetsState);
+ drawOutline();
+ }
+ }
+
+ private void computeOutlineBounds(Rect rootBounds, InsetsState insetsState, Rect outBounds) {
+ outBounds.set(rootBounds);
+ final InsetsSource taskBarInsetsSource =
+ insetsState.getSource(InsetsState.ITYPE_EXTRA_NAVIGATION_BAR);
+ // Only insets the divider bar with task bar when it's expanded so that the rounded corners
+ // will be drawn against task bar.
+ if (taskBarInsetsSource.getFrame().height() >= mExpandedTaskBarHeight) {
+ outBounds.inset(taskBarInsetsSource.calculateVisibleInsets(outBounds));
+ }
+
// Offset the coordinate from screen based to surface based.
outBounds.offset(-rootBounds.left, -rootBounds.top);
}
- private static void computeDisplayStableBounds(Context context, Rect outBounds) {
- final WindowMetrics windowMetrics =
- context.getSystemService(WindowManager.class).getMaximumWindowMetrics();
- outBounds.set(windowMetrics.getBounds());
- outBounds.inset(windowMetrics.getWindowInsets().getInsets(
- WindowInsets.Type.systemBars() | WindowInsets.Type.displayCutout()));
+ void drawOutline() {
+ if (mOutlineView == null) {
+ return;
+ }
+
+ computeOutlineBounds(mRootBounds, mInsetsState, mTempRect);
+ if (mTempRect.equals(mLastOutlineBounds)) {
+ return;
+ }
+
+ ViewGroup.MarginLayoutParams lp =
+ (ViewGroup.MarginLayoutParams) mOutlineView.getLayoutParams();
+ lp.leftMargin = mTempRect.left;
+ lp.topMargin = mTempRect.top;
+ lp.width = mTempRect.width();
+ lp.height = mTempRect.height();
+ mOutlineView.setLayoutParams(lp);
+ mLastOutlineBounds.set(mTempRect);
}
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/OutlineRoot.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/OutlineRoot.java
deleted file mode 100644
index 71d48ee..0000000
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/OutlineRoot.java
+++ /dev/null
@@ -1,62 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.wm.shell.splitscreen;
-
-import android.content.Context;
-import android.graphics.Rect;
-import android.util.AttributeSet;
-import android.widget.FrameLayout;
-
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
-
-import com.android.wm.shell.R;
-
-/** Root layout for holding split outline. */
-public class OutlineRoot extends FrameLayout {
- public OutlineRoot(@NonNull Context context) {
- super(context);
- }
-
- public OutlineRoot(@NonNull Context context,
- @Nullable AttributeSet attrs) {
- super(context, attrs);
- }
-
- public OutlineRoot(@NonNull Context context, @Nullable AttributeSet attrs,
- int defStyleAttr) {
- super(context, attrs, defStyleAttr);
- }
-
- public OutlineRoot(@NonNull Context context, @Nullable AttributeSet attrs,
- int defStyleAttr,
- int defStyleRes) {
- super(context, attrs, defStyleAttr, defStyleRes);
- }
-
- private OutlineView mOutlineView;
-
- @Override
- protected void onFinishInflate() {
- super.onFinishInflate();
- mOutlineView = findViewById(R.id.split_outline);
- }
-
- void updateOutlineBounds(Rect bounds, int color) {
- mOutlineView.updateOutlineBounds(bounds, color);
- }
-}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/OutlineView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/OutlineView.java
index ea66180..94dd9b2 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/OutlineView.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/OutlineView.java
@@ -16,13 +16,17 @@
package com.android.wm.shell.splitscreen;
+import static android.view.RoundedCorner.POSITION_BOTTOM_LEFT;
+import static android.view.RoundedCorner.POSITION_BOTTOM_RIGHT;
+import static android.view.RoundedCorner.POSITION_TOP_LEFT;
+import static android.view.RoundedCorner.POSITION_TOP_RIGHT;
+
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.Path;
-import android.graphics.Rect;
-import android.graphics.Region;
import android.util.AttributeSet;
+import android.view.RoundedCorner;
import android.view.View;
import androidx.annotation.NonNull;
@@ -33,44 +37,46 @@
/** View for drawing split outline. */
public class OutlineView extends View {
private final Paint mPaint = new Paint();
- private final Rect mBounds = new Rect();
+ private final Path mPath = new Path();
+ private final float[] mRadii = new float[8];
- public OutlineView(@NonNull Context context) {
- super(context);
- }
-
- public OutlineView(@NonNull Context context,
- @Nullable AttributeSet attrs) {
+ public OutlineView(@NonNull Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
- }
-
- public OutlineView(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
- super(context, attrs, defStyleAttr);
- }
-
- public OutlineView(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr,
- int defStyleRes) {
- super(context, attrs, defStyleAttr, defStyleRes);
+ mPaint.setStyle(Paint.Style.STROKE);
+ mPaint.setStrokeWidth(
+ getResources().getDimension(R.dimen.accessibility_focus_highlight_stroke_width));
+ mPaint.setColor(getResources().getColor(R.color.system_accent1_100, null));
}
@Override
- protected void onFinishInflate() {
- super.onFinishInflate();
- mPaint.setStyle(Paint.Style.STROKE);
- mPaint.setStrokeWidth(getResources()
- .getDimension(R.dimen.accessibility_focus_highlight_stroke_width));
+ protected void onAttachedToWindow() {
+ // TODO(b/200850654): match the screen corners with the actual display decor.
+ mRadii[0] = mRadii[1] = getCornerRadius(POSITION_TOP_LEFT);
+ mRadii[2] = mRadii[3] = getCornerRadius(POSITION_TOP_RIGHT);
+ mRadii[4] = mRadii[5] = getCornerRadius(POSITION_BOTTOM_RIGHT);
+ mRadii[6] = mRadii[7] = getCornerRadius(POSITION_BOTTOM_LEFT);
}
- void updateOutlineBounds(Rect bounds, int color) {
- if (mBounds.equals(bounds) && mPaint.getColor() == color) return;
- mBounds.set(bounds);
- mPaint.setColor(color);
+ private int getCornerRadius(@RoundedCorner.Position int position) {
+ final RoundedCorner roundedCorner = getDisplay().getRoundedCorner(position);
+ return roundedCorner == null ? 0 : roundedCorner.getRadius();
+ }
+
+ @Override
+ protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
+ if (changed) {
+ mPath.reset();
+ mPath.addRoundRect(0, 0, getWidth(), getHeight(), mRadii, Path.Direction.CW);
+ }
}
@Override
protected void onDraw(Canvas canvas) {
- if (mBounds.isEmpty()) return;
- final Path path = new Region(mBounds).getBoundaryPath();
- canvas.drawPath(path, mPaint);
+ canvas.drawPath(mPath, mPaint);
+ }
+
+ @Override
+ public boolean hasOverlappingRendering() {
+ return false;
}
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SideStage.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SideStage.java
index 2b19bb9..dc8fb9f 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SideStage.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SideStage.java
@@ -17,15 +17,19 @@
package com.android.wm.shell.splitscreen;
import android.annotation.CallSuper;
+import android.annotation.Nullable;
import android.app.ActivityManager;
import android.content.Context;
-import android.graphics.Color;
import android.graphics.Rect;
+import android.view.InsetsSourceControl;
+import android.view.InsetsState;
+import android.view.SurfaceControl;
import android.view.SurfaceSession;
import android.window.WindowContainerToken;
import android.window.WindowContainerTransaction;
import com.android.wm.shell.ShellTaskOrganizer;
+import com.android.wm.shell.common.DisplayInsetsController;
import com.android.wm.shell.common.SyncTransactionQueue;
/**
@@ -34,15 +38,18 @@
*
* @see StageCoordinator
*/
-class SideStage extends StageTaskListener {
+class SideStage extends StageTaskListener implements
+ DisplayInsetsController.OnInsetsChangedListener {
private static final String TAG = SideStage.class.getSimpleName();
private final Context mContext;
private OutlineManager mOutlineManager;
SideStage(Context context, ShellTaskOrganizer taskOrganizer, int displayId,
StageListenerCallbacks callbacks, SyncTransactionQueue syncQueue,
- SurfaceSession surfaceSession) {
- super(taskOrganizer, displayId, callbacks, syncQueue, surfaceSession);
+ SurfaceSession surfaceSession,
+ @Nullable StageTaskUnfoldController stageTaskUnfoldController) {
+ super(taskOrganizer, displayId, callbacks, syncQueue, surfaceSession,
+ stageTaskUnfoldController);
mContext = context;
}
@@ -77,33 +84,61 @@
return true;
}
- void enableOutline(boolean enable) {
- if (enable) {
- if (mOutlineManager == null && mRootTaskInfo != null) {
- mOutlineManager = new OutlineManager(mContext, mRootTaskInfo.configuration);
- mSyncQueue.runInSync(t -> mOutlineManager.inflate(t, mRootLeash, Color.YELLOW));
- updateOutlineBounds();
- }
- } else {
- if (mOutlineManager != null) {
- mOutlineManager.release();
- mOutlineManager = null;
- }
- }
+ @Nullable
+ public SurfaceControl getOutlineLeash() {
+ return mOutlineManager.getOutlineLeash();
}
- private void updateOutlineBounds() {
- if (mOutlineManager == null || mRootTaskInfo == null || !mRootTaskInfo.isVisible) return;
- mOutlineManager.drawOutlineBounds(
- mRootTaskInfo.configuration.windowConfiguration.getBounds());
+ @Override
+ @CallSuper
+ public void onTaskAppeared(ActivityManager.RunningTaskInfo taskInfo, SurfaceControl leash) {
+ super.onTaskAppeared(taskInfo, leash);
+ if (isRootTask(taskInfo)) {
+ mOutlineManager = new OutlineManager(mContext, taskInfo.configuration);
+ enableOutline(true);
+ }
}
@Override
@CallSuper
public void onTaskInfoChanged(ActivityManager.RunningTaskInfo taskInfo) {
super.onTaskInfoChanged(taskInfo);
- if (mRootTaskInfo != null && mRootTaskInfo.taskId == taskInfo.taskId) {
- updateOutlineBounds();
+ if (isRootTask(taskInfo)) {
+ mOutlineManager.setRootBounds(taskInfo.configuration.windowConfiguration.getBounds());
}
}
+
+ private boolean isRootTask(ActivityManager.RunningTaskInfo taskInfo) {
+ return mRootTaskInfo != null && mRootTaskInfo.taskId == taskInfo.taskId;
+ }
+
+ void enableOutline(boolean enable) {
+ if (mOutlineManager == null) {
+ return;
+ }
+
+ if (enable) {
+ if (mRootTaskInfo != null) {
+ mOutlineManager.inflate(mRootLeash,
+ mRootTaskInfo.configuration.windowConfiguration.getBounds());
+ }
+ } else {
+ mOutlineManager.release();
+ }
+ }
+
+ void setOutlineVisibility(boolean visible) {
+ mOutlineManager.setVisibility(visible);
+ }
+
+ @Override
+ public void insetsChanged(InsetsState insetsState) {
+ mOutlineManager.onInsetsChanged(insetsState);
+ }
+
+ @Override
+ public void insetsControlChanged(InsetsState insetsState,
+ InsetsSourceControl[] activeControls) {
+ insetsChanged(insetsState);
+ }
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java
index 6527cab..0d52719 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java
@@ -68,8 +68,11 @@
import java.io.PrintWriter;
import java.util.Arrays;
+import java.util.Optional;
import java.util.concurrent.Executor;
+import javax.inject.Provider;
+
/**
* Class manages split-screen multitasking mode and implements the main interface
* {@link SplitScreen}.
@@ -91,6 +94,7 @@
private final Transitions mTransitions;
private final TransactionPool mTransactionPool;
private final SplitscreenEventLogger mLogger;
+ private final Provider<Optional<StageTaskUnfoldController>> mUnfoldControllerProvider;
private StageCoordinator mStageCoordinator;
@@ -99,7 +103,8 @@
RootTaskDisplayAreaOrganizer rootTDAOrganizer,
ShellExecutor mainExecutor, DisplayImeController displayImeController,
DisplayInsetsController displayInsetsController,
- Transitions transitions, TransactionPool transactionPool) {
+ Transitions transitions, TransactionPool transactionPool,
+ Provider<Optional<StageTaskUnfoldController>> unfoldControllerProvider) {
mTaskOrganizer = shellTaskOrganizer;
mSyncQueue = syncQueue;
mContext = context;
@@ -109,6 +114,7 @@
mDisplayInsetsController = displayInsetsController;
mTransitions = transitions;
mTransactionPool = transactionPool;
+ mUnfoldControllerProvider = unfoldControllerProvider;
mLogger = new SplitscreenEventLogger();
}
@@ -131,7 +137,8 @@
// TODO: Multi-display
mStageCoordinator = new StageCoordinator(mContext, DEFAULT_DISPLAY, mSyncQueue,
mRootTDAOrganizer, mTaskOrganizer, mDisplayImeController,
- mDisplayInsetsController, mTransitions, mTransactionPool, mLogger);
+ mDisplayInsetsController, mTransitions, mTransactionPool, mLogger,
+ mUnfoldControllerProvider);
}
}
@@ -257,11 +264,6 @@
}
}
- final RemoteAnimationTarget divider = mStageCoordinator.getDividerBarLegacyTarget();
- if (divider.leash != null) {
- t.show(divider.leash);
- }
-
t.apply();
if (finishedCallback != null) {
try {
@@ -300,7 +302,9 @@
}
transaction.apply();
transaction.close();
- return new RemoteAnimationTarget[]{mStageCoordinator.getDividerBarLegacyTarget()};
+ return new RemoteAnimationTarget[]{
+ mStageCoordinator.getDividerBarLegacyTarget(),
+ mStageCoordinator.getOutlineLegacyTarget()};
}
/**
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java
index a046c42..414b4e4 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java
@@ -95,6 +95,9 @@
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.List;
+import java.util.Optional;
+
+import javax.inject.Provider;
/**
* Coordinates the staging (visibility, sizing, ...) of the split-screen {@link MainStage} and
@@ -121,8 +124,10 @@
private final MainStage mMainStage;
private final StageListenerImpl mMainStageListener = new StageListenerImpl();
+ private final StageTaskUnfoldController mMainUnfoldController;
private final SideStage mSideStage;
private final StageListenerImpl mSideStageListener = new StageListenerImpl();
+ private final StageTaskUnfoldController mSideUnfoldController;
@SplitPosition
private int mSideStagePosition = SPLIT_POSITION_BOTTOM_OR_RIGHT;
@@ -179,28 +184,35 @@
RootTaskDisplayAreaOrganizer rootTDAOrganizer, ShellTaskOrganizer taskOrganizer,
DisplayImeController displayImeController,
DisplayInsetsController displayInsetsController, Transitions transitions,
- TransactionPool transactionPool, SplitscreenEventLogger logger) {
+ TransactionPool transactionPool, SplitscreenEventLogger logger,
+ Provider<Optional<StageTaskUnfoldController>> unfoldControllerProvider) {
mContext = context;
mDisplayId = displayId;
mSyncQueue = syncQueue;
mRootTDAOrganizer = rootTDAOrganizer;
mTaskOrganizer = taskOrganizer;
mLogger = logger;
+ mMainUnfoldController = unfoldControllerProvider.get().orElse(null);
+ mSideUnfoldController = unfoldControllerProvider.get().orElse(null);
+
mMainStage = new MainStage(
mTaskOrganizer,
mDisplayId,
mMainStageListener,
mSyncQueue,
- mSurfaceSession);
+ mSurfaceSession,
+ mMainUnfoldController);
mSideStage = new SideStage(
mContext,
mTaskOrganizer,
mDisplayId,
mSideStageListener,
mSyncQueue,
- mSurfaceSession);
+ mSurfaceSession,
+ mSideUnfoldController);
mDisplayImeController = displayImeController;
mDisplayInsetsController = displayInsetsController;
+ mDisplayInsetsController.addInsetsChangedListener(mDisplayId, mSideStage);
mRootTDAOrganizer.registerListener(displayId, this);
final DeviceStateManager deviceStateManager =
mContext.getSystemService(DeviceStateManager.class);
@@ -217,7 +229,8 @@
MainStage mainStage, SideStage sideStage, DisplayImeController displayImeController,
DisplayInsetsController displayInsetsController, SplitLayout splitLayout,
Transitions transitions, TransactionPool transactionPool,
- SplitscreenEventLogger logger) {
+ SplitscreenEventLogger logger,
+ Provider<Optional<StageTaskUnfoldController>> unfoldControllerProvider) {
mContext = context;
mDisplayId = displayId;
mSyncQueue = syncQueue;
@@ -231,6 +244,8 @@
mSplitLayout = splitLayout;
mSplitTransitions = new SplitScreenTransitions(transactionPool, transitions,
mOnTransitionAnimationComplete);
+ mMainUnfoldController = unfoldControllerProvider.get().orElse(null);
+ mSideUnfoldController = unfoldControllerProvider.get().orElse(null);
mLogger = logger;
transitions.addHandler(this);
}
@@ -459,6 +474,7 @@
onLayoutChanged(mSplitLayout);
} else {
updateWindowBounds(mSplitLayout, wct);
+ updateUnfoldBounds();
}
}
}
@@ -514,6 +530,9 @@
mSideStage.removeAllTasks(wct, childrenToTop == mSideStage);
mMainStage.deactivate(wct, childrenToTop == mMainStage);
mTaskOrganizer.applyTransaction(wct);
+ mSyncQueue.runInSync(t -> t
+ .setWindowCrop(mMainStage.mRootLeash, null)
+ .setWindowCrop(mSideStage.mRootLeash, null));
// Hide divider and reset its position.
setDividerVisibility(false);
mSplitLayout.resetDividerPosition();
@@ -602,6 +621,11 @@
final SplitScreen.SplitScreenListener l = mListeners.get(i);
l.onSplitVisibilityChanged(mDividerVisible);
}
+
+ if (mMainUnfoldController != null && mSideUnfoldController != null) {
+ mMainUnfoldController.onSplitVisibilityChanged(mDividerVisible);
+ mSideUnfoldController.onSplitVisibilityChanged(mDividerVisible);
+ }
}
private void onStageRootTaskAppeared(StageListenerImpl stageListener) {
@@ -640,6 +664,7 @@
mDividerVisible = visible;
if (visible) {
mSplitLayout.init();
+ updateUnfoldBounds();
} else {
mSplitLayout.release();
}
@@ -682,6 +707,7 @@
t.setVisibility(mSideStage.mRootLeash, bothStageVisible)
.setVisibility(mMainStage.mRootLeash, bothStageVisible);
applyDividerVisibility(t);
+ applyOutlineVisibility(t);
}
});
}
@@ -703,6 +729,19 @@
}
}
+ private void applyOutlineVisibility(SurfaceControl.Transaction t) {
+ final SurfaceControl outlineLeash = mSideStage.getOutlineLeash();
+ if (outlineLeash == null) {
+ return;
+ }
+
+ if (mDividerVisible) {
+ t.show(outlineLeash).setLayer(outlineLeash, SPLIT_DIVIDER_LAYER);
+ } else {
+ t.hide(outlineLeash);
+ }
+ }
+
private void onStageHasChildrenChanged(StageListenerImpl stageListener) {
final boolean hasChildren = stageListener.mHasChildren;
final boolean isSideStage = stageListener == mSideStageListener;
@@ -762,17 +801,27 @@
@Override
public void onLayoutChanging(SplitLayout layout) {
mSyncQueue.runInSync(t -> updateSurfaceBounds(layout, t));
+ mSideStage.setOutlineVisibility(false);
}
@Override
public void onLayoutChanged(SplitLayout layout) {
final WindowContainerTransaction wct = new WindowContainerTransaction();
updateWindowBounds(layout, wct);
+ updateUnfoldBounds();
mSyncQueue.queue(wct);
mSyncQueue.runInSync(t -> updateSurfaceBounds(layout, t));
+ mSideStage.setOutlineVisibility(true);
mLogger.logResize(mSplitLayout.getDividerPositionAsFraction());
}
+ private void updateUnfoldBounds() {
+ if (mMainUnfoldController != null && mSideUnfoldController != null) {
+ mMainUnfoldController.onLayoutChanged(getMainStageBounds());
+ mSideUnfoldController.onLayoutChanged(getSideStageBounds());
+ }
+ }
+
/**
* Populates `wct` with operations that match the split windows to the current layout.
* To match relevant surfaces, make sure to call updateSurfaceBounds after `wct` is applied
@@ -829,6 +878,11 @@
mDisplayAreaInfo.configuration, this, mParentContainerCallbacks,
mDisplayImeController, mTaskOrganizer);
mDisplayInsetsController.addInsetsChangedListener(mDisplayId, mSplitLayout);
+
+ if (mMainUnfoldController != null && mSideUnfoldController != null) {
+ mMainUnfoldController.init();
+ mSideUnfoldController.init();
+ }
}
}
@@ -1145,6 +1199,18 @@
null /* taskInfo */, false /* allowEnterPip */, TYPE_DOCK_DIVIDER);
}
+ RemoteAnimationTarget getOutlineLegacyTarget() {
+ final Rect bounds = mSideStage.mRootTaskInfo.configuration.windowConfiguration.getBounds();
+ // Leverage TYPE_DOCK_DIVIDER type when wrapping outline remote animation target in order to
+ // distinguish as a split auxiliary target in Launcher.
+ return new RemoteAnimationTarget(-1 /* taskId */, -1 /* mode */,
+ mSideStage.getOutlineLeash(), false /* isTranslucent */, null /* clipRect */,
+ null /* contentInsets */, Integer.MAX_VALUE /* prefixOrderIndex */,
+ new android.graphics.Point(0, 0) /* position */, bounds, bounds,
+ new WindowConfiguration(), true, null /* startLeash */, null /* startBounds */,
+ null /* taskInfo */, false /* allowEnterPip */, TYPE_DOCK_DIVIDER);
+ }
+
@Override
public void dump(@NonNull PrintWriter pw, String prefix) {
final String innerPrefix = prefix + " ";
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageTaskListener.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageTaskListener.java
index 15b4ff9..84d570f 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageTaskListener.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageTaskListener.java
@@ -24,6 +24,7 @@
import static com.android.wm.shell.transition.Transitions.ENABLE_SHELL_TRANSITIONS;
import android.annotation.CallSuper;
+import android.annotation.Nullable;
import android.app.ActivityManager;
import android.graphics.Point;
import android.graphics.Rect;
@@ -80,12 +81,16 @@
protected SparseArray<ActivityManager.RunningTaskInfo> mChildrenTaskInfo = new SparseArray<>();
private final SparseArray<SurfaceControl> mChildrenLeashes = new SparseArray<>();
+ private final StageTaskUnfoldController mStageTaskUnfoldController;
+
StageTaskListener(ShellTaskOrganizer taskOrganizer, int displayId,
StageListenerCallbacks callbacks, SyncTransactionQueue syncQueue,
- SurfaceSession surfaceSession) {
+ SurfaceSession surfaceSession,
+ @Nullable StageTaskUnfoldController stageTaskUnfoldController) {
mCallbacks = callbacks;
mSyncQueue = syncQueue;
mSurfaceSession = surfaceSession;
+ mStageTaskUnfoldController = stageTaskUnfoldController;
taskOrganizer.createRootTask(displayId, WINDOWING_MODE_MULTI_WINDOW, this);
}
@@ -138,8 +143,11 @@
mRootTaskInfo = taskInfo;
mCallbacks.onRootTaskAppeared();
sendStatusChanged();
- mSyncQueue.runInSync(t -> mDimLayer =
- SurfaceUtils.makeDimLayer(t, mRootLeash, "Dim layer", mSurfaceSession));
+ mSyncQueue.runInSync(t -> {
+ t.hide(mRootLeash);
+ mDimLayer =
+ SurfaceUtils.makeDimLayer(t, mRootLeash, "Dim layer", mSurfaceSession);
+ });
} else if (taskInfo.parentTaskId == mRootTaskInfo.taskId) {
final int taskId = taskInfo.taskId;
mChildrenLeashes.put(taskId, leash);
@@ -155,6 +163,10 @@
throw new IllegalArgumentException(this + "\n Unknown task: " + taskInfo
+ "\n mRootTaskInfo: " + mRootTaskInfo);
}
+
+ if (mStageTaskUnfoldController != null) {
+ mStageTaskUnfoldController.onTaskAppeared(taskInfo, leash);
+ }
}
@Override
@@ -207,6 +219,10 @@
throw new IllegalArgumentException(this + "\n Unknown task: " + taskInfo
+ "\n mRootTaskInfo: " + mRootTaskInfo);
}
+
+ if (mStageTaskUnfoldController != null) {
+ mStageTaskUnfoldController.onTaskVanished(taskInfo);
+ }
}
@Override
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageTaskUnfoldController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageTaskUnfoldController.java
new file mode 100644
index 0000000..e904f6a
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageTaskUnfoldController.java
@@ -0,0 +1,224 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell.splitscreen;
+
+import static android.view.Display.DEFAULT_DISPLAY;
+
+import android.animation.RectEvaluator;
+import android.animation.TypeEvaluator;
+import android.annotation.NonNull;
+import android.app.ActivityManager;
+import android.content.Context;
+import android.graphics.Rect;
+import android.util.SparseArray;
+import android.view.InsetsSource;
+import android.view.InsetsState;
+import android.view.SurfaceControl;
+
+import com.android.internal.policy.ScreenDecorationsUtils;
+import com.android.wm.shell.common.DisplayInsetsController;
+import com.android.wm.shell.common.DisplayInsetsController.OnInsetsChangedListener;
+import com.android.wm.shell.common.TransactionPool;
+import com.android.wm.shell.unfold.ShellUnfoldProgressProvider;
+import com.android.wm.shell.unfold.ShellUnfoldProgressProvider.UnfoldListener;
+import com.android.wm.shell.unfold.UnfoldBackgroundController;
+
+import java.util.concurrent.Executor;
+
+/**
+ * Controls transformations of the split screen task surfaces in response
+ * to the unfolding/folding action on foldable devices
+ */
+public class StageTaskUnfoldController implements UnfoldListener, OnInsetsChangedListener {
+
+ private static final TypeEvaluator<Rect> RECT_EVALUATOR = new RectEvaluator(new Rect());
+ private static final float CROPPING_START_MARGIN_FRACTION = 0.05f;
+
+ private final SparseArray<AnimationContext> mAnimationContextByTaskId = new SparseArray<>();
+ private final ShellUnfoldProgressProvider mUnfoldProgressProvider;
+ private final DisplayInsetsController mDisplayInsetsController;
+ private final UnfoldBackgroundController mBackgroundController;
+ private final Executor mExecutor;
+ private final int mExpandedTaskBarHeight;
+ private final float mWindowCornerRadiusPx;
+ private final Rect mStageBounds = new Rect();
+ private final TransactionPool mTransactionPool;
+
+ private InsetsSource mTaskbarInsetsSource;
+ private boolean mBothStagesVisible;
+
+ public StageTaskUnfoldController(@NonNull Context context,
+ @NonNull TransactionPool transactionPool,
+ @NonNull ShellUnfoldProgressProvider unfoldProgressProvider,
+ @NonNull DisplayInsetsController displayInsetsController,
+ @NonNull UnfoldBackgroundController backgroundController,
+ @NonNull Executor executor) {
+ mUnfoldProgressProvider = unfoldProgressProvider;
+ mTransactionPool = transactionPool;
+ mExecutor = executor;
+ mBackgroundController = backgroundController;
+ mDisplayInsetsController = displayInsetsController;
+ mWindowCornerRadiusPx = ScreenDecorationsUtils.getWindowCornerRadius(context);
+ mExpandedTaskBarHeight = context.getResources().getDimensionPixelSize(
+ com.android.internal.R.dimen.taskbar_frame_height);
+ }
+
+ /**
+ * Initializes the controller, starts listening for the external events
+ */
+ public void init() {
+ mUnfoldProgressProvider.addListener(mExecutor, this);
+ mDisplayInsetsController.addInsetsChangedListener(DEFAULT_DISPLAY, this);
+ }
+
+ @Override
+ public void insetsChanged(InsetsState insetsState) {
+ mTaskbarInsetsSource = insetsState.getSource(InsetsState.ITYPE_EXTRA_NAVIGATION_BAR);
+ for (int i = mAnimationContextByTaskId.size() - 1; i >= 0; i--) {
+ AnimationContext context = mAnimationContextByTaskId.valueAt(i);
+ context.update();
+ }
+ }
+
+ /**
+ * Called when split screen task appeared
+ * @param taskInfo info for the appeared task
+ * @param leash surface leash for the appeared task
+ */
+ public void onTaskAppeared(ActivityManager.RunningTaskInfo taskInfo, SurfaceControl leash) {
+ AnimationContext context = new AnimationContext(leash);
+ mAnimationContextByTaskId.put(taskInfo.taskId, context);
+ }
+
+ /**
+ * Called when a split screen task vanished
+ * @param taskInfo info for the vanished task
+ */
+ public void onTaskVanished(ActivityManager.RunningTaskInfo taskInfo) {
+ AnimationContext context = mAnimationContextByTaskId.get(taskInfo.taskId);
+ if (context != null) {
+ final SurfaceControl.Transaction transaction = mTransactionPool.acquire();
+ resetSurface(transaction, context);
+ transaction.apply();
+ mTransactionPool.release(transaction);
+ }
+ mAnimationContextByTaskId.remove(taskInfo.taskId);
+ }
+
+ @Override
+ public void onStateChangeProgress(float progress) {
+ if (mAnimationContextByTaskId.size() == 0 || !mBothStagesVisible) return;
+
+ final SurfaceControl.Transaction transaction = mTransactionPool.acquire();
+ mBackgroundController.ensureBackground(transaction);
+
+ for (int i = mAnimationContextByTaskId.size() - 1; i >= 0; i--) {
+ AnimationContext context = mAnimationContextByTaskId.valueAt(i);
+
+ context.mCurrentCropRect.set(RECT_EVALUATOR
+ .evaluate(progress, context.mStartCropRect, context.mEndCropRect));
+
+ transaction.setWindowCrop(context.mLeash, context.mCurrentCropRect)
+ .setCornerRadius(context.mLeash, mWindowCornerRadiusPx);
+ }
+
+ transaction.apply();
+
+ mTransactionPool.release(transaction);
+ }
+
+ @Override
+ public void onStateChangeFinished() {
+ resetTransformations();
+ }
+
+ /**
+ * Called when split screen visibility changes
+ * @param bothStagesVisible true if both stages of the split screen are visible
+ */
+ public void onSplitVisibilityChanged(boolean bothStagesVisible) {
+ mBothStagesVisible = bothStagesVisible;
+ if (!bothStagesVisible) {
+ resetTransformations();
+ }
+ }
+
+ /**
+ * Called when split screen stage bounds changed
+ * @param bounds new bounds for this stage
+ */
+ public void onLayoutChanged(Rect bounds) {
+ mStageBounds.set(bounds);
+
+ for (int i = mAnimationContextByTaskId.size() - 1; i >= 0; i--) {
+ final AnimationContext context = mAnimationContextByTaskId.valueAt(i);
+ context.update();
+ }
+ }
+
+ private void resetTransformations() {
+ final SurfaceControl.Transaction transaction = mTransactionPool.acquire();
+
+ for (int i = mAnimationContextByTaskId.size() - 1; i >= 0; i--) {
+ final AnimationContext context = mAnimationContextByTaskId.valueAt(i);
+ resetSurface(transaction, context);
+ }
+ mBackgroundController.removeBackground(transaction);
+ transaction.apply();
+
+ mTransactionPool.release(transaction);
+ }
+
+ private void resetSurface(SurfaceControl.Transaction transaction, AnimationContext context) {
+ transaction
+ .setWindowCrop(context.mLeash, null)
+ .setCornerRadius(context.mLeash, 0.0F);
+ }
+
+ private class AnimationContext {
+ final SurfaceControl mLeash;
+ final Rect mStartCropRect = new Rect();
+ final Rect mEndCropRect = new Rect();
+ final Rect mCurrentCropRect = new Rect();
+
+ private AnimationContext(SurfaceControl leash) {
+ this.mLeash = leash;
+ update();
+ }
+
+ private void update() {
+ mStartCropRect.set(mStageBounds);
+
+ if (mTaskbarInsetsSource != null) {
+ // Only insets the cropping window with taskbar when taskbar is expanded
+ if (mTaskbarInsetsSource.getFrame().height() >= mExpandedTaskBarHeight) {
+ mStartCropRect.inset(mTaskbarInsetsSource
+ .calculateVisibleInsets(mStartCropRect));
+ }
+ }
+
+ // Offset to surface coordinates as layout bounds are in screen coordinates
+ mStartCropRect.offsetTo(0, 0);
+
+ mEndCropRect.set(mStartCropRect);
+
+ int maxSize = Math.max(mEndCropRect.width(), mEndCropRect.height());
+ int margin = (int) (maxSize * CROPPING_START_MARGIN_FRACTION);
+ mStartCropRect.inset(margin, margin, margin, margin);
+ }
+ }
+}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/StartingSurfaceDrawer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/StartingSurfaceDrawer.java
index ff3428c..979bf00 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/StartingSurfaceDrawer.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/StartingSurfaceDrawer.java
@@ -37,7 +37,6 @@
import android.content.res.TypedArray;
import android.graphics.Color;
import android.graphics.PixelFormat;
-import android.graphics.Rect;
import android.hardware.display.DisplayManager;
import android.os.IBinder;
import android.os.RemoteCallback;
@@ -48,7 +47,6 @@
import android.util.SparseArray;
import android.view.Choreographer;
import android.view.Display;
-import android.view.SurfaceControl;
import android.view.SurfaceControlViewHost;
import android.view.View;
import android.view.WindowManager;
@@ -58,6 +56,7 @@
import android.window.SplashScreenView.SplashScreenViewParcelable;
import android.window.StartingWindowInfo;
import android.window.StartingWindowInfo.StartingWindowType;
+import android.window.StartingWindowRemovalInfo;
import android.window.TaskSnapshot;
import com.android.internal.R;
@@ -118,6 +117,7 @@
private Choreographer mChoreographer;
private final WindowManagerGlobal mWindowManagerGlobal;
private StartingSurface.SysuiProxy mSysuiProxy;
+ private final StartingWindowRemovalInfo mTmpRemovalInfo = new StartingWindowRemovalInfo();
/**
* @param splashScreenExecutor The thread used to control add and remove starting window.
@@ -458,12 +458,13 @@
/**
* Called when the content of a task is ready to show, starting window can be removed.
*/
- public void removeStartingWindow(int taskId, SurfaceControl leash, Rect frame,
- boolean playRevealAnimation) {
+ public void removeStartingWindow(StartingWindowRemovalInfo removalInfo) {
if (DEBUG_SPLASH_SCREEN || DEBUG_TASK_SNAPSHOT) {
- Slog.d(TAG, "Task start finish, remove starting surface for task " + taskId);
+ Slog.d(TAG, "Task start finish, remove starting surface for task "
+ + removalInfo.taskId);
}
- removeWindowSynced(taskId, leash, frame, playRevealAnimation);
+ removeWindowSynced(removalInfo);
+
}
/**
@@ -556,7 +557,8 @@
}
private void removeWindowNoAnimate(int taskId) {
- removeWindowSynced(taskId, null, null, false);
+ mTmpRemovalInfo.taskId = taskId;
+ removeWindowSynced(mTmpRemovalInfo);
}
void onImeDrawnOnTask(int taskId) {
@@ -568,8 +570,8 @@
mStartingWindowRecords.remove(taskId);
}
- protected void removeWindowSynced(int taskId, SurfaceControl leash, Rect frame,
- boolean playRevealAnimation) {
+ protected void removeWindowSynced(StartingWindowRemovalInfo removalInfo) {
+ final int taskId = removalInfo.taskId;
final StartingWindowRecord record = mStartingWindowRecords.get(taskId);
if (record != null) {
if (record.mDecorView != null) {
@@ -580,9 +582,9 @@
if (record.mSuggestType == STARTING_WINDOW_TYPE_LEGACY_SPLASH_SCREEN) {
removeWindowInner(record.mDecorView, false);
} else {
- if (playRevealAnimation) {
+ if (removalInfo.playRevealAnimation) {
mSplashscreenContentDrawer.applyExitAnimation(record.mContentView,
- leash, frame,
+ removalInfo.windowAnimationLeash, removalInfo.mainFrame,
() -> removeWindowInner(record.mDecorView, true));
} else {
// the SplashScreenView has been copied to client, hide the view to skip
@@ -602,7 +604,7 @@
Slog.v(TAG, "Removing task snapshot window for " + taskId);
}
record.mTaskSnapshotWindow.scheduleRemove(
- () -> mStartingWindowRecords.remove(taskId));
+ () -> mStartingWindowRecords.remove(taskId), removalInfo.deferRemoveForIme);
}
}
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/StartingWindowController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/StartingWindowController.java
index 4433e27..99644f9 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/StartingWindowController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/StartingWindowController.java
@@ -28,16 +28,14 @@
import android.app.TaskInfo;
import android.content.Context;
import android.graphics.Color;
-import android.graphics.Rect;
-import android.os.Build;
import android.os.IBinder;
import android.os.RemoteException;
import android.os.Trace;
import android.util.Slog;
import android.util.SparseIntArray;
-import android.view.SurfaceControl;
import android.window.StartingWindowInfo;
import android.window.StartingWindowInfo.StartingWindowType;
+import android.window.StartingWindowRemovalInfo;
import android.window.TaskOrganizer;
import android.window.TaskSnapshot;
@@ -68,7 +66,7 @@
public class StartingWindowController implements RemoteCallable<StartingWindowController> {
private static final String TAG = StartingWindowController.class.getSimpleName();
- public static final boolean DEBUG_SPLASH_SCREEN = Build.isDebuggable();
+ public static final boolean DEBUG_SPLASH_SCREEN = false;
public static final boolean DEBUG_TASK_SNAPSHOT = false;
private static final long TASK_BG_COLOR_RETAIN_TIME_MS = 5000;
@@ -186,13 +184,12 @@
/**
* Called when the content of a task is ready to show, starting window can be removed.
*/
- public void removeStartingWindow(int taskId, SurfaceControl leash, Rect frame,
- boolean playRevealAnimation) {
+ public void removeStartingWindow(StartingWindowRemovalInfo removalInfo) {
mSplashScreenExecutor.execute(() -> mStartingSurfaceDrawer.removeStartingWindow(
- taskId, leash, frame, playRevealAnimation));
+ removalInfo));
mSplashScreenExecutor.executeDelayed(() -> {
synchronized (mTaskBackgroundColors) {
- mTaskBackgroundColors.delete(taskId);
+ mTaskBackgroundColors.delete(removalInfo.taskId);
}
}, TASK_BG_COLOR_RETAIN_TIME_MS);
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/TaskSnapshotWindow.java b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/TaskSnapshotWindow.java
index 979aa1f..3e88c46 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/TaskSnapshotWindow.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/TaskSnapshotWindow.java
@@ -123,14 +123,13 @@
* Ideally the delay time will be shorter when receiving
* {@link StartingSurfaceDrawer#onImeDrawnOnTask(int)}.
*/
- private static final long MAX_DELAY_REMOVAL_TIME_IME_VISIBLE = 450;
+ private static final long MAX_DELAY_REMOVAL_TIME_IME_VISIBLE = 600;
//tmp vars for unused relayout params
private static final Point TMP_SURFACE_SIZE = new Point();
private final Window mWindow;
private final Runnable mClearWindowHandler;
- private final long mDelayRemovalTime;
private final ShellExecutor mSplashScreenExecutor;
private final SurfaceControl mSurfaceControl;
private final IWindowSession mSession;
@@ -221,13 +220,10 @@
taskDescription.setBackgroundColor(WHITE);
}
- final long delayRemovalTime = snapshot.hasImeSurface() ? MAX_DELAY_REMOVAL_TIME_IME_VISIBLE
- : DELAY_REMOVAL_TIME_GENERAL;
-
final TaskSnapshotWindow snapshotSurface = new TaskSnapshotWindow(
surfaceControl, snapshot, layoutParams.getTitle(), taskDescription, appearance,
windowFlags, windowPrivateFlags, taskBounds, orientation, activityType,
- delayRemovalTime, topWindowInsetsState, clearWindowHandler, splashScreenExecutor);
+ topWindowInsetsState, clearWindowHandler, splashScreenExecutor);
final Window window = snapshotSurface.mWindow;
final InsetsState tmpInsetsState = new InsetsState();
@@ -265,9 +261,8 @@
public TaskSnapshotWindow(SurfaceControl surfaceControl,
TaskSnapshot snapshot, CharSequence title, TaskDescription taskDescription,
int appearance, int windowFlags, int windowPrivateFlags, Rect taskBounds,
- int currentOrientation, int activityType, long delayRemovalTime,
- InsetsState topWindowInsetsState, Runnable clearWindowHandler,
- ShellExecutor splashScreenExecutor) {
+ int currentOrientation, int activityType, InsetsState topWindowInsetsState,
+ Runnable clearWindowHandler, ShellExecutor splashScreenExecutor) {
mSplashScreenExecutor = splashScreenExecutor;
mSession = WindowManagerGlobal.getWindowSession();
mWindow = new Window();
@@ -283,7 +278,6 @@
mStatusBarColor = taskDescription.getStatusBarColor();
mOrientationOnCreation = currentOrientation;
mActivityType = activityType;
- mDelayRemovalTime = delayRemovalTime;
mTransaction = new SurfaceControl.Transaction();
mClearWindowHandler = clearWindowHandler;
mHasImeSurface = snapshot.hasImeSurface();
@@ -314,7 +308,7 @@
mSystemBarBackgroundPainter.drawNavigationBarBackground(c);
}
- void scheduleRemove(Runnable onRemove) {
+ void scheduleRemove(Runnable onRemove, boolean deferRemoveForIme) {
// Show the latest content as soon as possible for unlocking to home.
if (mActivityType == ACTIVITY_TYPE_HOME) {
removeImmediately();
@@ -329,9 +323,12 @@
TaskSnapshotWindow.this.removeImmediately();
onRemove.run();
};
- mSplashScreenExecutor.executeDelayed(mScheduledRunnable, mDelayRemovalTime);
+ final long delayRemovalTime = mHasImeSurface && deferRemoveForIme
+ ? MAX_DELAY_REMOVAL_TIME_IME_VISIBLE
+ : DELAY_REMOVAL_TIME_GENERAL;
+ mSplashScreenExecutor.executeDelayed(mScheduledRunnable, delayRemovalTime);
if (DEBUG) {
- Slog.d(TAG, "Defer removing snapshot surface in " + mDelayRemovalTime);
+ Slog.d(TAG, "Defer removing snapshot surface in " + delayRemovalTime);
}
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java
index b673d48..663d647 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java
@@ -41,6 +41,7 @@
import static android.window.TransitionInfo.FLAG_SHOW_WALLPAPER;
import static android.window.TransitionInfo.FLAG_STARTING_WINDOW_TRANSFER_RECIPIENT;
import static android.window.TransitionInfo.FLAG_TRANSLUCENT;
+import static android.window.TransitionInfo.isIndependent;
import static com.android.internal.policy.TransitionAnimation.WALLPAPER_TRANSITION_CLOSE;
import static com.android.internal.policy.TransitionAnimation.WALLPAPER_TRANSITION_INTRA_CLOSE;
@@ -70,6 +71,7 @@
import android.view.animation.Transformation;
import android.window.TransitionInfo;
import android.window.TransitionRequestInfo;
+import android.window.WindowContainerToken;
import android.window.WindowContainerTransaction;
import com.android.internal.R;
@@ -82,6 +84,7 @@
import com.android.wm.shell.common.ShellExecutor;
import com.android.wm.shell.common.TransactionPool;
import com.android.wm.shell.protolog.ShellProtoLogGroup;
+import com.android.wm.shell.util.CounterRotator;
import java.util.ArrayList;
@@ -269,9 +272,16 @@
final ArrayList<Animator> animations = new ArrayList<>();
mAnimations.put(transition, animations);
+ final ArrayMap<WindowContainerToken, CounterRotator> counterRotators = new ArrayMap<>();
+
final Runnable onAnimFinish = () -> {
if (!animations.isEmpty()) return;
+ for (int i = 0; i < counterRotators.size(); ++i) {
+ counterRotators.valueAt(i).cleanUp(info.getRootLeash());
+ }
+ counterRotators.clear();
+
if (mRotationAnimation != null) {
mRotationAnimation.kill();
mRotationAnimation = null;
@@ -285,16 +295,44 @@
for (int i = info.getChanges().size() - 1; i >= 0; --i) {
final TransitionInfo.Change change = info.getChanges().get(i);
- if (info.getType() == TRANSIT_CHANGE && change.getMode() == TRANSIT_CHANGE
- && (change.getFlags() & FLAG_IS_DISPLAY) != 0) {
- boolean isSeamless = isRotationSeamless(info, mDisplayController);
- final int anim = getRotationAnimation(info);
- if (!(isSeamless || anim == ROTATION_ANIMATION_JUMPCUT)) {
- mRotationAnimation = new ScreenRotationAnimation(mContext, mSurfaceSession,
- mTransactionPool, startTransaction, change, info.getRootLeash());
- mRotationAnimation.startAnimation(animations, onAnimFinish,
- mTransitionAnimationScaleSetting, mMainExecutor, mAnimExecutor);
- continue;
+ if (change.getMode() == TRANSIT_CHANGE && (change.getFlags() & FLAG_IS_DISPLAY) != 0) {
+ int rotateDelta = change.getEndRotation() - change.getStartRotation();
+ int displayW = change.getEndAbsBounds().width();
+ int displayH = change.getEndAbsBounds().height();
+ if (info.getType() == TRANSIT_CHANGE) {
+ boolean isSeamless = isRotationSeamless(info, mDisplayController);
+ final int anim = getRotationAnimation(info);
+ if (!(isSeamless || anim == ROTATION_ANIMATION_JUMPCUT)) {
+ mRotationAnimation = new ScreenRotationAnimation(mContext, mSurfaceSession,
+ mTransactionPool, startTransaction, change, info.getRootLeash());
+ mRotationAnimation.startAnimation(animations, onAnimFinish,
+ mTransitionAnimationScaleSetting, mMainExecutor, mAnimExecutor);
+ continue;
+ }
+ } else {
+ // opening/closing an app into a new orientation. Counter-rotate all
+ // "going-away" things since they are still in the old orientation.
+ for (int j = info.getChanges().size() - 1; j >= 0; --j) {
+ final TransitionInfo.Change innerChange = info.getChanges().get(j);
+ if (!Transitions.isClosingType(innerChange.getMode())
+ || !isIndependent(innerChange, info)
+ || innerChange.getParent() == null) {
+ continue;
+ }
+ CounterRotator crot = counterRotators.get(innerChange.getParent());
+ if (crot == null) {
+ crot = new CounterRotator();
+ crot.setup(startTransaction,
+ info.getChange(innerChange.getParent()).getLeash(),
+ rotateDelta, displayW, displayH);
+ if (crot.getSurface() != null) {
+ int layer = info.getChanges().size() - j;
+ startTransaction.setLayer(crot.getSurface(), layer);
+ }
+ counterRotators.put(innerChange.getParent(), crot);
+ }
+ crot.addChild(startTransaction, innerChange.getLeash());
+ }
}
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/unfold/ShellUnfoldProgressProvider.java b/libs/WindowManager/Shell/src/com/android/wm/shell/unfold/ShellUnfoldProgressProvider.java
new file mode 100644
index 0000000..74e4812
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/unfold/ShellUnfoldProgressProvider.java
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell.unfold;
+
+import android.annotation.FloatRange;
+
+import java.util.concurrent.Executor;
+
+/**
+ * Wrapper interface for unfold transition progress provider for the Shell
+ * @see com.android.systemui.unfold.UnfoldTransitionProgressProvider
+ */
+public interface ShellUnfoldProgressProvider {
+
+ /**
+ * Adds a transition listener
+ */
+ void addListener(Executor executor, UnfoldListener listener);
+
+ /**
+ * Listener for receiving unfold updates
+ */
+ interface UnfoldListener {
+ default void onStateChangeStarted() {}
+
+ default void onStateChangeProgress(@FloatRange(from = 0.0, to = 1.0) float progress) {}
+
+ default void onStateChangeFinished() {}
+ }
+}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/unfold/UnfoldBackgroundController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/unfold/UnfoldBackgroundController.java
new file mode 100644
index 0000000..9faf454
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/unfold/UnfoldBackgroundController.java
@@ -0,0 +1,89 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell.unfold;
+
+import static android.graphics.Color.blue;
+import static android.graphics.Color.green;
+import static android.graphics.Color.red;
+import static android.view.Display.DEFAULT_DISPLAY;
+
+import android.annotation.NonNull;
+import android.content.Context;
+import android.view.SurfaceControl;
+
+import com.android.wm.shell.R;
+import com.android.wm.shell.RootTaskDisplayAreaOrganizer;
+
+/**
+ * Controls background color layer for the unfold animations
+ */
+public class UnfoldBackgroundController {
+
+ private static final int BACKGROUND_LAYER_Z_INDEX = -1;
+
+ private final RootTaskDisplayAreaOrganizer mRootTaskDisplayAreaOrganizer;
+ private final float[] mBackgroundColor;
+ private SurfaceControl mBackgroundLayer;
+
+ public UnfoldBackgroundController(
+ @NonNull Context context,
+ @NonNull RootTaskDisplayAreaOrganizer rootTaskDisplayAreaOrganizer) {
+ mRootTaskDisplayAreaOrganizer = rootTaskDisplayAreaOrganizer;
+ mBackgroundColor = getBackgroundColor(context);
+ }
+
+ /**
+ * Ensures that unfold animation background color layer is present,
+ * @param transaction where we should add the background if it is not added
+ */
+ public void ensureBackground(@NonNull SurfaceControl.Transaction transaction) {
+ if (mBackgroundLayer != null) return;
+
+ SurfaceControl.Builder colorLayerBuilder = new SurfaceControl.Builder()
+ .setName("app-unfold-background")
+ .setCallsite("AppUnfoldTransitionController")
+ .setColorLayer();
+ mRootTaskDisplayAreaOrganizer.attachToDisplayArea(DEFAULT_DISPLAY, colorLayerBuilder);
+ mBackgroundLayer = colorLayerBuilder.build();
+
+ transaction
+ .setColor(mBackgroundLayer, mBackgroundColor)
+ .show(mBackgroundLayer)
+ .setLayer(mBackgroundLayer, BACKGROUND_LAYER_Z_INDEX);
+ }
+
+ /**
+ * Ensures that the background is not visible
+ * @param transaction as part of which the removal will happen if needed
+ */
+ public void removeBackground(@NonNull SurfaceControl.Transaction transaction) {
+ if (mBackgroundLayer == null) return;
+ if (mBackgroundLayer.isValid()) {
+ transaction.remove(mBackgroundLayer);
+ }
+ mBackgroundLayer = null;
+ }
+
+ private float[] getBackgroundColor(Context context) {
+ int colorInt = context.getResources().getColor(R.color.unfold_transition_background);
+ return new float[]{
+ (float) red(colorInt) / 255.0F,
+ (float) green(colorInt) / 255.0F,
+ (float) blue(colorInt) / 255.0F
+ };
+ }
+}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/util/CounterRotator.java b/libs/WindowManager/Shell/src/com/android/wm/shell/util/CounterRotator.java
new file mode 100644
index 0000000..b9b6716
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/util/CounterRotator.java
@@ -0,0 +1,87 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell.util;
+
+import android.view.SurfaceControl;
+
+import java.util.ArrayList;
+
+/**
+ * Utility class that takes care of counter-rotating surfaces during a transition animation.
+ */
+public class CounterRotator {
+ SurfaceControl mSurface = null;
+ ArrayList<SurfaceControl> mRotateChildren = null;
+
+ /** Gets the surface with the counter-rotation. */
+ public SurfaceControl getSurface() {
+ return mSurface;
+ }
+
+ /**
+ * Sets up this rotator.
+ *
+ * @param rotateDelta is the forward rotation change (the rotation the display is making).
+ * @param displayW (and H) Is the size of the rotating display.
+ */
+ public void setup(SurfaceControl.Transaction t, SurfaceControl parent, int rotateDelta,
+ float displayW, float displayH) {
+ if (rotateDelta == 0) return;
+ mRotateChildren = new ArrayList<>();
+ // We want to counter-rotate, so subtract from 4
+ rotateDelta = 4 - (rotateDelta + 4) % 4;
+ mSurface = new SurfaceControl.Builder()
+ .setName("Transition Unrotate")
+ .setContainerLayer()
+ .setParent(parent)
+ .build();
+ // column-major
+ if (rotateDelta == 1) {
+ t.setMatrix(mSurface, 0, 1, -1, 0);
+ t.setPosition(mSurface, displayW, 0);
+ } else if (rotateDelta == 2) {
+ t.setMatrix(mSurface, -1, 0, 0, -1);
+ t.setPosition(mSurface, displayW, displayH);
+ } else if (rotateDelta == 3) {
+ t.setMatrix(mSurface, 0, -1, 1, 0);
+ t.setPosition(mSurface, 0, displayH);
+ }
+ t.show(mSurface);
+ }
+
+ /**
+ * Add a surface that needs to be counter-rotate.
+ */
+ public void addChild(SurfaceControl.Transaction t, SurfaceControl child) {
+ if (mSurface == null) return;
+ t.reparent(child, mSurface);
+ mRotateChildren.add(child);
+ }
+
+ /**
+ * Clean-up. This undoes any reparenting and effectively stops the counter-rotation.
+ */
+ public void cleanUp(SurfaceControl rootLeash) {
+ if (mSurface == null) return;
+ SurfaceControl.Transaction t = new SurfaceControl.Transaction();
+ for (int i = mRotateChildren.size() - 1; i >= 0; --i) {
+ t.reparent(mRotateChildren.get(i), rootLeash);
+ }
+ t.remove(mSurface);
+ t.apply();
+ }
+}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/CommonAssertions.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/CommonAssertions.kt
index b36468b..c07f0eb 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/CommonAssertions.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/CommonAssertions.kt
@@ -14,13 +14,14 @@
* limitations under the License.
*/
+@file:JvmName("CommonAssertions")
package com.android.wm.shell.flicker
-import android.content.ComponentName
import android.graphics.Region
import android.view.Surface
import com.android.server.wm.flicker.FlickerTestParameter
import com.android.server.wm.flicker.helpers.WindowUtils
+import com.android.server.wm.traces.common.FlickerComponentName
fun FlickerTestParameter.appPairsDividerIsVisibleAtEnd() {
assertLayersEnd {
@@ -72,7 +73,7 @@
fun FlickerTestParameter.appPairsPrimaryBoundsIsVisibleAtEnd(
rotation: Int,
- primaryComponent: ComponentName
+ primaryComponent: FlickerComponentName
) {
assertLayersEnd {
val dividerRegion = layer(APP_PAIR_SPLIT_DIVIDER_COMPONENT).visibleRegion.region
@@ -83,7 +84,7 @@
fun FlickerTestParameter.dockedStackPrimaryBoundsIsVisibleAtEnd(
rotation: Int,
- primaryComponent: ComponentName
+ primaryComponent: FlickerComponentName
) {
assertLayersEnd {
val dividerRegion = layer(DOCKED_STACK_DIVIDER_COMPONENT).visibleRegion.region
@@ -94,7 +95,7 @@
fun FlickerTestParameter.appPairsSecondaryBoundsIsVisibleAtEnd(
rotation: Int,
- secondaryComponent: ComponentName
+ secondaryComponent: FlickerComponentName
) {
assertLayersEnd {
val dividerRegion = layer(APP_PAIR_SPLIT_DIVIDER_COMPONENT).visibleRegion.region
@@ -105,7 +106,7 @@
fun FlickerTestParameter.dockedStackSecondaryBoundsIsVisibleAtEnd(
rotation: Int,
- secondaryComponent: ComponentName
+ secondaryComponent: FlickerComponentName
) {
assertLayersEnd {
val dividerRegion = layer(DOCKED_STACK_DIVIDER_COMPONENT).visibleRegion.region
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/CommonConstants.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/CommonConstants.kt
index ff1a6e6..40891f3 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/CommonConstants.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/CommonConstants.kt
@@ -17,8 +17,8 @@
@file:JvmName("CommonConstants")
package com.android.wm.shell.flicker
-import android.content.ComponentName
+import com.android.server.wm.traces.common.FlickerComponentName
const val SYSTEM_UI_PACKAGE_NAME = "com.android.systemui"
-val APP_PAIR_SPLIT_DIVIDER_COMPONENT = ComponentName("", "AppPairSplitDivider#")
-val DOCKED_STACK_DIVIDER_COMPONENT = ComponentName("", "DockedStackDivider#")
\ No newline at end of file
+val APP_PAIR_SPLIT_DIVIDER_COMPONENT = FlickerComponentName("", "AppPairSplitDivider#")
+val DOCKED_STACK_DIVIDER_COMPONENT = FlickerComponentName("", "DockedStackDivider#")
\ No newline at end of file
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/WaitUtils.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/WaitUtils.kt
index a6d6735..b63d9ff 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/WaitUtils.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/WaitUtils.kt
@@ -14,6 +14,7 @@
* limitations under the License.
*/
+@file:JvmName("WaitUtils")
package com.android.wm.shell.flicker
import android.os.SystemClock
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTestCannotPairNonResizeableApps.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTestCannotPairNonResizeableApps.kt
index 19374ed..038be9c 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTestCannotPairNonResizeableApps.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTestCannotPairNonResizeableApps.kt
@@ -100,8 +100,8 @@
"Non resizeable app not initialized"
}
testSpec.assertWmEnd {
- isVisible(nonResizeableApp.component)
- isInvisible(primaryApp.component)
+ isAppWindowVisible(nonResizeableApp.component)
+ isAppWindowInvisible(primaryApp.component)
}
}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTestPairPrimaryAndSecondaryApps.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTestPairPrimaryAndSecondaryApps.kt
index 46ee892..bbc6b2d 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTestPairPrimaryAndSecondaryApps.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTestPairPrimaryAndSecondaryApps.kt
@@ -77,8 +77,8 @@
@Test
fun bothAppWindowsVisible() {
testSpec.assertWmEnd {
- isVisible(primaryApp.component)
- isVisible(secondaryApp.component)
+ isAppWindowVisible(primaryApp.component)
+ isAppWindowVisible(secondaryApp.component)
}
}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTestSupportPairNonResizeableApps.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTestSupportPairNonResizeableApps.kt
index f7ced71..bb784a8 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTestSupportPairNonResizeableApps.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTestSupportPairNonResizeableApps.kt
@@ -100,8 +100,8 @@
"Non resizeable app not initialized"
}
testSpec.assertWmEnd {
- isVisible(nonResizeableApp.component)
- isVisible(primaryApp.component)
+ isAppWindowVisible(nonResizeableApp.component)
+ isAppWindowVisible(primaryApp.component)
}
}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTestUnpairPrimaryAndSecondaryApps.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTestUnpairPrimaryAndSecondaryApps.kt
index 3debdd3..a1a4db1 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTestUnpairPrimaryAndSecondaryApps.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTestUnpairPrimaryAndSecondaryApps.kt
@@ -81,8 +81,8 @@
@Test
fun bothAppWindowsInvisible() {
testSpec.assertWmEnd {
- isInvisible(primaryApp.component)
- isInvisible(secondaryApp.component)
+ isAppWindowInvisible(primaryApp.component)
+ isAppWindowInvisible(secondaryApp.component)
}
}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTransition.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTransition.kt
index cdf89a5..c1ec324 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTransition.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTransition.kt
@@ -38,6 +38,7 @@
import com.android.server.wm.flicker.statusBarLayerIsVisible
import com.android.server.wm.flicker.statusBarLayerRotatesScales
import com.android.server.wm.flicker.statusBarWindowIsVisible
+import com.android.server.wm.traces.parser.toFlickerComponent
import com.android.wm.shell.flicker.helpers.AppPairsHelper
import com.android.wm.shell.flicker.helpers.BaseAppHelper
import com.android.wm.shell.flicker.helpers.MultiWindowHelper.Companion.getDevEnableNonResizableMultiWindow
@@ -55,7 +56,7 @@
protected val activityHelper = ActivityHelper.getInstance()
protected val appPairsHelper = AppPairsHelper(instrumentation,
Components.SplitScreenActivity.LABEL,
- Components.SplitScreenActivity.COMPONENT)
+ Components.SplitScreenActivity.COMPONENT.toFlickerComponent())
protected val primaryApp = SplitScreenHelper.getPrimary(instrumentation)
protected val secondaryApp = SplitScreenHelper.getSecondary(instrumentation)
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/RotateTwoLaunchedAppsInAppPairsMode.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/RotateTwoLaunchedAppsInAppPairsMode.kt
index 3e782e6..56a2531 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/RotateTwoLaunchedAppsInAppPairsMode.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/RotateTwoLaunchedAppsInAppPairsMode.kt
@@ -73,8 +73,8 @@
@Test
fun bothAppWindowsVisible() {
testSpec.assertWmEnd {
- isVisible(primaryApp.component)
- .isVisible(secondaryApp.component)
+ isAppWindowVisible(primaryApp.component)
+ isAppWindowVisible(secondaryApp.component)
}
}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/RotateTwoLaunchedAppsRotateAndEnterAppPairsMode.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/RotateTwoLaunchedAppsRotateAndEnterAppPairsMode.kt
index ee28c7a..0699a4f 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/RotateTwoLaunchedAppsRotateAndEnterAppPairsMode.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/RotateTwoLaunchedAppsRotateAndEnterAppPairsMode.kt
@@ -85,8 +85,8 @@
@Test
fun bothAppWindowsVisible() {
testSpec.assertWmEnd {
- isVisible(primaryApp.component)
- isVisible(secondaryApp.component)
+ isAppWindowVisible(primaryApp.component)
+ isAppWindowVisible(secondaryApp.component)
}
}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/AppPairsHelper.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/AppPairsHelper.kt
index 5a438af..623055f6 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/AppPairsHelper.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/AppPairsHelper.kt
@@ -17,15 +17,15 @@
package com.android.wm.shell.flicker.helpers
import android.app.Instrumentation
-import android.content.ComponentName
import android.graphics.Region
import com.android.server.wm.flicker.Flicker
import com.android.server.wm.flicker.helpers.WindowUtils
+import com.android.server.wm.traces.common.FlickerComponentName
class AppPairsHelper(
instrumentation: Instrumentation,
activityLabel: String,
- component: ComponentName
+ component: FlickerComponentName
) : BaseAppHelper(instrumentation, activityLabel, component) {
fun getPrimaryBounds(dividerBounds: Region): android.graphics.Region {
val primaryAppBounds = Region(0, 0, dividerBounds.bounds.right,
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/BaseAppHelper.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/BaseAppHelper.kt
index f15044e..57bc0d5 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/BaseAppHelper.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/BaseAppHelper.kt
@@ -17,7 +17,6 @@
package com.android.wm.shell.flicker.helpers
import android.app.Instrumentation
-import android.content.ComponentName
import android.content.pm.PackageManager.FEATURE_LEANBACK
import android.content.pm.PackageManager.FEATURE_LEANBACK_ONLY
import android.os.SystemProperties
@@ -28,13 +27,13 @@
import androidx.test.uiautomator.Until
import com.android.compatibility.common.util.SystemUtil
import com.android.server.wm.flicker.helpers.StandardAppHelper
-import com.android.server.wm.traces.parser.toWindowName
+import com.android.server.wm.traces.common.FlickerComponentName
import java.io.IOException
abstract class BaseAppHelper(
instrumentation: Instrumentation,
launcherName: String,
- component: ComponentName
+ component: FlickerComponentName
) : StandardAppHelper(
instrumentation,
launcherName,
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/FixedAppHelper.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/FixedAppHelper.kt
index b4ae187..471e010 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/FixedAppHelper.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/FixedAppHelper.kt
@@ -17,10 +17,11 @@
package com.android.wm.shell.flicker.helpers
import android.app.Instrumentation
+import com.android.server.wm.traces.parser.toFlickerComponent
import com.android.wm.shell.flicker.testapp.Components
class FixedAppHelper(instrumentation: Instrumentation) : BaseAppHelper(
instrumentation,
Components.FixedActivity.LABEL,
- Components.FixedActivity.COMPONENT
+ Components.FixedActivity.COMPONENT.toFlickerComponent()
)
\ No newline at end of file
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/ImeAppHelper.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/ImeAppHelper.kt
index 086e8b7..0f00ede 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/ImeAppHelper.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/ImeAppHelper.kt
@@ -21,13 +21,14 @@
import androidx.test.uiautomator.UiDevice
import androidx.test.uiautomator.Until
import com.android.server.wm.flicker.helpers.FIND_TIMEOUT
+import com.android.server.wm.traces.parser.toFlickerComponent
import com.android.server.wm.traces.parser.windowmanager.WindowManagerStateHelper
import com.android.wm.shell.flicker.testapp.Components
open class ImeAppHelper(instrumentation: Instrumentation) : BaseAppHelper(
instrumentation,
Components.ImeActivity.LABEL,
- Components.ImeActivity.COMPONENT
+ Components.ImeActivity.COMPONENT.toFlickerComponent()
) {
/**
* Opens the IME and wait for it to be displayed
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/MultiWindowHelper.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/MultiWindowHelper.kt
index 7f99e62..12ccbaf 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/MultiWindowHelper.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/MultiWindowHelper.kt
@@ -17,14 +17,14 @@
package com.android.wm.shell.flicker.helpers
import android.app.Instrumentation
-import android.content.ComponentName
import android.content.Context
import android.provider.Settings
+import com.android.server.wm.traces.common.FlickerComponentName
class MultiWindowHelper(
instrumentation: Instrumentation,
activityLabel: String,
- componentsInfo: ComponentName
+ componentsInfo: FlickerComponentName
) : BaseAppHelper(instrumentation, activityLabel, componentsInfo) {
companion object {
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/PipAppHelper.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/PipAppHelper.kt
index 1529f5b..2357b0d 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/PipAppHelper.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/PipAppHelper.kt
@@ -26,6 +26,7 @@
import androidx.test.uiautomator.Until
import com.android.server.wm.flicker.helpers.FIND_TIMEOUT
import com.android.server.wm.flicker.helpers.SYSTEMUI_PACKAGE
+import com.android.server.wm.traces.parser.toFlickerComponent
import com.android.server.wm.traces.parser.windowmanager.WindowManagerStateHelper
import com.android.wm.shell.flicker.pip.tv.closeTvPipWindow
import com.android.wm.shell.flicker.pip.tv.isFocusedOrHasFocusedChild
@@ -34,7 +35,7 @@
class PipAppHelper(instrumentation: Instrumentation) : BaseAppHelper(
instrumentation,
Components.PipActivity.LABEL,
- Components.PipActivity.COMPONENT
+ Components.PipActivity.COMPONENT.toFlickerComponent()
) {
private val mediaSessionManager: MediaSessionManager
get() = context.getSystemService(MediaSessionManager::class.java)
@@ -129,7 +130,7 @@
}
/**
- * Expands the pip window and dismisses it by clicking on the X button.
+ * Taps the pip window and dismisses it by clicking on the X button.
*/
fun closePipWindow(wmHelper: WindowManagerStateHelper) {
if (isTelevision) {
@@ -137,9 +138,12 @@
} else {
val windowRect = getWindowRect(wmHelper)
uiDevice.click(windowRect.centerX(), windowRect.centerY())
- val exitPipObject = uiDevice.findObject(By.res(SYSTEMUI_PACKAGE, "dismiss"))
+ // search and interact with the dismiss button
+ val dismissSelector = By.res(SYSTEMUI_PACKAGE, "dismiss")
+ uiDevice.wait(Until.hasObject(dismissSelector), FIND_TIMEOUT)
+ val dismissPipObject = uiDevice.findObject(dismissSelector)
?: error("PIP window dismiss button not found")
- val dismissButtonBounds = exitPipObject.visibleBounds
+ val dismissButtonBounds = dismissPipObject.visibleBounds
uiDevice.click(dismissButtonBounds.centerX(), dismissButtonBounds.centerY())
}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/SimpleAppHelper.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/SimpleAppHelper.kt
index ba13e38..4d0fbc4 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/SimpleAppHelper.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/SimpleAppHelper.kt
@@ -17,10 +17,11 @@
package com.android.wm.shell.flicker.helpers
import android.app.Instrumentation
+import com.android.server.wm.traces.parser.toFlickerComponent
import com.android.wm.shell.flicker.testapp.Components
class SimpleAppHelper(instrumentation: Instrumentation) : BaseAppHelper(
instrumentation,
Components.SimpleActivity.LABEL,
- Components.SimpleActivity.COMPONENT
+ Components.SimpleActivity.COMPONENT.toFlickerComponent()
)
\ No newline at end of file
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/SplitScreenHelper.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/SplitScreenHelper.kt
index 2d996ca..0ec9b2d 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/SplitScreenHelper.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/SplitScreenHelper.kt
@@ -17,14 +17,15 @@
package com.android.wm.shell.flicker.helpers
import android.app.Instrumentation
-import android.content.ComponentName
import android.content.res.Resources
+import com.android.server.wm.traces.common.FlickerComponentName
+import com.android.server.wm.traces.parser.toFlickerComponent
import com.android.wm.shell.flicker.testapp.Components
class SplitScreenHelper(
instrumentation: Instrumentation,
activityLabel: String,
- componentsInfo: ComponentName
+ componentsInfo: FlickerComponentName
) : BaseAppHelper(instrumentation, activityLabel, componentsInfo) {
companion object {
@@ -39,16 +40,16 @@
fun getPrimary(instrumentation: Instrumentation): SplitScreenHelper =
SplitScreenHelper(instrumentation,
Components.SplitScreenActivity.LABEL,
- Components.SplitScreenActivity.COMPONENT)
+ Components.SplitScreenActivity.COMPONENT.toFlickerComponent())
fun getSecondary(instrumentation: Instrumentation): SplitScreenHelper =
SplitScreenHelper(instrumentation,
Components.SplitScreenSecondaryActivity.LABEL,
- Components.SplitScreenSecondaryActivity.COMPONENT)
+ Components.SplitScreenSecondaryActivity.COMPONENT.toFlickerComponent())
fun getNonResizeable(instrumentation: Instrumentation): SplitScreenHelper =
SplitScreenHelper(instrumentation,
Components.NonResizeableActivity.LABEL,
- Components.NonResizeableActivity.COMPONENT)
+ Components.NonResizeableActivity.COMPONENT.toFlickerComponent())
}
}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/EnterSplitScreenDockActivity.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/EnterSplitScreenDockActivity.kt
index 508e939..bd44d08 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/EnterSplitScreenDockActivity.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/EnterSplitScreenDockActivity.kt
@@ -16,7 +16,6 @@
package com.android.wm.shell.flicker.legacysplitscreen
-import android.content.ComponentName
import android.platform.test.annotations.Presubmit
import android.view.Surface
import android.view.WindowManagerPolicyConstants
@@ -25,13 +24,13 @@
import com.android.server.wm.flicker.FlickerParametersRunnerFactory
import com.android.server.wm.flicker.FlickerTestParameter
import com.android.server.wm.flicker.FlickerTestParameterFactory
-import com.android.server.wm.flicker.annotation.Group1
+import com.android.server.wm.flicker.annotation.Group4
import com.android.server.wm.flicker.dsl.FlickerBuilder
import com.android.server.wm.flicker.helpers.launchSplitScreen
import com.android.server.wm.flicker.navBarWindowIsVisible
import com.android.server.wm.flicker.startRotation
import com.android.server.wm.flicker.statusBarWindowIsVisible
-import com.android.server.wm.traces.parser.windowmanager.WindowManagerStateHelper
+import com.android.server.wm.traces.common.FlickerComponentName
import com.android.wm.shell.flicker.dockedStackDividerBecomesVisible
import com.android.wm.shell.flicker.dockedStackPrimaryBoundsIsVisibleAtEnd
import com.android.wm.shell.flicker.helpers.SplitScreenHelper
@@ -50,7 +49,7 @@
@RunWith(Parameterized::class)
@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-@Group1
+@Group4
class EnterSplitScreenDockActivity(
testSpec: FlickerTestParameter
) : LegacySplitScreenTransition(testSpec) {
@@ -62,10 +61,10 @@
}
}
- override val ignoredWindows: List<ComponentName>
+ override val ignoredWindows: List<FlickerComponentName>
get() = listOf(LAUNCHER_COMPONENT, LIVE_WALLPAPER_COMPONENT,
- splitScreenApp.component, WindowManagerStateHelper.SPLASH_SCREEN_COMPONENT,
- WindowManagerStateHelper.SNAPSHOT_COMPONENT, LAUNCHER_COMPONENT)
+ splitScreenApp.component, FlickerComponentName.SPLASH_SCREEN,
+ FlickerComponentName.SNAPSHOT, LAUNCHER_COMPONENT)
@Presubmit
@Test
@@ -89,7 +88,7 @@
@Test
fun appWindowIsVisible() {
testSpec.assertWmEnd {
- isVisible(splitScreenApp.component)
+ isAppWindowVisible(splitScreenApp.component)
}
}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/EnterSplitScreenFromDetachedRecentTask.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/EnterSplitScreenFromDetachedRecentTask.kt
index 12f3909..625d48b 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/EnterSplitScreenFromDetachedRecentTask.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/EnterSplitScreenFromDetachedRecentTask.kt
@@ -16,16 +16,16 @@
package com.android.wm.shell.flicker.legacysplitscreen
-import android.content.ComponentName
import android.platform.test.annotations.Presubmit
import android.view.Surface
import androidx.test.filters.RequiresDevice
import com.android.server.wm.flicker.FlickerParametersRunnerFactory
import com.android.server.wm.flicker.FlickerTestParameter
import com.android.server.wm.flicker.FlickerTestParameterFactory
+import com.android.server.wm.flicker.annotation.Group4
import com.android.server.wm.flicker.dsl.FlickerBuilder
import com.android.server.wm.flicker.helpers.launchSplitScreen
-import com.android.server.wm.traces.parser.windowmanager.WindowManagerStateHelper
+import com.android.server.wm.traces.common.FlickerComponentName
import com.android.wm.shell.flicker.dockedStackDividerIsVisibleAtEnd
import com.android.wm.shell.flicker.helpers.SplitScreenHelper
import org.junit.FixMethodOrder
@@ -43,6 +43,7 @@
@RunWith(Parameterized::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
+@Group4
class EnterSplitScreenFromDetachedRecentTask(
testSpec: FlickerTestParameter
) : LegacySplitScreenTransition(testSpec) {
@@ -62,10 +63,10 @@
}
}
- override val ignoredWindows: List<ComponentName>
+ override val ignoredWindows: List<FlickerComponentName>
get() = listOf(LAUNCHER_COMPONENT,
- WindowManagerStateHelper.SPLASH_SCREEN_COMPONENT,
- WindowManagerStateHelper.SNAPSHOT_COMPONENT,
+ FlickerComponentName.SPLASH_SCREEN,
+ FlickerComponentName.SNAPSHOT,
splitScreenApp.component)
@Presubmit
@@ -76,7 +77,7 @@
@Test
fun appWindowIsVisible() {
testSpec.assertWmEnd {
- isVisible(splitScreenApp.component)
+ isAppWindowVisible(splitScreenApp.component)
}
}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/EnterSplitScreenLaunchToSide.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/EnterSplitScreenLaunchToSide.kt
index ac85c48..2ed2806 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/EnterSplitScreenLaunchToSide.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/EnterSplitScreenLaunchToSide.kt
@@ -16,21 +16,20 @@
package com.android.wm.shell.flicker.legacysplitscreen
-import android.content.ComponentName
import android.platform.test.annotations.Presubmit
import android.view.Surface
import androidx.test.filters.RequiresDevice
import com.android.server.wm.flicker.FlickerParametersRunnerFactory
import com.android.server.wm.flicker.FlickerTestParameter
import com.android.server.wm.flicker.FlickerTestParameterFactory
-import com.android.server.wm.flicker.annotation.Group1
+import com.android.server.wm.flicker.annotation.Group4
import com.android.server.wm.flicker.dsl.FlickerBuilder
import com.android.server.wm.flicker.helpers.launchSplitScreen
import com.android.server.wm.flicker.helpers.reopenAppFromOverview
import com.android.server.wm.flicker.navBarWindowIsVisible
import com.android.server.wm.flicker.startRotation
import com.android.server.wm.flicker.statusBarWindowIsVisible
-import com.android.server.wm.traces.parser.windowmanager.WindowManagerStateHelper
+import com.android.server.wm.traces.common.FlickerComponentName
import com.android.wm.shell.flicker.dockedStackDividerBecomesVisible
import com.android.wm.shell.flicker.dockedStackPrimaryBoundsIsVisibleAtEnd
import com.android.wm.shell.flicker.dockedStackSecondaryBoundsIsVisibleAtEnd
@@ -49,7 +48,7 @@
@RunWith(Parameterized::class)
@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-@Group1
+@Group4
class EnterSplitScreenLaunchToSide(
testSpec: FlickerTestParameter
) : LegacySplitScreenTransition(testSpec) {
@@ -62,10 +61,10 @@
}
}
- override val ignoredWindows: List<ComponentName>
+ override val ignoredWindows: List<FlickerComponentName>
get() = listOf(LAUNCHER_COMPONENT, splitScreenApp.component,
- secondaryApp.component, WindowManagerStateHelper.SPLASH_SCREEN_COMPONENT,
- WindowManagerStateHelper.SNAPSHOT_COMPONENT)
+ secondaryApp.component, FlickerComponentName.SPLASH_SCREEN,
+ FlickerComponentName.SNAPSHOT)
@Presubmit
@Test
@@ -92,7 +91,7 @@
// Because we log WM once per frame, sometimes the activity and the window
// become visible in the same entry, sometimes not, thus it is not possible to
// assert the visibility of the activity here
- this.isAppWindowInvisible(secondaryApp.component, ignoreActivity = true)
+ this.isAppWindowInvisible(secondaryApp.component)
.then()
// during re-parenting, the window may disappear and reappear from the
// trace, this occurs because we log only 1x per frame
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/EnterSplitScreenNotSupportNonResizable.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/EnterSplitScreenNotSupportNonResizable.kt
index 964af23..ee6cf34 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/EnterSplitScreenNotSupportNonResizable.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/EnterSplitScreenNotSupportNonResizable.kt
@@ -16,17 +16,16 @@
package com.android.wm.shell.flicker.legacysplitscreen
-import android.content.ComponentName
import android.platform.test.annotations.Presubmit
import android.view.Surface
import androidx.test.filters.RequiresDevice
import com.android.server.wm.flicker.FlickerParametersRunnerFactory
import com.android.server.wm.flicker.FlickerTestParameter
import com.android.server.wm.flicker.FlickerTestParameterFactory
-import com.android.server.wm.flicker.annotation.Group1
+import com.android.server.wm.flicker.annotation.Group4
import com.android.server.wm.flicker.dsl.FlickerBuilder
import com.android.server.wm.flicker.helpers.canSplitScreen
-import com.android.server.wm.traces.parser.windowmanager.WindowManagerStateHelper
+import com.android.server.wm.traces.common.FlickerComponentName
import com.android.wm.shell.flicker.dockedStackDividerNotExistsAtEnd
import com.android.wm.shell.flicker.helpers.MultiWindowHelper.Companion.resetMultiWindowConfig
import com.android.wm.shell.flicker.helpers.MultiWindowHelper.Companion.setSupportsNonResizableMultiWindow
@@ -51,7 +50,7 @@
@RunWith(Parameterized::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
-@Group1
+@Group4
class EnterSplitScreenNotSupportNonResizable(
testSpec: FlickerTestParameter
) : LegacySplitScreenTransition(testSpec) {
@@ -71,10 +70,10 @@
}
}
- override val ignoredWindows: List<ComponentName>
+ override val ignoredWindows: List<FlickerComponentName>
get() = listOf(LAUNCHER_COMPONENT,
- WindowManagerStateHelper.SPLASH_SCREEN_COMPONENT,
- WindowManagerStateHelper.SNAPSHOT_COMPONENT,
+ FlickerComponentName.SPLASH_SCREEN,
+ FlickerComponentName.SNAPSHOT,
nonResizeableApp.component,
splitScreenApp.component)
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/EnterSplitScreenSupportNonResizable.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/EnterSplitScreenSupportNonResizable.kt
index 1b8afa6..163b6ffda 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/EnterSplitScreenSupportNonResizable.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/EnterSplitScreenSupportNonResizable.kt
@@ -16,7 +16,6 @@
package com.android.wm.shell.flicker.legacysplitscreen
-import android.content.ComponentName
import android.platform.test.annotations.Presubmit
import android.view.Surface
import androidx.test.filters.RequiresDevice
@@ -26,7 +25,7 @@
import com.android.server.wm.flicker.annotation.Group2
import com.android.server.wm.flicker.dsl.FlickerBuilder
import com.android.server.wm.flicker.helpers.launchSplitScreen
-import com.android.server.wm.traces.parser.windowmanager.WindowManagerStateHelper
+import com.android.server.wm.traces.common.FlickerComponentName
import com.android.wm.shell.flicker.dockedStackDividerIsVisibleAtEnd
import com.android.wm.shell.flicker.helpers.MultiWindowHelper.Companion.resetMultiWindowConfig
import com.android.wm.shell.flicker.helpers.MultiWindowHelper.Companion.setSupportsNonResizableMultiWindow
@@ -68,12 +67,12 @@
}
}
- override val ignoredWindows: List<ComponentName>
+ override val ignoredWindows: List<FlickerComponentName>
get() = listOf(LAUNCHER_COMPONENT,
- WindowManagerStateHelper.SPLASH_SCREEN_COMPONENT,
- WindowManagerStateHelper.SNAPSHOT_COMPONENT,
- nonResizeableApp.component,
- splitScreenApp.component)
+ FlickerComponentName.SPLASH_SCREEN,
+ FlickerComponentName.SNAPSHOT,
+ nonResizeableApp.component,
+ splitScreenApp.component)
@Before
override fun setup() {
@@ -95,7 +94,7 @@
@Test
fun appWindowIsVisible() {
testSpec.assertWmEnd {
- isVisible(nonResizeableApp.component)
+ isAppWindowVisible(nonResizeableApp.component)
}
}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/ExitLegacySplitScreenFromBottom.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/ExitLegacySplitScreenFromBottom.kt
index 247965f..2b629b0 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/ExitLegacySplitScreenFromBottom.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/ExitLegacySplitScreenFromBottom.kt
@@ -16,7 +16,6 @@
package com.android.wm.shell.flicker.legacysplitscreen
-import android.content.ComponentName
import android.platform.test.annotations.Postsubmit
import android.view.Surface
import androidx.test.filters.FlakyTest
@@ -30,7 +29,7 @@
import com.android.server.wm.flicker.helpers.launchSplitScreen
import com.android.server.wm.flicker.navBarWindowIsVisible
import com.android.server.wm.flicker.statusBarWindowIsVisible
-import com.android.server.wm.traces.parser.windowmanager.WindowManagerStateHelper
+import com.android.server.wm.traces.common.FlickerComponentName
import com.android.wm.shell.flicker.DOCKED_STACK_DIVIDER_COMPONENT
import com.android.wm.shell.flicker.helpers.SplitScreenHelper
import org.junit.FixMethodOrder
@@ -70,10 +69,10 @@
}
}
- override val ignoredWindows: List<ComponentName>
- get() = listOf(LAUNCHER_COMPONENT, WindowManagerStateHelper.SPLASH_SCREEN_COMPONENT,
+ override val ignoredWindows: List<FlickerComponentName>
+ get() = listOf(LAUNCHER_COMPONENT, FlickerComponentName.SPLASH_SCREEN,
splitScreenApp.component, secondaryApp.component,
- WindowManagerStateHelper.SNAPSHOT_COMPONENT)
+ FlickerComponentName.SNAPSHOT)
@Postsubmit
@Test
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/ExitPrimarySplitScreenShowSecondaryFullscreen.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/ExitPrimarySplitScreenShowSecondaryFullscreen.kt
index ff34364..95fe3be 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/ExitPrimarySplitScreenShowSecondaryFullscreen.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/ExitPrimarySplitScreenShowSecondaryFullscreen.kt
@@ -16,7 +16,6 @@
package com.android.wm.shell.flicker.legacysplitscreen
-import android.content.ComponentName
import android.platform.test.annotations.Presubmit
import android.view.Surface
import androidx.test.filters.FlakyTest
@@ -30,7 +29,7 @@
import com.android.server.wm.flicker.helpers.reopenAppFromOverview
import com.android.server.wm.flicker.navBarWindowIsVisible
import com.android.server.wm.flicker.statusBarWindowIsVisible
-import com.android.server.wm.traces.parser.windowmanager.WindowManagerStateHelper
+import com.android.server.wm.traces.common.FlickerComponentName
import com.android.wm.shell.flicker.dockedStackDividerNotExistsAtEnd
import com.android.wm.shell.flicker.helpers.SplitScreenHelper
import org.junit.FixMethodOrder
@@ -70,10 +69,10 @@
}
}
- override val ignoredWindows: List<ComponentName>
- get() = listOf(LAUNCHER_COMPONENT, WindowManagerStateHelper.SPLASH_SCREEN_COMPONENT,
+ override val ignoredWindows: List<FlickerComponentName>
+ get() = listOf(LAUNCHER_COMPONENT, FlickerComponentName.SPLASH_SCREEN,
splitScreenApp.component, secondaryApp.component,
- WindowManagerStateHelper.SNAPSHOT_COMPONENT)
+ FlickerComponentName.SNAPSHOT)
@Presubmit
@Test
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/LegacySplitScreenFromIntentNotSupportNonResizable.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/LegacySplitScreenFromIntentNotSupportNonResizable.kt
index 95e4085..f7d628d 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/LegacySplitScreenFromIntentNotSupportNonResizable.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/LegacySplitScreenFromIntentNotSupportNonResizable.kt
@@ -16,7 +16,6 @@
package com.android.wm.shell.flicker.legacysplitscreen
-import android.content.ComponentName
import android.platform.test.annotations.Presubmit
import android.view.Surface
import androidx.test.filters.RequiresDevice
@@ -26,7 +25,7 @@
import com.android.server.wm.flicker.annotation.Group2
import com.android.server.wm.flicker.dsl.FlickerBuilder
import com.android.server.wm.flicker.helpers.launchSplitScreen
-import com.android.server.wm.traces.parser.windowmanager.WindowManagerStateHelper
+import com.android.server.wm.traces.common.FlickerComponentName
import com.android.wm.shell.flicker.DOCKED_STACK_DIVIDER_COMPONENT
import com.android.wm.shell.flicker.dockedStackDividerNotExistsAtEnd
import com.android.wm.shell.flicker.helpers.MultiWindowHelper.Companion.resetMultiWindowConfig
@@ -69,11 +68,11 @@
}
}
- override val ignoredWindows: List<ComponentName>
+ override val ignoredWindows: List<FlickerComponentName>
get() = listOf(DOCKED_STACK_DIVIDER_COMPONENT, LAUNCHER_COMPONENT, LETTERBOX_COMPONENT,
- nonResizeableApp.component, splitScreenApp.component,
- WindowManagerStateHelper.SPLASH_SCREEN_COMPONENT,
- WindowManagerStateHelper.SNAPSHOT_COMPONENT)
+ nonResizeableApp.component, splitScreenApp.component,
+ FlickerComponentName.SPLASH_SCREEN,
+ FlickerComponentName.SNAPSHOT)
@Before
override fun setup() {
@@ -120,12 +119,12 @@
// when the activity gets PAUSED the window may still be marked as visible
// it will be updated in the next log entry. This occurs because we record 1x
// per frame, thus ignore activity check here
- this.isAppWindowVisible(splitScreenApp.component, ignoreActivity = true)
+ this.isAppWindowVisible(splitScreenApp.component)
.then()
// immediately after the window (after onResume and before perform relayout)
// the activity is invisible. This may or not be logged, since we record 1x
// per frame, thus ignore activity check here
- .isAppWindowInvisible(splitScreenApp.component, ignoreActivity = true)
+ .isAppWindowInvisible(splitScreenApp.component)
}
}
@@ -142,13 +141,12 @@
.then()
// we log once per frame, upon logging, window may be visible or not depending
// on what was processed until that moment. Both behaviors are correct
- .isAppWindowInvisible(nonResizeableApp.component,
- ignoreActivity = true, isOptional = true)
+ .isAppWindowInvisible(nonResizeableApp.component, isOptional = true)
.then()
// immediately after the window (after onResume and before perform relayout)
// the activity is invisible. This may or not be logged, since we record 1x
// per frame, thus ignore activity check here
- .isAppWindowVisible(nonResizeableApp.component, ignoreActivity = true)
+ .isAppWindowVisible(nonResizeableApp.component)
}
}
@@ -159,7 +157,7 @@
@Test
fun nonResizableAppWindowBecomesVisibleAtEnd() {
testSpec.assertWmEnd {
- this.isVisible(nonResizeableApp.component)
+ isAppWindowVisible(nonResizeableApp.component)
}
}
@@ -171,8 +169,8 @@
@Test
fun onlyNonResizableAppWindowIsVisibleAtEnd() {
testSpec.assertWmEnd {
- isInvisible(splitScreenApp.component)
- isVisible(nonResizeableApp.component)
+ isAppWindowInvisible(splitScreenApp.component)
+ isAppWindowVisible(nonResizeableApp.component)
}
}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/LegacySplitScreenFromIntentSupportNonResizable.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/LegacySplitScreenFromIntentSupportNonResizable.kt
index 65346aa..a5c6571 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/LegacySplitScreenFromIntentSupportNonResizable.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/LegacySplitScreenFromIntentSupportNonResizable.kt
@@ -16,7 +16,6 @@
package com.android.wm.shell.flicker.legacysplitscreen
-import android.content.ComponentName
import android.platform.test.annotations.Presubmit
import android.view.Surface
import androidx.test.filters.RequiresDevice
@@ -26,7 +25,7 @@
import com.android.server.wm.flicker.annotation.Group2
import com.android.server.wm.flicker.dsl.FlickerBuilder
import com.android.server.wm.flicker.helpers.launchSplitScreen
-import com.android.server.wm.traces.parser.windowmanager.WindowManagerStateHelper
+import com.android.server.wm.traces.common.FlickerComponentName
import com.android.wm.shell.flicker.DOCKED_STACK_DIVIDER_COMPONENT
import com.android.wm.shell.flicker.dockedStackDividerIsVisibleAtEnd
import com.android.wm.shell.flicker.helpers.MultiWindowHelper.Companion.resetMultiWindowConfig
@@ -69,11 +68,11 @@
}
}
- override val ignoredWindows: List<ComponentName>
+ override val ignoredWindows: List<FlickerComponentName>
get() = listOf(DOCKED_STACK_DIVIDER_COMPONENT, LAUNCHER_COMPONENT, LETTERBOX_COMPONENT,
nonResizeableApp.component, splitScreenApp.component,
- WindowManagerStateHelper.SPLASH_SCREEN_COMPONENT,
- WindowManagerStateHelper.SNAPSHOT_COMPONENT)
+ FlickerComponentName.SPLASH_SCREEN,
+ FlickerComponentName.SNAPSHOT)
@Before
override fun setup() {
@@ -110,13 +109,12 @@
.then()
// we log once per frame, upon logging, window may be visible or not depending
// on what was processed until that moment. Both behaviors are correct
- .isAppWindowInvisible(nonResizeableApp.component,
- ignoreActivity = true, isOptional = true)
+ .isAppWindowInvisible(nonResizeableApp.component, isOptional = true)
.then()
// immediately after the window (after onResume and before perform relayout)
// the activity is invisible. This may or not be logged, since we record 1x
// per frame, thus ignore activity check here
- .isAppWindowVisible(nonResizeableApp.component, ignoreActivity = true)
+ .isAppWindowVisible(nonResizeableApp.component)
}
}
@@ -128,8 +126,8 @@
@Test
fun bothAppsWindowsAreVisibleAtEnd() {
testSpec.assertWmEnd {
- isVisible(splitScreenApp.component)
- isVisible(nonResizeableApp.component)
+ isAppWindowVisible(splitScreenApp.component)
+ isAppWindowVisible(nonResizeableApp.component)
}
}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/LegacySplitScreenFromRecentNotSupportNonResizable.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/LegacySplitScreenFromRecentNotSupportNonResizable.kt
index 547341a..6f486b0 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/LegacySplitScreenFromRecentNotSupportNonResizable.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/LegacySplitScreenFromRecentNotSupportNonResizable.kt
@@ -16,7 +16,6 @@
package com.android.wm.shell.flicker.legacysplitscreen
-import android.content.ComponentName
import android.platform.test.annotations.Postsubmit
import android.platform.test.annotations.Presubmit
import android.view.Surface
@@ -28,7 +27,7 @@
import com.android.server.wm.flicker.dsl.FlickerBuilder
import com.android.server.wm.flicker.helpers.launchSplitScreen
import com.android.server.wm.flicker.helpers.reopenAppFromOverview
-import com.android.server.wm.traces.parser.windowmanager.WindowManagerStateHelper
+import com.android.server.wm.traces.common.FlickerComponentName
import com.android.wm.shell.flicker.DOCKED_STACK_DIVIDER_COMPONENT
import com.android.wm.shell.flicker.dockedStackDividerNotExistsAtEnd
import com.android.wm.shell.flicker.helpers.MultiWindowHelper.Companion.resetMultiWindowConfig
@@ -71,11 +70,11 @@
}
}
- override val ignoredWindows: List<ComponentName>
+ override val ignoredWindows: List<FlickerComponentName>
get() = listOf(DOCKED_STACK_DIVIDER_COMPONENT, LAUNCHER_COMPONENT, LETTERBOX_COMPONENT,
- TOAST_COMPONENT, splitScreenApp.component, nonResizeableApp.component,
- WindowManagerStateHelper.SPLASH_SCREEN_COMPONENT,
- WindowManagerStateHelper.SNAPSHOT_COMPONENT)
+ TOAST_COMPONENT, splitScreenApp.component, nonResizeableApp.component,
+ FlickerComponentName.SPLASH_SCREEN,
+ FlickerComponentName.SNAPSHOT)
@Before
override fun setup() {
@@ -116,12 +115,12 @@
// when the activity gets PAUSED the window may still be marked as visible
// it will be updated in the next log entry. This occurs because we record 1x
// per frame, thus ignore activity check here
- this.isAppWindowVisible(splitScreenApp.component, ignoreActivity = true)
+ this.isAppWindowVisible(splitScreenApp.component)
.then()
// immediately after the window (after onResume and before perform relayout)
// the activity is invisible. This may or not be logged, since we record 1x
// per frame, thus ignore activity check here
- .isAppWindowInvisible(splitScreenApp.component, ignoreActivity = true)
+ .isAppWindowInvisible(splitScreenApp.component)
}
}
@@ -143,8 +142,8 @@
@Test
fun onlyNonResizableAppWindowIsVisibleAtEnd() {
testSpec.assertWmEnd {
- isInvisible(splitScreenApp.component)
- isVisible(nonResizeableApp.component)
+ isAppWindowInvisible(splitScreenApp.component)
+ isAppWindowVisible(nonResizeableApp.component)
}
}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/LegacySplitScreenFromRecentSupportNonResizable.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/LegacySplitScreenFromRecentSupportNonResizable.kt
index 3f86658..f03c927 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/LegacySplitScreenFromRecentSupportNonResizable.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/LegacySplitScreenFromRecentSupportNonResizable.kt
@@ -16,7 +16,6 @@
package com.android.wm.shell.flicker.legacysplitscreen
-import android.content.ComponentName
import android.platform.test.annotations.Presubmit
import android.view.Surface
import androidx.test.filters.RequiresDevice
@@ -27,7 +26,7 @@
import com.android.server.wm.flicker.dsl.FlickerBuilder
import com.android.server.wm.flicker.helpers.launchSplitScreen
import com.android.server.wm.flicker.helpers.reopenAppFromOverview
-import com.android.server.wm.traces.parser.windowmanager.WindowManagerStateHelper
+import com.android.server.wm.traces.common.FlickerComponentName
import com.android.wm.shell.flicker.DOCKED_STACK_DIVIDER_COMPONENT
import com.android.wm.shell.flicker.dockedStackDividerIsVisibleAtEnd
import com.android.wm.shell.flicker.helpers.MultiWindowHelper.Companion.resetMultiWindowConfig
@@ -70,11 +69,11 @@
}
}
- override val ignoredWindows: List<ComponentName>
+ override val ignoredWindows: List<FlickerComponentName>
get() = listOf(DOCKED_STACK_DIVIDER_COMPONENT, LAUNCHER_COMPONENT, LETTERBOX_COMPONENT,
- TOAST_COMPONENT, splitScreenApp.component, nonResizeableApp.component,
- WindowManagerStateHelper.SPLASH_SCREEN_COMPONENT,
- WindowManagerStateHelper.SNAPSHOT_COMPONENT)
+ TOAST_COMPONENT, splitScreenApp.component, nonResizeableApp.component,
+ FlickerComponentName.SPLASH_SCREEN,
+ FlickerComponentName.SNAPSHOT)
@Before
override fun setup() {
@@ -107,7 +106,7 @@
// Because we log WM once per frame, sometimes the activity and the window
// become visible in the same entry, sometimes not, thus it is not possible to
// assert the visibility of the activity here
- this.isAppWindowInvisible(nonResizeableApp.component, ignoreActivity = true)
+ this.isAppWindowInvisible(nonResizeableApp.component)
.then()
// during re-parenting, the window may disappear and reappear from the
// trace, this occurs because we log only 1x per frame
@@ -129,8 +128,8 @@
@Test
fun bothAppsWindowsAreVisibleAtEnd() {
testSpec.assertWmEnd {
- isVisible(splitScreenApp.component)
- isVisible(nonResizeableApp.component)
+ isAppWindowVisible(splitScreenApp.component)
+ isAppWindowVisible(nonResizeableApp.component)
}
}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/LegacySplitScreenToLauncher.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/LegacySplitScreenToLauncher.kt
index 5fb6f1f..b6680d9 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/LegacySplitScreenToLauncher.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/LegacySplitScreenToLauncher.kt
@@ -16,7 +16,6 @@
package com.android.wm.shell.flicker.legacysplitscreen
-import android.content.ComponentName
import android.platform.test.annotations.Postsubmit
import android.platform.test.annotations.Presubmit
import android.view.Surface
@@ -39,7 +38,7 @@
import com.android.server.wm.flicker.statusBarLayerIsVisible
import com.android.server.wm.flicker.statusBarLayerRotatesScales
import com.android.server.wm.flicker.statusBarWindowIsVisible
-import com.android.server.wm.traces.parser.windowmanager.WindowManagerStateHelper
+import com.android.server.wm.traces.common.FlickerComponentName
import com.android.wm.shell.flicker.dockedStackDividerBecomesInvisible
import com.android.wm.shell.flicker.helpers.SimpleAppHelper
import org.junit.FixMethodOrder
@@ -86,9 +85,9 @@
}
}
- override val ignoredWindows: List<ComponentName>
- get() = listOf(LAUNCHER_COMPONENT, WindowManagerStateHelper.SPLASH_SCREEN_COMPONENT,
- WindowManagerStateHelper.SNAPSHOT_COMPONENT)
+ override val ignoredWindows: List<FlickerComponentName>
+ get() = listOf(LAUNCHER_COMPONENT, FlickerComponentName.SPLASH_SCREEN,
+ FlickerComponentName.SNAPSHOT)
@Presubmit
@Test
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/LegacySplitScreenTransition.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/LegacySplitScreenTransition.kt
index 3117693..661c8b6 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/LegacySplitScreenTransition.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/LegacySplitScreenTransition.kt
@@ -17,7 +17,6 @@
package com.android.wm.shell.flicker.legacysplitscreen
import android.app.Instrumentation
-import android.content.ComponentName
import android.content.Context
import android.support.test.launcherhelper.LauncherStrategyFactory
import android.view.Surface
@@ -32,7 +31,7 @@
import com.android.server.wm.flicker.helpers.wakeUpAndGoToHomeScreen
import com.android.server.wm.flicker.repetitions
import com.android.server.wm.flicker.startRotation
-import com.android.server.wm.traces.parser.windowmanager.WindowManagerStateHelper
+import com.android.server.wm.traces.common.FlickerComponentName
import com.android.wm.shell.flicker.helpers.BaseAppHelper.Companion.isShellTransitionsEnabled
import com.android.wm.shell.flicker.helpers.MultiWindowHelper.Companion.getDevEnableNonResizableMultiWindow
import com.android.wm.shell.flicker.helpers.MultiWindowHelper.Companion.setDevEnableNonResizableMultiWindow
@@ -50,7 +49,7 @@
protected val splitScreenApp = SplitScreenHelper.getPrimary(instrumentation)
protected val secondaryApp = SplitScreenHelper.getSecondary(instrumentation)
protected val nonResizeableApp = SplitScreenHelper.getNonResizeable(instrumentation)
- protected val LAUNCHER_COMPONENT = ComponentName("",
+ protected val LAUNCHER_COMPONENT = FlickerComponentName("",
LauncherStrategyFactory.getInstance(instrumentation)
.launcherStrategy.supportedLauncherPackage)
private var prevDevEnableNonResizableMultiWindow = 0
@@ -79,9 +78,9 @@
*
* b/182720234
*/
- open val ignoredWindows: List<ComponentName> = listOf(
- WindowManagerStateHelper.SPLASH_SCREEN_COMPONENT,
- WindowManagerStateHelper.SNAPSHOT_COMPONENT)
+ open val ignoredWindows: List<FlickerComponentName> = listOf(
+ FlickerComponentName.SPLASH_SCREEN,
+ FlickerComponentName.SNAPSHOT)
protected open val transition: FlickerBuilder.(Map<String, Any?>) -> Unit
get() = { configuration ->
@@ -148,9 +147,9 @@
}
companion object {
- internal val LIVE_WALLPAPER_COMPONENT = ComponentName("",
+ internal val LIVE_WALLPAPER_COMPONENT = FlickerComponentName("",
"com.breel.wallpapers18.soundviz.wallpaper.variations.SoundVizWallpaperV2")
- internal val LETTERBOX_COMPONENT = ComponentName("", "Letterbox")
- internal val TOAST_COMPONENT = ComponentName("", "Toast")
+ internal val LETTERBOX_COMPONENT = FlickerComponentName("", "Letterbox")
+ internal val TOAST_COMPONENT = FlickerComponentName("", "Toast")
}
}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/OpenAppToLegacySplitScreen.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/OpenAppToLegacySplitScreen.kt
index a7ac6a7..34eff80 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/OpenAppToLegacySplitScreen.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/OpenAppToLegacySplitScreen.kt
@@ -16,7 +16,6 @@
package com.android.wm.shell.flicker.legacysplitscreen
-import android.content.ComponentName
import android.platform.test.annotations.Presubmit
import android.view.Surface
import androidx.test.filters.FlakyTest
@@ -29,7 +28,7 @@
import com.android.server.wm.flicker.entireScreenCovered
import com.android.server.wm.flicker.helpers.launchSplitScreen
import com.android.server.wm.flicker.statusBarLayerIsVisible
-import com.android.server.wm.traces.parser.windowmanager.WindowManagerStateHelper
+import com.android.server.wm.traces.common.FlickerComponentName
import com.android.wm.shell.flicker.appPairsDividerBecomesVisible
import com.android.wm.shell.flicker.helpers.SplitScreenHelper
import org.junit.FixMethodOrder
@@ -59,10 +58,10 @@
}
}
- override val ignoredWindows: List<ComponentName>
+ override val ignoredWindows: List<FlickerComponentName>
get() = listOf(LAUNCHER_COMPONENT, splitScreenApp.component,
- WindowManagerStateHelper.SPLASH_SCREEN_COMPONENT,
- WindowManagerStateHelper.SNAPSHOT_COMPONENT)
+ FlickerComponentName.SPLASH_SCREEN,
+ FlickerComponentName.SNAPSHOT)
@FlakyTest
@Test
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/ResizeLegacySplitScreen.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/ResizeLegacySplitScreen.kt
index cd15051..14b006e 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/ResizeLegacySplitScreen.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/ResizeLegacySplitScreen.kt
@@ -42,6 +42,7 @@
import com.android.server.wm.flicker.statusBarLayerIsVisible
import com.android.server.wm.flicker.statusBarLayerRotatesScales
import com.android.server.wm.flicker.statusBarWindowIsVisible
+import com.android.server.wm.traces.parser.toFlickerComponent
import com.android.wm.shell.flicker.DOCKED_STACK_DIVIDER_COMPONENT
import com.android.wm.shell.flicker.helpers.SimpleAppHelper
import com.android.wm.shell.flicker.testapp.Components
@@ -110,7 +111,7 @@
@Test
fun topAppWindowIsAlwaysVisible() {
testSpec.assertWm {
- this.isAppWindowVisible(Components.SimpleActivity.COMPONENT)
+ this.isAppWindowVisible(Components.SimpleActivity.COMPONENT.toFlickerComponent())
}
}
@@ -118,7 +119,7 @@
@Test
fun bottomAppWindowIsAlwaysVisible() {
testSpec.assertWm {
- this.isAppWindowVisible(Components.ImeActivity.COMPONENT)
+ this.isAppWindowVisible(Components.ImeActivity.COMPONENT.toFlickerComponent())
}
}
@@ -142,14 +143,14 @@
@Test
fun topAppLayerIsAlwaysVisible() {
testSpec.assertLayers {
- this.isVisible(Components.SimpleActivity.COMPONENT)
+ this.isVisible(Components.SimpleActivity.COMPONENT.toFlickerComponent())
}
}
@Test
fun bottomAppLayerIsAlwaysVisible() {
testSpec.assertLayers {
- this.isVisible(Components.ImeActivity.COMPONENT)
+ this.isVisible(Components.ImeActivity.COMPONENT.toFlickerComponent())
}
}
@@ -174,8 +175,10 @@
dividerBounds.bottom - WindowUtils.dockedStackDividerInset,
displayBounds.right,
displayBounds.bottom - WindowUtils.navigationBarHeight)
- visibleRegion(Components.SimpleActivity.COMPONENT).coversExactly(topAppBounds)
- visibleRegion(Components.ImeActivity.COMPONENT).coversExactly(bottomAppBounds)
+ visibleRegion(Components.SimpleActivity.COMPONENT.toFlickerComponent())
+ .coversExactly(topAppBounds)
+ visibleRegion(Components.ImeActivity.COMPONENT.toFlickerComponent())
+ .coversExactly(bottomAppBounds)
}
}
@@ -194,8 +197,10 @@
displayBounds.right,
displayBounds.bottom - WindowUtils.navigationBarHeight)
- visibleRegion(Components.SimpleActivity.COMPONENT).coversExactly(topAppBounds)
- visibleRegion(Components.ImeActivity.COMPONENT).coversExactly(bottomAppBounds)
+ visibleRegion(Components.SimpleActivity.COMPONENT.toFlickerComponent())
+ .coversExactly(topAppBounds)
+ visibleRegion(Components.ImeActivity.COMPONENT.toFlickerComponent())
+ .coversExactly(bottomAppBounds)
}
}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/RotateTwoLaunchedAppAndEnterSplitScreen.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/RotateTwoLaunchedAppAndEnterSplitScreen.kt
index 2be6936..56933c3 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/RotateTwoLaunchedAppAndEnterSplitScreen.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/RotateTwoLaunchedAppAndEnterSplitScreen.kt
@@ -101,7 +101,7 @@
// Because we log WM once per frame, sometimes the activity and the window
// become visible in the same entry, sometimes not, thus it is not possible to
// assert the visibility of the activity here
- this.isAppWindowInvisible(secondaryApp.component, ignoreActivity = true)
+ this.isAppWindowInvisible(secondaryApp.component)
.then()
// during re-parenting, the window may disappear and reappear from the
// trace, this occurs because we log only 1x per frame
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/CommonAssertions.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/CommonAssertions.kt
index 443204c2..f9b0800 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/CommonAssertions.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/CommonAssertions.kt
@@ -14,6 +14,7 @@
* limitations under the License.
*/
+@file:JvmName("CommonAssertions")
package com.android.wm.shell.flicker.pip
internal const val PIP_WINDOW_COMPONENT = "PipMenuActivity"
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipTest.kt
index 046972d..52a744f 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipTest.kt
@@ -26,7 +26,6 @@
import com.android.server.wm.flicker.LAUNCHER_COMPONENT
import com.android.server.wm.flicker.annotation.Group3
import com.android.server.wm.flicker.dsl.FlickerBuilder
-import com.android.server.wm.traces.parser.toLayerName
import org.junit.FixMethodOrder
import org.junit.Test
import org.junit.runner.RunWith
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipToOtherOrientationTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipToOtherOrientationTest.kt
index 097ccb8..2aa1ed8 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipToOtherOrientationTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipToOtherOrientationTest.kt
@@ -29,7 +29,7 @@
import com.android.server.wm.flicker.helpers.WindowUtils
import com.android.server.wm.flicker.navBarLayerRotatesAndScales
import com.android.server.wm.flicker.statusBarLayerRotatesScales
-import com.android.server.wm.traces.parser.windowmanager.WindowManagerStateHelper
+import com.android.server.wm.traces.common.FlickerComponentName
import com.android.wm.shell.flicker.helpers.FixedAppHelper
import com.android.wm.shell.flicker.pip.PipTransition.BroadcastActionTrigger.Companion.ORIENTATION_LANDSCAPE
import com.android.wm.shell.flicker.pip.PipTransition.BroadcastActionTrigger.Companion.ORIENTATION_PORTRAIT
@@ -106,7 +106,7 @@
}
/**
- * Checks that the [WindowManagerStateHelper.NAV_BAR_COMPONENT] has the correct position at
+ * Checks that the [FlickerComponentName.NAV_BAR] has the correct position at
* the start and end of the transition
*/
@FlakyTest
@@ -115,7 +115,7 @@
testSpec.navBarLayerRotatesAndScales(Surface.ROTATION_90, Surface.ROTATION_0)
/**
- * Checks that the [WindowManagerStateHelper.STATUS_BAR_COMPONENT] has the correct position at
+ * Checks that the [FlickerComponentName.STATUS_BAR] has the correct position at
* the start and end of the transition
*/
@Presubmit
@@ -150,7 +150,7 @@
@Test
fun testAppWindowInvisibleOnStart() {
testSpec.assertWmStart {
- isInvisible(testApp.component)
+ isAppWindowInvisible(testApp.component)
}
}
@@ -161,7 +161,7 @@
@Test
fun testAppWindowVisibleOnEnd() {
testSpec.assertWmEnd {
- isVisible(testApp.component)
+ isAppWindowVisible(testApp.component)
}
}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipToAppTransition.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipToAppTransition.kt
index faaaecb..64b7eb5 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipToAppTransition.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipToAppTransition.kt
@@ -18,7 +18,6 @@
import android.platform.test.annotations.Presubmit
import com.android.server.wm.flicker.FlickerTestParameter
-import com.android.server.wm.traces.parser.toLayerName
import com.android.wm.shell.flicker.helpers.FixedAppHelper
import org.junit.Test
@@ -63,7 +62,7 @@
// when the activity is STOPPING, sometimes it becomes invisible in an entry before
// the window, sometimes in the same entry. This occurs because we log 1x per frame
// thus we ignore activity here
- isAppWindowVisible(testApp.component, ignoreActivity = true)
+ isAppWindowVisible(testApp.component)
.isAppWindowOnTop(pipApp.component)
.then()
.isAppWindowInvisible(testApp.component)
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipTransition.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipTransition.kt
index 3414031..5207fed 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipTransition.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipTransition.kt
@@ -54,9 +54,9 @@
open fun pipWindowBecomesInvisible() {
testSpec.assertWm {
this.invoke("hasPipWindow") {
- it.isPinned(pipApp.component).isVisible(pipApp.component)
+ it.isPinned(pipApp.component).isAppWindowVisible(pipApp.component)
}.then().invoke("!hasPipWindow") {
- it.isNotPinned(pipApp.component).isInvisible(pipApp.component)
+ it.isNotPinned(pipApp.component).isAppWindowInvisible(pipApp.component)
}
}
}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipViaExpandButtonClickTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipViaExpandButtonClickTest.kt
index fa100b5..b53342d 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipViaExpandButtonClickTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipViaExpandButtonClickTest.kt
@@ -23,7 +23,6 @@
import com.android.server.wm.flicker.FlickerTestParameterFactory
import com.android.server.wm.flicker.annotation.Group3
import com.android.server.wm.flicker.dsl.FlickerBuilder
-import com.android.server.wm.traces.parser.toWindowName
import org.junit.FixMethodOrder
import org.junit.runner.RunWith
import org.junit.runners.MethodSorters
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipViaIntentTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipViaIntentTest.kt
index 617ef22..1fec3cf 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipViaIntentTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipViaIntentTest.kt
@@ -23,7 +23,6 @@
import com.android.server.wm.flicker.FlickerTestParameterFactory
import com.android.server.wm.flicker.annotation.Group3
import com.android.server.wm.flicker.dsl.FlickerBuilder
-import com.android.server.wm.traces.parser.toWindowName
import org.junit.FixMethodOrder
import org.junit.runner.RunWith
import org.junit.runners.MethodSorters
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExpandPipOnDoubleClickTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExpandPipOnDoubleClickTest.kt
index 89b2c40..ce840ef 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExpandPipOnDoubleClickTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExpandPipOnDoubleClickTest.kt
@@ -27,7 +27,6 @@
import com.android.server.wm.flicker.LAUNCHER_COMPONENT
import com.android.server.wm.flicker.annotation.Group3
import com.android.server.wm.flicker.dsl.FlickerBuilder
-import com.android.server.wm.traces.parser.toLayerName
import org.junit.FixMethodOrder
import org.junit.Test
import org.junit.runner.RunWith
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/MovePipShelfHeightTransition.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/MovePipShelfHeightTransition.kt
index ed04fc9..6e0324c 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/MovePipShelfHeightTransition.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/MovePipShelfHeightTransition.kt
@@ -20,8 +20,6 @@
import com.android.launcher3.tapl.LauncherInstrumentation
import com.android.server.wm.flicker.FlickerTestParameter
import com.android.server.wm.flicker.traces.RegionSubject
-import com.android.server.wm.traces.parser.toLayerName
-import com.android.server.wm.traces.parser.toWindowName
import com.android.wm.shell.flicker.helpers.FixedAppHelper
import org.junit.Test
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipKeyboardTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipKeyboardTest.kt
index 5719413..aba8ace 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipKeyboardTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipKeyboardTest.kt
@@ -22,12 +22,12 @@
import com.android.server.wm.flicker.FlickerParametersRunnerFactory
import com.android.server.wm.flicker.FlickerTestParameter
import com.android.server.wm.flicker.FlickerTestParameterFactory
-import com.android.server.wm.flicker.annotation.Group3
+import com.android.server.wm.flicker.annotation.Group4
import com.android.server.wm.flicker.dsl.FlickerBuilder
import com.android.server.wm.flicker.helpers.WindowUtils
import com.android.server.wm.flicker.helpers.setRotation
import com.android.server.wm.flicker.startRotation
-import com.android.server.wm.traces.parser.windowmanager.WindowManagerStateHelper
+import com.android.server.wm.traces.common.FlickerComponentName
import com.android.wm.shell.flicker.helpers.ImeAppHelper
import org.junit.FixMethodOrder
import org.junit.Test
@@ -43,7 +43,7 @@
@RunWith(Parameterized::class)
@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-@Group3
+@Group4
class PipKeyboardTest(testSpec: FlickerTestParameter) : PipTransition(testSpec) {
private val imeApp = ImeAppHelper(instrumentation)
@@ -90,7 +90,7 @@
@Test
fun pipIsAboveAppWindow() {
testSpec.assertWmTag(TAG_IME_VISIBLE) {
- isAboveWindow(WindowManagerStateHelper.IME_COMPONENT, pipApp.component)
+ isAboveWindow(FlickerComponentName.IME, pipApp.component)
}
}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipLegacySplitScreenTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipLegacySplitScreenTest.kt
index 0861652..9bea5c0 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipLegacySplitScreenTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipLegacySplitScreenTest.kt
@@ -23,7 +23,7 @@
import com.android.server.wm.flicker.FlickerParametersRunnerFactory
import com.android.server.wm.flicker.FlickerTestParameter
import com.android.server.wm.flicker.FlickerTestParameterFactory
-import com.android.server.wm.flicker.annotation.Group3
+import com.android.server.wm.flicker.annotation.Group4
import com.android.server.wm.flicker.dsl.FlickerBuilder
import com.android.server.wm.flicker.helpers.launchSplitScreen
import com.android.server.wm.flicker.helpers.wakeUpAndGoToHomeScreen
@@ -51,7 +51,7 @@
@RunWith(Parameterized::class)
@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-@Group3
+@Group4
class PipLegacySplitScreenTest(testSpec: FlickerTestParameter) : PipTransition(testSpec) {
private val imeApp = ImeAppHelper(instrumentation)
private val testApp = FixedAppHelper(instrumentation)
@@ -104,9 +104,9 @@
@Test
fun bothAppWindowsVisible() {
testSpec.assertWmEnd {
- isVisible(testApp.component)
- isVisible(imeApp.component)
- noWindowsOverlap(testApp.component, imeApp.component)
+ isAppWindowVisible(testApp.component)
+ isAppWindowVisible(imeApp.component)
+ doNotOverlap(testApp.component, imeApp.component)
}
}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipRotationTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipRotationTest.kt
index b105460..08d5209 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipRotationTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipRotationTest.kt
@@ -23,7 +23,7 @@
import com.android.server.wm.flicker.FlickerParametersRunnerFactory
import com.android.server.wm.flicker.FlickerTestParameter
import com.android.server.wm.flicker.FlickerTestParameterFactory
-import com.android.server.wm.flicker.annotation.Group3
+import com.android.server.wm.flicker.annotation.Group4
import com.android.server.wm.flicker.dsl.FlickerBuilder
import com.android.server.wm.flicker.endRotation
import com.android.server.wm.flicker.entireScreenCovered
@@ -62,7 +62,7 @@
@RunWith(Parameterized::class)
@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-@Group3
+@Group4
class PipRotationTest(testSpec: FlickerTestParameter) : PipTransition(testSpec) {
private val fixedApp = FixedAppHelper(instrumentation)
private val screenBoundsStart = WindowUtils.getDisplayBounds(testSpec.config.startRotation)
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/SetRequestedOrientationWhilePinnedTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/SetRequestedOrientationWhilePinnedTest.kt
index 02a3eb1..d6dbc36 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/SetRequestedOrientationWhilePinnedTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/SetRequestedOrientationWhilePinnedTest.kt
@@ -22,7 +22,7 @@
import com.android.server.wm.flicker.FlickerParametersRunnerFactory
import com.android.server.wm.flicker.FlickerTestParameter
import com.android.server.wm.flicker.FlickerTestParameterFactory
-import com.android.server.wm.flicker.annotation.Group3
+import com.android.server.wm.flicker.annotation.Group4
import com.android.server.wm.flicker.dsl.FlickerBuilder
import com.android.server.wm.flicker.helpers.WindowUtils
import com.android.wm.shell.flicker.pip.PipTransition.BroadcastActionTrigger.Companion.ORIENTATION_LANDSCAPE
@@ -43,7 +43,7 @@
@RunWith(Parameterized::class)
@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-@Group3
+@Group4
class SetRequestedOrientationWhilePinnedTest(
testSpec: FlickerTestParameter
) : PipTransition(testSpec) {
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/ShellTaskOrganizerTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/ShellTaskOrganizerTests.java
index 6b74b62..d5acbbcf 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/ShellTaskOrganizerTests.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/ShellTaskOrganizerTests.java
@@ -197,6 +197,43 @@
}
@Test
+ public void testAddListenerForMultipleTypes() {
+ RunningTaskInfo taskInfo1 = createTaskInfo(1, WINDOWING_MODE_FULLSCREEN);
+ mOrganizer.onTaskAppeared(taskInfo1, null);
+ RunningTaskInfo taskInfo2 = createTaskInfo(2, WINDOWING_MODE_MULTI_WINDOW);
+ mOrganizer.onTaskAppeared(taskInfo2, null);
+
+ TrackingTaskListener listener = new TrackingTaskListener();
+ mOrganizer.addListenerForType(listener,
+ TASK_LISTENER_TYPE_MULTI_WINDOW, TASK_LISTENER_TYPE_FULLSCREEN);
+
+ // onTaskAppeared event should be delivered once for each taskInfo.
+ assertTrue(listener.appeared.contains(taskInfo1));
+ assertTrue(listener.appeared.contains(taskInfo2));
+ assertEquals(2, listener.appeared.size());
+ }
+
+ @Test
+ public void testRemoveListenerForMultipleTypes() {
+ RunningTaskInfo taskInfo1 = createTaskInfo(1, WINDOWING_MODE_FULLSCREEN);
+ mOrganizer.onTaskAppeared(taskInfo1, null);
+ RunningTaskInfo taskInfo2 = createTaskInfo(2, WINDOWING_MODE_MULTI_WINDOW);
+ mOrganizer.onTaskAppeared(taskInfo2, null);
+
+ TrackingTaskListener listener = new TrackingTaskListener();
+ mOrganizer.addListenerForType(listener,
+ TASK_LISTENER_TYPE_MULTI_WINDOW, TASK_LISTENER_TYPE_FULLSCREEN);
+
+ mOrganizer.removeListener(listener);
+
+ // If listener is removed properly, onTaskInfoChanged event shouldn't be delivered.
+ mOrganizer.onTaskInfoChanged(taskInfo1);
+ assertTrue(listener.infoChanged.isEmpty());
+ mOrganizer.onTaskInfoChanged(taskInfo2);
+ assertTrue(listener.infoChanged.isEmpty());
+ }
+
+ @Test
public void testWindowingModeChange() {
RunningTaskInfo taskInfo = createTaskInfo(1, WINDOWING_MODE_MULTI_WINDOW);
TrackingTaskListener mwListener = new TrackingTaskListener();
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/apppairs/TestAppPairsController.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/apppairs/TestAppPairsController.java
index 27c6261..294bc12 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/apppairs/TestAppPairsController.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/apppairs/TestAppPairsController.java
@@ -21,6 +21,7 @@
import com.android.wm.shell.ShellTaskOrganizer;
import com.android.wm.shell.common.DisplayController;
import com.android.wm.shell.common.DisplayImeController;
+import com.android.wm.shell.common.DisplayInsetsController;
import com.android.wm.shell.common.ShellExecutor;
import com.android.wm.shell.common.SyncTransactionQueue;
@@ -30,7 +31,7 @@
public TestAppPairsController(ShellTaskOrganizer organizer, SyncTransactionQueue syncQueue,
DisplayController displayController) {
super(organizer, syncQueue, displayController, mock(ShellExecutor.class),
- mock(DisplayImeController.class));
+ mock(DisplayImeController.class), mock(DisplayInsetsController.class));
mPool = new TestAppPairsPool(this);
setPairsPool(mPool);
}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/BubbleDataTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/BubbleDataTest.java
index b0312e6..091022a 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/BubbleDataTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/BubbleDataTest.java
@@ -793,7 +793,7 @@
}
@Test
- public void test_expanded_removeLastBubble_collapsesStack() {
+ public void test_expanded_removeLastBubble_showsOverflowIfNotEmpty() {
// Setup
sendUpdatedEntryAtTime(mEntryA1, 1000);
changeExpandedStateAtTime(true, 2000);
@@ -802,6 +802,21 @@
// Test
mBubbleData.dismissBubbleWithKey(mEntryA1.getKey(), Bubbles.DISMISS_USER_GESTURE);
verifyUpdateReceived();
+ assertThat(mBubbleData.getOverflowBubbles().size()).isGreaterThan(0);
+ assertSelectionChangedTo(mBubbleData.getOverflow());
+ }
+
+ @Test
+ public void test_expanded_removeLastBubble_collapsesIfOverflowEmpty() {
+ // Setup
+ sendUpdatedEntryAtTime(mEntryA1, 1000);
+ changeExpandedStateAtTime(true, 2000);
+ mBubbleData.setListener(mListener);
+
+ // Test
+ mBubbleData.dismissBubbleWithKey(mEntryA1.getKey(), Bubbles.DISMISS_NO_BUBBLE_UP);
+ verifyUpdateReceived();
+ assertThat(mBubbleData.getOverflowBubbles()).isEmpty();
assertExpandedChangedTo(false);
}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/fullscreen/FullscreenTaskListenerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/fullscreen/FullscreenTaskListenerTest.java
new file mode 100644
index 0000000..d6f7e54
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/fullscreen/FullscreenTaskListenerTest.java
@@ -0,0 +1,151 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell.fullscreen;
+
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.inOrder;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
+import static org.mockito.Mockito.when;
+
+import android.app.ActivityManager.RunningTaskInfo;
+import android.app.WindowConfiguration;
+import android.content.res.Configuration;
+import android.graphics.Point;
+import android.view.SurfaceControl;
+
+import androidx.test.filters.SmallTest;
+
+import com.android.wm.shell.common.SyncTransactionQueue;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.InOrder;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+import java.util.Optional;
+
+@SmallTest
+public class FullscreenTaskListenerTest {
+
+ @Mock
+ private SyncTransactionQueue mSyncQueue;
+ @Mock
+ private FullscreenUnfoldController mUnfoldController;
+ @Mock
+ private SurfaceControl mSurfaceControl;
+
+ private Optional<FullscreenUnfoldController> mFullscreenUnfoldController;
+
+ private FullscreenTaskListener mListener;
+
+ @Before
+ public void setup() {
+ MockitoAnnotations.initMocks(this);
+ mFullscreenUnfoldController = Optional.of(mUnfoldController);
+ mListener = new FullscreenTaskListener(mSyncQueue, mFullscreenUnfoldController);
+ }
+
+ @Test
+ public void testAnimatableTaskAppeared_notifiesUnfoldController() {
+ RunningTaskInfo info = createTaskInfo(/* visible */ true, /* taskId */ 0);
+
+ mListener.onTaskAppeared(info, mSurfaceControl);
+
+ verify(mUnfoldController).onTaskAppeared(eq(info), any());
+ }
+
+ @Test
+ public void testMultipleAnimatableTasksAppeared_notifiesUnfoldController() {
+ RunningTaskInfo animatable1 = createTaskInfo(/* visible */ true, /* taskId */ 0);
+ RunningTaskInfo animatable2 = createTaskInfo(/* visible */ true, /* taskId */ 1);
+
+ mListener.onTaskAppeared(animatable1, mSurfaceControl);
+ mListener.onTaskAppeared(animatable2, mSurfaceControl);
+
+ InOrder order = inOrder(mUnfoldController);
+ order.verify(mUnfoldController).onTaskAppeared(eq(animatable1), any());
+ order.verify(mUnfoldController).onTaskAppeared(eq(animatable2), any());
+ }
+
+ @Test
+ public void testNonAnimatableTaskAppeared_doesNotNotifyUnfoldController() {
+ RunningTaskInfo info = createTaskInfo(/* visible */ false, /* taskId */ 0);
+
+ mListener.onTaskAppeared(info, mSurfaceControl);
+
+ verifyNoMoreInteractions(mUnfoldController);
+ }
+
+ @Test
+ public void testNonAnimatableTaskChanged_doesNotNotifyUnfoldController() {
+ RunningTaskInfo info = createTaskInfo(/* visible */ false, /* taskId */ 0);
+ mListener.onTaskAppeared(info, mSurfaceControl);
+
+ mListener.onTaskInfoChanged(info);
+
+ verifyNoMoreInteractions(mUnfoldController);
+ }
+
+ @Test
+ public void testNonAnimatableTaskVanished_doesNotNotifyUnfoldController() {
+ RunningTaskInfo info = createTaskInfo(/* visible */ false, /* taskId */ 0);
+ mListener.onTaskAppeared(info, mSurfaceControl);
+
+ mListener.onTaskVanished(info);
+
+ verifyNoMoreInteractions(mUnfoldController);
+ }
+
+ @Test
+ public void testAnimatableTaskBecameInactive_notifiesUnfoldController() {
+ RunningTaskInfo animatableTask = createTaskInfo(/* visible */ true, /* taskId */ 0);
+ mListener.onTaskAppeared(animatableTask, mSurfaceControl);
+ RunningTaskInfo notAnimatableTask = createTaskInfo(/* visible */ false, /* taskId */ 0);
+
+ mListener.onTaskInfoChanged(notAnimatableTask);
+
+ verify(mUnfoldController).onTaskVanished(eq(notAnimatableTask));
+ }
+
+ @Test
+ public void testAnimatableTaskVanished_notifiesUnfoldController() {
+ RunningTaskInfo taskInfo = createTaskInfo(/* visible */ true, /* taskId */ 0);
+ mListener.onTaskAppeared(taskInfo, mSurfaceControl);
+
+ mListener.onTaskVanished(taskInfo);
+
+ verify(mUnfoldController).onTaskVanished(eq(taskInfo));
+ }
+
+ private RunningTaskInfo createTaskInfo(boolean visible, int taskId) {
+ final RunningTaskInfo info = spy(new RunningTaskInfo());
+ info.isVisible = visible;
+ info.positionInParent = new Point();
+ when(info.getWindowingMode()).thenReturn(WindowConfiguration.WINDOWING_MODE_FULLSCREEN);
+ final Configuration configuration = new Configuration();
+ configuration.windowConfiguration.setActivityType(ACTIVITY_TYPE_STANDARD);
+ when(info.getConfiguration()).thenReturn(configuration);
+ info.taskId = taskId;
+ return info;
+ }
+}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/MainStageTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/MainStageTests.java
index 1bb5fd1..12b547a 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/MainStageTests.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/MainStageTests.java
@@ -56,7 +56,7 @@
MockitoAnnotations.initMocks(this);
mRootTaskInfo = new TestRunningTaskInfoBuilder().build();
mMainStage = new MainStage(mTaskOrganizer, DEFAULT_DISPLAY, mCallbacks, mSyncQueue,
- mSurfaceSession);
+ mSurfaceSession, null);
mMainStage.onTaskAppeared(mRootTaskInfo, mRootLeash);
}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SideStageTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SideStageTests.java
index 69ead3a..838aa81 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SideStageTests.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SideStageTests.java
@@ -29,6 +29,7 @@
import android.view.SurfaceSession;
import android.window.WindowContainerTransaction;
+import androidx.test.annotation.UiThreadTest;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
@@ -58,11 +59,12 @@
private SideStage mSideStage;
@Before
+ @UiThreadTest
public void setup() {
MockitoAnnotations.initMocks(this);
mRootTask = new TestRunningTaskInfoBuilder().build();
mSideStage = new SideStage(mContext, mTaskOrganizer, DEFAULT_DISPLAY, mCallbacks,
- mSyncQueue, mSurfaceSession);
+ mSyncQueue, mSurfaceSession, null);
mSideStage.onTaskAppeared(mRootTask, mRootLeash);
}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitTestUtils.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitTestUtils.java
index 736566e5..f90af23 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitTestUtils.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitTestUtils.java
@@ -18,7 +18,6 @@
import static android.view.Display.DEFAULT_DISPLAY;
import static android.window.DisplayAreaOrganizer.FEATURE_DEFAULT_TASK_CONTAINER;
-
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
@@ -39,6 +38,10 @@
import com.android.wm.shell.common.split.SplitLayout;
import com.android.wm.shell.transition.Transitions;
+import java.util.Optional;
+
+import javax.inject.Provider;
+
public class SplitTestUtils {
static SplitLayout createMockSplitLayout() {
@@ -68,10 +71,11 @@
MainStage mainStage, SideStage sideStage, DisplayImeController imeController,
DisplayInsetsController insetsController, SplitLayout splitLayout,
Transitions transitions, TransactionPool transactionPool,
- SplitscreenEventLogger logger) {
+ SplitscreenEventLogger logger,
+ Provider<Optional<StageTaskUnfoldController>> unfoldController) {
super(context, displayId, syncQueue, rootTDAOrganizer, taskOrganizer, mainStage,
sideStage, imeController, insetsController, splitLayout, transitions,
- transactionPool, logger);
+ transactionPool, logger, unfoldController);
// Prepare default TaskDisplayArea for testing.
mDisplayAreaInfo = new DisplayAreaInfo(
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitTransitionTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitTransitionTests.java
index d357e77..be103863 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitTransitionTests.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitTransitionTests.java
@@ -51,6 +51,7 @@
import android.window.TransitionRequestInfo;
import android.window.WindowContainerTransaction;
+import androidx.test.annotation.UiThreadTest;
import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
@@ -73,6 +74,8 @@
import org.mockito.MockitoAnnotations;
import org.mockito.stubbing.Answer;
+import java.util.Optional;
+
/** Tests for {@link StageCoordinator} */
@SmallTest
@RunWith(AndroidJUnit4.class)
@@ -96,6 +99,7 @@
private ActivityManager.RunningTaskInfo mSideChild;
@Before
+ @UiThreadTest
public void setup() {
MockitoAnnotations.initMocks(this);
final ShellExecutor mockExecutor = mock(ShellExecutor.class);
@@ -104,16 +108,16 @@
doReturn(mock(SurfaceControl.Transaction.class)).when(mTransactionPool).acquire();
mSplitLayout = SplitTestUtils.createMockSplitLayout();
mMainStage = new MainStage(mTaskOrganizer, DEFAULT_DISPLAY, mock(
- StageTaskListener.StageListenerCallbacks.class), mSyncQueue, mSurfaceSession);
+ StageTaskListener.StageListenerCallbacks.class), mSyncQueue, mSurfaceSession, null);
mMainStage.onTaskAppeared(new TestRunningTaskInfoBuilder().build(), createMockSurface());
mSideStage = new SideStage(mContext, mTaskOrganizer, DEFAULT_DISPLAY, mock(
- StageTaskListener.StageListenerCallbacks.class), mSyncQueue, mSurfaceSession);
+ StageTaskListener.StageListenerCallbacks.class), mSyncQueue, mSurfaceSession, null);
mSideStage.onTaskAppeared(new TestRunningTaskInfoBuilder().build(), createMockSurface());
mStageCoordinator = new SplitTestUtils.TestStageCoordinator(mContext, DEFAULT_DISPLAY,
mSyncQueue, mRootTDAOrganizer, mTaskOrganizer, mMainStage, mSideStage,
mDisplayImeController, mDisplayInsetsController, mSplitLayout, mTransitions,
mTransactionPool,
- mLogger);
+ mLogger, Optional::empty);
mSplitScreenTransitions = mStageCoordinator.getSplitTransitions();
doAnswer((Answer<IBinder>) invocation -> mock(IBinder.class))
.when(mTransitions).startTransition(anyInt(), any(), any());
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/StageCoordinatorTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/StageCoordinatorTests.java
index d930d4ca..a39d331 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/StageCoordinatorTests.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/StageCoordinatorTests.java
@@ -18,18 +18,20 @@
import static android.app.ActivityTaskManager.INVALID_TASK_ID;
import static android.view.Display.DEFAULT_DISPLAY;
-
import static com.android.internal.util.FrameworkStatsLog.SPLITSCREEN_UICHANGED__EXIT_REASON__RETURN_HOME;
import static com.android.wm.shell.common.split.SplitLayout.SPLIT_POSITION_BOTTOM_OR_RIGHT;
-
+import static com.android.wm.shell.common.split.SplitLayout.SPLIT_POSITION_TOP_OR_LEFT;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.clearInvocations;
import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import android.app.ActivityManager;
import android.graphics.Rect;
+import android.window.DisplayAreaInfo;
import android.window.WindowContainerTransaction;
import androidx.test.ext.junit.runners.AndroidJUnit4;
@@ -43,6 +45,7 @@
import com.android.wm.shell.common.DisplayInsetsController;
import com.android.wm.shell.common.SyncTransactionQueue;
import com.android.wm.shell.common.TransactionPool;
+import com.android.wm.shell.common.split.SplitLayout;
import com.android.wm.shell.transition.Transitions;
import org.junit.Before;
@@ -51,29 +54,55 @@
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
-/** Tests for {@link StageCoordinator} */
+import java.util.Optional;
+
+import javax.inject.Provider;
+
+/**
+ * Tests for {@link StageCoordinator}
+ */
@SmallTest
@RunWith(AndroidJUnit4.class)
public class StageCoordinatorTests extends ShellTestCase {
- @Mock private ShellTaskOrganizer mTaskOrganizer;
- @Mock private SyncTransactionQueue mSyncQueue;
- @Mock private RootTaskDisplayAreaOrganizer mRootTDAOrganizer;
- @Mock private MainStage mMainStage;
- @Mock private SideStage mSideStage;
- @Mock private DisplayImeController mDisplayImeController;
- @Mock private DisplayInsetsController mDisplayInsetsController;
- @Mock private Transitions mTransitions;
- @Mock private TransactionPool mTransactionPool;
- @Mock private SplitscreenEventLogger mLogger;
+ @Mock
+ private ShellTaskOrganizer mTaskOrganizer;
+ @Mock
+ private SyncTransactionQueue mSyncQueue;
+ @Mock
+ private RootTaskDisplayAreaOrganizer mRootTDAOrganizer;
+ @Mock
+ private MainStage mMainStage;
+ @Mock
+ private SideStage mSideStage;
+ @Mock
+ private StageTaskUnfoldController mMainUnfoldController;
+ @Mock
+ private StageTaskUnfoldController mSideUnfoldController;
+ @Mock
+ private SplitLayout mSplitLayout;
+ @Mock
+ private DisplayImeController mDisplayImeController;
+ @Mock
+ private DisplayInsetsController mDisplayInsetsController;
+ @Mock
+ private Transitions mTransitions;
+ @Mock
+ private TransactionPool mTransactionPool;
+ @Mock
+ private SplitscreenEventLogger mLogger;
+
+ private final Rect mBounds1 = new Rect(10, 20, 30, 40);
+ private final Rect mBounds2 = new Rect(5, 10, 15, 20);
+
private StageCoordinator mStageCoordinator;
@Before
public void setup() {
MockitoAnnotations.initMocks(this);
- mStageCoordinator = new SplitTestUtils.TestStageCoordinator(mContext, DEFAULT_DISPLAY,
- mSyncQueue, mRootTDAOrganizer, mTaskOrganizer, mMainStage, mSideStage,
- mDisplayImeController, mDisplayInsetsController, null /* splitLayout */,
- mTransitions, mTransactionPool, mLogger);
+ mStageCoordinator = createStageCoordinator(/* splitLayout */ null);
+
+ when(mSplitLayout.getBounds1()).thenReturn(mBounds1);
+ when(mSplitLayout.getBounds2()).thenReturn(mBounds2);
}
@Test
@@ -88,6 +117,38 @@
}
@Test
+ public void testDisplayAreaAppeared_initializesUnfoldControllers() {
+ mStageCoordinator.onDisplayAreaAppeared(mock(DisplayAreaInfo.class));
+
+ verify(mMainUnfoldController).init();
+ verify(mSideUnfoldController).init();
+ }
+
+ @Test
+ public void testLayoutChanged_topLeftSplitPosition_updatesUnfoldStageBounds() {
+ mStageCoordinator = createStageCoordinator(mSplitLayout);
+ mStageCoordinator.setSideStagePosition(SPLIT_POSITION_TOP_OR_LEFT, null);
+ clearInvocations(mMainUnfoldController, mSideUnfoldController);
+
+ mStageCoordinator.onLayoutChanged(mSplitLayout);
+
+ verify(mMainUnfoldController).onLayoutChanged(mBounds2);
+ verify(mSideUnfoldController).onLayoutChanged(mBounds1);
+ }
+
+ @Test
+ public void testLayoutChanged_bottomRightSplitPosition_updatesUnfoldStageBounds() {
+ mStageCoordinator = createStageCoordinator(mSplitLayout);
+ mStageCoordinator.setSideStagePosition(SPLIT_POSITION_BOTTOM_OR_RIGHT, null);
+ clearInvocations(mMainUnfoldController, mSideUnfoldController);
+
+ mStageCoordinator.onLayoutChanged(mSplitLayout);
+
+ verify(mMainUnfoldController).onLayoutChanged(mBounds1);
+ verify(mSideUnfoldController).onLayoutChanged(mBounds2);
+ }
+
+ @Test
public void testRemoveFromSideStage() {
final ActivityManager.RunningTaskInfo task = new TestRunningTaskInfoBuilder().build();
@@ -131,4 +192,27 @@
verify(mSideStage).removeAllTasks(any(WindowContainerTransaction.class), eq(true));
verify(mMainStage).deactivate(any(WindowContainerTransaction.class), eq(false));
}
+
+ private StageCoordinator createStageCoordinator(SplitLayout splitLayout) {
+ return new SplitTestUtils.TestStageCoordinator(mContext, DEFAULT_DISPLAY,
+ mSyncQueue, mRootTDAOrganizer, mTaskOrganizer, mMainStage, mSideStage,
+ mDisplayImeController, mDisplayInsetsController, splitLayout,
+ mTransitions, mTransactionPool, mLogger, new UnfoldControllerProvider());
+ }
+
+ private class UnfoldControllerProvider implements
+ Provider<Optional<StageTaskUnfoldController>> {
+
+ private boolean isMain = true;
+
+ @Override
+ public Optional<StageTaskUnfoldController> get() {
+ if (isMain) {
+ isMain = false;
+ return Optional.of(mMainUnfoldController);
+ } else {
+ return Optional.of(mSideUnfoldController);
+ }
+ }
+ }
}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/StageTaskListenerTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/StageTaskListenerTests.java
index 1a30f16..a5746a4 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/StageTaskListenerTests.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/StageTaskListenerTests.java
@@ -23,6 +23,7 @@
import static org.junit.Assume.assumeFalse;
import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.clearInvocations;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
@@ -60,8 +61,10 @@
@Mock private ShellTaskOrganizer mTaskOrganizer;
@Mock private StageTaskListener.StageListenerCallbacks mCallbacks;
@Mock private SyncTransactionQueue mSyncQueue;
+ @Mock private StageTaskUnfoldController mStageTaskUnfoldController;
@Captor private ArgumentCaptor<SyncTransactionQueue.TransactionRunnable> mRunnableCaptor;
private SurfaceSession mSurfaceSession = new SurfaceSession();
+ private SurfaceControl mSurfaceControl;
private ActivityManager.RunningTaskInfo mRootTask;
private StageTaskListener mStageTaskListener;
@@ -73,10 +76,12 @@
DEFAULT_DISPLAY,
mCallbacks,
mSyncQueue,
- mSurfaceSession);
+ mSurfaceSession,
+ mStageTaskUnfoldController);
mRootTask = new TestRunningTaskInfoBuilder().build();
mRootTask.parentTaskId = INVALID_TASK_ID;
- mStageTaskListener.onTaskAppeared(mRootTask, new SurfaceControl());
+ mSurfaceControl = new SurfaceControl.Builder(mSurfaceSession).setName("test").build();
+ mStageTaskListener.onTaskAppeared(mRootTask, mSurfaceControl);
}
@Test
@@ -103,12 +108,34 @@
final ActivityManager.RunningTaskInfo childTask =
new TestRunningTaskInfoBuilder().setParentTaskId(mRootTask.taskId).build();
- mStageTaskListener.onTaskAppeared(childTask, new SurfaceControl());
+ mStageTaskListener.onTaskAppeared(childTask, mSurfaceControl);
assertThat(mStageTaskListener.mChildrenTaskInfo.contains(childTask.taskId)).isTrue();
verify(mCallbacks).onStatusChanged(eq(mRootTask.isVisible), eq(true));
}
+ @Test
+ public void testTaskAppeared_notifiesUnfoldListener() {
+ final ActivityManager.RunningTaskInfo task =
+ new TestRunningTaskInfoBuilder().setParentTaskId(mRootTask.taskId).build();
+
+ mStageTaskListener.onTaskAppeared(task, mSurfaceControl);
+
+ verify(mStageTaskUnfoldController).onTaskAppeared(eq(task), eq(mSurfaceControl));
+ }
+
+ @Test
+ public void testTaskVanished_notifiesUnfoldListener() {
+ final ActivityManager.RunningTaskInfo task =
+ new TestRunningTaskInfoBuilder().setParentTaskId(mRootTask.taskId).build();
+ mStageTaskListener.onTaskAppeared(task, mSurfaceControl);
+ clearInvocations(mStageTaskUnfoldController);
+
+ mStageTaskListener.onTaskVanished(task);
+
+ verify(mStageTaskUnfoldController).onTaskVanished(eq(task));
+ }
+
@Test(expected = IllegalArgumentException.class)
public void testUnknownTaskVanished() {
final ActivityManager.RunningTaskInfo task = new TestRunningTaskInfoBuilder().build();
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/startingsurface/StartingSurfaceDrawerTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/startingsurface/StartingSurfaceDrawerTests.java
index 36722d9..e5a8aa0 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/startingsurface/StartingSurfaceDrawerTests.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/startingsurface/StartingSurfaceDrawerTests.java
@@ -57,12 +57,12 @@
import android.view.IWindowSession;
import android.view.InsetsState;
import android.view.Surface;
-import android.view.SurfaceControl;
import android.view.View;
import android.view.WindowManager;
import android.view.WindowManagerGlobal;
import android.view.WindowMetrics;
import android.window.StartingWindowInfo;
+import android.window.StartingWindowRemovalInfo;
import android.window.TaskSnapshot;
import androidx.test.ext.junit.runners.AndroidJUnit4;
@@ -119,10 +119,9 @@
}
@Override
- protected void removeWindowSynced(int taskId, SurfaceControl leash, Rect frame,
- boolean playRevealAnimation) {
+ protected void removeWindowSynced(StartingWindowRemovalInfo removalInfo) {
// listen for removeView
- if (mAddWindowForTask == taskId) {
+ if (mAddWindowForTask == removalInfo.taskId) {
mAddWindowForTask = 0;
}
}
@@ -172,9 +171,11 @@
eq(STARTING_WINDOW_TYPE_SPLASH_SCREEN));
assertEquals(mStartingSurfaceDrawer.mAddWindowForTask, taskId);
- mStartingSurfaceDrawer.removeStartingWindow(windowInfo.taskInfo.taskId, null, null, false);
+ StartingWindowRemovalInfo removalInfo = new StartingWindowRemovalInfo();
+ removalInfo.taskId = windowInfo.taskInfo.taskId;
+ mStartingSurfaceDrawer.removeStartingWindow(removalInfo);
waitHandlerIdle(mTestHandler);
- verify(mStartingSurfaceDrawer).removeWindowSynced(eq(taskId), any(), any(), eq(false));
+ verify(mStartingSurfaceDrawer).removeWindowSynced(any());
assertEquals(mStartingSurfaceDrawer.mAddWindowForTask, 0);
}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/startingsurface/TaskSnapshotWindowTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/startingsurface/TaskSnapshotWindowTest.java
index a098a68..aad9528 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/startingsurface/TaskSnapshotWindowTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/startingsurface/TaskSnapshotWindowTest.java
@@ -83,8 +83,7 @@
createTaskDescription(Color.WHITE, Color.RED, Color.BLUE),
0 /* appearance */, windowFlags /* windowFlags */, 0 /* privateWindowFlags */,
taskBounds, ORIENTATION_PORTRAIT, ACTIVITY_TYPE_STANDARD,
- 100 /* delayRemovalTime */, new InsetsState(),
- null /* clearWindow */, new TestShellExecutor());
+ new InsetsState(), null /* clearWindow */, new TestShellExecutor());
}
private TaskSnapshot createTaskSnapshot(int width, int height, Point taskSize,
diff --git a/media/java/android/media/AudioTrack.java b/media/java/android/media/AudioTrack.java
index 23d9532..aa72d965 100644
--- a/media/java/android/media/AudioTrack.java
+++ b/media/java/android/media/AudioTrack.java
@@ -1613,7 +1613,9 @@
AudioFormat.CHANNEL_OUT_BOTTOM_FRONT_LEFT |
AudioFormat.CHANNEL_OUT_BOTTOM_FRONT_CENTER |
AudioFormat.CHANNEL_OUT_BOTTOM_FRONT_RIGHT |
- AudioFormat.CHANNEL_OUT_LOW_FREQUENCY_2;
+ AudioFormat.CHANNEL_OUT_LOW_FREQUENCY_2 |
+ AudioFormat.CHANNEL_OUT_FRONT_WIDE_LEFT |
+ AudioFormat.CHANNEL_OUT_FRONT_WIDE_RIGHT;
// Returns a boolean whether the attributes, format, bufferSizeInBytes, mode allow
// power saving to be automatically enabled for an AudioTrack. Returns false if
@@ -1787,6 +1789,8 @@
| AudioFormat.CHANNEL_OUT_TOP_SIDE_RIGHT);
put("bottom front", AudioFormat.CHANNEL_OUT_BOTTOM_FRONT_LEFT
| AudioFormat.CHANNEL_OUT_BOTTOM_FRONT_RIGHT);
+ put("front wide", AudioFormat.CHANNEL_OUT_FRONT_WIDE_LEFT
+ | AudioFormat.CHANNEL_OUT_FRONT_WIDE_RIGHT);
}};
/**
diff --git a/packages/SettingsLib/Tile/src/com/android/settingslib/drawer/Tile.java b/packages/SettingsLib/Tile/src/com/android/settingslib/drawer/Tile.java
index 8f3e4bd..220cf6b 100644
--- a/packages/SettingsLib/Tile/src/com/android/settingslib/drawer/Tile.java
+++ b/packages/SettingsLib/Tile/src/com/android/settingslib/drawer/Tile.java
@@ -18,6 +18,7 @@
import static com.android.settingslib.drawer.TileUtils.META_DATA_KEY_ORDER;
import static com.android.settingslib.drawer.TileUtils.META_DATA_KEY_PROFILE;
+import static com.android.settingslib.drawer.TileUtils.META_DATA_NEW_TASK;
import static com.android.settingslib.drawer.TileUtils.META_DATA_PREFERENCE_ICON;
import static com.android.settingslib.drawer.TileUtils.META_DATA_PREFERENCE_KEYHINT;
import static com.android.settingslib.drawer.TileUtils.META_DATA_PREFERENCE_SUMMARY;
@@ -329,6 +330,18 @@
}
/**
+ * Whether the {@link Activity} should be launched in a separate task.
+ */
+ public boolean isNewTask(Context context) {
+ ensureMetadataNotStale(context);
+ if (mMetaData != null
+ && mMetaData.containsKey(META_DATA_NEW_TASK)) {
+ return mMetaData.getBoolean(META_DATA_NEW_TASK);
+ }
+ return false;
+ }
+
+ /**
* Ensures metadata is not stale for this tile.
*/
private void ensureMetadataNotStale(Context context) {
diff --git a/packages/SettingsLib/Tile/src/com/android/settingslib/drawer/TileUtils.java b/packages/SettingsLib/Tile/src/com/android/settingslib/drawer/TileUtils.java
index a2bec33..acc0087 100644
--- a/packages/SettingsLib/Tile/src/com/android/settingslib/drawer/TileUtils.java
+++ b/packages/SettingsLib/Tile/src/com/android/settingslib/drawer/TileUtils.java
@@ -230,6 +230,13 @@
public static final String META_DATA_KEY_PROFILE = "com.android.settings.profile";
/**
+ * Name of the meta-data item that should be set in the AndroidManifest.xml
+ * to specify whether the {@link android.app.Activity} should be launched in a separate task.
+ * This should be a boolean value {@code true} or {@code false}, set using {@code android:value}
+ */
+ public static final String META_DATA_NEW_TASK = "com.android.settings.new_task";
+
+ /**
* Build a list of DashboardCategory.
*/
public static List<DashboardCategory> getCategories(Context context,
diff --git a/packages/SettingsLib/res/values-as/strings.xml b/packages/SettingsLib/res/values-as/strings.xml
index 656fe6a..eafa87b 100644
--- a/packages/SettingsLib/res/values-as/strings.xml
+++ b/packages/SettingsLib/res/values-as/strings.xml
@@ -558,7 +558,7 @@
<string name="user_new_profile_name" msgid="2405500423304678841">"নতুন প্ৰ\'ফাইল"</string>
<string name="user_info_settings_title" msgid="6351390762733279907">"ব্যৱহাৰকাৰীৰ তথ্য"</string>
<string name="profile_info_settings_title" msgid="105699672534365099">"প্ৰ\'ফাইলৰ তথ্য"</string>
- <string name="user_need_lock_message" msgid="4311424336209509301">"আপুনি সীমিত প্ৰ\'ফাইল এটা সৃষ্টি কৰাৰ আগেয়ে, আপোনাৰ ব্যক্তিগত ডেটা আৰু এপবিলাকক সুৰক্ষিত কৰিবলৈ স্ক্ৰীণ লক এটা নিৰ্ধাৰণ কৰিব লাগিব।"</string>
+ <string name="user_need_lock_message" msgid="4311424336209509301">"আপুনি সীমিত প্ৰ’ফাইল এটা সৃষ্টি কৰাৰ আগেয়ে, আপোনাৰ ব্যক্তিগত ডেটা আৰু এপ্বিলাকক সুৰক্ষিত কৰিবলৈ স্ক্ৰীন লক এটা নিৰ্ধাৰণ কৰিব লাগিব।"</string>
<string name="user_set_lock_button" msgid="1427128184982594856">"লক ছেট কৰক"</string>
<string name="user_switch_to_user" msgid="6975428297154968543">"<xliff:g id="USER_NAME">%s</xliff:g>লৈ সলনি কৰক"</string>
<string name="creating_new_user_dialog_message" msgid="7232880257538970375">"নতুন ব্যৱহাৰকাৰী সৃষ্টি কৰি থকা হৈছে…"</string>
diff --git a/packages/SettingsLib/res/values-eu/strings.xml b/packages/SettingsLib/res/values-eu/strings.xml
index a7f5eac5..de97437 100644
--- a/packages/SettingsLib/res/values-eu/strings.xml
+++ b/packages/SettingsLib/res/values-eu/strings.xml
@@ -304,7 +304,7 @@
<string name="adb_warning_message" msgid="8145270656419669221">"USB bidezko arazketa garapen-xedeetarako soilik dago diseinatuta. Erabil ezazu ordenagailuaren eta gailuaren artean datuak kopiatzeko, aplikazioak gailuan jakinarazi gabe instalatzeko eta erregistro-datuak irakurtzeko."</string>
<string name="adbwifi_warning_title" msgid="727104571653031865">"Hari gabeko arazketa baimendu nahi duzu?"</string>
<string name="adbwifi_warning_message" msgid="8005936574322702388">"Hari gabeko arazketa garapen-xedeetarako soilik dago diseinatuta. Erabil ezazu ordenagailuaren eta gailuaren artean datuak kopiatzeko, gailuan aplikazioak jakinarazi gabe instalatzeko eta erregistroko datuak irakurtzeko."</string>
- <string name="adb_keys_warning_message" msgid="2968555274488101220">"Aurretik baimendutako ordenagailu guztiei USB bidezko arazketarako sarbidea baliogabetu nahi diezu?"</string>
+ <string name="adb_keys_warning_message" msgid="2968555274488101220">"Aurretik baimendutako ordenagailu guztiei USB bidezko arazketarako sarbidea kendu nahi diezu?"</string>
<string name="dev_settings_warning_title" msgid="8251234890169074553">"Baimendu garapenerako ezarpenak?"</string>
<string name="dev_settings_warning_message" msgid="37741686486073668">"Ezarpen hauek garapen-xedeetarako pentsatu dira soilik. Baliteke ezarpenen eraginez gailua matxuratzea edo funtzionamendu okerra izatea."</string>
<string name="verify_apps_over_usb_title" msgid="6031809675604442636">"Egiaztatu USB bidezko aplik."</string>
diff --git a/packages/SettingsLib/res/values-ky/strings.xml b/packages/SettingsLib/res/values-ky/strings.xml
index 7387985..29510a7 100644
--- a/packages/SettingsLib/res/values-ky/strings.xml
+++ b/packages/SettingsLib/res/values-ky/strings.xml
@@ -305,7 +305,7 @@
<string name="adbwifi_warning_title" msgid="727104571653031865">"Мүчүлүштүктөрдү Wi-Fi аркылуу оңдоого уруксат бересизби?"</string>
<string name="adbwifi_warning_message" msgid="8005936574322702388">"Мүчүлүштүктөрдү Wi-Fi аркылуу аныктоо – өндүрүү максатында гана түзүлгөн. Аны компьютериңиз менен түзмөгүңүздүн ортосунда маалыматты алмашуу, колдонмолорду түзмөгүңүзгө эскертүүсүз орнотуу жана маалыматтар таржымалын окуу үчүн колдонсоңуз болот."</string>
<string name="adb_keys_warning_message" msgid="2968555274488101220">"Сиз мурун USB жөндөөлөрүнө уруксат берген бардык компүтерлердин жеткиси жокко чыгарылсынбы?"</string>
- <string name="dev_settings_warning_title" msgid="8251234890169074553">"Жөндөөлөрдү өзгөртүү"</string>
+ <string name="dev_settings_warning_title" msgid="8251234890169074553">"Параметрлерди өзгөртүү"</string>
<string name="dev_settings_warning_message" msgid="37741686486073668">"Бул орнотуулар өндүрүүчүлөр үчүн гана берилген. Булар түзмөгүңүздүн колдонмолорун бузулушуна же туура эмес иштешине алып келиши мүмкүн."</string>
<string name="verify_apps_over_usb_title" msgid="6031809675604442636">"Орнотулуучу колдонмону текшерүү"</string>
<string name="verify_apps_over_usb_summary" msgid="1317933737581167839">"ADB/ADT аркылуу орнотулган колдонмолордун коопсуздугу текшерилет."</string>
diff --git a/packages/SettingsLib/res/values-or/strings.xml b/packages/SettingsLib/res/values-or/strings.xml
index c885c16..8938fcf 100644
--- a/packages/SettingsLib/res/values-or/strings.xml
+++ b/packages/SettingsLib/res/values-or/strings.xml
@@ -155,7 +155,7 @@
<string name="running_process_item_user_label" msgid="3988506293099805796">"ଉପଯୋଗକର୍ତ୍ତା: <xliff:g id="USER_NAME">%1$s</xliff:g>"</string>
<string name="launch_defaults_some" msgid="3631650616557252926">"କିଛି ପୂର୍ବ-ନିର୍ଦ୍ଧାରିତ ମାନ ସେଟ୍ ହୋଇଛି"</string>
<string name="launch_defaults_none" msgid="8049374306261262709">"କୌଣସି ଡିଫଲ୍ଟ ସେଟ୍ ହୋଇନାହିଁ"</string>
- <string name="tts_settings" msgid="8130616705989351312">"ଟେକ୍ସଟ-ରୁ-ସ୍ପିଚ୍ ସେଟିଂସ୍"</string>
+ <string name="tts_settings" msgid="8130616705989351312">"ଟେକ୍ସଟ୍-ଟୁ-ସ୍ପିଚ୍ ସେଟିଂସ"</string>
<string name="tts_settings_title" msgid="7602210956640483039">"ଟେକ୍ସଟ୍-ଟୁ-ସ୍ପିଚ୍ ଆଉଟ୍ପୁଟ୍"</string>
<string name="tts_default_rate_title" msgid="3964187817364304022">"ସ୍ପିଚ୍ ରେଟ୍"</string>
<string name="tts_default_rate_summary" msgid="3781937042151716987">"ଲେଖା ପଢ଼ିବାର ବେଗ"</string>
diff --git a/packages/SettingsLib/res/values-te/strings.xml b/packages/SettingsLib/res/values-te/strings.xml
index 6f8f99b..545cf95 100644
--- a/packages/SettingsLib/res/values-te/strings.xml
+++ b/packages/SettingsLib/res/values-te/strings.xml
@@ -90,7 +90,7 @@
<string name="bluetooth_profile_pbap" msgid="7064307749579335765">"కాంటాక్ట్ షేరింగ్"</string>
<string name="bluetooth_profile_pbap_summary" msgid="2955819694801952056">"కాంటాక్ట్ షేరింగ్ కోసం ఉపయోగించండి"</string>
<string name="bluetooth_profile_pan_nap" msgid="7871974753822470050">"ఇంటర్నెట్ కనెక్షన్ షేరింగ్"</string>
- <string name="bluetooth_profile_map" msgid="8907204701162107271">"వచన మెసేజ్లు"</string>
+ <string name="bluetooth_profile_map" msgid="8907204701162107271">"టెక్స్ట్ మెసేజ్లు"</string>
<string name="bluetooth_profile_sap" msgid="8304170950447934386">"SIM యాక్సెస్"</string>
<string name="bluetooth_profile_a2dp_high_quality" msgid="4739440941324792775">"HD ఆడియో: <xliff:g id="CODEC_NAME">%1$s</xliff:g>"</string>
<string name="bluetooth_profile_a2dp_high_quality_unknown_codec" msgid="2477639096903834374">"HD ఆడియో"</string>
diff --git a/packages/SystemUI/docs/keyguard.md b/packages/SystemUI/docs/keyguard.md
new file mode 100644
index 0000000..e3d48ae
--- /dev/null
+++ b/packages/SystemUI/docs/keyguard.md
@@ -0,0 +1,12 @@
+# Keyguard (aka Lockscreen)
+
+Keyguard is responsible for:
+
+1. Handling authentication to allow the user to unlock the device, via biometrics or [KeyguardBouncer][1]
+2. Displaying informational content such as the time, notifications, and smartspace
+3. Always-on Display (AOD)
+
+Keyguard is the first screen available when turning on the device, as long as the user has not specified a security method of NONE.
+
+[1]: /frameworks/base/packages/SystemUI/docs/keyguard/bouncer.md
+
diff --git a/packages/SystemUI/docs/keyguard/bouncer.md b/packages/SystemUI/docs/keyguard/bouncer.md
new file mode 100644
index 0000000..a724966
--- /dev/null
+++ b/packages/SystemUI/docs/keyguard/bouncer.md
@@ -0,0 +1,18 @@
+# Bouncer
+
+[KeyguardBouncer][1] is the component responsible for displaying the security method set by the user (password, PIN, pattern) as well as SIM-related security methods, allowing the user to unlock the device or SIM.
+
+## Components
+
+The bouncer contains a hierarchy of controllers/views to render the user's security method and to manage the authentication attempts.
+
+1. [KeyguardBouncer][1] - Entrypoint for managing the bouncer visibility.
+ 1. [KeyguardHostViewController][2] - Intercepts media keys. Can most likely be merged with the next item.
+ 1. [KeyguardSecurityContainerController][3] - Manages unlock attempt responses, one-handed use
+ 1. [KeyguardSecurityViewFlipperController][4] - Based upon the [KeyguardSecurityModel#SecurityMode][5], will instantiate the required view and controller. PIN, Pattern, etc.
+
+[1]: /frameworks/base/packages/SystemUI/com/android/systemui/statusbar/phone/KeyguardBouncer
+[2]: /frameworks/base/packages/SystemUI/com/android/keyguard/KeyguardHostViewController
+[3]: /frameworks/base/packages/SystemUI/com/android/keyguard/KeyguardSecurityContainerController
+[4]: /frameworks/base/packages/SystemUI/com/android/keyguard/KeyguardSecurityViewFlipperController
+[5]: /frameworks/base/packages/SystemUI/com/android/keyguard/KeyguardSecurityModel
diff --git a/packages/SystemUI/plugin/src/com/android/systemui/plugins/qs/QS.java b/packages/SystemUI/plugin/src/com/android/systemui/plugins/qs/QS.java
index 0424382..a03d849 100644
--- a/packages/SystemUI/plugin/src/com/android/systemui/plugins/qs/QS.java
+++ b/packages/SystemUI/plugin/src/com/android/systemui/plugins/qs/QS.java
@@ -16,7 +16,6 @@
import android.view.View;
import android.view.View.OnClickListener;
-import android.view.ViewGroup;
import com.android.systemui.plugins.FragmentBase;
import com.android.systemui.plugins.annotations.DependsOn;
@@ -56,7 +55,7 @@
void setQsExpansion(float qsExpansionFraction, float headerTranslation);
void setHeaderListening(boolean listening);
void notifyCustomizeChanged();
- void setContainer(ViewGroup container);
+ void setContainerController(QSContainerController controller);
void setExpandClickListener(OnClickListener onClickListener);
View getHeader();
diff --git a/packages/SystemUI/plugin/src/com/android/systemui/plugins/qs/QSContainerController.kt b/packages/SystemUI/plugin/src/com/android/systemui/plugins/qs/QSContainerController.kt
new file mode 100644
index 0000000..8bf982d
--- /dev/null
+++ b/packages/SystemUI/plugin/src/com/android/systemui/plugins/qs/QSContainerController.kt
@@ -0,0 +1,9 @@
+package com.android.systemui.plugins.qs
+
+interface QSContainerController {
+ fun setCustomizerAnimating(animating: Boolean)
+
+ fun setCustomizerShowing(showing: Boolean)
+
+ fun setDetailShowing(showing: Boolean)
+}
\ No newline at end of file
diff --git a/packages/SystemUI/res-keyguard/color/numpad_key_color_secondary.xml b/packages/SystemUI/res-keyguard/color/numpad_key_color_secondary.xml
new file mode 100644
index 0000000..08c66a2
--- /dev/null
+++ b/packages/SystemUI/res-keyguard/color/numpad_key_color_secondary.xml
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="utf-8"?>
+<selector xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:androidprv="http://schemas.android.com/apk/prv/res/android">
+ <item android:color="?androidprv:attr/colorAccentSecondaryVariant"/>
+</selector>
\ No newline at end of file
diff --git a/packages/SystemUI/res-keyguard/values/styles.xml b/packages/SystemUI/res-keyguard/values/styles.xml
index 6fd83c5..6e89fb0 100644
--- a/packages/SystemUI/res-keyguard/values/styles.xml
+++ b/packages/SystemUI/res-keyguard/values/styles.xml
@@ -17,8 +17,7 @@
*/
-->
-<resources
- xmlns:androidprv="http://schemas.android.com/apk/prv/res/android">
+<resources xmlns:android="http://schemas.android.com/apk/res/android">
<!-- Keyguard PIN pad styles -->
<style name="Keyguard.TextView" parent="@android:style/Widget.DeviceDefault.TextView">
<item name="android:textSize">@dimen/kg_status_line_font_size</item>
@@ -59,11 +58,11 @@
<item name="android:textColor">?android:attr/textColorPrimary</item>
</style>
<style name="NumPadKey.Delete">
- <item name="android:colorControlNormal">?androidprv:attr/colorAccentSecondaryVariant</item>
+ <item name="android:colorControlNormal">@color/numpad_key_color_secondary</item>
<item name="android:src">@drawable/ic_backspace_24dp</item>
</style>
<style name="NumPadKey.Enter">
- <item name="android:colorControlNormal">?androidprv:attr/colorAccentSecondaryVariant</item>
+ <item name="android:colorControlNormal">@color/numpad_key_color_secondary</item>
<item name="android:src">@drawable/ic_keyboard_tab_36dp</item>
</style>
<style name="Widget.TextView.NumPadKey.Klondike"
diff --git a/packages/SystemUI/res/color/prv_color_surface.xml b/packages/SystemUI/res/color/prv_color_surface.xml
new file mode 100644
index 0000000..b9d016c
--- /dev/null
+++ b/packages/SystemUI/res/color/prv_color_surface.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2021 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+<selector xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
+ xmlns:android="http://schemas.android.com/apk/res/android">
+ <item android:color="?androidprv:attr/colorSurface" />
+</selector>
\ No newline at end of file
diff --git a/packages/SystemUI/res/color/prv_text_color_on_accent.xml b/packages/SystemUI/res/color/prv_text_color_on_accent.xml
new file mode 100644
index 0000000..9f44aca
--- /dev/null
+++ b/packages/SystemUI/res/color/prv_text_color_on_accent.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2021 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+<selector xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
+ xmlns:android="http://schemas.android.com/apk/res/android">
+ <item android:color="?androidprv:attr/textColorOnAccent" />
+</selector>
\ No newline at end of file
diff --git a/packages/SystemUI/res/drawable/qs_dialog_btn_filled.xml b/packages/SystemUI/res/drawable/qs_dialog_btn_filled.xml
new file mode 100644
index 0000000..1a128df
--- /dev/null
+++ b/packages/SystemUI/res/drawable/qs_dialog_btn_filled.xml
@@ -0,0 +1,39 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2021 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+<inset xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
+ android:insetTop="@dimen/qs_dialog_button_vertical_inset"
+ android:insetBottom="@dimen/qs_dialog_button_vertical_inset">
+ <ripple android:color="?android:attr/colorControlHighlight">
+ <item android:id="@android:id/mask">
+ <shape android:shape="rectangle">
+ <solid android:color="@android:color/white"/>
+ <corners android:radius="?android:attr/buttonCornerRadius"/>
+ </shape>
+ </item>
+ <item>
+ <shape android:shape="rectangle">
+ <corners android:radius="?android:attr/buttonCornerRadius"/>
+ <solid android:color="?androidprv:attr/colorAccentPrimary"/>
+ <padding android:left="@dimen/qs_dialog_button_horizontal_padding"
+ android:top="@dimen/qs_dialog_button_vertical_padding"
+ android:right="@dimen/qs_dialog_button_horizontal_padding"
+ android:bottom="@dimen/qs_dialog_button_vertical_padding"/>
+ </shape>
+ </item>
+ </ripple>
+</inset>
diff --git a/packages/SystemUI/res/drawable/qs_dialog_btn_outline.xml b/packages/SystemUI/res/drawable/qs_dialog_btn_outline.xml
new file mode 100644
index 0000000..467c20f
--- /dev/null
+++ b/packages/SystemUI/res/drawable/qs_dialog_btn_outline.xml
@@ -0,0 +1,42 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2021 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+<inset xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
+ android:insetTop="@dimen/qs_dialog_button_vertical_inset"
+ android:insetBottom="@dimen/qs_dialog_button_vertical_inset">
+ <ripple android:color="?android:attr/colorControlHighlight">
+ <item android:id="@android:id/mask">
+ <shape android:shape="rectangle">
+ <solid android:color="@android:color/white"/>
+ <corners android:radius="?android:attr/buttonCornerRadius"/>
+ </shape>
+ </item>
+ <item>
+ <shape android:shape="rectangle">
+ <corners android:radius="?android:attr/buttonCornerRadius"/>
+ <solid android:color="@android:color/transparent"/>
+ <stroke android:color="?androidprv:attr/colorAccentPrimary"
+ android:width="1dp"
+ />
+ <padding android:left="@dimen/qs_dialog_button_horizontal_padding"
+ android:top="@dimen/qs_dialog_button_vertical_padding"
+ android:right="@dimen/qs_dialog_button_horizontal_padding"
+ android:bottom="@dimen/qs_dialog_button_vertical_padding"/>
+ </shape>
+ </item>
+ </ripple>
+</inset>
diff --git a/packages/SystemUI/res/layout/global_screenshot_static.xml b/packages/SystemUI/res/layout/global_screenshot_static.xml
index 6a9254c..21c5ab0 100644
--- a/packages/SystemUI/res/layout/global_screenshot_static.xml
+++ b/packages/SystemUI/res/layout/global_screenshot_static.xml
@@ -130,13 +130,4 @@
app:layout_constraintStart_toStartOf="@id/global_screenshot_preview"
app:layout_constraintTop_toTopOf="@id/global_screenshot_preview"
android:elevation="@dimen/screenshot_preview_elevation"/>
- <View
- android:id="@+id/screenshot_transition_view"
- android:layout_width="0dp"
- android:layout_height="0dp"
- android:visibility="invisible"
- app:layout_constraintStart_toStartOf="@id/global_screenshot_preview"
- app:layout_constraintTop_toTopOf="@id/global_screenshot_preview"
- app:layout_constraintEnd_toEndOf="@id/global_screenshot_preview"
- app:layout_constraintBottom_toBottomOf="@id/global_screenshot_preview"/>
</androidx.constraintlayout.widget.ConstraintLayout>
diff --git a/packages/SystemUI/res/layout/qs_user_dialog_content.xml b/packages/SystemUI/res/layout/qs_user_dialog_content.xml
new file mode 100644
index 0000000..321fe68
--- /dev/null
+++ b/packages/SystemUI/res/layout/qs_user_dialog_content.xml
@@ -0,0 +1,89 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<!--
+ ~ Copyright (C) 2021 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<androidx.constraintlayout.widget.ConstraintLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:sysui="http://schemas.android.com/apk/res-auto"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:padding="24dp"
+ android:layout_marginStart="16dp"
+ android:layout_marginEnd="16dp"
+ android:background="@drawable/qs_dialog_bg"
+>
+ <TextView
+ android:id="@+id/title"
+ android:layout_height="wrap_content"
+ android:layout_width="0dp"
+ android:textAlignment="center"
+ android:text="@string/qs_user_switch_dialog_title"
+ android:textAppearance="@style/TextAppearance.QSDialog.Title"
+ android:layout_marginBottom="32dp"
+ sysui:layout_constraintTop_toTopOf="parent"
+ sysui:layout_constraintStart_toStartOf="parent"
+ sysui:layout_constraintEnd_toEndOf="parent"
+ sysui:layout_constraintBottom_toTopOf="@id/grid"
+ />
+
+ <com.android.systemui.qs.PseudoGridView
+ android:id="@+id/grid"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginBottom="28dp"
+ sysui:verticalSpacing="4dp"
+ sysui:horizontalSpacing="4dp"
+ sysui:fixedChildWidth="80dp"
+ sysui:layout_constraintTop_toBottomOf="@id/title"
+ sysui:layout_constraintStart_toStartOf="parent"
+ sysui:layout_constraintEnd_toEndOf="parent"
+ sysui:layout_constraintBottom_toTopOf="@id/barrier"
+ />
+
+ <androidx.constraintlayout.widget.Barrier
+ android:id="@+id/barrier"
+ android:layout_height="wrap_content"
+ android:layout_width="wrap_content"
+ sysui:barrierDirection="top"
+ sysui:constraint_referenced_ids="settings,done"
+ />
+
+ <Button
+ android:id="@+id/settings"
+ android:layout_width="wrap_content"
+ android:layout_height="48dp"
+ android:text="@string/quick_settings_more_user_settings"
+ sysui:layout_constraintTop_toBottomOf="@id/barrier"
+ sysui:layout_constraintBottom_toBottomOf="parent"
+ sysui:layout_constraintStart_toStartOf="parent"
+ sysui:layout_constraintEnd_toStartOf="@id/done"
+ sysui:layout_constraintHorizontal_chainStyle="spread_inside"
+ style="@style/Widget.QSDialog.Button.BorderButton"
+ />
+
+ <Button
+ android:id="@+id/done"
+ android:layout_width="wrap_content"
+ android:layout_height="48dp"
+ android:text="@string/quick_settings_done"
+ sysui:layout_constraintTop_toBottomOf="@id/barrier"
+ sysui:layout_constraintBottom_toBottomOf="parent"
+ sysui:layout_constraintStart_toEndOf="@id/settings"
+ sysui:layout_constraintEnd_toEndOf="parent"
+ style="@style/Widget.QSDialog.Button"
+ />
+</androidx.constraintlayout.widget.ConstraintLayout>
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout/super_notification_shade.xml b/packages/SystemUI/res/layout/super_notification_shade.xml
index d9a5670..e02a1767 100644
--- a/packages/SystemUI/res/layout/super_notification_shade.xml
+++ b/packages/SystemUI/res/layout/super_notification_shade.xml
@@ -97,7 +97,7 @@
android:singleLine="true"
android:ellipsize="marquee"
android:focusable="true" />
- <FrameLayout android:id="@+id/keyboard_bouncer_container"
+ <FrameLayout android:id="@+id/keyguard_bouncer_container"
android:layout_height="0dp"
android:layout_width="match_parent"
android:layout_weight="1" />
diff --git a/packages/SystemUI/res/values-bn/strings.xml b/packages/SystemUI/res/values-bn/strings.xml
index 93ac040..57e70a6 100644
--- a/packages/SystemUI/res/values-bn/strings.xml
+++ b/packages/SystemUI/res/values-bn/strings.xml
@@ -1175,8 +1175,7 @@
<string name="unlock_to_view_networks" msgid="5072880496312015676">"নেটওয়ার্ক দেখার জন্য আনলক করুন"</string>
<string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"নেটওয়ার্ক সার্চ করা হচ্ছে…"</string>
<string name="wifi_failed_connect_message" msgid="4161863112079000071">"নেটওয়ার্কে কানেক্ট করা যায়নি"</string>
- <!-- no translation found for wifi_wont_autoconnect_for_now (5782282612749867762) -->
- <skip />
+ <string name="wifi_wont_autoconnect_for_now" msgid="5782282612749867762">"এখন ওয়াই-ফাই নিজে থেকে কানেক্ট হবে না"</string>
<string name="see_all_networks" msgid="3773666844913168122">"সবকটি দেখুন"</string>
<string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"নেটওয়ার্ক বদলাতে ইথারনেট ডিসকানেক্ট করুন"</string>
</resources>
diff --git a/packages/SystemUI/res/values-es/strings.xml b/packages/SystemUI/res/values-es/strings.xml
index c0c12fa..8db8770 100644
--- a/packages/SystemUI/res/values-es/strings.xml
+++ b/packages/SystemUI/res/values-es/strings.xml
@@ -124,8 +124,8 @@
<string name="screenrecord_permission_error" msgid="7856841237023137686">"No se han podido obtener los permisos"</string>
<string name="screenrecord_start_error" msgid="2200660692479682368">"No se ha podido empezar a grabar la pantalla"</string>
<string name="usb_preference_title" msgid="1439924437558480718">"Opciones de transferencia de archivos por USB"</string>
- <string name="use_mtp_button_title" msgid="5036082897886518086">"Activar como reproductor de medios (MTP)"</string>
- <string name="use_ptp_button_title" msgid="7676427598943446826">"Activar como cámara (PTP)"</string>
+ <string name="use_mtp_button_title" msgid="5036082897886518086">"Montar como reproductor de medios (MTP)"</string>
+ <string name="use_ptp_button_title" msgid="7676427598943446826">"Montar como cámara (PTP)"</string>
<string name="installer_cd_button_title" msgid="5499998592841984743">"Instalar Android File Transfer para Mac"</string>
<string name="accessibility_back" msgid="6530104400086152611">"Atrás"</string>
<string name="accessibility_home" msgid="5430449841237966217">"Inicio"</string>
diff --git a/packages/SystemUI/res/values-gu/strings.xml b/packages/SystemUI/res/values-gu/strings.xml
index 7928115..e713cb7 100644
--- a/packages/SystemUI/res/values-gu/strings.xml
+++ b/packages/SystemUI/res/values-gu/strings.xml
@@ -1175,8 +1175,7 @@
<string name="unlock_to_view_networks" msgid="5072880496312015676">"વાઇ-ફાઇ નેટવર્ક જોવા માટે અનલૉક કરો"</string>
<string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"નેટવર્ક શોધી રહ્યાં છીએ…"</string>
<string name="wifi_failed_connect_message" msgid="4161863112079000071">"નેટવર્ક સાથે કનેક્ટ કરવામાં નિષ્ફળ થયાં"</string>
- <!-- no translation found for wifi_wont_autoconnect_for_now (5782282612749867762) -->
- <skip />
+ <string name="wifi_wont_autoconnect_for_now" msgid="5782282612749867762">"હમણાં પૂરતું વાઇ-ફાઇ ઑટોમૅટિક રીતે કનેક્ટ થશે નહીં"</string>
<string name="see_all_networks" msgid="3773666844913168122">"બધા જુઓ"</string>
<string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"બીજા નેટવર્ક પર જવા માટે, ઇથરનેટ ડિસ્કનેક્ટ કરો"</string>
</resources>
diff --git a/packages/SystemUI/res/values-kn/strings.xml b/packages/SystemUI/res/values-kn/strings.xml
index 0a28735..ca69a82 100644
--- a/packages/SystemUI/res/values-kn/strings.xml
+++ b/packages/SystemUI/res/values-kn/strings.xml
@@ -1175,8 +1175,7 @@
<string name="unlock_to_view_networks" msgid="5072880496312015676">"ನೆಟ್ವರ್ಕ್ಗಳನ್ನು ವೀಕ್ಷಿಸಲು ಅನ್ಲಾಕ್ ಮಾಡಿ"</string>
<string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"ನೆಟ್ವರ್ಕ್ಗಳನ್ನು ಹುಡುಕಲಾಗುತ್ತಿದೆ…"</string>
<string name="wifi_failed_connect_message" msgid="4161863112079000071">"ನೆಟ್ವರ್ಕ್ಗೆ ಕನೆಕ್ಟ್ ಮಾಡಲು ವಿಫಲವಾಗಿದೆ"</string>
- <!-- no translation found for wifi_wont_autoconnect_for_now (5782282612749867762) -->
- <skip />
+ <string name="wifi_wont_autoconnect_for_now" msgid="5782282612749867762">"ಸದ್ಯದ ಮಟ್ಟಿಗೆ ವೈ-ಫೈ ಸ್ವಯಂಚಾಲಿತವಾಗಿ ಕನೆಕ್ಟ್ ಆಗುವುದಿಲ್ಲ"</string>
<string name="see_all_networks" msgid="3773666844913168122">"ಎಲ್ಲವನ್ನೂ ನೋಡಿ"</string>
<string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"ನೆಟ್ವರ್ಕ್ಗಳನ್ನು ಬದಲಿಸಲು, ಇಥರ್ನೆಟ್ ಅನ್ನು ಡಿಸ್ಕನೆಕ್ಟ್ ಮಾಡಿ"</string>
</resources>
diff --git a/packages/SystemUI/res/values-my/strings.xml b/packages/SystemUI/res/values-my/strings.xml
index 50fb26c..666f9ed 100644
--- a/packages/SystemUI/res/values-my/strings.xml
+++ b/packages/SystemUI/res/values-my/strings.xml
@@ -1175,7 +1175,7 @@
<string name="unlock_to_view_networks" msgid="5072880496312015676">"ကွန်ရက်များကြည့်ရန် ဖွင့်ပါ"</string>
<string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"ကွန်ရက်များကို ရှာဖွေနေသည်…"</string>
<string name="wifi_failed_connect_message" msgid="4161863112079000071">"ကွန်ရက်သို့ ချိတ်ဆက်၍မရပါ"</string>
- <string name="wifi_wont_autoconnect_for_now" msgid="5782282612749867762">"Wi-Fi က ယခု အလိုအလျောက် ချိတ်ဆက်မည်မဟုတ်ပါ"</string>
+ <string name="wifi_wont_autoconnect_for_now" msgid="5782282612749867762">"Wi-Fi က လောလောဆယ် အလိုအလျောက် ချိတ်ဆက်မည်မဟုတ်ပါ"</string>
<string name="see_all_networks" msgid="3773666844913168122">"အားလုံးကြည့်ရန်"</string>
<string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"ကွန်ရက်ပြောင်းရန် အီသာနက်ကို ချိတ်ဆက်မှုဖြုတ်ပါ"</string>
</resources>
diff --git a/packages/SystemUI/res/values-nb/strings.xml b/packages/SystemUI/res/values-nb/strings.xml
index d4eef72..c25bec7 100644
--- a/packages/SystemUI/res/values-nb/strings.xml
+++ b/packages/SystemUI/res/values-nb/strings.xml
@@ -602,8 +602,8 @@
<string name="volume_odi_captions_tip" msgid="8825655463280990941">"Automatisk medieteksting"</string>
<string name="accessibility_volume_close_odi_captions_tip" msgid="8924753283621160480">"Verktøytips for teksting"</string>
<string name="volume_odi_captions_content_description" msgid="4172765742046013630">"Overlegg med teksting"</string>
- <string name="volume_odi_captions_hint_enable" msgid="2073091194012843195">"slå på"</string>
- <string name="volume_odi_captions_hint_disable" msgid="2518846326748183407">"slå av"</string>
+ <string name="volume_odi_captions_hint_enable" msgid="2073091194012843195">"aktivér"</string>
+ <string name="volume_odi_captions_hint_disable" msgid="2518846326748183407">"deaktiver"</string>
<string name="accessibility_output_chooser" msgid="7807898688967194183">"Bytt enhet for lydutgang"</string>
<string name="screen_pinning_title" msgid="9058007390337841305">"Appen er festet"</string>
<string name="screen_pinning_description" msgid="8699395373875667743">"Gjør at den vises til du løsner den. Trykk og hold inne Tilbake og Oversikt for å løsne den."</string>
diff --git a/packages/SystemUI/res/values-te/strings.xml b/packages/SystemUI/res/values-te/strings.xml
index dda2830..95b8a63 100644
--- a/packages/SystemUI/res/values-te/strings.xml
+++ b/packages/SystemUI/res/values-te/strings.xml
@@ -101,7 +101,7 @@
<string name="screenrecord_background_processing_label" msgid="7244617554884238898">"స్క్రీన్ రికార్డింగ్ అవుతోంది"</string>
<string name="screenrecord_channel_description" msgid="4147077128486138351">"స్క్రీన్ రికార్డ్ సెషన్ కోసం ఆన్గోయింగ్ నోటిఫికేషన్"</string>
<string name="screenrecord_start_label" msgid="1750350278888217473">"రికార్డింగ్ను ప్రారంభించాలా?"</string>
- <string name="screenrecord_description" msgid="1123231719680353736">"రికార్డ్ చేస్తున్నప్పుడు, Android సిస్టమ్ మీ స్క్రీన్పై ప్రదర్శించబడిన లేదా మీ పరికరం నుండి ప్లే చేయబడిన ఏ సున్నితమైన సమాచారాన్నైనా క్యాప్చర్ చేయగలదు. ఈ సమాచారంలో, పాస్వర్డ్లు, పేమెంట్ వివరాలు, ఫోటోలు, మెసేజ్లు, ఆడియో ఉంటాయి."</string>
+ <string name="screenrecord_description" msgid="1123231719680353736">"రికార్డ్ చేస్తున్నప్పుడు, Android సిస్టమ్ మీ స్క్రీన్పై ప్రదర్శించబడిన లేదా మీ పరికరం నుండి ప్లే చేయబడిన ఏ సున్నితమైన సమాచారాన్నయినా క్యాప్చర్ చేయగలదు. ఈ సమాచారంలో, పాస్వర్డ్లు, పేమెంట్ వివరాలు, ఫోటోలు, మెసేజ్లు, ఆడియో కూడా ఉంటాయి."</string>
<string name="screenrecord_audio_label" msgid="6183558856175159629">"ఆడియోను రికార్డ్ చేయి"</string>
<string name="screenrecord_device_audio_label" msgid="9016927171280567791">"పరికరం ఆడియో"</string>
<string name="screenrecord_device_audio_description" msgid="4922694220572186193">"మీ పరికరం నుండి వచ్చే సంగీతం, కాల్స్, రింగ్టోన్ల వంటి ధ్వనులు"</string>
@@ -507,7 +507,7 @@
<string name="battery_saver_notification_title" msgid="8419266546034372562">"బ్యాటరీ సేవర్ ఆన్లో ఉంది"</string>
<string name="battery_saver_notification_text" msgid="2617841636449016951">"పనితీరుని మరియు నేపథ్య డేటాను తగ్గిస్తుంది"</string>
<string name="battery_saver_notification_action_text" msgid="6022091913807026887">"బ్యాటరీ సేవర్ను ఆఫ్ చేయండి"</string>
- <string name="media_projection_dialog_text" msgid="1755705274910034772">"రికార్డ్ చేస్తున్నప్పుడు లేదా ప్రసారం చేస్తున్నప్పుడు, మీ స్క్రీన్పై ప్రదర్శించబడిన లేదా మీ పరికరం నుండి ప్లే చేయబడిన సమాచారం మొత్తాన్ని, <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> యాక్సెస్ చేయగలుగుతుంది. ఈ సమాచారంలో, పాస్వర్డ్లు, చెల్లింపు వివరాలు, ఫోటోలు, మెసేజ్లు, మీరు ప్లే చేసే ఆడియో వంటివి ఉంటాయి."</string>
+ <string name="media_projection_dialog_text" msgid="1755705274910034772">"రికార్డ్ చేస్తున్నప్పుడు లేదా ప్రసారం చేస్తున్నప్పుడు, మీ స్క్రీన్పై ప్రదర్శించబడిన లేదా మీ పరికరం నుండి ప్లే చేయబడిన సమాచారం మొత్తాన్ని, <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> యాక్సెస్ చేయగలుగుతుంది. ఈ సమాచారంలో, పాస్వర్డ్లు, పేమెంట్ వివరాలు, ఫోటోలు, మెసేజ్లు, మీరు ప్లే చేసే ఆడియో వంటివి ఉంటాయి."</string>
<string name="media_projection_dialog_service_text" msgid="958000992162214611">"రికార్డ్ చేస్తున్నప్పుడు లేదా ప్రసారం చేస్తున్నప్పుడు మీ స్క్రీన్పై ప్రదర్శించబడిన లేదా మీ పరికరం నుండి ప్లే చేయబడిన సమాచారం మొత్తాన్ని, ఈ ఫంక్షన్ను అందిస్తున్న సర్వీస్ యాక్సెస్ చేయగలదు. ఈ సమాచారంలో, పాస్వర్డ్లు, పేమెంట్ వివరాలు, ఫోటోలు, మెసేజ్లు, మీరు ప్లే చేసే ఆడియో వంటివి ఉంటాయి."</string>
<string name="media_projection_dialog_service_title" msgid="2888507074107884040">"రికార్డ్ చేయడం లేదా ప్రసారం చేయడం ప్రారంభించాలా?"</string>
<string name="media_projection_dialog_title" msgid="3316063622495360646">"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>తో రికార్డ్ చేయడం లేదా ప్రసారం చేయడం ప్రారంభించాలా?"</string>
@@ -1145,7 +1145,7 @@
<string name="status_before_loading" msgid="1500477307859631381">"కంటెంట్ త్వరలో కనిపిస్తుంది"</string>
<string name="missed_call" msgid="4228016077700161689">"మిస్డ్ కాల్"</string>
<string name="messages_count_overflow_indicator" msgid="7850934067082006043">"<xliff:g id="NUMBER">%d</xliff:g>+"</string>
- <string name="people_tile_description" msgid="8154966188085545556">"ఇటీవలి మెసేజ్లు, మిస్డ్ కాల్స్, అలాగే స్టేటస్ అప్డేట్లను చూడండి"</string>
+ <string name="people_tile_description" msgid="8154966188085545556">"ఇటీవలి మెసేజ్లు, మిస్స్డ్ కాల్స్, అలాగే స్టేటస్ అప్డేట్లను చూడండి"</string>
<string name="people_tile_title" msgid="6589377493334871272">"సంభాషణ"</string>
<string name="paused_by_dnd" msgid="7856941866433556428">"అంతరాయం కలిగించవద్దు ద్వారా పాజ్ చేయబడింది"</string>
<string name="new_notification_text_content_description" msgid="2915029960094389291">"<xliff:g id="NAME">%1$s</xliff:g> మెసేజ్ను పంపారు: <xliff:g id="NOTIFICATION">%2$s</xliff:g>"</string>
diff --git a/packages/SystemUI/res/values-ur/strings.xml b/packages/SystemUI/res/values-ur/strings.xml
index da431826..44b91ad 100644
--- a/packages/SystemUI/res/values-ur/strings.xml
+++ b/packages/SystemUI/res/values-ur/strings.xml
@@ -1175,8 +1175,7 @@
<string name="unlock_to_view_networks" msgid="5072880496312015676">"نیٹ ورکس کو دیکھنے کے لیے غیر مقفل کریں"</string>
<string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"نیٹ ورکس تلاش کیے جا رہے ہیں…"</string>
<string name="wifi_failed_connect_message" msgid="4161863112079000071">"نیٹ ورک سے منسلک ہونے میں ناکام ہو گیا"</string>
- <!-- no translation found for wifi_wont_autoconnect_for_now (5782282612749867762) -->
- <skip />
+ <string name="wifi_wont_autoconnect_for_now" msgid="5782282612749867762">"ابھی Wi-Fi خود کار طور پر منسلک نہیں ہوگا"</string>
<string name="see_all_networks" msgid="3773666844913168122">"سبھی دیکھیں"</string>
<string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"نیٹ ورکس پر سوئچ کرنے کیلئے، ایتھرنیٹ غیر منسلک کریں"</string>
</resources>
diff --git a/packages/SystemUI/res/values/attrs.xml b/packages/SystemUI/res/values/attrs.xml
index 3121ce3..db69924 100644
--- a/packages/SystemUI/res/values/attrs.xml
+++ b/packages/SystemUI/res/values/attrs.xml
@@ -77,6 +77,7 @@
<attr name="numColumns" format="integer" />
<attr name="verticalSpacing" format="dimension" />
<attr name="horizontalSpacing" format="dimension" />
+ <attr name="fixedChildWidth" format="dimension" />
</declare-styleable>
<!-- Theme for icons in the status/nav bar (light/dark). background/fillColor is used for dual
diff --git a/packages/SystemUI/res/values/config.xml b/packages/SystemUI/res/values/config.xml
index 73643c8..d0de876 100644
--- a/packages/SystemUI/res/values/config.xml
+++ b/packages/SystemUI/res/values/config.xml
@@ -191,6 +191,15 @@
low powered state yet. -->
<bool name="doze_single_tap_uses_prox">true</bool>
+ <!-- Doze: whether the single tap sensor uses the proximity sensor in the given posture.
+ See doze_single_tap_uses_prox for usage. -->
+ <integer-array name="doze_single_tap_uses_prox_posture_mapping">
+ <item>1</item> <!-- UNKNOWN -->
+ <item>1</item> <!-- CLOSED -->
+ <item>1</item> <!-- HALF_OPENED -->
+ <item>1</item> <!-- OPENED -->
+ </integer-array>
+
<!-- Doze: whether the long press sensor uses the proximity sensor.
If both this parameter and doze_selectively_register_prox are true, registration for the
sensor will be delayed when the device first enters dozing but the device has not entered its
@@ -211,9 +220,27 @@
always-on display) -->
<string name="doze_brightness_sensor_type" translatable="false"></string>
+ <!-- Name of a sensor per posture state that provides a low-power estimate of the desired
+ display brightness, suitable to listen to while the device is asleep (e.g. during
+ always-on display) -->
+ <string-array name="doze_brightness_sensor_name_posture_mapping" translatable="false">
+ <!-- UNKNOWN -->
+ <!-- CLOSED -->
+ <!-- HALF_OPENED -->
+ <!-- OPENED -->
+ </string-array>
+
<!-- Override value to use for proximity sensor. -->
<string name="proximity_sensor_type" translatable="false"></string>
+ <!-- Sensor type per posture state to use for proximity sensor -->
+ <string-array name="proximity_sensor_posture_mapping" translatable="false">
+ <!-- UNKNOWN -->
+ <!-- CLOSED -->
+ <!-- HALF_OPENED -->
+ <!-- OPENED -->
+ </string-array>
+
<!-- If using proximity_sensor_type, specifies a threshold value to distinguish near and
far break points. A sensor value less than this is considered "near". -->
<item name="proximity_sensor_threshold" translatable="false" format="float" type="dimen"></item>
@@ -227,6 +254,15 @@
<!-- Override value to use for proximity sensor as confirmation for proximity_sensor_type. -->
<string name="proximity_sensor_secondary_type" translatable="false"></string>
+ <!-- Sensor type per posture state to use for proximity sensor as a confirmation for
+ proximity_sensor_type. -->
+ <string-array name="proximity_sensor_secondary_posture_mapping" translatable="false">
+ <!-- UNKNOWN -->
+ <!-- CLOSED -->
+ <!-- HALF_OPENED -->
+ <!-- OPENED -->
+ </string-array>
+
<!-- If using proximity_sensor_secondary_type, specifies a threshold value to distinguish
near and far break points. A sensor value less than this is considered "near". -->
<item name="proximity_sensor_secondary_threshold" translatable="false" format="float"
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index 9f25746..946bbc2 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -1639,4 +1639,9 @@
<item name="communal_source_height_percentage" format="float" type="dimen">0.80</item>
<dimen name="drag_and_drop_icon_size">70dp</dimen>
+
+ <dimen name="qs_dialog_button_horizontal_padding">16dp</dimen>
+ <dimen name="qs_dialog_button_vertical_padding">8dp</dimen>
+ <!-- The button will be 48dp tall, but the background needs to be 36dp tall -->
+ <dimen name="qs_dialog_button_vertical_inset">6dp</dimen>
</resources>
diff --git a/packages/SystemUI/res/values/flags.xml b/packages/SystemUI/res/values/flags.xml
index d442cc5..d6d98b9 100644
--- a/packages/SystemUI/res/values/flags.xml
+++ b/packages/SystemUI/res/values/flags.xml
@@ -46,9 +46,13 @@
<bool name="flag_ongoing_call_status_bar_chip">true</bool>
+ <bool name="flag_ongoing_call_in_immersive">false</bool>
+
<bool name="flag_smartspace">false</bool>
<bool name="flag_smartspace_deduping">true</bool>
<bool name="flag_combined_status_bar_signal_icons">false</bool>
+
+ <bool name="flag_new_user_switcher">true</bool>
</resources>
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index c26de37..ec63df5 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -3026,4 +3026,7 @@
<string name="see_all_networks">See all</string>
<!-- Summary for warning to disconnect ethernet first then switch to other networks. [CHAR LIMIT=60] -->
<string name="to_switch_networks_disconnect_ethernet">To switch networks, disconnect ethernet</string>
+
+ <!-- Title for User Switch dialog. [CHAR LIMIT=20] -->
+ <string name="qs_user_switch_dialog_title">Select user</string>
</resources>
diff --git a/packages/SystemUI/res/values/styles.xml b/packages/SystemUI/res/values/styles.xml
index 6594216..6e51ec7 100644
--- a/packages/SystemUI/res/values/styles.xml
+++ b/packages/SystemUI/res/values/styles.xml
@@ -929,6 +929,39 @@
<item name="actionDividerHeight">32dp</item>
</style>
+ <style name="Theme.SystemUI.Dialog.QSDialog">
+ <item name="android:windowIsTranslucent">true</item>
+ <item name="android:windowBackground">@android:color/transparent</item>
+ <item name="android:windowIsFloating">true</item>
+ <item name="android:backgroundDimEnabled">true</item>
+ <item name="android:windowCloseOnTouchOutside">true</item>
+ <item name="android:windowAnimationStyle">@android:style/Animation.Dialog</item>
+ <item name="android:dialogCornerRadius">28dp</item>
+ <item name="android:buttonCornerRadius">28dp</item>
+ <item name="android:colorBackground">@color/prv_color_surface</item>
+ </style>
+
+ <style name="TextAppearance.QSDialog.Title" parent="Theme.SystemUI.Dialog.QSDialog">
+ <item name="android:textColor">?android:attr/textColorPrimary</item>
+ <item name="android:textSize">24sp</item>
+ <item name="android:fontFamily">@*android:string/config_headlineFontFamily</item>
+ <item name="android:lineHeight">32sp</item>
+ </style>
+
+ <style name="Widget.QSDialog.Button" parent = "Theme.SystemUI.Dialog.QSDialog">
+ <item name="android:background">@drawable/qs_dialog_btn_filled</item>
+ <item name="android:textColor">@color/prv_text_color_on_accent</item>
+ <item name="android:textSize">14sp</item>
+ <item name="android:lineHeight">20sp</item>
+ <item name="android:fontFamily">@*android:string/config_bodyFontFamily</item>
+ <item name="android:stateListAnimator">@null</item>
+ </style>
+
+ <style name="Widget.QSDialog.Button.BorderButton">
+ <item name="android:background">@drawable/qs_dialog_btn_outline</item>
+ <item name="android:textColor">?android:attr/textColorPrimary</item>
+ </style>
+
<style name="Theme.SystemUI.Dialog.Internet">
<item name="android:windowBackground">@drawable/internet_dialog_background</item>
</style>
diff --git a/packages/SystemUI/shared/Android.bp b/packages/SystemUI/shared/Android.bp
index 3a23094..4880b12 100644
--- a/packages/SystemUI/shared/Android.bp
+++ b/packages/SystemUI/shared/Android.bp
@@ -43,6 +43,7 @@
"src/**/*.kt",
"src/**/*.aidl",
":wm_shell-aidls",
+ ":wm_shell_util-sources",
],
static_libs: [
@@ -50,5 +51,5 @@
"androidx.dynamicanimation_dynamicanimation",
],
java_version: "1.8",
- min_sdk_version: "26",
+ min_sdk_version: "current",
}
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteAnimationAdapterCompat.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteAnimationAdapterCompat.java
index 7aee721..dcc4ea1 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteAnimationAdapterCompat.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteAnimationAdapterCompat.java
@@ -40,7 +40,7 @@
import android.window.RemoteTransition;
import android.window.TransitionInfo;
-import java.util.ArrayList;
+import com.android.wm.shell.util.CounterRotator;
/**
* @see RemoteAnimationAdapter
@@ -109,52 +109,6 @@
};
}
- private static class CounterRotator {
- SurfaceControl mSurface = null;
- ArrayList<SurfaceControl> mRotateChildren = null;
-
- void setup(SurfaceControl.Transaction t, SurfaceControl parent, int rotateDelta,
- float displayW, float displayH) {
- if (rotateDelta == 0) return;
- mRotateChildren = new ArrayList<>();
- // We want to counter-rotate, so subtract from 4
- rotateDelta = 4 - (rotateDelta + 4) % 4;
- mSurface = new SurfaceControl.Builder()
- .setName("Transition Unrotate")
- .setContainerLayer()
- .setParent(parent)
- .build();
- // column-major
- if (rotateDelta == 1) {
- t.setMatrix(mSurface, 0, 1, -1, 0);
- t.setPosition(mSurface, displayW, 0);
- } else if (rotateDelta == 2) {
- t.setMatrix(mSurface, -1, 0, 0, -1);
- t.setPosition(mSurface, displayW, displayH);
- } else if (rotateDelta == 3) {
- t.setMatrix(mSurface, 0, -1, 1, 0);
- t.setPosition(mSurface, 0, displayH);
- }
- t.show(mSurface);
- }
-
- void addChild(SurfaceControl.Transaction t, SurfaceControl child) {
- if (mSurface == null) return;
- t.reparent(child, mSurface);
- mRotateChildren.add(child);
- }
-
- void cleanUp(SurfaceControl rootLeash) {
- if (mSurface == null) return;
- SurfaceControl.Transaction t = new SurfaceControl.Transaction();
- for (int i = mRotateChildren.size() - 1; i >= 0; --i) {
- t.reparent(mRotateChildren.get(i), rootLeash);
- }
- t.remove(mSurface);
- t.apply();
- }
- }
-
private static IRemoteTransition.Stub wrapRemoteTransition(
final RemoteAnimationRunnerCompat remoteAnimationAdapter) {
return new IRemoteTransition.Stub() {
@@ -204,14 +158,14 @@
if (launcherTask != null && rotateDelta != 0 && launcherTask.getParent() != null) {
counterLauncher.setup(t, info.getChange(launcherTask.getParent()).getLeash(),
rotateDelta, displayW, displayH);
- if (counterLauncher.mSurface != null) {
- t.setLayer(counterLauncher.mSurface, launcherLayer);
+ if (counterLauncher.getSurface() != null) {
+ t.setLayer(counterLauncher.getSurface(), launcherLayer);
}
}
if (isReturnToHome) {
- if (counterLauncher.mSurface != null) {
- t.setLayer(counterLauncher.mSurface, info.getChanges().size() * 3);
+ if (counterLauncher.getSurface() != null) {
+ t.setLayer(counterLauncher.getSurface(), info.getChanges().size() * 3);
}
// Need to "boost" the closing things since that's what launcher expects.
for (int i = info.getChanges().size() - 1; i >= 0; --i) {
@@ -237,8 +191,8 @@
if (wallpaper != null && rotateDelta != 0 && wallpaper.getParent() != null) {
counterWallpaper.setup(t, info.getChange(wallpaper.getParent()).getLeash(),
rotateDelta, displayW, displayH);
- if (counterWallpaper.mSurface != null) {
- t.setLayer(counterWallpaper.mSurface, -1);
+ if (counterWallpaper.getSurface() != null) {
+ t.setLayer(counterWallpaper.getSurface(), -1);
counterWallpaper.addChild(t, leashMap.get(wallpaper.getLeash()));
}
}
diff --git a/packages/SystemUI/src/com/android/keyguard/AnimatableClockController.java b/packages/SystemUI/src/com/android/keyguard/AnimatableClockController.java
index efcf40a..a383cab 100644
--- a/packages/SystemUI/src/com/android/keyguard/AnimatableClockController.java
+++ b/packages/SystemUI/src/com/android/keyguard/AnimatableClockController.java
@@ -20,12 +20,16 @@
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
+import android.content.res.Resources;
import android.graphics.Color;
import android.icu.text.NumberFormat;
+import androidx.annotation.VisibleForTesting;
+
import com.android.settingslib.Utils;
import com.android.systemui.R;
import com.android.systemui.broadcast.BroadcastDispatcher;
+import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.statusbar.phone.KeyguardBypassController;
import com.android.systemui.statusbar.policy.BatteryController;
@@ -67,20 +71,20 @@
BroadcastDispatcher broadcastDispatcher,
BatteryController batteryController,
KeyguardUpdateMonitor keyguardUpdateMonitor,
- KeyguardBypassController bypassController) {
+ KeyguardBypassController bypassController,
+ @Main Resources resources
+ ) {
super(view);
mStatusBarStateController = statusBarStateController;
- mIsDozing = mStatusBarStateController.isDozing();
- mDozeAmount = mStatusBarStateController.getDozeAmount();
mBroadcastDispatcher = broadcastDispatcher;
mKeyguardUpdateMonitor = keyguardUpdateMonitor;
mBypassController = bypassController;
mBatteryController = batteryController;
mBurmeseNumerals = mBurmeseNf.format(FORMAT_NUMBER);
- mBurmeseLineSpacing = getContext().getResources().getFloat(
+ mBurmeseLineSpacing = resources.getFloat(
R.dimen.keyguard_clock_line_spacing_scale_burmese);
- mDefaultLineSpacing = getContext().getResources().getFloat(
+ mDefaultLineSpacing = resources.getFloat(
R.dimen.keyguard_clock_line_spacing_scale);
}
@@ -106,7 +110,7 @@
}
};
- private final StatusBarStateController.StateListener mStatusBarStatePersistentListener =
+ private final StatusBarStateController.StateListener mStatusBarStateListener =
new StatusBarStateController.StateListener() {
@Override
public void onDozeAmountChanged(float linear, float eased) {
@@ -144,11 +148,11 @@
mBroadcastDispatcher.registerReceiver(mLocaleBroadcastReceiver,
new IntentFilter(Intent.ACTION_LOCALE_CHANGED));
mDozeAmount = mStatusBarStateController.getDozeAmount();
+ mIsDozing = mStatusBarStateController.isDozing() || mDozeAmount != 0;
mBatteryController.addCallback(mBatteryCallback);
mKeyguardUpdateMonitor.registerCallback(mKeyguardUpdateMonitorCallback);
- mStatusBarStateController.removeCallback(mStatusBarStatePersistentListener);
- mStatusBarStateController.addCallback(mStatusBarStatePersistentListener);
+ mStatusBarStateController.addCallback(mStatusBarStateListener);
refreshTime();
initColors();
@@ -160,7 +164,7 @@
mBroadcastDispatcher.unregisterReceiver(mLocaleBroadcastReceiver);
mKeyguardUpdateMonitor.removeCallback(mKeyguardUpdateMonitorCallback);
mBatteryController.removeCallback(mBatteryCallback);
- mStatusBarStateController.removeCallback(mStatusBarStatePersistentListener);
+ mStatusBarStateController.removeCallback(mStatusBarStateListener);
}
/** Animate the clock appearance */
@@ -189,6 +193,14 @@
mView.refreshFormat();
}
+ /**
+ * Return locallly stored dozing state.
+ */
+ @VisibleForTesting
+ public boolean isDozing() {
+ return mIsDozing;
+ }
+
private void updateLocale() {
Locale currLocale = Locale.getDefault();
if (!Objects.equals(currLocale, mLocale)) {
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java
index 8f14cd8..f81f0b9 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java
@@ -11,6 +11,7 @@
import android.util.TypedValue;
import android.view.View;
import android.view.ViewGroup;
+import android.view.ViewTreeObserver.OnPreDrawListener;
import android.widget.FrameLayout;
import android.widget.RelativeLayout;
@@ -93,6 +94,7 @@
private int[] mColorPalette;
private int mClockSwitchYAmount;
+ @VisibleForTesting boolean mChildrenAreLaidOut = false;
public KeyguardClockSwitch(Context context, AttributeSet attrs) {
super(context, attrs);
@@ -284,11 +286,31 @@
if (mDisplayedClockSize != null && clockSize == mDisplayedClockSize) {
return false;
}
- animateClockChange(clockSize == LARGE);
- mDisplayedClockSize = clockSize;
+
+ // let's make sure clock is changed only after all views were laid out so we can
+ // translate them properly
+ if (mChildrenAreLaidOut) {
+ animateClockChange(clockSize == LARGE);
+ mDisplayedClockSize = clockSize;
+ } else {
+ getViewTreeObserver().addOnPreDrawListener(new OnPreDrawListener() {
+ @Override
+ public boolean onPreDraw() {
+ switchToClock(clockSize);
+ getViewTreeObserver().removeOnPreDrawListener(this);
+ return true;
+ }
+ });
+ }
return true;
}
+ @Override
+ protected void onLayout(boolean changed, int l, int t, int r, int b) {
+ super.onLayout(changed, l, t, r, b);
+ mChildrenAreLaidOut = true;
+ }
+
public Paint getPaint() {
return mClockView.getPaint();
}
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java
index 260b393..4111020 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java
@@ -22,6 +22,7 @@
import static com.android.keyguard.KeyguardClockSwitch.LARGE;
import android.app.WallpaperManager;
+import android.content.res.Resources;
import android.text.TextUtils;
import android.view.View;
import android.widget.FrameLayout;
@@ -32,6 +33,7 @@
import com.android.systemui.R;
import com.android.systemui.broadcast.BroadcastDispatcher;
import com.android.systemui.colorextraction.SysuiColorExtractor;
+import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.keyguard.KeyguardUnlockAnimationController;
import com.android.systemui.plugins.ClockPlugin;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
@@ -67,6 +69,7 @@
private final BroadcastDispatcher mBroadcastDispatcher;
private final BatteryController mBatteryController;
private final LockscreenSmartspaceController mSmartspaceController;
+ private final Resources mResources;
/**
* Clock for both small and large sizes
@@ -118,7 +121,8 @@
KeyguardBypassController bypassController,
LockscreenSmartspaceController smartspaceController,
KeyguardUnlockAnimationController keyguardUnlockAnimationController,
- SmartspaceTransitionController smartspaceTransitionController) {
+ SmartspaceTransitionController smartspaceTransitionController,
+ @Main Resources resources) {
super(keyguardClockSwitch);
mStatusBarStateController = statusBarStateController;
mColorExtractor = colorExtractor;
@@ -130,6 +134,7 @@
mKeyguardUpdateMonitor = keyguardUpdateMonitor;
mBypassController = bypassController;
mSmartspaceController = smartspaceController;
+ mResources = resources;
mKeyguardUnlockAnimationController = keyguardUnlockAnimationController;
mSmartspaceTransitionController = smartspaceTransitionController;
@@ -159,7 +164,8 @@
mBroadcastDispatcher,
mBatteryController,
mKeyguardUpdateMonitor,
- mBypassController);
+ mBypassController,
+ mResources);
mClockViewController.init();
mLargeClockViewController =
@@ -169,7 +175,8 @@
mBroadcastDispatcher,
mBatteryController,
mKeyguardUpdateMonitor,
- mBypassController);
+ mBypassController,
+ mResources);
mLargeClockViewController.init();
}
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardMessageArea.java b/packages/SystemUI/src/com/android/keyguard/KeyguardMessageArea.java
index 62411db..11eeac2 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardMessageArea.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardMessageArea.java
@@ -153,6 +153,10 @@
colorState = mNextMessageColorState;
mNextMessageColorState = ColorStateList.valueOf(DEFAULT_COLOR);
}
+ if (mAltBouncerShowing) {
+ // alt bouncer has a black scrim, so always show the text in white
+ colorState = ColorStateList.valueOf(Color.WHITE);
+ }
setTextColor(colorState);
}
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java
index d80a408..0328b5a 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java
@@ -21,6 +21,8 @@
import static java.lang.Integer.max;
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
import android.animation.ValueAnimator;
import android.app.Activity;
import android.app.AlertDialog;
@@ -35,7 +37,6 @@
import android.view.VelocityTracker;
import android.view.View;
import android.view.ViewConfiguration;
-import android.view.ViewPropertyAnimator;
import android.view.WindowInsets;
import android.view.WindowInsetsAnimation;
import android.view.WindowManager;
@@ -43,6 +44,7 @@
import android.view.animation.Interpolator;
import android.widget.FrameLayout;
+import androidx.annotation.Nullable;
import androidx.annotation.VisibleForTesting;
import androidx.dynamicanimation.animation.DynamicAnimation;
import androidx.dynamicanimation.animation.SpringAnimation;
@@ -111,7 +113,7 @@
private boolean mIsSecurityViewLeftAligned = true;
private boolean mOneHandedMode = false;
- private ViewPropertyAnimator mRunningOneHandedAnimator;
+ @Nullable private ValueAnimator mRunningOneHandedAnimator;
private final WindowInsetsAnimation.Callback mWindowInsetsAnimationCallback =
new WindowInsetsAnimation.Callback(DISPATCH_MODE_STOP) {
@@ -347,9 +349,9 @@
Interpolator fadeOutInterpolator = Interpolators.FAST_OUT_LINEAR_IN;
Interpolator fadeInInterpolator = Interpolators.LINEAR_OUT_SLOW_IN;
- ValueAnimator anim = ValueAnimator.ofFloat(0.0f, 1.0f);
- anim.setDuration(BOUNCER_HANDEDNESS_ANIMATION_DURATION_MS);
- anim.setInterpolator(Interpolators.LINEAR);
+ mRunningOneHandedAnimator = ValueAnimator.ofFloat(0.0f, 1.0f);
+ mRunningOneHandedAnimator.setDuration(BOUNCER_HANDEDNESS_ANIMATION_DURATION_MS);
+ mRunningOneHandedAnimator.setInterpolator(Interpolators.LINEAR);
int initialTranslation = (int) mSecurityViewFlipper.getTranslationX();
int totalTranslation = (int) getResources().getDimension(
@@ -361,7 +363,15 @@
mSecurityViewFlipper.setLayerType(View.LAYER_TYPE_HARDWARE, /* paint= */null);
}
- anim.addUpdateListener(animation -> {
+ float initialAlpha = mSecurityViewFlipper.getAlpha();
+
+ mRunningOneHandedAnimator.addListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ mRunningOneHandedAnimator = null;
+ }
+ });
+ mRunningOneHandedAnimator.addUpdateListener(animation -> {
float switchPoint = BOUNCER_HANDEDNESS_ANIMATION_FADE_OUT_PROPORTION;
boolean isFadingOut = animation.getAnimatedFraction() < switchPoint;
@@ -378,13 +388,16 @@
if (isFadingOut) {
// The bouncer fades out over the first X%.
float fadeOutFraction = MathUtils.constrainedMap(
- /* rangeMin= */0.0f,
- /* rangeMax= */1.0f,
+ /* rangeMin= */1.0f,
+ /* rangeMax= */0.0f,
/* valueMin= */0.0f,
/* valueMax= */switchPoint,
animation.getAnimatedFraction());
float opacity = fadeOutInterpolator.getInterpolation(fadeOutFraction);
- mSecurityViewFlipper.setAlpha(1f - opacity);
+
+ // When fading out, the alpha needs to start from the initial opacity of the
+ // view flipper, otherwise we get a weird bit of jank as it ramps back to 100%.
+ mSecurityViewFlipper.setAlpha(opacity * initialAlpha);
// Animate away from the source.
mSecurityViewFlipper.setTranslationX(initialTranslation + currentTranslation);
@@ -409,7 +422,7 @@
}
});
- anim.start();
+ mRunningOneHandedAnimator.start();
} else {
mSecurityViewFlipper.setTranslationX(targetTranslation);
}
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardViewController.java
index ecc8c00..db729da 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardViewController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardViewController.java
@@ -18,7 +18,6 @@
import android.os.Bundle;
import android.view.View;
-import android.view.ViewGroup;
import android.view.ViewRootImpl;
import com.android.systemui.keyguard.KeyguardViewMediator;
@@ -186,14 +185,12 @@
/**
* Registers the StatusBar to which this Keyguard View is mounted.
* @param statusBar
- * @param container
* @param notificationPanelViewController
* @param biometricUnlockController
* @param notificationContainer
* @param bypassController
*/
void registerStatusBar(StatusBar statusBar,
- ViewGroup container,
NotificationPanelViewController notificationPanelViewController,
BiometricUnlockController biometricUnlockController,
View notificationContainer,
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricView.java b/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricView.java
index 60b0637..da69f45 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricView.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricView.java
@@ -824,11 +824,29 @@
return new AuthDialog.LayoutParams(width, totalHeight);
}
+ /**
+ * Simple heuristic which should return true displays that are larger than a normal phone.
+ * For example, tablet displays, or the unfolded display for foldables.
+ */
+ private boolean isLargeDisplay(int width, int height) {
+ return width > 1200 && height > 1200;
+ }
+
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
final int width = MeasureSpec.getSize(widthMeasureSpec);
final int height = MeasureSpec.getSize(heightMeasureSpec);
- final int newWidth = Math.min(width, height);
+
+ Log.d(TAG, "Width: " + width + ", height: " + height);
+
+ final int newWidth;
+ if (isLargeDisplay(width, height)) {
+ // TODO: Unless we can come up with a one-size-fits-all equation, we may want to
+ // consider moving this to an overlay.
+ newWidth = 2 * Math.min(width, height) / 3;
+ } else {
+ newWidth = Math.min(width, height);
+ }
// Use "newWidth" instead, so the landscape dialog width is the same as the portrait
// width.
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthPanelController.java b/packages/SystemUI/src/com/android/systemui/biometrics/AuthPanelController.java
index fa50f89..f1e42e0 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthPanelController.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthPanelController.java
@@ -117,24 +117,6 @@
mUseFullScreen = fullScreen;
}
- public ValueAnimator getTranslationAnimator(float relativeTranslationY) {
- final ValueAnimator animator = ValueAnimator.ofFloat(
- mPanelView.getY(), mPanelView.getY() - relativeTranslationY);
- animator.addUpdateListener(animation -> {
- final float translation = (float) animation.getAnimatedValue();
- mPanelView.setTranslationY(translation);
- });
- return animator;
- }
-
- public ValueAnimator getAlphaAnimator(float alpha) {
- final ValueAnimator animator = ValueAnimator.ofFloat(mPanelView.getAlpha(), alpha);
- animator.addUpdateListener(animation -> {
- mPanelView.setAlpha((float) animation.getAnimatedValue());
- });
- return animator;
- }
-
public void updateForContentDimensions(int contentWidth, int contentHeight,
int animateDurationMs) {
if (DEBUG) {
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthRippleController.kt b/packages/SystemUI/src/com/android/systemui/biometrics/AuthRippleController.kt
index eb6b193..0932a8c 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthRippleController.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthRippleController.kt
@@ -18,7 +18,6 @@
import android.animation.ValueAnimator
import android.content.Context
-import android.content.res.Configuration
import android.graphics.PointF
import android.hardware.biometrics.BiometricSourceType
import android.util.DisplayMetrics
@@ -125,6 +124,7 @@
return
}
+ updateSensorLocation()
if (biometricSourceType == BiometricSourceType.FINGERPRINT &&
fingerprintSensorLocation != null) {
mView.setSensorLocation(fingerprintSensorLocation!!)
@@ -266,9 +266,6 @@
private val configurationChangedListener =
object : ConfigurationController.ConfigurationListener {
- override fun onConfigChanged(newConfig: Configuration?) {
- updateSensorLocation()
- }
override fun onUiModeChanged() {
updateRippleColor()
}
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/FalsingCollectorImpl.java b/packages/SystemUI/src/com/android/systemui/classifier/FalsingCollectorImpl.java
index 14e5991..be326da 100644
--- a/packages/SystemUI/src/com/android/systemui/classifier/FalsingCollectorImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/classifier/FalsingCollectorImpl.java
@@ -37,6 +37,7 @@
import com.android.systemui.util.concurrency.DelayableExecutor;
import com.android.systemui.util.sensors.ProximitySensor;
import com.android.systemui.util.sensors.ThresholdSensor;
+import com.android.systemui.util.sensors.ThresholdSensorEvent;
import com.android.systemui.util.time.SystemClock;
import java.util.Collections;
@@ -405,7 +406,7 @@
mProximitySensor.unregister(mSensorEventListener);
}
- private void onProximityEvent(ThresholdSensor.ThresholdSensorEvent proximityEvent) {
+ private void onProximityEvent(ThresholdSensorEvent proximityEvent) {
// TODO: some of these classifiers might allow us to abort early, meaning we don't have to
// make these calls.
mFalsingManager.onProximityEvent(new ProximityEventImpl(proximityEvent));
@@ -423,9 +424,9 @@
}
private static class ProximityEventImpl implements FalsingManager.ProximityEvent {
- private ThresholdSensor.ThresholdSensorEvent mThresholdSensorEvent;
+ private ThresholdSensorEvent mThresholdSensorEvent;
- ProximityEventImpl(ThresholdSensor.ThresholdSensorEvent thresholdSensorEvent) {
+ ProximityEventImpl(ThresholdSensorEvent thresholdSensorEvent) {
mThresholdSensorEvent = thresholdSensorEvent;
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/DependencyProvider.java b/packages/SystemUI/src/com/android/systemui/dagger/DependencyProvider.java
index 2d873f2..fa23842 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/DependencyProvider.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/DependencyProvider.java
@@ -24,8 +24,6 @@
import android.content.Context;
import android.content.SharedPreferences;
import android.content.om.OverlayManager;
-import android.hardware.SensorManager;
-import android.hardware.devicestate.DeviceStateManager;
import android.hardware.display.AmbientDisplayConfiguration;
import android.hardware.display.ColorDisplayManager;
import android.os.Handler;
@@ -56,22 +54,18 @@
import com.android.systemui.doze.AlwaysOnDisplayPolicy;
import com.android.systemui.dump.DumpManager;
import com.android.systemui.keyguard.KeyguardViewMediator;
-import com.android.systemui.keyguard.LifecycleScreenStatusProvider;
import com.android.systemui.qs.ReduceBrightColorsController;
import com.android.systemui.settings.UserTracker;
import com.android.systemui.shared.system.ActivityManagerWrapper;
import com.android.systemui.shared.system.DevicePolicyManagerWrapper;
import com.android.systemui.shared.system.TaskStackChangeListeners;
import com.android.systemui.shared.system.WindowManagerWrapper;
+import com.android.systemui.statusbar.connectivity.NetworkController;
import com.android.systemui.statusbar.phone.AutoHideController;
import com.android.systemui.statusbar.phone.ConfigurationControllerImpl;
import com.android.systemui.statusbar.policy.ConfigurationController;
import com.android.systemui.statusbar.policy.DataSaverController;
-import com.android.systemui.statusbar.policy.NetworkController;
import com.android.systemui.theme.ThemeOverlayApplier;
-import com.android.systemui.unfold.UnfoldTransitionFactory;
-import com.android.systemui.unfold.UnfoldTransitionProgressProvider;
-import com.android.systemui.unfold.config.UnfoldTransitionConfig;
import com.android.systemui.util.leak.LeakDetector;
import com.android.systemui.util.settings.SecureSettings;
@@ -287,37 +281,6 @@
/** */
@Provides
@SysUISingleton
- public UnfoldTransitionProgressProvider provideUnfoldTransitionProgressProvider(
- Context context,
- UnfoldTransitionConfig config,
- LifecycleScreenStatusProvider screenStatusProvider,
- DeviceStateManager deviceStateManager,
- SensorManager sensorManager,
- @Main Executor executor,
- @Main Handler handler
- ) {
- return UnfoldTransitionFactory
- .createUnfoldTransitionProgressProvider(
- context,
- config,
- screenStatusProvider,
- deviceStateManager,
- sensorManager,
- handler,
- executor
- );
- }
-
- /** */
- @Provides
- @SysUISingleton
- public UnfoldTransitionConfig provideUnfoldTransitionConfig(Context context) {
- return UnfoldTransitionFactory.createConfig(context);
- }
-
- /** */
- @Provides
- @SysUISingleton
public Choreographer providesChoreographer() {
return Choreographer.getInstance();
}
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/GlobalModule.java b/packages/SystemUI/src/com/android/systemui/dagger/GlobalModule.java
index 648f345..18f8519 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/GlobalModule.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/GlobalModule.java
@@ -24,6 +24,7 @@
import com.android.internal.logging.UiEventLoggerImpl;
import com.android.systemui.dagger.qualifiers.TestHarness;
import com.android.systemui.plugins.PluginsModule;
+import com.android.systemui.unfold.UnfoldTransitionModule;
import com.android.systemui.util.concurrency.GlobalConcurrencyModule;
import javax.inject.Singleton;
@@ -49,6 +50,7 @@
@Module(includes = {
FrameworkServicesModule.class,
GlobalConcurrencyModule.class,
+ UnfoldTransitionModule.class,
PluginsModule.class,
})
public class GlobalModule {
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeSensors.java b/packages/SystemUI/src/com/android/systemui/doze/DozeSensors.java
index 55e6154..3cefce8 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeSensors.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeSensors.java
@@ -47,6 +47,7 @@
import com.android.systemui.biometrics.AuthController;
import com.android.systemui.plugins.SensorManagerPlugin;
import com.android.systemui.statusbar.phone.DozeParameters;
+import com.android.systemui.statusbar.policy.DevicePostureController;
import com.android.systemui.util.sensors.AsyncSensorManager;
import com.android.systemui.util.sensors.ProximitySensor;
import com.android.systemui.util.settings.SecureSettings;
@@ -82,6 +83,9 @@
private boolean mListeningTouchScreenSensors;
private boolean mListeningProxSensors;
+ @DevicePostureController.DevicePostureInt
+ private int mDevicePosture;
+
// whether to only register sensors that use prox when the display state is dozing or off
private boolean mSelectivelyRegisterProxSensors;
@@ -106,7 +110,8 @@
DozeParameters dozeParameters, AmbientDisplayConfiguration config, WakeLock wakeLock,
Callback callback, Consumer<Boolean> proxCallback, DozeLog dozeLog,
ProximitySensor proximitySensor, SecureSettings secureSettings,
- AuthController authController) {
+ AuthController authController,
+ int devicePosture) {
mContext = context;
mSensorManager = sensorManager;
mConfig = config;
@@ -120,6 +125,7 @@
mListeningProxSensors = !mSelectivelyRegisterProxSensors;
mScreenOffUdfpsEnabled =
config.screenOffUdfpsEnabled(KeyguardUpdateMonitor.getCurrentUser());
+ mDevicePosture = devicePosture;
boolean udfpsEnrolled =
authController.isUdfpsEnrolled(KeyguardUpdateMonitor.getCurrentUser());
@@ -142,7 +148,7 @@
false /* requires prox */,
dozeLog),
new TriggerSensor(
- findSensorWithType(config.doubleTapSensorType()),
+ findSensor(config.doubleTapSensorType()),
Settings.Secure.DOZE_DOUBLE_TAP_GESTURE,
true /* configured */,
DozeLog.REASON_SENSOR_DOUBLE_TAP,
@@ -150,7 +156,7 @@
true /* touchscreen */,
dozeLog),
new TriggerSensor(
- findSensorWithType(config.tapSensorType()),
+ findSensor(config.tapSensorType(mDevicePosture)),
Settings.Secure.DOZE_TAP_SCREEN_GESTURE,
true /* settingDef */,
true /* configured */,
@@ -158,10 +164,10 @@
false /* reports touch coordinates */,
true /* touchscreen */,
false /* ignoresSetting */,
- dozeParameters.singleTapUsesProx() /* requiresProx */,
+ dozeParameters.singleTapUsesProx(mDevicePosture) /* requiresProx */,
dozeLog),
new TriggerSensor(
- findSensorWithType(config.longPressSensorType()),
+ findSensor(config.longPressSensorType()),
Settings.Secure.DOZE_PULSE_ON_LONG_PRESS,
false /* settingDef */,
true /* configured */,
@@ -172,7 +178,7 @@
dozeParameters.longPressUsesProx() /* requiresProx */,
dozeLog),
new TriggerSensor(
- findSensorWithType(config.udfpsLongPressSensorType()),
+ findSensor(config.udfpsLongPressSensorType()),
"doze_pulse_on_auth",
true /* settingDef */,
udfpsEnrolled && (alwaysOn || mScreenOffUdfpsEnabled),
@@ -200,7 +206,7 @@
mConfig.getWakeLockScreenDebounce(),
dozeLog),
new TriggerSensor(
- findSensorWithType(config.quickPickupSensorType()),
+ findSensor(config.quickPickupSensorType()),
Settings.Secure.DOZE_QUICK_PICKUP_GESTURE,
true /* setting default */,
config.quickPickupSensorEnabled(KeyguardUpdateMonitor.getCurrentUser())
@@ -238,21 +244,29 @@
mDebounceFrom = SystemClock.uptimeMillis();
}
- private Sensor findSensorWithType(String type) {
- return findSensorWithType(mSensorManager, type);
+ private Sensor findSensor(String type) {
+ return findSensor(mSensorManager, type, null);
}
/**
- * Utility method to find a {@link Sensor} for the supplied string type.
+ * Utility method to find a {@link Sensor} for the supplied string type and string name.
+ *
+ * Return the first sensor in the list that matches the specified inputs. Ignores type or name
+ * if the input is null or empty.
+ *
+ * @param type sensorType
+ * @parm name sensorName, to differentiate between sensors with the same type
*/
- public static Sensor findSensorWithType(SensorManager sensorManager, String type) {
- if (TextUtils.isEmpty(type)) {
- return null;
- }
- List<Sensor> sensorList = sensorManager.getSensorList(Sensor.TYPE_ALL);
- for (Sensor s : sensorList) {
- if (type.equals(s.getStringType())) {
- return s;
+ public static Sensor findSensor(SensorManager sensorManager, String type, String name) {
+ final boolean isNameSpecified = !TextUtils.isEmpty(name);
+ final boolean isTypeSpecified = !TextUtils.isEmpty(type);
+ if (isNameSpecified || isTypeSpecified) {
+ final List<Sensor> sensors = sensorManager.getSensorList(Sensor.TYPE_ALL);
+ for (Sensor sensor : sensors) {
+ if ((!isNameSpecified || name.equals(sensor.getName()))
+ && (!isTypeSpecified || type.equals(sensor.getStringType()))) {
+ return sensor;
+ }
}
}
return null;
@@ -370,6 +384,8 @@
/** Dump current state */
public void dump(PrintWriter pw) {
pw.println("mListening=" + mListening);
+ pw.println("mDevicePosture="
+ + DevicePostureController.devicePostureToString(mDevicePosture));
pw.println("mListeningTouchScreenSensors=" + mListeningTouchScreenSensors);
pw.println("mSelectivelyRegisterProxSensors=" + mSelectivelyRegisterProxSensors);
pw.println("mListeningProxSensors=" + mListeningProxSensors);
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java b/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java
index 756adca..b17f078 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java
@@ -41,10 +41,12 @@
import com.android.systemui.doze.DozeMachine.State;
import com.android.systemui.doze.dagger.DozeScope;
import com.android.systemui.statusbar.phone.DozeParameters;
+import com.android.systemui.statusbar.policy.DevicePostureController;
import com.android.systemui.statusbar.policy.KeyguardStateController;
import com.android.systemui.util.Assert;
import com.android.systemui.util.concurrency.DelayableExecutor;
import com.android.systemui.util.sensors.AsyncSensorManager;
+import com.android.systemui.util.sensors.ProximityCheck;
import com.android.systemui.util.sensors.ProximitySensor;
import com.android.systemui.util.settings.SecureSettings;
import com.android.systemui.util.wakelock.WakeLock;
@@ -89,12 +91,15 @@
private final TriggerReceiver mBroadcastReceiver = new TriggerReceiver();
private final DockEventListener mDockEventListener = new DockEventListener();
private final DockManager mDockManager;
- private final ProximitySensor.ProximityCheck mProxCheck;
+ private final ProximityCheck mProxCheck;
private final BroadcastDispatcher mBroadcastDispatcher;
private final AuthController mAuthController;
private final DelayableExecutor mMainExecutor;
private final KeyguardStateController mKeyguardStateController;
private final UiEventLogger mUiEventLogger;
+ private final DevicePostureController mDevicePostureController;
+
+ private @DevicePostureController.DevicePostureInt int mDevicePosture;
private long mNotificationPulseTime;
private boolean mPulsePending;
@@ -177,12 +182,14 @@
AmbientDisplayConfiguration config,
DozeParameters dozeParameters, AsyncSensorManager sensorManager,
WakeLock wakeLock, DockManager dockManager,
- ProximitySensor proximitySensor, ProximitySensor.ProximityCheck proxCheck,
+ ProximitySensor proximitySensor,
+ ProximityCheck proxCheck,
DozeLog dozeLog, BroadcastDispatcher broadcastDispatcher,
SecureSettings secureSettings, AuthController authController,
@Main DelayableExecutor mainExecutor,
UiEventLogger uiEventLogger,
- KeyguardStateController keyguardStateController) {
+ KeyguardStateController keyguardStateController,
+ DevicePostureController devicePostureController) {
mContext = context;
mDozeHost = dozeHost;
mConfig = config;
@@ -190,9 +197,11 @@
mSensorManager = sensorManager;
mWakeLock = wakeLock;
mAllowPulseTriggers = true;
+ mDevicePostureController = devicePostureController;
+ mDevicePosture = devicePostureController.getDevicePosture();
mDozeSensors = new DozeSensors(context, mSensorManager, dozeParameters,
config, wakeLock, this::onSensor, this::onProximityFar, dozeLog, proximitySensor,
- secureSettings, authController);
+ secureSettings, authController, mDevicePosture);
mUiModeManager = mContext.getSystemService(UiModeManager.class);
mDockManager = dockManager;
mProxCheck = proxCheck;
diff --git a/packages/SystemUI/src/com/android/systemui/doze/dagger/DozeModule.java b/packages/SystemUI/src/com/android/systemui/doze/dagger/DozeModule.java
index 9c6e02a..571b666 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/dagger/DozeModule.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/dagger/DozeModule.java
@@ -38,6 +38,7 @@
import com.android.systemui.doze.DozeUi;
import com.android.systemui.doze.DozeWallpaperState;
import com.android.systemui.statusbar.phone.DozeParameters;
+import com.android.systemui.statusbar.policy.DevicePostureController;
import com.android.systemui.util.sensors.AsyncSensorManager;
import com.android.systemui.util.wakelock.DelayedWakeLock;
import com.android.systemui.util.wakelock.WakeLock;
@@ -94,8 +95,15 @@
@Provides
@BrightnessSensor
static Optional<Sensor> providesBrightnessSensor(
- AsyncSensorManager sensorManager, Context context) {
- return Optional.ofNullable(DozeSensors.findSensorWithType(sensorManager,
- context.getString(R.string.doze_brightness_sensor_type)));
+ AsyncSensorManager sensorManager,
+ Context context,
+ DozeParameters dozeParameters,
+ DevicePostureController devicePostureController) {
+ return Optional.ofNullable(
+ DozeSensors.findSensor(
+ sensorManager,
+ context.getString(R.string.doze_brightness_sensor_type),
+ dozeParameters.brightnessName(devicePostureController.getDevicePosture())
+ ));
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/dump/DumpManager.kt b/packages/SystemUI/src/com/android/systemui/dump/DumpManager.kt
index b0f3959..21a1b75 100644
--- a/packages/SystemUI/src/com/android/systemui/dump/DumpManager.kt
+++ b/packages/SystemUI/src/com/android/systemui/dump/DumpManager.kt
@@ -18,11 +18,11 @@
import android.util.ArrayMap
import com.android.systemui.Dumpable
-import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.log.LogBuffer
import java.io.FileDescriptor
import java.io.PrintWriter
import javax.inject.Inject
+import javax.inject.Singleton
/**
* Maintains a registry of things that should be dumped when a bug report is taken
@@ -33,7 +33,7 @@
*
* See [DumpHandler] for more information on how and when this information is dumped.
*/
-@SysUISingleton
+@Singleton
open class DumpManager @Inject constructor() {
private val dumpables: MutableMap<String, RegisteredDumpable<Dumpable>> = ArrayMap()
private val buffers: MutableMap<String, RegisteredDumpable<LogBuffer>> = ArrayMap()
diff --git a/packages/SystemUI/src/com/android/systemui/flags/FeatureFlags.java b/packages/SystemUI/src/com/android/systemui/flags/FeatureFlags.java
index e51f90f..f81811a 100644
--- a/packages/SystemUI/src/com/android/systemui/flags/FeatureFlags.java
+++ b/packages/SystemUI/src/com/android/systemui/flags/FeatureFlags.java
@@ -161,6 +161,11 @@
return mFlagReader.isEnabled(R.bool.flag_ongoing_call_status_bar_chip);
}
+ public boolean isOngoingCallInImmersiveEnabled() {
+ return isOngoingCallStatusBarChipEnabled()
+ && mFlagReader.isEnabled(R.bool.flag_ongoing_call_in_immersive);
+ }
+
public boolean isSmartspaceEnabled() {
return mFlagReader.isEnabled(R.bool.flag_smartspace);
}
@@ -187,6 +192,13 @@
return FeatureFlagUtils.isEnabled(mContext, FeatureFlagUtils.SETTINGS_PROVIDER_MODEL);
}
+ /**
+ * Use the new version of the user switcher
+ */
+ public boolean useNewUserSwitcher() {
+ return mFlagReader.isEnabled(R.bool.flag_new_user_switcher);
+ }
+
/** static method for the system setting */
public static boolean isProviderModelSettingEnabled(Context context) {
return FeatureFlagUtils.isEnabled(context, FeatureFlagUtils.SETTINGS_PROVIDER_MODEL);
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
index 1561eb6..c569439 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
@@ -77,7 +77,6 @@
import android.view.RemoteAnimationTarget;
import android.view.SyncRtSurfaceTransactionApplier;
import android.view.View;
-import android.view.ViewGroup;
import android.view.WindowManager;
import android.view.WindowManagerPolicyConstants;
import android.view.animation.Animation;
@@ -2623,7 +2622,6 @@
* Registers the StatusBar to which the Keyguard View is mounted.
*
* @param statusBar
- * @param container
* @param panelView
* @param biometricUnlockController
* @param notificationContainer
@@ -2631,10 +2629,10 @@
* @return the View Controller for the Keyguard View this class is mediating.
*/
public KeyguardViewController registerStatusBar(StatusBar statusBar,
- ViewGroup container, NotificationPanelViewController panelView,
+ NotificationPanelViewController panelView,
BiometricUnlockController biometricUnlockController,
View notificationContainer, KeyguardBypassController bypassController) {
- mKeyguardViewControllerLazy.get().registerStatusBar(statusBar, container, panelView,
+ mKeyguardViewControllerLazy.get().registerStatusBar(statusBar, panelView,
biometricUnlockController, notificationContainer, bypassController);
return mKeyguardViewControllerLazy.get();
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/LifecycleScreenStatusProvider.kt b/packages/SystemUI/src/com/android/systemui/keyguard/LifecycleScreenStatusProvider.kt
index f25ec55..044a57c 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/LifecycleScreenStatusProvider.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/LifecycleScreenStatusProvider.kt
@@ -15,12 +15,12 @@
*/
package com.android.systemui.keyguard
-import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.unfold.updates.screen.ScreenStatusProvider
import com.android.systemui.unfold.updates.screen.ScreenStatusProvider.ScreenListener
import javax.inject.Inject
+import javax.inject.Singleton
-@SysUISingleton
+@Singleton
class LifecycleScreenStatusProvider @Inject constructor(screenLifecycle: ScreenLifecycle) :
ScreenStatusProvider, ScreenLifecycle.Observer {
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ScreenLifecycle.java b/packages/SystemUI/src/com/android/systemui/keyguard/ScreenLifecycle.java
index 30983aa..d17c39a 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ScreenLifecycle.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ScreenLifecycle.java
@@ -19,18 +19,18 @@
import android.os.Trace;
import com.android.systemui.Dumpable;
-import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.dump.DumpManager;
import java.io.FileDescriptor;
import java.io.PrintWriter;
import javax.inject.Inject;
+import javax.inject.Singleton;
/**
* Tracks the screen lifecycle.
*/
-@SysUISingleton
+@Singleton
public class ScreenLifecycle extends Lifecycle<ScreenLifecycle.Observer> implements Dumpable {
public static final int SCREEN_OFF = 0;
diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaCarouselController.kt b/packages/SystemUI/src/com/android/systemui/media/MediaCarouselController.kt
index c743fe1..0e70945 100644
--- a/packages/SystemUI/src/com/android/systemui/media/MediaCarouselController.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/MediaCarouselController.kt
@@ -833,11 +833,12 @@
)
private val comparator =
- compareByDescending<MediaSortKey> { it.data.isPlaying }
+ compareByDescending<MediaSortKey> { it.data.isPlaying == true && it.data.isLocalSession }
+ .thenByDescending { it.data.isPlaying }
.thenByDescending { if (shouldPrioritizeSs) it.isSsMediaRec else !it.isSsMediaRec }
- .thenByDescending { it.data.isLocalSession }
.thenByDescending { !it.data.resumption }
.thenByDescending { it.updateTime }
+ .thenByDescending { !it.data.isLocalSession }
private val mediaPlayers = TreeMap<MediaSortKey, MediaControlPanel>(comparator)
private val mediaData: MutableMap<String, MediaSortKey> = mutableMapOf()
diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaDataManager.kt b/packages/SystemUI/src/com/android/systemui/media/MediaDataManager.kt
index 06a1eea..3631d2f 100644
--- a/packages/SystemUI/src/com/android/systemui/media/MediaDataManager.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/MediaDataManager.kt
@@ -768,7 +768,7 @@
val resumeAction = getResumeMediaAction(removed.resumeAction!!)
val updated = removed.copy(token = null, actions = listOf(resumeAction),
actionsToShowInCompact = listOf(0), active = false, resumption = true,
- isClearable = true)
+ isPlaying = false, isClearable = true)
val pkg = removed.packageName
val migrate = mediaEntries.put(pkg, updated) == null
// Notify listeners of "new" controls when migrating or removed and update when not
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarController.java b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarController.java
index a1a630a..7622d66 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarController.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarController.java
@@ -195,7 +195,8 @@
mNavigationModeController.addListener(this);
mTaskbarDelegate = taskbarDelegate;
mTaskbarDelegate.setOverviewProxyService(commandQueue, overviewProxyService,
- navigationBarA11yHelper, navigationModeController, sysUiFlagsContainer);
+ navigationBarA11yHelper, navigationModeController, sysUiFlagsContainer,
+ dumpManager);
mIsTablet = isTablet(mContext);
mUserTracker = userTracker;
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/TaskbarDelegate.java b/packages/SystemUI/src/com/android/systemui/navigationbar/TaskbarDelegate.java
index 71d2a73..4d29612 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/TaskbarDelegate.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/TaskbarDelegate.java
@@ -20,6 +20,7 @@
import static android.app.StatusBarManager.NAVIGATION_HINT_IME_SHOWN;
import static android.app.StatusBarManager.WINDOW_STATE_SHOWING;
import static android.view.WindowInsetsController.BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE;
+import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION;
import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_A11Y_BUTTON_CLICKABLE;
import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_A11Y_BUTTON_LONG_CLICKABLE;
@@ -34,15 +35,23 @@
import android.app.StatusBarManager;
import android.app.StatusBarManager.WindowVisibleState;
+import android.content.ComponentCallbacks;
import android.content.Context;
+import android.content.res.Configuration;
+import android.hardware.display.DisplayManager;
import android.inputmethodservice.InputMethodService;
import android.os.IBinder;
+import android.view.Display;
import android.view.InsetsVisibilities;
import android.view.View;
import android.view.WindowInsetsController.Behavior;
+import androidx.annotation.NonNull;
+
import com.android.internal.view.AppearanceRegion;
import com.android.systemui.Dependency;
+import com.android.systemui.Dumpable;
+import com.android.systemui.dump.DumpManager;
import com.android.systemui.model.SysUiState;
import com.android.systemui.navigationbar.gestural.EdgeBackGestureHandler;
import com.android.systemui.recents.OverviewProxyService;
@@ -50,12 +59,16 @@
import com.android.systemui.shared.system.ActivityManagerWrapper;
import com.android.systemui.statusbar.CommandQueue;
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+
import javax.inject.Inject;
import javax.inject.Singleton;
@Singleton
public class TaskbarDelegate implements CommandQueue.Callbacks,
- OverviewProxyService.OverviewProxyListener, NavigationModeController.ModeChangedListener {
+ OverviewProxyService.OverviewProxyListener, NavigationModeController.ModeChangedListener,
+ ComponentCallbacks, Dumpable {
private final EdgeBackGestureHandler mEdgeBackGestureHandler;
@@ -71,24 +84,30 @@
private int mDisabledFlags;
private @WindowVisibleState int mTaskBarWindowState = WINDOW_STATE_SHOWING;
private @Behavior int mBehavior;
+ private final Context mContext;
+ private final DisplayManager mDisplayManager;
+ private Context mWindowContext;
@Inject
public TaskbarDelegate(Context context) {
mEdgeBackGestureHandler = Dependency.get(EdgeBackGestureHandler.Factory.class)
.create(context);
+ mContext = context;
+ mDisplayManager = mContext.getSystemService(DisplayManager.class);
}
public void setOverviewProxyService(CommandQueue commandQueue,
OverviewProxyService overviewProxyService,
NavigationBarA11yHelper navigationBarA11yHelper,
NavigationModeController navigationModeController,
- SysUiState sysUiState) {
+ SysUiState sysUiState, DumpManager dumpManager) {
// TODO: adding this in the ctor results in a dagger dependency cycle :(
mCommandQueue = commandQueue;
mOverviewProxyService = overviewProxyService;
mNavigationBarA11yHelper = navigationBarA11yHelper;
mNavigationModeController = navigationModeController;
mSysUiState = sysUiState;
+ dumpManager.registerDumpable(this);
}
public void destroy() {
@@ -97,6 +116,10 @@
mNavigationModeController.removeListener(this);
mNavigationBarA11yHelper.removeA11yEventListener(mNavA11yEventListener);
mEdgeBackGestureHandler.onNavBarDetached();
+ if (mWindowContext != null) {
+ mWindowContext.unregisterComponentCallbacks(this);
+ mWindowContext = null;
+ }
}
public void init(int displayId) {
@@ -107,6 +130,10 @@
mNavigationModeController.addListener(this));
mNavigationBarA11yHelper.registerA11yEventListener(mNavA11yEventListener);
mEdgeBackGestureHandler.onNavBarAttached();
+ // Initialize component callback
+ Display display = mDisplayManager.getDisplay(displayId);
+ mWindowContext = mContext.createWindowContext(display, TYPE_APPLICATION, null);
+ mWindowContext.registerComponentCallbacks(this);
// Set initial state for any listeners
updateSysuiFlags();
}
@@ -193,4 +220,22 @@
private boolean allowSystemGestureIgnoringBarVisibility() {
return mBehavior != BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE;
}
+
+ @Override
+ public void onConfigurationChanged(Configuration configuration) {
+ mEdgeBackGestureHandler.onConfigurationChanged(configuration);
+ }
+
+ @Override
+ public void onLowMemory() {}
+
+ @Override
+ public void dump(@NonNull FileDescriptor fd, @NonNull PrintWriter pw, @NonNull String[] args) {
+ pw.println("TaskbarDelegate (displayId=" + mDisplayId + "):");
+ pw.println(" mNavigationIconHints=" + mNavigationIconHints);
+ pw.println(" mDisabledFlags=" + mDisabledFlags);
+ pw.println(" mTaskBarWindowState=" + mTaskBarWindowState);
+ pw.println(" mBehavior=" + mBehavior);
+ mEdgeBackGestureHandler.dump(pw);
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgeBackGestureHandler.java b/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgeBackGestureHandler.java
index bf1a98f..c6da342 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgeBackGestureHandler.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgeBackGestureHandler.java
@@ -101,8 +101,8 @@
private static final int MAX_NUM_LOGGED_PREDICTIONS = 10;
private static final int MAX_NUM_LOGGED_GESTURES = 10;
- // Temporary log until b/176302696 is resolved
- static final boolean DEBUG_MISSING_GESTURE = false;
+ // Temporary log until b/201642126 is resolved
+ static final boolean DEBUG_MISSING_GESTURE = true;
static final String DEBUG_MISSING_GESTURE_TAG = "NoBackGesture";
private static final boolean ENABLE_PER_WINDOW_INPUT_ROTATION =
diff --git a/packages/SystemUI/src/com/android/systemui/qs/PseudoGridView.java b/packages/SystemUI/src/com/android/systemui/qs/PseudoGridView.java
index 87c64c7..2f189be 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/PseudoGridView.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/PseudoGridView.java
@@ -38,6 +38,7 @@
private int mNumColumns = 3;
private int mVerticalSpacing;
private int mHorizontalSpacing;
+ private int mFixedChildWidth = -1;
public PseudoGridView(Context context, AttributeSet attrs) {
super(context, attrs);
@@ -53,6 +54,8 @@
mVerticalSpacing = a.getDimensionPixelSize(attr, 0);
} else if (attr == R.styleable.PseudoGridView_horizontalSpacing) {
mHorizontalSpacing = a.getDimensionPixelSize(attr, 0);
+ } else if (attr == R.styleable.PseudoGridView_fixedChildWidth) {
+ mFixedChildWidth = a.getDimensionPixelSize(attr, -1);
}
}
@@ -65,8 +68,15 @@
throw new UnsupportedOperationException("Needs a maximum width");
}
int width = MeasureSpec.getSize(widthMeasureSpec);
-
- int childWidth = (width - (mNumColumns - 1) * mHorizontalSpacing) / mNumColumns;
+ int childWidth;
+ int necessarySpaceForChildWidth =
+ mFixedChildWidth * mNumColumns + mHorizontalSpacing * (mNumColumns - 1);
+ if (mFixedChildWidth != -1 && necessarySpaceForChildWidth <= width) {
+ childWidth = mFixedChildWidth;
+ width = mFixedChildWidth * mNumColumns + mHorizontalSpacing * (mNumColumns - 1);
+ } else {
+ childWidth = (width - (mNumColumns - 1) * mHorizontalSpacing) / mNumColumns;
+ }
int childWidthSpec = MeasureSpec.makeMeasureSpec(childWidth, MeasureSpec.EXACTLY);
int childHeightSpec = MeasureSpec.UNSPECIFIED;
int totalHeight = 0;
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSContainerImpl.java b/packages/SystemUI/src/com/android/systemui/qs/QSContainerImpl.java
index 8e43661..3fc4f50 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSContainerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSContainerImpl.java
@@ -26,7 +26,6 @@
import android.graphics.PointF;
import android.util.AttributeSet;
import android.view.View;
-import android.view.WindowInsets;
import android.widget.FrameLayout;
import android.widget.ImageView;
@@ -58,7 +57,6 @@
private int mSideMargins;
private boolean mQsDisabled;
private int mContentPadding = -1;
- private int mNavBarInset = 0;
private boolean mClippingEnabled;
public QSContainerImpl(Context context, AttributeSet attrs) {
@@ -95,24 +93,13 @@
}
@Override
- public WindowInsets onApplyWindowInsets(WindowInsets insets) {
- mNavBarInset = insets.getInsets(WindowInsets.Type.navigationBars()).bottom;
- mQSPanelContainer.setPaddingRelative(
- mQSPanelContainer.getPaddingStart(),
- mQSPanelContainer.getPaddingTop(),
- mQSPanelContainer.getPaddingEnd(),
- mNavBarInset
- );
- return super.onApplyWindowInsets(insets);
- }
-
- @Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
// QSPanel will show as many rows as it can (up to TileLayout.MAX_ROWS) such that the
// bottom and footer are inside the screen.
MarginLayoutParams layoutParams = (MarginLayoutParams) mQSPanelContainer.getLayoutParams();
- int maxQs = getDisplayHeight() - layoutParams.topMargin - layoutParams.bottomMargin
+ int availableHeight = View.MeasureSpec.getSize(heightMeasureSpec);
+ int maxQs = availableHeight - layoutParams.topMargin - layoutParams.bottomMargin
- getPaddingBottom();
int padding = mPaddingLeft + mPaddingRight + layoutParams.leftMargin
+ layoutParams.rightMargin;
@@ -122,11 +109,11 @@
MeasureSpec.makeMeasureSpec(maxQs, MeasureSpec.AT_MOST));
int width = mQSPanelContainer.getMeasuredWidth() + padding;
super.onMeasure(MeasureSpec.makeMeasureSpec(width, MeasureSpec.EXACTLY),
- MeasureSpec.makeMeasureSpec(getDisplayHeight(), MeasureSpec.EXACTLY));
+ MeasureSpec.makeMeasureSpec(availableHeight, MeasureSpec.EXACTLY));
// QSCustomizer will always be the height of the screen, but do this after
// other measuring to avoid changing the height of the QS.
mQSCustomizer.measure(widthMeasureSpec,
- MeasureSpec.makeMeasureSpec(getDisplayHeight(), MeasureSpec.EXACTLY));
+ MeasureSpec.makeMeasureSpec(availableHeight, MeasureSpec.EXACTLY));
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSDetail.java b/packages/SystemUI/src/com/android/systemui/qs/QSDetail.java
index 929927e..58a942a 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSDetail.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSDetail.java
@@ -46,8 +46,8 @@
import com.android.systemui.plugins.ActivityStarter;
import com.android.systemui.plugins.FalsingManager;
import com.android.systemui.plugins.qs.DetailAdapter;
+import com.android.systemui.plugins.qs.QSContainerController;
import com.android.systemui.statusbar.CommandQueue;
-import com.android.systemui.statusbar.phone.NotificationsQuickSettingsContainer;
public class QSDetail extends LinearLayout {
@@ -86,7 +86,7 @@
private boolean mSwitchState;
private QSFooter mFooter;
- private NotificationsQuickSettingsContainer mContainer;
+ private QSContainerController mQsContainerController;
public QSDetail(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
@@ -120,8 +120,8 @@
mClipper = new QSDetailClipper(this);
}
- public void setContainer(NotificationsQuickSettingsContainer container) {
- mContainer = container;
+ public void setContainerController(QSContainerController controller) {
+ mQsContainerController = controller;
}
/** */
@@ -262,8 +262,8 @@
}
sendAccessibilityEvent(AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED);
animateDetailVisibleDiff(x, y, visibleDiff, listener);
- if (mContainer != null) {
- mContainer.setDetailShowing(showingDetail);
+ if (mQsContainerController != null) {
+ mQsContainerController.setDetailShowing(showingDetail);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java b/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java
index 6fd8141..b8376da 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java
@@ -43,6 +43,7 @@
import com.android.systemui.media.MediaHost;
import com.android.systemui.plugins.FalsingManager;
import com.android.systemui.plugins.qs.QS;
+import com.android.systemui.plugins.qs.QSContainerController;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.qs.customize.QSCustomizerController;
import com.android.systemui.qs.dagger.QSFragmentComponent;
@@ -50,7 +51,6 @@
import com.android.systemui.statusbar.StatusBarState;
import com.android.systemui.statusbar.notification.stack.StackStateAnimator;
import com.android.systemui.statusbar.phone.KeyguardBypassController;
-import com.android.systemui.statusbar.phone.NotificationsQuickSettingsContainer;
import com.android.systemui.statusbar.policy.BrightnessMirrorController;
import com.android.systemui.statusbar.policy.RemoteInputQuickSettingsDisabler;
import com.android.systemui.util.InjectionInflationController;
@@ -336,11 +336,9 @@
}
@Override
- public void setContainer(ViewGroup container) {
- if (container instanceof NotificationsQuickSettingsContainer) {
- mQSCustomizerController.setContainer((NotificationsQuickSettingsContainer) container);
- mQSDetail.setContainer((NotificationsQuickSettingsContainer) container);
- }
+ public void setContainerController(QSContainerController controller) {
+ mQSCustomizerController.setContainerController(controller);
+ mQSDetail.setContainerController(controller);
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/qs/carrier/QSCarrierGroupController.java b/packages/SystemUI/src/com/android/systemui/qs/carrier/QSCarrierGroupController.java
index 953f9fb..fec61d9 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/carrier/QSCarrierGroupController.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/carrier/QSCarrierGroupController.java
@@ -42,8 +42,8 @@
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.flags.FeatureFlags;
import com.android.systemui.plugins.ActivityStarter;
-import com.android.systemui.statusbar.policy.NetworkController;
-import com.android.systemui.statusbar.policy.NetworkController.MobileDataIndicators;
+import com.android.systemui.statusbar.connectivity.NetworkController;
+import com.android.systemui.statusbar.connectivity.NetworkController.MobileDataIndicators;
import com.android.systemui.util.CarrierConfigTracker;
import java.util.function.Consumer;
diff --git a/packages/SystemUI/src/com/android/systemui/qs/customize/QSCustomizer.java b/packages/SystemUI/src/com/android/systemui/qs/customize/QSCustomizer.java
index d33982c..1a6d490 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/customize/QSCustomizer.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/customize/QSCustomizer.java
@@ -33,9 +33,9 @@
import com.android.systemui.R;
import com.android.systemui.plugins.qs.QS;
+import com.android.systemui.plugins.qs.QSContainerController;
import com.android.systemui.qs.QSDetailClipper;
import com.android.systemui.statusbar.phone.LightBarController;
-import com.android.systemui.statusbar.phone.NotificationsQuickSettingsContainer;
/**
* Allows full-screen customization of QS, through show() and hide().
@@ -54,7 +54,7 @@
private boolean isShown;
private final RecyclerView mRecyclerView;
private boolean mCustomizing;
- private NotificationsQuickSettingsContainer mNotifQsContainer;
+ private QSContainerController mQsContainerController;
private QS mQs;
private int mX;
private int mY;
@@ -103,8 +103,8 @@
lightBarController.setQsCustomizing(mIsShowingNavBackdrop && isShown);
}
- public void setContainer(NotificationsQuickSettingsContainer notificationsQsContainer) {
- mNotifQsContainer = notificationsQsContainer;
+ public void setContainerController(QSContainerController controller) {
+ mQsContainerController = controller;
}
public void setQs(QS qs) {
@@ -123,8 +123,8 @@
mOpening = true;
setVisibility(View.VISIBLE);
mClipper.animateCircularClip(mX, mY, true, new ExpandAnimatorListener(tileAdapter));
- mNotifQsContainer.setCustomizerAnimating(true);
- mNotifQsContainer.setCustomizerShowing(true);
+ mQsContainerController.setCustomizerAnimating(true);
+ mQsContainerController.setCustomizerShowing(true);
}
}
@@ -136,8 +136,8 @@
mClipper.showBackground();
isShown = true;
setCustomizing(true);
- mNotifQsContainer.setCustomizerAnimating(false);
- mNotifQsContainer.setCustomizerShowing(true);
+ mQsContainerController.setCustomizerAnimating(false);
+ mQsContainerController.setCustomizerShowing(true);
}
}
@@ -154,8 +154,8 @@
} else {
setVisibility(View.GONE);
}
- mNotifQsContainer.setCustomizerAnimating(animate);
- mNotifQsContainer.setCustomizerShowing(false);
+ mQsContainerController.setCustomizerAnimating(animate);
+ mQsContainerController.setCustomizerShowing(false);
}
}
@@ -193,7 +193,7 @@
setCustomizing(true);
}
mOpening = false;
- mNotifQsContainer.setCustomizerAnimating(false);
+ mQsContainerController.setCustomizerAnimating(false);
mRecyclerView.setAdapter(mTileAdapter);
}
@@ -201,7 +201,7 @@
public void onAnimationCancel(Animator animation) {
mOpening = false;
mQs.notifyCustomizeChanged();
- mNotifQsContainer.setCustomizerAnimating(false);
+ mQsContainerController.setCustomizerAnimating(false);
}
}
@@ -211,7 +211,7 @@
if (!isShown) {
setVisibility(View.GONE);
}
- mNotifQsContainer.setCustomizerAnimating(false);
+ mQsContainerController.setCustomizerAnimating(false);
}
@Override
@@ -219,7 +219,7 @@
if (!isShown) {
setVisibility(View.GONE);
}
- mNotifQsContainer.setCustomizerAnimating(false);
+ mQsContainerController.setCustomizerAnimating(false);
}
};
diff --git a/packages/SystemUI/src/com/android/systemui/qs/customize/QSCustomizerController.java b/packages/SystemUI/src/com/android/systemui/qs/customize/QSCustomizerController.java
index 49d18e6..618a429 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/customize/QSCustomizerController.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/customize/QSCustomizerController.java
@@ -35,13 +35,13 @@
import com.android.internal.logging.UiEventLogger;
import com.android.systemui.R;
import com.android.systemui.keyguard.ScreenLifecycle;
+import com.android.systemui.plugins.qs.QSContainerController;
import com.android.systemui.plugins.qs.QSTile;
import com.android.systemui.qs.QSEditEvent;
import com.android.systemui.qs.QSFragment;
import com.android.systemui.qs.QSTileHost;
import com.android.systemui.qs.dagger.QSScope;
import com.android.systemui.statusbar.phone.LightBarController;
-import com.android.systemui.statusbar.phone.NotificationsQuickSettingsContainer;
import com.android.systemui.statusbar.policy.ConfigurationController;
import com.android.systemui.statusbar.policy.ConfigurationController.ConfigurationListener;
import com.android.systemui.statusbar.policy.KeyguardStateController;
@@ -233,8 +233,8 @@
}
/** */
- public void setContainer(NotificationsQuickSettingsContainer container) {
- mView.setContainer(container);
+ public void setContainerController(QSContainerController controller) {
+ mView.setContainerController(controller);
}
public boolean isShown() {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/CastTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/CastTile.java
index 04f089d..9de6ceb 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/CastTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/CastTile.java
@@ -51,13 +51,13 @@
import com.android.systemui.qs.QSHost;
import com.android.systemui.qs.logging.QSLogger;
import com.android.systemui.qs.tileimpl.QSTileImpl;
+import com.android.systemui.statusbar.connectivity.NetworkController;
+import com.android.systemui.statusbar.connectivity.NetworkController.WifiIndicators;
import com.android.systemui.statusbar.phone.SystemUIDialog;
import com.android.systemui.statusbar.policy.CastController;
import com.android.systemui.statusbar.policy.CastController.CastDevice;
import com.android.systemui.statusbar.policy.HotspotController;
import com.android.systemui.statusbar.policy.KeyguardStateController;
-import com.android.systemui.statusbar.policy.NetworkController;
-import com.android.systemui.statusbar.policy.NetworkController.WifiIndicators;
import java.util.ArrayList;
import java.util.LinkedHashMap;
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/CellularTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/CellularTile.java
index b1af841..35dadd4 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/CellularTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/CellularTile.java
@@ -56,11 +56,11 @@
import com.android.systemui.qs.SignalTileView;
import com.android.systemui.qs.logging.QSLogger;
import com.android.systemui.qs.tileimpl.QSTileImpl;
+import com.android.systemui.statusbar.connectivity.NetworkController;
+import com.android.systemui.statusbar.connectivity.NetworkController.IconState;
+import com.android.systemui.statusbar.connectivity.NetworkController.MobileDataIndicators;
+import com.android.systemui.statusbar.connectivity.NetworkController.SignalCallback;
import com.android.systemui.statusbar.phone.SystemUIDialog;
-import com.android.systemui.statusbar.policy.NetworkController;
-import com.android.systemui.statusbar.policy.NetworkController.IconState;
-import com.android.systemui.statusbar.policy.NetworkController.MobileDataIndicators;
-import com.android.systemui.statusbar.policy.NetworkController.SignalCallback;
import javax.inject.Inject;
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/InternetTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/InternetTile.java
index 530804e..98d0a72 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/InternetTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/InternetTile.java
@@ -52,13 +52,13 @@
import com.android.systemui.qs.logging.QSLogger;
import com.android.systemui.qs.tileimpl.QSTileImpl;
import com.android.systemui.qs.tiles.dialog.InternetDialogFactory;
-import com.android.systemui.statusbar.policy.NetworkController;
-import com.android.systemui.statusbar.policy.NetworkController.AccessPointController;
-import com.android.systemui.statusbar.policy.NetworkController.IconState;
-import com.android.systemui.statusbar.policy.NetworkController.MobileDataIndicators;
-import com.android.systemui.statusbar.policy.NetworkController.SignalCallback;
-import com.android.systemui.statusbar.policy.NetworkController.WifiIndicators;
-import com.android.systemui.statusbar.policy.WifiIcons;
+import com.android.systemui.statusbar.connectivity.NetworkController;
+import com.android.systemui.statusbar.connectivity.NetworkController.AccessPointController;
+import com.android.systemui.statusbar.connectivity.NetworkController.IconState;
+import com.android.systemui.statusbar.connectivity.NetworkController.MobileDataIndicators;
+import com.android.systemui.statusbar.connectivity.NetworkController.SignalCallback;
+import com.android.systemui.statusbar.connectivity.NetworkController.WifiIndicators;
+import com.android.systemui.statusbar.connectivity.WifiIcons;
import java.io.FileDescriptor;
import java.io.PrintWriter;
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/UserDetailView.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/UserDetailView.java
index 04437ea..821bd51 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/UserDetailView.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/UserDetailView.java
@@ -39,6 +39,8 @@
import com.android.systemui.qs.QSUserSwitcherEvent;
import com.android.systemui.statusbar.policy.UserSwitcherController;
+import java.util.function.Consumer;
+
import javax.inject.Inject;
/**
@@ -75,6 +77,7 @@
private View mCurrentUserView;
private final UiEventLogger mUiEventLogger;
private final FalsingManager mFalsingManager;
+ private Consumer<UserSwitcherController.UserRecord> mClickCallback;
@Inject
public Adapter(Context context, UserSwitcherController controller,
@@ -92,6 +95,10 @@
return createUserDetailItemView(convertView, parent, item);
}
+ public void injectCallback(Consumer<UserSwitcherController.UserRecord> clickCallback) {
+ mClickCallback = clickCallback;
+ }
+
public UserDetailItemView createUserDetailItemView(View convertView, ViewGroup parent,
UserSwitcherController.UserRecord item) {
UserDetailItemView v = UserDetailItemView.convertOrInflate(
@@ -167,6 +174,13 @@
}
onUserListItemClicked(tag);
}
+ if (mClickCallback != null) {
+ mClickCallback.accept(tag);
+ }
+ }
+
+ public void linkToViewGroup(ViewGroup viewGroup) {
+ PseudoGridView.ViewGroupAdapterBridge.link(viewGroup, this);
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/WifiTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/WifiTile.java
index 41a3020..e6e7e21 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/WifiTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/WifiTile.java
@@ -52,11 +52,11 @@
import com.android.systemui.qs.logging.QSLogger;
import com.android.systemui.qs.tileimpl.QSIconViewImpl;
import com.android.systemui.qs.tileimpl.QSTileImpl;
-import com.android.systemui.statusbar.policy.NetworkController;
-import com.android.systemui.statusbar.policy.NetworkController.AccessPointController;
-import com.android.systemui.statusbar.policy.NetworkController.SignalCallback;
-import com.android.systemui.statusbar.policy.NetworkController.WifiIndicators;
-import com.android.systemui.statusbar.policy.WifiIcons;
+import com.android.systemui.statusbar.connectivity.NetworkController;
+import com.android.systemui.statusbar.connectivity.NetworkController.AccessPointController;
+import com.android.systemui.statusbar.connectivity.NetworkController.SignalCallback;
+import com.android.systemui.statusbar.connectivity.NetworkController.WifiIndicators;
+import com.android.systemui.statusbar.connectivity.WifiIcons;
import com.android.wifitrackerlib.WifiEntry;
import java.util.List;
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialogController.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialogController.java
index aaba5ef..b9cd08e 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialogController.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialogController.java
@@ -75,9 +75,9 @@
import com.android.systemui.dagger.qualifiers.Background;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.plugins.ActivityStarter;
+import com.android.systemui.statusbar.connectivity.NetworkController;
+import com.android.systemui.statusbar.connectivity.NetworkController.AccessPointController;
import com.android.systemui.statusbar.policy.KeyguardStateController;
-import com.android.systemui.statusbar.policy.NetworkController;
-import com.android.systemui.statusbar.policy.NetworkController.AccessPointController;
import com.android.systemui.toast.SystemUIToast;
import com.android.systemui.toast.ToastFactory;
import com.android.systemui.util.CarrierConfigTracker;
diff --git a/packages/SystemUI/src/com/android/systemui/qs/user/UserDialog.kt b/packages/SystemUI/src/com/android/systemui/qs/user/UserDialog.kt
new file mode 100644
index 0000000..2ad06c1
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/qs/user/UserDialog.kt
@@ -0,0 +1,86 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.qs.user
+
+import android.content.Context
+import android.os.Bundle
+import android.view.Gravity
+import android.view.View
+import android.view.ViewGroup
+import android.view.WindowInsets
+import android.view.WindowManager
+import com.android.systemui.qs.PseudoGridView
+import com.android.systemui.statusbar.phone.SystemUIDialog
+import com.android.systemui.R
+
+/**
+ * Dialog for switching users or creating new ones.
+ */
+class UserDialog(
+ context: Context
+) : SystemUIDialog(context, R.style.Theme_SystemUI_Dialog_QSDialog) {
+
+ // create() is no-op after creation
+ private lateinit var _doneButton: View
+ /**
+ * Button with text "Done" in dialog.
+ */
+ val doneButton: View
+ get() {
+ create()
+ return _doneButton
+ }
+
+ private lateinit var _settingsButton: View
+ /**
+ * Button with text "User Settings" in dialog.
+ */
+ val settingsButton: View
+ get() {
+ create()
+ return _settingsButton
+ }
+
+ private lateinit var _grid: PseudoGridView
+ /**
+ * Grid to populate with user avatar from adapter
+ */
+ val grid: ViewGroup
+ get() {
+ create()
+ return _grid
+ }
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+ window?.apply {
+ setType(WindowManager.LayoutParams.TYPE_STATUS_BAR_SUB_PANEL)
+ attributes.fitInsetsTypes = attributes.fitInsetsTypes or WindowInsets.Type.statusBars()
+ attributes.receiveInsetsIgnoringZOrder = true
+ setLayout(
+ context.resources.getDimensionPixelSize(R.dimen.qs_panel_width),
+ ViewGroup.LayoutParams.WRAP_CONTENT
+ )
+ setGravity(Gravity.CENTER)
+ }
+ setContentView(R.layout.qs_user_dialog_content)
+
+ _doneButton = requireViewById(R.id.done)
+ _settingsButton = requireViewById(R.id.settings)
+ _grid = requireViewById(R.id.grid)
+ }
+}
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/qs/user/UserSwitchDialogController.kt b/packages/SystemUI/src/com/android/systemui/qs/user/UserSwitchDialogController.kt
new file mode 100644
index 0000000..a5e4ba1
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/qs/user/UserSwitchDialogController.kt
@@ -0,0 +1,87 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.qs.user
+
+import android.content.Context
+import android.content.Intent
+import android.provider.Settings
+import android.view.View
+import androidx.annotation.VisibleForTesting
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.plugins.ActivityStarter
+import com.android.systemui.plugins.FalsingManager
+import com.android.systemui.qs.tiles.UserDetailView
+import javax.inject.Inject
+import javax.inject.Provider
+
+/**
+ * Controller for [UserDialog].
+ */
+@SysUISingleton
+class UserSwitchDialogController @VisibleForTesting constructor(
+ private val userDetailViewAdapterProvider: Provider<UserDetailView.Adapter>,
+ private val activityStarter: ActivityStarter,
+ private val falsingManager: FalsingManager,
+ private val dialogFactory: (Context) -> UserDialog
+) {
+
+ @Inject
+ constructor(
+ userDetailViewAdapterProvider: Provider<UserDetailView.Adapter>,
+ activityStarter: ActivityStarter,
+ falsingManager: FalsingManager
+ ) : this(
+ userDetailViewAdapterProvider,
+ activityStarter,
+ falsingManager,
+ { UserDialog(it) }
+ )
+
+ companion object {
+ private val USER_SETTINGS_INTENT = Intent(Settings.ACTION_USER_SETTINGS)
+ }
+
+ /**
+ * Show a [UserDialog].
+ *
+ * Populate the dialog with information from and adapter obtained from
+ * [userDetailViewAdapterProvider] and show it as launched from [view].
+ */
+ fun showDialog(view: View) {
+ with(dialogFactory(view.context)) {
+ setShowForAllUsers(true)
+ setCanceledOnTouchOutside(true)
+ create() // Needs to be called before we can retrieve views
+
+ settingsButton.setOnClickListener {
+ if (!falsingManager.isFalseTap(FalsingManager.LOW_PENALTY)) {
+ activityStarter.postStartActivityDismissingKeyguard(USER_SETTINGS_INTENT, 0)
+ }
+ dismiss()
+ }
+ doneButton.setOnClickListener { dismiss() }
+
+ val adapter = userDetailViewAdapterProvider.get()
+ adapter.injectCallback {
+ dismiss()
+ }
+ adapter.linkToViewGroup(grid)
+
+ show()
+ }
+ }
+}
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/LongScreenshotActivity.java b/packages/SystemUI/src/com/android/systemui/screenshot/LongScreenshotActivity.java
index 31d51f1..a42b34c 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/LongScreenshotActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/LongScreenshotActivity.java
@@ -326,18 +326,11 @@
| Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
mTransitionView.setImageBitmap(mOutputBitmap);
+ mTransitionView.setVisibility(View.VISIBLE);
mTransitionView.setTransitionName(
ChooserActivity.FIRST_IMAGE_PREVIEW_TRANSITION_NAME);
// TODO: listen for transition completing instead of finishing onStop
mTransitionStarted = true;
- int[] locationOnScreen = new int[2];
- mTransitionView.getLocationOnScreen(locationOnScreen);
- int[] locationInWindow = new int[2];
- mTransitionView.getLocationInWindow(locationInWindow);
- int deltaX = locationOnScreen[0] - locationInWindow[0];
- int deltaY = locationOnScreen[1] - locationInWindow[1];
- mTransitionView.setX(mTransitionView.getX() - deltaX);
- mTransitionView.setY(mTransitionView.getY() - deltaY);
startActivity(intent,
ActivityOptions.makeSceneTransitionAnimation(this, mTransitionView,
ChooserActivity.FIRST_IMAGE_PREVIEW_TRANSITION_NAME).toBundle());
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java
index 8def475..5b4db14 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java
@@ -940,12 +940,10 @@
*/
private Supplier<ActionTransition> getActionTransitionSupplier() {
return () -> {
- View preview = mScreenshotView.getTransitionView();
- preview.setX(preview.getX() - mScreenshotView.getStaticLeftMargin());
Pair<ActivityOptions, ExitTransitionCoordinator> transition =
ActivityOptions.startSharedElementAnimation(
mWindow, new ScreenshotExitTransitionCallbacksSupplier(true).get(),
- null, Pair.create(mScreenshotView.getTransitionView(),
+ null, Pair.create(mScreenshotView.getScreenshotPreview(),
ChooserActivity.FIRST_IMAGE_PREVIEW_TRANSITION_NAME));
transition.second.startExit();
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotView.java b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotView.java
index dfb39e3..7222b03 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotView.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotView.java
@@ -137,13 +137,11 @@
private int mNavMode;
private boolean mOrientationPortrait;
private boolean mDirectionLTR;
- private int mStaticLeftMargin;
private ScreenshotSelectorView mScreenshotSelectorView;
private ImageView mScrollingScrim;
private View mScreenshotStatic;
private ImageView mScreenshotPreview;
- private View mTransitionView;
private View mScreenshotPreviewBorder;
private ImageView mScrollablePreview;
private ImageView mScreenshotFlash;
@@ -341,7 +339,6 @@
mScrollingScrim = requireNonNull(findViewById(R.id.screenshot_scrolling_scrim));
mScreenshotStatic = requireNonNull(findViewById(R.id.global_screenshot_static));
mScreenshotPreview = requireNonNull(findViewById(R.id.global_screenshot_preview));
- mTransitionView = requireNonNull(findViewById(R.id.screenshot_transition_view));
mScreenshotPreviewBorder = requireNonNull(
findViewById(R.id.global_screenshot_preview_border));
mScreenshotPreview.setClipToOutline(true);
@@ -387,12 +384,8 @@
requestFocus();
}
- View getTransitionView() {
- return mTransitionView;
- }
-
- int getStaticLeftMargin() {
- return mStaticLeftMargin;
+ View getScreenshotPreview() {
+ return mScreenshotPreview;
}
/**
@@ -442,7 +435,6 @@
Math.max(navBarInsets.bottom, waterfall.bottom));
}
}
- mStaticLeftMargin = p.leftMargin;
mScreenshotStatic.setLayoutParams(p);
mScreenshotStatic.requestLayout();
}
diff --git a/packages/SystemUI/src/com/android/systemui/sensorprivacy/SensorUseDialog.kt b/packages/SystemUI/src/com/android/systemui/sensorprivacy/SensorUseDialog.kt
new file mode 100644
index 0000000..c50365f
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/sensorprivacy/SensorUseDialog.kt
@@ -0,0 +1,74 @@
+package com.android.systemui.sensorprivacy
+
+import android.content.Context
+import android.content.DialogInterface
+import android.content.res.Resources
+import android.text.Html
+import android.view.LayoutInflater
+import android.view.View
+import android.view.WindowManager
+import android.widget.ImageView
+import com.android.internal.widget.DialogTitle
+import com.android.systemui.R
+import com.android.systemui.statusbar.phone.SystemUIDialog
+
+class SensorUseDialog(
+ context: Context,
+ val sensor: Int,
+ val clickListener: DialogInterface.OnClickListener
+) : SystemUIDialog(context) {
+
+ // TODO move to onCreate (b/200815309)
+ init {
+ window!!.addFlags(WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED)
+ window!!.addSystemFlags(
+ WindowManager.LayoutParams.SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS)
+
+ val layoutInflater = LayoutInflater.from(context)
+ val customTitleView = layoutInflater.inflate(R.layout.sensor_use_started_title, null)
+ customTitleView.requireViewById<DialogTitle>(R.id.sensor_use_started_title_message)
+ .setText(when (sensor) {
+ SensorUseStartedActivity.MICROPHONE ->
+ R.string.sensor_privacy_start_use_mic_dialog_title
+ SensorUseStartedActivity.CAMERA ->
+ R.string.sensor_privacy_start_use_camera_dialog_title
+ SensorUseStartedActivity.ALL_SENSORS ->
+ R.string.sensor_privacy_start_use_mic_camera_dialog_title
+ else -> Resources.ID_NULL
+ })
+ customTitleView.requireViewById<ImageView>(R.id.sensor_use_microphone_icon).visibility =
+ if (sensor == SensorUseStartedActivity.MICROPHONE ||
+ sensor == SensorUseStartedActivity.ALL_SENSORS) {
+ View.VISIBLE
+ } else {
+ View.GONE
+ }
+ customTitleView.requireViewById<ImageView>(R.id.sensor_use_camera_icon).visibility =
+ if (sensor == SensorUseStartedActivity.CAMERA ||
+ sensor == SensorUseStartedActivity.ALL_SENSORS) {
+ View.VISIBLE
+ } else {
+ View.GONE
+ }
+
+ setCustomTitle(customTitleView)
+ setMessage(Html.fromHtml(context.getString(when (sensor) {
+ SensorUseStartedActivity.MICROPHONE ->
+ R.string.sensor_privacy_start_use_mic_dialog_content
+ SensorUseStartedActivity.CAMERA ->
+ R.string.sensor_privacy_start_use_camera_dialog_content
+ SensorUseStartedActivity.ALL_SENSORS ->
+ R.string.sensor_privacy_start_use_mic_camera_dialog_content
+ else -> Resources.ID_NULL
+ }), 0))
+
+ setButton(BUTTON_POSITIVE,
+ context.getString(com.android.internal.R.string
+ .sensor_privacy_start_use_dialog_turn_on_button), clickListener)
+ setButton(BUTTON_NEGATIVE,
+ context.getString(com.android.internal.R.string
+ .cancel), clickListener)
+
+ setCancelable(false)
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/sensorprivacy/SensorUseStartedActivity.kt b/packages/SystemUI/src/com/android/systemui/sensorprivacy/SensorUseStartedActivity.kt
index f0fb5eb..b0071d9 100644
--- a/packages/SystemUI/src/com/android/systemui/sensorprivacy/SensorUseStartedActivity.kt
+++ b/packages/SystemUI/src/com/android/systemui/sensorprivacy/SensorUseStartedActivity.kt
@@ -16,33 +16,28 @@
package com.android.systemui.sensorprivacy
+import android.app.Activity
+import android.app.AlertDialog
import android.content.DialogInterface
+import android.content.DialogInterface.BUTTON_NEGATIVE
+import android.content.DialogInterface.BUTTON_POSITIVE
import android.content.Intent
import android.content.Intent.EXTRA_PACKAGE_NAME
-import android.content.pm.PackageManager
-import android.content.res.Resources
import android.hardware.SensorPrivacyManager
import android.hardware.SensorPrivacyManager.EXTRA_ALL_SENSORS
import android.hardware.SensorPrivacyManager.EXTRA_SENSOR
import android.hardware.SensorPrivacyManager.Sources.DIALOG
import android.os.Bundle
import android.os.Handler
-import android.text.Html
-import android.view.View.GONE
-import android.view.View.VISIBLE
-import android.widget.ImageView
-import com.android.internal.app.AlertActivity
-import com.android.internal.widget.DialogTitle
-import com.android.systemui.R
+import com.android.internal.util.FrameworkStatsLog.PRIVACY_TOGGLE_DIALOG_INTERACTION
+import com.android.internal.util.FrameworkStatsLog.PRIVACY_TOGGLE_DIALOG_INTERACTION__ACTION__CANCEL
+import com.android.internal.util.FrameworkStatsLog.PRIVACY_TOGGLE_DIALOG_INTERACTION__ACTION__ENABLE
+import com.android.internal.util.FrameworkStatsLog.write
import com.android.systemui.dagger.qualifiers.Background
import com.android.systemui.statusbar.phone.KeyguardDismissUtil
import com.android.systemui.statusbar.policy.IndividualSensorPrivacyController
import com.android.systemui.statusbar.policy.KeyguardStateController
import javax.inject.Inject
-import com.android.internal.util.FrameworkStatsLog.PRIVACY_TOGGLE_DIALOG_INTERACTION
-import com.android.internal.util.FrameworkStatsLog.PRIVACY_TOGGLE_DIALOG_INTERACTION__ACTION__ENABLE
-import com.android.internal.util.FrameworkStatsLog.PRIVACY_TOGGLE_DIALOG_INTERACTION__ACTION__CANCEL
-import com.android.internal.util.FrameworkStatsLog.write
/**
* Dialog to be shown on top of apps that are attempting to use a sensor (e.g. microphone) which is
@@ -55,7 +50,7 @@
private val keyguardStateController: KeyguardStateController,
private val keyguardDismissUtil: KeyguardDismissUtil,
@Background private val bgHandler: Handler
-) : AlertActivity(), DialogInterface.OnClickListener {
+) : Activity(), DialogInterface.OnClickListener {
companion object {
private val LOG_TAG = SensorUseStartedActivity::class.java.simpleName
@@ -63,9 +58,9 @@
private const val SUPPRESS_REMINDERS_REMOVAL_DELAY_MILLIS = 2000L
private const val UNLOCK_DELAY_MILLIS = 200L
- private const val CAMERA = SensorPrivacyManager.Sensors.CAMERA
- private const val MICROPHONE = SensorPrivacyManager.Sensors.MICROPHONE
- private const val ALL_SENSORS = Integer.MAX_VALUE
+ internal const val CAMERA = SensorPrivacyManager.Sensors.CAMERA
+ internal const val MICROPHONE = SensorPrivacyManager.Sensors.MICROPHONE
+ internal const val ALL_SENSORS = Integer.MAX_VALUE
}
private var sensor = -1
@@ -74,6 +69,8 @@
private lateinit var sensorPrivacyListener: IndividualSensorPrivacyController.Callback
+ private var mDialog: AlertDialog? = null
+
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
@@ -91,7 +88,7 @@
IndividualSensorPrivacyController.Callback { _, _ ->
if (!sensorPrivacyController.isSensorBlocked(MICROPHONE) &&
!sensorPrivacyController.isSensorBlocked(CAMERA)) {
- dismiss()
+ finish()
}
}
@@ -109,71 +106,22 @@
}
}
sensorPrivacyListener =
- IndividualSensorPrivacyController.Callback {
- whichSensor: Int, isBlocked: Boolean ->
+ IndividualSensorPrivacyController.Callback { whichSensor: Int,
+ isBlocked: Boolean ->
if (whichSensor == sensor && !isBlocked) {
- dismiss()
+ finish()
}
}
sensorPrivacyController.addCallback(sensorPrivacyListener)
- sensorPrivacyController.addCallback { _, isBlocked ->
- if (!isBlocked) {
- dismiss()
- }
- }
- }
-
- mAlertParams.apply {
- try {
- mCustomTitleView = mInflater.inflate(R.layout.sensor_use_started_title, null)
- mCustomTitleView.findViewById<DialogTitle>(R.id.sensor_use_started_title_message)!!
- .setText(when (sensor) {
- MICROPHONE ->
- R.string.sensor_privacy_start_use_mic_dialog_title
- CAMERA ->
- R.string.sensor_privacy_start_use_camera_dialog_title
- ALL_SENSORS ->
- R.string.sensor_privacy_start_use_mic_camera_dialog_title
- else -> Resources.ID_NULL
- })
-
- mCustomTitleView.findViewById<ImageView>(R.id.sensor_use_microphone_icon)!!
- .visibility = if (sensor == MICROPHONE || sensor == ALL_SENSORS) {
- VISIBLE
- } else {
- GONE
- }
- mCustomTitleView.findViewById<ImageView>(R.id.sensor_use_camera_icon)!!
- .visibility = if (sensor == CAMERA || sensor == ALL_SENSORS) {
- VISIBLE
- } else {
- GONE
- }
-
- mMessage = Html.fromHtml(getString(when (sensor) {
- MICROPHONE ->
- R.string.sensor_privacy_start_use_mic_dialog_content
- CAMERA ->
- R.string.sensor_privacy_start_use_camera_dialog_content
- ALL_SENSORS ->
- R.string.sensor_privacy_start_use_mic_camera_dialog_content
- else -> Resources.ID_NULL
- }, packageManager.getApplicationInfo(sensorUsePackageName, 0)
- .loadLabel(packageManager)), 0)
- } catch (e: PackageManager.NameNotFoundException) {
+ if (!sensorPrivacyController.isSensorBlocked(sensor)) {
finish()
return
}
-
- mPositiveButtonText = getString(
- com.android.internal.R.string.sensor_privacy_start_use_dialog_turn_on_button)
- mNegativeButtonText = getString(android.R.string.cancel)
- mPositiveButtonListener = this@SensorUseStartedActivity
- mNegativeButtonListener = this@SensorUseStartedActivity
}
- setupAlert()
+ mDialog = SensorUseDialog(this, sensor, this)
+ mDialog!!.show()
}
override fun onStart() {
@@ -212,7 +160,7 @@
}
}
- dismiss()
+ finish()
}
override fun onStop() {
@@ -229,6 +177,7 @@
override fun onDestroy() {
super.onDestroy()
+ mDialog?.dismiss()
sensorPrivacyController.removeCallback(sensorPrivacyListener)
}
@@ -263,4 +212,4 @@
.suppressSensorPrivacyReminders(sensor, suppressed)
}
}
-}
\ No newline at end of file
+}
diff --git a/packages/SystemUI/src/com/android/systemui/sensorprivacy/television/TvUnblockSensorActivity.java b/packages/SystemUI/src/com/android/systemui/sensorprivacy/television/TvUnblockSensorActivity.java
index 8cd3632..cc5cf4b 100644
--- a/packages/SystemUI/src/com/android/systemui/sensorprivacy/television/TvUnblockSensorActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/sensorprivacy/television/TvUnblockSensorActivity.java
@@ -24,6 +24,7 @@
import android.os.Bundle;
import android.util.Log;
import android.view.View;
+import android.view.WindowManager;
import android.widget.Button;
import android.widget.ImageView;
import android.widget.TextView;
@@ -57,6 +58,8 @@
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
+ getWindow().addSystemFlags(
+ WindowManager.LayoutParams.SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS);
boolean allSensors = getIntent().getBooleanExtra(SensorPrivacyManager.EXTRA_ALL_SENSORS,
false);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/OperatorNameViewController.java b/packages/SystemUI/src/com/android/systemui/statusbar/OperatorNameViewController.java
index e49f48f..bcba5cc 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/OperatorNameViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/OperatorNameViewController.java
@@ -26,7 +26,7 @@
import com.android.keyguard.KeyguardUpdateMonitorCallback;
import com.android.systemui.demomode.DemoModeCommandReceiver;
import com.android.systemui.plugins.DarkIconDispatcher;
-import com.android.systemui.statusbar.policy.NetworkController;
+import com.android.systemui.statusbar.connectivity.NetworkController;
import com.android.systemui.tuner.TunerService;
import com.android.systemui.util.ViewController;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/AccessPointControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/AccessPointControllerImpl.java
similarity index 98%
rename from packages/SystemUI/src/com/android/systemui/statusbar/policy/AccessPointControllerImpl.java
rename to packages/SystemUI/src/com/android/systemui/statusbar/connectivity/AccessPointControllerImpl.java
index 6d6320e..a23d73d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/AccessPointControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/AccessPointControllerImpl.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.systemui.statusbar.policy;
+package com.android.systemui.statusbar.connectivity;
import android.content.Context;
import android.content.Intent;
@@ -55,6 +55,7 @@
import javax.inject.Inject;
+/** */
public class AccessPointControllerImpl
implements NetworkController.AccessPointController,
WifiPickerTracker.WifiPickerTrackerCallback, LifecycleOwner {
@@ -116,17 +117,19 @@
super.finalize();
}
+ /** */
public boolean canConfigWifi() {
return !mUserManager.hasUserRestriction(UserManager.DISALLOW_CONFIG_WIFI,
new UserHandle(mCurrentUser));
}
+ /** */
public boolean canConfigMobileData() {
return !mUserManager.hasUserRestriction(UserManager.DISALLOW_CONFIG_MOBILE_NETWORKS,
UserHandle.of(mCurrentUser)) && mUserTracker.getUserInfo().isAdmin();
}
- public void onUserSwitched(int newUserId) {
+ void onUserSwitched(int newUserId) {
mCurrentUser = newUserId;
}
@@ -225,7 +228,7 @@
}
}
- public void dump(PrintWriter pw) {
+ void dump(PrintWriter pw) {
IndentingPrintWriter ipw = new IndentingPrintWriter(pw);
ipw.println("AccessPointControllerImpl:");
ipw.increaseIndent();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/CallbackHandler.java b/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/CallbackHandler.java
similarity index 94%
rename from packages/SystemUI/src/com/android/systemui/statusbar/policy/CallbackHandler.java
rename to packages/SystemUI/src/com/android/systemui/statusbar/connectivity/CallbackHandler.java
index 7ac6d63..052a789 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/CallbackHandler.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/CallbackHandler.java
@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package com.android.systemui.statusbar.policy;
+package com.android.systemui.statusbar.connectivity;
import android.os.Handler;
import android.os.Looper;
@@ -22,11 +22,11 @@
import com.android.internal.annotations.VisibleForTesting;
import com.android.systemui.dagger.qualifiers.Main;
-import com.android.systemui.statusbar.policy.NetworkController.EmergencyListener;
-import com.android.systemui.statusbar.policy.NetworkController.IconState;
-import com.android.systemui.statusbar.policy.NetworkController.MobileDataIndicators;
-import com.android.systemui.statusbar.policy.NetworkController.SignalCallback;
-import com.android.systemui.statusbar.policy.NetworkController.WifiIndicators;
+import com.android.systemui.statusbar.connectivity.NetworkController.EmergencyListener;
+import com.android.systemui.statusbar.connectivity.NetworkController.IconState;
+import com.android.systemui.statusbar.connectivity.NetworkController.MobileDataIndicators;
+import com.android.systemui.statusbar.connectivity.NetworkController.SignalCallback;
+import com.android.systemui.statusbar.connectivity.NetworkController.WifiIndicators;
import java.io.PrintWriter;
import java.text.SimpleDateFormat;
@@ -235,7 +235,7 @@
.append("icon=").append(icon)
.toString();
recordLastCallback(log);
- obtainMessage(MSG_ETHERNET_CHANGED, icon).sendToTarget();;
+ obtainMessage(MSG_ETHERNET_CHANGED, icon).sendToTarget();
}
@Override
@@ -252,14 +252,14 @@
.toString();
recordLastCallback(log);
}
- obtainMessage(MSG_AIRPLANE_MODE_CHANGED, icon).sendToTarget();;
+ obtainMessage(MSG_AIRPLANE_MODE_CHANGED, icon).sendToTarget();
}
- public void setListening(EmergencyListener listener, boolean listening) {
+ void setListening(EmergencyListener listener, boolean listening) {
obtainMessage(MSG_ADD_REMOVE_EMERGENCY, listening ? 1 : 0, 0, listener).sendToTarget();
}
- public void setListening(SignalCallback listener, boolean listening) {
+ void setListening(SignalCallback listener, boolean listening) {
obtainMessage(MSG_ADD_REMOVE_SIGNAL, listening ? 1 : 0, 0, listener).sendToTarget();
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/EthernetIcons.java b/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/EthernetIcons.java
similarity index 93%
rename from packages/SystemUI/src/com/android/systemui/statusbar/policy/EthernetIcons.java
rename to packages/SystemUI/src/com/android/systemui/statusbar/connectivity/EthernetIcons.java
index b391bd9..196aad9 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/EthernetIcons.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/EthernetIcons.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.systemui.statusbar.policy;
+package com.android.systemui.statusbar.connectivity;
import com.android.systemui.R;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/EthernetSignalController.java b/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/EthernetSignalController.java
similarity index 91%
rename from packages/SystemUI/src/com/android/systemui/statusbar/policy/EthernetSignalController.java
rename to packages/SystemUI/src/com/android/systemui/statusbar/connectivity/EthernetSignalController.java
index 80b75a7..c9d40ad 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/EthernetSignalController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/EthernetSignalController.java
@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package com.android.systemui.statusbar.policy;
+package com.android.systemui.statusbar.connectivity;
import android.content.Context;
import android.net.NetworkCapabilities;
@@ -21,12 +21,12 @@
import com.android.settingslib.AccessibilityContentDescriptions;
import com.android.settingslib.SignalIcon.IconGroup;
import com.android.settingslib.SignalIcon.State;
-import com.android.systemui.statusbar.policy.NetworkController.IconState;
-import com.android.systemui.statusbar.policy.NetworkController.SignalCallback;
+import com.android.systemui.statusbar.connectivity.NetworkController.IconState;
+import com.android.systemui.statusbar.connectivity.NetworkController.SignalCallback;
import java.util.BitSet;
-
+/** */
public class EthernetSignalController extends
SignalController<State, IconGroup> {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/MobileSignalController.java b/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/MobileSignalController.java
similarity index 97%
rename from packages/SystemUI/src/com/android/systemui/statusbar/policy/MobileSignalController.java
rename to packages/SystemUI/src/com/android/systemui/statusbar/connectivity/MobileSignalController.java
index a543c7c..20ef4ee 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/MobileSignalController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/MobileSignalController.java
@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package com.android.systemui.statusbar.policy;
+package com.android.systemui.statusbar.connectivity;
import static com.android.settingslib.mobile.MobileMappings.getDefaultIcons;
import static com.android.settingslib.mobile.MobileMappings.getIconKey;
@@ -58,9 +58,9 @@
import com.android.settingslib.net.SignalStrengthUtil;
import com.android.systemui.R;
import com.android.systemui.flags.FeatureFlags;
-import com.android.systemui.statusbar.policy.NetworkController.IconState;
-import com.android.systemui.statusbar.policy.NetworkController.MobileDataIndicators;
-import com.android.systemui.statusbar.policy.NetworkController.SignalCallback;
+import com.android.systemui.statusbar.connectivity.NetworkController.IconState;
+import com.android.systemui.statusbar.connectivity.NetworkController.MobileDataIndicators;
+import com.android.systemui.statusbar.connectivity.NetworkController.SignalCallback;
import com.android.systemui.util.CarrierConfigTracker;
import java.io.PrintWriter;
@@ -245,7 +245,7 @@
mProviderModelSetting = featureFlags.isProviderModelSettingEnabled();
}
- public void setConfiguration(Config config) {
+ void setConfiguration(Config config) {
mConfig = config;
updateInflateSignalStrength();
mNetworkToIconLookup = mapIconSets(mConfig);
@@ -253,12 +253,12 @@
updateTelephony();
}
- public void setAirplaneMode(boolean airplaneMode) {
+ void setAirplaneMode(boolean airplaneMode) {
mCurrentState.airplaneMode = airplaneMode;
notifyListenersIfNecessary();
}
- public void setUserSetupComplete(boolean userSetup) {
+ void setUserSetupComplete(boolean userSetup) {
mCurrentState.userSetup = userSetup;
notifyListenersIfNecessary();
}
@@ -272,7 +272,7 @@
notifyListenersIfNecessary();
}
- public void setCarrierNetworkChangeMode(boolean carrierNetworkChangeMode) {
+ void setCarrierNetworkChangeMode(boolean carrierNetworkChangeMode) {
mCurrentState.carrierNetworkChangeMode = carrierNetworkChangeMode;
updateTelephony();
}
@@ -413,7 +413,7 @@
if (mCurrentState.dataSim) {
// If using provider model behavior, only show QS icons if the state is also default
if (pm && !mCurrentState.isDefault) {
- return new QsInfo(qsTypeIcon, qsIcon, qsDescription);
+ return new QsInfo(qsTypeIcon, qsIcon, qsDescription);
}
if (mCurrentState.showQuickSettingsRatIcon() || mConfig.alwaysShowDataRatIcon) {
@@ -501,7 +501,7 @@
return mCurrentState.carrierNetworkChangeMode;
}
- public void handleBroadcast(Intent intent) {
+ void handleBroadcast(Intent intent) {
String action = intent.getAction();
if (action.equals(TelephonyManager.ACTION_SERVICE_PROVIDERS_UPDATED)) {
updateNetworkName(intent.getBooleanExtra(TelephonyManager.EXTRA_SHOW_SPN, false),
@@ -740,10 +740,10 @@
* mTelephonyDisplayInfo, and mSimState. It should be called any time one of these is updated.
* This will call listeners if necessary.
*/
- private final void updateTelephony() {
+ private void updateTelephony() {
if (Log.isLoggable(mTag, Log.DEBUG)) {
- Log.d(mTag, "updateTelephonySignalStrength: hasService=" +
- Utils.isInService(mServiceState) + " ss=" + mSignalStrength
+ Log.d(mTag, "updateTelephonySignalStrength: hasService="
+ + Utils.isInService(mServiceState) + " ss=" + mSignalStrength
+ " displayInfo=" + mTelephonyDisplayInfo);
}
checkDefaultData();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkController.java b/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/NetworkController.java
similarity index 94%
rename from packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkController.java
rename to packages/SystemUI/src/com/android/systemui/statusbar/connectivity/NetworkController.java
index 9aec903..1433096 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/NetworkController.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.systemui.statusbar.policy;
+package com.android.systemui.statusbar.connectivity;
import android.content.Context;
import android.content.Intent;
@@ -22,29 +22,44 @@
import com.android.settingslib.net.DataUsageController;
import com.android.systemui.demomode.DemoMode;
-import com.android.systemui.statusbar.policy.NetworkController.SignalCallback;
+import com.android.systemui.statusbar.connectivity.NetworkController.SignalCallback;
+import com.android.systemui.statusbar.policy.CallbackController;
+import com.android.systemui.statusbar.policy.DataSaverController;
import com.android.wifitrackerlib.MergedCarrierEntry;
import com.android.wifitrackerlib.WifiEntry;
import java.util.List;
+/** */
public interface NetworkController extends CallbackController<SignalCallback>, DemoMode {
-
+ /** */
boolean hasMobileDataFeature();
+ /** */
void setWifiEnabled(boolean enabled);
+ /** */
AccessPointController getAccessPointController();
+ /** */
DataUsageController getMobileDataController();
+ /** */
DataSaverController getDataSaverController();
+ /** */
String getMobileDataNetworkName();
+ /** */
boolean isMobileDataNetworkInService();
+ /** */
int getNumberSubscriptions();
+ /** */
boolean hasVoiceCallingFeature();
+ /** */
void addEmergencyListener(EmergencyListener listener);
+ /** */
void removeEmergencyListener(EmergencyListener listener);
+ /** */
boolean hasEmergencyCryptKeeperText();
+ /** */
boolean isRadioOn();
/**
@@ -143,7 +158,8 @@
}
}
- public interface SignalCallback {
+ /** */
+ interface SignalCallback {
/**
* Callback for listeners to be able to update the state of any UI tracking connectivity of
* WiFi networks.
@@ -156,14 +172,19 @@
*/
default void setMobileDataIndicators(MobileDataIndicators mobileDataIndicators) {}
+ /** */
default void setSubs(List<SubscriptionInfo> subs) {}
+ /** */
default void setNoSims(boolean show, boolean simDetected) {}
+ /** */
default void setEthernetIndicators(IconState icon) {}
+ /** */
default void setIsAirplaneMode(IconState icon) {}
+ /** */
default void setMobileDataEnabled(boolean enabled) {}
/**
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/NetworkControllerImpl.java
similarity index 98%
rename from packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java
rename to packages/SystemUI/src/com/android/systemui/statusbar/connectivity/NetworkControllerImpl.java
index 156def5..d9b0ee7 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/NetworkControllerImpl.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.systemui.statusbar.policy;
+package com.android.systemui.statusbar.connectivity;
import static android.net.NetworkCapabilities.NET_CAPABILITY_VALIDATED;
import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR;
@@ -76,7 +76,12 @@
import com.android.systemui.qs.tiles.dialog.InternetDialogFactory;
import com.android.systemui.qs.tiles.dialog.InternetDialogUtil;
import com.android.systemui.settings.CurrentUserTracker;
+import com.android.systemui.statusbar.policy.ConfigurationController;
+import com.android.systemui.statusbar.policy.DataSaverController;
+import com.android.systemui.statusbar.policy.DataSaverControllerImpl;
+import com.android.systemui.statusbar.policy.DeviceProvisionedController;
import com.android.systemui.statusbar.policy.DeviceProvisionedController.DeviceProvisionedListener;
+import com.android.systemui.statusbar.policy.EncryptionHelper;
import com.android.systemui.telephony.TelephonyListenerManager;
import com.android.systemui.util.CarrierConfigTracker;
@@ -376,11 +381,10 @@
@Override
public void onCapabilitiesChanged(
- Network network, NetworkCapabilities networkCapabilities) {
- boolean lastValidated = (mLastNetworkCapabilities != null) &&
- mLastNetworkCapabilities.hasCapability(NET_CAPABILITY_VALIDATED);
- boolean validated =
- networkCapabilities.hasCapability(NET_CAPABILITY_VALIDATED);
+ Network network, NetworkCapabilities networkCapabilities) {
+ boolean lastValidated = (mLastNetworkCapabilities != null)
+ && mLastNetworkCapabilities.hasCapability(NET_CAPABILITY_VALIDATED);
+ boolean validated = networkCapabilities.hasCapability(NET_CAPABILITY_VALIDATED);
// This callback is invoked a lot (i.e. when RSSI changes), so avoid updating
// icons when connectivity state has remained the same.
@@ -544,19 +548,23 @@
return mDataUsageController;
}
+ /** */
public void addEmergencyListener(EmergencyListener listener) {
mCallbackHandler.setListening(listener, true);
mCallbackHandler.setEmergencyCallsOnly(isEmergencyOnly());
}
+ /** */
public void removeEmergencyListener(EmergencyListener listener) {
mCallbackHandler.setListening(listener, false);
}
+ /** */
public boolean hasMobileDataFeature() {
return mHasMobileDataFeature;
}
+ /** */
public boolean hasVoiceCallingFeature() {
return mPhone.getPhoneType() != TelephonyManager.PHONE_TYPE_NONE;
}
@@ -661,7 +669,7 @@
}
}
- public boolean isEmergencyOnly() {
+ boolean isEmergencyOnly() {
if (mMobileSignalControllers.size() == 0) {
// When there are no active subscriptions, determine emengency state from last
// broadcast.
@@ -690,8 +698,10 @@
if (mMobileSignalControllers.size() == 1) {
mEmergencySource = EMERGENCY_ASSUMED_VOICE_CONTROLLER
+ mMobileSignalControllers.keyAt(0);
- if (DEBUG) Log.d(TAG, "Getting assumed emergency from "
- + mMobileSignalControllers.keyAt(0));
+ if (DEBUG) {
+ Log.d(TAG, "Getting assumed emergency from "
+ + mMobileSignalControllers.keyAt(0));
+ }
return mMobileSignalControllers.valueAt(0).getState().isEmergency;
}
if (DEBUG) Log.e(TAG, "Cannot find controller for voice sub: " + voiceSubId);
@@ -915,7 +925,7 @@
@GuardedBy("mLock")
@VisibleForTesting
- public void setCurrentSubscriptionsLocked(List<SubscriptionInfo> subscriptions) {
+ void setCurrentSubscriptionsLocked(List<SubscriptionInfo> subscriptions) {
Collections.sort(subscriptions, new Comparator<SubscriptionInfo>() {
@Override
public int compare(SubscriptionInfo lhs, SubscriptionInfo rhs) {
@@ -1125,6 +1135,7 @@
mEthernetSignalController.updateConnectivity(mConnectedTransports, mValidatedTransports);
}
+ /** */
public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
pw.println("NetworkController state:");
@@ -1178,7 +1189,7 @@
mCallbackHandler.dump(pw);
}
- private static final String emergencyToString(int emergencySource) {
+ private static String emergencyToString(int emergencySource) {
if (emergencySource > EMERGENCY_NO_SUB) {
return "ASSUMED_VOICE_CONTROLLER(" + (emergencySource - EMERGENCY_VOICE_CONTROLLER)
+ ")";
@@ -1423,10 +1434,12 @@
return info;
}
+ /** */
public boolean hasEmergencyCryptKeeperText() {
return EncryptionHelper.IS_DATA_ENCRYPTED;
}
+ /** */
public boolean isRadioOn() {
return !mAirplaneMode;
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/SignalController.java b/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/SignalController.java
similarity index 92%
rename from packages/SystemUI/src/com/android/systemui/statusbar/policy/SignalController.java
rename to packages/SystemUI/src/com/android/systemui/statusbar/connectivity/SignalController.java
index 4b6722c..d23dba5 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/SignalController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/SignalController.java
@@ -13,9 +13,9 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package com.android.systemui.statusbar.policy;
+package com.android.systemui.statusbar.connectivity;
-import static com.android.systemui.statusbar.policy.NetworkControllerImpl.TAG;
+import static com.android.systemui.statusbar.connectivity.NetworkControllerImpl.TAG;
import android.annotation.NonNull;
import android.content.Context;
@@ -23,8 +23,8 @@
import com.android.settingslib.SignalIcon.IconGroup;
import com.android.settingslib.SignalIcon.State;
-import com.android.systemui.statusbar.policy.NetworkController.IconState;
-import com.android.systemui.statusbar.policy.NetworkController.SignalCallback;
+import com.android.systemui.statusbar.connectivity.NetworkController.IconState;
+import com.android.systemui.statusbar.connectivity.NetworkController.SignalCallback;
import java.io.PrintWriter;
import java.util.BitSet;
@@ -83,7 +83,7 @@
return mCurrentState;
}
- public void updateConnectivity(BitSet connectedTransports, BitSet validatedTransports) {
+ void updateConnectivity(BitSet connectedTransports, BitSet validatedTransports) {
mCurrentState.inetCondition = validatedTransports.get(mTransportType) ? 1 : 0;
notifyListenersIfNecessary();
}
@@ -114,7 +114,7 @@
return false;
}
- public void saveLastState() {
+ void saveLastState() {
if (RECORD_HISTORY) {
recordLastState();
}
@@ -161,7 +161,7 @@
}
}
- public void notifyListenersIfNecessary() {
+ void notifyListenersIfNecessary() {
if (isDirty()) {
saveLastState();
notifyListeners();
@@ -192,7 +192,7 @@
mHistoryIndex = (mHistoryIndex + 1) % HISTORY_SIZE;
}
- public void dump(PrintWriter pw) {
+ void dump(PrintWriter pw) {
pw.println(" - " + mTag + " -----");
pw.println(" Current State: " + mCurrentState);
if (RECORD_HISTORY) {
@@ -210,7 +210,7 @@
}
}
- public final void notifyListeners() {
+ final void notifyListeners() {
notifyListeners(mCallbackHandler);
}
@@ -219,7 +219,7 @@
* based on current state, and only need to be called in the scenario where
* mCurrentState != mLastState.
*/
- public abstract void notifyListeners(SignalCallback callback);
+ abstract void notifyListeners(SignalCallback callback);
/**
* Generate a blank T.
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/WifiIcons.java b/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/WifiIcons.java
similarity index 97%
rename from packages/SystemUI/src/com/android/systemui/statusbar/policy/WifiIcons.java
rename to packages/SystemUI/src/com/android/systemui/statusbar/connectivity/WifiIcons.java
index 577cc4f..3c449ad 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/WifiIcons.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/WifiIcons.java
@@ -14,12 +14,13 @@
* limitations under the License.
*/
-package com.android.systemui.statusbar.policy;
+package com.android.systemui.statusbar.connectivity;
import com.android.settingslib.AccessibilityContentDescriptions;
import com.android.settingslib.R;
import com.android.settingslib.SignalIcon.IconGroup;
+/** */
public class WifiIcons {
static final int[] WIFI_FULL_ICONS = {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/WifiSignalController.java b/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/WifiSignalController.java
similarity index 96%
rename from packages/SystemUI/src/com/android/systemui/statusbar/policy/WifiSignalController.java
rename to packages/SystemUI/src/com/android/systemui/statusbar/connectivity/WifiSignalController.java
index 3f7ddc6..3622a66 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/WifiSignalController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/WifiSignalController.java
@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package com.android.systemui.statusbar.policy;
+package com.android.systemui.statusbar.connectivity;
import static android.net.wifi.WifiManager.TrafficStateCallback.DATA_ACTIVITY_IN;
import static android.net.wifi.WifiManager.TrafficStateCallback.DATA_ACTIVITY_INOUT;
@@ -37,14 +37,15 @@
import com.android.settingslib.wifi.WifiStatusTracker;
import com.android.systemui.R;
import com.android.systemui.flags.FeatureFlags;
-import com.android.systemui.statusbar.policy.NetworkController.IconState;
-import com.android.systemui.statusbar.policy.NetworkController.MobileDataIndicators;
-import com.android.systemui.statusbar.policy.NetworkController.SignalCallback;
-import com.android.systemui.statusbar.policy.NetworkController.WifiIndicators;
+import com.android.systemui.statusbar.connectivity.NetworkController.IconState;
+import com.android.systemui.statusbar.connectivity.NetworkController.MobileDataIndicators;
+import com.android.systemui.statusbar.connectivity.NetworkController.SignalCallback;
+import com.android.systemui.statusbar.connectivity.NetworkController.WifiIndicators;
import java.io.PrintWriter;
import java.util.Objects;
+/** */
public class WifiSignalController extends
SignalController<WifiSignalController.WifiState, IconGroup> {
private final boolean mHasMobileDataFeature;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/dagger/StatusBarDependenciesModule.java b/packages/SystemUI/src/com/android/systemui/statusbar/dagger/StatusBarDependenciesModule.java
index ea00d92..0352212 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/dagger/StatusBarDependenciesModule.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/dagger/StatusBarDependenciesModule.java
@@ -65,6 +65,7 @@
import com.android.systemui.statusbar.phone.StatusBarIconController;
import com.android.systemui.statusbar.phone.StatusBarIconControllerImpl;
import com.android.systemui.statusbar.phone.StatusBarRemoteInputCallback;
+import com.android.systemui.statusbar.phone.StatusBarWindowController;
import com.android.systemui.statusbar.phone.SystemUIHostDialogProvider;
import com.android.systemui.statusbar.phone.ongoingcall.OngoingCallController;
import com.android.systemui.statusbar.phone.ongoingcall.OngoingCallLogger;
@@ -252,11 +253,17 @@
ActivityStarter activityStarter,
@Main Executor mainExecutor,
IActivityManager iActivityManager,
- OngoingCallLogger logger) {
+ OngoingCallLogger logger,
+ DumpManager dumpManager,
+ StatusBarWindowController statusBarWindowController) {
+ Optional<StatusBarWindowController> windowController =
+ featureFlags.isOngoingCallInImmersiveEnabled()
+ ? Optional.of(statusBarWindowController)
+ : Optional.empty();
OngoingCallController ongoingCallController =
new OngoingCallController(
notifCollection, featureFlags, systemClock, activityStarter, mainExecutor,
- iActivityManager, logger);
+ iActivityManager, logger, dumpManager, windowController);
ongoingCallController.init();
return ongoingCallController;
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
index 4400909..278f09b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
@@ -254,6 +254,7 @@
private boolean mExpandedInThisMotion;
private boolean mShouldShowShelfOnly;
protected boolean mScrollingEnabled;
+ private boolean mIsCurrentUserSetup;
protected FooterView mFooterView;
protected EmptyShadeView mEmptyShadeView;
private boolean mDismissAllInProgress;
@@ -693,6 +694,7 @@
boolean showDismissView = mClearAllEnabled &&
mController.hasActiveClearableNotifications(ROWS_ALL);
boolean showFooterView = (showDismissView || getVisibleNotificationCount() > 0)
+ && mIsCurrentUserSetup // see: b/193149550
&& mStatusBarState != StatusBarState.KEYGUARD
&& !mUnlockedScreenOffAnimationController.isScreenOffAnimationPlaying()
&& !mIsRemoteInputActive;
@@ -5621,6 +5623,16 @@
}
/**
+ * Sets whether the current user is set up, which is required to show the footer (b/193149550)
+ */
+ public void setCurrentUserSetup(boolean isCurrentUserSetup) {
+ if (mIsCurrentUserSetup != isCurrentUserSetup) {
+ mIsCurrentUserSetup = isCurrentUserSetup;
+ updateFooter();
+ }
+ }
+
+ /**
* A listener that is notified when the empty space below the notifications is clicked on
*/
@ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java
index 75eb88f..9fe06a0 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java
@@ -119,6 +119,8 @@
import com.android.systemui.statusbar.phone.dagger.StatusBarComponent;
import com.android.systemui.statusbar.policy.ConfigurationController;
import com.android.systemui.statusbar.policy.ConfigurationController.ConfigurationListener;
+import com.android.systemui.statusbar.policy.DeviceProvisionedController;
+import com.android.systemui.statusbar.policy.DeviceProvisionedController.DeviceProvisionedListener;
import com.android.systemui.statusbar.policy.OnHeadsUpChangedListener;
import com.android.systemui.statusbar.policy.ZenModeController;
import com.android.systemui.tuner.TunerService;
@@ -146,6 +148,7 @@
private final HeadsUpManagerPhone mHeadsUpManager;
private final NotificationRoundnessManager mNotificationRoundnessManager;
private final TunerService mTunerService;
+ private final DeviceProvisionedController mDeviceProvisionedController;
private final DynamicPrivacyController mDynamicPrivacyController;
private final ConfigurationController mConfigurationController;
private final ZenModeController mZenModeController;
@@ -226,6 +229,28 @@
}
};
+ private final DeviceProvisionedListener mDeviceProvisionedListener =
+ new DeviceProvisionedListener() {
+ @Override
+ public void onDeviceProvisionedChanged() {
+ updateCurrentUserIsSetup();
+ }
+
+ @Override
+ public void onUserSwitched() {
+ updateCurrentUserIsSetup();
+ }
+
+ @Override
+ public void onUserSetupChanged() {
+ updateCurrentUserIsSetup();
+ }
+
+ private void updateCurrentUserIsSetup() {
+ mView.setCurrentUserSetup(mDeviceProvisionedController.isCurrentUserSetup());
+ }
+ };
+
private final DynamicPrivacyController.Listener mDynamicPrivacyControllerListener = () -> {
if (mView.isExpanded()) {
// The bottom might change because we're using the final actual height of the view
@@ -600,6 +625,7 @@
HeadsUpManagerPhone headsUpManager,
NotificationRoundnessManager notificationRoundnessManager,
TunerService tunerService,
+ DeviceProvisionedController deviceProvisionedController,
DynamicPrivacyController dynamicPrivacyController,
ConfigurationController configurationController,
SysuiStatusBarStateController statusBarStateController,
@@ -636,6 +662,7 @@
mHeadsUpManager = headsUpManager;
mNotificationRoundnessManager = notificationRoundnessManager;
mTunerService = tunerService;
+ mDeviceProvisionedController = deviceProvisionedController;
mDynamicPrivacyController = dynamicPrivacyController;
mConfigurationController = configurationController;
mStatusBarStateController = statusBarStateController;
@@ -787,6 +814,9 @@
return Unit.INSTANCE;
});
+ // callback is invoked synchronously, updating mView immediately
+ mDeviceProvisionedController.addCallback(mDeviceProvisionedListener);
+
if (mView.isAttachedToWindow()) {
mOnAttachStateChangeListener.onViewAttachedToWindow(mView);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragment.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragment.java
index c16cc12..989f6b8 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragment.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragment.java
@@ -46,6 +46,8 @@
import com.android.systemui.statusbar.OperatorNameView;
import com.android.systemui.statusbar.OperatorNameViewController;
import com.android.systemui.statusbar.StatusBarState;
+import com.android.systemui.statusbar.connectivity.NetworkController;
+import com.android.systemui.statusbar.connectivity.NetworkController.SignalCallback;
import com.android.systemui.statusbar.events.SystemStatusAnimationCallback;
import com.android.systemui.statusbar.events.SystemStatusAnimationScheduler;
import com.android.systemui.statusbar.phone.StatusBarIconController.DarkIconManager;
@@ -53,8 +55,6 @@
import com.android.systemui.statusbar.phone.ongoingcall.OngoingCallListener;
import com.android.systemui.statusbar.policy.EncryptionHelper;
import com.android.systemui.statusbar.policy.KeyguardStateController;
-import com.android.systemui.statusbar.policy.NetworkController;
-import com.android.systemui.statusbar.policy.NetworkController.SignalCallback;
import org.jetbrains.annotations.NotNull;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeParameters.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeParameters.java
index a8c62fe..5bf982b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeParameters.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeParameters.java
@@ -22,6 +22,7 @@
import android.os.SystemProperties;
import android.os.UserHandle;
import android.provider.Settings;
+import android.util.Log;
import android.util.MathUtils;
import androidx.annotation.NonNull;
@@ -35,6 +36,7 @@
import com.android.systemui.dump.DumpManager;
import com.android.systemui.flags.FeatureFlags;
import com.android.systemui.statusbar.policy.BatteryController;
+import com.android.systemui.statusbar.policy.DevicePostureController;
import com.android.systemui.tuner.TunerService;
import java.io.FileDescriptor;
@@ -256,9 +258,20 @@
}
/**
+ * Whether the single tap sensor uses the proximity sensor for this device posture.
+ */
+ public boolean singleTapUsesProx(@DevicePostureController.DevicePostureInt int devicePosture) {
+ return getPostureSpecificBool(
+ mResources.getIntArray(R.array.doze_single_tap_uses_prox_posture_mapping),
+ singleTapUsesProx(),
+ devicePosture
+ );
+ }
+
+ /**
* Whether the single tap sensor uses the proximity sensor.
*/
- public boolean singleTapUsesProx() {
+ private boolean singleTapUsesProx() {
return mResources.getBoolean(R.bool.doze_single_tap_uses_prox);
}
@@ -270,6 +283,17 @@
}
/**
+ * Sensor to use for brightness changes.
+ */
+ public String brightnessName(@DevicePostureController.DevicePostureInt int posture) {
+ return AmbientDisplayConfiguration.getSensorFromPostureMapping(
+ mResources.getStringArray(R.array.doze_brightness_sensor_name_posture_mapping),
+ null /* defaultValue */,
+ posture
+ );
+ }
+
+ /**
* Callback to listen for DozeParameter changes.
*/
public void addCallback(Callback callback) {
@@ -308,6 +332,20 @@
pw.println(getSelectivelyRegisterSensorsUsingProx());
}
+ private boolean getPostureSpecificBool(
+ int[] postureMapping,
+ boolean defaultSensorBool,
+ int posture) {
+ boolean bool = defaultSensorBool;
+ if (posture < postureMapping.length) {
+ bool = postureMapping[posture] != 0;
+ } else {
+ Log.e("DozeParameters", "Unsupported doze posture " + posture);
+ }
+
+ return bool;
+ }
+
interface Callback {
/**
* Invoked when the value of getAlwaysOn may have changed.
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java
index f1d5e02..a090ac3 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java
@@ -126,6 +126,19 @@
mExpansionCallbacks.add(expansionCallback);
}
+ /**
+ * Enable/disable only the back button
+ */
+ public void setBackButtonEnabled(boolean enabled) {
+ int vis = mContainer.getSystemUiVisibility();
+ if (enabled) {
+ vis &= ~View.STATUS_BAR_DISABLE_BACK;
+ } else {
+ vis |= View.STATUS_BAR_DISABLE_BACK;
+ }
+ mContainer.setSystemUiVisibility(vis);
+ }
+
public void show(boolean resetSecuritySelection) {
show(resetSecuritySelection, true /* scrimmed */);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewController.java
index 893aa6d..5feb405 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewController.java
@@ -101,8 +101,8 @@
@Override
public void onOverlayChanged() {
- KeyguardStatusBarViewController.this.onThemeChanged();
mView.onOverlayChanged();
+ KeyguardStatusBarViewController.this.onThemeChanged();
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/MultiUserSwitchController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/MultiUserSwitchController.java
index 3fdf1ce..dd21c8a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/MultiUserSwitchController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/MultiUserSwitchController.java
@@ -23,11 +23,13 @@
import android.view.ViewGroup;
import com.android.systemui.R;
+import com.android.systemui.flags.FeatureFlags;
import com.android.systemui.plugins.FalsingManager;
import com.android.systemui.plugins.qs.DetailAdapter;
import com.android.systemui.qs.FooterActionsView;
import com.android.systemui.qs.QSDetailDisplayer;
import com.android.systemui.qs.dagger.QSScope;
+import com.android.systemui.qs.user.UserSwitchDialogController;
import com.android.systemui.statusbar.policy.UserSwitcherController;
import com.android.systemui.util.ViewController;
@@ -39,6 +41,8 @@
private final UserSwitcherController mUserSwitcherController;
private final QSDetailDisplayer mQsDetailDisplayer;
private final FalsingManager mFalsingManager;
+ private final UserSwitchDialogController mUserSwitchDialogController;
+ private final FeatureFlags mFeatureFlags;
private UserSwitcherController.BaseUserAdapter mUserListener;
@@ -49,14 +53,18 @@
return;
}
- View center = mView.getChildCount() > 0 ? mView.getChildAt(0) : mView;
+ if (mFeatureFlags.useNewUserSwitcher()) {
+ mUserSwitchDialogController.showDialog(v);
+ } else {
+ View center = mView.getChildCount() > 0 ? mView.getChildAt(0) : mView;
- int[] tmpInt = new int[2];
- center.getLocationInWindow(tmpInt);
- tmpInt[0] += center.getWidth() / 2;
- tmpInt[1] += center.getHeight() / 2;
+ int[] tmpInt = new int[2];
+ center.getLocationInWindow(tmpInt);
+ tmpInt[0] += center.getWidth() / 2;
+ tmpInt[1] += center.getHeight() / 2;
- mQsDetailDisplayer.showDetailAdapter(getUserDetailAdapter(), tmpInt[0], tmpInt[1]);
+ mQsDetailDisplayer.showDetailAdapter(getUserDetailAdapter(), tmpInt[0], tmpInt[1]);
+ }
}
};
@@ -66,31 +74,40 @@
private final UserSwitcherController mUserSwitcherController;
private final QSDetailDisplayer mQsDetailDisplayer;
private final FalsingManager mFalsingManager;
+ private final UserSwitchDialogController mUserSwitchDialogController;
+ private final FeatureFlags mFeatureFlags;
@Inject
public Factory(UserManager userManager, UserSwitcherController userSwitcherController,
- QSDetailDisplayer qsDetailDisplayer, FalsingManager falsingManager) {
+ QSDetailDisplayer qsDetailDisplayer, FalsingManager falsingManager,
+ UserSwitchDialogController userSwitchDialogController,
+ FeatureFlags featureFlags) {
mUserManager = userManager;
mUserSwitcherController = userSwitcherController;
mQsDetailDisplayer = qsDetailDisplayer;
mFalsingManager = falsingManager;
+ mUserSwitchDialogController = userSwitchDialogController;
+ mFeatureFlags = featureFlags;
}
public MultiUserSwitchController create(FooterActionsView view) {
return new MultiUserSwitchController(view.findViewById(R.id.multi_user_switch),
mUserManager, mUserSwitcherController, mQsDetailDisplayer,
- mFalsingManager);
+ mFalsingManager, mUserSwitchDialogController, mFeatureFlags);
}
}
private MultiUserSwitchController(MultiUserSwitch view, UserManager userManager,
UserSwitcherController userSwitcherController, QSDetailDisplayer qsDetailDisplayer,
- FalsingManager falsingManager) {
+ FalsingManager falsingManager, UserSwitchDialogController userSwitchDialogController,
+ FeatureFlags featureFlags) {
super(view);
mUserManager = userManager;
mUserSwitcherController = userSwitcherController;
mQsDetailDisplayer = qsDetailDisplayer;
mFalsingManager = falsingManager;
+ mUserSwitchDialogController = userSwitchDialogController;
+ mFeatureFlags = featureFlags;
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java
index ba04761..27770c7 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java
@@ -34,6 +34,9 @@
import static com.android.systemui.statusbar.StatusBarState.SHADE;
import static com.android.systemui.statusbar.StatusBarState.SHADE_LOCKED;
import static com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout.ROWS_ALL;
+import static com.android.systemui.statusbar.phone.PanelBar.STATE_CLOSED;
+import static com.android.systemui.statusbar.phone.PanelBar.STATE_OPEN;
+import static com.android.systemui.statusbar.phone.PanelBar.STATE_OPENING;
import static java.lang.Float.isNaN;
@@ -57,7 +60,6 @@
import android.graphics.Rect;
import android.graphics.Region;
import android.graphics.drawable.Drawable;
-import android.hardware.biometrics.BiometricSourceType;
import android.hardware.biometrics.SensorLocationInternal;
import android.hardware.fingerprint.FingerprintSensorPropertiesInternal;
import android.os.Bundle;
@@ -80,6 +82,7 @@
import android.view.ViewStub;
import android.view.ViewTreeObserver;
import android.view.WindowInsets;
+import android.view.accessibility.AccessibilityEvent;
import android.view.accessibility.AccessibilityManager;
import android.view.accessibility.AccessibilityNodeInfo;
import android.widget.FrameLayout;
@@ -310,6 +313,7 @@
private KeyguardStatusViewController mKeyguardStatusViewController;
private LockIconViewController mLockIconViewController;
private NotificationsQuickSettingsContainer mNotificationContainerParent;
+ private NotificationsQSContainerController mNotificationsQSContainerController;
private boolean mAnimateNextPositionUpdate;
private float mQuickQsOffsetHeight;
private UnlockedScreenOffAnimationController mUnlockedScreenOffAnimationController;
@@ -653,6 +657,7 @@
ConversationNotificationManager conversationNotificationManager,
MediaHierarchyManager mediaHierarchyManager,
StatusBarKeyguardViewManager statusBarKeyguardViewManager,
+ NotificationsQSContainerController notificationsQSContainerController,
NotificationStackScrollLayoutController notificationStackScrollLayoutController,
KeyguardStatusViewComponent.Factory keyguardStatusViewComponentFactory,
KeyguardQsUserSwitchComponent.Factory keyguardQsUserSwitchComponentFactory,
@@ -708,6 +713,8 @@
mFlingAnimationUtilsBuilder = flingAnimationUtilsBuilder;
mMediaHierarchyManager = mediaHierarchyManager;
mStatusBarKeyguardViewManager = statusBarKeyguardViewManager;
+ mNotificationsQSContainerController = notificationsQSContainerController;
+ mNotificationsQSContainerController.init();
mNotificationStackScrollLayoutController = notificationStackScrollLayoutController;
mGroupManager = groupManager;
mNotificationIconAreaController = notificationIconAreaController;
@@ -1005,7 +1012,7 @@
constraintSet.setMargin(R.id.notification_stack_scroller, TOP, topMargin);
constraintSet.setMargin(R.id.qs_frame, TOP, topMargin);
constraintSet.applyTo(mNotificationContainerParent);
- mNotificationContainerParent.setSplitShadeEnabled(mShouldUseSplitNotificationShade);
+ mNotificationsQSContainerController.setSplitShadeEnabled(mShouldUseSplitNotificationShade);
updateKeyguardStatusViewAlignment(/* animate= */false);
@@ -1798,15 +1805,6 @@
return !mQsTouchAboveFalsingThreshold;
}
- /**
- * Percentage of panel expansion offset, caused by pulling down on a heads-up.
- */
- @Override
- public void setMinFraction(float minFraction) {
- mMinFraction = minFraction;
- mDepthController.setPanelPullDownMinFraction(mMinFraction);
- }
-
private float computeQsExpansionFraction() {
if (mQSAnimatingHiddenFromCollapsed) {
// When hiding QS from collapsed state, the expansion can sometimes temporarily
@@ -2106,7 +2104,7 @@
requestPanelHeightUpdate();
mFalsingCollector.setQsExpanded(expanded);
mStatusBar.setQsExpanded(expanded);
- mNotificationContainerParent.setQsExpanded(expanded);
+ mNotificationsQSContainerController.setQsExpanded(expanded);
mPulseExpansionHandler.setQsExpanded(expanded);
mKeyguardBypassController.setQSExpanded(expanded);
mStatusBarKeyguardViewManager.setQsExpanded(expanded);
@@ -2228,12 +2226,6 @@
updateQSExpansionEnabledAmbient();
}
- @Override
- public void setIsShadeOpening(boolean opening) {
- mAmbientState.setIsShadeOpening(opening);
- updateQSExpansionEnabledAmbient();
- }
-
private void updateQSExpansionEnabledAmbient() {
final float scrollRangeToTop = mAmbientState.getTopPadding() - mQuickQsOffsetHeight;
mQsExpansionEnabledAmbient = mShouldUseSplitNotificationShade
@@ -3341,8 +3333,14 @@
return mBarState == KEYGUARD;
}
+ /**
+ * Sets the minimum fraction for the panel expansion offset. This may be non-zero in certain
+ * cases, such as if there's a heads-up notification.
+ */
public void setPanelScrimMinFraction(float minFraction) {
mBar.onPanelMinFractionChanged(minFraction);
+ mMinFraction = minFraction;
+ mDepthController.setPanelPullDownMinFraction(mMinFraction);
}
public void clearNotificationEffects() {
@@ -4630,4 +4628,26 @@
return insets;
}
}
+
+ private final PanelBar.PanelStateChangeListener mPanelStateChangeListener =
+ new PanelBar.PanelStateChangeListener() {
+
+ @PanelBar.PanelState
+ private int mCurrentState = STATE_CLOSED;
+
+ @Override
+ public void onStateChanged(@PanelBar.PanelState int state) {
+ mAmbientState.setIsShadeOpening(state == STATE_OPENING);
+ updateQSExpansionEnabledAmbient();
+
+ if (state == STATE_OPEN && mCurrentState != state) {
+ mView.sendAccessibilityEvent(AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED);
+ }
+ mCurrentState = state;
+ }
+ };
+
+ public PanelBar.PanelStateChangeListener getPanelStateChangeListener() {
+ return mPanelStateChangeListener;
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationShadeWindowViewController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationShadeWindowViewController.java
index 03d0bb0..9d06d2b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationShadeWindowViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationShadeWindowViewController.java
@@ -171,6 +171,13 @@
mBrightnessMirror = mView.findViewById(R.id.brightness_mirror_container);
}
+ /**
+ * @return Location where to place the KeyguardBouncer
+ */
+ public ViewGroup getBouncerContainer() {
+ return mView.findViewById(R.id.keyguard_bouncer_container);
+ }
+
/** Inflates the {@link R.layout#status_bar_expanded} layout and sets it up. */
public void setupExpandedStatusBar() {
mStackScrollLayout = mView.findViewById(R.id.notification_stack_scroller);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationsQSContainerController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationsQSContainerController.kt
new file mode 100644
index 0000000..34bb6d3
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationsQSContainerController.kt
@@ -0,0 +1,141 @@
+package com.android.systemui.statusbar.phone
+
+import android.view.WindowInsets
+import com.android.systemui.navigationbar.NavigationModeController
+import com.android.systemui.plugins.qs.QS
+import com.android.systemui.plugins.qs.QSContainerController
+import com.android.systemui.recents.OverviewProxyService
+import com.android.systemui.recents.OverviewProxyService.OverviewProxyListener
+import com.android.systemui.shared.system.QuickStepContract
+import com.android.systemui.util.ViewController
+import java.util.function.Consumer
+import javax.inject.Inject
+
+class NotificationsQSContainerController @Inject constructor(
+ view: NotificationsQuickSettingsContainer,
+ private val navigationModeController: NavigationModeController,
+ private val overviewProxyService: OverviewProxyService
+) : ViewController<NotificationsQuickSettingsContainer>(view), QSContainerController {
+
+ var qsExpanded = false
+ set(value) {
+ if (field != value) {
+ field = value
+ mView.invalidate()
+ }
+ }
+ var splitShadeEnabled = false
+ set(value) {
+ if (field != value) {
+ field = value
+ // in case device configuration changed while showing QS details/customizer
+ updateBottomSpacing()
+ }
+ }
+
+ private var isQSDetailShowing = false
+ private var isQSCustomizing = false
+ private var isQSCustomizerAnimating = false
+
+ private var notificationsBottomMargin = 0
+ private var bottomStableInsets = 0
+ private var bottomCutoutInsets = 0
+
+ private var isGestureNavigation = true
+ private var taskbarVisible = false
+ private val taskbarVisibilityListener: OverviewProxyListener = object : OverviewProxyListener {
+ override fun onTaskbarStatusUpdated(visible: Boolean, stashed: Boolean) {
+ taskbarVisible = visible
+ }
+ }
+ private val windowInsetsListener: Consumer<WindowInsets> = Consumer { insets ->
+ // when taskbar is visible, stableInsetBottom will include its height
+ bottomStableInsets = insets.stableInsetBottom
+ bottomCutoutInsets = insets.displayCutout?.safeInsetBottom ?: 0
+ updateBottomSpacing()
+ }
+
+ override fun onInit() {
+ val currentMode: Int = navigationModeController.addListener { mode: Int ->
+ isGestureNavigation = QuickStepContract.isGesturalMode(mode)
+ }
+ isGestureNavigation = QuickStepContract.isGesturalMode(currentMode)
+ }
+
+ public override fun onViewAttached() {
+ notificationsBottomMargin = mView.defaultNotificationsMarginBottom
+ overviewProxyService.addCallback(taskbarVisibilityListener)
+ mView.setInsetsChangedListener(windowInsetsListener)
+ mView.setQSFragmentAttachedListener { qs: QS -> qs.setContainerController(this) }
+ }
+
+ override fun onViewDetached() {
+ overviewProxyService.removeCallback(taskbarVisibilityListener)
+ mView.removeOnInsetsChangedListener()
+ mView.removeQSFragmentAttachedListener()
+ }
+
+ override fun setCustomizerAnimating(animating: Boolean) {
+ if (isQSCustomizerAnimating != animating) {
+ isQSCustomizerAnimating = animating
+ mView.invalidate()
+ }
+ }
+
+ override fun setCustomizerShowing(showing: Boolean) {
+ isQSCustomizing = showing
+ updateBottomSpacing()
+ }
+
+ override fun setDetailShowing(showing: Boolean) {
+ isQSDetailShowing = showing
+ updateBottomSpacing()
+ }
+
+ private fun updateBottomSpacing() {
+ val (containerPadding, notificationsMargin) = calculateBottomSpacing()
+ var qsScrollPaddingBottom = 0
+ if (!(splitShadeEnabled || isQSCustomizing || isQSDetailShowing || isGestureNavigation ||
+ taskbarVisible)) {
+ // no taskbar, portrait, navigation buttons enabled:
+ // padding is needed so QS can scroll up over bottom insets - to reach the point when
+ // the whole QS is above bottom insets
+ qsScrollPaddingBottom = bottomStableInsets
+ }
+ mView.setPadding(0, 0, 0, containerPadding)
+ mView.setNotificationsMarginBottom(notificationsMargin)
+ mView.setQSScrollPaddingBottom(qsScrollPaddingBottom)
+ }
+
+ private fun calculateBottomSpacing(): Pair<Int, Int> {
+ val containerPadding: Int
+ var stackScrollMargin = notificationsBottomMargin
+ if (splitShadeEnabled) {
+ if (isGestureNavigation) {
+ // only default cutout padding, taskbar always hides
+ containerPadding = bottomCutoutInsets
+ } else if (taskbarVisible) {
+ // navigation buttons + visible taskbar means we're NOT on homescreen
+ containerPadding = bottomStableInsets
+ } else {
+ // navigation buttons + hidden taskbar means we're on homescreen
+ containerPadding = 0
+ // we need extra margin for notifications as navigation buttons are below them
+ stackScrollMargin = bottomStableInsets + notificationsBottomMargin
+ }
+ } else {
+ if (isQSCustomizing || isQSDetailShowing) {
+ // Clear out bottom paddings/margins so the qs customization can be full height.
+ containerPadding = 0
+ stackScrollMargin = 0
+ } else if (isGestureNavigation) {
+ containerPadding = bottomCutoutInsets
+ } else if (taskbarVisible) {
+ containerPadding = bottomStableInsets
+ } else {
+ containerPadding = 0
+ }
+ }
+ return containerPadding to stackScrollMargin
+ }
+}
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationsQuickSettingsContainer.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationsQuickSettingsContainer.java
index 68e28cd..9210a8b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationsQuickSettingsContainer.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationsQuickSettingsContainer.java
@@ -33,6 +33,7 @@
import java.util.ArrayList;
import java.util.Comparator;
+import java.util.function.Consumer;
/**
* The container with notification stack scroller and quick settings inside.
@@ -43,17 +44,15 @@
private View mQsFrame;
private View mStackScroller;
private View mKeyguardStatusBar;
- private boolean mQsExpanded;
- private boolean mCustomizerAnimating;
- private boolean mCustomizing;
- private boolean mDetailShowing;
- private int mBottomPadding;
private int mStackScrollerMargin;
private ArrayList<View> mDrawingOrderedChildren = new ArrayList<>();
private ArrayList<View> mLayoutDrawingOrder = new ArrayList<>();
private final Comparator<View> mIndexComparator = Comparator.comparingInt(this::indexOfChild);
- private boolean mSplitShadeEnabled;
+ private Consumer<WindowInsets> mInsetsChangedListener = insets -> {};
+ private Consumer<QS> mQSFragmentAttachedListener = qs -> {};
+ private QS mQs;
+ private View mQSScrollView;
public NotificationsQuickSettingsContainer(Context context, AttributeSet attrs) {
super(context, attrs);
@@ -69,6 +68,59 @@
}
@Override
+ public void onFragmentViewCreated(String tag, Fragment fragment) {
+ mQs = (QS) fragment;
+ mQSFragmentAttachedListener.accept(mQs);
+ mQSScrollView = mQs.getView().findViewById(R.id.expanded_qs_scroll_view);
+ }
+
+ @Override
+ public void onHasViewsAboveShelfChanged(boolean hasViewsAboveShelf) {
+ invalidate();
+ }
+
+ public void setNotificationsMarginBottom(int margin) {
+ LayoutParams params = (LayoutParams) mStackScroller.getLayoutParams();
+ params.bottomMargin = margin;
+ mStackScroller.setLayoutParams(params);
+ }
+
+ public void setQSScrollPaddingBottom(int paddingBottom) {
+ if (mQSScrollView != null) {
+ mQSScrollView.setPaddingRelative(
+ mQSScrollView.getPaddingLeft(),
+ mQSScrollView.getPaddingTop(),
+ mQSScrollView.getPaddingRight(),
+ paddingBottom
+ );
+ }
+ }
+
+ public int getDefaultNotificationsMarginBottom() {
+ return mStackScrollerMargin;
+ }
+
+ public void setInsetsChangedListener(Consumer<WindowInsets> onInsetsChangedListener) {
+ mInsetsChangedListener = onInsetsChangedListener;
+ }
+
+ public void removeOnInsetsChangedListener() {
+ mInsetsChangedListener = insets -> {};
+ }
+
+ public void setQSFragmentAttachedListener(Consumer<QS> qsFragmentAttachedListener) {
+ mQSFragmentAttachedListener = qsFragmentAttachedListener;
+ // listener might be attached after fragment is attached
+ if (mQs != null) {
+ mQSFragmentAttachedListener.accept(mQs);
+ }
+ }
+
+ public void removeQSFragmentAttachedListener() {
+ mQSFragmentAttachedListener = qs -> {};
+ }
+
+ @Override
protected void onAttachedToWindow() {
super.onAttachedToWindow();
FragmentHostManager.get(this).addTagListener(QS.TAG, this);
@@ -82,8 +134,7 @@
@Override
public WindowInsets onApplyWindowInsets(WindowInsets insets) {
- mBottomPadding = insets.getStableInsetBottom();
- setPadding(0, 0, 0, mBottomPadding);
+ mInsetsChangedListener.accept(insets);
return insets;
}
@@ -119,66 +170,4 @@
}
}
- @Override
- public void onFragmentViewCreated(String tag, Fragment fragment) {
- QS container = (QS) fragment;
- container.setContainer(this);
- }
-
- public void setQsExpanded(boolean expanded) {
- if (mQsExpanded != expanded) {
- mQsExpanded = expanded;
- invalidate();
- }
- }
-
- public void setCustomizerAnimating(boolean isAnimating) {
- if (mCustomizerAnimating != isAnimating) {
- mCustomizerAnimating = isAnimating;
- invalidate();
- }
- }
-
- public void setCustomizerShowing(boolean isShowing) {
- mCustomizing = isShowing;
- updateBottomMargin();
- }
-
- public void setDetailShowing(boolean isShowing) {
- mDetailShowing = isShowing;
- updateBottomMargin();
- }
-
- /**
- * Sets if split shade is enabled and adjusts margins/paddings depending on QS details and
- * customizer state
- */
- public void setSplitShadeEnabled(boolean splitShadeEnabled) {
- mSplitShadeEnabled = splitShadeEnabled;
- // in case device was rotated while showing QS details/customizer
- updateBottomMargin();
- }
-
- private void updateBottomMargin() {
- // in split shade, QS state changes should not influence notifications panel
- if (!mSplitShadeEnabled && (mCustomizing || mDetailShowing)) {
- // Clear out bottom paddings/margins so the qs customization can be full height.
- setPadding(0, 0, 0, 0);
- setBottomMargin(mStackScroller, 0);
- } else {
- setPadding(0, 0, 0, mBottomPadding);
- setBottomMargin(mStackScroller, mStackScrollerMargin);
- }
- }
-
- private void setBottomMargin(View v, int bottomMargin) {
- LayoutParams params = (LayoutParams) v.getLayoutParams();
- params.bottomMargin = bottomMargin;
- v.setLayoutParams(params);
- }
-
- @Override
- public void onHasViewsAboveShelfChanged(boolean hasViewsAboveShelf) {
- invalidate();
- }
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelBar.java
index 247ede9..1f1090d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelBar.java
@@ -17,8 +17,9 @@
package com.android.systemui.statusbar.phone;
import static java.lang.Float.isNaN;
+import static java.lang.annotation.RetentionPolicy.SOURCE;
-import android.annotation.CallSuper;
+import android.annotation.IntDef;
import android.content.Context;
import android.os.Bundle;
import android.os.Parcelable;
@@ -27,6 +28,10 @@
import android.view.MotionEvent;
import android.widget.FrameLayout;
+import androidx.annotation.Nullable;
+
+import java.lang.annotation.Retention;
+
public abstract class PanelBar extends FrameLayout {
public static final boolean DEBUG = false;
public static final String TAG = PanelBar.class.getSimpleName();
@@ -40,26 +45,27 @@
Log.v(TAG, String.format(fmt, args));
}
+ /** Enum for the current state of the panel. */
+ @Retention(SOURCE)
+ @IntDef({STATE_CLOSED, STATE_OPENING, STATE_OPEN})
+ @interface PanelState {}
public static final int STATE_CLOSED = 0;
public static final int STATE_OPENING = 1;
public static final int STATE_OPEN = 2;
- PanelViewController mPanel;
+ private PanelViewController mPanel;
+ @Nullable private PanelStateChangeListener mPanelStateChangeListener;
private int mState = STATE_CLOSED;
private boolean mTracking;
- public void go(int state) {
+ private void go(@PanelState int state) {
if (DEBUG) LOG("go state: %d -> %d", mState, state);
mState = state;
- if (mPanel != null) {
- mPanel.setIsShadeOpening(state == STATE_OPENING);
+ if (mPanelStateChangeListener != null) {
+ mPanelStateChangeListener.onStateChanged(state);
}
}
- protected boolean isShadeOpening() {
- return mState == STATE_OPENING;
- }
-
@Override
protected Parcelable onSaveInstanceState() {
Bundle bundle = new Bundle();
@@ -97,6 +103,11 @@
pv.setBar(this);
}
+ /** Sets the listener that will be notified of panel state changes. */
+ public void setPanelStateChangeListener(PanelStateChangeListener listener) {
+ mPanelStateChangeListener = listener;
+ }
+
public boolean panelEnabled() {
return true;
}
@@ -137,10 +148,7 @@
/**
* Percentage of panel expansion offset, caused by pulling down on a heads-up.
*/
- @CallSuper
- public void onPanelMinFractionChanged(float minFraction) {
- mPanel.setMinFraction(minFraction);
- }
+ abstract void onPanelMinFractionChanged(float minFraction);
/**
* @param frac the fraction from the expansion in [0, 1]
@@ -166,7 +174,6 @@
}
if (fullyOpened && !mTracking) {
go(STATE_OPEN);
- onPanelFullyOpened();
} else if (fullyClosed && !mTracking && mState != STATE_CLOSED) {
go(STATE_CLOSED);
onPanelCollapsed();
@@ -207,10 +214,6 @@
if (DEBUG) LOG("onPanelCollapsed");
}
- public void onPanelFullyOpened() {
- if (DEBUG) LOG("onPanelFullyOpened");
- }
-
public void onTrackingStarted() {
mTracking = true;
}
@@ -226,4 +229,10 @@
public void onClosingFinished() {
}
+
+ /** An interface that will be notified of panel state changes. */
+ public interface PanelStateChangeListener {
+ /** Called when the state changes. */
+ void onStateChanged(@PanelState int state);
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelViewController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelViewController.java
index b215515..768567b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelViewController.java
@@ -343,13 +343,6 @@
protected abstract float getOpeningHeight();
/**
- * Minimum fraction from where expansion should start. This is set when pulling down on a
- * heads-up notification.
- * @param minFraction Fraction from 0 to 1.
- */
- public abstract void setMinFraction(float minFraction);
-
- /**
* @return whether the swiping direction is upwards and above a 45 degree angle compared to the
* horizontal direction
*/
@@ -1171,11 +1164,6 @@
return new OnConfigurationChangedListener();
}
- /**
- * Set that the panel is currently opening and not fully opened or closed.
- */
- public abstract void setIsShadeOpening(boolean opening);
-
public class TouchHandler implements View.OnTouchListener {
public boolean onInterceptTouchEvent(MotionEvent event) {
if (mInstantExpanding || !mNotificationsDragEnabled || mTouchDisabled || (mMotionAborted
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarView.java
index 9ab6cdd..1cca477 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarView.java
@@ -56,7 +56,6 @@
StatusBar mBar;
- boolean mIsFullyOpenedPanel = false;
private ScrimController mScrimController;
private float mMinFraction;
private Runnable mHideExpandedRunnable = new Runnable() {
@@ -216,7 +215,6 @@
super.onPanelCollapsed();
// Close the status bar in the next frame so we can show the end of the animation.
post(mHideExpandedRunnable);
- mIsFullyOpenedPanel = false;
}
public void removePendingHideExpandedRunnables() {
@@ -224,15 +222,6 @@
}
@Override
- public void onPanelFullyOpened() {
- super.onPanelFullyOpened();
- if (!mIsFullyOpenedPanel) {
- mPanel.getView().sendAccessibilityEvent(AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED);
- }
- mIsFullyOpenedPanel = true;
- }
-
- @Override
public boolean onTouchEvent(MotionEvent event) {
boolean barConsumedEvent = mBar.interceptTouchEvent(event);
@@ -283,7 +272,6 @@
if (isNaN(minFraction)) {
throw new IllegalArgumentException("minFraction cannot be NaN");
}
- super.onPanelMinFractionChanged(minFraction);
if (mMinFraction != minFraction) {
mMinFraction = minFraction;
updateScrimFraction();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
index 3bdc08b..78ab0d7 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
@@ -199,6 +199,7 @@
import com.android.systemui.statusbar.PulseExpansionHandler;
import com.android.systemui.statusbar.StatusBarState;
import com.android.systemui.statusbar.SysuiStatusBarStateController;
+import com.android.systemui.statusbar.connectivity.NetworkController;
import com.android.systemui.statusbar.events.SystemStatusAnimationScheduler;
import com.android.systemui.statusbar.notification.DynamicPrivacyController;
import com.android.systemui.statusbar.notification.NotificationActivityStarter;
@@ -226,7 +227,6 @@
import com.android.systemui.statusbar.policy.DeviceProvisionedController.DeviceProvisionedListener;
import com.android.systemui.statusbar.policy.ExtensionController;
import com.android.systemui.statusbar.policy.KeyguardStateController;
-import com.android.systemui.statusbar.policy.NetworkController;
import com.android.systemui.statusbar.policy.UserInfoControllerImpl;
import com.android.systemui.statusbar.policy.UserSwitcherController;
import com.android.systemui.tuner.TunerService;
@@ -1161,6 +1161,8 @@
mStatusBarView = (PhoneStatusBarView) statusBarFragment.getView();
mStatusBarView.setBar(this);
mStatusBarView.setPanel(mNotificationPanelViewController);
+ mStatusBarView.setPanelStateChangeListener(
+ mNotificationPanelViewController.getPanelStateChangeListener());
mStatusBarView.setScrimController(mScrimController);
mStatusBarView.setExpansionChangedListeners(mExpansionChangedListeners);
for (ExpansionChangedListener listener : mExpansionChangedListeners) {
@@ -1550,6 +1552,9 @@
time, PowerManager.WAKE_REASON_GESTURE, "com.android.systemui:" + why);
mWakeUpComingFromTouch = true;
where.getLocationInWindow(mTmpInt2);
+
+ // NOTE, the incoming view can sometimes be the entire container... unsure if
+ // this location is valuable enough
mWakeUpTouchLocation = new PointF(mTmpInt2[0] + where.getWidth() / 2,
mTmpInt2[1] + where.getHeight() / 2);
mFalsingCollector.onScreenOnFromTouch();
@@ -1643,7 +1648,7 @@
}
});
mStatusBarKeyguardViewManager.registerStatusBar(
- /* statusBar= */ this, getBouncerContainer(),
+ /* statusBar= */ this,
mNotificationPanelViewController, mBiometricUnlockController,
mStackScroller, mKeyguardBypassController);
mKeyguardIndicationController
@@ -1678,8 +1683,8 @@
return mNotificationPanelViewController;
}
- protected ViewGroup getBouncerContainer() {
- return mNotificationShadeWindowView.findViewById(R.id.keyboard_bouncer_container);
+ public ViewGroup getBouncerContainer() {
+ return mNotificationShadeWindowViewController.getBouncerContainer();
}
public int getStatusBarHeight() {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
index 5bc97a5..07489a9 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
@@ -120,7 +120,8 @@
@Override
public void onFullyShown() {
updateStates();
- mStatusBar.wakeUpIfDozing(SystemClock.uptimeMillis(), mContainer, "BOUNCER_VISIBLE");
+ mStatusBar.wakeUpIfDozing(SystemClock.uptimeMillis(),
+ mStatusBar.getBouncerContainer(), "BOUNCER_VISIBLE");
}
@Override
@@ -174,7 +175,6 @@
private NotificationPanelViewController mNotificationPanelViewController;
private BiometricUnlockController mBiometricUnlockController;
- private ViewGroup mContainer;
private View mNotificationContainer;
protected KeyguardBouncer mBouncer;
@@ -270,14 +270,14 @@
@Override
public void registerStatusBar(StatusBar statusBar,
- ViewGroup container,
NotificationPanelViewController notificationPanelViewController,
BiometricUnlockController biometricUnlockController,
View notificationContainer,
KeyguardBypassController bypassController) {
mStatusBar = statusBar;
- mContainer = container;
mBiometricUnlockController = biometricUnlockController;
+
+ ViewGroup container = mStatusBar.getBouncerContainer();
mBouncer = mKeyguardBouncerFactory.create(container, mExpansionCallback);
mNotificationPanelViewController = notificationPanelViewController;
notificationPanelViewController.addExpansionListener(this);
@@ -356,7 +356,8 @@
} else if (mPulsing && expansion == KeyguardBouncer.EXPANSION_VISIBLE) {
// Panel expanded while pulsing but didn't translate the bouncer (because we are
// unlocked.) Let's simply wake-up to dismiss the lock screen.
- mStatusBar.wakeUpIfDozing(SystemClock.uptimeMillis(), mContainer, "BOUNCER_VISIBLE");
+ mStatusBar.wakeUpIfDozing(SystemClock.uptimeMillis(), mStatusBar.getBouncerContainer(),
+ "BOUNCER_VISIBLE");
}
}
@@ -833,7 +834,7 @@
}
public void onKeyguardFadedAway() {
- mContainer.postDelayed(() -> mNotificationShadeWindowController
+ mNotificationContainer.postDelayed(() -> mNotificationShadeWindowController
.setKeyguardFadingAway(false), 100);
ViewGroupFadeHelper.reset(mNotificationPanelViewController.getView());
mStatusBar.finishKeyguardFadingAway();
@@ -958,10 +959,6 @@
};
protected void updateStates() {
- if (mContainer == null ) {
- return;
- }
- int vis = mContainer.getSystemUiVisibility();
boolean showing = mShowing;
boolean occluded = mOccluded;
boolean bouncerShowing = mBouncer.isShowing();
@@ -973,9 +970,9 @@
(mLastBouncerDismissible || !mLastShowing || mLastRemoteInputActive)
|| mFirstUpdate) {
if (bouncerDismissible || !showing || remoteInputActive) {
- mContainer.setSystemUiVisibility(vis & ~View.STATUS_BAR_DISABLE_BACK);
+ mBouncer.setBackButtonEnabled(true);
} else {
- mContainer.setSystemUiVisibility(vis | View.STATUS_BAR_DISABLE_BACK);
+ mBouncer.setBackButtonEnabled(false);
}
}
@@ -1042,11 +1039,11 @@
if (delay == 0) {
mMakeNavigationBarVisibleRunnable.run();
} else {
- mContainer.postOnAnimationDelayed(mMakeNavigationBarVisibleRunnable,
+ mNotificationContainer.postOnAnimationDelayed(mMakeNavigationBarVisibleRunnable,
delay);
}
} else {
- mContainer.removeCallbacks(mMakeNavigationBarVisibleRunnable);
+ mNotificationContainer.removeCallbacks(mMakeNavigationBarVisibleRunnable);
mStatusBar.getNotificationShadeWindowView().getWindowInsetsController()
.hide(navigationBars());
}
@@ -1149,7 +1146,6 @@
public void showBouncerMessage(String message, ColorStateList colorState) {
if (isShowingAlternateAuth()) {
if (mKeyguardMessageAreaController != null) {
- mKeyguardMessageAreaController.setNextMessageColor(colorState);
mKeyguardMessageAreaController.setMessage(message);
}
} else {
@@ -1372,4 +1368,4 @@
void requestUdfps(boolean requestUdfps, int color);
}
-}
\ No newline at end of file
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarSignalPolicy.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarSignalPolicy.java
index 7136432..fbd9ef7 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarSignalPolicy.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarSignalPolicy.java
@@ -26,11 +26,11 @@
import com.android.systemui.R;
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.flags.FeatureFlags;
-import com.android.systemui.statusbar.policy.NetworkController;
-import com.android.systemui.statusbar.policy.NetworkController.IconState;
-import com.android.systemui.statusbar.policy.NetworkController.MobileDataIndicators;
-import com.android.systemui.statusbar.policy.NetworkController.WifiIndicators;
-import com.android.systemui.statusbar.policy.NetworkControllerImpl;
+import com.android.systemui.statusbar.connectivity.NetworkController;
+import com.android.systemui.statusbar.connectivity.NetworkController.IconState;
+import com.android.systemui.statusbar.connectivity.NetworkController.MobileDataIndicators;
+import com.android.systemui.statusbar.connectivity.NetworkController.WifiIndicators;
+import com.android.systemui.statusbar.connectivity.NetworkControllerImpl;
import com.android.systemui.statusbar.policy.SecurityController;
import com.android.systemui.tuner.TunerService;
import com.android.systemui.tuner.TunerService.Tunable;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowController.java
index aec27d0..b708861 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowController.java
@@ -208,6 +208,12 @@
apply(mCurrentState);
}
+ /** Sets whether there is currently an ongoing call. */
+ public void setIsCallOngoing(boolean isCallOngoing) {
+ mCurrentState.mIsCallOngoing = isCallOngoing;
+ apply(mCurrentState);
+ }
+
/**
* Return the container in which we should run launch animations started from the status bar and
* expanding into the opening window.
@@ -248,10 +254,13 @@
private static class State {
boolean mForceStatusBarVisible;
boolean mIsLaunchAnimationRunning;
+ boolean mIsCallOngoing;
}
private void applyForceStatusBarVisibleFlag(State state) {
- if (state.mForceStatusBarVisible || state.mIsLaunchAnimationRunning) {
+ if (state.mForceStatusBarVisible
+ || state.mIsLaunchAnimationRunning
+ || state.mIsCallOngoing) {
mLpChanged.privateFlags |= PRIVATE_FLAG_FORCE_SHOW_STATUS_BAR;
} else {
mLpChanged.privateFlags &= ~PRIVATE_FLAG_FORCE_SHOW_STATUS_BAR;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarPhoneModule.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarPhoneModule.java
index 0d43b93..5689707 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarPhoneModule.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarPhoneModule.java
@@ -62,6 +62,7 @@
import com.android.systemui.statusbar.OperatorNameViewController;
import com.android.systemui.statusbar.PulseExpansionHandler;
import com.android.systemui.statusbar.SysuiStatusBarStateController;
+import com.android.systemui.statusbar.connectivity.NetworkController;
import com.android.systemui.statusbar.events.SystemStatusAnimationScheduler;
import com.android.systemui.statusbar.notification.DynamicPrivacyController;
import com.android.systemui.statusbar.notification.NotificationEntryManager;
@@ -104,7 +105,6 @@
import com.android.systemui.statusbar.policy.DeviceProvisionedController;
import com.android.systemui.statusbar.policy.ExtensionController;
import com.android.systemui.statusbar.policy.KeyguardStateController;
-import com.android.systemui.statusbar.policy.NetworkController;
import com.android.systemui.statusbar.policy.UserInfoControllerImpl;
import com.android.systemui.statusbar.policy.UserSwitcherController;
import com.android.systemui.tuner.TunerService;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarViewModule.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarViewModule.java
index ecf3b86..2791678 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarViewModule.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarViewModule.java
@@ -29,9 +29,9 @@
import com.android.systemui.statusbar.NotificationShelfController;
import com.android.systemui.statusbar.notification.row.dagger.NotificationShelfComponent;
import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout;
-import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController;
import com.android.systemui.statusbar.phone.NotificationPanelView;
import com.android.systemui.statusbar.phone.NotificationShadeWindowView;
+import com.android.systemui.statusbar.phone.NotificationsQuickSettingsContainer;
import com.android.systemui.statusbar.phone.TapAgainView;
import com.android.systemui.util.InjectionInflationController;
@@ -67,8 +67,8 @@
@Provides
@StatusBarComponent.StatusBarScope
public static NotificationStackScrollLayout providesNotificationStackScrollLayout(
- NotificationStackScrollLayoutController notificationStackScrollLayoutController) {
- return notificationStackScrollLayoutController.getView();
+ NotificationShadeWindowView notificationShadeWindowView) {
+ return notificationShadeWindowView.findViewById(R.id.notification_stack_scroller);
}
/** */
@@ -149,4 +149,12 @@
public static TapAgainView getTapAgainView(NotificationPanelView npv) {
return npv.getTapAgainView();
}
+
+ /** */
+ @Provides
+ @StatusBarComponent.StatusBarScope
+ public static NotificationsQuickSettingsContainer getNotificationsQuickSettingsContainer(
+ NotificationShadeWindowView notificationShadeWindowView) {
+ return notificationShadeWindowView.findViewById(R.id.notification_container_parent);
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallController.kt
index eeff010..b7c80de 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallController.kt
@@ -26,17 +26,23 @@
import android.view.View
import androidx.annotation.VisibleForTesting
import com.android.internal.jank.InteractionJankMonitor
+import com.android.systemui.Dumpable
import com.android.systemui.R
import com.android.systemui.animation.ActivityLaunchAnimator
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Main
+import com.android.systemui.dump.DumpManager
import com.android.systemui.plugins.ActivityStarter
import com.android.systemui.flags.FeatureFlags
import com.android.systemui.statusbar.notification.collection.NotificationEntry
import com.android.systemui.statusbar.notification.collection.notifcollection.CommonNotifCollection
import com.android.systemui.statusbar.notification.collection.notifcollection.NotifCollectionListener
+import com.android.systemui.statusbar.phone.StatusBarWindowController
import com.android.systemui.statusbar.policy.CallbackController
import com.android.systemui.util.time.SystemClock
+import java.io.FileDescriptor
+import java.io.PrintWriter
+import java.util.Optional
import java.util.concurrent.Executor
import javax.inject.Inject
@@ -51,13 +57,15 @@
private val activityStarter: ActivityStarter,
@Main private val mainExecutor: Executor,
private val iActivityManager: IActivityManager,
- private val logger: OngoingCallLogger
-) : CallbackController<OngoingCallListener> {
+ private val logger: OngoingCallLogger,
+ private val dumpManager: DumpManager,
+ private val statusBarWindowController: Optional<StatusBarWindowController>,
+) : CallbackController<OngoingCallListener>, Dumpable {
/** Non-null if there's an active call notification. */
private var callNotificationInfo: CallNotificationInfo? = null
/** True if the application managing the call is visible to the user. */
- private var isCallAppVisible: Boolean = true
+ private var isCallAppVisible: Boolean = false
private var chipView: View? = null
private var uidObserver: IUidObserver.Stub? = null
@@ -103,16 +111,7 @@
}
}
- // Fix for b/199600334
- override fun onEntryCleanUp(entry: NotificationEntry) {
- removeChipIfNeeded(entry)
- }
-
override fun onEntryRemoved(entry: NotificationEntry, reason: Int) {
- removeChipIfNeeded(entry)
- }
-
- private fun removeChipIfNeeded(entry: NotificationEntry) {
if (entry.sbn.key == callNotificationInfo?.key) {
removeChip()
}
@@ -120,6 +119,7 @@
}
fun init() {
+ dumpManager.registerDumpable(this)
if (featureFlags.isOngoingCallStatusBarChipEnabled) {
notifCollection.addCollectionListener(notifListener)
}
@@ -204,7 +204,7 @@
}
setUpUidObserver(currentCallNotificationInfo)
-
+ statusBarWindowController.ifPresent { it.setIsCallOngoing(true) }
mListeners.forEach { l -> l.onOngoingCallStateChanged(animate = true) }
} else {
// If we failed to update the chip, don't store the call info. Then [hasOngoingCall]
@@ -271,6 +271,7 @@
private fun removeChip() {
callNotificationInfo = null
tearDownChipView()
+ statusBarWindowController.ifPresent { it.setIsCallOngoing(false) }
mListeners.forEach { l -> l.onOngoingCallStateChanged(animate = true) }
if (uidObserver != null) {
iActivityManager.unregisterUidObserver(uidObserver)
@@ -299,6 +300,11 @@
*/
fun hasValidStartTime(): Boolean = callStartTime > 0
}
+
+ override fun dump(fd: FileDescriptor, pw: PrintWriter, args: Array<out String>) {
+ pw.println("Active call notification: $callNotificationInfo")
+ pw.println("Call app visible: $isCallAppVisible")
+ }
}
private fun isCallNotification(entry: NotificationEntry): Boolean {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/DevicePostureController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/DevicePostureController.java
index fbfa5e5..bbba19d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/DevicePostureController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/DevicePostureController.java
@@ -46,10 +46,31 @@
int DEVICE_POSTURE_HALF_OPENED = 2;
int DEVICE_POSTURE_OPENED = 3;
int DEVICE_POSTURE_FLIPPED = 4;
+ int SUPPORTED_POSTURES_SIZE = DEVICE_POSTURE_FLIPPED + 1;
/** Return the current device posture. */
@DevicePostureInt int getDevicePosture();
+ /**
+ * String representation of DevicePostureInt.
+ */
+ static String devicePostureToString(@DevicePostureInt int posture) {
+ switch (posture) {
+ case DEVICE_POSTURE_CLOSED:
+ return "DEVICE_POSTURE_CLOSED";
+ case DEVICE_POSTURE_HALF_OPENED:
+ return "DEVICE_POSTURE_HALF_OPENED";
+ case DEVICE_POSTURE_OPENED:
+ return "DEVICE_POSTURE_OPENED";
+ case DEVICE_POSTURE_FLIPPED:
+ return "DEVICE_POSTURE_FLIPPED";
+ case DEVICE_POSTURE_UNKNOWN:
+ return "DEVICE_POSTURE_UNKNOWN";
+ default:
+ return "UNSUPPORTED POSTURE posture=" + posture;
+ }
+ }
+
/** Callback to be notified about device posture changes. */
interface Callback {
/** Called when the posture changes. */
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardQsUserSwitchController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardQsUserSwitchController.java
index d838a05..b4aba38 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardQsUserSwitchController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardQsUserSwitchController.java
@@ -35,10 +35,12 @@
import com.android.settingslib.drawable.CircleFramedDrawable;
import com.android.systemui.R;
import com.android.systemui.dagger.qualifiers.Main;
+import com.android.systemui.flags.FeatureFlags;
import com.android.systemui.keyguard.ScreenLifecycle;
import com.android.systemui.plugins.FalsingManager;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.qs.tiles.UserDetailView;
+import com.android.systemui.qs.user.UserSwitchDialogController;
import com.android.systemui.statusbar.SysuiStatusBarStateController;
import com.android.systemui.statusbar.notification.AnimatableProperty;
import com.android.systemui.statusbar.notification.PropertyAnimator;
@@ -76,6 +78,8 @@
private final ConfigurationController mConfigurationController;
private final KeyguardVisibilityHelper mKeyguardVisibilityHelper;
private final KeyguardUserDetailAdapter mUserDetailAdapter;
+ private final FeatureFlags mFeatureFlags;
+ private final UserSwitchDialogController mUserSwitchDialogController;
private NotificationPanelViewController mNotificationPanelViewController;
private UserAvatarView mUserAvatarView;
UserSwitcherController.UserRecord mCurrentUser;
@@ -124,7 +128,9 @@
SysuiStatusBarStateController statusBarStateController,
DozeParameters dozeParameters,
Provider<UserDetailView.Adapter> userDetailViewAdapterProvider,
- UnlockedScreenOffAnimationController unlockedScreenOffAnimationController) {
+ UnlockedScreenOffAnimationController unlockedScreenOffAnimationController,
+ FeatureFlags featureFlags,
+ UserSwitchDialogController userSwitchDialogController) {
super(view);
if (DEBUG) Log.d(TAG, "New KeyguardQsUserSwitchController");
mContext = context;
@@ -139,6 +145,8 @@
keyguardStateController, dozeParameters,
unlockedScreenOffAnimationController, /* animateYPos= */ false);
mUserDetailAdapter = new KeyguardUserDetailAdapter(context, userDetailViewAdapterProvider);
+ mFeatureFlags = featureFlags;
+ mUserSwitchDialogController = userSwitchDialogController;
}
@Override
@@ -162,7 +170,11 @@
}
// Tapping anywhere in the view will open QS user panel
- openQsUserPanel();
+ if (mFeatureFlags.useNewUserSwitcher()) {
+ mUserSwitchDialogController.showDialog(mView);
+ } else {
+ openQsUserPanel();
+ }
});
mUserAvatarView.setAccessibilityDelegate(new View.AccessibilityDelegate() {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/dagger/StatusBarPolicyModule.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/dagger/StatusBarPolicyModule.java
index 1eec639..9290879 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/dagger/StatusBarPolicyModule.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/dagger/StatusBarPolicyModule.java
@@ -23,7 +23,9 @@
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.settings.UserTracker;
-import com.android.systemui.statusbar.policy.AccessPointControllerImpl;
+import com.android.systemui.statusbar.connectivity.AccessPointControllerImpl;
+import com.android.systemui.statusbar.connectivity.NetworkController;
+import com.android.systemui.statusbar.connectivity.NetworkControllerImpl;
import com.android.systemui.statusbar.policy.BluetoothController;
import com.android.systemui.statusbar.policy.BluetoothControllerImpl;
import com.android.systemui.statusbar.policy.CastController;
@@ -42,8 +44,6 @@
import com.android.systemui.statusbar.policy.KeyguardStateControllerImpl;
import com.android.systemui.statusbar.policy.LocationController;
import com.android.systemui.statusbar.policy.LocationControllerImpl;
-import com.android.systemui.statusbar.policy.NetworkController;
-import com.android.systemui.statusbar.policy.NetworkControllerImpl;
import com.android.systemui.statusbar.policy.NextAlarmController;
import com.android.systemui.statusbar.policy.NextAlarmControllerImpl;
import com.android.systemui.statusbar.policy.RotationLockController;
diff --git a/packages/SystemUI/src/com/android/systemui/unfold/ShellUnfoldProgressProvider.kt b/packages/SystemUI/src/com/android/systemui/unfold/ShellUnfoldProgressProvider.kt
new file mode 100644
index 0000000..4a88435
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/unfold/ShellUnfoldProgressProvider.kt
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.unfold
+
+import com.android.systemui.unfold.UnfoldTransitionProgressProvider.TransitionProgressListener
+import com.android.wm.shell.unfold.ShellUnfoldProgressProvider
+import com.android.wm.shell.unfold.ShellUnfoldProgressProvider.UnfoldListener
+import java.util.concurrent.Executor
+
+class ShellUnfoldProgressProvider(
+ private val unfoldProgressProvider: UnfoldTransitionProgressProvider
+) : ShellUnfoldProgressProvider {
+
+ override fun addListener(executor: Executor, listener: UnfoldListener) {
+ unfoldProgressProvider.addCallback(object : TransitionProgressListener {
+ override fun onTransitionStarted() {
+ executor.execute {
+ listener.onStateChangeStarted()
+ }
+ }
+
+ override fun onTransitionProgress(progress: Float) {
+ executor.execute {
+ listener.onStateChangeProgress(progress)
+ }
+ }
+
+ override fun onTransitionFinished() {
+ executor.execute {
+ listener.onStateChangeFinished()
+ }
+ }
+ })
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/unfold/UnfoldTransitionModule.kt b/packages/SystemUI/src/com/android/systemui/unfold/UnfoldTransitionModule.kt
new file mode 100644
index 0000000..b23aa20
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/unfold/UnfoldTransitionModule.kt
@@ -0,0 +1,74 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.unfold
+
+import android.content.Context
+import android.hardware.SensorManager
+import android.hardware.devicestate.DeviceStateManager
+import android.os.Handler
+import com.android.systemui.dagger.qualifiers.Main
+import com.android.systemui.keyguard.LifecycleScreenStatusProvider
+import com.android.systemui.unfold.config.UnfoldTransitionConfig
+import com.android.wm.shell.unfold.ShellUnfoldProgressProvider
+import dagger.Lazy
+import dagger.Module
+import dagger.Provides
+import java.util.Optional
+import java.util.concurrent.Executor
+import javax.inject.Singleton
+
+@Module
+class UnfoldTransitionModule {
+
+ @Provides
+ @Singleton
+ fun provideUnfoldTransitionProgressProvider(
+ context: Context,
+ config: UnfoldTransitionConfig,
+ screenStatusProvider: LifecycleScreenStatusProvider,
+ deviceStateManager: DeviceStateManager,
+ sensorManager: SensorManager,
+ @Main executor: Executor,
+ @Main handler: Handler
+ ): UnfoldTransitionProgressProvider =
+ createUnfoldTransitionProgressProvider(
+ context,
+ config,
+ screenStatusProvider,
+ deviceStateManager,
+ sensorManager,
+ handler,
+ executor
+ )
+
+ @Provides
+ @Singleton
+ fun provideUnfoldTransitionConfig(context: Context): UnfoldTransitionConfig =
+ createConfig(context)
+
+ @Provides
+ @Singleton
+ fun provideShellProgressProvider(
+ config: UnfoldTransitionConfig,
+ provider: Lazy<UnfoldTransitionProgressProvider>
+ ): Optional<ShellUnfoldProgressProvider> =
+ if (config.isEnabled) {
+ Optional.ofNullable(ShellUnfoldProgressProvider(provider.get()))
+ } else {
+ Optional.empty()
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/util/WallpaperController.kt b/packages/SystemUI/src/com/android/systemui/util/WallpaperController.kt
index a981045..2c01a70 100644
--- a/packages/SystemUI/src/com/android/systemui/util/WallpaperController.kt
+++ b/packages/SystemUI/src/com/android/systemui/util/WallpaperController.kt
@@ -40,8 +40,8 @@
this.wallpaperInfo = wallpaperInfo
}
- private val shouldUseDefaultDeviceStateChangeTransition: Boolean
- get() = wallpaperInfo?.shouldUseDefaultDeviceStateChangeTransition()
+ private val shouldUseDefaultDisplayStateChangeTransition: Boolean
+ get() = wallpaperInfo?.shouldUseDefaultDisplayStateChangeTransition()
?: true
fun setNotificationShadeZoom(zoomOut: Float) {
@@ -50,7 +50,7 @@
}
fun setUnfoldTransitionZoom(zoomOut: Float) {
- if (shouldUseDefaultDeviceStateChangeTransition) {
+ if (shouldUseDefaultDisplayStateChangeTransition) {
unfoldTransitionZoomOut = zoomOut
updateZoom()
}
diff --git a/packages/SystemUI/src/com/android/systemui/util/sensors/PostureDependentProximitySensor.java b/packages/SystemUI/src/com/android/systemui/util/sensors/PostureDependentProximitySensor.java
new file mode 100644
index 0000000..28cba8e
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/util/sensors/PostureDependentProximitySensor.java
@@ -0,0 +1,109 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.util.sensors;
+
+import android.util.Log;
+
+import androidx.annotation.NonNull;
+
+import com.android.systemui.statusbar.policy.DevicePostureController;
+import com.android.systemui.util.concurrency.DelayableExecutor;
+import com.android.systemui.util.concurrency.Execution;
+
+import javax.inject.Inject;
+
+/**
+ * Proximity sensor that changes proximity sensor usage based on the current posture.
+ * Posture -> prox sensor mapping can be found in SystemUI config overlays at:
+ * - proximity_sensor_posture_mapping
+ * - proximity_sensor_secondary_posture_mapping.
+ * where the array indices correspond to the following postures:
+ * [UNKNOWN, CLOSED, HALF_OPENED, OPENED]
+ */
+class PostureDependentProximitySensor extends ProximitySensorImpl {
+ private final ThresholdSensor[] mPostureToPrimaryProxSensorMap;
+ private final ThresholdSensor[] mPostureToSecondaryProxSensorMap;
+
+ @Inject
+ PostureDependentProximitySensor(
+ @PrimaryProxSensor ThresholdSensor[] postureToPrimaryProxSensorMap,
+ @SecondaryProxSensor ThresholdSensor[] postureToSecondaryProxSensorMap,
+ @NonNull DelayableExecutor delayableExecutor,
+ @NonNull Execution execution,
+ @NonNull DevicePostureController devicePostureController
+ ) {
+ super(
+ postureToPrimaryProxSensorMap[0],
+ postureToSecondaryProxSensorMap[0],
+ delayableExecutor,
+ execution
+ );
+ mPostureToPrimaryProxSensorMap = postureToPrimaryProxSensorMap;
+ mPostureToSecondaryProxSensorMap = postureToSecondaryProxSensorMap;
+ mDevicePosture = devicePostureController.getDevicePosture();
+ devicePostureController.addCallback(mDevicePostureCallback);
+
+ chooseSensors();
+ }
+ private void chooseSensors() {
+ if (mDevicePosture >= mPostureToPrimaryProxSensorMap.length
+ || mDevicePosture >= mPostureToSecondaryProxSensorMap.length) {
+ Log.e("PostureDependentProxSensor",
+ "unsupported devicePosture=" + mDevicePosture);
+ return;
+ }
+
+ ThresholdSensor newPrimaryProx = mPostureToPrimaryProxSensorMap[mDevicePosture];
+ ThresholdSensor newSecondaryProx = mPostureToSecondaryProxSensorMap[mDevicePosture];
+
+ if (newPrimaryProx != mPrimaryThresholdSensor
+ || newSecondaryProx != mSecondaryThresholdSensor) {
+ logDebug("Register new proximity sensors newPosture="
+ + DevicePostureController.devicePostureToString(mDevicePosture));
+ unregisterInternal();
+
+ if (mPrimaryThresholdSensor != null) {
+ mPrimaryThresholdSensor.unregister(mPrimaryEventListener);
+ }
+ if (mSecondaryThresholdSensor != null) {
+ mSecondaryThresholdSensor.unregister(mSecondaryEventListener);
+ }
+
+ mPrimaryThresholdSensor = newPrimaryProx;
+ mSecondaryThresholdSensor = newSecondaryProx;
+
+ mInitializedListeners = false;
+ registerInternal();
+ }
+ }
+
+ private final DevicePostureController.Callback mDevicePostureCallback =
+ posture -> {
+ if (mDevicePosture == posture) {
+ return;
+ }
+
+ mDevicePosture = posture;
+ chooseSensors();
+ };
+
+ @Override
+ public String toString() {
+ return String.format("{posture=%s, proximitySensor=%s}",
+ DevicePostureController.devicePostureToString(mDevicePosture), super.toString());
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/util/sensors/ProximityCheck.java b/packages/SystemUI/src/com/android/systemui/util/sensors/ProximityCheck.java
new file mode 100644
index 0000000..a8a6341
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/util/sensors/ProximityCheck.java
@@ -0,0 +1,90 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.util.sensors;
+
+import com.android.systemui.dagger.qualifiers.Main;
+import com.android.systemui.util.concurrency.DelayableExecutor;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.function.Consumer;
+
+import javax.inject.Inject;
+
+/**
+ * Convenience class allowing for briefly checking the proximity sensor.
+ */
+public class ProximityCheck implements Runnable {
+
+ private final ProximitySensor mSensor;
+ private final DelayableExecutor mDelayableExecutor;
+ private List<Consumer<Boolean>> mCallbacks = new ArrayList<>();
+ private final ThresholdSensor.Listener mListener;
+ private final AtomicBoolean mRegistered = new AtomicBoolean();
+
+ @Inject
+ public ProximityCheck(
+ ProximitySensor sensor,
+ @Main DelayableExecutor delayableExecutor) {
+ mSensor = sensor;
+ mSensor.setTag("prox_check");
+ mDelayableExecutor = delayableExecutor;
+ mListener = this::onProximityEvent;
+ }
+
+ /** Set a descriptive tag for the sensors registration. */
+ public void setTag(String tag) {
+ mSensor.setTag(tag);
+ }
+
+ @Override
+ public void run() {
+ unregister();
+ onProximityEvent(null);
+ }
+
+ /**
+ * Query the proximity sensor, timing out if no result.
+ */
+ public void check(long timeoutMs, Consumer<Boolean> callback) {
+ if (!mSensor.isLoaded()) {
+ callback.accept(null);
+ return;
+ }
+ mCallbacks.add(callback);
+ if (!mRegistered.getAndSet(true)) {
+ mSensor.register(mListener);
+ mDelayableExecutor.executeDelayed(this, timeoutMs);
+ }
+ }
+
+ private void unregister() {
+ mSensor.unregister(mListener);
+ mRegistered.set(false);
+ }
+
+ private void onProximityEvent(ThresholdSensorEvent proximityEvent) {
+ mCallbacks.forEach(
+ booleanConsumer ->
+ booleanConsumer.accept(
+ proximityEvent == null ? null : proximityEvent.getBelow()));
+ mCallbacks.clear();
+ unregister();
+ mRegistered.set(false);
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/util/sensors/ProximitySensor.java b/packages/SystemUI/src/com/android/systemui/util/sensors/ProximitySensor.java
index bd11039..d3f1c93 100644
--- a/packages/SystemUI/src/com/android/systemui/util/sensors/ProximitySensor.java
+++ b/packages/SystemUI/src/com/android/systemui/util/sensors/ProximitySensor.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2019 The Android Open Source Project
+ * Copyright (C) 2021 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -16,149 +16,24 @@
package com.android.systemui.util.sensors;
-import android.hardware.SensorManager;
-import android.util.Log;
-
-import com.android.internal.annotations.VisibleForTesting;
-import com.android.systemui.dagger.qualifiers.Main;
-import com.android.systemui.util.concurrency.DelayableExecutor;
-import com.android.systemui.util.concurrency.Execution;
-
-import java.util.ArrayList;
-import java.util.List;
-import java.util.concurrent.atomic.AtomicBoolean;
-import java.util.function.Consumer;
-
-import javax.inject.Inject;
-
/**
- * Wrapper around SensorManager customized for the Proximity sensor.
- *
- * The ProximitySensor supports the concept of a primary and a
- * secondary hardware sensor. The primary sensor is used for a first
- * pass check if the phone covered. When triggered, it then checks
- * the secondary sensor for confirmation (if there is one). It does
- * not send a proximity event until the secondary sensor confirms (or
- * rejects) the reading. The secondary sensor is, in fact, the source
- * of truth.
- *
- * This is necessary as sometimes keeping the secondary sensor on for
- * extends periods is undesirable. It may, however, result in increased
- * latency for proximity readings.
- *
- * Phones should configure this via a config.xml overlay. If no
- * proximity sensor is set (primary or secondary) we fall back to the
- * default Sensor.TYPE_PROXIMITY. If proximity_sensor_type is set in
- * config.xml, that will be used as the primary sensor. If
- * proximity_sensor_secondary_type is set, that will function as the
- * secondary sensor. If no secondary is set, only the primary will be
- * used.
+ * Wrapper class for a dual ProximitySensor which supports a secondary sensor gated upon the
+ * primary).
*/
-public class ProximitySensor implements ThresholdSensor {
- private static final String TAG = "ProxSensor";
- private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
- private static final long SECONDARY_PING_INTERVAL_MS = 5000;
-
- private final ThresholdSensor mPrimaryThresholdSensor;
- private final ThresholdSensor mSecondaryThresholdSensor;
- private final DelayableExecutor mDelayableExecutor;
- private final Execution mExecution;
- private final List<ThresholdSensor.Listener> mListeners = new ArrayList<>();
- private String mTag = null;
- @VisibleForTesting protected boolean mPaused;
- private ThresholdSensorEvent mLastPrimaryEvent;
- @VisibleForTesting
- ThresholdSensorEvent mLastEvent;
- private boolean mRegistered;
- private final AtomicBoolean mAlerting = new AtomicBoolean();
- private Runnable mCancelSecondaryRunnable;
- private boolean mInitializedListeners = false;
- private boolean mSecondarySafe = false;
-
- private final ThresholdSensor.Listener mPrimaryEventListener = this::onPrimarySensorEvent;
-
- private final ThresholdSensor.Listener mSecondaryEventListener =
- new ThresholdSensor.Listener() {
- @Override
- public void onThresholdCrossed(ThresholdSensorEvent event) {
- // If we no longer have a "below" signal and the secondary sensor is not
- // considered "safe", then we need to turn it off.
- if (!mSecondarySafe
- && (mLastPrimaryEvent == null
- || !mLastPrimaryEvent.getBelow()
- || !event.getBelow())) {
- chooseSensor();
- if (mLastPrimaryEvent == null || !mLastPrimaryEvent.getBelow()) {
- // Only check the secondary as long as the primary thinks we're near.
- if (mCancelSecondaryRunnable != null) {
- mCancelSecondaryRunnable.run();
- mCancelSecondaryRunnable = null;
- }
- return;
- } else {
- // Check this sensor again in a moment.
- mCancelSecondaryRunnable = mDelayableExecutor.executeDelayed(() -> {
- // This is safe because we know that mSecondaryThresholdSensor
- // is loaded, otherwise we wouldn't be here.
- mPrimaryThresholdSensor.pause();
- mSecondaryThresholdSensor.resume();
- },
- SECONDARY_PING_INTERVAL_MS);
- }
- }
- logDebug("Secondary sensor event: " + event.getBelow() + ".");
-
- if (!mPaused) {
- onSensorEvent(event);
- }
- }
- };
-
- @Inject
- public ProximitySensor(
- @PrimaryProxSensor ThresholdSensor primary,
- @SecondaryProxSensor ThresholdSensor secondary,
- @Main DelayableExecutor delayableExecutor,
- Execution execution) {
- mPrimaryThresholdSensor = primary;
- mSecondaryThresholdSensor = secondary;
- mDelayableExecutor = delayableExecutor;
- mExecution = execution;
- }
-
- @Override
- public void setTag(String tag) {
- mTag = tag;
- mPrimaryThresholdSensor.setTag(tag + ":primary");
- mSecondaryThresholdSensor.setTag(tag + ":secondary");
- }
-
- @Override
- public void setDelay(int delay) {
- mExecution.assertIsMainThread();
- mPrimaryThresholdSensor.setDelay(delay);
- mSecondaryThresholdSensor.setDelay(delay);
- }
+public interface ProximitySensor extends ThresholdSensor {
+ /**
+ * Returns true if we are registered with the SensorManager.
+ */
+ boolean isRegistered();
/**
- * Unregister with the {@link SensorManager} without unsetting listeners on this object.
+ * Whether the proximity sensor reports near. Can return null if no information has been
+ * received yet.
*/
- @Override
- public void pause() {
- mExecution.assertIsMainThread();
- mPaused = true;
- unregisterInternal();
- }
+ Boolean isNear();
- /**
- * Register with the {@link SensorManager}. No-op if no listeners are registered on this object.
- */
- @Override
- public void resume() {
- mExecution.assertIsMainThread();
- mPaused = false;
- registerInternal();
- }
+ /** Update all listeners with the last value this class received from the sensor. */
+ void alertListeners();
/**
* Sets that it is safe to leave the secondary sensor on indefinitely.
@@ -166,249 +41,5 @@
* The secondary sensor will be turned on if there are any registered listeners, regardless
* of what is reported by the primary sensor.
*/
- public void setSecondarySafe(boolean safe) {
- mSecondarySafe = mSecondaryThresholdSensor.isLoaded() && safe;
- chooseSensor();
- }
-
- /**
- * Returns true if we are registered with the SensorManager.
- */
- public boolean isRegistered() {
- return mRegistered;
- }
-
- /**
- * Returns {@code false} if a Proximity sensor is not available.
- */
- @Override
- public boolean isLoaded() {
- return mPrimaryThresholdSensor.isLoaded();
- }
-
- /**
- * Add a listener.
- *
- * Registers itself with the {@link SensorManager} if this is the first listener
- * added. If the ProximitySensor is paused, it will be registered when resumed.
- */
- @Override
- public void register(ThresholdSensor.Listener listener) {
- mExecution.assertIsMainThread();
- if (!isLoaded()) {
- return;
- }
-
- if (mListeners.contains(listener)) {
- logDebug("ProxListener registered multiple times: " + listener);
- } else {
- mListeners.add(listener);
- }
- registerInternal();
- }
-
- protected void registerInternal() {
- mExecution.assertIsMainThread();
- if (mRegistered || mPaused || mListeners.isEmpty()) {
- return;
- }
- if (!mInitializedListeners) {
- mPrimaryThresholdSensor.pause();
- mSecondaryThresholdSensor.pause();
- mPrimaryThresholdSensor.register(mPrimaryEventListener);
- mSecondaryThresholdSensor.register(mSecondaryEventListener);
- mInitializedListeners = true;
- }
- logDebug("Registering sensor listener");
-
- mRegistered = true;
- chooseSensor();
- }
-
- private void chooseSensor() {
- mExecution.assertIsMainThread();
- if (!mRegistered || mPaused || mListeners.isEmpty()) {
- return;
- }
- if (mSecondarySafe) {
- mSecondaryThresholdSensor.resume();
- mPrimaryThresholdSensor.pause();
- } else {
- mPrimaryThresholdSensor.resume();
- mSecondaryThresholdSensor.pause();
- }
- }
-
- /**
- * Remove a listener.
- *
- * If all listeners are removed from an instance of this class,
- * it will unregister itself with the SensorManager.
- */
- @Override
- public void unregister(ThresholdSensor.Listener listener) {
- mExecution.assertIsMainThread();
- mListeners.remove(listener);
- if (mListeners.size() == 0) {
- unregisterInternal();
- }
- }
-
- protected void unregisterInternal() {
- mExecution.assertIsMainThread();
- if (!mRegistered) {
- return;
- }
- logDebug("unregistering sensor listener");
- mPrimaryThresholdSensor.pause();
- mSecondaryThresholdSensor.pause();
- if (mCancelSecondaryRunnable != null) {
- mCancelSecondaryRunnable.run();
- mCancelSecondaryRunnable = null;
- }
- mLastPrimaryEvent = null; // Forget what we know.
- mLastEvent = null;
- mRegistered = false;
- }
-
- public Boolean isNear() {
- return isLoaded() && mLastEvent != null ? mLastEvent.getBelow() : null;
- }
-
- /** Update all listeners with the last value this class received from the sensor. */
- public void alertListeners() {
- mExecution.assertIsMainThread();
- if (mAlerting.getAndSet(true)) {
- return;
- }
- if (mLastEvent != null) {
- ThresholdSensorEvent lastEvent = mLastEvent; // Listeners can null out mLastEvent.
- List<ThresholdSensor.Listener> listeners = new ArrayList<>(mListeners);
- listeners.forEach(proximitySensorListener ->
- proximitySensorListener.onThresholdCrossed(lastEvent));
- }
-
- mAlerting.set(false);
- }
-
- private void onPrimarySensorEvent(ThresholdSensorEvent event) {
- mExecution.assertIsMainThread();
- if (mLastPrimaryEvent != null && event.getBelow() == mLastPrimaryEvent.getBelow()) {
- return;
- }
-
- mLastPrimaryEvent = event;
-
- if (mSecondarySafe && mSecondaryThresholdSensor.isLoaded()) {
- logDebug("Primary sensor reported " + (event.getBelow() ? "near" : "far")
- + ". Checking secondary.");
- if (mCancelSecondaryRunnable == null) {
- mSecondaryThresholdSensor.resume();
- }
- return;
- }
-
-
- if (!mSecondaryThresholdSensor.isLoaded()) { // No secondary
- logDebug("Primary sensor event: " + event.getBelow() + ". No secondary.");
- onSensorEvent(event);
- } else if (event.getBelow()) { // Covered? Check secondary.
- logDebug("Primary sensor event: " + event.getBelow() + ". Checking secondary.");
- if (mCancelSecondaryRunnable != null) {
- mCancelSecondaryRunnable.run();
- }
- mSecondaryThresholdSensor.resume();
- } else { // Uncovered. Report immediately.
- onSensorEvent(event);
- }
- }
-
- private void onSensorEvent(ThresholdSensorEvent event) {
- mExecution.assertIsMainThread();
- if (mLastEvent != null && event.getBelow() == mLastEvent.getBelow()) {
- return;
- }
-
- if (!mSecondarySafe && !event.getBelow()) {
- chooseSensor();
- }
-
- mLastEvent = event;
- alertListeners();
- }
-
- @Override
- public String toString() {
- return String.format("{registered=%s, paused=%s, near=%s, primarySensor=%s, "
- + "secondarySensor=%s secondarySafe=%s}",
- isRegistered(), mPaused, isNear(), mPrimaryThresholdSensor,
- mSecondaryThresholdSensor, mSecondarySafe);
- }
-
- /**
- * Convenience class allowing for briefly checking the proximity sensor.
- */
- public static class ProximityCheck implements Runnable {
-
- private final ProximitySensor mSensor;
- private final DelayableExecutor mDelayableExecutor;
- private List<Consumer<Boolean>> mCallbacks = new ArrayList<>();
- private final ThresholdSensor.Listener mListener;
- private final AtomicBoolean mRegistered = new AtomicBoolean();
-
- @Inject
- public ProximityCheck(ProximitySensor sensor, @Main DelayableExecutor delayableExecutor) {
- mSensor = sensor;
- mSensor.setTag("prox_check");
- mDelayableExecutor = delayableExecutor;
- mListener = this::onProximityEvent;
- }
-
- /** Set a descriptive tag for the sensors registration. */
- public void setTag(String tag) {
- mSensor.setTag(tag);
- }
-
- @Override
- public void run() {
- unregister();
- onProximityEvent(null);
- }
-
- /**
- * Query the proximity sensor, timing out if no result.
- */
- public void check(long timeoutMs, Consumer<Boolean> callback) {
- if (!mSensor.isLoaded()) {
- callback.accept(null);
- return;
- }
- mCallbacks.add(callback);
- if (!mRegistered.getAndSet(true)) {
- mSensor.register(mListener);
- mDelayableExecutor.executeDelayed(this, timeoutMs);
- }
- }
-
- private void unregister() {
- mSensor.unregister(mListener);
- mRegistered.set(false);
- }
-
- private void onProximityEvent(ThresholdSensorEvent proximityEvent) {
- mCallbacks.forEach(
- booleanConsumer ->
- booleanConsumer.accept(
- proximityEvent == null ? null : proximityEvent.getBelow()));
- mCallbacks.clear();
- unregister();
- mRegistered.set(false);
- }
- }
-
- private void logDebug(String msg) {
- if (DEBUG) {
- Log.d(TAG, (mTag != null ? "[" + mTag + "] " : "") + msg);
- }
- }
+ void setSecondarySafe(boolean safe);
}
diff --git a/packages/SystemUI/src/com/android/systemui/util/sensors/ProximitySensorImpl.java b/packages/SystemUI/src/com/android/systemui/util/sensors/ProximitySensorImpl.java
new file mode 100644
index 0000000..e639313a
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/util/sensors/ProximitySensorImpl.java
@@ -0,0 +1,360 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.util.sensors;
+
+import android.hardware.SensorManager;
+import android.util.Log;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.systemui.dagger.qualifiers.Main;
+import com.android.systemui.statusbar.policy.DevicePostureController;
+import com.android.systemui.util.concurrency.DelayableExecutor;
+import com.android.systemui.util.concurrency.Execution;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.atomic.AtomicBoolean;
+
+import javax.inject.Inject;
+
+/**
+ * Wrapper around SensorManager customized for the Proximity sensor.
+ *
+ * The ProximitySensor supports the concept of a primary and a
+ * secondary hardware sensor. The primary sensor is used for a first
+ * pass check if the phone covered. When triggered, it then checks
+ * the secondary sensor for confirmation (if there is one). It does
+ * not send a proximity event until the secondary sensor confirms (or
+ * rejects) the reading. The secondary sensor is, in fact, the source
+ * of truth.
+ *
+ * This is necessary as sometimes keeping the secondary sensor on for
+ * extends periods is undesirable. It may, however, result in increased
+ * latency for proximity readings.
+ *
+ * Phones should configure this via a config.xml overlay. If no
+ * proximity sensor is set (primary or secondary) we fall back to the
+ * default Sensor.TYPE_PROXIMITY. If proximity_sensor_type is set in
+ * config.xml, that will be used as the primary sensor. If
+ * proximity_sensor_secondary_type is set, that will function as the
+ * secondary sensor. If no secondary is set, only the primary will be
+ * used.
+ */
+class ProximitySensorImpl implements ProximitySensor {
+ private static final String TAG = "ProxSensor";
+ private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
+ private static final long SECONDARY_PING_INTERVAL_MS = 5000;
+
+ ThresholdSensor mPrimaryThresholdSensor;
+ ThresholdSensor mSecondaryThresholdSensor;
+ private final DelayableExecutor mDelayableExecutor;
+ private final Execution mExecution;
+ private final List<ThresholdSensor.Listener> mListeners = new ArrayList<>();
+ private String mTag = null;
+ @VisibleForTesting protected boolean mPaused;
+ private ThresholdSensorEvent mLastPrimaryEvent;
+ @VisibleForTesting
+ ThresholdSensorEvent mLastEvent;
+ private boolean mRegistered;
+ private final AtomicBoolean mAlerting = new AtomicBoolean();
+ private Runnable mCancelSecondaryRunnable;
+ boolean mInitializedListeners = false;
+ private boolean mSecondarySafe = false; // safe to skip primary sensor check and use secondary
+ protected @DevicePostureController.DevicePostureInt int mDevicePosture;
+
+ final ThresholdSensor.Listener mPrimaryEventListener = this::onPrimarySensorEvent;
+
+ final ThresholdSensor.Listener mSecondaryEventListener =
+ new ThresholdSensor.Listener() {
+ @Override
+ public void onThresholdCrossed(ThresholdSensorEvent event) {
+ // If we no longer have a "below" signal and the secondary sensor is not
+ // considered "safe", then we need to turn it off.
+ if (!mSecondarySafe
+ && (mLastPrimaryEvent == null
+ || !mLastPrimaryEvent.getBelow()
+ || !event.getBelow())) {
+ chooseSensor();
+ if (mLastPrimaryEvent == null || !mLastPrimaryEvent.getBelow()) {
+ // Only check the secondary as long as the primary thinks we're near.
+ if (mCancelSecondaryRunnable != null) {
+ mCancelSecondaryRunnable.run();
+ mCancelSecondaryRunnable = null;
+ }
+ return;
+ } else {
+ // Check this sensor again in a moment.
+ mCancelSecondaryRunnable = mDelayableExecutor.executeDelayed(() -> {
+ // This is safe because we know that mSecondaryThresholdSensor
+ // is loaded, otherwise we wouldn't be here.
+ mPrimaryThresholdSensor.pause();
+ mSecondaryThresholdSensor.resume();
+ },
+ SECONDARY_PING_INTERVAL_MS);
+ }
+ }
+ logDebug("Secondary sensor event: " + event.getBelow() + ".");
+
+ if (!mPaused) {
+ onSensorEvent(event);
+ }
+ }
+ };
+
+ @Inject
+ ProximitySensorImpl(
+ @PrimaryProxSensor ThresholdSensor primary,
+ @SecondaryProxSensor ThresholdSensor secondary,
+ @Main DelayableExecutor delayableExecutor,
+ Execution execution) {
+ mPrimaryThresholdSensor = primary;
+ mSecondaryThresholdSensor = secondary;
+ mDelayableExecutor = delayableExecutor;
+ mExecution = execution;
+ }
+
+ @Override
+ public void setTag(String tag) {
+ mTag = tag;
+ mPrimaryThresholdSensor.setTag(tag + ":primary");
+ mSecondaryThresholdSensor.setTag(tag + ":secondary");
+ }
+
+ @Override
+ public void setDelay(int delay) {
+ mExecution.assertIsMainThread();
+ mPrimaryThresholdSensor.setDelay(delay);
+ mSecondaryThresholdSensor.setDelay(delay);
+ }
+
+ /**
+ * Unregister with the {@link SensorManager} without unsetting listeners on this object.
+ */
+ @Override
+ public void pause() {
+ mExecution.assertIsMainThread();
+ mPaused = true;
+ unregisterInternal();
+ }
+
+ /**
+ * Register with the {@link SensorManager}. No-op if no listeners are registered on this object.
+ */
+ @Override
+ public void resume() {
+ mExecution.assertIsMainThread();
+ mPaused = false;
+ registerInternal();
+ }
+
+ @Override
+ public void setSecondarySafe(boolean safe) {
+ mSecondarySafe = mSecondaryThresholdSensor.isLoaded() && safe;
+ chooseSensor();
+ }
+
+ /**
+ * Returns true if we are registered with the SensorManager.
+ */
+ @Override
+ public boolean isRegistered() {
+ return mRegistered;
+ }
+
+ /**
+ * Returns {@code false} if a Proximity sensor is not available.
+ */
+ @Override
+ public boolean isLoaded() {
+ return mPrimaryThresholdSensor.isLoaded();
+ }
+
+ /**
+ * Add a listener.
+ *
+ * Registers itself with the {@link SensorManager} if this is the first listener
+ * added. If the ProximitySensor is paused, it will be registered when resumed.
+ */
+ @Override
+ public void register(ThresholdSensor.Listener listener) {
+ mExecution.assertIsMainThread();
+ if (!isLoaded()) {
+ return;
+ }
+
+ if (mListeners.contains(listener)) {
+ logDebug("ProxListener registered multiple times: " + listener);
+ } else {
+ mListeners.add(listener);
+ }
+ registerInternal();
+ }
+
+ protected void registerInternal() {
+ mExecution.assertIsMainThread();
+ if (mRegistered || mPaused || mListeners.isEmpty()) {
+ return;
+ }
+ if (!mInitializedListeners) {
+ mPrimaryThresholdSensor.pause();
+ mSecondaryThresholdSensor.pause();
+ mPrimaryThresholdSensor.register(mPrimaryEventListener);
+ mSecondaryThresholdSensor.register(mSecondaryEventListener);
+ mInitializedListeners = true;
+ }
+
+ mRegistered = true;
+ chooseSensor();
+ }
+
+ private void chooseSensor() {
+ mExecution.assertIsMainThread();
+ if (!mRegistered || mPaused || mListeners.isEmpty()) {
+ return;
+ }
+ if (mSecondarySafe) {
+ mSecondaryThresholdSensor.resume();
+ mPrimaryThresholdSensor.pause();
+ } else {
+ mPrimaryThresholdSensor.resume();
+ mSecondaryThresholdSensor.pause();
+ }
+ }
+
+ /**
+ * Remove a listener.
+ *
+ * If all listeners are removed from an instance of this class,
+ * it will unregister itself with the SensorManager.
+ */
+ @Override
+ public void unregister(ThresholdSensor.Listener listener) {
+ mExecution.assertIsMainThread();
+ mListeners.remove(listener);
+ if (mListeners.size() == 0) {
+ unregisterInternal();
+ }
+ }
+
+ @Override
+ public String getName() {
+ return mPrimaryThresholdSensor.getName();
+ }
+
+ @Override
+ public String getType() {
+ return mPrimaryThresholdSensor.getType();
+ }
+
+ protected void unregisterInternal() {
+ mExecution.assertIsMainThread();
+ if (!mRegistered) {
+ return;
+ }
+ logDebug("unregistering sensor listener");
+ mPrimaryThresholdSensor.pause();
+ mSecondaryThresholdSensor.pause();
+ if (mCancelSecondaryRunnable != null) {
+ mCancelSecondaryRunnable.run();
+ mCancelSecondaryRunnable = null;
+ }
+ mLastPrimaryEvent = null; // Forget what we know.
+ mLastEvent = null;
+ mRegistered = false;
+ }
+
+ @Override
+ public Boolean isNear() {
+ return isLoaded() && mLastEvent != null ? mLastEvent.getBelow() : null;
+ }
+
+ @Override
+ public void alertListeners() {
+ mExecution.assertIsMainThread();
+ if (mAlerting.getAndSet(true)) {
+ return;
+ }
+ if (mLastEvent != null) {
+ ThresholdSensorEvent lastEvent = mLastEvent; // Listeners can null out mLastEvent.
+ List<ThresholdSensor.Listener> listeners = new ArrayList<>(mListeners);
+ listeners.forEach(proximitySensorListener ->
+ proximitySensorListener.onThresholdCrossed(lastEvent));
+ }
+
+ mAlerting.set(false);
+ }
+
+ private void onPrimarySensorEvent(ThresholdSensorEvent event) {
+ mExecution.assertIsMainThread();
+ if (mLastPrimaryEvent != null && event.getBelow() == mLastPrimaryEvent.getBelow()) {
+ return;
+ }
+
+ mLastPrimaryEvent = event;
+
+ if (mSecondarySafe && mSecondaryThresholdSensor.isLoaded()) {
+ logDebug("Primary sensor reported " + (event.getBelow() ? "near" : "far")
+ + ". Checking secondary.");
+ if (mCancelSecondaryRunnable == null) {
+ mSecondaryThresholdSensor.resume();
+ }
+ return;
+ }
+
+
+ if (!mSecondaryThresholdSensor.isLoaded()) { // No secondary
+ logDebug("Primary sensor event: " + event.getBelow() + ". No secondary.");
+ onSensorEvent(event);
+ } else if (event.getBelow()) { // Covered? Check secondary.
+ logDebug("Primary sensor event: " + event.getBelow() + ". Checking secondary.");
+ if (mCancelSecondaryRunnable != null) {
+ mCancelSecondaryRunnable.run();
+ }
+ mSecondaryThresholdSensor.resume();
+ } else { // Uncovered. Report immediately.
+ onSensorEvent(event);
+ }
+ }
+
+ private void onSensorEvent(ThresholdSensorEvent event) {
+ mExecution.assertIsMainThread();
+ if (mLastEvent != null && event.getBelow() == mLastEvent.getBelow()) {
+ return;
+ }
+
+ if (!mSecondarySafe && !event.getBelow()) {
+ chooseSensor();
+ }
+
+ mLastEvent = event;
+ alertListeners();
+ }
+
+ @Override
+ public String toString() {
+ return String.format("{registered=%s, paused=%s, near=%s, posture=%s, primarySensor=%s, "
+ + "secondarySensor=%s secondarySafe=%s}",
+ isRegistered(), mPaused, isNear(), mDevicePosture, mPrimaryThresholdSensor,
+ mSecondaryThresholdSensor, mSecondarySafe);
+ }
+
+ void logDebug(String msg) {
+ if (DEBUG) {
+ Log.d(TAG, (mTag != null ? "[" + mTag + "] " : "") + msg);
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/util/sensors/SensorModule.java b/packages/SystemUI/src/com/android/systemui/util/sensors/SensorModule.java
index 11e7df8..0be6068c 100644
--- a/packages/SystemUI/src/com/android/systemui/util/sensors/SensorModule.java
+++ b/packages/SystemUI/src/com/android/systemui/util/sensors/SensorModule.java
@@ -16,11 +16,23 @@
package com.android.systemui.util.sensors;
+import android.content.res.Resources;
import android.hardware.Sensor;
import android.hardware.SensorManager;
+import android.text.TextUtils;
+import android.util.Log;
+
+import androidx.annotation.NonNull;
import com.android.systemui.R;
+import com.android.systemui.dagger.qualifiers.Main;
+import com.android.systemui.statusbar.policy.DevicePostureController;
+import com.android.systemui.util.concurrency.DelayableExecutor;
+import java.util.HashMap;
+import java.util.Map;
+
+import dagger.Lazy;
import dagger.Module;
import dagger.Provides;
@@ -31,8 +43,10 @@
public class SensorModule {
@Provides
@PrimaryProxSensor
- static ThresholdSensor providePrimaryProxSensor(SensorManager sensorManager,
- ThresholdSensorImpl.Builder thresholdSensorBuilder) {
+ static ThresholdSensor providePrimaryProximitySensor(
+ SensorManager sensorManager,
+ ThresholdSensorImpl.Builder thresholdSensorBuilder
+ ) {
try {
return thresholdSensorBuilder
.setSensorDelay(SensorManager.SENSOR_DELAY_NORMAL)
@@ -52,8 +66,9 @@
@Provides
@SecondaryProxSensor
- static ThresholdSensor provideSecondaryProxSensor(
- ThresholdSensorImpl.Builder thresholdSensorBuilder) {
+ static ThresholdSensor provideSecondaryProximitySensor(
+ ThresholdSensorImpl.Builder thresholdSensorBuilder
+ ) {
try {
return thresholdSensorBuilder
.setSensorResourceId(R.string.proximity_sensor_secondary_type, true)
@@ -64,4 +79,153 @@
return thresholdSensorBuilder.setSensor(null).setThresholdValue(0).build();
}
}
+
+ /**
+ * If postures are supported on the device, returns a posture dependent proximity sensor
+ * which switches proximity sensors based on the current posture.
+ *
+ * If postures are not supported the regular {@link ProximitySensorImpl} will be returned.
+ */
+ @Provides
+ static ProximitySensor provideProximitySensor(
+ @Main Resources resources,
+ Lazy<PostureDependentProximitySensor> postureDependentProximitySensorProvider,
+ Lazy<ProximitySensorImpl> proximitySensorProvider
+ ) {
+ if (hasPostureSupport(
+ resources.getStringArray(R.array.proximity_sensor_posture_mapping))) {
+ return postureDependentProximitySensorProvider.get();
+ } else {
+ return proximitySensorProvider.get();
+ }
+ }
+
+ @Provides
+ static ProximityCheck provideProximityCheck(
+ ProximitySensor proximitySensor,
+ @Main DelayableExecutor delayableExecutor
+ ) {
+ return new ProximityCheck(
+ proximitySensor,
+ delayableExecutor
+ );
+ }
+
+ @Provides
+ @PrimaryProxSensor
+ @NonNull
+ static ThresholdSensor[] providePostureToProximitySensorMapping(
+ ThresholdSensorImpl.BuilderFactory thresholdSensorImplBuilderFactory,
+ @Main Resources resources
+ ) {
+ return createPostureToSensorMapping(
+ thresholdSensorImplBuilderFactory,
+ resources.getStringArray(R.array.proximity_sensor_posture_mapping),
+ R.dimen.proximity_sensor_threshold,
+ R.dimen.proximity_sensor_threshold_latch
+ );
+ }
+
+ @Provides
+ @SecondaryProxSensor
+ @NonNull
+ static ThresholdSensor[] providePostureToSecondaryProximitySensorMapping(
+ ThresholdSensorImpl.BuilderFactory thresholdSensorImplBuilderFactory,
+ @Main Resources resources
+ ) {
+ return createPostureToSensorMapping(
+ thresholdSensorImplBuilderFactory,
+ resources.getStringArray(R.array.proximity_sensor_secondary_posture_mapping),
+ R.dimen.proximity_sensor_secondary_threshold,
+ R.dimen.proximity_sensor_secondary_threshold_latch
+ );
+ }
+
+ /**
+ * Builds sensors to use per posture.
+ *
+ * @param sensorTypes an array where the index represents
+ * {@link DevicePostureController.DevicePostureInt} and the value
+ * at the given index is the sensorType. Empty values represent
+ * no sensor desired.
+ * @param proximitySensorThresholdResourceId resource id for the threshold for all sensor
+ * postures. This currently only supports one value.
+ * This needs to be updated in the future if postures
+ * use different sensors with differing thresholds.
+ * @param proximitySensorThresholdLatchResourceId resource id for the latch for all sensor
+ * postures. This currently only supports one
+ * value. This needs to be updated in the future
+ * if postures use different sensors with
+ * differing latches.
+ * @return an array where the index represents the device posture
+ * {@link DevicePostureController.DevicePostureInt} and the value at the index is the sensor to
+ * use when the device is in that posture.
+ */
+ @NonNull
+ private static ThresholdSensor[] createPostureToSensorMapping(
+ ThresholdSensorImpl.BuilderFactory thresholdSensorImplBuilderFactory,
+ String[] sensorTypes,
+ int proximitySensorThresholdResourceId,
+ int proximitySensorThresholdLatchResourceId
+
+ ) {
+ ThresholdSensor noProxSensor = thresholdSensorImplBuilderFactory
+ .createBuilder()
+ .setSensor(null).setThresholdValue(0).build();
+
+
+ // length and index of sensorMap correspond to DevicePostureController.DevicePostureInt:
+ final ThresholdSensor[] sensorMap =
+ new ThresholdSensor[DevicePostureController.SUPPORTED_POSTURES_SIZE];
+ for (int i = 0; i < DevicePostureController.SUPPORTED_POSTURES_SIZE; i++) {
+ sensorMap[i] = noProxSensor;
+ }
+
+ if (!hasPostureSupport(sensorTypes)) {
+ Log.e("SensorModule", "config doesn't support postures,"
+ + " but attempting to retrieve proxSensorMapping");
+ return sensorMap;
+ }
+
+ // Map of sensorType => Sensor, so we reuse the same sensor if it's the same between
+ // postures
+ Map<String, ThresholdSensor> typeToSensorMap = new HashMap<>();
+ for (int i = 0; i < sensorTypes.length; i++) {
+ try {
+ final String sensorType = sensorTypes[i];
+ if (typeToSensorMap.containsKey(sensorType)) {
+ sensorMap[i] = typeToSensorMap.get(sensorType);
+ } else {
+ sensorMap[i] = thresholdSensorImplBuilderFactory
+ .createBuilder()
+ .setSensorType(sensorTypes[i], true)
+ .setThresholdResourceId(proximitySensorThresholdResourceId)
+ .setThresholdLatchResourceId(proximitySensorThresholdLatchResourceId)
+ .build();
+ typeToSensorMap.put(sensorType, sensorMap[i]);
+ }
+ } catch (IllegalStateException e) {
+ // do nothing, sensor at this posture is already set to noProxSensor
+ }
+ }
+
+ return sensorMap;
+ }
+
+ /**
+ * Returns true if there's at least one non-empty sensor type in the given array.
+ */
+ private static boolean hasPostureSupport(String[] postureToSensorTypeMapping) {
+ if (postureToSensorTypeMapping == null || postureToSensorTypeMapping.length == 0) {
+ return false;
+ }
+
+ for (String sensorType : postureToSensorTypeMapping) {
+ if (!TextUtils.isEmpty(sensorType)) {
+ return true;
+ }
+ }
+
+ return false;
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/util/sensors/ThresholdSensor.java b/packages/SystemUI/src/com/android/systemui/util/sensors/ThresholdSensor.java
index 363a734..d81a8d5 100644
--- a/packages/SystemUI/src/com/android/systemui/util/sensors/ThresholdSensor.java
+++ b/packages/SystemUI/src/com/android/systemui/util/sensors/ThresholdSensor.java
@@ -16,8 +16,6 @@
package com.android.systemui.util.sensors;
-import java.util.Locale;
-
/**
* A wrapper class for sensors that have a boolean state - above/below.
*/
@@ -77,6 +75,16 @@
void unregister(Listener listener);
/**
+ * Name of the sensor.
+ */
+ String getName();
+
+ /**
+ * Type of the sensor.
+ */
+ String getType();
+
+ /**
* Interface for listening to events on {@link ThresholdSensor}
*/
interface Listener {
@@ -85,34 +93,4 @@
*/
void onThresholdCrossed(ThresholdSensorEvent event);
}
-
- /**
- * Returned when the below/above state of a {@link ThresholdSensor} changes.
- */
- class ThresholdSensorEvent {
- private final boolean mBelow;
- private final long mTimestampNs;
-
- public ThresholdSensorEvent(boolean below, long timestampNs) {
- mBelow = below;
- mTimestampNs = timestampNs;
- }
-
- public boolean getBelow() {
- return mBelow;
- }
-
- public long getTimestampNs() {
- return mTimestampNs;
- }
-
- public long getTimestampMs() {
- return mTimestampNs / 1000000;
- }
-
- @Override
- public String toString() {
- return String.format((Locale) null, "{near=%s, timestamp_ns=%d}", mBelow, mTimestampNs);
- }
- }
}
diff --git a/packages/SystemUI/src/com/android/systemui/util/sensors/ThresholdSensorEvent.java b/packages/SystemUI/src/com/android/systemui/util/sensors/ThresholdSensorEvent.java
new file mode 100644
index 0000000..afce09f
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/util/sensors/ThresholdSensorEvent.java
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.util.sensors;
+
+import java.util.Locale;
+
+/**
+ * Returned when the below/above state of a {@link ThresholdSensor} changes.
+ */
+public class ThresholdSensorEvent {
+ private final boolean mBelow;
+ private final long mTimestampNs;
+
+ public ThresholdSensorEvent(boolean below, long timestampNs) {
+ mBelow = below;
+ mTimestampNs = timestampNs;
+ }
+
+ public boolean getBelow() {
+ return mBelow;
+ }
+
+ public long getTimestampNs() {
+ return mTimestampNs;
+ }
+
+ public long getTimestampMs() {
+ return mTimestampNs / 1000000;
+ }
+
+ @Override
+ public String toString() {
+ return String.format((Locale) null, "{near=%s, timestamp_ns=%d}", mBelow, mTimestampNs);
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/util/sensors/ThresholdSensorImpl.java b/packages/SystemUI/src/com/android/systemui/util/sensors/ThresholdSensorImpl.java
index d10cf9b..a9086b1 100644
--- a/packages/SystemUI/src/com/android/systemui/util/sensors/ThresholdSensorImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/util/sensors/ThresholdSensorImpl.java
@@ -21,6 +21,7 @@
import android.hardware.SensorEvent;
import android.hardware.SensorEventListener;
import android.hardware.SensorManager;
+import android.text.TextUtils;
import android.util.Log;
import com.android.internal.annotations.VisibleForTesting;
@@ -32,7 +33,10 @@
import javax.inject.Inject;
-class ThresholdSensorImpl implements ThresholdSensor {
+/**
+ * Sensor that will only trigger beyond some lower and upper threshold.
+ */
+public class ThresholdSensorImpl implements ThresholdSensor {
private static final String TAG = "ThresholdSensor";
private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
@@ -198,6 +202,15 @@
alertListenersInternal(belowThreshold, timestampNs);
}
+ @Override
+ public String getName() {
+ return mSensor != null ? mSensor.getName() : null;
+ }
+
+ @Override
+ public String getType() {
+ return mSensor != null ? mSensor.getStringType() : null;
+ }
@Override
public String toString() {
@@ -211,7 +224,12 @@
}
}
- static class Builder {
+ /**
+ * Use to build a ThresholdSensor. Should only be used once per sensor built, since
+ * parameters are not reset after calls to build(). For ease of retrievingnew Builders, use
+ * {@link BuilderFactory}.
+ */
+ public static class Builder {
private final Resources mResources;
private final AsyncSensorManager mSensorManager;
private final Execution mExecution;
@@ -318,7 +336,7 @@
@VisibleForTesting
Sensor findSensorByType(String sensorType, boolean requireWakeUp) {
- if (sensorType.isEmpty()) {
+ if (TextUtils.isEmpty(sensorType)) {
return null;
}
@@ -336,4 +354,29 @@
return sensor;
}
}
+
+ /**
+ * Factory that creates a new ThresholdSensorImpl.Builder. In general, Builders should not be
+ * reused after creating a ThresholdSensor or else there may be default threshold and sensor
+ * values set from the previous built sensor.
+ */
+ public static class BuilderFactory {
+ private final Resources mResources;
+ private final AsyncSensorManager mSensorManager;
+ private final Execution mExecution;
+
+ @Inject
+ BuilderFactory(
+ @Main Resources resources,
+ AsyncSensorManager sensorManager,
+ Execution execution) {
+ mResources = resources;
+ mSensorManager = sensorManager;
+ mExecution = execution;
+ }
+
+ ThresholdSensorImpl.Builder createBuilder() {
+ return new Builder(mResources, mSensorManager, mExecution);
+ }
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/wmshell/WMShellBaseModule.java b/packages/SystemUI/src/com/android/systemui/wmshell/WMShellBaseModule.java
index f420a85..514a903 100644
--- a/packages/SystemUI/src/com/android/systemui/wmshell/WMShellBaseModule.java
+++ b/packages/SystemUI/src/com/android/systemui/wmshell/WMShellBaseModule.java
@@ -28,7 +28,6 @@
import com.android.internal.statusbar.IStatusBarService;
import com.android.systemui.dagger.WMComponent;
import com.android.systemui.dagger.WMSingleton;
-import com.android.wm.shell.FullscreenTaskListener;
import com.android.wm.shell.RootTaskDisplayAreaOrganizer;
import com.android.wm.shell.ShellCommandHandler;
import com.android.wm.shell.ShellCommandHandlerImpl;
@@ -57,6 +56,8 @@
import com.android.wm.shell.common.annotations.ShellSplashscreenThread;
import com.android.wm.shell.draganddrop.DragAndDropController;
import com.android.wm.shell.freeform.FreeformTaskListener;
+import com.android.wm.shell.fullscreen.FullscreenTaskListener;
+import com.android.wm.shell.fullscreen.FullscreenUnfoldController;
import com.android.wm.shell.hidedisplaycutout.HideDisplayCutout;
import com.android.wm.shell.hidedisplaycutout.HideDisplayCutoutController;
import com.android.wm.shell.legacysplitscreen.LegacySplitScreen;
@@ -72,6 +73,7 @@
import com.android.wm.shell.sizecompatui.SizeCompatUIController;
import com.android.wm.shell.splitscreen.SplitScreen;
import com.android.wm.shell.splitscreen.SplitScreenController;
+import com.android.wm.shell.splitscreen.StageTaskUnfoldController;
import com.android.wm.shell.startingsurface.StartingSurface;
import com.android.wm.shell.startingsurface.StartingWindowController;
import com.android.wm.shell.startingsurface.StartingWindowTypeAlgorithm;
@@ -79,10 +81,15 @@
import com.android.wm.shell.tasksurfacehelper.TaskSurfaceHelperController;
import com.android.wm.shell.transition.ShellTransitions;
import com.android.wm.shell.transition.Transitions;
+import com.android.wm.shell.unfold.ShellUnfoldProgressProvider;
+import com.android.wm.shell.unfold.UnfoldBackgroundController;
import java.util.Optional;
+import javax.inject.Provider;
+
import dagger.BindsOptionalOf;
+import dagger.Lazy;
import dagger.Module;
import dagger.Provides;
@@ -218,8 +225,60 @@
@WMSingleton
@Provides
- static FullscreenTaskListener provideFullscreenTaskListener(SyncTransactionQueue syncQueue) {
- return new FullscreenTaskListener(syncQueue);
+ static FullscreenTaskListener provideFullscreenTaskListener(
+ SyncTransactionQueue syncQueue, Optional<FullscreenUnfoldController> controller) {
+ return new FullscreenTaskListener(syncQueue, controller);
+ }
+
+ //
+ // Unfold transition
+ //
+
+ @WMSingleton
+ @Provides
+ static Optional<FullscreenUnfoldController> provideFullscreenUnfoldController(
+ Context context,
+ Optional<ShellUnfoldProgressProvider> progressProvider,
+ Lazy<UnfoldBackgroundController> unfoldBackgroundController,
+ DisplayInsetsController displayInsetsController,
+ @ShellMainThread ShellExecutor mainExecutor
+ ) {
+ return progressProvider.map(shellUnfoldTransitionProgressProvider ->
+ new FullscreenUnfoldController(context, mainExecutor,
+ unfoldBackgroundController.get(), shellUnfoldTransitionProgressProvider,
+ displayInsetsController));
+ }
+
+ @Provides
+ static Optional<StageTaskUnfoldController> provideStageTaskUnfoldController(
+ Optional<ShellUnfoldProgressProvider> progressProvider,
+ Context context,
+ TransactionPool transactionPool,
+ Lazy<UnfoldBackgroundController> unfoldBackgroundController,
+ DisplayInsetsController displayInsetsController,
+ @ShellMainThread ShellExecutor mainExecutor
+ ) {
+ return progressProvider.map(shellUnfoldTransitionProgressProvider ->
+ new StageTaskUnfoldController(
+ context,
+ transactionPool,
+ shellUnfoldTransitionProgressProvider,
+ displayInsetsController,
+ unfoldBackgroundController.get(),
+ mainExecutor
+ ));
+ }
+
+ @WMSingleton
+ @Provides
+ static UnfoldBackgroundController provideUnfoldBackgroundController(
+ RootTaskDisplayAreaOrganizer rootTaskDisplayAreaOrganizer,
+ Context context
+ ) {
+ return new UnfoldBackgroundController(
+ context,
+ rootTaskDisplayAreaOrganizer
+ );
}
//
@@ -379,12 +438,13 @@
@ShellMainThread ShellExecutor mainExecutor,
DisplayImeController displayImeController,
DisplayInsetsController displayInsetsController, Transitions transitions,
- TransactionPool transactionPool) {
+ TransactionPool transactionPool,
+ Provider<Optional<StageTaskUnfoldController>> stageTaskUnfoldControllerProvider) {
if (ActivityTaskManager.supportsSplitScreenMultiWindow(context)) {
return Optional.of(new SplitScreenController(shellTaskOrganizer, syncQueue, context,
rootTaskDisplayAreaOrganizer, mainExecutor, displayImeController,
displayInsetsController, transitions,
- transactionPool));
+ transactionPool, stageTaskUnfoldControllerProvider));
} else {
return Optional.empty();
}
@@ -474,6 +534,7 @@
Optional<AppPairsController> appPairsOptional,
Optional<PipTouchHandler> pipTouchHandlerOptional,
FullscreenTaskListener fullscreenTaskListener,
+ Optional<FullscreenUnfoldController> appUnfoldTransitionController,
Optional<Optional<FreeformTaskListener>> freeformTaskListener,
Transitions transitions,
StartingWindowController startingWindow,
@@ -489,6 +550,7 @@
appPairsOptional,
pipTouchHandlerOptional,
fullscreenTaskListener,
+ appUnfoldTransitionController,
freeformTaskListener,
transitions,
startingWindow,
diff --git a/packages/SystemUI/src/com/android/systemui/wmshell/WMShellModule.java b/packages/SystemUI/src/com/android/systemui/wmshell/WMShellModule.java
index 83c2a9b..a7c5ad2 100644
--- a/packages/SystemUI/src/com/android/systemui/wmshell/WMShellModule.java
+++ b/packages/SystemUI/src/com/android/systemui/wmshell/WMShellModule.java
@@ -126,9 +126,10 @@
static AppPairsController provideAppPairs(ShellTaskOrganizer shellTaskOrganizer,
SyncTransactionQueue syncQueue, DisplayController displayController,
@ShellMainThread ShellExecutor mainExecutor,
- DisplayImeController displayImeController) {
+ DisplayImeController displayImeController,
+ DisplayInsetsController displayInsetsController) {
return new AppPairsController(shellTaskOrganizer, syncQueue, displayController,
- mainExecutor, displayImeController);
+ mainExecutor, displayImeController, displayInsetsController);
}
//
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchControllerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchControllerTest.java
index 88b596c..5c7885f 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchControllerTest.java
@@ -142,7 +142,8 @@
mBypassController,
mSmartspaceController,
mKeyguardUnlockAnimationController,
- mSmartSpaceTransitionController
+ mSmartSpaceTransitionController,
+ mResources
);
when(mStatusBarStateController.getState()).thenReturn(StatusBarState.SHADE);
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchTest.java
index ce02b83..e4336fe 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchTest.java
@@ -97,6 +97,7 @@
mLargeClockFrame = mKeyguardClockSwitch.findViewById(R.id.lockscreen_clock_view_large);
mLargeClockView = mKeyguardClockSwitch.findViewById(R.id.animatable_clock_view_large);
mBigClock = new TextClock(getContext());
+ mKeyguardClockSwitch.mChildrenAreLaidOut = true;
MockitoAnnotations.initMocks(this);
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthRippleControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthRippleControllerTest.kt
index f2f0029..2c4808a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthRippleControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthRippleControllerTest.kt
@@ -21,11 +21,12 @@
import android.testing.AndroidTestingRunner
import android.testing.TestableLooper.RunWithLooper
import androidx.test.filters.SmallTest
+import com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession
import com.android.keyguard.KeyguardUpdateMonitor
import com.android.keyguard.KeyguardUpdateMonitorCallback
import com.android.systemui.SysuiTestCase
-import com.android.systemui.plugins.statusbar.StatusBarStateController
import com.android.systemui.keyguard.WakefulnessLifecycle
+import com.android.systemui.plugins.statusbar.StatusBarStateController
import com.android.systemui.statusbar.LightRevealScrim
import com.android.systemui.statusbar.NotificationShadeWindowController
import com.android.systemui.statusbar.commandline.CommandRegistry
@@ -34,6 +35,8 @@
import com.android.systemui.statusbar.phone.StatusBar
import com.android.systemui.statusbar.policy.ConfigurationController
import com.android.systemui.statusbar.policy.KeyguardStateController
+import com.android.systemui.util.leak.RotationUtils
+import org.junit.After
import org.junit.Assert.assertFalse
import org.junit.Assert.assertTrue
import org.junit.Before
@@ -48,12 +51,15 @@
import org.mockito.Mockito.reset
import org.mockito.Mockito.verify
import org.mockito.MockitoAnnotations
-
+import org.mockito.MockitoSession
+import org.mockito.quality.Strictness
import javax.inject.Provider
@SmallTest
@RunWith(AndroidTestingRunner::class)
class AuthRippleControllerTest : SysuiTestCase() {
+ private lateinit var staticMockSession: MockitoSession
+
private lateinit var controller: AuthRippleController
@Mock private lateinit var statusBar: StatusBar
@Mock private lateinit var rippleView: AuthRippleView
@@ -74,6 +80,12 @@
@Before
fun setUp() {
MockitoAnnotations.initMocks(this)
+ staticMockSession = mockitoSession()
+ .mockStatic(RotationUtils::class.java)
+ .strictness(Strictness.LENIENT)
+ .startMocking()
+
+ `when`(RotationUtils.getRotation(context)).thenReturn(RotationUtils.ROTATION_NONE)
`when`(udfpsControllerProvider.get()).thenReturn(udfpsController)
controller = AuthRippleController(
@@ -96,6 +108,11 @@
`when`(statusBar.lightRevealScrim).thenReturn(lightRevealScrim)
}
+ @After
+ fun tearDown() {
+ staticMockSession.finishMocking()
+ }
+
@Test
fun testFingerprintTrigger_Ripple() {
// GIVEN fp exists, keyguard is visible, user doesn't need strong auth
diff --git a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeConfigurationUtil.java b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeConfigurationUtil.java
index d6226aa..866791c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeConfigurationUtil.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeConfigurationUtil.java
@@ -40,7 +40,7 @@
when(params.doubleTapReportsTouchCoordinates()).thenReturn(false);
when(params.getDisplayNeedsBlanking()).thenReturn(false);
when(params.getSelectivelyRegisterSensorsUsingProx()).thenReturn(false);
- when(params.singleTapUsesProx()).thenReturn(true);
+ when(params.singleTapUsesProx(anyInt())).thenReturn(true);
when(params.longPressUsesProx()).thenReturn(true);
when(params.getQuickPickupAodDuration()).thenReturn(500);
@@ -61,14 +61,13 @@
when(config.getWakeLockScreenDebounce()).thenReturn(0L);
when(config.doubleTapSensorType()).thenReturn(null);
- when(config.tapSensorType()).thenReturn(null);
when(config.longPressSensorType()).thenReturn(null);
when(config.udfpsLongPressSensorType()).thenReturn(null);
when(config.quickPickupSensorType()).thenReturn(null);
when(config.tapGestureEnabled(anyInt())).thenReturn(true);
when(config.tapSensorAvailable()).thenReturn(true);
- when(config.tapSensorType()).thenReturn(FakeSensorManager.TAP_SENSOR_TYPE);
+ when(config.tapSensorType(anyInt())).thenReturn(FakeSensorManager.TAP_SENSOR_TYPE);
when(config.dozePickupSensorAvailable()).thenReturn(false);
when(config.wakeScreenGestureAvailable()).thenReturn(false);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeSensorsTest.java b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeSensorsTest.java
index 5c4c27c..42e34c8 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeSensorsTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeSensorsTest.java
@@ -19,6 +19,7 @@
import static com.android.systemui.doze.DozeLog.REASON_SENSOR_TAP;
import static com.android.systemui.plugins.SensorManagerPlugin.Sensor.TYPE_WAKE_LOCK_SCREEN;
+import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.any;
@@ -47,6 +48,7 @@
import com.android.systemui.doze.DozeSensors.TriggerSensor;
import com.android.systemui.plugins.SensorManagerPlugin;
import com.android.systemui.statusbar.phone.DozeParameters;
+import com.android.systemui.statusbar.policy.DevicePostureController;
import com.android.systemui.util.sensors.AsyncSensorManager;
import com.android.systemui.util.sensors.ProximitySensor;
import com.android.systemui.util.settings.FakeSettings;
@@ -58,6 +60,11 @@
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
+import java.lang.reflect.Constructor;
+import java.lang.reflect.Field;
+import java.lang.reflect.Method;
+import java.util.ArrayList;
+import java.util.List;
import java.util.function.Consumer;
@RunWith(AndroidTestingRunner.class)
@@ -85,6 +92,8 @@
private AuthController mAuthController;
@Mock
private ProximitySensor mProximitySensor;
+ private @DevicePostureController.DevicePostureInt int mDevicePosture =
+ DevicePostureController.DEVICE_POSTURE_UNKNOWN;
private FakeSettings mFakeSettings = new FakeSettings();
private SensorManagerPlugin.SensorEventListener mWakeLockScreenListener;
private TestableLooper mTestableLooper;
@@ -101,6 +110,7 @@
((Runnable) invocation.getArgument(0)).run();
return null;
}).when(mWakeLock).wrap(any(Runnable.class));
+ mDevicePosture = DevicePostureController.DEVICE_POSTURE_UNKNOWN;
mDozeSensors = new TestableDozeSensors();
}
@@ -157,7 +167,7 @@
// GIVEN we only should register sensors using prox when not in low-powered mode / off
// and the single tap sensor uses the proximity sensor
when(mDozeParameters.getSelectivelyRegisterSensorsUsingProx()).thenReturn(true);
- when(mDozeParameters.singleTapUsesProx()).thenReturn(true);
+ when(mDozeParameters.singleTapUsesProx(anyInt())).thenReturn(true);
TestableDozeSensors dozeSensors = new TestableDozeSensors();
// THEN on initialization, the tap sensor isn't requested
@@ -258,12 +268,55 @@
assertTrue(triggerSensor.mRegistered);
}
+ @Test
+ public void testPostureOpen_registersCorrectTapGesture() {
+ // GIVEN device posture open
+ mDevicePosture = DevicePostureController.DEVICE_POSTURE_OPENED;
+
+ // WHEN DozeSensors are initialized
+ new TestableDozeSensors();
+
+ // THEN we use the posture to determine which tap sensor to use
+ verify(mAmbientDisplayConfiguration).tapSensorType(eq(mDevicePosture));
+ }
+
+ @Test
+ public void testFindSensor() throws Exception {
+ // GIVEN a prox sensor
+ List<Sensor> sensors = new ArrayList<>();
+ Sensor proxSensor =
+ createSensor(Sensor.TYPE_PROXIMITY, Sensor.STRING_TYPE_PROXIMITY);
+ sensors.add(proxSensor);
+
+ when(mSensorManager.getSensorList(anyInt())).thenReturn(sensors);
+
+ // WHEN we try to find the prox sensor with the same type and name
+ // THEN we find the added sensor
+ assertEquals(
+ proxSensor,
+ DozeSensors.findSensor(
+ mSensorManager,
+ Sensor.STRING_TYPE_PROXIMITY,
+ proxSensor.getName()));
+
+ // WHEN we try to find a prox sensor with a different name
+ // THEN no sensor is found
+ assertEquals(
+ null,
+ DozeSensors.findSensor(
+ mSensorManager,
+ Sensor.STRING_TYPE_PROXIMITY,
+ "some other name"));
+ }
+
+
private class TestableDozeSensors extends DozeSensors {
TestableDozeSensors() {
super(getContext(), mSensorManager, mDozeParameters,
mAmbientDisplayConfiguration, mWakeLock, mCallback, mProxCallback, mDozeLog,
- mProximitySensor, mFakeSettings, mAuthController);
+ mProximitySensor, mFakeSettings, mAuthController,
+ mDevicePosture);
for (TriggerSensor sensor : mSensors) {
if (sensor instanceof PluginSensor
&& ((PluginSensor) sensor).mPluginSensor.getType()
@@ -288,4 +341,23 @@
mDozeLog);
}
}
+
+ public static void setSensorType(Sensor sensor, int type, String strType) throws Exception {
+ Method setter = Sensor.class.getDeclaredMethod("setType", Integer.TYPE);
+ setter.setAccessible(true);
+ setter.invoke(sensor, type);
+ if (strType != null) {
+ Field f = sensor.getClass().getDeclaredField("mStringType");
+ f.setAccessible(true);
+ f.set(sensor, strType);
+ }
+ }
+
+ public static Sensor createSensor(int type, String strType) throws Exception {
+ Constructor<Sensor> constr = Sensor.class.getDeclaredConstructor();
+ constr.setAccessible(true);
+ Sensor sensor = constr.newInstance();
+ setSensorType(sensor, type, strType);
+ return sensor;
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeTriggersTest.java b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeTriggersTest.java
index 9577c7a..31fa3f8 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeTriggersTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeTriggersTest.java
@@ -45,6 +45,7 @@
import com.android.systemui.dock.DockManager;
import com.android.systemui.doze.DozeTriggers.DozingUpdateUiEvent;
import com.android.systemui.statusbar.phone.DozeParameters;
+import com.android.systemui.statusbar.policy.DevicePostureController;
import com.android.systemui.statusbar.policy.KeyguardStateController;
import com.android.systemui.util.concurrency.FakeExecutor;
import com.android.systemui.util.concurrency.FakeThreadFactory;
@@ -52,7 +53,8 @@
import com.android.systemui.util.sensors.FakeProximitySensor;
import com.android.systemui.util.sensors.FakeSensorManager;
import com.android.systemui.util.sensors.FakeThresholdSensor;
-import com.android.systemui.util.sensors.ProximitySensor;
+import com.android.systemui.util.sensors.ProximityCheck;
+import com.android.systemui.util.sensors.ThresholdSensorEvent;
import com.android.systemui.util.settings.FakeSettings;
import com.android.systemui.util.time.FakeSystemClock;
import com.android.systemui.util.wakelock.WakeLock;
@@ -79,13 +81,15 @@
@Mock
private DockManager mDockManager;
@Mock
- private ProximitySensor.ProximityCheck mProximityCheck;
+ private ProximityCheck mProximityCheck;
@Mock
private AuthController mAuthController;
@Mock
private UiEventLogger mUiEventLogger;
@Mock
private KeyguardStateController mKeyguardStateController;
+ @Mock
+ private DevicePostureController mDevicePostureController;
private DozeTriggers mTriggers;
private FakeSensorManager mSensors;
@@ -117,7 +121,8 @@
mTriggers = new DozeTriggers(mContext, mHost, config, dozeParameters,
asyncSensorManager, wakeLock, mDockManager, mProximitySensor,
mProximityCheck, mock(DozeLog.class), mBroadcastDispatcher, new FakeSettings(),
- mAuthController, mExecutor, mUiEventLogger, mKeyguardStateController);
+ mAuthController, mExecutor, mUiEventLogger, mKeyguardStateController,
+ mDevicePostureController);
mTriggers.setDozeMachine(mMachine);
waitForSensorManager();
}
@@ -132,14 +137,14 @@
mTriggers.transitionTo(DozeMachine.State.INITIALIZED, DozeMachine.State.DOZE);
clearInvocations(mMachine);
- mProximitySensor.setLastEvent(new ProximitySensor.ThresholdSensorEvent(true, 1));
+ mProximitySensor.setLastEvent(new ThresholdSensorEvent(true, 1));
captor.getValue().onNotificationAlerted(null /* pulseSuppressedListener */);
mProximitySensor.alertListeners();
verify(mMachine, never()).requestState(any());
verify(mMachine, never()).requestPulse(anyInt());
- mProximitySensor.setLastEvent(new ProximitySensor.ThresholdSensorEvent(false, 2));
+ mProximitySensor.setLastEvent(new ThresholdSensorEvent(false, 2));
mProximitySensor.alertListeners();
waitForSensorManager();
captor.getValue().onNotificationAlerted(null /* pulseSuppressedListener */);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/AnimatableClockControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/keyguard/AnimatableClockControllerTest.java
new file mode 100644
index 0000000..df11284
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/AnimatableClockControllerTest.java
@@ -0,0 +1,144 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.keyguard;
+
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession;
+
+import static junit.framework.Assert.assertFalse;
+import static junit.framework.Assert.assertTrue;
+
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.anyObject;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.content.res.Resources;
+import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper;
+import android.view.View;
+
+import androidx.test.filters.SmallTest;
+
+import com.android.keyguard.AnimatableClockController;
+import com.android.keyguard.AnimatableClockView;
+import com.android.keyguard.KeyguardUpdateMonitor;
+import com.android.settingslib.Utils;
+import com.android.systemui.SysuiTestCase;
+import com.android.systemui.broadcast.BroadcastDispatcher;
+import com.android.systemui.plugins.statusbar.StatusBarStateController;
+import com.android.systemui.statusbar.phone.KeyguardBypassController;
+import com.android.systemui.statusbar.policy.BatteryController;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Captor;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.mockito.MockitoSession;
+import org.mockito.quality.Strictness;
+
+@SmallTest
+@RunWith(AndroidTestingRunner.class)
+@TestableLooper.RunWithLooper
+public class AnimatableClockControllerTest extends SysuiTestCase {
+ @Mock
+ private AnimatableClockView mClockView;
+ @Mock
+ private StatusBarStateController mStatusBarStateController;
+ @Mock
+ private BroadcastDispatcher mBroadcastDispatcher;
+ @Mock
+ private BatteryController mBatteryController;
+ @Mock
+ private KeyguardUpdateMonitor mKeyguardUpdateMonitor;
+ @Mock
+ private KeyguardBypassController mBypassController;
+ @Mock
+ private Resources mResources;
+
+ private MockitoSession mStaticMockSession;
+ private AnimatableClockController mAnimatableClockController;
+
+ // Capture listeners so that they can be used to send events
+ @Captor private ArgumentCaptor<View.OnAttachStateChangeListener> mAttachCaptor =
+ ArgumentCaptor.forClass(View.OnAttachStateChangeListener.class);
+ private View.OnAttachStateChangeListener mAttachListener;
+
+ @Captor private ArgumentCaptor<StatusBarStateController.StateListener> mStatusBarStateCaptor;
+ private StatusBarStateController.StateListener mStatusBarStateCallback;
+
+ @Before
+ public void setUp() throws Exception {
+ MockitoAnnotations.initMocks(this);
+ mStaticMockSession = mockitoSession()
+ .mockStatic(Utils.class)
+ .strictness(Strictness.LENIENT) // it's ok if mocked classes aren't used
+ .startMocking();
+ when(Utils.getColorAttrDefaultColor(anyObject(), anyInt())).thenReturn(0);
+
+ mAnimatableClockController = new AnimatableClockController(
+ mClockView,
+ mStatusBarStateController,
+ mBroadcastDispatcher,
+ mBatteryController,
+ mKeyguardUpdateMonitor,
+ mBypassController,
+ mResources
+ );
+ mAnimatableClockController.init();
+ captureAttachListener();
+ }
+
+ @After
+ public void tearDown() {
+ mStaticMockSession.finishMocking();
+ }
+
+ @Test
+ public void testOnAttachedUpdatesDozeStateToTrue() {
+ // GIVEN dozing
+ when(mStatusBarStateController.isDozing()).thenReturn(true);
+ when(mStatusBarStateController.getDozeAmount()).thenReturn(1f);
+
+ // WHEN the clock view gets attached
+ mAttachListener.onViewAttachedToWindow(mClockView);
+
+ // THEN the clock controller updated its dozing state to true
+ assertTrue(mAnimatableClockController.isDozing());
+ }
+
+ @Test
+ public void testOnAttachedUpdatesDozeStateToFalse() {
+ // GIVEN not dozing
+ when(mStatusBarStateController.isDozing()).thenReturn(false);
+ when(mStatusBarStateController.getDozeAmount()).thenReturn(0f);
+
+ // WHEN the clock view gets attached
+ mAttachListener.onViewAttachedToWindow(mClockView);
+
+ // THEN the clock controller updated its dozing state to false
+ assertFalse(mAnimatableClockController.isDozing());
+ }
+
+ private void captureAttachListener() {
+ verify(mClockView).addOnAttachStateChangeListener(mAttachCaptor.capture());
+ mAttachListener = mAttachCaptor.getValue();
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/MediaCarouselControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/MediaCarouselControllerTest.kt
new file mode 100644
index 0000000..175ec87f
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/MediaCarouselControllerTest.kt
@@ -0,0 +1,174 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.media
+
+import android.testing.AndroidTestingRunner
+import android.testing.TestableLooper
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.classifier.FalsingCollector
+import com.android.systemui.dagger.qualifiers.Main
+import com.android.systemui.dump.DumpManager
+import com.android.systemui.plugins.ActivityStarter
+import com.android.systemui.plugins.FalsingManager
+import com.android.systemui.statusbar.notification.collection.legacy.VisualStabilityManager
+import com.android.systemui.statusbar.policy.ConfigurationController
+import com.android.systemui.util.concurrency.DelayableExecutor
+import com.android.systemui.util.time.FakeSystemClock
+import junit.framework.Assert.assertEquals
+import junit.framework.Assert.assertTrue
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.Mock
+import org.mockito.MockitoAnnotations
+import javax.inject.Provider
+
+private val DATA = MediaData(
+ userId = -1,
+ initialized = false,
+ backgroundColor = 0,
+ app = null,
+ appIcon = null,
+ artist = null,
+ song = null,
+ artwork = null,
+ actions = emptyList(),
+ actionsToShowInCompact = emptyList(),
+ packageName = "INVALID",
+ token = null,
+ clickIntent = null,
+ device = null,
+ active = true,
+ resumeAction = null)
+
+private val SMARTSPACE_KEY = "smartspace"
+
+@SmallTest
+@TestableLooper.RunWithLooper(setAsMainLooper = true)
+@RunWith(AndroidTestingRunner::class)
+class MediaCarouselControllerTest : SysuiTestCase() {
+
+ @Mock lateinit var mediaControlPanelFactory: Provider<MediaControlPanel>
+ @Mock lateinit var panel: MediaControlPanel
+ @Mock lateinit var visualStabilityManager: VisualStabilityManager
+ @Mock lateinit var mediaHostStatesManager: MediaHostStatesManager
+ @Mock lateinit var activityStarter: ActivityStarter
+ @Mock @Main private lateinit var executor: DelayableExecutor
+ @Mock lateinit var mediaDataManager: MediaDataManager
+ @Mock lateinit var configurationController: ConfigurationController
+ @Mock lateinit var falsingCollector: FalsingCollector
+ @Mock lateinit var falsingManager: FalsingManager
+ @Mock lateinit var dumpManager: DumpManager
+
+ private val clock = FakeSystemClock()
+ private lateinit var mediaCarouselController: MediaCarouselController
+
+ @Before
+ fun setup() {
+ MockitoAnnotations.initMocks(this)
+
+ mediaCarouselController = MediaCarouselController(
+ context,
+ mediaControlPanelFactory,
+ visualStabilityManager,
+ mediaHostStatesManager,
+ activityStarter,
+ clock,
+ executor,
+ mediaDataManager,
+ configurationController,
+ falsingCollector,
+ falsingManager,
+ dumpManager
+ )
+
+ MediaPlayerData.clear()
+ }
+
+ @Test
+ fun testPlayerOrdering() {
+ // Test values: key, data, last active time
+ val playingLocal = Triple("playing local",
+ DATA.copy(active = true, isPlaying = true, isLocalSession = true, resumption = false),
+ 4500L)
+
+ val playingRemote = Triple("playing remote",
+ DATA.copy(active = true, isPlaying = true, isLocalSession = false, resumption = false),
+ 5000L)
+
+ val pausedLocal = Triple("paused local",
+ DATA.copy(active = true, isPlaying = false, isLocalSession = true, resumption = false),
+ 1000L)
+
+ val pausedRemote = Triple("paused remote",
+ DATA.copy(active = true, isPlaying = false, isLocalSession = false, resumption = false),
+ 2000L)
+
+ val resume1 = Triple("resume 1",
+ DATA.copy(active = false, isPlaying = false, isLocalSession = true, resumption = true),
+ 500L)
+
+ val resume2 = Triple("resume 2",
+ DATA.copy(active = false, isPlaying = false, isLocalSession = true, resumption = true),
+ 1000L)
+
+ // Expected ordering for media players:
+ // Actively playing local sessions
+ // Actively playing remote sessions
+ // Paused sessions, by last active
+ // Resume controls, by last active
+
+ val expected = listOf(playingLocal, playingRemote, pausedRemote, pausedLocal, resume2,
+ resume1)
+
+ expected.forEach {
+ clock.setCurrentTimeMillis(it.third)
+ MediaPlayerData.addMediaPlayer(it.first, it.second.copy(notificationKey = it.first),
+ panel, clock)
+ }
+
+ for ((index, key) in MediaPlayerData.playerKeys().withIndex()) {
+ assertEquals(expected.get(index).first, key.data.notificationKey)
+ }
+ }
+
+ @Test
+ fun testOrderWithSmartspace_prioritized() {
+ testPlayerOrdering()
+
+ // If smartspace is prioritized
+ MediaPlayerData.addMediaRecommendation(SMARTSPACE_KEY, EMPTY_SMARTSPACE_MEDIA_DATA, panel,
+ true, clock)
+
+ // Then it should be shown immediately after any actively playing controls
+ assertTrue(MediaPlayerData.playerKeys().elementAt(2).isSsMediaRec)
+ }
+
+ @Test
+ fun testOrderWithSmartspace_notPrioritized() {
+ testPlayerOrdering()
+
+ // If smartspace is not prioritized
+ MediaPlayerData.addMediaRecommendation(SMARTSPACE_KEY, EMPTY_SMARTSPACE_MEDIA_DATA, panel,
+ false, clock)
+
+ // Then it should be shown at the end of the carousel
+ val size = MediaPlayerData.playerKeys().size
+ assertTrue(MediaPlayerData.playerKeys().elementAt(size - 1).isSsMediaRec)
+ }
+}
\ No newline at end of file
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/MediaDataFilterTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/MediaDataFilterTest.kt
index 28aed20..a435e79 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/MediaDataFilterTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/MediaDataFilterTest.kt
@@ -86,6 +86,7 @@
@Before
fun setup() {
MockitoAnnotations.initMocks(this)
+ MediaPlayerData.clear()
mediaDataFilter = MediaDataFilter(context, broadcastDispatcher, mediaResumeListener,
lockscreenUserManager, executor, clock)
mediaDataFilter.mediaDataManager = mediaDataManager
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/MediaDataManagerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/MediaDataManagerTest.kt
index d864aae..2b2fc51 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/MediaDataManagerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/MediaDataManagerTest.kt
@@ -255,6 +255,7 @@
.onMediaDataLoaded(eq(PACKAGE_NAME), eq(KEY), capture(mediaDataCaptor), eq(true),
eq(false))
assertThat(mediaDataCaptor.value.resumption).isTrue()
+ assertThat(mediaDataCaptor.value.isPlaying).isFalse()
}
@Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/MediaPlayerDataTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/MediaPlayerDataTest.kt
index e9e965e..8dc9eff 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/MediaPlayerDataTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/MediaPlayerDataTest.kt
@@ -127,7 +127,7 @@
val players = MediaPlayerData.players()
assertThat(players).hasSize(6)
assertThat(players).containsExactly(playerIsPlaying, playerIsPlayingAndRemote,
- playerIsStoppedAndLocal, playerCanResume, playerIsStoppedAndRemote,
+ playerIsStoppedAndRemote, playerIsStoppedAndLocal, playerCanResume,
playerUndetermined).inOrder()
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/carrier/QSCarrierGroupControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/carrier/QSCarrierGroupControllerTest.java
index a1b7210..8ae7100 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/carrier/QSCarrierGroupControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/carrier/QSCarrierGroupControllerTest.java
@@ -39,8 +39,8 @@
import com.android.keyguard.CarrierTextManager;
import com.android.systemui.flags.FeatureFlags;
import com.android.systemui.plugins.ActivityStarter;
-import com.android.systemui.statusbar.policy.NetworkController;
-import com.android.systemui.statusbar.policy.NetworkController.MobileDataIndicators;
+import com.android.systemui.statusbar.connectivity.NetworkController;
+import com.android.systemui.statusbar.connectivity.NetworkController.MobileDataIndicators;
import com.android.systemui.util.CarrierConfigTracker;
import com.android.systemui.utils.leaks.LeakCheckedTest;
import com.android.systemui.utils.os.FakeHandler;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/CastTileTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/CastTileTest.java
index e939411..f0bd0657 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/CastTileTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/CastTileTest.java
@@ -43,12 +43,12 @@
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.qs.QSTileHost;
import com.android.systemui.qs.logging.QSLogger;
+import com.android.systemui.statusbar.connectivity.NetworkController;
+import com.android.systemui.statusbar.connectivity.NetworkController.WifiIndicators;
import com.android.systemui.statusbar.policy.CastController;
import com.android.systemui.statusbar.policy.CastController.CastDevice;
import com.android.systemui.statusbar.policy.HotspotController;
import com.android.systemui.statusbar.policy.KeyguardStateController;
-import com.android.systemui.statusbar.policy.NetworkController;
-import com.android.systemui.statusbar.policy.NetworkController.WifiIndicators;
import org.junit.Before;
import org.junit.Test;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetDialogControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetDialogControllerTest.java
index f9d5be6..fe32839 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetDialogControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetDialogControllerTest.java
@@ -48,9 +48,9 @@
import com.android.systemui.broadcast.BroadcastDispatcher;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.plugins.ActivityStarter;
+import com.android.systemui.statusbar.connectivity.NetworkController;
+import com.android.systemui.statusbar.connectivity.NetworkController.AccessPointController;
import com.android.systemui.statusbar.policy.KeyguardStateController;
-import com.android.systemui.statusbar.policy.NetworkController;
-import com.android.systemui.statusbar.policy.NetworkController.AccessPointController;
import com.android.systemui.toast.SystemUIToast;
import com.android.systemui.toast.ToastFactory;
import com.android.systemui.util.CarrierConfigTracker;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/user/UserDialogTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/user/UserDialogTest.kt
new file mode 100644
index 0000000..d5fe588
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/user/UserDialogTest.kt
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.qs.user
+
+import android.testing.AndroidTestingRunner
+import android.testing.TestableLooper
+import android.view.View
+import android.view.ViewGroup
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.google.common.truth.Truth.assertThat
+import org.junit.After
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@SmallTest
+@RunWith(AndroidTestingRunner::class)
+@TestableLooper.RunWithLooper
+class UserDialogTest : SysuiTestCase() {
+
+ private lateinit var dialog: UserDialog
+
+ @Before
+ fun setUp() {
+ dialog = UserDialog(mContext)
+ }
+
+ @After
+ fun tearDown() {
+ dialog.dismiss()
+ }
+
+ @Test
+ fun doneButtonExists() {
+ assertThat(dialog.doneButton).isInstanceOf(View::class.java)
+ }
+
+ @Test
+ fun settingsButtonExists() {
+ assertThat(dialog.settingsButton).isInstanceOf(View::class.java)
+ }
+
+ @Test
+ fun gridExistsAndIsViewGroup() {
+ assertThat(dialog.grid).isInstanceOf(ViewGroup::class.java)
+ }
+}
\ No newline at end of file
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/user/UserSwitchDialogControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/user/UserSwitchDialogControllerTest.kt
new file mode 100644
index 0000000..a1760a7
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/user/UserSwitchDialogControllerTest.kt
@@ -0,0 +1,203 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.qs.user
+
+import android.content.Intent
+import android.provider.Settings
+import android.testing.AndroidTestingRunner
+import android.view.View
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.plugins.ActivityStarter
+import com.android.systemui.plugins.FalsingManager
+import com.android.systemui.qs.PseudoGridView
+import com.android.systemui.qs.tiles.UserDetailView
+import com.android.systemui.statusbar.policy.UserSwitcherController
+import com.android.systemui.util.mockito.argumentCaptor
+import com.android.systemui.util.mockito.capture
+import com.android.systemui.util.mockito.eq
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.ArgumentCaptor
+import org.mockito.ArgumentMatcher
+import org.mockito.Captor
+import org.mockito.Mock
+import org.mockito.Mockito.`when`
+import org.mockito.Mockito.any
+import org.mockito.Mockito.anyInt
+import org.mockito.Mockito.argThat
+import org.mockito.Mockito.inOrder
+import org.mockito.Mockito.mock
+import org.mockito.Mockito.never
+import org.mockito.Mockito.verify
+import org.mockito.MockitoAnnotations
+import java.util.function.Consumer
+
+@SmallTest
+@RunWith(AndroidTestingRunner::class)
+class UserSwitchDialogControllerTest : SysuiTestCase() {
+
+ @Mock
+ private lateinit var dialog: UserDialog
+ @Mock
+ private lateinit var falsingManager: FalsingManager
+ @Mock
+ private lateinit var settingsView: View
+ @Mock
+ private lateinit var doneView: View
+ @Mock
+ private lateinit var activityStarter: ActivityStarter
+ @Mock
+ private lateinit var userDetailViewAdapter: UserDetailView.Adapter
+ @Mock
+ private lateinit var launchView: View
+ @Mock
+ private lateinit var gridView: PseudoGridView
+ @Captor
+ private lateinit var clickCaptor: ArgumentCaptor<View.OnClickListener>
+
+ private lateinit var controller: UserSwitchDialogController
+
+ @Before
+ fun setUp() {
+ MockitoAnnotations.initMocks(this)
+
+ `when`(dialog.settingsButton).thenReturn(settingsView)
+ `when`(dialog.doneButton).thenReturn(doneView)
+ `when`(dialog.grid).thenReturn(gridView)
+
+ `when`(launchView.context).thenReturn(mContext)
+
+ controller = UserSwitchDialogController(
+ { userDetailViewAdapter },
+ activityStarter,
+ falsingManager,
+ { dialog }
+ )
+ }
+
+ @Test
+ fun showDialog_callsDialogShow() {
+ controller.showDialog(launchView)
+ verify(dialog).show()
+ }
+
+ @Test
+ fun createCalledBeforeDoneButton() {
+ controller.showDialog(launchView)
+ val inOrder = inOrder(dialog)
+ inOrder.verify(dialog).create()
+ inOrder.verify(dialog).doneButton
+ }
+
+ @Test
+ fun createCalledBeforeSettingsButton() {
+ controller.showDialog(launchView)
+ val inOrder = inOrder(dialog)
+ inOrder.verify(dialog).create()
+ inOrder.verify(dialog).settingsButton
+ }
+
+ @Test
+ fun createCalledBeforeGrid() {
+ controller.showDialog(launchView)
+ val inOrder = inOrder(dialog)
+ inOrder.verify(dialog).create()
+ inOrder.verify(dialog).grid
+ }
+
+ @Test
+ fun dialog_showForAllUsers() {
+ controller.showDialog(launchView)
+ verify(dialog).setShowForAllUsers(true)
+ }
+
+ @Test
+ fun dialog_cancelOnTouchOutside() {
+ controller.showDialog(launchView)
+ verify(dialog).setCanceledOnTouchOutside(true)
+ }
+
+ @Test
+ fun adapterAndGridLinked() {
+ controller.showDialog(launchView)
+ verify(userDetailViewAdapter).linkToViewGroup(gridView)
+ }
+
+ @Test
+ fun clickDoneButton_dismiss() {
+ controller.showDialog(launchView)
+
+ verify(doneView).setOnClickListener(capture(clickCaptor))
+
+ clickCaptor.value.onClick(doneView)
+
+ verify(activityStarter, never()).postStartActivityDismissingKeyguard(any(), anyInt())
+ verify(dialog).dismiss()
+ }
+
+ @Test
+ fun clickSettingsButton_noFalsing_opensSettingsAndDismisses() {
+ `when`(falsingManager.isFalseTap(anyInt())).thenReturn(false)
+
+ controller.showDialog(launchView)
+
+ verify(settingsView).setOnClickListener(capture(clickCaptor))
+
+ clickCaptor.value.onClick(settingsView)
+
+ verify(activityStarter)
+ .postStartActivityDismissingKeyguard(
+ argThat(IntentMatcher(Settings.ACTION_USER_SETTINGS)),
+ eq(0)
+ )
+ verify(dialog).dismiss()
+ }
+
+ @Test
+ fun clickSettingsButton_Falsing_notOpensSettingsAndDismisses() {
+ `when`(falsingManager.isFalseTap(anyInt())).thenReturn(true)
+
+ controller.showDialog(launchView)
+
+ verify(settingsView).setOnClickListener(capture(clickCaptor))
+
+ clickCaptor.value.onClick(settingsView)
+
+ verify(activityStarter, never()).postStartActivityDismissingKeyguard(any(), anyInt())
+ verify(dialog).dismiss()
+ }
+
+ @Test
+ fun callbackFromDetailView_dismissesDialog() {
+ val captor = argumentCaptor<Consumer<UserSwitcherController.UserRecord>>()
+
+ controller.showDialog(launchView)
+ verify(userDetailViewAdapter).injectCallback(capture(captor))
+
+ captor.value.accept(mock(UserSwitcherController.UserRecord::class.java))
+
+ verify(dialog).dismiss()
+ }
+
+ private class IntentMatcher(private val action: String) : ArgumentMatcher<Intent> {
+ override fun matches(argument: Intent?): Boolean {
+ return argument?.action == action
+ }
+ }
+}
\ No newline at end of file
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/AccessPointControllerImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/connectivity/AccessPointControllerImplTest.kt
similarity index 99%
rename from packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/AccessPointControllerImplTest.kt
rename to packages/SystemUI/tests/src/com/android/systemui/statusbar/connectivity/AccessPointControllerImplTest.kt
index 4068f93..7896a26 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/AccessPointControllerImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/connectivity/AccessPointControllerImplTest.kt
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.systemui.statusbar.policy
+package com.android.systemui.statusbar.connectivity
import android.os.UserManager
import android.test.suitebuilder.annotation.SmallTest
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/CallbackHandlerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/connectivity/CallbackHandlerTest.java
similarity index 91%
rename from packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/CallbackHandlerTest.java
rename to packages/SystemUI/tests/src/com/android/systemui/statusbar/connectivity/CallbackHandlerTest.java
index 3c55df9..11a53c5 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/CallbackHandlerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/connectivity/CallbackHandlerTest.java
@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package com.android.systemui.statusbar.policy;
+package com.android.systemui.statusbar.connectivity;
import static junit.framework.Assert.assertEquals;
import static junit.framework.Assert.assertTrue;
@@ -28,11 +28,11 @@
import com.android.settingslib.mobile.TelephonyIcons;
import com.android.systemui.SysuiTestCase;
-import com.android.systemui.statusbar.policy.NetworkController.EmergencyListener;
-import com.android.systemui.statusbar.policy.NetworkController.IconState;
-import com.android.systemui.statusbar.policy.NetworkController.MobileDataIndicators;
-import com.android.systemui.statusbar.policy.NetworkController.SignalCallback;
-import com.android.systemui.statusbar.policy.NetworkController.WifiIndicators;
+import com.android.systemui.statusbar.connectivity.NetworkController.EmergencyListener;
+import com.android.systemui.statusbar.connectivity.NetworkController.IconState;
+import com.android.systemui.statusbar.connectivity.NetworkController.MobileDataIndicators;
+import com.android.systemui.statusbar.connectivity.NetworkController.SignalCallback;
+import com.android.systemui.statusbar.connectivity.NetworkController.WifiIndicators;
import com.android.systemui.tests.R;
import org.junit.Before;
@@ -182,7 +182,8 @@
@Test
public void testSignalCallback_setIsAirplaneMode() {
- IconState state = new IconState(true, R.drawable.stat_sys_airplane_mode, "Test Description");
+ IconState state =
+ new IconState(true, R.drawable.stat_sys_airplane_mode, "Test Description");
mHandler.setIsAirplaneMode(state);
waitForCallbacks();
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerBaseTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/connectivity/NetworkControllerBaseTest.java
similarity index 97%
rename from packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerBaseTest.java
rename to packages/SystemUI/tests/src/com/android/systemui/statusbar/connectivity/NetworkControllerBaseTest.java
index c488ee9..67cab74 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerBaseTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/connectivity/NetworkControllerBaseTest.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.systemui.statusbar.policy;
+package com.android.systemui.statusbar.connectivity;
import static android.telephony.AccessNetworkConstants.TRANSPORT_TYPE_WWAN;
import static android.telephony.NetworkRegistrationInfo.DOMAIN_PS;
@@ -72,10 +72,11 @@
import com.android.systemui.demomode.DemoModeController;
import com.android.systemui.dump.DumpManager;
import com.android.systemui.flags.FeatureFlags;
+import com.android.systemui.statusbar.connectivity.NetworkController.IconState;
+import com.android.systemui.statusbar.connectivity.NetworkController.MobileDataIndicators;
+import com.android.systemui.statusbar.connectivity.NetworkController.SignalCallback;
+import com.android.systemui.statusbar.policy.DeviceProvisionedController;
import com.android.systemui.statusbar.policy.DeviceProvisionedController.DeviceProvisionedListener;
-import com.android.systemui.statusbar.policy.NetworkController.IconState;
-import com.android.systemui.statusbar.policy.NetworkController.MobileDataIndicators;
-import com.android.systemui.statusbar.policy.NetworkController.SignalCallback;
import com.android.systemui.telephony.TelephonyListenerManager;
import com.android.systemui.util.CarrierConfigTracker;
import com.android.systemui.util.concurrency.FakeExecutor;
@@ -268,7 +269,7 @@
setSubscriptions(mSubId);
mMobileSignalController = mNetworkController.mMobileSignalControllers.get(mSubId);
ArgumentCaptor<ConnectivityManager.NetworkCallback> callbackArg =
- ArgumentCaptor.forClass(ConnectivityManager.NetworkCallback.class);
+ ArgumentCaptor.forClass(ConnectivityManager.NetworkCallback.class);
verify(mMockCm, atLeastOnce())
.registerDefaultNetworkCallback(callbackArg.capture(), isA(Handler.class));
int captureSize = callbackArg.getAllValues().size();
@@ -404,7 +405,7 @@
}
private static void setConnectivityCommon(NetworkCapabilities.Builder builder,
- int networkType, boolean validated, boolean isConnected){
+ int networkType, boolean validated, boolean isConnected) {
// TODO: Separate out into several NetworkCapabilities.
if (isConnected) {
builder.addTransportType(networkType);
@@ -538,7 +539,7 @@
}
protected void verifyLastMobileDataIndicators(boolean visible, int icon, int typeIcon,
- boolean roaming, boolean inet) {
+ boolean roaming, boolean inet) {
ArgumentCaptor<MobileDataIndicators> indicatorsArg =
ArgumentCaptor.forClass(MobileDataIndicators.class);
@@ -646,7 +647,7 @@
}
protected void assertNetworkNameEquals(String expected) {
- assertEquals("Network name", expected, mMobileSignalController.getState().networkName);
+ assertEquals("Network name", expected, mMobileSignalController.getState().networkName);
}
protected void assertDataNetworkNameEquals(String expected) {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerDataTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/connectivity/NetworkControllerDataTest.java
similarity index 93%
rename from packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerDataTest.java
rename to packages/SystemUI/tests/src/com/android/systemui/statusbar/connectivity/NetworkControllerDataTest.java
index 3433a14..00dedd9 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerDataTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/connectivity/NetworkControllerDataTest.java
@@ -1,4 +1,20 @@
-package com.android.systemui.statusbar.policy;
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.connectivity;
import static android.telephony.AccessNetworkConstants.TRANSPORT_TYPE_WWAN;
import static android.telephony.NetworkRegistrationInfo.DOMAIN_PS;
@@ -22,6 +38,7 @@
import com.android.settingslib.mobile.TelephonyIcons;
import com.android.settingslib.net.DataUsageController;
import com.android.systemui.dump.DumpManager;
+import com.android.systemui.statusbar.policy.DeviceProvisionedController;
import com.android.systemui.util.CarrierConfigTracker;
import org.junit.Test;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerEthernetTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/connectivity/NetworkControllerEthernetTest.java
similarity index 71%
rename from packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerEthernetTest.java
rename to packages/SystemUI/tests/src/com/android/systemui/statusbar/connectivity/NetworkControllerEthernetTest.java
index 6aab9c7..675d755 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerEthernetTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/connectivity/NetworkControllerEthernetTest.java
@@ -1,4 +1,20 @@
-package com.android.systemui.statusbar.policy;
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.connectivity;
import static junit.framework.Assert.assertEquals;
@@ -7,7 +23,7 @@
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper.RunWithLooper;
-import com.android.systemui.statusbar.policy.NetworkController.IconState;
+import com.android.systemui.statusbar.connectivity.NetworkController.IconState;
import org.junit.Test;
import org.junit.runner.RunWith;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerSignalTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/connectivity/NetworkControllerSignalTest.java
similarity index 78%
rename from packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerSignalTest.java
rename to packages/SystemUI/tests/src/com/android/systemui/statusbar/connectivity/NetworkControllerSignalTest.java
index 4ff1301..73eddd1 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerSignalTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/connectivity/NetworkControllerSignalTest.java
@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package com.android.systemui.statusbar.policy;
+package com.android.systemui.statusbar.connectivity;
import static junit.framework.Assert.assertEquals;
import static junit.framework.Assert.assertFalse;
@@ -42,6 +42,7 @@
import com.android.settingslib.net.DataUsageController;
import com.android.systemui.R;
import com.android.systemui.dump.DumpManager;
+import com.android.systemui.statusbar.policy.DeviceProvisionedController;
import com.android.systemui.util.CarrierConfigTracker;
import org.junit.Test;
@@ -280,7 +281,7 @@
// TODO: Put this somewhere else, maybe in its own file.
@Test
public void testHasCorrectMobileControllers() {
- int[] testSubscriptions = new int[] { 1, 5, 3 };
+ int[] testSubscriptions = new int[]{1, 5, 3};
int notTestSubscription = 0;
MobileSignalController mobileSignalController = Mockito.mock(MobileSignalController.class);
@@ -312,8 +313,8 @@
// We will not add one subscription to make sure it's controller gets removed.
int indexToSkipSubscription = 1;
- int[] testSubscriptions = new int[] { 1, 5, 3 };
- MobileSignalController[] mobileSignalControllers = new MobileSignalController[] {
+ int[] testSubscriptions = new int[]{1, 5, 3};
+ MobileSignalController[] mobileSignalControllers = new MobileSignalController[]{
Mockito.mock(MobileSignalController.class),
Mockito.mock(MobileSignalController.class),
Mockito.mock(MobileSignalController.class),
@@ -401,24 +402,24 @@
@Test
public void testOnReceive_stringsUpdatedAction_bothFalse() {
Intent intent = createStringsUpdatedIntent(false /* showSpn */,
- "Irrelevant" /* spn */,
- false /* showPlmn */,
- "Irrelevant" /* plmn */);
+ "Irrelevant" /* spn */,
+ false /* showPlmn */,
+ "Irrelevant" /* plmn */);
mNetworkController.onReceive(mContext, intent);
String defaultNetworkName = mMobileSignalController
.getTextIfExists(
- com.android.internal.R.string.lockscreen_carrier_default).toString();
+ com.android.internal.R.string.lockscreen_carrier_default).toString();
assertNetworkNameEquals(defaultNetworkName);
}
@Test
public void testOnReceive_stringsUpdatedAction_bothTrueAndNull() {
Intent intent = createStringsUpdatedIntent(true /* showSpn */,
- null /* spn */,
- true /* showPlmn */,
- null /* plmn */);
+ null /* spn */,
+ true /* showPlmn */,
+ null /* plmn */);
mNetworkController.onReceive(mContext, intent);
@@ -433,15 +434,15 @@
String plmn = "Test2";
Intent intent = createStringsUpdatedIntent(true /* showSpn */,
- spn /* spn */,
- true /* showPlmn */,
- plmn /* plmn */);
+ spn /* spn */,
+ true /* showPlmn */,
+ plmn /* plmn */);
mNetworkController.onReceive(mContext, intent);
assertNetworkNameEquals(plmn
+ mMobileSignalController.getTextIfExists(
- R.string.status_bar_network_name_separator).toString()
+ R.string.status_bar_network_name_separator).toString()
+ spn);
}
@@ -477,145 +478,149 @@
@Test
public void testOnUpdateDataActivity_dataOut() {
- setupDefaultSignal();
+ setupDefaultSignal();
- updateDataActivity(TelephonyManager.DATA_ACTIVITY_OUT);
+ updateDataActivity(TelephonyManager.DATA_ACTIVITY_OUT);
- verifyLastQsMobileDataIndicators(true /* visible */,
- DEFAULT_LEVEL /* icon */,
- DEFAULT_QS_ICON /* typeIcon */,
- false /* dataIn */,
- true /* dataOut */);
+ verifyLastQsMobileDataIndicators(true /* visible */,
+ DEFAULT_LEVEL /* icon */,
+ DEFAULT_QS_ICON /* typeIcon */,
+ false /* dataIn */,
+ true /* dataOut */);
}
@Test
public void testOnUpdateDataActivity_dataInOut() {
- setupDefaultSignal();
+ setupDefaultSignal();
- updateDataActivity(TelephonyManager.DATA_ACTIVITY_INOUT);
+ updateDataActivity(TelephonyManager.DATA_ACTIVITY_INOUT);
- verifyLastQsMobileDataIndicators(true /* visible */,
- DEFAULT_LEVEL /* icon */,
- DEFAULT_QS_ICON /* typeIcon */,
- true /* dataIn */,
- true /* dataOut */);
+ verifyLastQsMobileDataIndicators(true /* visible */,
+ DEFAULT_LEVEL /* icon */,
+ DEFAULT_QS_ICON /* typeIcon */,
+ true /* dataIn */,
+ true /* dataOut */);
}
@Test
public void testOnUpdateDataActivity_dataActivityNone() {
- setupDefaultSignal();
+ setupDefaultSignal();
- updateDataActivity(TelephonyManager.DATA_ACTIVITY_NONE);
+ updateDataActivity(TelephonyManager.DATA_ACTIVITY_NONE);
- verifyLastQsMobileDataIndicators(true /* visible */,
- DEFAULT_LEVEL /* icon */,
- DEFAULT_QS_ICON /* typeIcon */,
- false /* dataIn */,
- false /* dataOut */);
+ verifyLastQsMobileDataIndicators(true /* visible */,
+ DEFAULT_LEVEL /* icon */,
+ DEFAULT_QS_ICON /* typeIcon */,
+ false /* dataIn */,
+ false /* dataOut */);
}
@Test
public void testCarrierNetworkChange_carrierNetworkChange() {
- int strength = SignalStrength.SIGNAL_STRENGTH_GREAT;
+ int strength = SignalStrength.SIGNAL_STRENGTH_GREAT;
- setupDefaultSignal();
- setLevel(strength);
+ setupDefaultSignal();
+ setLevel(strength);
- // Verify baseline
- verifyLastMobileDataIndicators(true /* visible */,
- strength /* strengthIcon */,
- DEFAULT_ICON /* typeIcon */);
+ // Verify baseline
+ verifyLastMobileDataIndicators(true /* visible */,
+ strength /* strengthIcon */,
+ DEFAULT_ICON /* typeIcon */);
- // API call is made
- setCarrierNetworkChange(true /* enabled */);
+ // API call is made
+ setCarrierNetworkChange(true /* enabled */);
- // Carrier network change is true, show special indicator
- verifyLastMobileDataIndicators(true /* visible */,
- SignalDrawable.getCarrierChangeState(CellSignalStrength.getNumSignalStrengthLevels()),
- 0 /* typeIcon */);
+ // Carrier network change is true, show special indicator
+ verifyLastMobileDataIndicators(true /* visible */,
+ SignalDrawable.getCarrierChangeState(
+ CellSignalStrength.getNumSignalStrengthLevels()),
+ 0 /* typeIcon */);
- // Revert back
- setCarrierNetworkChange(false /* enabled */);
+ // Revert back
+ setCarrierNetworkChange(false /* enabled */);
- // Verify back in previous state
- verifyLastMobileDataIndicators(true /* visible */,
- strength /* strengthIcon */,
- DEFAULT_ICON /* typeIcon */);
+ // Verify back in previous state
+ verifyLastMobileDataIndicators(true /* visible */,
+ strength /* strengthIcon */,
+ DEFAULT_ICON /* typeIcon */);
}
@Test
public void testCarrierNetworkChange_roamingBeforeNetworkChange() {
- int strength = SignalStrength.SIGNAL_STRENGTH_GREAT;
+ int strength = SignalStrength.SIGNAL_STRENGTH_GREAT;
- setupDefaultSignal();
- setLevel(strength);
- setGsmRoaming(true);
+ setupDefaultSignal();
+ setLevel(strength);
+ setGsmRoaming(true);
- // Verify baseline
- verifyLastMobileDataIndicators(true /* visible */,
- strength /* strengthIcon */,
- DEFAULT_ICON /* typeIcon */,
- true /* roaming */);
+ // Verify baseline
+ verifyLastMobileDataIndicators(true /* visible */,
+ strength /* strengthIcon */,
+ DEFAULT_ICON /* typeIcon */,
+ true /* roaming */);
- // API call is made
- setCarrierNetworkChange(true /* enabled */);
+ // API call is made
+ setCarrierNetworkChange(true /* enabled */);
- // Carrier network change is true, show special indicator, no roaming.
- verifyLastMobileDataIndicators(true /* visible */,
- SignalDrawable.getCarrierChangeState(CellSignalStrength.getNumSignalStrengthLevels()),
- 0 /* typeIcon */,
- false /* roaming */);
+ // Carrier network change is true, show special indicator, no roaming.
+ verifyLastMobileDataIndicators(true /* visible */,
+ SignalDrawable.getCarrierChangeState(
+ CellSignalStrength.getNumSignalStrengthLevels()),
+ 0 /* typeIcon */,
+ false /* roaming */);
- // Revert back
- setCarrierNetworkChange(false /* enabled */);
+ // Revert back
+ setCarrierNetworkChange(false /* enabled */);
- // Verify back in previous state
- verifyLastMobileDataIndicators(true /* visible */,
- strength /* strengthIcon */,
- DEFAULT_ICON /* typeIcon */,
- true /* roaming */);
+ // Verify back in previous state
+ verifyLastMobileDataIndicators(true /* visible */,
+ strength /* strengthIcon */,
+ DEFAULT_ICON /* typeIcon */,
+ true /* roaming */);
}
@Test
public void testCarrierNetworkChange_roamingAfterNetworkChange() {
- int strength = SignalStrength.SIGNAL_STRENGTH_GREAT;
+ int strength = SignalStrength.SIGNAL_STRENGTH_GREAT;
- setupDefaultSignal();
- setLevel(strength);
+ setupDefaultSignal();
+ setLevel(strength);
- // Verify baseline
- verifyLastMobileDataIndicators(true /* visible */,
- strength /* strengthIcon */,
- DEFAULT_ICON /* typeIcon */,
- false /* roaming */);
+ // Verify baseline
+ verifyLastMobileDataIndicators(true /* visible */,
+ strength /* strengthIcon */,
+ DEFAULT_ICON /* typeIcon */,
+ false /* roaming */);
- // API call is made
- setCarrierNetworkChange(true /* enabled */);
+ // API call is made
+ setCarrierNetworkChange(true /* enabled */);
- // Carrier network change is true, show special indicator, no roaming.
- verifyLastMobileDataIndicators(true /* visible */,
- SignalDrawable.getCarrierChangeState(CellSignalStrength.getNumSignalStrengthLevels()),
- 0 /* typeIcon */,
- false /* roaming */);
+ // Carrier network change is true, show special indicator, no roaming.
+ verifyLastMobileDataIndicators(true /* visible */,
+ SignalDrawable.getCarrierChangeState(
+ CellSignalStrength.getNumSignalStrengthLevels()),
+ 0 /* typeIcon */,
+ false /* roaming */);
- setGsmRoaming(true);
+ setGsmRoaming(true);
- // Roaming should not show.
- verifyLastMobileDataIndicators(true /* visible */,
- SignalDrawable.getCarrierChangeState(CellSignalStrength.getNumSignalStrengthLevels()),
- 0 /* typeIcon */,
- false /* roaming */);
+ // Roaming should not show.
+ verifyLastMobileDataIndicators(true /* visible */,
+ SignalDrawable.getCarrierChangeState(
+ CellSignalStrength.getNumSignalStrengthLevels()),
+ 0 /* typeIcon */,
+ false /* roaming */);
- // Revert back
- setCarrierNetworkChange(false /* enabled */);
+ // Revert back
+ setCarrierNetworkChange(false /* enabled */);
- // Verify back in previous state
- verifyLastMobileDataIndicators(true /* visible */,
- strength /* strengthIcon */,
- DEFAULT_ICON /* typeIcon */,
- true /* roaming */);
+ // Verify back in previous state
+ verifyLastMobileDataIndicators(true /* visible */,
+ strength /* strengthIcon */,
+ DEFAULT_ICON /* typeIcon */,
+ true /* roaming */);
}
private void verifyEmergencyOnly(boolean isEmergencyOnly) {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerWifiTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/connectivity/NetworkControllerWifiTest.java
similarity index 94%
rename from packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerWifiTest.java
rename to packages/SystemUI/tests/src/com/android/systemui/statusbar/connectivity/NetworkControllerWifiTest.java
index 4a5770d..ffeaf20 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerWifiTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/connectivity/NetworkControllerWifiTest.java
@@ -1,10 +1,25 @@
-package com.android.systemui.statusbar.policy;
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.connectivity;
import static junit.framework.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.anyLong;
-import static org.mockito.Matchers.anyLong;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
@@ -21,7 +36,7 @@
import android.testing.TestableLooper.RunWithLooper;
import com.android.settingslib.mobile.TelephonyIcons;
-import com.android.systemui.statusbar.policy.NetworkController.WifiIndicators;
+import com.android.systemui.statusbar.connectivity.NetworkController.WifiIndicators;
import org.junit.Before;
import org.junit.Test;
@@ -165,7 +180,7 @@
}
@Test
- public void testWifiIconDisconnectedViaCallback(){
+ public void testWifiIconDisconnectedViaCallback() {
// Setup normal connection
String testSsid = "Test SSID";
int testLevel = 2;
@@ -183,7 +198,7 @@
}
@Test
- public void testVpnWithUnderlyingWifi(){
+ public void testVpnWithUnderlyingWifi() {
String testSsid = "Test SSID";
int testLevel = 2;
setWifiEnabled(true);
@@ -299,7 +314,7 @@
protected void setWifiLevel(int level) {
float amountPerLevel = (MAX_RSSI - MIN_RSSI) / (WifiIcons.WIFI_LEVEL_COUNT - 1);
- int rssi = (int)(MIN_RSSI + level * amountPerLevel);
+ int rssi = (int) (MIN_RSSI + level * amountPerLevel);
// Put RSSI in the middle of the range.
rssi += amountPerLevel / 2;
when(mWifiInfo.getRssi()).thenReturn(rssi);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutControllerTest.java
index 07ebaea..baed694 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutControllerTest.java
@@ -75,6 +75,7 @@
import com.android.systemui.statusbar.phone.ShadeController;
import com.android.systemui.statusbar.phone.StatusBar;
import com.android.systemui.statusbar.policy.ConfigurationController;
+import com.android.systemui.statusbar.policy.DeviceProvisionedController;
import com.android.systemui.statusbar.policy.ZenModeController;
import com.android.systemui.tuner.TunerService;
@@ -99,6 +100,7 @@
@Mock private HeadsUpManagerPhone mHeadsUpManager;
@Mock private NotificationRoundnessManager mNotificationRoundnessManager;
@Mock private TunerService mTunerService;
+ @Mock private DeviceProvisionedController mDeviceProvisionedController;
@Mock private DynamicPrivacyController mDynamicPrivacyController;
@Mock private ConfigurationController mConfigurationController;
@Mock private NotificationStackScrollLayout mNotificationStackScrollLayout;
@@ -152,6 +154,7 @@
mHeadsUpManager,
mNotificationRoundnessManager,
mTunerService,
+ mDeviceProvisionedController,
mDynamicPrivacyController,
mConfigurationController,
mSysuiStatusBarStateController,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java
index 4e76b16..9f42fa4d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java
@@ -280,6 +280,8 @@
@Test
public void testUpdateFooter_noNotifications() {
setBarStateForTest(StatusBarState.SHADE);
+ mStackScroller.setCurrentUserSetup(true);
+
FooterView view = mock(FooterView.class);
mStackScroller.setFooterView(view);
mStackScroller.updateFooter();
@@ -289,6 +291,7 @@
@Test
public void testUpdateFooter_remoteInput() {
setBarStateForTest(StatusBarState.SHADE);
+ mStackScroller.setCurrentUserSetup(true);
ExpandableNotificationRow row = mock(ExpandableNotificationRow.class);
when(row.canViewBeDismissed()).thenReturn(true);
@@ -308,6 +311,7 @@
@Test
public void testUpdateFooter_oneClearableNotification() {
setBarStateForTest(StatusBarState.SHADE);
+ mStackScroller.setCurrentUserSetup(true);
when(mEmptyShadeView.getVisibility()).thenReturn(GONE);
when(mStackScrollLayoutController.hasActiveClearableNotifications(ROWS_ALL))
@@ -321,8 +325,25 @@
}
@Test
+ public void testUpdateFooter_oneClearableNotification_beforeUserSetup() {
+ setBarStateForTest(StatusBarState.SHADE);
+ mStackScroller.setCurrentUserSetup(false);
+
+ when(mEmptyShadeView.getVisibility()).thenReturn(GONE);
+ when(mStackScrollLayoutController.hasActiveClearableNotifications(ROWS_ALL))
+ .thenReturn(true);
+ when(mStackScrollLayoutController.hasActiveNotifications()).thenReturn(true);
+
+ FooterView view = mock(FooterView.class);
+ mStackScroller.setFooterView(view);
+ mStackScroller.updateFooter();
+ verify(mStackScroller).updateFooterView(false, true, true);
+ }
+
+ @Test
public void testUpdateFooter_oneNonClearableNotification() {
setBarStateForTest(StatusBarState.SHADE);
+ mStackScroller.setCurrentUserSetup(true);
ExpandableNotificationRow row = mock(ExpandableNotificationRow.class);
when(row.canViewBeDismissed()).thenReturn(false);
@@ -341,6 +362,8 @@
@Test
public void testUpdateFooter_atEnd() {
+ mStackScroller.setCurrentUserSetup(true);
+
// add footer
mStackScroller.inflateFooterView();
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragmentTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragmentTest.java
index b08dbee..f23f148 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragmentTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragmentTest.java
@@ -45,10 +45,10 @@
import com.android.systemui.statusbar.CommandQueue;
import com.android.systemui.statusbar.DisableFlagsLogger;
import com.android.systemui.statusbar.OperatorNameViewController;
+import com.android.systemui.statusbar.connectivity.NetworkController;
import com.android.systemui.statusbar.events.SystemStatusAnimationScheduler;
import com.android.systemui.statusbar.phone.ongoingcall.OngoingCallController;
import com.android.systemui.statusbar.policy.KeyguardStateController;
-import com.android.systemui.statusbar.policy.NetworkController;
import org.junit.Before;
import org.junit.Ignore;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationPanelViewControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationPanelViewControllerTest.java
index f7423bb..b18ea4b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationPanelViewControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationPanelViewControllerTest.java
@@ -300,6 +300,8 @@
private LockscreenGestureLogger mLockscreenGestureLogger;
@Mock
private DumpManager mDumpManager;
+ @Mock
+ private NotificationsQSContainerController mNotificationsQSContainerController;
private SysuiStatusBarStateController mStatusBarStateController;
private NotificationPanelViewController mNotificationPanelViewController;
@@ -414,6 +416,7 @@
() -> flingAnimationUtilsBuilder, mStatusBarTouchableRegionManager,
mConversationNotificationManager, mMediaHiearchyManager,
mStatusBarKeyguardViewManager,
+ mNotificationsQSContainerController,
mNotificationStackScrollLayoutController,
mKeyguardStatusViewComponentFactory,
mKeyguardQsUserSwitchComponentFactory,
@@ -469,8 +472,8 @@
}
@Test
- public void testSetMinFraction() {
- mNotificationPanelViewController.setMinFraction(0.5f);
+ public void testSetPanelScrimMinFraction() {
+ mNotificationPanelViewController.setPanelScrimMinFraction(0.5f);
verify(mNotificationShadeDepthController).setPanelPullDownMinFraction(eq(0.5f));
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationQSContainerControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationQSContainerControllerTest.kt
new file mode 100644
index 0000000..337e64592
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationQSContainerControllerTest.kt
@@ -0,0 +1,254 @@
+package com.android.systemui.statusbar.phone
+
+import android.testing.AndroidTestingRunner
+import android.testing.TestableLooper
+import android.view.WindowInsets
+import android.view.WindowManagerPolicyConstants
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.navigationbar.NavigationModeController
+import com.android.systemui.navigationbar.NavigationModeController.ModeChangedListener
+import com.android.systemui.recents.OverviewProxyService
+import com.android.systemui.recents.OverviewProxyService.OverviewProxyListener
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.ArgumentCaptor
+import org.mockito.Captor
+import org.mockito.Mock
+import org.mockito.Mockito
+import org.mockito.Mockito.RETURNS_DEEP_STUBS
+import org.mockito.Mockito.anyInt
+import org.mockito.Mockito.doNothing
+import org.mockito.Mockito.eq
+import org.mockito.Mockito.mock
+import org.mockito.Mockito.verify
+import org.mockito.MockitoAnnotations
+import java.util.function.Consumer
+import org.mockito.Mockito.`when` as whenever
+
+@SmallTest
+@RunWith(AndroidTestingRunner::class)
+@TestableLooper.RunWithLooper
+class NotificationQSContainerControllerTest : SysuiTestCase() {
+
+ companion object {
+ const val STABLE_INSET_BOTTOM = 100
+ const val CUTOUT_HEIGHT = 50
+ const val GESTURES_NAVIGATION = WindowManagerPolicyConstants.NAV_BAR_MODE_GESTURAL
+ const val BUTTONS_NAVIGATION = WindowManagerPolicyConstants.NAV_BAR_MODE_3BUTTON
+ const val NOTIFICATIONS_MARGIN = 50
+ }
+
+ @Mock
+ private lateinit var navigationModeController: NavigationModeController
+ @Mock
+ private lateinit var overviewProxyService: OverviewProxyService
+ @Mock
+ private lateinit var notificationsQSContainer: NotificationsQuickSettingsContainer
+ @Captor
+ lateinit var navigationModeCaptor: ArgumentCaptor<ModeChangedListener>
+ @Captor
+ lateinit var taskbarVisibilityCaptor: ArgumentCaptor<OverviewProxyListener>
+ @Captor
+ lateinit var windowInsetsCallbackCaptor: ArgumentCaptor<Consumer<WindowInsets>>
+
+ private lateinit var notificationsQSContainerController: NotificationsQSContainerController
+ private lateinit var navigationModeCallback: ModeChangedListener
+ private lateinit var taskbarVisibilityCallback: OverviewProxyListener
+ private lateinit var windowInsetsCallback: Consumer<WindowInsets>
+
+ @Before
+ fun setup() {
+ MockitoAnnotations.initMocks(this)
+ notificationsQSContainerController = NotificationsQSContainerController(
+ notificationsQSContainer,
+ navigationModeController,
+ overviewProxyService
+ )
+ whenever(notificationsQSContainer.defaultNotificationsMarginBottom)
+ .thenReturn(NOTIFICATIONS_MARGIN)
+ whenever(navigationModeController.addListener(navigationModeCaptor.capture()))
+ .thenReturn(GESTURES_NAVIGATION)
+ doNothing().`when`(overviewProxyService).addCallback(taskbarVisibilityCaptor.capture())
+ doNothing().`when`(notificationsQSContainer)
+ .setInsetsChangedListener(windowInsetsCallbackCaptor.capture())
+
+ notificationsQSContainerController.init()
+ notificationsQSContainerController.onViewAttached()
+
+ navigationModeCallback = navigationModeCaptor.value
+ taskbarVisibilityCallback = taskbarVisibilityCaptor.value
+ windowInsetsCallback = windowInsetsCallbackCaptor.value
+ }
+
+ @Test
+ fun testTaskbarVisibleInSplitShade() {
+ notificationsQSContainerController.splitShadeEnabled = true
+ given(taskbarVisible = true,
+ navigationMode = GESTURES_NAVIGATION,
+ insets = windowInsets().withStableBottom())
+ then(expectedContainerPadding = 0, // taskbar should disappear when shade is expanded
+ expectedNotificationsMargin = NOTIFICATIONS_MARGIN)
+
+ given(taskbarVisible = true,
+ navigationMode = BUTTONS_NAVIGATION,
+ insets = windowInsets().withStableBottom())
+ then(expectedContainerPadding = STABLE_INSET_BOTTOM,
+ expectedNotificationsMargin = NOTIFICATIONS_MARGIN)
+ }
+
+ @Test
+ fun testTaskbarNotVisibleInSplitShade() {
+ // when taskbar is not visible, it means we're on the home screen
+ notificationsQSContainerController.splitShadeEnabled = true
+ given(taskbarVisible = false,
+ navigationMode = GESTURES_NAVIGATION,
+ insets = windowInsets().withStableBottom())
+ then(expectedContainerPadding = 0)
+
+ given(taskbarVisible = false,
+ navigationMode = BUTTONS_NAVIGATION,
+ insets = windowInsets().withStableBottom())
+ then(expectedContainerPadding = 0, // qs goes full height as it's not obscuring nav buttons
+ expectedNotificationsMargin = STABLE_INSET_BOTTOM + NOTIFICATIONS_MARGIN)
+ }
+
+ @Test
+ fun testTaskbarNotVisibleInSplitShadeWithCutout() {
+ notificationsQSContainerController.splitShadeEnabled = true
+ given(taskbarVisible = false,
+ navigationMode = GESTURES_NAVIGATION,
+ insets = windowInsets().withCutout())
+ then(expectedContainerPadding = CUTOUT_HEIGHT)
+
+ given(taskbarVisible = false,
+ navigationMode = BUTTONS_NAVIGATION,
+ insets = windowInsets().withCutout().withStableBottom())
+ then(expectedContainerPadding = 0,
+ expectedNotificationsMargin = STABLE_INSET_BOTTOM + NOTIFICATIONS_MARGIN)
+ }
+
+ @Test
+ fun testTaskbarVisibleInSinglePaneShade() {
+ notificationsQSContainerController.splitShadeEnabled = false
+ given(taskbarVisible = true,
+ navigationMode = GESTURES_NAVIGATION,
+ insets = windowInsets().withStableBottom())
+ then(expectedContainerPadding = 0)
+
+ given(taskbarVisible = true,
+ navigationMode = BUTTONS_NAVIGATION,
+ insets = windowInsets().withStableBottom())
+ then(expectedContainerPadding = STABLE_INSET_BOTTOM)
+ }
+
+ @Test
+ fun testTaskbarNotVisibleInSinglePaneShade() {
+ notificationsQSContainerController.splitShadeEnabled = false
+ given(taskbarVisible = false,
+ navigationMode = GESTURES_NAVIGATION,
+ insets = emptyInsets())
+ then(expectedContainerPadding = 0)
+
+ given(taskbarVisible = false,
+ navigationMode = GESTURES_NAVIGATION,
+ insets = windowInsets().withCutout().withStableBottom())
+ then(expectedContainerPadding = CUTOUT_HEIGHT)
+
+ given(taskbarVisible = false,
+ navigationMode = BUTTONS_NAVIGATION,
+ insets = windowInsets().withStableBottom())
+ then(expectedContainerPadding = 0,
+ expectedQsPadding = STABLE_INSET_BOTTOM)
+ }
+
+ @Test
+ fun testCustomizingInSinglePaneShade() {
+ notificationsQSContainerController.splitShadeEnabled = false
+ notificationsQSContainerController.setCustomizerShowing(true)
+ // always sets spacings to 0
+ given(taskbarVisible = false,
+ navigationMode = GESTURES_NAVIGATION,
+ insets = windowInsets().withStableBottom())
+ then(expectedContainerPadding = 0,
+ expectedNotificationsMargin = 0)
+
+ given(taskbarVisible = false,
+ navigationMode = BUTTONS_NAVIGATION,
+ insets = emptyInsets())
+ then(expectedContainerPadding = 0,
+ expectedNotificationsMargin = 0)
+ }
+
+ @Test
+ fun testDetailShowingInSinglePaneShade() {
+ notificationsQSContainerController.splitShadeEnabled = false
+ notificationsQSContainerController.setDetailShowing(true)
+ // always sets spacings to 0
+ given(taskbarVisible = false,
+ navigationMode = GESTURES_NAVIGATION,
+ insets = windowInsets().withStableBottom())
+ then(expectedContainerPadding = 0,
+ expectedNotificationsMargin = 0)
+
+ given(taskbarVisible = false,
+ navigationMode = BUTTONS_NAVIGATION,
+ insets = emptyInsets())
+ then(expectedContainerPadding = 0,
+ expectedNotificationsMargin = 0)
+ }
+
+ @Test
+ fun testDetailShowingInSplitShade() {
+ notificationsQSContainerController.splitShadeEnabled = true
+ given(taskbarVisible = false,
+ navigationMode = GESTURES_NAVIGATION,
+ insets = windowInsets().withStableBottom())
+ then(expectedContainerPadding = 0)
+
+ notificationsQSContainerController.setDetailShowing(true)
+ // should not influence spacing
+ given(taskbarVisible = false,
+ navigationMode = BUTTONS_NAVIGATION,
+ insets = emptyInsets())
+ then(expectedContainerPadding = 0)
+ }
+
+ private fun given(
+ taskbarVisible: Boolean,
+ navigationMode: Int,
+ insets: WindowInsets
+ ) {
+ Mockito.clearInvocations(notificationsQSContainer)
+ taskbarVisibilityCallback.onTaskbarStatusUpdated(taskbarVisible, false)
+ navigationModeCallback.onNavigationModeChanged(navigationMode)
+ windowInsetsCallback.accept(insets)
+ }
+
+ fun then(
+ expectedContainerPadding: Int,
+ expectedNotificationsMargin: Int = NOTIFICATIONS_MARGIN,
+ expectedQsPadding: Int = 0
+ ) {
+ verify(notificationsQSContainer)
+ .setPadding(anyInt(), anyInt(), anyInt(), eq(expectedContainerPadding))
+ verify(notificationsQSContainer).setNotificationsMarginBottom(expectedNotificationsMargin)
+ verify(notificationsQSContainer).setQSScrollPaddingBottom(expectedQsPadding)
+ Mockito.clearInvocations(notificationsQSContainer)
+ }
+
+ private fun windowInsets() = mock(WindowInsets::class.java, RETURNS_DEEP_STUBS)
+
+ private fun emptyInsets() = mock(WindowInsets::class.java)
+
+ private fun WindowInsets.withCutout(): WindowInsets {
+ whenever(displayCutout.safeInsetBottom).thenReturn(CUTOUT_HEIGHT)
+ return this
+ }
+
+ private fun WindowInsets.withStableBottom(): WindowInsets {
+ whenever(stableInsetBottom).thenReturn(STABLE_INSET_BOTTOM)
+ return this
+ }
+}
\ No newline at end of file
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewTest.kt
index aee9f12..ec7e07f90 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewTest.kt
@@ -35,6 +35,8 @@
private lateinit var panelView: ViewGroup
@Mock
private lateinit var scrimController: ScrimController
+ @Mock
+ private lateinit var statusBar: StatusBar
private lateinit var view: PhoneStatusBarView
@@ -48,6 +50,7 @@
view = PhoneStatusBarView(mContext, null)
view.setPanel(panelViewController)
view.setScrimController(scrimController)
+ view.setBar(statusBar)
}
@Test
@@ -72,7 +75,7 @@
@Test
fun panelExpansionChanged_fracZero_stateChangeListenerNotified() {
- val listener = TestStateChangedListener()
+ val listener = TestExpansionStateChangedListener()
view.setPanelExpansionStateChangedListener(listener)
view.panelExpansionChanged(0f, false)
@@ -82,7 +85,7 @@
@Test
fun panelExpansionChanged_fracOne_stateChangeListenerNotified() {
- val listener = TestStateChangedListener()
+ val listener = TestExpansionStateChangedListener()
view.setPanelExpansionStateChangedListener(listener)
view.panelExpansionChanged(1f, false)
@@ -92,7 +95,7 @@
@Test
fun panelExpansionChanged_fracHalf_stateChangeListenerNotNotified() {
- val listener = TestStateChangedListener()
+ val listener = TestExpansionStateChangedListener()
view.setPanelExpansionStateChangedListener(listener)
view.panelExpansionChanged(0.5f, false)
@@ -106,11 +109,59 @@
// No assert needed, just testing no crash
}
- private class TestStateChangedListener : PhoneStatusBarView.PanelExpansionStateChangedListener {
+ @Test
+ fun panelStateChanged_toStateOpening_listenerNotified() {
+ val listener = TestStateChangedListener()
+ view.setPanelStateChangeListener(listener)
+
+ view.panelExpansionChanged(0.5f, true)
+
+ assertThat(listener.state).isEqualTo(PanelBar.STATE_OPENING)
+ }
+
+ @Test
+ fun panelStateChanged_toStateOpen_listenerNotified() {
+ val listener = TestStateChangedListener()
+ view.setPanelStateChangeListener(listener)
+
+ view.panelExpansionChanged(1f, true)
+
+ assertThat(listener.state).isEqualTo(PanelBar.STATE_OPEN)
+ }
+
+ @Test
+ fun panelStateChanged_toStateClosed_listenerNotified() {
+ val listener = TestStateChangedListener()
+ view.setPanelStateChangeListener(listener)
+
+ // First, open the panel
+ view.panelExpansionChanged(1f, true)
+
+ // Then, close it again
+ view.panelExpansionChanged(0f, false)
+
+ assertThat(listener.state).isEqualTo(PanelBar.STATE_CLOSED)
+ }
+
+ @Test
+ fun panelStateChanged_noListener_noCrash() {
+ view.panelExpansionChanged(1f, true)
+ // No assert needed, just testing no crash
+ }
+
+ private class TestExpansionStateChangedListener
+ : PhoneStatusBarView.PanelExpansionStateChangedListener {
var stateChangeCalled: Boolean = false
override fun onPanelExpansionStateChanged() {
stateChangeCalled = true
}
}
+
+ private class TestStateChangedListener : PanelBar.PanelStateChangeListener {
+ var state: Int = 0
+ override fun onStateChanged(state: Int) {
+ this.state = state
+ }
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java
index 6e3d5ce..3fcd071 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java
@@ -16,15 +16,12 @@
package com.android.systemui.statusbar.phone;
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.anyFloat;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.clearInvocations;
-import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.reset;
@@ -119,7 +116,7 @@
any(ViewGroup.class),
any(KeyguardBouncer.BouncerExpansionCallback.class)))
.thenReturn(mBouncer);
-
+ when(mStatusBar.getBouncerContainer()).thenReturn(mContainer);
when(mContainer.findViewById(anyInt())).thenReturn(mKeyguardMessageArea);
mWakefulnessLifecycle = new WakefulnessLifecycle(
getContext(),
@@ -143,7 +140,7 @@
mUnlockedScreenOffAnimationController,
mKeyguardMessageAreaFactory,
mShadeController);
- mStatusBarKeyguardViewManager.registerStatusBar(mStatusBar, mContainer,
+ mStatusBarKeyguardViewManager.registerStatusBar(mStatusBar,
mNotificationPanelView, mBiometrucUnlockController,
mNotificationContainer, mBypassController);
mStatusBarKeyguardViewManager.show(null);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java
index fdda76d..741d95f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java
@@ -112,6 +112,7 @@
import com.android.systemui.statusbar.PulseExpansionHandler;
import com.android.systemui.statusbar.StatusBarState;
import com.android.systemui.statusbar.StatusBarStateControllerImpl;
+import com.android.systemui.statusbar.connectivity.NetworkController;
import com.android.systemui.statusbar.events.SystemStatusAnimationScheduler;
import com.android.systemui.statusbar.notification.DynamicPrivacyController;
import com.android.systemui.statusbar.notification.NotificationEntryManager;
@@ -137,7 +138,6 @@
import com.android.systemui.statusbar.policy.ExtensionController;
import com.android.systemui.statusbar.policy.HeadsUpManager;
import com.android.systemui.statusbar.policy.KeyguardStateController;
-import com.android.systemui.statusbar.policy.NetworkController;
import com.android.systemui.statusbar.policy.UserInfoControllerImpl;
import com.android.systemui.statusbar.policy.UserSwitcherController;
import com.android.systemui.tuner.TunerService;
@@ -459,7 +459,7 @@
mock(DumpManager.class),
mActivityLaunchAnimator,
mDialogLaunchAnimator);
- when(mKeyguardViewMediator.registerStatusBar(any(StatusBar.class), any(ViewGroup.class),
+ when(mKeyguardViewMediator.registerStatusBar(any(StatusBar.class),
any(NotificationPanelViewController.class), any(BiometricUnlockController.class),
any(ViewGroup.class), any(KeyguardBypassController.class)))
.thenReturn(mStatusBarKeyguardViewManager);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallControllerTest.kt
index efe2c17..e97aba2 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallControllerTest.kt
@@ -33,12 +33,14 @@
import com.android.internal.logging.testing.UiEventLoggerFake
import com.android.systemui.R
import com.android.systemui.SysuiTestCase
+import com.android.systemui.dump.DumpManager
import com.android.systemui.plugins.ActivityStarter
import com.android.systemui.flags.FeatureFlags
import com.android.systemui.statusbar.notification.collection.NotificationEntry
import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder
import com.android.systemui.statusbar.notification.collection.notifcollection.CommonNotifCollection
import com.android.systemui.statusbar.notification.collection.notifcollection.NotifCollectionListener
+import com.android.systemui.statusbar.phone.StatusBarWindowController
import com.android.systemui.util.concurrency.FakeExecutor
import com.android.systemui.util.mockito.any
import com.android.systemui.util.time.FakeSystemClock
@@ -59,6 +61,7 @@
import org.mockito.Mockito.times
import org.mockito.Mockito.verify
import org.mockito.MockitoAnnotations
+import java.util.Optional
private const val CALL_UID = 900
@@ -83,6 +86,7 @@
@Mock private lateinit var mockOngoingCallListener: OngoingCallListener
@Mock private lateinit var mockActivityStarter: ActivityStarter
@Mock private lateinit var mockIActivityManager: IActivityManager
+ @Mock private lateinit var mockStatusBarWindowController: StatusBarWindowController
private lateinit var chipView: View
@@ -105,7 +109,10 @@
mockActivityStarter,
mainExecutor,
mockIActivityManager,
- OngoingCallLogger(uiEventLoggerFake))
+ OngoingCallLogger(uiEventLoggerFake),
+ DumpManager(),
+ Optional.of(mockStatusBarWindowController),
+ )
controller.init()
controller.addCallback(mockOngoingCallListener)
controller.setChipView(chipView)
@@ -131,6 +138,13 @@
}
@Test
+ fun onEntryUpdated_isOngoingCallNotif_windowControllerUpdated() {
+ notifCollectionListener.onEntryUpdated(createOngoingCallNotifEntry())
+
+ verify(mockStatusBarWindowController).setIsCallOngoing(true)
+ }
+
+ @Test
fun onEntryUpdated_notOngoingCallNotif_listenerNotNotified() {
notifCollectionListener.onEntryUpdated(createNotCallNotifEntry())
@@ -221,16 +235,14 @@
verify(mockOngoingCallListener).onOngoingCallStateChanged(anyBoolean())
}
- /** Regression test for b/201097913. */
@Test
- fun onEntryCleanUp_callNotifAddedThenRemoved_listenerNotified() {
+ fun onEntryUpdated_callNotifAddedThenRemoved_windowControllerUpdated() {
val ongoingCallNotifEntry = createOngoingCallNotifEntry()
notifCollectionListener.onEntryAdded(ongoingCallNotifEntry)
- reset(mockOngoingCallListener)
- notifCollectionListener.onEntryCleanUp(ongoingCallNotifEntry)
+ notifCollectionListener.onEntryRemoved(ongoingCallNotifEntry, REASON_USER_STOPPED)
- verify(mockOngoingCallListener).onOngoingCallStateChanged(anyBoolean())
+ verify(mockStatusBarWindowController).setIsCallOngoing(false)
}
/** Regression test for b/188491504. */
diff --git a/packages/SystemUI/tests/src/com/android/systemui/util/WallpaperControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/util/WallpaperControllerTest.kt
index de86821..b54aadb 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/util/WallpaperControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/util/WallpaperControllerTest.kt
@@ -138,7 +138,7 @@
private fun createWallpaperInfo(useDefaultTransition: Boolean = true): WallpaperInfo {
val info = mock(WallpaperInfo::class.java)
- whenever(info.shouldUseDefaultDeviceStateChangeTransition())
+ whenever(info.shouldUseDefaultDisplayStateChangeTransition())
.thenReturn(useDefaultTransition)
return info
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/util/sensors/FakeProximitySensor.java b/packages/SystemUI/tests/src/com/android/systemui/util/sensors/FakeProximitySensor.java
index 50947ab..22cf744 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/util/sensors/FakeProximitySensor.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/util/sensors/FakeProximitySensor.java
@@ -19,14 +19,21 @@
import com.android.systemui.util.concurrency.DelayableExecutor;
import com.android.systemui.util.concurrency.FakeExecution;
-public class FakeProximitySensor extends ProximitySensor {
+public class FakeProximitySensor extends ProximitySensorImpl {
private boolean mAvailable;
private boolean mRegistered;
- public FakeProximitySensor(ThresholdSensor primary, ThresholdSensor secondary,
- DelayableExecutor delayableExecutor) {
- super(primary, secondary == null ? new FakeThresholdSensor() : secondary,
- delayableExecutor, new FakeExecution());
+ public FakeProximitySensor(
+ ThresholdSensor primary,
+ ThresholdSensor secondary,
+ DelayableExecutor delayableExecutor
+ ) {
+ super(
+ primary,
+ secondary == null ? new FakeThresholdSensor() : secondary,
+ delayableExecutor,
+ new FakeExecution()
+ );
mAvailable = true;
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/util/sensors/FakeThresholdSensor.java b/packages/SystemUI/tests/src/com/android/systemui/util/sensors/FakeThresholdSensor.java
index d9f9789..0d4a6c7 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/util/sensors/FakeThresholdSensor.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/util/sensors/FakeThresholdSensor.java
@@ -59,6 +59,16 @@
mListeners.remove(listener);
}
+ @Override
+ public String getName() {
+ return "FakeThresholdSensorName";
+ }
+
+ @Override
+ public String getType() {
+ return "FakeThresholdSensorType";
+ }
+
public void setLoaded(boolean loaded) {
mIsLoaded = loaded;
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/util/sensors/PostureDependentProximitySensorTest.java b/packages/SystemUI/tests/src/com/android/systemui/util/sensors/PostureDependentProximitySensorTest.java
new file mode 100644
index 0000000..075f393
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/util/sensors/PostureDependentProximitySensorTest.java
@@ -0,0 +1,114 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.util.sensors;
+
+import static org.junit.Assert.assertEquals;
+import static org.mockito.Mockito.verify;
+
+import android.content.res.Resources;
+import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper;
+
+import androidx.test.filters.SmallTest;
+
+import com.android.systemui.SysuiTestCase;
+import com.android.systemui.statusbar.policy.DevicePostureController;
+import com.android.systemui.util.concurrency.FakeExecution;
+import com.android.systemui.util.concurrency.FakeExecutor;
+import com.android.systemui.util.time.FakeSystemClock;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Captor;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+@SmallTest
+@RunWith(AndroidTestingRunner.class)
+@TestableLooper.RunWithLooper
+public class PostureDependentProximitySensorTest extends SysuiTestCase {
+ @Mock private Resources mResources;
+ @Mock private DevicePostureController mDevicePostureController;
+ @Mock private AsyncSensorManager mSensorManager;
+
+ @Captor private ArgumentCaptor<DevicePostureController.Callback> mPostureListenerCaptor =
+ ArgumentCaptor.forClass(DevicePostureController.Callback.class);
+ private DevicePostureController.Callback mPostureListener;
+
+ private PostureDependentProximitySensor mProximitySensor;
+ private FakeExecutor mFakeExecutor = new FakeExecutor(new FakeSystemClock());
+
+ @Before
+ public void setUp() throws Exception {
+ MockitoAnnotations.initMocks(this);
+ allowTestableLooperAsMainThread();
+
+ mProximitySensor = new PostureDependentProximitySensor(
+ new ThresholdSensor[DevicePostureController.SUPPORTED_POSTURES_SIZE],
+ new ThresholdSensor[DevicePostureController.SUPPORTED_POSTURES_SIZE],
+ mFakeExecutor,
+ new FakeExecution(),
+ mDevicePostureController
+ );
+ }
+
+ @Test
+ public void testPostureChangeListenerAdded() {
+ capturePostureListener();
+ }
+
+ @Test
+ public void testPostureChangeListenerUpdatesPosture() {
+ // GIVEN posture listener is registered
+ capturePostureListener();
+
+ // WHEN the posture changes to DEVICE_POSTURE_OPENED
+ mPostureListener.onPostureChanged(DevicePostureController.DEVICE_POSTURE_OPENED);
+
+ // THEN device posture is updated to DEVICE_POSTURE_OPENED
+ assertEquals(DevicePostureController.DEVICE_POSTURE_OPENED,
+ mProximitySensor.mDevicePosture);
+
+ // WHEN the posture changes to DEVICE_POSTURE_CLOSED
+ mPostureListener.onPostureChanged(DevicePostureController.DEVICE_POSTURE_CLOSED);
+
+ // THEN device posture is updated to DEVICE_POSTURE_CLOSED
+ assertEquals(DevicePostureController.DEVICE_POSTURE_CLOSED,
+ mProximitySensor.mDevicePosture);
+
+ // WHEN the posture changes to DEVICE_POSTURE_FLIPPED
+ mPostureListener.onPostureChanged(DevicePostureController.DEVICE_POSTURE_FLIPPED);
+
+ // THEN device posture is updated to DEVICE_POSTURE_FLIPPED
+ assertEquals(DevicePostureController.DEVICE_POSTURE_FLIPPED,
+ mProximitySensor.mDevicePosture);
+
+ // WHEN the posture changes to DEVICE_POSTURE_HALF_OPENED
+ mPostureListener.onPostureChanged(DevicePostureController.DEVICE_POSTURE_HALF_OPENED);
+
+ // THEN device posture is updated to DEVICE_POSTURE_HALF_OPENED
+ assertEquals(DevicePostureController.DEVICE_POSTURE_HALF_OPENED,
+ mProximitySensor.mDevicePosture);
+ }
+
+ private void capturePostureListener() {
+ verify(mDevicePostureController).addCallback(mPostureListenerCaptor.capture());
+ mPostureListener = mPostureListenerCaptor.getValue();
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/util/sensors/ProximityCheckTest.java b/packages/SystemUI/tests/src/com/android/systemui/util/sensors/ProximityCheckTest.java
index 242fe9f..19dbf9a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/util/sensors/ProximityCheckTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/util/sensors/ProximityCheckTest.java
@@ -49,7 +49,7 @@
private TestableCallback mTestableCallback = new TestableCallback();
- private ProximitySensor.ProximityCheck mProximityCheck;
+ private ProximityCheck mProximityCheck;
@Before
public void setUp() throws Exception {
@@ -58,7 +58,7 @@
thresholdSensor.setLoaded(true);
mFakeProximitySensor = new FakeProximitySensor(thresholdSensor, null, mFakeExecutor);
- mProximityCheck = new ProximitySensor.ProximityCheck(mFakeProximitySensor, mFakeExecutor);
+ mProximityCheck = new ProximityCheck(mFakeProximitySensor, mFakeExecutor);
}
@Test
@@ -67,7 +67,7 @@
assertNull(mTestableCallback.mLastResult);
- mFakeProximitySensor.setLastEvent(new ProximitySensor.ThresholdSensorEvent(true, 0));
+ mFakeProximitySensor.setLastEvent(new ThresholdSensorEvent(true, 0));
mFakeProximitySensor.alertListeners();
assertTrue(mTestableCallback.mLastResult);
@@ -103,7 +103,7 @@
mProximityCheck.check(100, mTestableCallback);
- mFakeProximitySensor.setLastEvent(new ProximitySensor.ThresholdSensorEvent(true, 0));
+ mFakeProximitySensor.setLastEvent(new ThresholdSensorEvent(true, 0));
mFakeProximitySensor.alertListeners();
assertThat(mTestableCallback.mLastResult).isNotNull();
@@ -123,7 +123,7 @@
assertNull(mTestableCallback.mLastResult);
- mFakeProximitySensor.setLastEvent(new ProximitySensor.ThresholdSensorEvent(true, 0));
+ mFakeProximitySensor.setLastEvent(new ThresholdSensorEvent(true, 0));
mFakeProximitySensor.alertListeners();
assertTrue(mTestableCallback.mLastResult);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/util/sensors/ProximitySensorDualTest.java b/packages/SystemUI/tests/src/com/android/systemui/util/sensors/ProximitySensorImplDualTest.java
similarity index 98%
rename from packages/SystemUI/tests/src/com/android/systemui/util/sensors/ProximitySensorDualTest.java
rename to packages/SystemUI/tests/src/com/android/systemui/util/sensors/ProximitySensorImplDualTest.java
index 0e9d96c..5e75578 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/util/sensors/ProximitySensorDualTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/util/sensors/ProximitySensorImplDualTest.java
@@ -42,7 +42,7 @@
@SmallTest
@RunWith(AndroidTestingRunner.class)
@TestableLooper.RunWithLooper
-public class ProximitySensorDualTest extends SysuiTestCase {
+public class ProximitySensorImplDualTest extends SysuiTestCase {
private ProximitySensor mProximitySensor;
private FakeExecutor mFakeExecutor = new FakeExecutor(new FakeSystemClock());
private FakeThresholdSensor mThresholdSensorPrimary;
@@ -57,7 +57,7 @@
mThresholdSensorSecondary = new FakeThresholdSensor();
mThresholdSensorSecondary.setLoaded(true);
- mProximitySensor = new ProximitySensor(
+ mProximitySensor = new ProximitySensorImpl(
mThresholdSensorPrimary, mThresholdSensorSecondary, mFakeExecutor,
new FakeExecution());
}
@@ -430,11 +430,11 @@
}
private static class TestableListener implements ThresholdSensor.Listener {
- ThresholdSensor.ThresholdSensorEvent mLastEvent;
+ ThresholdSensorEvent mLastEvent;
int mCallCount = 0;
@Override
- public void onThresholdCrossed(ThresholdSensor.ThresholdSensorEvent proximityEvent) {
+ public void onThresholdCrossed(ThresholdSensorEvent proximityEvent) {
mLastEvent = proximityEvent;
mCallCount++;
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/util/sensors/ProximitySensorSingleTest.java b/packages/SystemUI/tests/src/com/android/systemui/util/sensors/ProximitySensorImplSingleTest.java
similarity index 95%
rename from packages/SystemUI/tests/src/com/android/systemui/util/sensors/ProximitySensorSingleTest.java
rename to packages/SystemUI/tests/src/com/android/systemui/util/sensors/ProximitySensorImplSingleTest.java
index 6c6d355..752cd32 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/util/sensors/ProximitySensorSingleTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/util/sensors/ProximitySensorImplSingleTest.java
@@ -42,7 +42,7 @@
@SmallTest
@RunWith(AndroidTestingRunner.class)
@TestableLooper.RunWithLooper
-public class ProximitySensorSingleTest extends SysuiTestCase {
+public class ProximitySensorImplSingleTest extends SysuiTestCase {
private ProximitySensor mProximitySensor;
private FakeExecutor mFakeExecutor = new FakeExecutor(new FakeSystemClock());
private FakeThresholdSensor mThresholdSensor;
@@ -54,7 +54,7 @@
mThresholdSensor = new FakeThresholdSensor();
mThresholdSensor.setLoaded(true);
- mProximitySensor = new ProximitySensor(
+ mProximitySensor = new ProximitySensorImpl(
mThresholdSensor, new FakeThresholdSensor(), mFakeExecutor, new FakeExecution());
}
@@ -215,7 +215,7 @@
public void testPreventRecursiveAlert() {
TestableListener listenerA = new TestableListener() {
@Override
- public void onThresholdCrossed(ProximitySensor.ThresholdSensorEvent proximityEvent) {
+ public void onThresholdCrossed(ThresholdSensorEvent proximityEvent) {
super.onThresholdCrossed(proximityEvent);
if (mCallCount < 2) {
mProximitySensor.alertListeners();
@@ -231,11 +231,11 @@
}
private static class TestableListener implements ThresholdSensor.Listener {
- ThresholdSensor.ThresholdSensorEvent mLastEvent;
+ ThresholdSensorEvent mLastEvent;
int mCallCount = 0;
@Override
- public void onThresholdCrossed(ThresholdSensor.ThresholdSensorEvent proximityEvent) {
+ public void onThresholdCrossed(ThresholdSensorEvent proximityEvent) {
mLastEvent = proximityEvent;
mCallCount++;
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/util/sensors/ThresholdSensorImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/util/sensors/ThresholdSensorImplTest.java
index 125063a..b10f16c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/util/sensors/ThresholdSensorImplTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/util/sensors/ThresholdSensorImplTest.java
@@ -380,7 +380,7 @@
int mCallCount;
@Override
- public void onThresholdCrossed(ThresholdSensor.ThresholdSensorEvent event) {
+ public void onThresholdCrossed(ThresholdSensorEvent event) {
mBelow = event.getBelow();
mTimestampNs = event.getTimestampNs();
mCallCount++;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeNetworkController.java b/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeNetworkController.java
index e7acfae..8ea9da6 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeNetworkController.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeNetworkController.java
@@ -18,9 +18,9 @@
import android.testing.LeakCheck;
import com.android.settingslib.net.DataUsageController;
+import com.android.systemui.statusbar.connectivity.NetworkController;
+import com.android.systemui.statusbar.connectivity.NetworkController.SignalCallback;
import com.android.systemui.statusbar.policy.DataSaverController;
-import com.android.systemui.statusbar.policy.NetworkController;
-import com.android.systemui.statusbar.policy.NetworkController.SignalCallback;
public class FakeNetworkController extends BaseLeakChecker<SignalCallback>
implements NetworkController {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/LeakCheckedTest.java b/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/LeakCheckedTest.java
index fedc08d..dc6a8fb 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/LeakCheckedTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/LeakCheckedTest.java
@@ -19,6 +19,7 @@
import com.android.systemui.SysuiTestCase;
import com.android.systemui.shared.plugins.PluginManager;
+import com.android.systemui.statusbar.connectivity.NetworkController;
import com.android.systemui.statusbar.phone.ManagedProfileController;
import com.android.systemui.statusbar.phone.StatusBarIconController;
import com.android.systemui.statusbar.policy.BatteryController;
@@ -29,7 +30,6 @@
import com.android.systemui.statusbar.policy.HotspotController;
import com.android.systemui.statusbar.policy.KeyguardStateController;
import com.android.systemui.statusbar.policy.LocationController;
-import com.android.systemui.statusbar.policy.NetworkController;
import com.android.systemui.statusbar.policy.NextAlarmController;
import com.android.systemui.statusbar.policy.RotationLockController;
import com.android.systemui.statusbar.policy.SecurityController;
diff --git a/packages/services/CameraExtensionsProxy/src/com/android/cameraextensions/CameraExtensionsProxyService.java b/packages/services/CameraExtensionsProxy/src/com/android/cameraextensions/CameraExtensionsProxyService.java
index b0893cc..ed37d7e 100644
--- a/packages/services/CameraExtensionsProxy/src/com/android/cameraextensions/CameraExtensionsProxyService.java
+++ b/packages/services/CameraExtensionsProxy/src/com/android/cameraextensions/CameraExtensionsProxyService.java
@@ -19,6 +19,7 @@
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
+import android.graphics.Camera;
import android.graphics.GraphicBuffer;
import android.graphics.Rect;
import android.hardware.camera2.CameraAccessException;
@@ -135,6 +136,7 @@
private HashMap<String, CameraCharacteristics> mCharacteristicsHashMap = new HashMap<>();
private HashMap<String, Long> mMetadataVendorIdMap = new HashMap<>();
+ private CameraManager mCameraManager;
private static boolean checkForAdvancedAPI() {
if (EXTENSIONS_PRESENT && EXTENSIONS_VERSION.startsWith(ADVANCED_VERSION_PREFIX)) {
@@ -460,12 +462,12 @@
// This will setup the camera vendor tag descriptor in the service process
// along with all camera characteristics.
try {
- CameraManager manager = getSystemService(CameraManager.class);
+ mCameraManager = getSystemService(CameraManager.class);
- String [] cameraIds = manager.getCameraIdListNoLazy();
+ String [] cameraIds = mCameraManager.getCameraIdListNoLazy();
if (cameraIds != null) {
for (String cameraId : cameraIds) {
- CameraCharacteristics chars = manager.getCameraCharacteristics(cameraId);
+ CameraCharacteristics chars = mCameraManager.getCameraCharacteristics(cameraId);
mCharacteristicsHashMap.put(cameraId, chars);
Object thisClass = CameraCharacteristics.Key.class;
Class<CameraCharacteristics.Key<?>> keyClass =
@@ -1174,8 +1176,9 @@
@Override
public void onInit(String cameraId, CameraMetadataNative cameraCharacteristics) {
mCameraId = cameraId;
- mPreviewExtender.onInit(cameraId, new CameraCharacteristics(cameraCharacteristics),
- CameraExtensionsProxyService.this);
+ CameraCharacteristics chars = new CameraCharacteristics(cameraCharacteristics);
+ mCameraManager.registerDeviceStateListener(chars);
+ mPreviewExtender.onInit(cameraId, chars, CameraExtensionsProxyService.this);
}
@Override
@@ -1200,13 +1203,16 @@
@Override
public void init(String cameraId, CameraMetadataNative chars) {
- mPreviewExtender.init(cameraId, new CameraCharacteristics(chars));
+ CameraCharacteristics c = new CameraCharacteristics(chars);
+ mCameraManager.registerDeviceStateListener(c);
+ mPreviewExtender.init(cameraId, c);
}
@Override
public boolean isExtensionAvailable(String cameraId, CameraMetadataNative chars) {
- return mPreviewExtender.isExtensionAvailable(cameraId,
- new CameraCharacteristics(chars));
+ CameraCharacteristics c = new CameraCharacteristics(chars);
+ mCameraManager.registerDeviceStateListener(c);
+ return mPreviewExtender.isExtensionAvailable(cameraId, c);
}
@Override
@@ -1283,8 +1289,9 @@
@Override
public void onInit(String cameraId, CameraMetadataNative cameraCharacteristics) {
- mImageExtender.onInit(cameraId, new CameraCharacteristics(cameraCharacteristics),
- CameraExtensionsProxyService.this);
+ CameraCharacteristics chars = new CameraCharacteristics(cameraCharacteristics);
+ mCameraManager.registerDeviceStateListener(chars);
+ mImageExtender.onInit(cameraId, chars, CameraExtensionsProxyService.this);
mCameraId = cameraId;
}
@@ -1310,13 +1317,16 @@
@Override
public void init(String cameraId, CameraMetadataNative chars) {
- mImageExtender.init(cameraId, new CameraCharacteristics(chars));
+ CameraCharacteristics c = new CameraCharacteristics(chars);
+ mCameraManager.registerDeviceStateListener(c);
+ mImageExtender.init(cameraId, c);
}
@Override
public boolean isExtensionAvailable(String cameraId, CameraMetadataNative chars) {
- return mImageExtender.isExtensionAvailable(cameraId,
- new CameraCharacteristics(chars));
+ CameraCharacteristics c = new CameraCharacteristics(chars);
+ mCameraManager.registerDeviceStateListener(c);
+ return mImageExtender.isExtensionAvailable(cameraId, c);
}
@Override
diff --git a/services/contentcapture/java/com/android/server/contentcapture/ContentCapturePerUserService.java b/services/contentcapture/java/com/android/server/contentcapture/ContentCapturePerUserService.java
index 904def0..6bd1fa6 100644
--- a/services/contentcapture/java/com/android/server/contentcapture/ContentCapturePerUserService.java
+++ b/services/contentcapture/java/com/android/server/contentcapture/ContentCapturePerUserService.java
@@ -190,6 +190,9 @@
Slog.w(TAG, "remote service died: " + service);
synchronized (mLock) {
mZombie = true;
+ writeServiceEvent(
+ FrameworkStatsLog.CONTENT_CAPTURE_SERVICE_EVENTS__EVENT__ON_REMOTE_SERVICE_DIED,
+ getServiceComponentName());
}
}
diff --git a/services/core/java/com/android/server/SensorPrivacyService.java b/services/core/java/com/android/server/SensorPrivacyService.java
index 1176c50..06a78c8 100644
--- a/services/core/java/com/android/server/SensorPrivacyService.java
+++ b/services/core/java/com/android/server/SensorPrivacyService.java
@@ -39,6 +39,7 @@
import static android.hardware.SensorPrivacyManager.Sources.QS_TILE;
import static android.hardware.SensorPrivacyManager.Sources.SETTINGS;
import static android.hardware.SensorPrivacyManager.Sources.SHELL;
+import static android.os.UserHandle.USER_NULL;
import static android.os.UserHandle.USER_SYSTEM;
import static android.service.SensorPrivacyIndividualEnabledSensorProto.UNKNOWN;
@@ -195,7 +196,7 @@
private EmergencyCallHelper mEmergencyCallHelper;
private KeyguardManager mKeyguardManager;
- private int mCurrentUser = -1;
+ private int mCurrentUser = USER_NULL;
public SensorPrivacyService(Context context) {
super(context);
@@ -228,9 +229,9 @@
@Override
public void onUserStarting(TargetUser user) {
- if (mCurrentUser == -1) {
+ if (mCurrentUser == USER_NULL) {
mCurrentUser = user.getUserIdentifier();
- mSensorPrivacyServiceImpl.userSwitching(-1, user.getUserIdentifier());
+ mSensorPrivacyServiceImpl.userSwitching(USER_NULL, user.getUserIdentifier());
}
}
@@ -711,6 +712,16 @@
@Override
public void setIndividualSensorPrivacy(@UserIdInt int userId,
@SensorPrivacyManager.Sources.Source int source, int sensor, boolean enable) {
+ if (DEBUG) {
+ Log.d(TAG, "callingUid=" + Binder.getCallingUid()
+ + " callingPid=" + Binder.getCallingPid()
+ + " setIndividualSensorPrivacy("
+ + "userId=" + userId
+ + " source=" + source
+ + " sensor=" + sensor
+ + " enable=" + enable
+ + ")");
+ }
enforceManageSensorPrivacyPermission();
if (userId == UserHandle.USER_CURRENT) {
userId = mCurrentUser;
@@ -895,6 +906,14 @@
@Override
public boolean isIndividualSensorPrivacyEnabled(@UserIdInt int userId, int sensor) {
+ if (DEBUG) {
+ Log.d(TAG, "callingUid=" + Binder.getCallingUid()
+ + " callingPid=" + Binder.getCallingPid()
+ + " isIndividualSensorPrivacyEnabled("
+ + "userId=" + userId
+ + " sensor=" + sensor
+ + ")");
+ }
enforceObserveSensorPrivacyPermission();
if (userId == UserHandle.USER_CURRENT) {
userId = mCurrentUser;
@@ -1276,13 +1295,13 @@
micState = isIndividualSensorPrivacyEnabledLocked(to, MICROPHONE);
camState = isIndividualSensorPrivacyEnabledLocked(to, CAMERA);
}
- if (prevMicState != micState) {
+ if (from == USER_NULL || prevMicState != micState) {
mHandler.onUserGlobalSensorPrivacyChanged(MICROPHONE, micState);
setGlobalRestriction(MICROPHONE, micState);
}
- if (prevCamState != camState) {
+ if (from == USER_NULL || prevCamState != camState) {
mHandler.onUserGlobalSensorPrivacyChanged(CAMERA, camState);
- setGlobalRestriction(CAMERA, micState);
+ setGlobalRestriction(CAMERA, camState);
}
}
diff --git a/services/core/java/com/android/server/Watchdog.java b/services/core/java/com/android/server/Watchdog.java
index fcd049f..d10ab8e 100644
--- a/services/core/java/com/android/server/Watchdog.java
+++ b/services/core/java/com/android/server/Watchdog.java
@@ -142,6 +142,8 @@
);
public static final String[] AIDL_INTERFACE_PREFIXES_OF_INTEREST = new String[] {
+ "android.hardware.biometrics.face.IFace/",
+ "android.hardware.biometrics.fingerprint.IFingerprint/",
"android.hardware.light.ILights/",
"android.hardware.power.stats.IPowerStats/",
};
diff --git a/services/core/java/com/android/server/am/BatteryExternalStatsWorker.java b/services/core/java/com/android/server/am/BatteryExternalStatsWorker.java
index 7ba032f..e9aa3be 100644
--- a/services/core/java/com/android/server/am/BatteryExternalStatsWorker.java
+++ b/services/core/java/com/android/server/am/BatteryExternalStatsWorker.java
@@ -659,11 +659,13 @@
// Inform mStats about each applicable measured energy (unless addressed elsewhere).
if (measuredEnergyDeltas != null) {
- final long displayChargeUC = measuredEnergyDeltas.displayChargeUC;
- if (displayChargeUC != MeasuredEnergySnapshot.UNAVAILABLE) {
+ final long[] displayChargeUC = measuredEnergyDeltas.displayChargeUC;
+ if (displayChargeUC != null && displayChargeUC.length > 0) {
+ // TODO (b/194107383): pass all display ordinals to mStats.
+ final long primaryDisplayChargeUC = displayChargeUC[0];
// If updating, pass in what BatteryExternalStatsWorker thinks screenState is.
- mStats.updateDisplayMeasuredEnergyStatsLocked(displayChargeUC, screenState,
- elapsedRealtime);
+ mStats.updateDisplayMeasuredEnergyStatsLocked(primaryDisplayChargeUC,
+ screenState, elapsedRealtime);
}
final long gnssChargeUC = measuredEnergyDeltas.gnssChargeUC;
@@ -948,6 +950,7 @@
switch (consumer.type) {
case EnergyConsumerType.OTHER:
case EnergyConsumerType.CPU_CLUSTER:
+ case EnergyConsumerType.DISPLAY:
break;
default:
Slog.w(TAG, "EnergyConsumer '" + consumer.name + "' has unexpected ordinal "
diff --git a/services/core/java/com/android/server/am/MeasuredEnergySnapshot.java b/services/core/java/com/android/server/am/MeasuredEnergySnapshot.java
index a9fca4f..0359aa5 100644
--- a/services/core/java/com/android/server/am/MeasuredEnergySnapshot.java
+++ b/services/core/java/com/android/server/am/MeasuredEnergySnapshot.java
@@ -49,6 +49,9 @@
/** Number of ordinals for {@link EnergyConsumerType#CPU_CLUSTER}. */
private final int mNumCpuClusterOrdinals;
+ /** Number of ordinals for {@link EnergyConsumerType#DISPLAY}. */
+ private final int mNumDisplayOrdinals;
+
/** Number of ordinals for {@link EnergyConsumerType#OTHER}. */
private final int mNumOtherOrdinals;
@@ -95,6 +98,7 @@
mNumCpuClusterOrdinals = calculateNumOrdinals(EnergyConsumerType.CPU_CLUSTER,
idToConsumerMap);
+ mNumDisplayOrdinals = calculateNumOrdinals(EnergyConsumerType.DISPLAY, idToConsumerMap);
mNumOtherOrdinals = calculateNumOrdinals(EnergyConsumerType.OTHER, idToConsumerMap);
mAttributionSnapshots = new SparseArray<>(mNumOtherOrdinals);
}
@@ -108,7 +112,7 @@
public long[] cpuClusterChargeUC = null;
/** The chargeUC for {@link EnergyConsumerType#DISPLAY}. */
- public long displayChargeUC = UNAVAILABLE;
+ public long[] displayChargeUC = null;
/** The chargeUC for {@link EnergyConsumerType#GNSS}. */
public long gnssChargeUC = UNAVAILABLE;
@@ -212,7 +216,10 @@
break;
case EnergyConsumerType.DISPLAY:
- output.displayChargeUC = deltaChargeUC;
+ if (output.displayChargeUC == null) {
+ output.displayChargeUC = new long[mNumDisplayOrdinals];
+ }
+ output.displayChargeUC[ordinal] = deltaChargeUC;
break;
case EnergyConsumerType.GNSS:
diff --git a/services/core/java/com/android/server/display/DisplayModeDirector.java b/services/core/java/com/android/server/display/DisplayModeDirector.java
index 5fc301e..0a22f2f 100644
--- a/services/core/java/com/android/server/display/DisplayModeDirector.java
+++ b/services/core/java/com/android/server/display/DisplayModeDirector.java
@@ -17,6 +17,7 @@
package com.android.server.display;
import static android.hardware.display.DisplayManagerInternal.REFRESH_RATE_LIMIT_HIGH_BRIGHTNESS_MODE;
+import static android.os.PowerManager.BRIGHTNESS_INVALID;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -40,6 +41,7 @@
import android.os.IThermalService;
import android.os.Looper;
import android.os.Message;
+import android.os.PowerManager;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.SystemClock;
@@ -60,6 +62,7 @@
import com.android.internal.R;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.display.BrightnessSynchronizer;
import com.android.internal.os.BackgroundThread;
import com.android.server.LocalServices;
import com.android.server.display.utils.AmbientFilter;
@@ -155,7 +158,7 @@
mAppRequestObserver = new AppRequestObserver();
mSettingsObserver = new SettingsObserver(context, handler);
mDisplayObserver = new DisplayObserver(context, handler);
- mBrightnessObserver = new BrightnessObserver(context, handler);
+ mBrightnessObserver = new BrightnessObserver(context, handler, injector);
mUdfpsObserver = new UdfpsObserver();
final BallotBox ballotBox = (displayId, priority, vote) -> {
synchronized (mLock) {
@@ -1427,8 +1430,6 @@
@Override
public void onDisplayChanged(int displayId) {
updateDisplayModes(displayId);
- // TODO: Break the coupling between DisplayObserver and BrightnessObserver.
- mBrightnessObserver.onDisplayChanged(displayId);
}
private void updateDisplayModes(int displayId) {
@@ -1465,7 +1466,7 @@
* {@link R.array#config_ambientThresholdsOfPeakRefreshRate}.
*/
@VisibleForTesting
- public class BrightnessObserver extends ContentObserver {
+ public class BrightnessObserver implements DisplayManager.DisplayListener {
private final static int LIGHT_SENSOR_RATE_MS = 250;
private int[] mLowDisplayBrightnessThresholds;
private int[] mLowAmbientBrightnessThresholds;
@@ -1488,6 +1489,8 @@
private int mBrightness = -1;
private final Context mContext;
+ private final Injector mInjector;
+ private final Handler mHandler;
// Enable light sensor only when mShouldObserveAmbientLowChange is true or
// mShouldObserveAmbientHighChange is true, screen is on, peak refresh rate
@@ -1500,9 +1503,11 @@
private int mRefreshRateInLowZone;
private int mRefreshRateInHighZone;
- BrightnessObserver(Context context, Handler handler) {
- super(handler);
+ BrightnessObserver(Context context, Handler handler, Injector injector) {
mContext = context;
+ mHandler = handler;
+ mInjector = injector;
+
mLowDisplayBrightnessThresholds = context.getResources().getIntArray(
R.array.config_brightnessThresholdsOfPeakRefreshRate);
mLowAmbientBrightnessThresholds = context.getResources().getIntArray(
@@ -1569,8 +1574,7 @@
public void observe(SensorManager sensorManager) {
mSensorManager = sensorManager;
final ContentResolver cr = mContext.getContentResolver();
- mBrightness = Settings.System.getIntForUser(cr,
- Settings.System.SCREEN_BRIGHTNESS, -1 /*default*/, cr.getUserId());
+ mBrightness = getBrightness(Display.DEFAULT_DISPLAY);
// DeviceConfig is accessible after system ready.
int[] lowDisplayBrightnessThresholds =
@@ -1603,6 +1607,10 @@
restartObserver();
mDeviceConfigDisplaySettings.startListening();
+
+ mInjector.registerDisplayListener(this, mHandler,
+ DisplayManager.EVENT_FLAG_DISPLAY_CHANGED |
+ DisplayManager.EVENT_FLAG_DISPLAY_BRIGHTNESS);
}
public void setLoggingEnabled(boolean loggingEnabled) {
@@ -1718,28 +1726,30 @@
}
}
+ @Override
+ public void onDisplayAdded(int displayId) {}
+
+ @Override
+ public void onDisplayRemoved(int displayId) {}
+
+ @Override
public void onDisplayChanged(int displayId) {
if (displayId == Display.DEFAULT_DISPLAY) {
updateDefaultDisplayState();
- }
- }
- @Override
- public void onChange(boolean selfChange, Uri uri, int userId) {
- synchronized (mLock) {
- final ContentResolver cr = mContext.getContentResolver();
- int brightness = Settings.System.getIntForUser(cr,
- Settings.System.SCREEN_BRIGHTNESS, -1 /*default*/, cr.getUserId());
- if (brightness != mBrightness) {
- mBrightness = brightness;
- onBrightnessChangedLocked();
+ // We don't support multiple display blocking zones yet, so only handle
+ // brightness changes for the default display for now.
+ int brightness = getBrightness(displayId);
+ synchronized (mLock) {
+ if (brightness != mBrightness) {
+ mBrightness = brightness;
+ onBrightnessChangedLocked();
+ }
}
}
}
private void restartObserver() {
- final ContentResolver cr = mContext.getContentResolver();
-
if (mRefreshRateInLowZone > 0) {
mShouldObserveDisplayLowChange = hasValidThreshold(
mLowDisplayBrightnessThresholds);
@@ -1760,15 +1770,6 @@
mShouldObserveAmbientHighChange = false;
}
- if (mShouldObserveDisplayLowChange || mShouldObserveDisplayHighChange) {
- // Content Service does not check if an listener has already been registered.
- // To ensure only one listener is registered, force an unregistration first.
- mInjector.unregisterBrightnessObserver(cr, this);
- mInjector.registerBrightnessObserver(cr, this);
- } else {
- mInjector.unregisterBrightnessObserver(cr, this);
- }
-
if (mShouldObserveAmbientLowChange || mShouldObserveAmbientHighChange) {
Resources resources = mContext.getResources();
String lightSensorType = resources.getString(
@@ -1968,6 +1969,15 @@
return mDefaultDisplayState == Display.STATE_ON;
}
+ private int getBrightness(int displayId) {
+ final BrightnessInfo info = mInjector.getBrightnessInfo(displayId);
+ if (info != null) {
+ return BrightnessSynchronizer.brightnessFloatToInt(info.adjustedBrightness);
+ }
+
+ return BRIGHTNESS_INVALID;
+ }
+
private final class LightSensorEventListener implements SensorEventListener {
final private static int INJECT_EVENTS_INTERVAL_MS = LIGHT_SENSOR_RATE_MS;
private float mLastSensorData;
@@ -2283,6 +2293,7 @@
private final BallotBox mBallotBox;
private final Handler mHandler;
private final SparseIntArray mHbmMode = new SparseIntArray();
+ private final SparseBooleanArray mHbmActive = new SparseBooleanArray();
private final Injector mInjector;
private final DeviceConfigDisplaySettings mDeviceConfigDisplaySettings;
private int mRefreshRateInHbmSunlight;
@@ -2351,6 +2362,7 @@
public void onDisplayRemoved(int displayId) {
mBallotBox.vote(displayId, Vote.PRIORITY_HIGH_BRIGHTNESS_MODE, null);
mHbmMode.delete(displayId);
+ mHbmActive.delete(displayId);
}
@Override
@@ -2360,12 +2372,17 @@
// Display no longer there. Assume we'll get an onDisplayRemoved very soon.
return;
}
+
final int hbmMode = info.highBrightnessMode;
- if (hbmMode == mHbmMode.get(displayId)) {
+ final boolean isHbmActive = hbmMode != BrightnessInfo.HIGH_BRIGHTNESS_MODE_OFF &&
+ info.adjustedBrightness > info.highBrightnessTransitionPoint;
+ if (hbmMode == mHbmMode.get(displayId) &&
+ isHbmActive == mHbmActive.get(displayId)) {
// no change, ignore.
return;
}
mHbmMode.put(displayId, hbmMode);
+ mHbmActive.put(displayId, isHbmActive);
recalculateVotesForDisplay(displayId);
}
@@ -2379,28 +2396,36 @@
}
private void recalculateVotesForDisplay(int displayId) {
- final int hbmMode = mHbmMode.get(displayId, BrightnessInfo.HIGH_BRIGHTNESS_MODE_OFF);
Vote vote = null;
- if (hbmMode == BrightnessInfo.HIGH_BRIGHTNESS_MODE_SUNLIGHT) {
- // Device resource properties take priority over DisplayDeviceConfig
- if (mRefreshRateInHbmSunlight > 0) {
- vote = Vote.forRefreshRates(mRefreshRateInHbmSunlight,
- mRefreshRateInHbmSunlight);
- } else {
- final List<RefreshRateLimitation> limits =
- mDisplayManagerInternal.getRefreshRateLimitations(displayId);
- for (int i = 0; limits != null && i < limits.size(); i++) {
- final RefreshRateLimitation limitation = limits.get(i);
- if (limitation.type == REFRESH_RATE_LIMIT_HIGH_BRIGHTNESS_MODE) {
- vote = Vote.forRefreshRates(limitation.range.min, limitation.range.max);
- break;
+ if (mHbmActive.get(displayId, false)) {
+ final int hbmMode =
+ mHbmMode.get(displayId, BrightnessInfo.HIGH_BRIGHTNESS_MODE_OFF);
+ if (hbmMode == BrightnessInfo.HIGH_BRIGHTNESS_MODE_SUNLIGHT) {
+ // Device resource properties take priority over DisplayDeviceConfig
+ if (mRefreshRateInHbmSunlight > 0) {
+ vote = Vote.forRefreshRates(mRefreshRateInHbmSunlight,
+ mRefreshRateInHbmSunlight);
+ } else {
+ final List<RefreshRateLimitation> limits =
+ mDisplayManagerInternal.getRefreshRateLimitations(displayId);
+ for (int i = 0; limits != null && i < limits.size(); i++) {
+ final RefreshRateLimitation limitation = limits.get(i);
+ if (limitation.type == REFRESH_RATE_LIMIT_HIGH_BRIGHTNESS_MODE) {
+ vote = Vote.forRefreshRates(limitation.range.min,
+ limitation.range.max);
+ break;
+ }
}
}
+ } else if (hbmMode == BrightnessInfo.HIGH_BRIGHTNESS_MODE_HDR &&
+ mRefreshRateInHbmHdr > 0) {
+ // HBM for HDR vote isn't supported through DisplayDeviceConfig yet, so look for
+ // a vote from Device properties
+ vote = Vote.forRefreshRates(mRefreshRateInHbmHdr, mRefreshRateInHbmHdr);
+ } else {
+ Slog.w(TAG, "Unexpected HBM mode " + hbmMode + " for display ID " + displayId);
}
- }
- if (hbmMode == BrightnessInfo.HIGH_BRIGHTNESS_MODE_HDR
- && mRefreshRateInHbmHdr > 0) {
- vote = Vote.forRefreshRates(mRefreshRateInHbmHdr, mRefreshRateInHbmHdr);
+
}
mBallotBox.vote(displayId, Vote.PRIORITY_HIGH_BRIGHTNESS_MODE, vote);
}
@@ -2408,6 +2433,7 @@
void dumpLocked(PrintWriter pw) {
pw.println(" HbmObserver");
pw.println(" mHbmMode: " + mHbmMode);
+ pw.println(" mHbmActive: " + mHbmActive);
pw.println(" mRefreshRateInHbmSunlight: " + mRefreshRateInHbmSunlight);
pw.println(" mRefreshRateInHbmHdr: " + mRefreshRateInHbmHdr);
}
@@ -2630,19 +2656,11 @@
}
interface Injector {
- // TODO: brightnessfloat: change this to the float setting
- Uri DISPLAY_BRIGHTNESS_URI = Settings.System.getUriFor(Settings.System.SCREEN_BRIGHTNESS);
Uri PEAK_REFRESH_RATE_URI = Settings.System.getUriFor(Settings.System.PEAK_REFRESH_RATE);
@NonNull
DeviceConfigInterface getDeviceConfig();
- void registerBrightnessObserver(@NonNull ContentResolver cr,
- @NonNull ContentObserver observer);
-
- void unregisterBrightnessObserver(@NonNull ContentResolver cr,
- @NonNull ContentObserver observer);
-
void registerPeakRefreshRateObserver(@NonNull ContentResolver cr,
@NonNull ContentObserver observer);
@@ -2672,19 +2690,6 @@
}
@Override
- public void registerBrightnessObserver(@NonNull ContentResolver cr,
- @NonNull ContentObserver observer) {
- cr.registerContentObserver(DISPLAY_BRIGHTNESS_URI, false /*notifyDescendants*/,
- observer, UserHandle.USER_SYSTEM);
- }
-
- @Override
- public void unregisterBrightnessObserver(@NonNull ContentResolver cr,
- @NonNull ContentObserver observer) {
- cr.unregisterContentObserver(observer);
- }
-
- @Override
public void registerPeakRefreshRateObserver(@NonNull ContentResolver cr,
@NonNull ContentObserver observer) {
cr.registerContentObserver(PEAK_REFRESH_RATE_URI, false /*notifyDescendants*/,
diff --git a/services/core/java/com/android/server/display/DisplayPowerController.java b/services/core/java/com/android/server/display/DisplayPowerController.java
index 1063481..63d32c8 100644
--- a/services/core/java/com/android/server/display/DisplayPowerController.java
+++ b/services/core/java/com/android/server/display/DisplayPowerController.java
@@ -125,6 +125,7 @@
private static final int MSG_IGNORE_PROXIMITY = 8;
private static final int MSG_STOP = 9;
private static final int MSG_UPDATE_BRIGHTNESS = 10;
+ private static final int MSG_UPDATE_RBC = 11;
private static final int PROXIMITY_UNKNOWN = -1;
private static final int PROXIMITY_NEGATIVE = 0;
@@ -422,13 +423,13 @@
// PowerManager.BRIGHTNESS_INVALID_FLOAT when there's no temporary adjustment set.
private float mTemporaryAutoBrightnessAdjustment;
- // Whether a reduce bright colors (rbc) change has been initiated by the user. We want to
- // retain the current backlight level when rbc is toggled, since rbc additionally makes the
- // screen appear dimmer using screen colors rather than backlight levels, and therefore we
- // don't actually want to compensate for this by then in/decreasing the backlight when
- // toggling this feature.
+ // Whether reduce bright colors (rbc) has been turned on, or a change in strength has been
+ // requested. We want to retain the current backlight level when rbc is toggled, since rbc
+ // additionally makes the screen appear dimmer using screen colors rather than backlight levels,
+ // and therefore we don't actually want to compensate for this by then in/decreasing the
+ // backlight when toggling this feature.
// This should be false during system start up.
- private boolean mPendingUserRbcChange;
+ private boolean mPendingRbcOnOrChanged = false;
// Animators.
private ObjectAnimator mColorFadeOnAnimator;
@@ -564,23 +565,35 @@
@Override
public void onReduceBrightColorsActivationChanged(boolean activated,
boolean userInitiated) {
- applyReduceBrightColorsSplineAdjustment(userInitiated);
+ applyReduceBrightColorsSplineAdjustment(
+ /* rbcStrengthChanged= */ false, activated);
+
}
@Override
public void onReduceBrightColorsStrengthChanged(int strength) {
- applyReduceBrightColorsSplineAdjustment(/*userInitiated*/ false);
+ applyReduceBrightColorsSplineAdjustment(
+ /* rbcStrengthChanged= */ true, /* justActivated= */ false);
}
});
if (active) {
- applyReduceBrightColorsSplineAdjustment(/*userInitiated*/ false);
+ applyReduceBrightColorsSplineAdjustment(
+ /* rbcStrengthChanged= */ false, /* justActivated= */ false);
}
} else {
mCdsi = null;
}
}
- private void applyReduceBrightColorsSplineAdjustment(boolean userInitiated) {
+ private void applyReduceBrightColorsSplineAdjustment(
+ boolean rbcStrengthChanged, boolean justActivated) {
+ final int strengthChanged = rbcStrengthChanged ? 1 : 0;
+ final int activated = justActivated ? 1 : 0;
+ mHandler.obtainMessage(MSG_UPDATE_RBC, strengthChanged, activated).sendToTarget();
+ sendUpdatePowerState();
+ }
+
+ private void handleRbcChanged(boolean strengthChanged, boolean justActivated) {
if (mBrightnessMapper == null) {
Log.w(TAG, "No brightness mapping available to recalculate splines");
return;
@@ -591,8 +604,13 @@
adjustedNits[i] = mCdsi.getReduceBrightColorsAdjustedBrightnessNits(mNitsRange[i]);
}
mBrightnessMapper.recalculateSplines(mCdsi.isReduceBrightColorsActivated(), adjustedNits);
- mPendingUserRbcChange = userInitiated;
- sendUpdatePowerState();
+
+ mPendingRbcOnOrChanged = strengthChanged || justActivated;
+
+ // Reset model if strength changed OR rbc is turned off
+ if (strengthChanged || !justActivated && mAutomaticBrightnessController != null) {
+ mAutomaticBrightnessController.resetShortTermModel();
+ }
}
/**
@@ -926,7 +944,8 @@
private void reloadReduceBrightColours() {
if (mCdsi != null && mCdsi.isReduceBrightColorsActivated()) {
- applyReduceBrightColorsSplineAdjustment(/*userInitiated*/ false);
+ applyReduceBrightColorsSplineAdjustment(
+ /* rbcStrengthChanged= */ false, /* justActivated= */ false);
}
}
@@ -1259,10 +1278,6 @@
putScreenBrightnessSetting(brightnessState, /* updateCurrent */ true);
}
- // We save the brightness info *after* the brightness setting has been changed so that
- // the brightness info reflects the latest value.
- saveBrightnessInfo(getScreenBrightnessSetting());
-
// Apply dimming by at least some minimum amount when user activity
// timeout is about to expire.
if (mPowerRequest.policy == DisplayPowerRequest.POLICY_DIM) {
@@ -1393,6 +1408,11 @@
hadUserBrightnessPoint);
}
+ // We save the brightness info *after* the brightness setting has been changed and
+ // adjustments made so that the brightness info reflects the latest value.
+ saveBrightnessInfo(getScreenBrightnessSetting(), animateValue);
+ } else {
+ saveBrightnessInfo(getScreenBrightnessSetting());
}
// Log any changes to what is currently driving the brightness setting.
@@ -1509,18 +1529,27 @@
synchronized (mCachedBrightnessInfo) {
return new BrightnessInfo(
mCachedBrightnessInfo.brightness,
+ mCachedBrightnessInfo.adjustedBrightness,
mCachedBrightnessInfo.brightnessMin,
mCachedBrightnessInfo.brightnessMax,
- mCachedBrightnessInfo.hbmMode);
+ mCachedBrightnessInfo.hbmMode,
+ mCachedBrightnessInfo.highBrightnessTransitionPoint);
}
}
private void saveBrightnessInfo(float brightness) {
+ saveBrightnessInfo(brightness, brightness);
+ }
+
+ private void saveBrightnessInfo(float brightness, float adjustedBrightness) {
synchronized (mCachedBrightnessInfo) {
mCachedBrightnessInfo.brightness = brightness;
+ mCachedBrightnessInfo.adjustedBrightness = adjustedBrightness;
mCachedBrightnessInfo.brightnessMin = mHbmController.getCurrentBrightnessMin();
mCachedBrightnessInfo.brightnessMax = mHbmController.getCurrentBrightnessMax();
mCachedBrightnessInfo.hbmMode = mHbmController.getHighBrightnessMode();
+ mCachedBrightnessInfo.highBrightnessTransitionPoint =
+ mHbmController.getTransitionPoint();
}
}
@@ -2062,21 +2091,24 @@
return true;
}
+ // We want to return true if the user has set the screen brightness.
+ // If they have just turned RBC on (and therefore added that interaction to the curve),
+ // or changed the brightness another way, then we should return true.
private boolean updateUserSetScreenBrightness() {
- final boolean brightnessSplineChanged = mPendingUserRbcChange;
- if (mPendingUserRbcChange && !Float.isNaN(mCurrentScreenBrightnessSetting)) {
+ final boolean treatAsIfUserChanged = mPendingRbcOnOrChanged;
+ if (treatAsIfUserChanged && !Float.isNaN(mCurrentScreenBrightnessSetting)) {
mLastUserSetScreenBrightness = mCurrentScreenBrightnessSetting;
}
- mPendingUserRbcChange = false;
+ mPendingRbcOnOrChanged = false;
if ((Float.isNaN(mPendingScreenBrightnessSetting)
|| mPendingScreenBrightnessSetting < 0.0f)) {
- return brightnessSplineChanged;
+ return treatAsIfUserChanged;
}
if (mCurrentScreenBrightnessSetting == mPendingScreenBrightnessSetting) {
mPendingScreenBrightnessSetting = PowerManager.BRIGHTNESS_INVALID_FLOAT;
mTemporaryScreenBrightness = PowerManager.BRIGHTNESS_INVALID_FLOAT;
- return brightnessSplineChanged;
+ return treatAsIfUserChanged;
}
setCurrentScreenBrightness(mPendingScreenBrightnessSetting);
mLastUserSetScreenBrightness = mPendingScreenBrightnessSetting;
@@ -2195,6 +2227,18 @@
pw.println(" mSkipScreenOnBrightnessRamp=" + mSkipScreenOnBrightnessRamp);
pw.println(" mColorFadeFadesConfig=" + mColorFadeFadesConfig);
pw.println(" mColorFadeEnabled=" + mColorFadeEnabled);
+ synchronized (mCachedBrightnessInfo) {
+ pw.println(" mCachedBrightnessInfo.brightness=" + mCachedBrightnessInfo.brightness);
+ pw.println(" mCachedBrightnessInfo.adjustedBrightness=" +
+ mCachedBrightnessInfo.adjustedBrightness);
+ pw.println(" mCachedBrightnessInfo.brightnessMin=" +
+ mCachedBrightnessInfo.brightnessMin);
+ pw.println(" mCachedBrightnessInfo.brightnessMax=" +
+ mCachedBrightnessInfo.brightnessMax);
+ pw.println(" mCachedBrightnessInfo.hbmMode=" + mCachedBrightnessInfo.hbmMode);
+ pw.println(" mCachedBrightnessInfo.highBrightnessTransitionPoint=" +
+ mCachedBrightnessInfo.highBrightnessTransitionPoint);
+ }
pw.println(" mDisplayBlanksAfterDozeConfig=" + mDisplayBlanksAfterDozeConfig);
pw.println(" mBrightnessBucketsInDozeConfig=" + mBrightnessBucketsInDozeConfig);
@@ -2406,6 +2450,12 @@
}
handleSettingsChange(false /*userSwitch*/);
break;
+
+ case MSG_UPDATE_RBC:
+ final int strengthChanged = msg.arg1;
+ final int justActivated = msg.arg2;
+ handleRbcChanged(strengthChanged == 1, justActivated == 1);
+ break;
}
}
}
@@ -2606,8 +2656,10 @@
static class CachedBrightnessInfo {
public float brightness;
+ public float adjustedBrightness;
public float brightnessMin;
public float brightnessMax;
public int hbmMode;
+ public float highBrightnessTransitionPoint;
}
}
diff --git a/services/core/java/com/android/server/display/HighBrightnessModeController.java b/services/core/java/com/android/server/display/HighBrightnessModeController.java
index 2791f6a..1e1cfeb 100644
--- a/services/core/java/com/android/server/display/HighBrightnessModeController.java
+++ b/services/core/java/com/android/server/display/HighBrightnessModeController.java
@@ -59,6 +59,9 @@
private static final float HDR_PERCENT_OF_SCREEN_REQUIRED = 0.50f;
+ @VisibleForTesting
+ static final float HBM_TRANSITION_POINT_INVALID = Float.POSITIVE_INFINITY;
+
private final float mBrightnessMin;
private final float mBrightnessMax;
private final Handler mHandler;
@@ -214,6 +217,14 @@
return mHbmMode;
}
+ float getTransitionPoint() {
+ if (deviceSupportsHbm()) {
+ return mHbmData.transitionPoint;
+ } else {
+ return HBM_TRANSITION_POINT_INVALID;
+ }
+ }
+
void stop() {
registerHdrListener(null /*displayToken*/);
mSkinThermalStatusObserver.stopObserving();
diff --git a/services/core/java/com/android/server/input/InputManagerService.java b/services/core/java/com/android/server/input/InputManagerService.java
index b0b8be2..fae7e45 100644
--- a/services/core/java/com/android/server/input/InputManagerService.java
+++ b/services/core/java/com/android/server/input/InputManagerService.java
@@ -900,6 +900,7 @@
@Override // Binder call
public VerifiedInputEvent verifyInputEvent(InputEvent event) {
+ Objects.requireNonNull(event, "event must not be null");
return nativeVerifyInputEvent(mPtr, event);
}
diff --git a/services/core/java/com/android/server/location/contexthub/ContextHubClientBroker.java b/services/core/java/com/android/server/location/contexthub/ContextHubClientBroker.java
index fa33338..03e421b 100644
--- a/services/core/java/com/android/server/location/contexthub/ContextHubClientBroker.java
+++ b/services/core/java/com/android/server/location/contexthub/ContextHubClientBroker.java
@@ -209,6 +209,12 @@
*/
private AtomicBoolean mIsPendingIntentCancelled = new AtomicBoolean(false);
+ /**
+ * True if a permissions query has been issued and is being processed. Used to prevent too many
+ * queries from being issued by a single client at once.
+ */
+ private AtomicBoolean mIsPermQueryIssued = new AtomicBoolean(false);
+
/*
* True if the application creating the client has the ACCESS_CONTEXT_HUB permission.
*/
@@ -240,11 +246,11 @@
private final IContextHubTransactionCallback mQueryPermsCallback =
new IContextHubTransactionCallback.Stub() {
@Override
- public void onTransactionComplete(int result) {
- }
+ public void onTransactionComplete(int result) {}
@Override
public void onQueryResponse(int result, List<NanoAppState> nanoAppStateList) {
+ mIsPermQueryIssued.set(false);
if (result != ContextHubTransaction.RESULT_SUCCESS && nanoAppStateList != null) {
Log.e(TAG, "Permissions query failed, but still received nanoapp state");
} else if (nanoAppStateList != null) {
@@ -656,9 +662,11 @@
* communicated with in the past.
*/
private void checkNanoappPermsAsync() {
- ContextHubServiceTransaction transaction = mTransactionManager.createQueryTransaction(
- mAttachedContextHubInfo.getId(), mQueryPermsCallback, mPackage);
- mTransactionManager.addTransaction(transaction);
+ if (!mIsPermQueryIssued.getAndSet(true)) {
+ ContextHubServiceTransaction transaction = mTransactionManager.createQueryTransaction(
+ mAttachedContextHubInfo.getId(), mQueryPermsCallback, mPackage);
+ mTransactionManager.addTransaction(transaction);
+ }
}
private int updateNanoAppAuthState(
diff --git a/services/core/java/com/android/server/location/injector/LocationPermissionsHelper.java b/services/core/java/com/android/server/location/injector/LocationPermissionsHelper.java
index a47c48f..2df2101 100644
--- a/services/core/java/com/android/server/location/injector/LocationPermissionsHelper.java
+++ b/services/core/java/com/android/server/location/injector/LocationPermissionsHelper.java
@@ -100,7 +100,7 @@
return false;
}
- return mAppOps.checkOpNoThrow(permissionLevel, identity);
+ return mAppOps.checkOpNoThrow(LocationPermissions.asAppOp(permissionLevel), identity);
}
protected abstract boolean hasPermission(String permission, CallerIdentity callerIdentity);
diff --git a/services/core/java/com/android/server/location/provider/StationaryThrottlingLocationProvider.java b/services/core/java/com/android/server/location/provider/StationaryThrottlingLocationProvider.java
index ad87c45..cc51cea 100644
--- a/services/core/java/com/android/server/location/provider/StationaryThrottlingLocationProvider.java
+++ b/services/core/java/com/android/server/location/provider/StationaryThrottlingLocationProvider.java
@@ -154,6 +154,7 @@
mDeviceStationaryHelper.removeListener(this);
mDeviceStationary = false;
mDeviceStationaryRealtimeMs = Long.MIN_VALUE;
+ onThrottlingChangedLocked(false);
}
}
}
diff --git a/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java b/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java
index dab980a..3019146 100644
--- a/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java
+++ b/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java
@@ -557,11 +557,14 @@
grantPermissionsToSystemPackage(pm, verifier, userId, PHONE_PERMISSIONS, SMS_PERMISSIONS);
// SetupWizard
- grantPermissionsToSystemPackage(pm,
- ArrayUtils.firstOrNull(getKnownPackages(
- PackageManagerInternal.PACKAGE_SETUP_WIZARD, userId)), userId,
- PHONE_PERMISSIONS, CONTACTS_PERMISSIONS, ALWAYS_LOCATION_PERMISSIONS,
- CAMERA_PERMISSIONS);
+ final String setupWizardPackage = ArrayUtils.firstOrNull(getKnownPackages(
+ PackageManagerInternal.PACKAGE_SETUP_WIZARD, userId));
+ grantPermissionsToSystemPackage(pm, setupWizardPackage, userId, PHONE_PERMISSIONS,
+ CONTACTS_PERMISSIONS, ALWAYS_LOCATION_PERMISSIONS, CAMERA_PERMISSIONS);
+ if (mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_AUTOMOTIVE, 0)) {
+ grantPermissionsToSystemPackage(
+ pm, setupWizardPackage, userId, NEARBY_DEVICES_PERMISSIONS);
+ }
// Camera
grantPermissionsToSystemPackage(pm,
diff --git a/services/core/java/com/android/server/pm/permission/LegacyPermissionManagerService.java b/services/core/java/com/android/server/pm/permission/LegacyPermissionManagerService.java
index b1676d0..ea554d3 100644
--- a/services/core/java/com/android/server/pm/permission/LegacyPermissionManagerService.java
+++ b/services/core/java/com/android/server/pm/permission/LegacyPermissionManagerService.java
@@ -30,6 +30,7 @@
import android.os.ServiceManager;
import android.os.UserHandle;
import android.permission.ILegacyPermissionManager;
+import android.util.EventLog;
import android.util.Log;
import com.android.internal.annotations.VisibleForTesting;
@@ -187,10 +188,25 @@
private void verifyCallerCanCheckAccess(String packageName, String message, int pid, int uid) {
// If the check is being requested by an app then only allow the app to query its own
// access status.
+ boolean reportError = false;
int callingUid = mInjector.getCallingUid();
int callingPid = mInjector.getCallingPid();
if (UserHandle.getAppId(callingUid) >= Process.FIRST_APPLICATION_UID && (callingUid != uid
|| callingPid != pid)) {
+ reportError = true;
+ }
+ // If the query is against an app on the device, then the check should only be allowed if
+ // the provided uid matches that of the specified package.
+ if (packageName != null && UserHandle.getAppId(uid) >= Process.FIRST_APPLICATION_UID) {
+ int packageUid = mInjector.getPackageUidForUser(packageName, UserHandle.getUserId(uid));
+ if (uid != packageUid) {
+ EventLog.writeEvent(0x534e4554, "193441322",
+ UserHandle.getAppId(callingUid) >= Process.FIRST_APPLICATION_UID
+ ? callingUid : uid, "Package uid mismatch");
+ reportError = true;
+ }
+ }
+ if (reportError) {
String response = String.format(
"Calling uid %d, pid %d cannot access for package %s (uid=%d, pid=%d): %s",
callingUid, callingPid, packageName, uid, pid, message);
@@ -385,12 +401,14 @@
@VisibleForTesting
public static class Injector {
private final Context mContext;
+ private final PackageManagerInternal mPackageManagerInternal;
/**
* Public constructor that accepts a {@code context} within which to operate.
*/
public Injector(@NonNull Context context) {
mContext = context;
+ mPackageManagerInternal = LocalServices.getService(PackageManagerInternal.class);
}
/**
@@ -453,5 +471,12 @@
return mContext.getPackageManager().getApplicationInfoAsUser(packageName, 0,
UserHandle.getUserHandleForUid(uid));
}
+
+ /**
+ * Returns the uid for the specified {@code packageName} under the provided {@code userId}.
+ */
+ public int getPackageUidForUser(String packageName, int userId) {
+ return mPackageManagerInternal.getPackageUid(packageName, 0, userId);
+ }
}
}
diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java
index dd2583a0c..5acff2b 100644
--- a/services/core/java/com/android/server/policy/PhoneWindowManager.java
+++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java
@@ -1816,7 +1816,11 @@
@Override
public int onAppTransitionStartingLocked(boolean keyguardGoingAway, long duration,
long statusBarAnimationStartTime, long statusBarAnimationDuration) {
- return handleStartTransitionForKeyguardLw(keyguardGoingAway, duration);
+ // When remote animation is enabled for KEYGUARD_GOING_AWAY transition, SysUI
+ // receives IRemoteAnimationRunner#onAnimationStart to start animation, so we don't
+ // need to call IKeyguardService#keyguardGoingAway here.
+ return handleStartTransitionForKeyguardLw(keyguardGoingAway
+ && !WindowManagerService.sEnableRemoteKeyguardGoingAwayAnimation, duration);
}
@Override
@@ -3064,7 +3068,7 @@
private int handleStartTransitionForKeyguardLw(boolean keyguardGoingAway, long duration) {
final int res = applyKeyguardOcclusionChange();
if (res != 0) return res;
- if (!WindowManagerService.sEnableRemoteKeyguardGoingAwayAnimation && keyguardGoingAway) {
+ if (keyguardGoingAway) {
if (DEBUG_KEYGUARD) Slog.d(TAG, "Starting keyguard exit animation");
startKeyguardExitAnimation(SystemClock.uptimeMillis(), duration);
}
diff --git a/services/core/java/com/android/server/policy/keyguard/KeyguardServiceDelegate.java b/services/core/java/com/android/server/policy/keyguard/KeyguardServiceDelegate.java
index d190678..86ff33e 100644
--- a/services/core/java/com/android/server/policy/keyguard/KeyguardServiceDelegate.java
+++ b/services/core/java/com/android/server/policy/keyguard/KeyguardServiceDelegate.java
@@ -410,8 +410,7 @@
}
public void startKeyguardExitAnimation(long startTime, long fadeoutDuration) {
- if (!WindowManagerService.sEnableRemoteKeyguardGoingAwayAnimation
- && mKeyguardService != null) {
+ if (mKeyguardService != null) {
mKeyguardService.startKeyguardExitAnimation(startTime, fadeoutDuration);
}
}
diff --git a/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java b/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java
index 61770ea..106cff1 100644
--- a/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java
+++ b/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java
@@ -67,6 +67,7 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.annotation.UserIdInt;
import android.app.ActivityManagerInternal;
import android.app.AppOpsManager;
import android.app.AppOpsManager.HistoricalOp;
@@ -82,6 +83,7 @@
import android.bluetooth.BluetoothActivityEnergyInfo;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.UidTraffic;
+import android.content.ContentResolver;
import android.content.Context;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageInfo;
@@ -734,6 +736,10 @@
case FrameworkStatsLog.RKP_ERROR_STATS:
case FrameworkStatsLog.KEYSTORE2_CRASH_STATS:
return pullKeystoreAtoms(atomTag, data);
+ case FrameworkStatsLog.ACCESSIBILITY_SHORTCUT_STATS:
+ return pullAccessibilityShortcutStatsLocked(atomTag, data);
+ case FrameworkStatsLog.ACCESSIBILITY_FLOATING_MENU_STATS:
+ return pullAccessibilityFloatingMenuStatsLocked(atomTag, data);
default:
throw new UnsupportedOperationException("Unknown tagId=" + atomTag);
}
@@ -930,6 +936,8 @@
registerKeystoreKeyOperationWithGeneralInfo();
registerRkpErrorStats();
registerKeystoreCrashStats();
+ registerAccessibilityShortcutStats();
+ registerAccessibilityFloatingMenuStats();
}
private void initAndRegisterNetworkStatsPullers() {
@@ -4150,6 +4158,26 @@
mStatsCallbackImpl);
}
+ private void registerAccessibilityShortcutStats() {
+ int tagId = FrameworkStatsLog.ACCESSIBILITY_SHORTCUT_STATS;
+ mStatsManager.setPullAtomCallback(
+ tagId,
+ null, // use default PullAtomMetadata values
+ DIRECT_EXECUTOR,
+ mStatsCallbackImpl
+ );
+ }
+
+ private void registerAccessibilityFloatingMenuStats() {
+ int tagId = FrameworkStatsLog.ACCESSIBILITY_FLOATING_MENU_STATS;
+ mStatsManager.setPullAtomCallback(
+ tagId,
+ null, // use default PullAtomMetadata values
+ DIRECT_EXECUTOR,
+ mStatsCallbackImpl
+ );
+ }
+
int parseKeystoreStorageStats(KeystoreAtom[] atoms, List<StatsEvent> pulledData) {
for (KeystoreAtom atomWrapper : atoms) {
if (atomWrapper.payload.getTag() != KeystoreAtomPayload.storageStats) {
@@ -4341,6 +4369,144 @@
}
}
+ int pullAccessibilityShortcutStatsLocked(int atomTag, List<StatsEvent> pulledData) {
+ UserManager userManager = mContext.getSystemService(UserManager.class);
+ if (userManager == null) {
+ return StatsManager.PULL_SKIP;
+ }
+ final long token = Binder.clearCallingIdentity();
+ try {
+ final ContentResolver resolver = mContext.getContentResolver();
+ final int hardware_shortcut_type =
+ FrameworkStatsLog.ACCESSIBILITY_SHORTCUT_REPORTED__SHORTCUT_TYPE__VOLUME_KEY;
+ final int triple_tap_shortcut =
+ FrameworkStatsLog.ACCESSIBILITY_SHORTCUT_REPORTED__SHORTCUT_TYPE__TRIPLE_TAP;
+ for (UserInfo userInfo : userManager.getUsers()) {
+ final int userId = userInfo.getUserHandle().getIdentifier();
+
+ if (isAccessibilityShortcutUser(mContext, userId)) {
+ final int software_shortcut_type = Settings.Secure.getIntForUser(resolver,
+ Settings.Secure.ACCESSIBILITY_BUTTON_MODE, 0, userId);
+ final String software_shortcut_list = Settings.Secure.getStringForUser(resolver,
+ Settings.Secure.ACCESSIBILITY_BUTTON_TARGETS, userId);
+ final int software_shortcut_service_num = countAccessibilityServices(
+ software_shortcut_list);
+
+ final String hardware_shortcut_list = Settings.Secure.getStringForUser(resolver,
+ Settings.Secure.ACCESSIBILITY_SHORTCUT_TARGET_SERVICE, userId);
+ final int hardware_shortcut_service_num = countAccessibilityServices(
+ hardware_shortcut_list);
+
+ // only allow magnification to use it for now
+ final int triple_tap_service_num = Settings.Secure.getIntForUser(resolver,
+ Settings.Secure.ACCESSIBILITY_DISPLAY_MAGNIFICATION_ENABLED, 0, userId);
+
+ pulledData.add(
+ FrameworkStatsLog.buildStatsEvent(atomTag,
+ software_shortcut_type, software_shortcut_service_num,
+ hardware_shortcut_type, hardware_shortcut_service_num,
+ triple_tap_shortcut, triple_tap_service_num));
+ }
+ }
+ } catch (RuntimeException e) {
+ Slog.e(TAG, "pulling accessibility shortcuts stats failed at getUsers", e);
+ return StatsManager.PULL_SKIP;
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ return StatsManager.PULL_SUCCESS;
+ }
+
+ int pullAccessibilityFloatingMenuStatsLocked(int atomTag, List<StatsEvent> pulledData) {
+ UserManager userManager = mContext.getSystemService(UserManager.class);
+ if (userManager == null) {
+ return StatsManager.PULL_SKIP;
+ }
+ final long token = Binder.clearCallingIdentity();
+ try {
+ final ContentResolver resolver = mContext.getContentResolver();
+ final int defaultSize = 0;
+ final int defaultIconType = 0;
+ final int defaultFadeEnabled = 1;
+ final float defaultOpacity = 0.55f;
+
+ for (UserInfo userInfo : userManager.getUsers()) {
+ final int userId = userInfo.getUserHandle().getIdentifier();
+
+ if (isAccessibilityFloatingMenuUser(mContext, userId)) {
+ final int size = Settings.Secure.getIntForUser(resolver,
+ Settings.Secure.ACCESSIBILITY_FLOATING_MENU_SIZE, defaultSize, userId);
+ final int type = Settings.Secure.getIntForUser(resolver,
+ Settings.Secure.ACCESSIBILITY_FLOATING_MENU_ICON_TYPE,
+ defaultIconType, userId);
+ final boolean fadeEnabled = (Settings.Secure.getIntForUser(resolver,
+ Settings.Secure.ACCESSIBILITY_FLOATING_MENU_FADE_ENABLED,
+ defaultFadeEnabled, userId)) == 1;
+ final float opacity = Settings.Secure.getFloatForUser(resolver,
+ Settings.Secure.ACCESSIBILITY_FLOATING_MENU_OPACITY,
+ defaultOpacity, userId);
+
+ pulledData.add(
+ FrameworkStatsLog.buildStatsEvent(atomTag, size, type, fadeEnabled,
+ opacity));
+ }
+ }
+ } catch (RuntimeException e) {
+ Slog.e(TAG, "pulling accessibility floating menu stats failed at getUsers", e);
+ return StatsManager.PULL_SKIP;
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ return StatsManager.PULL_SUCCESS;
+ }
+
+ /**
+ * Counts how many accessibility services (including features) there are in the colon-separated
+ * string list.
+ *
+ * @param semicolonList colon-separated string, it should be
+ * {@link Settings.Secure#ACCESSIBILITY_BUTTON_TARGETS} or
+ * {@link Settings.Secure#ACCESSIBILITY_SHORTCUT_TARGET_SERVICE}.
+ * @return The number of accessibility services
+ */
+ private int countAccessibilityServices(String semicolonList) {
+ if (TextUtils.isEmpty(semicolonList)) {
+ return 0;
+ }
+ final int semiColonNums = (int) semicolonList.chars().filter(ch -> ch == ':').count();
+ return TextUtils.isEmpty(semicolonList) ? 0 : semiColonNums + 1;
+ }
+
+ private boolean isAccessibilityShortcutUser(Context context, @UserIdInt int userId) {
+ final ContentResolver resolver = context.getContentResolver();
+
+ final String software_shortcut_list = Settings.Secure.getStringForUser(resolver,
+ Settings.Secure.ACCESSIBILITY_BUTTON_TARGETS, userId);
+ final String hardware_shortcut_list = Settings.Secure.getStringForUser(resolver,
+ Settings.Secure.ACCESSIBILITY_SHORTCUT_TARGET_SERVICE, userId);
+ final boolean hardware_shortcut_dialog_shown = Settings.Secure.getIntForUser(resolver,
+ Settings.Secure.ACCESSIBILITY_SHORTCUT_DIALOG_SHOWN, 0, userId) == 1;
+ final boolean software_shortcut_enabled = !TextUtils.isEmpty(software_shortcut_list);
+ final boolean hardware_shortcut_enabled =
+ hardware_shortcut_dialog_shown && !TextUtils.isEmpty(hardware_shortcut_list);
+ final boolean triple_tap_shortcut_enabled = Settings.Secure.getIntForUser(resolver,
+ Settings.Secure.ACCESSIBILITY_DISPLAY_MAGNIFICATION_ENABLED, 0, userId) == 1;
+
+ return software_shortcut_enabled || hardware_shortcut_enabled
+ || triple_tap_shortcut_enabled;
+ }
+
+ private boolean isAccessibilityFloatingMenuUser(Context context, @UserIdInt int userId) {
+ final ContentResolver resolver = context.getContentResolver();
+ final int mode = Settings.Secure.getIntForUser(resolver,
+ Settings.Secure.ACCESSIBILITY_BUTTON_MODE, 0, userId);
+ final String software_string = Settings.Secure.getStringForUser(resolver,
+ Settings.Secure.ACCESSIBILITY_BUTTON_TARGETS, userId);
+
+ return (mode == Settings.Secure.ACCESSIBILITY_BUTTON_MODE_FLOATING_MENU)
+ && !TextUtils.isEmpty(software_string);
+ }
+
// Thermal event received from vendor thermal management subsystem
private static final class ThermalEventListener extends IThermalEventListener.Stub {
@Override
diff --git a/services/core/java/com/android/server/wm/ActivityClientController.java b/services/core/java/com/android/server/wm/ActivityClientController.java
index cc0db1d..ee72fc8 100644
--- a/services/core/java/com/android/server/wm/ActivityClientController.java
+++ b/services/core/java/com/android/server/wm/ActivityClientController.java
@@ -1072,7 +1072,7 @@
r.mDisplayContent.mAppTransition.overridePendingAppTransition(
packageName, enterAnim, exitAnim, null, null,
r.mOverrideTaskTransition);
- mService.getTransitionController().setOverrideAnimation(
+ r.mTransitionController.setOverrideAnimation(
TransitionInfo.AnimationOptions.makeCustomAnimOptions(packageName,
enterAnim, exitAnim, r.mOverrideTaskTransition),
null /* startCallback */, null /* finishCallback */);
diff --git a/services/core/java/com/android/server/wm/ActivityInterceptorCallback.java b/services/core/java/com/android/server/wm/ActivityInterceptorCallback.java
new file mode 100644
index 0000000..1c2333a
--- /dev/null
+++ b/services/core/java/com/android/server/wm/ActivityInterceptorCallback.java
@@ -0,0 +1,98 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.wm;
+
+import android.annotation.IntDef;
+import android.annotation.Nullable;
+import android.app.ActivityOptions;
+import android.content.Intent;
+import android.content.pm.ActivityInfo;
+import android.content.pm.ResolveInfo;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * Callback to intercept activity starts and possibly block/redirect them.
+ */
+public abstract class ActivityInterceptorCallback {
+ /**
+ * Intercept the launch intent based on various signals. If an interception happened, returns
+ * a new/existing non-null {@link Intent} which may redirect to another activity.
+ *
+ * @return null if no interception occurred, or a non-null intent which replaces the
+ * existing intent.
+ */
+ public abstract @Nullable Intent intercept(ActivityInterceptorInfo info);
+
+ /**
+ * The unique id of each interceptor which determines the order it will execute in.
+ */
+ @IntDef(suffix = { "_ORDERED_ID" }, value = {
+ FIRST_ORDERED_ID,
+ LAST_ORDERED_ID // Update this when adding new ids
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface OrderedId {}
+
+ /**
+ * The first id, used by the framework to determine the valid range of ids.
+ */
+ static final int FIRST_ORDERED_ID = 0;
+
+ /**
+ * The final id, used by the framework to determine the valid range of ids. Update this when
+ * adding new ids.
+ */
+ static final int LAST_ORDERED_ID = FIRST_ORDERED_ID;
+
+ /**
+ * Data class for storing the various arguments needed for activity interception.
+ */
+ public static final class ActivityInterceptorInfo {
+ public final int realCallingUid;
+ public final int realCallingPid;
+ public final int userId;
+ public final String callingPackage;
+ public final String callingFeatureId;
+ public final Intent intent;
+ public final ResolveInfo rInfo;
+ public final ActivityInfo aInfo;
+ public final String resolvedType;
+ public final int callingPid;
+ public final int callingUid;
+ public final ActivityOptions checkedOptions;
+
+ public ActivityInterceptorInfo(int realCallingUid, int realCallingPid, int userId,
+ String callingPackage, String callingFeatureId, Intent intent,
+ ResolveInfo rInfo, ActivityInfo aInfo, String resolvedType, int callingPid,
+ int callingUid, ActivityOptions checkedOptions) {
+ this.realCallingUid = realCallingUid;
+ this.realCallingPid = realCallingPid;
+ this.userId = userId;
+ this.callingPackage = callingPackage;
+ this.callingFeatureId = callingFeatureId;
+ this.intent = intent;
+ this.rInfo = rInfo;
+ this.aInfo = aInfo;
+ this.resolvedType = resolvedType;
+ this.callingPid = callingPid;
+ this.callingUid = callingUid;
+ this.checkedOptions = checkedOptions;
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index db44dd8..6d144e1 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -1411,9 +1411,8 @@
this.task = newTask;
if (shouldStartChangeTransition(newParent, oldParent)) {
- // The new parent and old parent may be in different position. Need to offset the
- // animation surface to keep it in its original position.
- initializeChangeTransition(getBounds(), newParent.getBounds());
+ // Animate change transition on TaskFragment level to get the correct window crop.
+ newParent.initializeChangeTransition(getBounds(), getSurfaceControl());
}
super.onParentChanged(newParent, oldParent);
@@ -1532,7 +1531,7 @@
}
// TODO(b/169035022): move to a more-appropriate place.
- mAtmService.getTransitionController().collect(this);
+ mTransitionController.collect(this);
if (prevDc.mOpeningApps.remove(this)) {
// Transfer opening transition to new display.
mDisplayContent.mOpeningApps.add(this);
@@ -2406,17 +2405,6 @@
});
}
- void removeStartingWindowIfNeeded() {
- // Removing the task snapshot after the task is actually focused (see
- // Task#onWindowFocusChanged). Since some of the app contents may draw in this time and
- // requires more times to draw finish, in case flicking may happen when removing the task
- // snapshot too early. (i.e. Showing IME.)
- if ((mStartingData instanceof SnapshotStartingData) && !getTask().isFocused()) {
- return;
- }
- removeStartingWindow();
- }
-
void removeStartingWindow() {
if (transferSplashScreenIfNeeded()) {
return;
@@ -3097,9 +3085,9 @@
mAtmService.deferWindowLayout();
try {
- final Transition newTransition = (!mAtmService.getTransitionController().isCollecting()
- && mAtmService.getTransitionController().getTransitionPlayer() != null)
- ? mAtmService.getTransitionController().createTransition(TRANSIT_CLOSE) : null;
+ final Transition newTransition = (!mTransitionController.isCollecting()
+ && mTransitionController.getTransitionPlayer() != null)
+ ? mTransitionController.createTransition(TRANSIT_CLOSE) : null;
mTaskSupervisor.mNoHistoryActivities.remove(this);
makeFinishingLocked();
// Make a local reference to its task since this.task could be set to null once this
@@ -3132,7 +3120,7 @@
final boolean endTask = task.getTopNonFinishingActivity() == null
&& !task.isClearingToReuseTask();
if (newTransition != null) {
- mAtmService.getTransitionController().requestStartTransition(newTransition,
+ mTransitionController.requestStartTransition(newTransition,
endTask ? task : null, null /* remote */);
}
if (isState(RESUMED)) {
@@ -3560,12 +3548,12 @@
if (stopped) {
abortAndClearOptionsAnimation();
}
- if (mAtmService.getTransitionController().isCollecting()) {
+ if (mTransitionController.isCollecting()) {
// We don't want the finishing to change the transition ready state since there will not
// be corresponding setReady for finishing.
- mAtmService.getTransitionController().collectExistenceChange(this);
+ mTransitionController.collectExistenceChange(this);
} else {
- mAtmService.getTransitionController().requestTransitionIfNeeded(TRANSIT_CLOSE, this);
+ mTransitionController.requestTransitionIfNeeded(TRANSIT_CLOSE, this);
}
}
@@ -3817,7 +3805,7 @@
} else if (getDisplayContent().mAppTransition.isTransitionSet()) {
getDisplayContent().mClosingApps.add(this);
delayed = true;
- } else if (mAtmService.getTransitionController().inTransition()) {
+ } else if (mTransitionController.inTransition()) {
delayed = true;
}
@@ -3829,7 +3817,7 @@
}
// TODO(b/169035022): move to a more-appropriate place.
- mAtmService.getTransitionController().collect(this);
+ mTransitionController.collect(this);
ProtoLog.v(WM_DEBUG_APP_TRANSITIONS,
"Removing app %s delayed=%b animation=%s animating=%b", this, delayed,
@@ -4030,7 +4018,7 @@
ProtoLog.v(WM_DEBUG_ADD_REMOVE,
"Removing starting %s from %s", tStartingWindow, fromActivity);
- mAtmService.getTransitionController().collect(tStartingWindow);
+ mTransitionController.collect(tStartingWindow);
tStartingWindow.reparent(this, POSITION_TOP);
// Propagate other interesting state between the tokens. If the old token is displayed,
@@ -4056,7 +4044,7 @@
// the token we transfer the animation over. Thus, set this flag to indicate
// we've transferred the animation.
mUseTransferredAnimation = true;
- } else if (mAtmService.getTransitionController().getTransitionPlayer() != null) {
+ } else if (mTransitionController.getTransitionPlayer() != null) {
// In the new transit system, just set this every time we transfer the window
mUseTransferredAnimation = true;
}
@@ -4553,8 +4541,7 @@
}
if (options != null) {
- mAtmService.getTransitionController().setOverrideAnimation(options,
- startCallback, finishCallback);
+ mTransitionController.setOverrideAnimation(options, startCallback, finishCallback);
}
}
@@ -4785,7 +4772,7 @@
Debug.getCallers(6));
// Before setting mVisibleRequested so we can track changes.
- mAtmService.getTransitionController().collect(this);
+ mTransitionController.collect(this);
onChildVisibilityRequested(visible);
@@ -4857,7 +4844,7 @@
}
// If in a transition, defer commits for activities that are going invisible
- if (!visible && mAtmService.getTransitionController().inTransition(this)) {
+ if (!visible && inTransition()) {
return;
}
// If we are preparing an app transition, then delay changing
@@ -4942,7 +4929,8 @@
} else {
// If we are being set visible, and the starting window is not yet displayed,
// then make sure it doesn't get displayed.
- if (mStartingWindow != null && !mStartingWindow.isDrawn()) {
+ if (mStartingWindow != null && !mStartingWindow.isDrawn()
+ && (firstWindowDrawn || allDrawn)) {
mStartingWindow.clearPolicyVisibilityFlag(LEGACY_POLICY_VISIBILITY);
mStartingWindow.mLegacyPolicyVisibilityAfterAnim = false;
}
@@ -4984,8 +4972,7 @@
* this has become invisible.
*/
private void postApplyAnimation(boolean visible) {
- final boolean usingShellTransitions =
- mAtmService.getTransitionController().getTransitionPlayer() != null;
+ final boolean usingShellTransitions = mTransitionController.isShellTransitionsEnabled();
final boolean delayed = isAnimating(TRANSITION | PARENTS | CHILDREN,
ANIMATION_TYPE_APP_TRANSITION | ANIMATION_TYPE_WINDOW_ANIMATION
| ANIMATION_TYPE_RECENTS);
@@ -5435,7 +5422,7 @@
// returns. Just need to confirm this reasoning makes sense.
final boolean deferHidingClient = canEnterPictureInPicture
&& !isState(STARTED, STOPPING, STOPPED, PAUSED);
- if (!mAtmService.getTransitionController().isShellTransitionsEnabled()
+ if (!mTransitionController.isShellTransitionsEnabled()
&& deferHidingClient && pictureInPictureArgs.isAutoEnterEnabled()) {
// Go ahead and just put the activity in pip if it supports auto-pip.
mAtmService.enterPictureInPictureMode(this, pictureInPictureArgs);
@@ -5957,7 +5944,7 @@
}
void startFreezingScreen(int overrideOriginalDisplayRotation) {
- if (mAtmService.getTransitionController().isShellTransitionsEnabled()) {
+ if (mTransitionController.isShellTransitionsEnabled()) {
return;
}
ProtoLog.i(WM_DEBUG_ORIENTATION,
@@ -6083,13 +6070,13 @@
final Task associatedTask =
mSharedStartingData != null ? mSharedStartingData.mAssociatedTask : null;
if (associatedTask == null) {
- removeStartingWindowIfNeeded();
+ removeStartingWindow();
} else if (associatedTask.getActivity(
r -> r.mVisibleRequested && !r.firstWindowDrawn) == null) {
// The last drawn activity may not be the one that owns the starting window.
final ActivityRecord r = associatedTask.topActivityContainsStartingWindow();
if (r != null) {
- r.removeStartingWindowIfNeeded();
+ r.removeStartingWindow();
}
}
updateReportedVisibilityLocked();
@@ -7603,7 +7590,7 @@
}
boolean isInTransition() {
- return mAtmService.getTransitionController().inTransition() // Shell transitions.
+ return mTransitionController.inTransition() // Shell transitions.
|| isAnimating(PARENTS | TRANSITION); // Legacy transitions.
}
diff --git a/services/core/java/com/android/server/wm/ActivityStartInterceptor.java b/services/core/java/com/android/server/wm/ActivityStartInterceptor.java
index 979cea9..223f0be 100644
--- a/services/core/java/com/android/server/wm/ActivityStartInterceptor.java
+++ b/services/core/java/com/android/server/wm/ActivityStartInterceptor.java
@@ -51,6 +51,7 @@
import android.os.RemoteException;
import android.os.UserHandle;
import android.os.UserManager;
+import android.util.SparseArray;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.app.BlockedAppActivity;
@@ -178,7 +179,33 @@
// before issuing the work challenge.
return true;
}
- return interceptLockedManagedProfileIfNeeded();
+ if (interceptLockedManagedProfileIfNeeded()) {
+ return true;
+ }
+
+ final SparseArray<ActivityInterceptorCallback> callbacks =
+ mService.getActivityInterceptorCallbacks();
+ final ActivityInterceptorCallback.ActivityInterceptorInfo interceptorInfo =
+ new ActivityInterceptorCallback.ActivityInterceptorInfo(mRealCallingUid,
+ mRealCallingPid, mUserId, mCallingPackage, mCallingFeatureId, mIntent,
+ mRInfo, mAInfo, mResolvedType, mCallingPid, mCallingUid,
+ mActivityOptions);
+
+ for (int i = 0; i < callbacks.size(); i++) {
+ final ActivityInterceptorCallback callback = callbacks.valueAt(i);
+ final Intent newIntent = callback.intercept(interceptorInfo);
+ if (newIntent == null) {
+ continue;
+ }
+ mIntent = newIntent;
+ mCallingPid = mRealCallingPid;
+ mCallingUid = mRealCallingUid;
+ mRInfo = mSupervisor.resolveIntent(mIntent, null, mUserId, 0, mRealCallingUid);
+ mAInfo = mSupervisor.resolveActivity(mIntent, mRInfo, mStartFlags,
+ null /*profilerInfo*/);
+ return true;
+ }
+ return false;
}
private boolean hasCrossProfileAnimation() {
diff --git a/services/core/java/com/android/server/wm/ActivityStarter.java b/services/core/java/com/android/server/wm/ActivityStarter.java
index 980ebf0..1707895 100644
--- a/services/core/java/com/android/server/wm/ActivityStarter.java
+++ b/services/core/java/com/android/server/wm/ActivityStarter.java
@@ -1564,14 +1564,15 @@
// startActivityInner. Otherwise, logic in startActivityInner could start a different
// transition based on a sub-action.
// Only do the create here (and defer requestStart) since startActivityInner might abort.
- final Transition newTransition = (!mService.getTransitionController().isCollecting()
- && mService.getTransitionController().getTransitionPlayer() != null)
- ? mService.getTransitionController().createTransition(TRANSIT_OPEN) : null;
+ final TransitionController transitionController = r.mTransitionController;
+ final Transition newTransition = (!transitionController.isCollecting()
+ && transitionController.getTransitionPlayer() != null)
+ ? transitionController.createTransition(TRANSIT_OPEN) : null;
RemoteTransition remoteTransition = r.takeRemoteTransition();
if (newTransition != null && remoteTransition != null) {
newTransition.setRemoteTransition(remoteTransition);
}
- mService.getTransitionController().collect(r);
+ transitionController.collect(r);
final boolean isTransient = r.getOptions() != null && r.getOptions().getTransientLaunch();
try {
mService.deferWindowLayout();
@@ -1618,19 +1619,19 @@
if (started) {
// The activity is started new rather than just brought forward, so record
// it as an existence change.
- mService.getTransitionController().collectExistenceChange(r);
+ transitionController.collectExistenceChange(r);
}
if (isTransient) {
// `r` isn't guaranteed to be the actual relevant activity, so we must wait
// until after we launched to identify the relevant activity.
- mService.getTransitionController().setTransientLaunch(mLastStartActivityRecord);
+ transitionController.setTransientLaunch(mLastStartActivityRecord);
}
if (newTransition != null) {
- mService.getTransitionController().requestStartTransition(newTransition,
+ transitionController.requestStartTransition(newTransition,
mTargetTask, remoteTransition);
} else if (started) {
// Make the collecting transition wait until this request is ready.
- mService.getTransitionController().setReady(r, false);
+ transitionController.setReady(r, false);
}
}
}
@@ -2769,7 +2770,7 @@
mNewTaskInfo != null ? mNewTaskInfo : mStartActivity.info,
mNewTaskIntent != null ? mNewTaskIntent : mIntent, mVoiceSession,
mVoiceInteractor, toTop, mStartActivity, mSourceRecord, mOptions);
- mService.getTransitionController().collectExistenceChange(task);
+ task.mTransitionController.collectExistenceChange(task);
addOrReparentStartingActivity(task, "setTaskFromReuseOrCreateNewTask - mReuseTask");
ProtoLog.v(WM_DEBUG_TASKS, "Starting new activity %s in new task %s",
diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java b/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java
index 9db13ba..3150ccd 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java
@@ -678,4 +678,12 @@
/** Called when the device is waking up */
public abstract void notifyWakingUp();
+
+ /**
+ * Registers a callback which can intercept activity starts.
+ * @throws IllegalArgumentException if duplicate ids are provided
+ */
+ public abstract void registerActivityStartInterceptor(
+ @ActivityInterceptorCallback.OrderedId int id,
+ ActivityInterceptorCallback callback);
}
diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
index 89f7d92..a4f188c 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
@@ -92,6 +92,8 @@
import static com.android.server.am.ActivityManagerServiceDumpProcessesProto.ScreenCompatPackage.PACKAGE;
import static com.android.server.am.EventLogTags.writeBootProgressEnableScreen;
import static com.android.server.am.EventLogTags.writeConfigurationChanged;
+import static com.android.server.wm.ActivityInterceptorCallback.FIRST_ORDERED_ID;
+import static com.android.server.wm.ActivityInterceptorCallback.LAST_ORDERED_ID;
import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_ALL;
import static com.android.server.wm.ActivityTaskManagerDebugConfig.POSTFIX_ROOT_TASK;
import static com.android.server.wm.ActivityTaskManagerDebugConfig.POSTFIX_SWITCH;
@@ -458,6 +460,8 @@
/** The controller for all operations related to locktask. */
private LockTaskController mLockTaskController;
private ActivityStartController mActivityStartController;
+ private SparseArray<ActivityInterceptorCallback> mActivityInterceptorCallbacks =
+ new SparseArray<>();
PackageConfigPersister mPackageConfigPersister;
boolean mSuppressResizeConfigChanges;
@@ -1115,6 +1119,10 @@
return mBackgroundActivityStartCallback;
}
+ SparseArray<ActivityInterceptorCallback> getActivityInterceptorCallbacks() {
+ return mActivityInterceptorCallbacks;
+ }
+
private void start() {
LocalServices.addService(ActivityTaskManagerInternal.class, mInternal);
}
@@ -3415,9 +3423,15 @@
final List<RemoteAction> actions = r.pictureInPictureArgs.getActions();
mRootWindowContainer.moveActivityToPinnedRootTask(
r, "enterPictureInPictureMode");
- final Task rootTask = r.getRootTask();
- rootTask.setPictureInPictureAspectRatio(aspectRatio);
- rootTask.setPictureInPictureActions(actions);
+ final Task task = r.getTask();
+ task.setPictureInPictureAspectRatio(aspectRatio);
+ task.setPictureInPictureActions(actions);
+
+ // Continue the pausing process after entering pip.
+ if (task.getPausingActivity() == r) {
+ task.schedulePauseActivity(r, false /* userLeaving */,
+ false /* pauseImmediately */, "auto-pip");
+ }
}
};
@@ -4738,7 +4752,7 @@
mContext.getText(R.string.heavy_weight_notification_detail))
.setContentIntent(PendingIntent.getActivityAsUser(mContext, 0,
intent, PendingIntent.FLAG_CANCEL_CURRENT
- | PendingIntent.FLAG_IMMUTABLE, null,
+ | PendingIntent.FLAG_IMMUTABLE, null,
new UserHandle(userId)))
.build();
try {
@@ -6583,5 +6597,22 @@
getTransitionController().requestTransitionIfNeeded(TRANSIT_WAKE, 0 /* flags */,
null /* trigger */, mRootWindowContainer.getDefaultDisplay());
}
+
+ @Override
+ public void registerActivityStartInterceptor(
+ @ActivityInterceptorCallback.OrderedId int id,
+ ActivityInterceptorCallback callback) {
+ synchronized (mGlobalLock) {
+ if (mActivityInterceptorCallbacks.contains(id)) {
+ throw new IllegalArgumentException("Duplicate id provided: " + id);
+ }
+ if (id > LAST_ORDERED_ID || id < FIRST_ORDERED_ID) {
+ throw new IllegalArgumentException(
+ "Provided id " + id + " is not in range of valid ids ["
+ + FIRST_ORDERED_ID + "," + LAST_ORDERED_ID + "]");
+ }
+ mActivityInterceptorCallbacks.put(id, callback);
+ }
+ }
}
}
diff --git a/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java b/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java
index 11936b2..ba30592 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java
@@ -853,10 +853,6 @@
proc.getThread(), r.appToken);
final boolean isTransitionForward = r.isTransitionForward();
- IBinder fragmentToken = null;
- if (r.getTaskFragment().getTaskFragmentOrganizerPid() == r.getPid()) {
- fragmentToken = r.getTaskFragment().getFragmentToken();
- }
clientTransaction.addCallback(LaunchActivityItem.obtain(new Intent(r.intent),
System.identityHashCode(r), r.info,
// TODO: Have this take the merged configuration instead of separate global
@@ -868,7 +864,7 @@
results, newIntents, r.takeOptions(), isTransitionForward,
proc.createProfilerInfoIfNeeded(), r.assistToken, activityClientController,
r.createFixedRotationAdjustmentsIfNeeded(), r.shareableActivityToken,
- r.getLaunchedFromBubble(), fragmentToken));
+ r.getLaunchedFromBubble()));
// Set desired final state.
final ActivityLifecycleItem lifecycleItem;
@@ -1391,7 +1387,7 @@
mUserLeaving = true;
}
- mService.getTransitionController().requestTransitionIfNeeded(TRANSIT_TO_FRONT,
+ task.mTransitionController.requestTransitionIfNeeded(TRANSIT_TO_FRONT,
0 /* flags */, task, task /* readyGroupRef */,
options != null ? options.getRemoteTransition() : null);
reason = reason + " findTaskToMoveToFront";
@@ -1567,17 +1563,17 @@
return;
}
if (task.isVisible()) {
- if (mService.getTransitionController().isCollecting()) {
+ if (task.mTransitionController.isCollecting()) {
// We don't want the finishing to change the transition ready state since there will
// not be corresponding setReady for finishing.
- mService.getTransitionController().collectExistenceChange(task);
+ task.mTransitionController.collectExistenceChange(task);
} else {
- mService.getTransitionController().requestTransitionIfNeeded(TRANSIT_CLOSE, task);
+ task.mTransitionController.requestTransitionIfNeeded(TRANSIT_CLOSE, task);
}
} else {
// Removing a non-visible task doesn't require a transition, but if there is one
// collecting, this should be a member just in case.
- mService.getTransitionController().collect(task);
+ task.mTransitionController.collect(task);
}
task.mInRemoveTask = true;
try {
@@ -1891,7 +1887,7 @@
final ActivityRecord s = mStoppingActivities.get(i);
final boolean animating = s.isAnimating(TRANSITION | PARENTS,
ANIMATION_TYPE_APP_TRANSITION | ANIMATION_TYPE_RECENTS)
- || mService.getTransitionController().inTransition(s);
+ || s.inTransition();
ProtoLog.v(WM_DEBUG_STATES, "Stopping %s: nowVisible=%b animating=%b "
+ "finishing=%s", s, s.nowVisible, animating, s.finishing);
if (!animating || mService.mShuttingDown) {
@@ -2192,7 +2188,7 @@
}
if (!task.supportsSplitScreenWindowingMode() || forceNonResizable) {
- if (mService.getTransitionController().getTransitionPlayer() != null) return;
+ if (task.mTransitionController.isShellTransitionsEnabled()) return;
// Dismiss docked root task. If task appeared to be in docked root task but is not
// resizable - we need to move it to top of fullscreen root task, otherwise it will
// be covered.
diff --git a/services/core/java/com/android/server/wm/AppTransition.java b/services/core/java/com/android/server/wm/AppTransition.java
index 929ac56f..e21a00b 100644
--- a/services/core/java/com/android/server/wm/AppTransition.java
+++ b/services/core/java/com/android/server/wm/AppTransition.java
@@ -1547,7 +1547,7 @@
}
boolean prepareAppTransition(@TransitionType int transit, @TransitionFlags int flags) {
- if (mService.mAtmService.getTransitionController().getTransitionPlayer() != null) {
+ if (mDisplayContent.mTransitionController.isShellTransitionsEnabled()) {
return false;
}
mNextAppTransitionRequests.add(transit);
diff --git a/services/core/java/com/android/server/wm/AppTransitionController.java b/services/core/java/com/android/server/wm/AppTransitionController.java
index 10ed8dc..ffaf710 100644
--- a/services/core/java/com/android/server/wm/AppTransitionController.java
+++ b/services/core/java/com/android/server/wm/AppTransitionController.java
@@ -343,9 +343,6 @@
switch (changingType) {
case TYPE_TASK:
return TRANSIT_OLD_TASK_CHANGE_WINDOWING_MODE;
- case TYPE_ACTIVITY:
- // ActivityRecord is put in a change transition only when it is reparented
- // to an organized TaskFragment. See ActivityRecord#shouldStartChangeTransition.
case TYPE_TASK_FRAGMENT:
return TRANSIT_OLD_TASK_FRAGMENT_CHANGE;
default:
@@ -907,7 +904,7 @@
final AccessibilityController accessibilityController =
mDisplayContent.mWmService.mAccessibilityController;
- if (accessibilityController != null) {
+ if (accessibilityController.hasCallbacks()) {
accessibilityController.onAppWindowTransition(mDisplayContent.getDisplayId(), transit);
}
}
@@ -1111,6 +1108,11 @@
// the same transition.
for (int i = rootTasks.size() - 1; i >= 0; i--) {
final Task rootTask = rootTasks.valueAt(i);
+ if (rootTask == null) {
+ // It is possible that one activity may have been removed from the hierarchy. No
+ // need to check for this case.
+ continue;
+ }
final boolean notReady = rootTask.forAllLeafTaskFragments(taskFragment -> {
if (!taskFragment.isReadyToTransit()) {
ProtoLog.v(WM_DEBUG_APP_TRANSITIONS, "Organized TaskFragment is not ready= %s",
diff --git a/services/core/java/com/android/server/wm/ConfigurationContainer.java b/services/core/java/com/android/server/wm/ConfigurationContainer.java
index eeb85c5..5a2cf17 100644
--- a/services/core/java/com/android/server/wm/ConfigurationContainer.java
+++ b/services/core/java/com/android/server/wm/ConfigurationContainer.java
@@ -28,6 +28,8 @@
import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_SECONDARY;
import static android.app.WindowConfiguration.activityTypeToString;
import static android.app.WindowConfiguration.windowingModeToString;
+import static android.app.WindowConfigurationProto.WINDOWING_MODE;
+import static android.content.ConfigurationProto.WINDOW_CONFIGURATION;
import static com.android.server.wm.ConfigurationContainerProto.FULL_CONFIGURATION;
import static com.android.server.wm.ConfigurationContainerProto.MERGED_OVERRIDE_CONFIGURATION;
@@ -695,22 +697,40 @@
@CallSuper
protected void dumpDebug(ProtoOutputStream proto, long fieldId,
@WindowTraceLogLevel int logLevel) {
- // Critical log level logs only visible elements to mitigate performance overheard
- if (logLevel != WindowTraceLogLevel.ALL && !mHasOverrideConfiguration) {
- return;
+ final long token = proto.start(fieldId);
+
+ if (logLevel == WindowTraceLogLevel.ALL || mHasOverrideConfiguration) {
+ mRequestedOverrideConfiguration.dumpDebug(proto, OVERRIDE_CONFIGURATION,
+ logLevel == WindowTraceLogLevel.CRITICAL);
}
- final long token = proto.start(fieldId);
- mRequestedOverrideConfiguration.dumpDebug(proto, OVERRIDE_CONFIGURATION,
- logLevel == WindowTraceLogLevel.CRITICAL);
+ // Unless trace level is set to `WindowTraceLogLevel.ALL` don't dump anything that isn't
+ // required to mitigate performance overhead
if (logLevel == WindowTraceLogLevel.ALL) {
mFullConfiguration.dumpDebug(proto, FULL_CONFIGURATION, false /* critical */);
mMergedOverrideConfiguration.dumpDebug(proto, MERGED_OVERRIDE_CONFIGURATION,
false /* critical */);
}
+
+ if (logLevel == WindowTraceLogLevel.TRIM) {
+ // Required for Fass to automatically detect pip transitions in Winscope traces
+ dumpDebugWindowingMode(proto);
+ }
+
proto.end(token);
}
+ private void dumpDebugWindowingMode(ProtoOutputStream proto) {
+ final long fullConfigToken = proto.start(FULL_CONFIGURATION);
+ final long windowConfigToken = proto.start(WINDOW_CONFIGURATION);
+
+ int windowingMode = mFullConfiguration.windowConfiguration.getWindowingMode();
+ proto.write(WINDOWING_MODE, windowingMode);
+
+ proto.end(windowConfigToken);
+ proto.end(fullConfigToken);
+ }
+
/**
* Dumps the names of this container children in the input print writer indenting each
* level with the input prefix.
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index f800f0e..46a4f8a 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -1064,7 +1064,7 @@
mAppTransition = new AppTransition(mWmService.mContext, mWmService, this);
mAppTransition.registerListenerLocked(mWmService.mActivityManagerAppTransitionNotifier);
- mAtmService.getTransitionController().registerLegacyListener(
+ mTransitionController.registerLegacyListener(
mWmService.mActivityManagerAppTransitionNotifier);
mAppTransition.registerListenerLocked(mFixedRotationTransitionListener);
mAppTransitionController = new AppTransitionController(mWmService, this);
@@ -1284,7 +1284,7 @@
addWindowToken(token.token, token);
- if (mWmService.mAccessibilityController != null) {
+ if (mWmService.mAccessibilityController.hasCallbacks()) {
final int prevDisplayId = prevDc != null ? prevDc.getDisplayId() : INVALID_DISPLAY;
mWmService.mAccessibilityController.onSomeWindowResizedOrMoved(prevDisplayId,
getDisplayId());
@@ -1395,7 +1395,7 @@
if (configChanged) {
mWaitingForConfig = true;
- if (mAtmService.getTransitionController().isShellTransitionsEnabled()) {
+ if (mTransitionController.isShellTransitionsEnabled()) {
requestChangeTransitionIfNeeded(changes);
} else {
mWmService.startFreezingDisplay(0 /* exitAnim */, 0 /* enterAnim */, this);
@@ -1515,7 +1515,7 @@
} else if (currentConfig != null
// If waiting for a remote rotation, don't prematurely update configuration.
&& !(mDisplayRotation.isWaitingForRemoteRotation()
- || mAtmService.getTransitionController().isCollecting(this))) {
+ || mTransitionController.isCollecting(this))) {
// No obvious action we need to take, but if our current state mismatches the
// activity manager's, update it, disregarding font scale, which should remain set
// to the value of the previous configuration.
@@ -1666,7 +1666,7 @@
}
/** Returns {@code true} if the IME is possible to show on the launching activity. */
- private boolean mayImeShowOnLaunchingActivity(@NonNull ActivityRecord r) {
+ boolean mayImeShowOnLaunchingActivity(@NonNull ActivityRecord r) {
final WindowState win = r.findMainWindow();
if (win == null) {
return false;
@@ -1914,8 +1914,7 @@
*/
private void applyRotation(final int oldRotation, final int rotation) {
mDisplayRotation.applyCurrentRotation(rotation);
- final boolean shellTransitions =
- mWmService.mAtmService.getTransitionController().getTransitionPlayer() != null;
+ final boolean shellTransitions = mTransitionController.getTransitionPlayer() != null;
final boolean rotateSeamlessly =
mDisplayRotation.isRotatingSeamlessly() && !shellTransitions;
final Transaction transaction =
@@ -3070,7 +3069,7 @@
if (isAnimating(TRANSITION | PARENTS)
// isAnimating is a legacy transition query and will be removed, so also add a
// check for whether this is in a shell-transition when not using legacy.
- || mAtmService.getTransitionController().inTransition()) {
+ || mTransitionController.inTransition()) {
mDeferredRemoval = true;
return;
}
@@ -3176,7 +3175,7 @@
* be non-zero. This method is no-op if the display has been collected.
*/
void requestChangeTransitionIfNeeded(@ActivityInfo.Config int changes) {
- final TransitionController controller = mAtmService.getTransitionController();
+ final TransitionController controller = mTransitionController;
if (controller.isCollecting()) {
if (!controller.isCollecting(this)) {
controller.collect(this);
@@ -3215,8 +3214,8 @@
screenRotationAnimation.dumpDebug(proto, SCREEN_ROTATION_ANIMATION);
}
mDisplayFrames.dumpDebug(proto, DISPLAY_FRAMES);
- if (mAtmService.getTransitionController().isShellTransitionsEnabled()) {
- mAtmService.getTransitionController().dumpDebugLegacy(proto, APP_TRANSITION);
+ if (mTransitionController.isShellTransitionsEnabled()) {
+ mTransitionController.dumpDebugLegacy(proto, APP_TRANSITION);
} else {
mAppTransition.dumpDebug(proto, APP_TRANSITION);
}
@@ -3570,7 +3569,7 @@
// focused one starts firing events.
// TODO(b/151179149) investigate what info accessibility service needs before input can
// dispatch focus to clients.
- if (mWmService.mAccessibilityController != null) {
+ if (mWmService.mAccessibilityController.hasCallbacks()) {
mWmService.mH.sendMessage(PooledLambda.obtainMessage(
this::updateAccessibilityOnWindowFocusChanged,
mWmService.mAccessibilityController));
@@ -3805,7 +3804,11 @@
}
boolean shouldImeAttachedToApp() {
- return isImeControlledByApp()
+ // Force attaching IME to the display when magnifying, or it would be magnified with
+ // target app together.
+ final boolean allowAttachToApp = (mMagnificationSpec == null);
+
+ return allowAttachToApp && isImeControlledByApp()
&& mImeLayeringTarget != null
&& mImeLayeringTarget.mActivityRecord != null
&& mImeLayeringTarget.getWindowingMode() == WINDOWING_MODE_FULLSCREEN
@@ -4144,14 +4147,10 @@
*/
@VisibleForTesting
SurfaceControl computeImeParent() {
- // Force attaching IME to the display when magnifying, or it would be magnified with
- // target app together.
- final boolean allowAttachToApp = (mMagnificationSpec == null);
-
// Attach it to app if the target is part of an app and such app is covering the entire
// screen. If it's not covering the entire screen the IME might extend beyond the apps
// bounds.
- if (allowAttachToApp && shouldImeAttachedToApp()) {
+ if (shouldImeAttachedToApp()) {
if (mImeLayeringTarget.mActivityRecord != mImeInputTarget.mActivityRecord) {
// Do not change parent if the window hasn't requested IME.
return null;
@@ -5109,7 +5108,7 @@
void requestTransitionAndLegacyPrepare(@WindowManager.TransitionType int transit,
@WindowManager.TransitionFlags int flags) {
prepareAppTransition(transit, flags);
- mAtmService.getTransitionController().requestTransitionIfNeeded(transit, flags,
+ mTransitionController.requestTransitionIfNeeded(transit, flags,
null /* trigger */, this);
}
@@ -5117,12 +5116,12 @@
void requestTransitionAndLegacyPrepare(@WindowManager.TransitionType int transit,
@Nullable WindowContainer trigger) {
prepareAppTransition(transit);
- mAtmService.getTransitionController().requestTransitionIfNeeded(transit, 0 /* flags */,
+ mTransitionController.requestTransitionIfNeeded(transit, 0 /* flags */,
trigger, this);
}
void executeAppTransition() {
- mAtmService.getTransitionController().setReady(this);
+ mTransitionController.setReady(this);
if (mAppTransition.isTransitionSet()) {
ProtoLog.w(WM_DEBUG_APP_TRANSITIONS,
"Execute app transition: %s, displayId: %d Callers=%s",
@@ -5171,9 +5170,9 @@
/** Check if pending app transition is for activity / task launch. */
boolean isNextTransitionForward() {
// TODO(b/191375840): decouple "forwardness" from transition system.
- if (mAtmService.getTransitionController().isShellTransitionsEnabled()) {
+ if (mTransitionController.isShellTransitionsEnabled()) {
@WindowManager.TransitionType int type =
- mAtmService.getTransitionController().getCollectingTransitionType();
+ mTransitionController.getCollectingTransitionType();
return type == TRANSIT_OPEN || type == TRANSIT_TO_FRONT;
}
return mAppTransition.containsTransitRequest(TRANSIT_OPEN)
@@ -5217,7 +5216,7 @@
}
if (!mLocationInParentWindow.equals(x, y)) {
mLocationInParentWindow.set(x, y);
- if (mWmService.mAccessibilityController != null) {
+ if (mWmService.mAccessibilityController.hasCallbacks()) {
mWmService.mAccessibilityController.onSomeWindowResizedOrMoved(mDisplayId);
}
notifyLocationInParentDisplayChanged();
@@ -5748,7 +5747,7 @@
}
mWmService.mDisplayNotificationController.dispatchDisplayChanged(
this, getConfiguration());
- if (isReady() && mAtmService.getTransitionController().isShellTransitionsEnabled()) {
+ if (isReady() && mTransitionController.isShellTransitionsEnabled()) {
requestChangeTransitionIfNeeded(changes);
}
}
@@ -5775,7 +5774,7 @@
@Override
void onResize() {
super.onResize();
- if (mWmService.mAccessibilityController != null) {
+ if (mWmService.mAccessibilityController.hasCallbacks()) {
mWmService.mAccessibilityController.onDisplaySizeChanged(this);
}
}
diff --git a/services/core/java/com/android/server/wm/DisplayPolicy.java b/services/core/java/com/android/server/wm/DisplayPolicy.java
index 3dff680..ccfb174 100644
--- a/services/core/java/com/android/server/wm/DisplayPolicy.java
+++ b/services/core/java/com/android/server/wm/DisplayPolicy.java
@@ -641,8 +641,7 @@
}
};
displayContent.mAppTransition.registerListenerLocked(mAppTransitionListener);
- mService.mAtmService.getTransitionController().registerLegacyListener(
- mAppTransitionListener);
+ displayContent.mTransitionController.registerLegacyListener(mAppTransitionListener);
mImmersiveModeConfirmation = new ImmersiveModeConfirmation(mContext, looper,
mService.mVrModeEnabled);
@@ -3145,6 +3144,7 @@
pw.print(" mAllowLockscreenWhenOn="); pw.println(mAllowLockscreenWhenOn);
pw.print(prefix); pw.print("mRemoteInsetsControllerControlsSystemBars=");
pw.println(mDisplayContent.getInsetsPolicy().getRemoteInsetsControllerControlsSystemBars());
+ mSystemGestures.dump(pw, prefix);
pw.print(prefix); pw.println("Looper state:");
mHandler.getLooper().dump(new PrintWriterPrinter(pw), prefix + " ");
diff --git a/services/core/java/com/android/server/wm/DisplayRotation.java b/services/core/java/com/android/server/wm/DisplayRotation.java
index 225a6ea..34e8149 100644
--- a/services/core/java/com/android/server/wm/DisplayRotation.java
+++ b/services/core/java/com/android/server/wm/DisplayRotation.java
@@ -413,7 +413,7 @@
*/
boolean updateRotationUnchecked(boolean forceUpdate) {
final boolean useShellTransitions =
- mService.mAtmService.getTransitionController().getTransitionPlayer() != null;
+ mDisplayContent.mTransitionController.isShellTransitionsEnabled();
final int displayId = mDisplayContent.getDisplayId();
if (!forceUpdate && !useShellTransitions) {
@@ -586,17 +586,17 @@
mService.mH.removeCallbacks(mDisplayRotationHandlerTimeout);
mIsWaitingForRemoteRotation = false;
- if (mService.mAtmService.getTransitionController().getTransitionPlayer() != null) {
- if (!mService.mAtmService.getTransitionController().isCollecting()) {
+ if (mDisplayContent.mTransitionController.isShellTransitionsEnabled()) {
+ if (!mDisplayContent.mTransitionController.isCollecting()) {
throw new IllegalStateException("Trying to rotate outside a transition");
}
- mService.mAtmService.getTransitionController().collect(mDisplayContent);
+ mDisplayContent.mTransitionController.collect(mDisplayContent);
// Go through all tasks and collect them before the rotation
// TODO(shell-transitions): move collect() to onConfigurationChange once wallpaper
// handling is synchronized.
mDisplayContent.forAllTasks(task -> {
if (task.isVisible()) {
- mService.mAtmService.getTransitionController().collect(task);
+ mDisplayContent.mTransitionController.collect(task);
}
});
mDisplayContent.getInsetsStateController().addProvidersToTransition();
diff --git a/services/core/java/com/android/server/wm/DockedTaskDividerController.java b/services/core/java/com/android/server/wm/DockedTaskDividerController.java
index fb9d064..925a6d8 100644
--- a/services/core/java/com/android/server/wm/DockedTaskDividerController.java
+++ b/services/core/java/com/android/server/wm/DockedTaskDividerController.java
@@ -46,7 +46,7 @@
void setTouchRegion(Rect touchRegion) {
mTouchRegion.set(touchRegion);
// We need to report touchable region changes to accessibility.
- if (mDisplayContent.mWmService.mAccessibilityController != null) {
+ if (mDisplayContent.mWmService.mAccessibilityController.hasCallbacks()) {
mDisplayContent.mWmService.mAccessibilityController.onSomeWindowResizedOrMoved(
mDisplayContent.getDisplayId());
}
diff --git a/services/core/java/com/android/server/wm/EnsureActivitiesVisibleHelper.java b/services/core/java/com/android/server/wm/EnsureActivitiesVisibleHelper.java
index 6560d15..cddb1e7 100644
--- a/services/core/java/com/android/server/wm/EnsureActivitiesVisibleHelper.java
+++ b/services/core/java/com/android/server/wm/EnsureActivitiesVisibleHelper.java
@@ -135,7 +135,7 @@
setActivityVisibilityState(child.asActivityRecord(), starting, resumeTopActivity);
}
}
- if (mTaskFragment.mAtmService.getTransitionController().getTransitionPlayer() != null) {
+ if (mTaskFragment.mTransitionController.isShellTransitionsEnabled()) {
mTaskFragment.getDisplayContent().mWallpaperController.adjustWallpaperWindows();
}
}
diff --git a/services/core/java/com/android/server/wm/InsetsStateController.java b/services/core/java/com/android/server/wm/InsetsStateController.java
index 2c4adcb..c4ca8e3 100644
--- a/services/core/java/com/android/server/wm/InsetsStateController.java
+++ b/services/core/java/com/android/server/wm/InsetsStateController.java
@@ -254,7 +254,7 @@
if (p == null) continue;
final WindowContainer wc = p.mWin;
if (wc == null) continue;
- mDisplayContent.mAtmService.getTransitionController().collect(wc);
+ mDisplayContent.mTransitionController.collect(wc);
}
}
diff --git a/services/core/java/com/android/server/wm/Letterbox.java b/services/core/java/com/android/server/wm/Letterbox.java
index 45411a9..4b98013 100644
--- a/services/core/java/com/android/server/wm/Letterbox.java
+++ b/services/core/java/com/android/server/wm/Letterbox.java
@@ -36,6 +36,7 @@
import com.android.server.UiThread;
+import java.util.function.IntConsumer;
import java.util.function.Supplier;
/**
@@ -70,7 +71,7 @@
private final LetterboxSurface mFullWindowSurface = new LetterboxSurface("fullWindow");
private final LetterboxSurface[] mSurfaces = { mLeft, mTop, mRight, mBottom };
// Reachability gestures.
- private final Runnable mDoubleTapCallback;
+ private final IntConsumer mDoubleTapCallback;
/**
* Constructs a Letterbox.
@@ -84,7 +85,7 @@
Supplier<Boolean> hasWallpaperBackgroundSupplier,
Supplier<Integer> blurRadiusSupplier,
Supplier<Float> darkScrimAlphaSupplier,
- Runnable doubleTapCallback) {
+ IntConsumer doubleTapCallback) {
mSurfaceControlFactory = surfaceControlFactory;
mTransactionFactory = transactionFactory;
mAreCornersRounded = areCornersRounded;
@@ -262,7 +263,7 @@
@Override
public boolean onDoubleTapEvent(MotionEvent e) {
if (e.getAction() == MotionEvent.ACTION_UP) {
- mDoubleTapCallback.run();
+ mDoubleTapCallback.accept((int) e.getX());
return true;
}
return false;
diff --git a/services/core/java/com/android/server/wm/LetterboxConfiguration.java b/services/core/java/com/android/server/wm/LetterboxConfiguration.java
index 72fbfcc..cbb473c 100644
--- a/services/core/java/com/android/server/wm/LetterboxConfiguration.java
+++ b/services/core/java/com/android/server/wm/LetterboxConfiguration.java
@@ -54,6 +54,27 @@
/** Using wallpaper as a background which can be blurred or dimmed with dark scrim. */
static final int LETTERBOX_BACKGROUND_WALLPAPER = 3;
+ /**
+ * Enum for Letterbox reachability position types.
+ *
+ * <p>Order from left to right is important since it's used in {@link
+ * #movePositionForReachabilityToNextRightStop} and {@link
+ * #movePositionForReachabilityToNextLeftStop}.
+ */
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef({LETTERBOX_REACHABILITY_POSITION_LEFT, LETTERBOX_REACHABILITY_POSITION_CENTER,
+ LETTERBOX_REACHABILITY_POSITION_RIGHT})
+ @interface LetterboxReachabilityPosition {};
+
+ /** Letterboxed app window is aligned to the left side. */
+ static final int LETTERBOX_REACHABILITY_POSITION_LEFT = 0;
+
+ /** Letterboxed app window is positioned in the horizontal center. */
+ static final int LETTERBOX_REACHABILITY_POSITION_CENTER = 1;
+
+ /** Letterboxed app window is aligned to the right side. */
+ static final int LETTERBOX_REACHABILITY_POSITION_RIGHT = 2;
+
final Context mContext;
// Aspect ratio of letterbox for fixed orientation, values <=
@@ -85,25 +106,25 @@
// side of the screen and 1.0 to the right side.
private float mLetterboxHorizontalPositionMultiplier;
- // Default horizontal position of a center of the letterboxed app window when reachability is
- // enabled and an app is fullscreen in landscape device orientatio. 0 corresponds to the left
- // side of the screen and 1.0 to the right side.
- // It is used as a starting point for mLetterboxHorizontalMultiplierForReachability.
- private float mDefaultPositionMultiplierForReachability;
+ // Default horizontal position the letterboxed app window when reachability is enabled and
+ // an app is fullscreen in landscape device orientatio.
+ // It is used as a starting point for mLetterboxPositionForReachability.
+ @LetterboxReachabilityPosition
+ private int mDefaultPositionForReachability;
// Whether reachability repositioning is allowed for letterboxed fullscreen apps in landscape
// device orientation.
private boolean mIsReachabilityEnabled;
- // Horizontal position of a center of the letterboxed app window. 0 corresponds to
- // the left side of the screen and 1 to the right side. Keep it global to prevent
- // "jumps" when switching between letterboxed apps. It's updated to reposition the app
- // window in response to a double tap gesture (see LetterboxUiController#handleDoubleTap).
- // Used in LetterboxUiController#getHorizontalPositionMultiplier which is called from
+ // Horizontal position of a center of the letterboxed app window which is global to prevent
+ // "jumps" when switching between letterboxed apps. It's updated to reposition the app window
+ // in response to a double tap gesture (see LetterboxUiController#handleDoubleTap). Used in
+ // LetterboxUiController#getHorizontalPositionMultiplier which is called from
// ActivityRecord#updateResolvedBoundsHorizontalPosition.
// TODO(b/199426138): Global reachability setting causes a jump when resuming an app from
// Overview after changing position in another app.
- private volatile float mLetterboxHorizontalMultiplierForReachability;
+ @LetterboxReachabilityPosition
+ private volatile int mLetterboxPositionForReachability;
LetterboxConfiguration(Context systemUiContext) {
mContext = systemUiContext;
@@ -120,9 +141,8 @@
R.dimen.config_letterboxHorizontalPositionMultiplier);
mIsReachabilityEnabled = mContext.getResources().getBoolean(
R.bool.config_letterboxIsReachabilityEnabled);
- mDefaultPositionMultiplierForReachability = mContext.getResources().getFloat(
- R.dimen.config_letterboxDefaultPositionMultiplierForReachability);
- mLetterboxHorizontalMultiplierForReachability = mDefaultPositionMultiplierForReachability;
+ mDefaultPositionForReachability = readLetterboxReachabilityPositionFromConfig(mContext);
+ mLetterboxPositionForReachability = mDefaultPositionForReachability;
}
/**
@@ -395,58 +415,90 @@
}
/*
- * Gets default horizontal position of a center of the letterboxed app window when reachability
- * is enabled specified in {@link
- * R.dimen.config_letterboxDefaultPositionMultiplierForReachability} or via an ADB command.
- * 0 corresponds to the left side of the screen and 1 to the right side. The returned value is
- * >= 0.0 and <= 1.0.
+ * Gets default horizontal position of the letterboxed app window when reachability is enabled.
+ * Specified in {@link R.integer.config_letterboxDefaultPositionForReachability} or via an ADB
+ * command.
*/
- float getDefaultPositionMultiplierForReachability() {
- return (mDefaultPositionMultiplierForReachability < 0.0f
- || mDefaultPositionMultiplierForReachability > 1.0f)
- // Default to a right position if invalid value is provided.
- ? 1.0f : mDefaultPositionMultiplierForReachability;
+ @LetterboxReachabilityPosition
+ int getDefaultPositionForReachability() {
+ return mDefaultPositionForReachability;
}
/**
- * Overrides default horizontal position of a center of the letterboxed app window when
- * reachability is enabled. If given value < 0.0 or > 1.0, then it and a value of {@link
- * R.dimen.config_letterboxDefaultPositionMultiplierForReachability} are ignored and the right
- * position (1.0) is used.
+ * Overrides default horizonal position of the letterboxed app window when reachability
+ * is enabled.
*/
- void setDefaultPositionMultiplierForReachability(float multiplier) {
- mDefaultPositionMultiplierForReachability = multiplier;
+ void setDefaultPositionForReachability(@LetterboxReachabilityPosition int position) {
+ mDefaultPositionForReachability = position;
}
/**
- * Resets default horizontal position of a center of the letterboxed app window when
- * reachability is enabled to {@link
- * R.dimen.config_letterboxDefaultPositionMultiplierForReachability}.
+ * Resets default horizontal position of the letterboxed app window when reachability is
+ * enabled to {@link R.integer.config_letterboxDefaultPositionForReachability}.
*/
- void resetDefaultPositionMultiplierForReachability() {
- mDefaultPositionMultiplierForReachability = mContext.getResources().getFloat(
- R.dimen.config_letterboxDefaultPositionMultiplierForReachability);
+ void resetDefaultPositionForReachability() {
+ mDefaultPositionForReachability = readLetterboxReachabilityPositionFromConfig(mContext);
+ }
+
+ @LetterboxReachabilityPosition
+ private static int readLetterboxReachabilityPositionFromConfig(Context context) {
+ int position = context.getResources().getInteger(
+ R.integer.config_letterboxDefaultPositionForReachability);
+ return position == LETTERBOX_REACHABILITY_POSITION_LEFT
+ || position == LETTERBOX_REACHABILITY_POSITION_CENTER
+ || position == LETTERBOX_REACHABILITY_POSITION_RIGHT
+ ? position : LETTERBOX_REACHABILITY_POSITION_CENTER;
}
/*
* Gets horizontal position of a center of the letterboxed app window when reachability
* is enabled specified. 0 corresponds to the left side of the screen and 1 to the right side.
*
- * <p>The position multiplier is changed to a symmetrical value computed as (1 - current
- * multiplier) after each double tap in the letterbox area.
+ * <p>The position multiplier is changed after each double tap in the letterbox area.
*/
float getHorizontalMultiplierForReachability() {
- return mLetterboxHorizontalMultiplierForReachability;
+ switch (mLetterboxPositionForReachability) {
+ case LETTERBOX_REACHABILITY_POSITION_LEFT:
+ return 0.0f;
+ case LETTERBOX_REACHABILITY_POSITION_CENTER:
+ return 0.5f;
+ case LETTERBOX_REACHABILITY_POSITION_RIGHT:
+ return 1.0f;
+ default:
+ throw new AssertionError(
+ "Unexpected letterbox position type: " + mLetterboxPositionForReachability);
+ }
+ }
+
+ /** Returns a string representing the given {@link LetterboxReachabilityPosition}. */
+ static String letterboxReachabilityPositionToString(
+ @LetterboxReachabilityPosition int position) {
+ switch (position) {
+ case LETTERBOX_REACHABILITY_POSITION_LEFT:
+ return "LETTERBOX_REACHABILITY_POSITION_LEFT";
+ case LETTERBOX_REACHABILITY_POSITION_CENTER:
+ return "LETTERBOX_REACHABILITY_POSITION_CENTER";
+ case LETTERBOX_REACHABILITY_POSITION_RIGHT:
+ return "LETTERBOX_REACHABILITY_POSITION_RIGHT";
+ default:
+ throw new AssertionError(
+ "Unexpected letterbox position type: " + position);
+ }
}
/**
- * Changes horizontal position of a center of the letterboxed app window to the opposite
- * (1 - current multiplier) when reachability is enabled specified. 0 corresponds to the left
- * side of the screen and 1 to the right side.
+ * Changes letterbox position for reachability to the next available one on the right side.
*/
- void flipHorizontalMultiplierForReachability() {
- mLetterboxHorizontalMultiplierForReachability =
- 1.0f - mLetterboxHorizontalMultiplierForReachability;
+ void movePositionForReachabilityToNextRightStop() {
+ mLetterboxPositionForReachability = Math.min(
+ mLetterboxPositionForReachability + 1, LETTERBOX_REACHABILITY_POSITION_RIGHT);
+ }
+
+ /**
+ * Changes letterbox position for reachability to the next available one on the left side.
+ */
+ void movePositionForReachabilityToNextLeftStop() {
+ mLetterboxPositionForReachability = Math.max(mLetterboxPositionForReachability - 1, 0);
}
}
diff --git a/services/core/java/com/android/server/wm/LetterboxUiController.java b/services/core/java/com/android/server/wm/LetterboxUiController.java
index c712c04..7d07357 100644
--- a/services/core/java/com/android/server/wm/LetterboxUiController.java
+++ b/services/core/java/com/android/server/wm/LetterboxUiController.java
@@ -210,12 +210,23 @@
return mActivityRecord.mWmService.mContext.getResources();
}
- private void handleDoubleTap() {
+ private void handleDoubleTap(int x) {
if (!isReachabilityEnabled() || mActivityRecord.isInTransition()) {
return;
}
- mLetterboxConfiguration.flipHorizontalMultiplierForReachability();
+ if (mLetterbox.getInnerFrame().left <= x && mLetterbox.getInnerFrame().right >= x) {
+ // Only react to clicks at the sides of the letterboxed app window.
+ return;
+ }
+
+ if (mLetterbox.getInnerFrame().left > x) {
+ // Moving to the next stop on the left side of the app window: right > center > left.
+ mLetterboxConfiguration.movePositionForReachabilityToNextLeftStop();
+ } else if (mLetterbox.getInnerFrame().right < x) {
+ // Moving to the next stop on the right side of the app window: left > center > right.
+ mLetterboxConfiguration.movePositionForReachabilityToNextRightStop();
+ }
// TODO(197549949): Add animation for transition.
mActivityRecord.recomputeConfiguration();
diff --git a/services/core/java/com/android/server/wm/PinnedTaskController.java b/services/core/java/com/android/server/wm/PinnedTaskController.java
index 6014a87..b4963c5 100644
--- a/services/core/java/com/android/server/wm/PinnedTaskController.java
+++ b/services/core/java/com/android/server/wm/PinnedTaskController.java
@@ -211,7 +211,7 @@
}
mFreezingTaskConfig = true;
mDestRotatedBounds = new Rect(bounds);
- if (!mService.mAtmService.getTransitionController().isShellTransitionsEnabled()) {
+ if (!mDisplayContent.mTransitionController.isShellTransitionsEnabled()) {
continueOrientationChange();
}
}
diff --git a/services/core/java/com/android/server/wm/RootWindowContainer.java b/services/core/java/com/android/server/wm/RootWindowContainer.java
index 4020788..97ea41c 100644
--- a/services/core/java/com/android/server/wm/RootWindowContainer.java
+++ b/services/core/java/com/android/server/wm/RootWindowContainer.java
@@ -2211,7 +2211,7 @@
// display area, so reparent.
rootTask.reparent(taskDisplayArea, true /* onTop */);
}
- mService.getTransitionController().requestTransitionIfNeeded(TRANSIT_PIP, rootTask);
+ rootTask.mTransitionController.requestTransitionIfNeeded(TRANSIT_PIP, rootTask);
// Defer the windowing mode change until after the transition to prevent the activity
// from doing work and changing the activity visuals while animating
diff --git a/services/core/java/com/android/server/wm/ShellRoot.java b/services/core/java/com/android/server/wm/ShellRoot.java
index be6a5d2..6ed59e9 100644
--- a/services/core/java/com/android/server/wm/ShellRoot.java
+++ b/services/core/java/com/android/server/wm/ShellRoot.java
@@ -197,7 +197,7 @@
mAccessibilityWindow = null;
}
}
- if (mDisplayContent.mWmService.mAccessibilityController != null) {
+ if (mDisplayContent.mWmService.mAccessibilityController.hasCallbacks()) {
mDisplayContent.mWmService.mAccessibilityController.onSomeWindowResizedOrMoved(
mDisplayContent.getDisplayId());
}
diff --git a/services/core/java/com/android/server/wm/SurfaceFreezer.java b/services/core/java/com/android/server/wm/SurfaceFreezer.java
index 9c4f6f5..89986ce 100644
--- a/services/core/java/com/android/server/wm/SurfaceFreezer.java
+++ b/services/core/java/com/android/server/wm/SurfaceFreezer.java
@@ -72,8 +72,11 @@
*
* @param startBounds The original bounds (on screen) of the surface we are snapshotting.
* @param relativePosition The related position of the snapshot surface to its parent.
+ * @param freezeTarget The surface to take snapshot from. If {@code null}, we will take a
+ * snapshot from the {@link #mAnimatable} surface.
*/
- void freeze(SurfaceControl.Transaction t, Rect startBounds, Point relativePosition) {
+ void freeze(SurfaceControl.Transaction t, Rect startBounds, Point relativePosition,
+ @Nullable SurfaceControl freezeTarget) {
mFreezeBounds.set(startBounds);
mLeash = SurfaceAnimator.createAnimationLeash(mAnimatable, mAnimatable.getSurfaceControl(),
@@ -82,7 +85,7 @@
mWmService.mTransactionFactory);
mAnimatable.onAnimationLeashCreated(t, mLeash);
- SurfaceControl freezeTarget = mAnimatable.getFreezeSnapshotTarget();
+ freezeTarget = freezeTarget != null ? freezeTarget : mAnimatable.getFreezeSnapshotTarget();
if (freezeTarget != null) {
SurfaceControl.ScreenshotHardwareBuffer screenshotBuffer = createSnapshotBuffer(
freezeTarget, startBounds);
diff --git a/services/core/java/com/android/server/wm/SystemGesturesPointerEventListener.java b/services/core/java/com/android/server/wm/SystemGesturesPointerEventListener.java
index 513b1b7..658f4ef 100644
--- a/services/core/java/com/android/server/wm/SystemGesturesPointerEventListener.java
+++ b/services/core/java/com/android/server/wm/SystemGesturesPointerEventListener.java
@@ -16,6 +16,12 @@
package com.android.server.wm;
+import static android.view.DisplayCutout.BOUNDS_POSITION_BOTTOM;
+import static android.view.DisplayCutout.BOUNDS_POSITION_LEFT;
+import static android.view.DisplayCutout.BOUNDS_POSITION_RIGHT;
+import static android.view.DisplayCutout.BOUNDS_POSITION_TOP;
+
+import android.annotation.NonNull;
import android.content.Context;
import android.content.res.Resources;
import android.graphics.Rect;
@@ -33,6 +39,8 @@
import android.view.WindowManagerPolicyConstants.PointerEventListener;
import android.widget.OverScroller;
+import java.io.PrintWriter;
+
/**
* Listens for system-wide input gestures, firing callbacks when detected.
* @hide
@@ -54,7 +62,8 @@
private final Context mContext;
private final Handler mHandler;
private int mDisplayCutoutTouchableRegionSize;
- private int mSwipeStartThreshold;
+ // The thresholds for each edge of the display
+ private final Rect mSwipeStartThreshold = new Rect();
private int mSwipeDistanceThreshold;
private final Callbacks mCallbacks;
private final int[] mDownPointerId = new int[MAX_TRACKED_POINTERS];
@@ -66,7 +75,6 @@
int screenHeight;
int screenWidth;
- private DisplayInfo mTmpDisplayInfo = new DisplayInfo();
private int mDownPointers;
private boolean mSwipeFireable;
private boolean mDebugFireable;
@@ -88,27 +96,41 @@
void onConfigurationChanged() {
final Resources r = mContext.getResources();
+ final int defaultThreshold = r.getDimensionPixelSize(
+ com.android.internal.R.dimen.system_gestures_start_threshold);
+ mSwipeStartThreshold.set(defaultThreshold, defaultThreshold, defaultThreshold,
+ defaultThreshold);
+ mSwipeDistanceThreshold = defaultThreshold;
+
final Display display = DisplayManagerGlobal.getInstance()
.getRealDisplay(Display.DEFAULT_DISPLAY);
- display.getDisplayInfo(mTmpDisplayInfo);
- mSwipeStartThreshold = mTmpDisplayInfo.logicalWidth > mTmpDisplayInfo.logicalHeight
- ? r.getDimensionPixelSize(com.android.internal.R.dimen.status_bar_height_landscape)
- : r.getDimensionPixelSize(com.android.internal.R.dimen.status_bar_height_portrait);
-
final DisplayCutout displayCutout = display.getCutout();
if (displayCutout != null) {
- final Rect bounds = displayCutout.getBoundingRectTop();
- if (!bounds.isEmpty()) {
- // Expand swipe start threshold such that we can catch touches that just start below
- // the notch area
- mDisplayCutoutTouchableRegionSize = r.getDimensionPixelSize(
- com.android.internal.R.dimen.display_cutout_touchable_region_size);
- mSwipeStartThreshold += mDisplayCutoutTouchableRegionSize;
+ // Expand swipe start threshold such that we can catch touches that just start beyond
+ // the notch area
+ mDisplayCutoutTouchableRegionSize = r.getDimensionPixelSize(
+ com.android.internal.R.dimen.display_cutout_touchable_region_size);
+ final Rect[] bounds = displayCutout.getBoundingRectsAll();
+ if (bounds[BOUNDS_POSITION_LEFT] != null) {
+ mSwipeStartThreshold.left = Math.max(mSwipeStartThreshold.left,
+ bounds[BOUNDS_POSITION_LEFT].width() + mDisplayCutoutTouchableRegionSize);
+ }
+ if (bounds[BOUNDS_POSITION_TOP] != null) {
+ mSwipeStartThreshold.top = Math.max(mSwipeStartThreshold.top,
+ bounds[BOUNDS_POSITION_TOP].height() + mDisplayCutoutTouchableRegionSize);
+ }
+ if (bounds[BOUNDS_POSITION_RIGHT] != null) {
+ mSwipeStartThreshold.right = Math.max(mSwipeStartThreshold.right,
+ bounds[BOUNDS_POSITION_RIGHT].width() + mDisplayCutoutTouchableRegionSize);
+ }
+ if (bounds[BOUNDS_POSITION_BOTTOM] != null) {
+ mSwipeStartThreshold.bottom = Math.max(mSwipeStartThreshold.bottom,
+ bounds[BOUNDS_POSITION_BOTTOM].height()
+ + mDisplayCutoutTouchableRegionSize);
}
}
- mSwipeDistanceThreshold = mSwipeStartThreshold;
if (DEBUG) Slog.d(TAG, "mSwipeStartThreshold=" + mSwipeStartThreshold
- + " mSwipeDistanceThreshold=" + mSwipeDistanceThreshold);
+ + " mSwipeDistanceThreshold=" + mSwipeDistanceThreshold);
}
private static <T> T checkNull(String name, T arg) {
@@ -275,22 +297,22 @@
final long elapsed = time - mDownTime[i];
if (DEBUG) Slog.d(TAG, "pointer " + mDownPointerId[i]
+ " moved (" + fromX + "->" + x + "," + fromY + "->" + y + ") in " + elapsed);
- if (fromY <= mSwipeStartThreshold
+ if (fromY <= mSwipeStartThreshold.top
&& y > fromY + mSwipeDistanceThreshold
&& elapsed < SWIPE_TIMEOUT_MS) {
return SWIPE_FROM_TOP;
}
- if (fromY >= screenHeight - mSwipeStartThreshold
+ if (fromY >= screenHeight - mSwipeStartThreshold.bottom
&& y < fromY - mSwipeDistanceThreshold
&& elapsed < SWIPE_TIMEOUT_MS) {
return SWIPE_FROM_BOTTOM;
}
- if (fromX >= screenWidth - mSwipeStartThreshold
+ if (fromX >= screenWidth - mSwipeStartThreshold.right
&& x < fromX - mSwipeDistanceThreshold
&& elapsed < SWIPE_TIMEOUT_MS) {
return SWIPE_FROM_RIGHT;
}
- if (fromX <= mSwipeStartThreshold
+ if (fromX <= mSwipeStartThreshold.left
&& x > fromX + mSwipeDistanceThreshold
&& elapsed < SWIPE_TIMEOUT_MS) {
return SWIPE_FROM_LEFT;
@@ -298,6 +320,15 @@
return SWIPE_NONE;
}
+ public void dump(@NonNull PrintWriter pw, @NonNull String prefix) {
+ final String inner = prefix + " ";
+ pw.println(prefix + TAG + ":");
+ pw.print(inner); pw.print("mDisplayCutoutTouchableRegionSize=");
+ pw.println(mDisplayCutoutTouchableRegionSize);
+ pw.print(inner); pw.print("mSwipeStartThreshold="); pw.println(mSwipeStartThreshold);
+ pw.print(inner); pw.print("mSwipeDistanceThreshold="); pw.println(mSwipeDistanceThreshold);
+ }
+
private final class FlingGestureDetector extends GestureDetector.SimpleOnGestureListener {
private OverScroller mOverscroller;
diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java
index d89d212..594b6be 100644
--- a/services/core/java/com/android/server/wm/Task.java
+++ b/services/core/java/com/android/server/wm/Task.java
@@ -4270,7 +4270,7 @@
/**
* @return true if the task is currently focused.
*/
- boolean isFocused() {
+ private boolean isFocused() {
if (mDisplayContent == null || mDisplayContent.mFocusedApp == null) {
return false;
}
@@ -4328,14 +4328,10 @@
}
/**
- * Called on the task of a window which gained or lost focus.
+ * Called on the task when it gained or lost focus.
* @param hasFocus
*/
void onAppFocusChanged(boolean hasFocus) {
- final ActivityRecord topAct = getTopVisibleActivity();
- if (topAct != null && (topAct.mStartingData instanceof SnapshotStartingData)) {
- topAct.removeStartingWindowIfNeeded();
- }
updateShadowsRadius(hasFocus, getSyncTransaction());
dispatchTaskInfoChangedIfNeeded(false /* force */);
}
@@ -4582,7 +4578,7 @@
// From fullscreen to PiP.
if (topActivity != null && currentMode == WINDOWING_MODE_FULLSCREEN
&& windowingMode == WINDOWING_MODE_PINNED
- && !mAtmService.getTransitionController().isShellTransitionsEnabled()) {
+ && !mTransitionController.isShellTransitionsEnabled()) {
mDisplayContent.mPinnedTaskController
.deferOrientationChangeForEnteringPipFromFullScreenIfNeeded();
}
diff --git a/services/core/java/com/android/server/wm/TaskFragment.java b/services/core/java/com/android/server/wm/TaskFragment.java
index 2b5a820..99f6f34 100644
--- a/services/core/java/com/android/server/wm/TaskFragment.java
+++ b/services/core/java/com/android/server/wm/TaskFragment.java
@@ -1394,7 +1394,8 @@
ProtoLog.v(WM_DEBUG_STATES, "Moving to PAUSING: %s", prev);
mPausingActivity = prev;
mLastPausedActivity = prev;
- if (prev.isNoHistory() && !mTaskSupervisor.mNoHistoryActivities.contains(prev)) {
+ if (!prev.finishing && prev.isNoHistory()
+ && !mTaskSupervisor.mNoHistoryActivities.contains(prev)) {
mTaskSupervisor.mNoHistoryActivities.add(prev);
}
prev.setState(PAUSING, "startPausingLocked");
@@ -1427,23 +1428,8 @@
+ "directly: %s", prev);
didAutoPip = mAtmService.enterPictureInPictureMode(prev, prev.pictureInPictureArgs);
- mPausingActivity = null;
} else {
- ProtoLog.v(WM_DEBUG_STATES, "Enqueueing pending pause: %s", prev);
- try {
- EventLogTags.writeWmPauseActivity(prev.mUserId, System.identityHashCode(prev),
- prev.shortComponentName, "userLeaving=" + userLeaving, reason);
-
- mAtmService.getLifecycleManager().scheduleTransaction(prev.app.getThread(),
- prev.appToken, PauseActivityItem.obtain(prev.finishing, userLeaving,
- prev.configChangeFlags, pauseImmediately));
- } catch (Exception e) {
- // Ignore exception, if process died other code will cleanup.
- Slog.w(TAG, "Exception thrown during pause", e);
- mPausingActivity = null;
- mLastPausedActivity = null;
- mTaskSupervisor.mNoHistoryActivities.remove(prev);
- }
+ schedulePauseActivity(prev, userLeaving, pauseImmediately, reason);
}
} else {
mPausingActivity = null;
@@ -1458,7 +1444,7 @@
}
// If already entered PIP mode, no need to keep pausing.
- if (mPausingActivity != null && !didAutoPip) {
+ if (mPausingActivity != null) {
// Have the window manager pause its key dispatching until the new
// activity has started. If we're pausing the activity just because
// the screen is being turned off and the UI is sleeping, don't interrupt
@@ -1478,7 +1464,7 @@
} else {
prev.schedulePauseTimeout();
// Unset readiness since we now need to wait until this pause is complete.
- mAtmService.getTransitionController().setReady(this, false /* ready */);
+ mTransitionController.setReady(this, false /* ready */);
return true;
}
@@ -1493,6 +1479,25 @@
}
}
+ void schedulePauseActivity(ActivityRecord prev, boolean userLeaving,
+ boolean pauseImmediately, String reason) {
+ ProtoLog.v(WM_DEBUG_STATES, "Enqueueing pending pause: %s", prev);
+ try {
+ EventLogTags.writeWmPauseActivity(prev.mUserId, System.identityHashCode(prev),
+ prev.shortComponentName, "userLeaving=" + userLeaving, reason);
+
+ mAtmService.getLifecycleManager().scheduleTransaction(prev.app.getThread(),
+ prev.appToken, PauseActivityItem.obtain(prev.finishing, userLeaving,
+ prev.configChangeFlags, pauseImmediately));
+ } catch (Exception e) {
+ // Ignore exception, if process died other code will cleanup.
+ Slog.w(TAG, "Exception thrown during pause", e);
+ mPausingActivity = null;
+ mLastPausedActivity = null;
+ mTaskSupervisor.mNoHistoryActivities.remove(prev);
+ }
+ }
+
@VisibleForTesting
void completePause(boolean resumeNext, ActivityRecord resuming) {
// Complete the pausing process of a pausing activity, so it doesn't make sense to
@@ -2151,10 +2156,6 @@
}
}
- int getTaskFragmentOrganizerPid() {
- return mTaskFragmentOrganizerPid;
- }
-
/**
* Returns a {@link TaskFragmentInfo} with information from this TaskFragment. Should not be
* called from {@link Task}.
@@ -2286,7 +2287,7 @@
return false;
}
return isAnimating(TRANSITION | CHILDREN, WindowState.EXIT_ANIMATING_TYPES)
- || mAtmService.getTransitionController().inTransition(this);
+ || inTransition();
}
@Override
diff --git a/services/core/java/com/android/server/wm/TaskLaunchParamsModifier.java b/services/core/java/com/android/server/wm/TaskLaunchParamsModifier.java
index 9cc24e2..e31a662 100644
--- a/services/core/java/com/android/server/wm/TaskLaunchParamsModifier.java
+++ b/services/core/java/com/android/server/wm/TaskLaunchParamsModifier.java
@@ -261,8 +261,7 @@
if (launchMode == WINDOWING_MODE_PINNED) {
if (DEBUG) appendLog("picture-in-picture");
} else if (!root.isResizeable()) {
- if (shouldLaunchUnresizableAppInFreeform(root, suggestedDisplayArea,
- options.getLaunchWindowingMode())) {
+ if (shouldLaunchUnresizableAppInFreeform(root, suggestedDisplayArea, options)) {
launchMode = WINDOWING_MODE_FREEFORM;
if (outParams.mBounds.isEmpty()) {
getTaskBounds(root, suggestedDisplayArea, layout, launchMode,
@@ -618,8 +617,8 @@
}
private boolean shouldLaunchUnresizableAppInFreeform(ActivityRecord activity,
- TaskDisplayArea displayArea, int launchWindowingMode) {
- if (launchWindowingMode == WINDOWING_MODE_FULLSCREEN) {
+ TaskDisplayArea displayArea, @Nullable ActivityOptions options) {
+ if (options != null && options.getLaunchWindowingMode() == WINDOWING_MODE_FULLSCREEN) {
// Do not launch the activity in freeform if it explicitly requested fullscreen mode.
return false;
}
diff --git a/services/core/java/com/android/server/wm/TaskOrganizerController.java b/services/core/java/com/android/server/wm/TaskOrganizerController.java
index ccda126..f1345e8 100644
--- a/services/core/java/com/android/server/wm/TaskOrganizerController.java
+++ b/services/core/java/com/android/server/wm/TaskOrganizerController.java
@@ -28,7 +28,6 @@
import android.app.WindowConfiguration;
import android.content.Intent;
import android.content.pm.ParceledListSlice;
-import android.graphics.Rect;
import android.os.Binder;
import android.os.IBinder;
import android.os.Parcel;
@@ -40,6 +39,7 @@
import android.window.ITaskOrganizerController;
import android.window.SplashScreenView;
import android.window.StartingWindowInfo;
+import android.window.StartingWindowRemovalInfo;
import android.window.TaskAppearedInfo;
import android.window.TaskSnapshot;
import android.window.WindowContainerToken;
@@ -498,12 +498,15 @@
if (lastOrganizer == null) {
return;
}
- SurfaceControl windowAnimationLeash = null;
- Rect mainFrame = null;
+ final StartingWindowRemovalInfo removalInfo = new StartingWindowRemovalInfo();
+ removalInfo.taskId = task.mTaskId;
+ removalInfo.playRevealAnimation = prepareAnimation;
final boolean playShiftUpAnimation = !task.inMultiWindowMode();
- if (prepareAnimation && playShiftUpAnimation) {
- final ActivityRecord topActivity = task.topActivityContainsStartingWindow();
- if (topActivity != null) {
+ final ActivityRecord topActivity = task.topActivityContainsStartingWindow();
+ if (topActivity != null) {
+ removalInfo.deferRemoveForIme = topActivity.mDisplayContent
+ .mayImeShowOnLaunchingActivity(topActivity);
+ if (prepareAnimation && playShiftUpAnimation) {
final WindowState mainWindow =
topActivity.findMainWindow(false/* includeStartingApp */);
if (mainWindow != null) {
@@ -512,15 +515,15 @@
final SurfaceControl.Transaction t = mainWindow.getPendingTransaction();
mainWindow.startAnimation(t, adaptor, false,
ANIMATION_TYPE_STARTING_REVEAL);
- windowAnimationLeash = adaptor.mAnimationLeash;
- mainFrame = mainWindow.getRelativeFrame();
- t.setPosition(windowAnimationLeash, mainFrame.left, mainFrame.top);
+ removalInfo.windowAnimationLeash = adaptor.mAnimationLeash;
+ removalInfo.mainFrame = mainWindow.getRelativeFrame();
+ t.setPosition(removalInfo.windowAnimationLeash,
+ removalInfo.mainFrame.left, removalInfo.mainFrame.top);
}
}
}
try {
- lastOrganizer.removeStartingWindow(task.mTaskId, windowAnimationLeash,
- mainFrame, prepareAnimation);
+ lastOrganizer.removeStartingWindow(removalInfo);
} catch (RemoteException e) {
Slog.e(TAG, "Exception sending onStartTaskFinished callback", e);
}
diff --git a/services/core/java/com/android/server/wm/Transition.java b/services/core/java/com/android/server/wm/Transition.java
index 1909875..e50e8ef 100644
--- a/services/core/java/com/android/server/wm/Transition.java
+++ b/services/core/java/com/android/server/wm/Transition.java
@@ -758,8 +758,13 @@
(flags & TRANSIT_FLAG_KEYGUARD_GOING_AWAY_WITH_WALLPAPER) != 0,
(flags & TRANSIT_FLAG_KEYGUARD_GOING_AWAY_TO_SHADE) != 0,
(flags & TRANSIT_FLAG_KEYGUARD_GOING_AWAY_SUBTLE_ANIMATION) != 0);
- mController.mAtm.mWindowManager.mPolicy.startKeyguardExitAnimation(
- SystemClock.uptimeMillis(), 0 /* duration */);
+ if (!WindowManagerService.sEnableRemoteKeyguardGoingAwayAnimation) {
+ // When remote animation is enabled for KEYGUARD_GOING_AWAY transition, SysUI
+ // receives IRemoteAnimationRunner#onAnimationStart to start animation, so we don't
+ // need to call IKeyguardService#keyguardGoingAway here.
+ mController.mAtm.mWindowManager.mPolicy.startKeyguardExitAnimation(
+ SystemClock.uptimeMillis(), 0 /* duration */);
+ }
}
if ((flags & TRANSIT_FLAG_KEYGUARD_LOCKED) != 0) {
mController.mAtm.mWindowManager.mPolicy.applyKeyguardOcclusionChange();
diff --git a/services/core/java/com/android/server/wm/WallpaperController.java b/services/core/java/com/android/server/wm/WallpaperController.java
index 7893612..0909462 100644
--- a/services/core/java/com/android/server/wm/WallpaperController.java
+++ b/services/core/java/com/android/server/wm/WallpaperController.java
@@ -128,7 +128,7 @@
}
mFindResults.resetTopWallpaper = true;
- if (mService.mAtmService.getTransitionController().getTransitionPlayer() == null) {
+ if (!w.mTransitionController.isShellTransitionsEnabled()) {
if (w.mActivityRecord != null && !w.mActivityRecord.isVisible()
&& !w.mActivityRecord.isAnimating(TRANSITION | PARENTS)) {
// If this window's app token is hidden and not animating, it is of no interest.
diff --git a/services/core/java/com/android/server/wm/WallpaperWindowToken.java b/services/core/java/com/android/server/wm/WallpaperWindowToken.java
index b54e8b7..75c84c4 100644
--- a/services/core/java/com/android/server/wm/WallpaperWindowToken.java
+++ b/services/core/java/com/android/server/wm/WallpaperWindowToken.java
@@ -112,7 +112,7 @@
setVisibility(visible);
}
final WallpaperController wallpaperController = mDisplayContent.mWallpaperController;
- if (mWmService.mAtmService.getTransitionController().getTransitionPlayer() != null) {
+ if (mTransitionController.isShellTransitionsEnabled()) {
return;
}
@@ -157,12 +157,12 @@
*/
void setVisibility(boolean visible) {
// Before setting mVisibleRequested so we can track changes.
- mWmService.mAtmService.getTransitionController().collect(this);
+ mTransitionController.collect(this);
setVisibleRequested(visible);
// If in a transition, defer commits for activities that are going invisible
- if (!visible && (mWmService.mAtmService.getTransitionController().inTransition()
+ if (!visible && (mTransitionController.inTransition()
|| getDisplayContent().mAppTransition.isRunning())) {
return;
}
diff --git a/services/core/java/com/android/server/wm/WindowAnimator.java b/services/core/java/com/android/server/wm/WindowAnimator.java
index eb32486..4a43f4f 100644
--- a/services/core/java/com/android/server/wm/WindowAnimator.java
+++ b/services/core/java/com/android/server/wm/WindowAnimator.java
@@ -164,7 +164,7 @@
final DisplayContent dc = root.getDisplayContent(displayId);
dc.checkAppWindowsReadyToShow();
- if (accessibilityController != null) {
+ if (accessibilityController.hasCallbacks()) {
accessibilityController.drawMagnifiedRegionBorderIfNeeded(displayId,
mTransaction);
}
diff --git a/services/core/java/com/android/server/wm/WindowContainer.java b/services/core/java/com/android/server/wm/WindowContainer.java
index 0bc3712..6b21858 100644
--- a/services/core/java/com/android/server/wm/WindowContainer.java
+++ b/services/core/java/com/android/server/wm/WindowContainer.java
@@ -57,6 +57,7 @@
import static com.android.server.wm.WindowContainerProto.IDENTIFIER;
import static com.android.server.wm.WindowContainerProto.ORIENTATION;
import static com.android.server.wm.WindowContainerProto.SURFACE_ANIMATOR;
+import static com.android.server.wm.WindowContainerProto.SURFACE_CONTROL;
import static com.android.server.wm.WindowContainerProto.VISIBLE;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_ANIM;
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
@@ -197,10 +198,10 @@
* Applied as part of the animation pass in "prepareSurfaces".
*/
protected final SurfaceAnimator mSurfaceAnimator;
- private boolean mAnyParentAnimating;
final SurfaceFreezer mSurfaceFreezer;
protected final WindowManagerService mWmService;
+ final TransitionController mTransitionController;
/**
* Sources which triggered a surface animation on this container. An animation target can be
@@ -331,6 +332,7 @@
WindowContainer(WindowManagerService wms) {
mWmService = wms;
+ mTransitionController = mWmService.mAtmService.getTransitionController();
mPendingTransaction = wms.mTransactionFactory.get();
mSyncTransaction = wms.mTransactionFactory.get();
mSurfaceAnimator = new SurfaceAnimator(this, this::onAnimationFinished, wms);
@@ -1008,7 +1010,7 @@
}
boolean inTransition() {
- return mWmService.mAtmService.getTransitionController().inTransition(this);
+ return mTransitionController.inTransition(this);
}
void sendAppVisibilityToClients() {
@@ -2310,7 +2312,7 @@
void assignLayer(Transaction t, int layer) {
// Don't assign layers while a transition animation is playing
// TODO(b/173528115): establish robust best-practices around z-order fighting.
- if (mWmService.mAtmService.getTransitionController().isPlaying()) return;
+ if (mTransitionController.isPlaying()) return;
final boolean changed = layer != mLastLayer || mLastRelativeToLayer != null;
if (mSurfaceControl != null && changed) {
setLayer(t, layer);
@@ -2428,6 +2430,9 @@
if (mSurfaceAnimator.isAnimating()) {
mSurfaceAnimator.dumpDebug(proto, SURFACE_ANIMATOR);
}
+ if (mSurfaceControl != null) {
+ mSurfaceControl.dumpDebug(proto, SURFACE_CONTROL);
+ }
// add children to proto
for (int i = 0; i < getChildCount(); i++) {
@@ -2620,23 +2625,27 @@
* For now, this will only be called for the following cases:
* 1. {@link Task} is changing windowing mode between fullscreen and freeform.
* 2. {@link TaskFragment} is organized and is changing window bounds.
- * 3. {@link ActivityRecord} is reparented into an organized {@link TaskFragment}.
+ * 3. {@link ActivityRecord} is reparented into an organized {@link TaskFragment}. (The
+ * transition will happen on the {@link TaskFragment} for this case).
*
- * This shouldn't be called on other {@link WindowContainer} unless there is a valid use case.
+ * This shouldn't be called on other {@link WindowContainer} unless there is a valid
+ * use case.
*
* @param startBounds The original bounds (on screen) of the surface we are snapshotting.
- * @param parentBounds The parent bounds (on screen) to calculate the animation surface
- * position.
+ * @param freezeTarget The surface to take snapshot from. If {@code null}, we will take a
+ * snapshot from {@link #getFreezeSnapshotTarget()}.
*/
- void initializeChangeTransition(Rect startBounds, Rect parentBounds) {
+ void initializeChangeTransition(Rect startBounds, @Nullable SurfaceControl freezeTarget) {
mDisplayContent.prepareAppTransition(TRANSIT_CHANGE);
mDisplayContent.mChangingContainers.add(this);
+ // Calculate the relative position in parent container.
+ final Rect parentBounds = getParent().getBounds();
mTmpPoint.set(startBounds.left - parentBounds.left, startBounds.top - parentBounds.top);
- mSurfaceFreezer.freeze(getSyncTransaction(), startBounds, mTmpPoint);
+ mSurfaceFreezer.freeze(getSyncTransaction(), startBounds, mTmpPoint, freezeTarget);
}
void initializeChangeTransition(Rect startBounds) {
- initializeChangeTransition(startBounds, getParent().getBounds());
+ initializeChangeTransition(startBounds, null /* freezeTarget */);
}
ArraySet<WindowContainer> getAnimationSources() {
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 0e99c0f..4cf8c97 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -636,7 +636,7 @@
/** List of window currently causing non-system overlay windows to be hidden. */
private ArrayList<WindowState> mHidingNonSystemOverlayWindows = new ArrayList<>();
- AccessibilityController mAccessibilityController;
+ final AccessibilityController mAccessibilityController;
private RecentsAnimationController mRecentsAnimationController;
Watermark mWatermark;
@@ -1387,6 +1387,7 @@
mStartingSurfaceController = new StartingSurfaceController(this);
mBlurController = new BlurController(mContext, mPowerManager);
+ mAccessibilityController = new AccessibilityController(this);
}
DisplayAreaPolicy.Provider getDisplayAreaPolicyProvider() {
@@ -1804,7 +1805,7 @@
winAnimator.mEnterAnimationPending = true;
winAnimator.mEnteringAnimation = true;
// Check if we need to prepare a transition for replacing window first.
- if (mAtmService.getTransitionController().getTransitionPlayer() == null
+ if (!win.mTransitionController.isShellTransitionsEnabled()
&& activity != null && activity.isVisible()
&& !prepareWindowReplacementTransition(activity)) {
// If not, check if need to set up a dummy transition during display freeze
@@ -2164,7 +2165,7 @@
mWindowPlacerLocked.performSurfacePlacement();
// We need to report touchable region changes to accessibility.
- if (mAccessibilityController != null) {
+ if (mAccessibilityController.hasCallbacks()) {
mAccessibilityController.onSomeWindowResizedOrMovedWithCallingUid(
uid, w.getDisplayContent().getDisplayId());
}
@@ -2177,7 +2178,7 @@
public void onRectangleOnScreenRequested(IBinder token, Rect rectangle) {
synchronized (mGlobalLock) {
- if (mAccessibilityController != null) {
+ if (mAccessibilityController.hasCallbacks()) {
WindowState window = mWindowMap.get(token);
if (window != null) {
mAccessibilityController.onRectangleOnScreenRequested(
@@ -2267,7 +2268,7 @@
win.mActivityRecord.checkKeyguardFlagsChanged();
}
if (((attrChanges & LayoutParams.ACCESSIBILITY_TITLE_CHANGED) != 0)
- && (mAccessibilityController != null)) {
+ && (mAccessibilityController.hasCallbacks())) {
// No move or resize, but the controller checks for title changes as well
mAccessibilityController.onSomeWindowResizedOrMovedWithCallingUid(
uid, win.getDisplayContent().getDisplayId());
@@ -2569,7 +2570,7 @@
if (win.mAttrs.type == TYPE_APPLICATION_STARTING) {
transit = WindowManagerPolicy.TRANSIT_PREVIEW_DONE;
}
- if (mAtmService.getTransitionController().inTransition(win)) {
+ if (win.inTransition()) {
focusMayChange = true;
win.mAnimatingExit = true;
} else if (win.isWinVisibleLw() && winAnimator.applyAnimationLocked(transit, false)) {
@@ -2594,7 +2595,7 @@
win.mDestroying = true;
win.destroySurface(false, stopped);
}
- if (mAccessibilityController != null) {
+ if (mAccessibilityController.hasCallbacks()) {
mAccessibilityController.onWindowTransition(win, transit);
}
@@ -4051,7 +4052,7 @@
final boolean pendingRemoteRotation = rotationChanged
&& (displayContent.getDisplayRotation().isWaitingForRemoteRotation()
- || mAtmService.getTransitionController().isCollecting());
+ || displayContent.mTransitionController.isCollecting());
// Even if alwaysSend, we are waiting for a transition or remote to provide
// rotated configuration, so we can't update configuration yet.
if (!pendingRemoteRotation) {
@@ -6423,7 +6424,7 @@
mInputManagerCallback.dump(pw, " ");
mTaskSnapshotController.dump(pw, " ");
- if (mAccessibilityController != null) {
+ if (mAccessibilityController.hasCallbacks()) {
mAccessibilityController.dump(pw, " ");
}
@@ -7443,7 +7444,7 @@
@Override
public void setMagnificationSpec(int displayId, MagnificationSpec spec) {
synchronized (mGlobalLock) {
- if (mAccessibilityController != null) {
+ if (mAccessibilityController.hasCallbacks()) {
mAccessibilityController.setMagnificationSpec(displayId, spec);
} else {
throw new IllegalStateException("Magnification callbacks not set!");
@@ -7454,7 +7455,7 @@
@Override
public void setForceShowMagnifiableBounds(int displayId, boolean show) {
synchronized (mGlobalLock) {
- if (mAccessibilityController != null) {
+ if (mAccessibilityController.hasCallbacks()) {
mAccessibilityController.setForceShowMagnifiableBounds(displayId, show);
} else {
throw new IllegalStateException("Magnification callbacks not set!");
@@ -7465,7 +7466,7 @@
@Override
public void getMagnificationRegion(int displayId, @NonNull Region magnificationRegion) {
synchronized (mGlobalLock) {
- if (mAccessibilityController != null) {
+ if (mAccessibilityController.hasCallbacks()) {
mAccessibilityController.getMagnificationRegion(displayId, magnificationRegion);
} else {
throw new IllegalStateException("Magnification callbacks not set!");
@@ -7481,7 +7482,7 @@
return null;
}
MagnificationSpec spec = null;
- if (mAccessibilityController != null) {
+ if (mAccessibilityController.hasCallbacks()) {
spec = mAccessibilityController.getMagnificationSpecForWindow(windowState);
}
if ((spec == null || spec.isNop()) && windowState.mGlobalScale == 1.0f) {
@@ -7500,16 +7501,7 @@
public boolean setMagnificationCallbacks(int displayId,
@Nullable MagnificationCallbacks callbacks) {
synchronized (mGlobalLock) {
- if (mAccessibilityController == null) {
- mAccessibilityController = new AccessibilityController(
- WindowManagerService.this);
- }
- boolean result = mAccessibilityController.setMagnificationCallbacks(
- displayId, callbacks);
- if (!mAccessibilityController.hasCallbacks()) {
- mAccessibilityController = null;
- }
- return result;
+ return mAccessibilityController.setMagnificationCallbacks(displayId, callbacks);
}
}
@@ -7517,17 +7509,8 @@
public boolean setWindowsForAccessibilityCallback(int displayId,
WindowsForAccessibilityCallback callback) {
synchronized (mGlobalLock) {
- if (mAccessibilityController == null) {
- mAccessibilityController = new AccessibilityController(
- WindowManagerService.this);
- }
- final boolean result =
- mAccessibilityController.setWindowsForAccessibilityCallback(
- displayId, callback);
- if (!mAccessibilityController.hasCallbacks()) {
- mAccessibilityController = null;
- }
- return result;
+ return mAccessibilityController
+ .setWindowsForAccessibilityCallback(displayId, callback);
}
}
@@ -7700,13 +7683,7 @@
@Override
public void computeWindowsForAccessibility(int displayId) {
- final AccessibilityController accessibilityController;
- synchronized (mGlobalLock) {
- accessibilityController = mAccessibilityController;
- }
- if (accessibilityController != null) {
- accessibilityController.performComputeChangedWindowsNot(displayId, true);
- }
+ mAccessibilityController.performComputeChangedWindowsNot(displayId, true);
}
@Override
diff --git a/services/core/java/com/android/server/wm/WindowManagerShellCommand.java b/services/core/java/com/android/server/wm/WindowManagerShellCommand.java
index 47d7f03..0f8587c 100644
--- a/services/core/java/com/android/server/wm/WindowManagerShellCommand.java
+++ b/services/core/java/com/android/server/wm/WindowManagerShellCommand.java
@@ -23,6 +23,9 @@
import static com.android.server.wm.LetterboxConfiguration.LETTERBOX_BACKGROUND_APP_COLOR_BACKGROUND_FLOATING;
import static com.android.server.wm.LetterboxConfiguration.LETTERBOX_BACKGROUND_SOLID_COLOR;
import static com.android.server.wm.LetterboxConfiguration.LETTERBOX_BACKGROUND_WALLPAPER;
+import static com.android.server.wm.LetterboxConfiguration.LETTERBOX_REACHABILITY_POSITION_CENTER;
+import static com.android.server.wm.LetterboxConfiguration.LETTERBOX_REACHABILITY_POSITION_LEFT;
+import static com.android.server.wm.LetterboxConfiguration.LETTERBOX_REACHABILITY_POSITION_RIGHT;
import android.content.res.Resources.NotFoundException;
import android.graphics.Color;
@@ -44,6 +47,7 @@
import com.android.server.LocalServices;
import com.android.server.statusbar.StatusBarManagerInternal;
import com.android.server.wm.LetterboxConfiguration.LetterboxBackgroundType;
+import com.android.server.wm.LetterboxConfiguration.LetterboxReachabilityPosition;
import java.io.IOException;
import java.io.PrintWriter;
@@ -787,22 +791,33 @@
return 0;
}
- private int runSetLetterboxDefaultPositionMultiplierForReachability(PrintWriter pw)
+ private int runSetLetterboxDefaultPositionForReachability(PrintWriter pw)
throws RemoteException {
- final float multiplier;
+ @LetterboxReachabilityPosition final int position;
try {
String arg = getNextArgRequired();
- multiplier = Float.parseFloat(arg);
- } catch (NumberFormatException e) {
- getErrPrintWriter().println("Error: bad multiplier format " + e);
- return -1;
+ switch (arg) {
+ case "left":
+ position = LETTERBOX_REACHABILITY_POSITION_LEFT;
+ break;
+ case "center":
+ position = LETTERBOX_REACHABILITY_POSITION_CENTER;
+ break;
+ case "right":
+ position = LETTERBOX_REACHABILITY_POSITION_RIGHT;
+ break;
+ default:
+ getErrPrintWriter().println(
+ "Error: 'left', 'center' or 'right' are expected as an argument");
+ return -1;
+ }
} catch (IllegalArgumentException e) {
getErrPrintWriter().println(
- "Error: multiplier should be provided as an argument " + e);
+ "Error: 'left', 'center' or 'right' are expected as an argument" + e);
return -1;
}
synchronized (mInternal.mGlobalLock) {
- mLetterboxConfiguration.setDefaultPositionMultiplierForReachability(multiplier);
+ mLetterboxConfiguration.setDefaultPositionForReachability(position);
}
return 0;
}
@@ -841,8 +856,8 @@
case "--isReachabilityEnabled":
runSetLetterboxIsReachabilityEnabled(pw);
break;
- case "--defaultPositionMultiplierReachability":
- runSetLetterboxDefaultPositionMultiplierForReachability(pw);
+ case "--defaultPositionForReachability":
+ runSetLetterboxDefaultPositionForReachability(pw);
break;
default:
getErrPrintWriter().println(
@@ -885,8 +900,8 @@
case "isReachabilityEnabled":
mLetterboxConfiguration.getIsReachabilityEnabled();
break;
- case "defaultPositionMultiplierForReachability":
- mLetterboxConfiguration.getDefaultPositionMultiplierForReachability();
+ case "defaultPositionForReachability":
+ mLetterboxConfiguration.getDefaultPositionForReachability();
break;
default:
getErrPrintWriter().println(
@@ -982,7 +997,7 @@
mLetterboxConfiguration.resetLetterboxBackgroundWallpaperDarkScrimAlpha();
mLetterboxConfiguration.resetLetterboxHorizontalPositionMultiplier();
mLetterboxConfiguration.resetIsReachabilityEnabled();
- mLetterboxConfiguration.resetDefaultPositionMultiplierForReachability();
+ mLetterboxConfiguration.resetDefaultPositionForReachability();
}
}
@@ -996,8 +1011,9 @@
+ mLetterboxConfiguration.getFixedOrientationLetterboxAspectRatio());
pw.println("Is reachability enabled: "
+ mLetterboxConfiguration.getIsReachabilityEnabled());
- pw.println("Default position multiplier for reachability: "
- + mLetterboxConfiguration.getDefaultPositionMultiplierForReachability());
+ pw.println("Default position for reachability: "
+ + LetterboxConfiguration.letterboxReachabilityPositionToString(
+ mLetterboxConfiguration.getDefaultPositionForReachability()));
pw.println("Background type: "
+ LetterboxConfiguration.letterboxBackgroundTypeToString(
@@ -1135,11 +1151,9 @@
pw.println(" --isReachabilityEnabled [true|1|false|0]");
pw.println(" Whether reachability repositioning is allowed for letterboxed");
pw.println(" fullscreen apps in landscape device orientation.");
- pw.println(" --defaultPositionMultiplierReachability multiplier");
- pw.println(" Default horizontal position of app window center when reachability is");
- pw.println(" enabled. If multiplier < 0.0 or > 1, both it and ");
- pw.println(" R.dimen.config_letterboxDefaultPositionMultiplierForReachability");
- pw.println(" are ignored and right position (1.0) is used.");
+ pw.println(" --defaultPositionForReachability [left|center|right]");
+ pw.println(" Default horizontal position of app window when reachability is.");
+ pw.println(" enabled.");
pw.println(" reset-letterbox-style [aspectRatio|cornerRadius|backgroundType");
pw.println(" |backgroundColor|wallpaperBlurRadius|wallpaperDarkScrimAlpha");
pw.println(" |horizontalPositionMultiplier|isReachabilityEnabled");
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index 3adecf3..ad4734f 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -1175,8 +1175,7 @@
if (WindowManager.LayoutParams.isSystemAlertWindowType(mAttrs.type)) {
return TouchOcclusionMode.USE_OPACITY;
}
- if (isAnimating(PARENTS | TRANSITION, ANIMATION_TYPE_ALL)
- || mWmService.mAtmService.getTransitionController().inTransition(this)) {
+ if (isAnimating(PARENTS | TRANSITION, ANIMATION_TYPE_ALL) || inTransition()) {
return TouchOcclusionMode.USE_OPACITY;
}
return TouchOcclusionMode.BLOCK_UNTRUSTED;
@@ -2217,7 +2216,7 @@
mWmService.mAccessibilityController;
final int winTransit = TRANSIT_EXIT;
mWinAnimator.applyAnimationLocked(winTransit, false /* isEntrance */);
- if (accessibilityController != null) {
+ if (accessibilityController.hasCallbacks()) {
accessibilityController.onWindowTransition(this, winTransit);
}
}
@@ -2238,7 +2237,7 @@
}
if (isVisibleNow() && animateExit) {
mWinAnimator.applyAnimationLocked(TRANSIT_EXIT, false);
- if (mWmService.mAccessibilityController != null) {
+ if (mWmService.mAccessibilityController.hasCallbacks()) {
mWmService.mAccessibilityController.onWindowTransition(this, TRANSIT_EXIT);
}
changed = true;
@@ -2288,7 +2287,7 @@
startMoveAnimation(left, top);
}
- if (mWmService.mAccessibilityController != null) {
+ if (mWmService.mAccessibilityController.hasCallbacks()) {
mWmService.mAccessibilityController.onSomeWindowResizedOrMoved(getDisplayId());
}
updateLocationInParentDisplayIfNeeded();
@@ -2326,7 +2325,7 @@
&& (mWindowFrames.mRelFrame.top != mWindowFrames.mLastRelFrame.top
|| mWindowFrames.mRelFrame.left != mWindowFrames.mLastRelFrame.left)
&& (!mIsChildWindow || !getParentWindow().hasMoved())
- && !mWmService.mAtmService.getTransitionController().isCollecting();
+ && !mTransitionController.isCollecting();
}
boolean isObscuringDisplay() {
@@ -2574,7 +2573,7 @@
setDisplayLayoutNeeded();
mWmService.requestTraversal();
}
- if (mWmService.mAccessibilityController != null) {
+ if (mWmService.mAccessibilityController.hasCallbacks()) {
mWmService.mAccessibilityController.onWindowTransition(this, transit);
}
}
@@ -2712,8 +2711,7 @@
// Don't allow transient-launch activities to take IME.
if (rootTask != null && mActivityRecord != null
- && mWmService.mAtmService.getTransitionController().isTransientLaunch(
- mActivityRecord)) {
+ && mTransitionController.isTransientLaunch(mActivityRecord)) {
return false;
}
@@ -3600,7 +3598,7 @@
if (mAttrs.type >= FIRST_SYSTEM_WINDOW && mAttrs.type != TYPE_TOAST) {
mWmService.mAtmService.mActiveUids.onNonAppSurfaceVisibilityChanged(mOwnerUid, shown);
}
- if (mIsImWindow && mWmService.mAccessibilityController != null) {
+ if (mIsImWindow && mWmService.mAccessibilityController.hasCallbacks()) {
mWmService.mAccessibilityController.onImeSurfaceShownChanged(this, shown);
}
}
@@ -3944,7 +3942,7 @@
"Requested redraw for orientation change: %s", this);
}
- if (mWmService.mAccessibilityController != null) {
+ if (mWmService.mAccessibilityController.hasCallbacks()) {
mWmService.mAccessibilityController.onSomeWindowResizedOrMoved(displayId);
}
updateLocationInParentDisplayIfNeeded();
@@ -5070,7 +5068,7 @@
if (isAnimating()) {
return;
}
- if (mWmService.mAccessibilityController != null) {
+ if (mWmService.mAccessibilityController.hasCallbacks()) {
mWmService.mAccessibilityController.onSomeWindowResizedOrMoved(getDisplayId());
}
@@ -5679,9 +5677,11 @@
}
boolean needsRelativeLayeringToIme() {
- // We only use the relative layering mode in split screen, as part of elevating the IME
- // and windows above it's target above the docked divider.
- if (!inSplitScreenWindowingMode()) {
+ // We use the relative layering when IME isn't attached to the app. Such as part of
+ // elevating the IME and windows above it's target above the docked divider in
+ // split-screen, or make the popupMenu to be above the IME when the parent window is the
+ // IME layering target in bubble/freeform mode.
+ if (mDisplayContent.shouldImeAttachedToApp()) {
return false;
}
@@ -6073,7 +6073,7 @@
}
if (mActivityRecord != null
- && mWmService.mAtmService.getTransitionController().isShellTransitionsEnabled()
+ && mTransitionController.isShellTransitionsEnabled()
&& mAttrs.type == TYPE_APPLICATION_STARTING) {
mWmService.mAtmService.mTaskSupervisor.getActivityMetricsLogger()
.notifyStartingWindowDrawn(mActivityRecord);
diff --git a/services/core/java/com/android/server/wm/WindowStateAnimator.java b/services/core/java/com/android/server/wm/WindowStateAnimator.java
index d4d8971..423b3a0 100644
--- a/services/core/java/com/android/server/wm/WindowStateAnimator.java
+++ b/services/core/java/com/android/server/wm/WindowStateAnimator.java
@@ -677,7 +677,7 @@
applyAnimationLocked(transit, true);
}
- if (mService.mAccessibilityController != null) {
+ if (mService.mAccessibilityController.hasCallbacks()) {
mService.mAccessibilityController.onWindowTransition(mWin, transit);
}
}
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index 147889d..a3ff6da 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -3618,6 +3618,9 @@
final CallerIdentity caller = getCallerIdentity();
Preconditions.checkCallAuthorization(hasFullCrossUsersPermission(caller, userHandle));
+ Preconditions.checkCallAuthorization(
+ isCallingFromPackage(adminReceiver.getPackageName(), caller.getUid())
+ || isSystemUid(caller));
synchronized (getLockObject()) {
ActiveAdmin administrator = getActiveAdminUncheckedLocked(adminReceiver, userHandle);
@@ -8353,7 +8356,8 @@
}
@Override
- public boolean setDeviceOwner(ComponentName admin, String ownerName, int userId) {
+ public boolean setDeviceOwner(ComponentName admin, String ownerName, int userId,
+ boolean setProfileOwnerOnCurrentUserIfNecessary) {
if (!mHasFeature) {
logMissingFeatureAction("Cannot set " + ComponentName.flattenToShortString(admin)
+ " as device owner for user " + userId);
@@ -8414,19 +8418,18 @@
mDeviceAdminServiceController.startServiceForOwner(
admin.getPackageName(), userId, "set-device-owner");
- Slogf.i(LOG_TAG, "Device owner set: %s on user %d", admin.flattenToShortString(),
- userId);
+ Slogf.i(LOG_TAG, "Device owner set: " + admin + " on user " + userId);
- if (mInjector.userManagerIsHeadlessSystemUserMode()) {
+ if (setProfileOwnerOnCurrentUserIfNecessary
+ && mInjector.userManagerIsHeadlessSystemUserMode()) {
int currentForegroundUser = getCurrentForegroundUserId();
- Slogf.i(LOG_TAG, "setDeviceOwner(): setting %s as profile owner on user %d",
- admin.flattenToShortString(), currentForegroundUser);
+ Slogf.i(LOG_TAG, "setDeviceOwner(): setting " + admin
+ + " as profile owner on user " + currentForegroundUser);
// Sets profile owner on current foreground user since
// the human user will complete the DO setup workflow from there.
- mInjector.binderWithCleanCallingIdentity(() -> manageUserUnchecked(
- /* deviceOwner= */ admin, /* profileOwner= */ admin,
+ manageUserUnchecked(/* deviceOwner= */ admin, /* profileOwner= */ admin,
/* managedUser= */ currentForegroundUser, /* adminExtras= */ null,
- /* showDisclaimer= */ false));
+ /* showDisclaimer= */ false);
}
return true;
}
@@ -10629,19 +10632,21 @@
}
}
final String adminPkg = admin.getPackageName();
- try {
- // Install the profile owner if not present.
- if (!mIPackageManager.isPackageAvailable(adminPkg, userId)) {
- mIPackageManager.installExistingPackageAsUser(adminPkg, userId,
- PackageManager.INSTALL_ALL_WHITELIST_RESTRICTED_PERMISSIONS,
- PackageManager.INSTALL_REASON_POLICY,
- /* allowlistedRestrictedPermissions= */ null);
+ mInjector.binderWithCleanCallingIdentity(() -> {
+ try {
+ // Install the profile owner if not present.
+ if (!mIPackageManager.isPackageAvailable(adminPkg, userId)) {
+ mIPackageManager.installExistingPackageAsUser(adminPkg, userId,
+ PackageManager.INSTALL_ALL_WHITELIST_RESTRICTED_PERMISSIONS,
+ PackageManager.INSTALL_REASON_POLICY,
+ /* allowlistedRestrictedPermissions= */ null);
+ }
+ } catch (RemoteException e) {
+ // Does not happen, same process
+ Slogf.wtf(LOG_TAG, e, "Failed to install admin package %s for user %d",
+ adminPkg, userId);
}
- } catch (RemoteException e) {
- // Does not happen, same process
- Slogf.wtf(LOG_TAG, e, "Failed to install admin package %s for user %d",
- adminPkg, userId);
- }
+ });
// Set admin.
setActiveAdmin(profileOwner, /* refreshing= */ true, userId);
@@ -10680,7 +10685,10 @@
}
}
- if (!mOwners.hasDeviceOwner() || !user.isFull() || user.isManagedProfile()) return;
+ if (!mOwners.hasDeviceOwner() || !user.isFull() || user.isManagedProfile()
+ || user.isGuest()) {
+ return;
+ }
if (mInjector.userManagerIsHeadlessSystemUserMode()) {
ComponentName admin = mOwners.getDeviceOwnerComponent();
@@ -12713,74 +12721,21 @@
// This method is called from AM with its lock held, so don't take the DPMS lock.
// b/29242568
- ComponentName profileOwner = mOwners.getProfileOwnerComponent(userId);
- if (profileOwner != null) {
- return DevicePolicyManagerService.this
- .createShowAdminSupportIntent(profileOwner, userId);
- }
-
- final Pair<Integer, ComponentName> deviceOwner =
- mOwners.getDeviceOwnerUserIdAndComponent();
- if (deviceOwner != null && deviceOwner.first == userId) {
- return DevicePolicyManagerService.this
- .createShowAdminSupportIntent(deviceOwner.second, userId);
- }
-
- // We're not specifying the device admin because there isn't one.
- if (useDefaultIfNoAdmin) {
- return DevicePolicyManagerService.this.createShowAdminSupportIntent(null, userId);
+ if (getEnforcingAdminAndUserDetailsInternal(userId, null) != null
+ || useDefaultIfNoAdmin) {
+ return DevicePolicyManagerService.this.createShowAdminSupportIntent(userId);
}
return null;
}
@Override
public Intent createUserRestrictionSupportIntent(int userId, String userRestriction) {
- final long ident = mInjector.binderClearCallingIdentity();
- try {
- final List<UserManager.EnforcingUser> sources = mUserManager
- .getUserRestrictionSources(userRestriction, UserHandle.of(userId));
- if (sources == null || sources.isEmpty()) {
- // The restriction is not enforced.
- return null;
- } else if (sources.size() > 1) {
- // In this case, we'll show an admin support dialog that does not
- // specify the admin.
- // TODO(b/128928355): if this restriction is enforced by multiple DPCs, return
- // the admin for the calling user.
- return DevicePolicyManagerService.this.createShowAdminSupportIntent(
- null, userId);
- }
- final UserManager.EnforcingUser enforcingUser = sources.get(0);
- final int sourceType = enforcingUser.getUserRestrictionSource();
- final int enforcingUserId = enforcingUser.getUserHandle().getIdentifier();
- if (sourceType == UserManager.RESTRICTION_SOURCE_PROFILE_OWNER) {
- // Restriction was enforced by PO
- final ComponentName profileOwner = mOwners.getProfileOwnerComponent(
- enforcingUserId);
- if (profileOwner != null) {
- return DevicePolicyManagerService.this.createShowAdminSupportIntent(
- profileOwner, enforcingUserId);
- }
- } else if (sourceType == UserManager.RESTRICTION_SOURCE_DEVICE_OWNER) {
- // Restriction was enforced by DO
- final Pair<Integer, ComponentName> deviceOwner =
- mOwners.getDeviceOwnerUserIdAndComponent();
- if (deviceOwner != null) {
- return DevicePolicyManagerService.this.createShowAdminSupportIntent(
- deviceOwner.second, deviceOwner.first);
- }
- } else if (sourceType == UserManager.RESTRICTION_SOURCE_SYSTEM) {
- /*
- * In this case, the user restriction is enforced by the system.
- * So we won't show an admin support intent, even if it is also
- * enforced by a profile/device owner.
- */
- return null;
- }
- } finally {
- mInjector.binderRestoreCallingIdentity(ident);
+ Intent intent = null;
+ if (getEnforcingAdminAndUserDetailsInternal(userId, userRestriction) != null) {
+ intent = DevicePolicyManagerService.this.createShowAdminSupportIntent(userId);
+ intent.putExtra(DevicePolicyManager.EXTRA_RESTRICTION, userRestriction);
}
- return null;
+ return intent;
}
@Override
@@ -13075,53 +13030,153 @@
}
}
- private Intent createShowAdminSupportIntent(ComponentName admin, int userId) {
+ private Intent createShowAdminSupportIntent(int userId) {
// This method is called with AMS lock held, so don't take DPMS lock
final Intent intent = new Intent(Settings.ACTION_SHOW_ADMIN_SUPPORT_DETAILS);
intent.putExtra(Intent.EXTRA_USER_ID, userId);
- intent.putExtra(DevicePolicyManager.EXTRA_DEVICE_ADMIN, admin);
intent.setFlags(FLAG_ACTIVITY_NEW_TASK);
return intent;
}
- @Override
- public Intent createAdminSupportIntent(String restriction) {
- Objects.requireNonNull(restriction);
- final CallerIdentity caller = getCallerIdentity();
- Intent intent = null;
- if (DevicePolicyManager.POLICY_DISABLE_CAMERA.equals(restriction) ||
- DevicePolicyManager.POLICY_DISABLE_SCREEN_CAPTURE.equals(restriction)) {
+ /**
+ * @param restriction The restriction enforced by admin. It could be any user restriction or
+ * policy like {@link DevicePolicyManager#POLICY_DISABLE_CAMERA} and
+ * {@link DevicePolicyManager#POLICY_DISABLE_SCREEN_CAPTURE}.
+ */
+ private Bundle getEnforcingAdminAndUserDetailsInternal(int userId, String restriction) {
+ Bundle result = null;
+ if (restriction == null) {
+ ComponentName profileOwner = mOwners.getProfileOwnerComponent(userId);
+ if (profileOwner != null) {
+ result = new Bundle();
+ result.putInt(Intent.EXTRA_USER_ID, userId);
+ result.putParcelable(DevicePolicyManager.EXTRA_DEVICE_ADMIN,
+ profileOwner);
+ return result;
+ }
+ final Pair<Integer, ComponentName> deviceOwner =
+ mOwners.getDeviceOwnerUserIdAndComponent();
+ if (deviceOwner != null && deviceOwner.first == userId) {
+ result = new Bundle();
+ result.putInt(Intent.EXTRA_USER_ID, userId);
+ result.putParcelable(DevicePolicyManager.EXTRA_DEVICE_ADMIN,
+ deviceOwner.second);
+ return result;
+ }
+ } else if (DevicePolicyManager.POLICY_DISABLE_CAMERA.equals(restriction)
+ || DevicePolicyManager.POLICY_DISABLE_SCREEN_CAPTURE.equals(restriction)) {
synchronized (getLockObject()) {
- final DevicePolicyData policy = getUserData(caller.getUserId());
+ final DevicePolicyData policy = getUserData(userId);
final int N = policy.mAdminList.size();
for (int i = 0; i < N; i++) {
final ActiveAdmin admin = policy.mAdminList.get(i);
if ((admin.disableCamera &&
- DevicePolicyManager.POLICY_DISABLE_CAMERA.equals(restriction)) ||
- (admin.disableScreenCapture && DevicePolicyManager
- .POLICY_DISABLE_SCREEN_CAPTURE.equals(restriction))) {
- intent = createShowAdminSupportIntent(admin.info.getComponent(),
- caller.getUserId());
- break;
+ DevicePolicyManager.POLICY_DISABLE_CAMERA.equals(restriction))
+ || (admin.disableScreenCapture && DevicePolicyManager
+ .POLICY_DISABLE_SCREEN_CAPTURE.equals(restriction))) {
+ result = new Bundle();
+ result.putInt(Intent.EXTRA_USER_ID, userId);
+ result.putParcelable(DevicePolicyManager.EXTRA_DEVICE_ADMIN,
+ admin.info.getComponent());
+ return result;
}
}
// For the camera, a device owner on a different user can disable it globally,
// so we need an additional check.
- if (intent == null
+ if (result == null
&& DevicePolicyManager.POLICY_DISABLE_CAMERA.equals(restriction)) {
final ActiveAdmin admin = getDeviceOwnerAdminLocked();
if (admin != null && admin.disableCamera) {
- intent = createShowAdminSupportIntent(admin.info.getComponent(),
- mOwners.getDeviceOwnerUserId());
+ result = new Bundle();
+ result.putInt(Intent.EXTRA_USER_ID, mOwners.getDeviceOwnerUserId());
+ result.putParcelable(DevicePolicyManager.EXTRA_DEVICE_ADMIN,
+ admin.info.getComponent());
+ return result;
}
}
}
} else {
- // if valid, |restriction| can only be a user restriction
- intent = mLocalService.createUserRestrictionSupportIntent(caller.getUserId(),
- restriction);
+ long ident = mInjector.binderClearCallingIdentity();
+ try {
+ List<UserManager.EnforcingUser> sources = mUserManager
+ .getUserRestrictionSources(restriction, UserHandle.of(userId));
+ if (sources == null || sources.isEmpty()) {
+ // The restriction is not enforced.
+ return null;
+ } else if (sources.size() > 1) {
+ // In this case, we'll show an admin support dialog that does not
+ // specify the admin.
+ // TODO(b/128928355): if this restriction is enforced by multiple DPCs, return
+ // the admin for the calling user.
+ result = new Bundle();
+ result.putInt(Intent.EXTRA_USER_ID, userId);
+ return result;
+ }
+ final UserManager.EnforcingUser enforcingUser = sources.get(0);
+ final int sourceType = enforcingUser.getUserRestrictionSource();
+ final int enforcingUserId = enforcingUser.getUserHandle().getIdentifier();
+ if (sourceType == UserManager.RESTRICTION_SOURCE_PROFILE_OWNER) {
+ // Restriction was enforced by PO
+ final ComponentName profileOwner = mOwners.getProfileOwnerComponent(
+ enforcingUserId);
+ if (profileOwner != null) {
+ result = new Bundle();
+ result.putInt(Intent.EXTRA_USER_ID, enforcingUserId);
+ result.putParcelable(DevicePolicyManager.EXTRA_DEVICE_ADMIN,
+ profileOwner);
+ return result;
+ }
+ } else if (sourceType == UserManager.RESTRICTION_SOURCE_DEVICE_OWNER) {
+ // Restriction was enforced by DO
+ final Pair<Integer, ComponentName> deviceOwner =
+ mOwners.getDeviceOwnerUserIdAndComponent();
+ if (deviceOwner != null) {
+ result = new Bundle();
+ result.putInt(Intent.EXTRA_USER_ID, deviceOwner.first);
+ result.putParcelable(DevicePolicyManager.EXTRA_DEVICE_ADMIN,
+ deviceOwner.second);
+ return result;
+ }
+ } else if (sourceType == UserManager.RESTRICTION_SOURCE_SYSTEM) {
+ /*
+ * In this case, the user restriction is enforced by the system.
+ * So we won't show an admin support intent, even if it is also
+ * enforced by a profile/device owner.
+ */
+ return null;
+ }
+ } finally {
+ mInjector.binderRestoreCallingIdentity(ident);
+ }
}
- if (intent != null) {
+ return null;
+ }
+
+ /**
+ * @param restriction The restriction enforced by admin. It could be any user restriction or
+ * policy like {@link DevicePolicyManager#POLICY_DISABLE_CAMERA} and
+ * {@link DevicePolicyManager#POLICY_DISABLE_SCREEN_CAPTURE}.
+ * @return Details of admin and user which enforced the restriction for the userId.
+ */
+ @Override
+ public Bundle getEnforcingAdminAndUserDetails(int userId, String restriction) {
+ Preconditions.checkCallAuthorization(isSystemUid(getCallerIdentity()));
+ return getEnforcingAdminAndUserDetailsInternal(userId, restriction);
+ }
+
+ /**
+ * @param restriction The restriction enforced by admin. It could be any user restriction or
+ * policy like {@link DevicePolicyManager#POLICY_DISABLE_CAMERA} and
+ * {@link DevicePolicyManager#POLICY_DISABLE_SCREEN_CAPTURE}.
+ */
+ @Override
+ public Intent createAdminSupportIntent(String restriction) {
+ Objects.requireNonNull(restriction);
+ final CallerIdentity caller = getCallerIdentity();
+ final int userId = caller.getUserId();
+ Intent intent = null;
+ if (getEnforcingAdminAndUserDetailsInternal(userId, restriction) != null) {
+ intent = createShowAdminSupportIntent(userId);
intent.putExtra(DevicePolicyManager.EXTRA_RESTRICTION, restriction);
}
return intent;
@@ -17429,7 +17484,8 @@
// TODO(b/178187130): Directly set DO and remove the check once silent provisioning is no
// longer used.
if (getDeviceOwnerComponent(/* callingUserOnly= */ true) == null) {
- return setDeviceOwner(adminComponent, name, userId);
+ return setDeviceOwner(adminComponent, name, userId,
+ /* setProfileOwnerOnCurrentUserIfNecessary= */ true);
}
return true;
}
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerServiceShellCommand.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerServiceShellCommand.java
index 85fe65c..e1d720c 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerServiceShellCommand.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerServiceShellCommand.java
@@ -46,11 +46,13 @@
private static final String USER_OPTION = "--user";
private static final String NAME_OPTION = "--name";
+ private static final String DO_ONLY_OPTION = "--device-owner-only";
private final DevicePolicyManagerService mService;
private int mUserId = UserHandle.USER_SYSTEM;
private String mName = "";
private ComponentName mComponent;
+ private boolean mSetDoOnly;
DevicePolicyManagerServiceShellCommand(DevicePolicyManagerService service) {
mService = Objects.requireNonNull(service);
@@ -130,8 +132,8 @@
pw.printf(" %s [ %s <USER_ID> | current ] <COMPONENT>\n",
CMD_SET_ACTIVE_ADMIN, USER_OPTION);
pw.printf(" Sets the given component as active admin for an existing user.\n\n");
- pw.printf(" %s [ %s <USER_ID> | current *EXPERIMENTAL* ] [ %s <NAME> ] "
- + "<COMPONENT>\n", CMD_SET_DEVICE_OWNER, USER_OPTION, NAME_OPTION);
+ pw.printf(" %s [ %s <USER_ID> | current *EXPERIMENTAL* ] [ %s <NAME> ] [ %s ]"
+ + "<COMPONENT>\n", CMD_SET_DEVICE_OWNER, USER_OPTION, NAME_OPTION, DO_ONLY_OPTION);
pw.printf(" Sets the given component as active admin, and its package as device owner."
+ "\n\n");
pw.printf(" %s [ %s <USER_ID> | current ] [ %s <NAME> ] <COMPONENT>\n",
@@ -254,7 +256,8 @@
mService.setActiveAdmin(mComponent, /* refreshing= */ true, mUserId);
try {
- if (!mService.setDeviceOwner(mComponent, mName, mUserId)) {
+ if (!mService.setDeviceOwner(mComponent, mName, mUserId,
+ /* setProfileOwnerOnCurrentUserIfNecessary= */ !mSetDoOnly)) {
throw new RuntimeException(
"Can't set package " + mComponent + " as device owner.");
}
@@ -351,6 +354,8 @@
if (mUserId == UserHandle.USER_CURRENT) {
mUserId = ActivityManager.getCurrentUser();
}
+ } else if (DO_ONLY_OPTION.equals(opt)) {
+ mSetDoOnly = true;
} else if (canHaveName && NAME_OPTION.equals(opt)) {
mName = getNextArgRequired();
} else {
diff --git a/services/tests/mockingservicestests/assets/SensorPrivacyServiceMockingTest/persisted_file_micMute_camMute.xml b/services/tests/mockingservicestests/assets/SensorPrivacyServiceMockingTest/persisted_file_micMute_camMute.xml
new file mode 100644
index 0000000..a4de08a
--- /dev/null
+++ b/services/tests/mockingservicestests/assets/SensorPrivacyServiceMockingTest/persisted_file_micMute_camMute.xml
@@ -0,0 +1,7 @@
+<?xml version='1.0' encoding='UTF-8' standalone='yes' ?>
+<sensor-privacy persistence-version="1" version="1">
+ <user id="0" enabled="false">
+ <individual-sensor-privacy sensor="1" enabled="true" />
+ <individual-sensor-privacy sensor="2" enabled="true" />
+ </user>
+</sensor-privacy>
diff --git a/services/tests/mockingservicestests/assets/SensorPrivacyServiceMockingTest/persisted_file_micMute_camUnmute.xml b/services/tests/mockingservicestests/assets/SensorPrivacyServiceMockingTest/persisted_file_micMute_camUnmute.xml
new file mode 100644
index 0000000..47649d7
--- /dev/null
+++ b/services/tests/mockingservicestests/assets/SensorPrivacyServiceMockingTest/persisted_file_micMute_camUnmute.xml
@@ -0,0 +1,7 @@
+<?xml version='1.0' encoding='UTF-8' standalone='yes' ?>
+<sensor-privacy persistence-version="1" version="1">
+ <user id="0" enabled="false">
+ <individual-sensor-privacy sensor="1" enabled="true" />
+ <individual-sensor-privacy sensor="2" enabled="false" />
+ </user>
+</sensor-privacy>
diff --git a/services/tests/mockingservicestests/assets/SensorPrivacyServiceMockingTest/persisted_file_micUnmute_camMute.xml b/services/tests/mockingservicestests/assets/SensorPrivacyServiceMockingTest/persisted_file_micUnmute_camMute.xml
new file mode 100644
index 0000000..4fd9ebf
--- /dev/null
+++ b/services/tests/mockingservicestests/assets/SensorPrivacyServiceMockingTest/persisted_file_micUnmute_camMute.xml
@@ -0,0 +1,7 @@
+<?xml version='1.0' encoding='UTF-8' standalone='yes' ?>
+<sensor-privacy persistence-version="1" version="1">
+ <user id="0" enabled="false">
+ <individual-sensor-privacy sensor="1" enabled="false" />
+ <individual-sensor-privacy sensor="2" enabled="true" />
+ </user>
+</sensor-privacy>
diff --git a/services/tests/mockingservicestests/assets/SensorPrivacyServiceMockingTest/persisted_file_micUnmute_camUnmute.xml b/services/tests/mockingservicestests/assets/SensorPrivacyServiceMockingTest/persisted_file_micUnmute_camUnmute.xml
new file mode 100644
index 0000000..e8f9edf
--- /dev/null
+++ b/services/tests/mockingservicestests/assets/SensorPrivacyServiceMockingTest/persisted_file_micUnmute_camUnmute.xml
@@ -0,0 +1,7 @@
+<?xml version='1.0' encoding='UTF-8' standalone='yes' ?>
+<sensor-privacy persistence-version="1" version="1">
+ <user id="0" enabled="false">
+ <individual-sensor-privacy sensor="1" enabled="false" />
+ <individual-sensor-privacy sensor="2" enabled="false" />
+ </user>
+</sensor-privacy>
diff --git a/services/tests/mockingservicestests/src/com/android/server/location/provider/StationaryThrottlingLocationProviderTest.java b/services/tests/mockingservicestests/src/com/android/server/location/provider/StationaryThrottlingLocationProviderTest.java
index 63996f0..4d6f49e 100644
--- a/services/tests/mockingservicestests/src/com/android/server/location/provider/StationaryThrottlingLocationProviderTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/location/provider/StationaryThrottlingLocationProviderTest.java
@@ -90,7 +90,7 @@
}
@Test
- public void testThrottle() {
+ public void testThrottle_stationaryExit() {
ProviderRequest request = new ProviderRequest.Builder().setIntervalMillis(50).build();
mProvider.getController().setRequest(request);
@@ -113,6 +113,29 @@
}
@Test
+ public void testThrottle_idleExit() {
+ ProviderRequest request = new ProviderRequest.Builder().setIntervalMillis(50).build();
+
+ mProvider.getController().setRequest(request);
+ verify(mDelegate).onSetRequest(request);
+
+ mDelegateProvider.reportLocation(createLocationResult("test_provider", mRandom));
+ verify(mListener, times(1)).onReportLocation(any(LocationResult.class));
+
+ mInjector.getDeviceIdleHelper().setIdle(true);
+ verify(mDelegate, never()).onSetRequest(ProviderRequest.EMPTY_REQUEST);
+
+ mInjector.getDeviceStationaryHelper().setStationary(true);
+ verify(mDelegate).onSetRequest(ProviderRequest.EMPTY_REQUEST);
+ verify(mListener, timeout(75).times(2)).onReportLocation(any(LocationResult.class));
+ verify(mListener, timeout(75).times(3)).onReportLocation(any(LocationResult.class));
+
+ mInjector.getDeviceIdleHelper().setIdle(false);
+ verify(mDelegate, times(2)).onSetRequest(request);
+ verify(mListener, after(75).times(3)).onReportLocation(any(LocationResult.class));
+ }
+
+ @Test
public void testThrottle_NoInitialLocation() {
ProviderRequest request = new ProviderRequest.Builder().setIntervalMillis(50).build();
diff --git a/services/tests/mockingservicestests/src/com/android/server/sensorprivacy/SensorPrivacyServiceMockingTest.java b/services/tests/mockingservicestests/src/com/android/server/sensorprivacy/SensorPrivacyServiceMockingTest.java
index ba79a76..38f01b5 100644
--- a/services/tests/mockingservicestests/src/com/android/server/sensorprivacy/SensorPrivacyServiceMockingTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/sensorprivacy/SensorPrivacyServiceMockingTest.java
@@ -16,12 +16,18 @@
package com.android.server.sensorprivacy;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.doAnswer;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyBoolean;
+import static org.mockito.ArgumentMatchers.anyInt;
+
import android.app.ActivityManager;
import android.app.ActivityTaskManager;
import android.app.AppOpsManager;
+import android.app.AppOpsManagerInternal;
import android.content.Context;
import android.content.pm.UserInfo;
import android.os.Environment;
@@ -33,8 +39,10 @@
import com.android.dx.mockito.inline.extended.ExtendedMockito;
import com.android.server.LocalServices;
import com.android.server.SensorPrivacyService;
+import com.android.server.SystemService;
import com.android.server.pm.UserManagerInternal;
+import org.junit.Assert;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
@@ -44,6 +52,7 @@
import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
+import java.util.concurrent.CompletableFuture;
@RunWith(AndroidTestingRunner.class)
public class SensorPrivacyServiceMockingTest {
@@ -63,10 +72,21 @@
public static final String PERSISTENCE_FILE6 =
String.format(PERSISTENCE_FILE_PATHS_TEMPLATE, 6);
+ public static final String PERSISTENCE_FILE_MIC_MUTE_CAM_MUTE =
+ "SensorPrivacyServiceMockingTest/persisted_file_micMute_camMute.xml";
+ public static final String PERSISTENCE_FILE_MIC_MUTE_CAM_UNMUTE =
+ "SensorPrivacyServiceMockingTest/persisted_file_micMute_camUnmute.xml";
+ public static final String PERSISTENCE_FILE_MIC_UNMUTE_CAM_MUTE =
+ "SensorPrivacyServiceMockingTest/persisted_file_micUnmute_camMute.xml";
+ public static final String PERSISTENCE_FILE_MIC_UNMUTE_CAM_UNMUTE =
+ "SensorPrivacyServiceMockingTest/persisted_file_micUnmute_camUnmute.xml";
+
private Context mContext;
@Mock
private AppOpsManager mMockedAppOpsManager;
@Mock
+ private AppOpsManagerInternal mMockedAppOpsManagerInternal;
+ @Mock
private UserManagerInternal mMockedUserManagerInternal;
@Mock
private ActivityManager mMockedActivityManager;
@@ -134,13 +154,103 @@
}
}
+ @Test
+ public void testServiceInit_AppOpsRestricted_micMute_camMute() throws IOException {
+ testServiceInit_AppOpsRestricted(PERSISTENCE_FILE_MIC_MUTE_CAM_MUTE, true, true);
+ }
+
+ @Test
+ public void testServiceInit_AppOpsRestricted_micMute_camUnmute() throws IOException {
+ testServiceInit_AppOpsRestricted(PERSISTENCE_FILE_MIC_MUTE_CAM_UNMUTE, true, false);
+ }
+
+ @Test
+ public void testServiceInit_AppOpsRestricted_micUnmute_camMute() throws IOException {
+ testServiceInit_AppOpsRestricted(PERSISTENCE_FILE_MIC_UNMUTE_CAM_MUTE, false, true);
+ }
+
+ @Test
+ public void testServiceInit_AppOpsRestricted_micUnmute_camUnmute() throws IOException {
+ testServiceInit_AppOpsRestricted(PERSISTENCE_FILE_MIC_UNMUTE_CAM_UNMUTE, false, false);
+ }
+
+ private void testServiceInit_AppOpsRestricted(String persistenceFileMicMuteCamMute,
+ boolean expectedMicState, boolean expectedCamState)
+ throws IOException {
+ MockitoSession mockitoSession = ExtendedMockito.mockitoSession()
+ .initMocks(this)
+ .strictness(Strictness.WARN)
+ .spyStatic(LocalServices.class)
+ .spyStatic(Environment.class)
+ .startMocking();
+
+ try {
+ mContext = InstrumentationRegistry.getInstrumentation().getContext();
+ spyOn(mContext);
+
+ doReturn(mMockedAppOpsManager).when(mContext).getSystemService(AppOpsManager.class);
+ doReturn(mMockedAppOpsManagerInternal)
+ .when(() -> LocalServices.getService(AppOpsManagerInternal.class));
+ doReturn(mMockedUserManagerInternal)
+ .when(() -> LocalServices.getService(UserManagerInternal.class));
+ doReturn(mMockedActivityManager).when(mContext).getSystemService(ActivityManager.class);
+ doReturn(mMockedActivityTaskManager)
+ .when(mContext).getSystemService(ActivityTaskManager.class);
+ doReturn(mMockedTelephonyManager).when(mContext).getSystemService(
+ TelephonyManager.class);
+
+ String dataDir = mContext.getApplicationInfo().dataDir;
+ doReturn(new File(dataDir)).when(() -> Environment.getDataSystemDirectory());
+
+ File onDeviceFile = new File(dataDir, "sensor_privacy.xml");
+ onDeviceFile.delete();
+
+ doReturn(new int[]{0}).when(mMockedUserManagerInternal).getUserIds();
+ doReturn(ExtendedMockito.mock(UserInfo.class)).when(mMockedUserManagerInternal)
+ .getUserInfo(0);
+
+ CompletableFuture<Boolean> micState = new CompletableFuture<>();
+ CompletableFuture<Boolean> camState = new CompletableFuture<>();
+ doAnswer(invocation -> {
+ int code = invocation.getArgument(0);
+ boolean restricted = invocation.getArgument(1);
+ if (code == AppOpsManager.OP_RECORD_AUDIO) {
+ micState.complete(restricted);
+ } else if (code == AppOpsManager.OP_CAMERA) {
+ camState.complete(restricted);
+ }
+ return null;
+ }).when(mMockedAppOpsManagerInternal).setGlobalRestriction(anyInt(), anyBoolean(),
+ any());
+
+ initServiceWithPersistenceFile(onDeviceFile, persistenceFileMicMuteCamMute, 0);
+
+ Assert.assertTrue(micState.join() == expectedMicState);
+ Assert.assertTrue(camState.join() == expectedCamState);
+
+ } finally {
+ mockitoSession.finishMocking();
+ }
+ }
+
private void initServiceWithPersistenceFile(File onDeviceFile,
String persistenceFilePath) throws IOException {
+ initServiceWithPersistenceFile(onDeviceFile, persistenceFilePath, -1);
+ }
+
+ private void initServiceWithPersistenceFile(File onDeviceFile,
+ String persistenceFilePath, int startingUserId) throws IOException {
if (persistenceFilePath != null) {
Files.copy(mContext.getAssets().open(persistenceFilePath),
onDeviceFile.toPath());
}
- new SensorPrivacyService(mContext);
+ SensorPrivacyService service = new SensorPrivacyService(mContext);
+ if (startingUserId != -1) {
+ SystemService.TargetUser mockedTargetUser =
+ ExtendedMockito.mock(SystemService.TargetUser.class);
+ doReturn(startingUserId).when(mockedTargetUser).getUserIdentifier();
+ service.onUserStarting(mockedTargetUser);
+ }
onDeviceFile.delete();
}
}
diff --git a/services/tests/servicestests/src/com/android/server/am/BatteryExternalStatsWorkerTest.java b/services/tests/servicestests/src/com/android/server/am/BatteryExternalStatsWorkerTest.java
index 4a67ec7..8a8a624 100644
--- a/services/tests/servicestests/src/com/android/server/am/BatteryExternalStatsWorkerTest.java
+++ b/services/tests/servicestests/src/com/android/server/am/BatteryExternalStatsWorkerTest.java
@@ -74,15 +74,20 @@
@Test
public void testTargetedEnergyConsumerQuerying() {
final int numCpuClusters = 4;
+ final int numDisplays = 5;
final int numOther = 3;
// Add some energy consumers used by BatteryExternalStatsWorker.
final IntArray tempAllIds = new IntArray();
- final int displayId = mPowerStatsInternal.addEnergyConsumer(EnergyConsumerType.DISPLAY, 0,
- "display");
- tempAllIds.add(displayId);
- mPowerStatsInternal.incrementEnergyConsumption(displayId, 12345);
+ final int[] displayIds = new int[numDisplays];
+ for (int i = 0; i < numDisplays; i++) {
+ displayIds[i] = mPowerStatsInternal.addEnergyConsumer(
+ EnergyConsumerType.DISPLAY, i, "display" + i);
+ tempAllIds.add(displayIds[i]);
+ mPowerStatsInternal.incrementEnergyConsumption(displayIds[i], 12345 + i);
+ }
+ Arrays.sort(displayIds);
final int wifiId = mPowerStatsInternal.addEnergyConsumer(EnergyConsumerType.WIFI, 0,
"wifi");
@@ -130,9 +135,13 @@
final EnergyConsumerResult[] displayResults =
mBatteryExternalStatsWorker.getMeasuredEnergyLocked(UPDATE_DISPLAY).getNow(null);
- // Results should only have the display energy consumer
- assertEquals(1, displayResults.length);
- assertEquals(displayId, displayResults[0].id);
+ // Results should only have the cpu cluster energy consumers
+ final int[] receivedDisplayIds = new int[displayResults.length];
+ for (int i = 0; i < displayResults.length; i++) {
+ receivedDisplayIds[i] = displayResults[i].id;
+ }
+ Arrays.sort(receivedDisplayIds);
+ assertArrayEquals(displayIds, receivedDisplayIds);
final EnergyConsumerResult[] wifiResults =
mBatteryExternalStatsWorker.getMeasuredEnergyLocked(UPDATE_WIFI).getNow(null);
diff --git a/services/tests/servicestests/src/com/android/server/am/MeasuredEnergySnapshotTest.java b/services/tests/servicestests/src/com/android/server/am/MeasuredEnergySnapshotTest.java
index 8c87506..a0cbcad 100644
--- a/services/tests/servicestests/src/com/android/server/am/MeasuredEnergySnapshotTest.java
+++ b/services/tests/servicestests/src/com/android/server/am/MeasuredEnergySnapshotTest.java
@@ -16,8 +16,6 @@
package com.android.server.am;
-import static com.android.server.am.MeasuredEnergySnapshot.UNAVAILABLE;
-
import static com.google.common.truth.Truth.assertThat;
import static org.junit.Assert.assertEquals;
@@ -120,7 +118,7 @@
// results0
MeasuredEnergyDeltaData delta = snapshot.updateAndGetDelta(RESULTS_0, VOLTAGE_0);
if (delta != null) { // null is fine here. If non-null, it better be uninteresting though.
- assertEquals(UNAVAILABLE, delta.displayChargeUC);
+ assertNull(delta.displayChargeUC);
assertNull(delta.otherTotalChargeUC);
assertNull(delta.otherUidChargesUC);
}
@@ -130,7 +128,7 @@
assertNotNull(delta);
long expectedChargeUC;
expectedChargeUC = calculateChargeConsumedUC(14_000, VOLTAGE_0, 24_000, VOLTAGE_1);
- assertEquals(expectedChargeUC, delta.displayChargeUC);
+ assertEquals(expectedChargeUC, delta.displayChargeUC[0]);
assertNotNull(delta.otherTotalChargeUC);
@@ -149,14 +147,14 @@
delta = snapshot.updateAndGetDelta(RESULTS_2, VOLTAGE_2);
assertNotNull(delta);
expectedChargeUC = calculateChargeConsumedUC(24_000, VOLTAGE_1, 36_000, VOLTAGE_2);
- assertEquals(expectedChargeUC, delta.displayChargeUC);
+ assertEquals(expectedChargeUC, delta.displayChargeUC[0]);
assertNull(delta.otherUidChargesUC);
assertNull(delta.otherTotalChargeUC);
// results3
delta = snapshot.updateAndGetDelta(RESULTS_3, VOLTAGE_3);
assertNotNull(delta);
- assertEquals(UNAVAILABLE, delta.displayChargeUC);
+ assertNull(delta.displayChargeUC);
assertNotNull(delta.otherTotalChargeUC);
@@ -183,7 +181,7 @@
delta = snapshot.updateAndGetDelta(RESULTS_4, VOLTAGE_4);
assertNotNull(delta);
expectedChargeUC = calculateChargeConsumedUC(36_000, VOLTAGE_2, 43_000, VOLTAGE_4);
- assertEquals(expectedChargeUC, delta.displayChargeUC);
+ assertEquals(expectedChargeUC, delta.displayChargeUC[0]);
assertNotNull(delta.otherTotalChargeUC);
expectedChargeUC = calculateChargeConsumedUC(190_000, VOLTAGE_3, 290_000, VOLTAGE_4);
@@ -210,7 +208,7 @@
// results0
MeasuredEnergyDeltaData delta = snapshot.updateAndGetDelta(RESULTS_0, VOLTAGE_0);
if (delta != null) { // null is fine here. If non-null, it better be uninteresting though.
- assertEquals(UNAVAILABLE, delta.displayChargeUC);
+ assertNull(delta.displayChargeUC);
assertNull(delta.otherTotalChargeUC);
assertNull(delta.otherUidChargesUC);
}
@@ -220,7 +218,7 @@
assertNotNull(delta);
final long expectedChargeUC =
calculateChargeConsumedUC(14_000, VOLTAGE_0, 24_000, VOLTAGE_1);
- assertEquals(expectedChargeUC, delta.displayChargeUC);
+ assertEquals(expectedChargeUC, delta.displayChargeUC[0]);
assertNull(delta.otherTotalChargeUC); // Although in the results, they're not in the idMap
assertNull(delta.otherUidChargesUC);
}
diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
index 63e4efc..3ac30d02 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
@@ -2960,9 +2960,6 @@
assertThat(intent.getAction()).isEqualTo(Settings.ACTION_SHOW_ADMIN_SUPPORT_DETAILS);
assertThat(intent.getIntExtra(Intent.EXTRA_USER_ID, -1))
.isEqualTo(UserHandle.getUserId(DpmMockContext.CALLER_SYSTEM_USER_UID));
- assertThat(
- (ComponentName) intent.getParcelableExtra(DevicePolicyManager.EXTRA_DEVICE_ADMIN))
- .isEqualTo(admin1);
assertThat(intent.getStringExtra(DevicePolicyManager.EXTRA_RESTRICTION))
.isEqualTo(UserManager.DISALLOW_ADJUST_VOLUME);
@@ -2999,7 +2996,7 @@
assertThat(intent.getStringExtra(DevicePolicyManager.EXTRA_RESTRICTION))
.isEqualTo(DevicePolicyManager.POLICY_DISABLE_CAMERA);
assertThat(intent.getIntExtra(Intent.EXTRA_USER_ID, -1))
- .isEqualTo(UserHandle.getUserId(DpmMockContext.CALLER_SYSTEM_USER_UID));
+ .isEqualTo(UserHandle.getUserId(DpmMockContext.CALLER_UID));
// ScreenCapture should not be disabled by device owner
intent = dpm.createAdminSupportIntent(DevicePolicyManager.POLICY_DISABLE_SCREEN_CAPTURE);
assertThat(intent).isNull();
diff --git a/services/tests/servicestests/src/com/android/server/display/DisplayModeDirectorTest.java b/services/tests/servicestests/src/com/android/server/display/DisplayModeDirectorTest.java
index 0dd5c61..418831f 100644
--- a/services/tests/servicestests/src/com/android/server/display/DisplayModeDirectorTest.java
+++ b/services/tests/servicestests/src/com/android/server/display/DisplayModeDirectorTest.java
@@ -26,6 +26,7 @@
import static android.hardware.display.DisplayManager.DeviceConfig.KEY_REFRESH_RATE_IN_LOW_ZONE;
import static com.android.server.display.DisplayModeDirector.Vote.INVALID_SIZE;
+import static com.android.server.display.HighBrightnessModeController.HBM_TRANSITION_POINT_INVALID;
import static com.google.common.truth.Truth.assertThat;
@@ -74,6 +75,7 @@
import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
+import com.android.internal.display.BrightnessSynchronizer;
import com.android.internal.util.Preconditions;
import com.android.internal.util.test.FakeSettingsProvider;
import com.android.internal.util.test.FakeSettingsProviderRule;
@@ -110,6 +112,7 @@
private static final boolean DEBUG = false;
private static final float FLOAT_TOLERANCE = 0.01f;
private static final int DISPLAY_ID = 0;
+ private static final float TRANSITION_POINT = 0.763f;
private Context mContext;
private FakesInjector mInjector;
@@ -751,19 +754,27 @@
director.start(sensorManager);
- ArgumentCaptor<SensorEventListener> listenerCaptor =
+ ArgumentCaptor<DisplayListener> displayListenerCaptor =
+ ArgumentCaptor.forClass(DisplayListener.class);
+ verify(mInjector).registerDisplayListener(displayListenerCaptor.capture(),
+ any(Handler.class),
+ eq(DisplayManager.EVENT_FLAG_DISPLAY_CHANGED
+ | DisplayManager.EVENT_FLAG_DISPLAY_BRIGHTNESS));
+ DisplayListener displayListener = displayListenerCaptor.getValue();
+
+ ArgumentCaptor<SensorEventListener> sensorListenerCaptor =
ArgumentCaptor.forClass(SensorEventListener.class);
Mockito.verify(sensorManager, Mockito.timeout(TimeUnit.SECONDS.toMillis(1)))
.registerListener(
- listenerCaptor.capture(),
+ sensorListenerCaptor.capture(),
eq(lightSensor),
anyInt(),
any(Handler.class));
- SensorEventListener listener = listenerCaptor.getValue();
+ SensorEventListener sensorListener = sensorListenerCaptor.getValue();
- setBrightness(10);
+ setBrightness(10, 10, displayListener);
// Sensor reads 20 lux,
- listener.onSensorChanged(TestUtils.createSensorEvent(lightSensor, 20 /*lux*/));
+ sensorListener.onSensorChanged(TestUtils.createSensorEvent(lightSensor, 20 /*lux*/));
Vote vote = director.getVote(Display.DEFAULT_DISPLAY, Vote.PRIORITY_FLICKER_REFRESH_RATE);
assertVoteForRefreshRate(vote, 90 /*fps*/);
@@ -771,9 +782,11 @@
assertThat(vote).isNotNull();
assertThat(vote.disableRefreshRateSwitching).isTrue();
- setBrightness(125);
+ // We expect DisplayModeDirector to act on BrightnessInfo.adjustedBrightness; set only this
+ // parameter to the necessary threshold
+ setBrightness(10, 125, displayListener);
// Sensor reads 1000 lux,
- listener.onSensorChanged(TestUtils.createSensorEvent(lightSensor, 1000 /*lux*/));
+ sensorListener.onSensorChanged(TestUtils.createSensorEvent(lightSensor, 1000 /*lux*/));
vote = director.getVote(Display.DEFAULT_DISPLAY, Vote.PRIORITY_FLICKER_REFRESH_RATE);
assertThat(vote).isNull();
@@ -799,6 +812,14 @@
director.start(sensorManager);
+ ArgumentCaptor<DisplayListener> displayListenerCaptor =
+ ArgumentCaptor.forClass(DisplayListener.class);
+ verify(mInjector).registerDisplayListener(displayListenerCaptor.capture(),
+ any(Handler.class),
+ eq(DisplayManager.EVENT_FLAG_DISPLAY_CHANGED
+ | DisplayManager.EVENT_FLAG_DISPLAY_BRIGHTNESS));
+ DisplayListener displayListener = displayListenerCaptor.getValue();
+
ArgumentCaptor<SensorEventListener> listenerCaptor =
ArgumentCaptor.forClass(SensorEventListener.class);
verify(sensorManager, Mockito.timeout(TimeUnit.SECONDS.toMillis(1)))
@@ -807,20 +828,22 @@
eq(lightSensor),
anyInt(),
any(Handler.class));
- SensorEventListener listener = listenerCaptor.getValue();
+ SensorEventListener sensorListener = listenerCaptor.getValue();
- setBrightness(100);
+ setBrightness(100, 100, displayListener);
// Sensor reads 2000 lux,
- listener.onSensorChanged(TestUtils.createSensorEvent(lightSensor, 2000));
+ sensorListener.onSensorChanged(TestUtils.createSensorEvent(lightSensor, 2000));
Vote vote = director.getVote(Display.DEFAULT_DISPLAY, Vote.PRIORITY_FLICKER_REFRESH_RATE);
assertThat(vote).isNull();
vote = director.getVote(Display.DEFAULT_DISPLAY, Vote.PRIORITY_FLICKER_REFRESH_RATE_SWITCH);
assertThat(vote).isNull();
- setBrightness(255);
+ // We expect DisplayModeDirector to act on BrightnessInfo.adjustedBrightness; set only this
+ // parameter to the necessary threshold
+ setBrightness(100, 255, displayListener);
// Sensor reads 9000 lux,
- listener.onSensorChanged(TestUtils.createSensorEvent(lightSensor, 9000));
+ sensorListener.onSensorChanged(TestUtils.createSensorEvent(lightSensor, 9000));
vote = director.getVote(Display.DEFAULT_DISPLAY, Vote.PRIORITY_FLICKER_REFRESH_RATE);
assertVoteForRefreshRate(vote, 60 /*fps*/);
@@ -1435,16 +1458,58 @@
Vote vote = director.getVote(DISPLAY_ID, Vote.PRIORITY_HIGH_BRIGHTNESS_MODE);
assertNull(vote);
- // Turn on HBM
+ // Turn on HBM, with brightness in the HBM range
when(mInjector.getBrightnessInfo(DISPLAY_ID)).thenReturn(
- new BrightnessInfo(0.45f, 0.0f, 1.0f, BrightnessInfo.HIGH_BRIGHTNESS_MODE_HDR));
+ new BrightnessInfo(TRANSITION_POINT + FLOAT_TOLERANCE, 0.0f, 1.0f,
+ BrightnessInfo.HIGH_BRIGHTNESS_MODE_HDR, TRANSITION_POINT));
+ listener.onDisplayChanged(DISPLAY_ID);
+ vote = director.getVote(DISPLAY_ID, Vote.PRIORITY_HIGH_BRIGHTNESS_MODE);
+ assertVoteForRefreshRate(vote, hbmRefreshRate);
+
+ // Turn on HBM, with brightness below the HBM range
+ when(mInjector.getBrightnessInfo(DISPLAY_ID)).thenReturn(
+ new BrightnessInfo(TRANSITION_POINT - FLOAT_TOLERANCE, 0.0f, 1.0f,
+ BrightnessInfo.HIGH_BRIGHTNESS_MODE_HDR, TRANSITION_POINT));
+ listener.onDisplayChanged(DISPLAY_ID);
+ vote = director.getVote(DISPLAY_ID, Vote.PRIORITY_HIGH_BRIGHTNESS_MODE);
+ assertNull(vote);
+
+ // Turn off HBM
+ when(mInjector.getBrightnessInfo(DISPLAY_ID)).thenReturn(
+ new BrightnessInfo(0.45f, 0.0f, 1.0f, BrightnessInfo.HIGH_BRIGHTNESS_MODE_OFF,
+ TRANSITION_POINT));
+ listener.onDisplayChanged(DISPLAY_ID);
+ vote = director.getVote(DISPLAY_ID, Vote.PRIORITY_HIGH_BRIGHTNESS_MODE);
+ assertNull(vote);
+
+ // Turn on HBM, with brightness in the HBM range
+ when(mInjector.getBrightnessInfo(DISPLAY_ID)).thenReturn(
+ new BrightnessInfo(TRANSITION_POINT + FLOAT_TOLERANCE, 0.0f, 1.0f,
+ BrightnessInfo.HIGH_BRIGHTNESS_MODE_HDR, TRANSITION_POINT));
listener.onDisplayChanged(DISPLAY_ID);
vote = director.getVote(DISPLAY_ID, Vote.PRIORITY_HIGH_BRIGHTNESS_MODE);
assertVoteForRefreshRate(vote, hbmRefreshRate);
// Turn off HBM
when(mInjector.getBrightnessInfo(DISPLAY_ID)).thenReturn(
- new BrightnessInfo(0.45f, 0.0f, 1.0f, BrightnessInfo.HIGH_BRIGHTNESS_MODE_OFF));
+ new BrightnessInfo(0.45f, 0.0f, 1.0f, BrightnessInfo.HIGH_BRIGHTNESS_MODE_OFF,
+ TRANSITION_POINT));
+ listener.onDisplayChanged(DISPLAY_ID);
+ vote = director.getVote(DISPLAY_ID, Vote.PRIORITY_HIGH_BRIGHTNESS_MODE);
+ assertNull(vote);
+
+ // Turn on HBM, with brightness below the HBM range
+ when(mInjector.getBrightnessInfo(DISPLAY_ID)).thenReturn(
+ new BrightnessInfo(TRANSITION_POINT - FLOAT_TOLERANCE, 0.0f, 1.0f,
+ BrightnessInfo.HIGH_BRIGHTNESS_MODE_HDR, TRANSITION_POINT));
+ listener.onDisplayChanged(DISPLAY_ID);
+ vote = director.getVote(DISPLAY_ID, Vote.PRIORITY_HIGH_BRIGHTNESS_MODE);
+ assertNull(vote);
+
+ // Turn off HBM
+ when(mInjector.getBrightnessInfo(DISPLAY_ID)).thenReturn(
+ new BrightnessInfo(0.45f, 0.0f, 1.0f, BrightnessInfo.HIGH_BRIGHTNESS_MODE_OFF,
+ TRANSITION_POINT));
listener.onDisplayChanged(DISPLAY_ID);
vote = director.getVote(DISPLAY_ID, Vote.PRIORITY_HIGH_BRIGHTNESS_MODE);
assertNull(vote);
@@ -1514,7 +1579,8 @@
// Turn on HBM
when(mInjector.getBrightnessInfo(DISPLAY_ID)).thenReturn(
- new BrightnessInfo(1.0f, 0.0f, 1.0f, BrightnessInfo.HIGH_BRIGHTNESS_MODE_SUNLIGHT));
+ new BrightnessInfo(1.0f, 0.0f, 1.0f, BrightnessInfo.HIGH_BRIGHTNESS_MODE_SUNLIGHT,
+ TRANSITION_POINT));
listener.onDisplayChanged(DISPLAY_ID);
vote = director.getVote(DISPLAY_ID, Vote.PRIORITY_HIGH_BRIGHTNESS_MODE);
assertVoteForRefreshRate(vote, initialRefreshRate);
@@ -1531,14 +1597,16 @@
// Turn off HBM
when(mInjector.getBrightnessInfo(DISPLAY_ID)).thenReturn(
- new BrightnessInfo(0.43f, 0.1f, 0.8f, BrightnessInfo.HIGH_BRIGHTNESS_MODE_OFF));
+ new BrightnessInfo(0.43f, 0.1f, 0.8f, BrightnessInfo.HIGH_BRIGHTNESS_MODE_OFF,
+ TRANSITION_POINT));
listener.onDisplayChanged(DISPLAY_ID);
vote = director.getVote(DISPLAY_ID, Vote.PRIORITY_HIGH_BRIGHTNESS_MODE);
assertNull(vote);
// Turn HBM on again and ensure the updated vote value stuck
when(mInjector.getBrightnessInfo(DISPLAY_ID)).thenReturn(
- new BrightnessInfo(1.0f, 0.0f, 1.0f, BrightnessInfo.HIGH_BRIGHTNESS_MODE_SUNLIGHT));
+ new BrightnessInfo(1.0f, 0.0f, 1.0f, BrightnessInfo.HIGH_BRIGHTNESS_MODE_SUNLIGHT,
+ TRANSITION_POINT));
listener.onDisplayChanged(DISPLAY_ID);
vote = director.getVote(DISPLAY_ID, Vote.PRIORITY_HIGH_BRIGHTNESS_MODE);
assertVoteForRefreshRate(vote, updatedRefreshRate);
@@ -1553,7 +1621,8 @@
// Turn off HBM
when(mInjector.getBrightnessInfo(DISPLAY_ID)).thenReturn(
- new BrightnessInfo(0.43f, 0.1f, 0.8f, BrightnessInfo.HIGH_BRIGHTNESS_MODE_OFF));
+ new BrightnessInfo(0.43f, 0.1f, 0.8f, BrightnessInfo.HIGH_BRIGHTNESS_MODE_OFF,
+ TRANSITION_POINT));
listener.onDisplayChanged(DISPLAY_ID);
vote = director.getVote(DISPLAY_ID, Vote.PRIORITY_HIGH_BRIGHTNESS_MODE);
assertNull(vote);
@@ -1584,14 +1653,82 @@
// Turn on HBM
when(mInjector.getBrightnessInfo(DISPLAY_ID)).thenReturn(
- new BrightnessInfo(1.0f, 0.0f, 1.0f, BrightnessInfo.HIGH_BRIGHTNESS_MODE_SUNLIGHT));
+ new BrightnessInfo(1.0f, 0.0f, 1.0f, BrightnessInfo.HIGH_BRIGHTNESS_MODE_SUNLIGHT,
+ TRANSITION_POINT));
listener.onDisplayChanged(DISPLAY_ID);
vote = director.getVote(DISPLAY_ID, Vote.PRIORITY_HIGH_BRIGHTNESS_MODE);
assertNull(vote);
// Turn off HBM
when(mInjector.getBrightnessInfo(DISPLAY_ID)).thenReturn(
- new BrightnessInfo(0.43f, 0.1f, 0.8f, BrightnessInfo.HIGH_BRIGHTNESS_MODE_OFF));
+ new BrightnessInfo(0.43f, 0.1f, 0.8f, BrightnessInfo.HIGH_BRIGHTNESS_MODE_OFF,
+ TRANSITION_POINT));
+ listener.onDisplayChanged(DISPLAY_ID);
+ vote = director.getVote(DISPLAY_ID, Vote.PRIORITY_HIGH_BRIGHTNESS_MODE);
+ assertNull(vote);
+ }
+
+ @Test
+ public void testHbmVoting_HbmUnsupported() {
+ DisplayModeDirector director =
+ createDirectorFromRefreshRateArray(new float[] {60.0f, 90.0f}, 0);
+ director.start(createMockSensorManager());
+
+ ArgumentCaptor<DisplayListener> captor =
+ ArgumentCaptor.forClass(DisplayListener.class);
+ verify(mInjector).registerDisplayListener(captor.capture(), any(Handler.class),
+ eq(DisplayManager.EVENT_FLAG_DISPLAY_BRIGHTNESS
+ | DisplayManager.EVENT_FLAG_DISPLAY_REMOVED));
+ DisplayListener listener = captor.getValue();
+
+ // Specify Limitation
+ when(mDisplayManagerInternalMock.getRefreshRateLimitations(DISPLAY_ID)).thenReturn(
+ List.of(new RefreshRateLimitation(
+ DisplayManagerInternal.REFRESH_RATE_LIMIT_HIGH_BRIGHTNESS_MODE,
+ 60.0f, 60.0f)));
+
+ // Verify that there is no HBM vote initially
+ Vote vote = director.getVote(DISPLAY_ID, Vote.PRIORITY_HIGH_BRIGHTNESS_MODE);
+ assertNull(vote);
+
+ // Turn on HBM when HBM is supported; expect a valid transition point and a vote.
+ when(mInjector.getBrightnessInfo(DISPLAY_ID)).thenReturn(
+ new BrightnessInfo(1.0f, 0.0f, 1.0f, BrightnessInfo.HIGH_BRIGHTNESS_MODE_SUNLIGHT,
+ TRANSITION_POINT));
+ listener.onDisplayChanged(DISPLAY_ID);
+ vote = director.getVote(DISPLAY_ID, Vote.PRIORITY_HIGH_BRIGHTNESS_MODE);
+ assertVoteForRefreshRate(vote, 60.0f);
+
+ // Turn off HBM
+ when(mInjector.getBrightnessInfo(DISPLAY_ID)).thenReturn(
+ new BrightnessInfo(1.0f, 0.0f, 1.0f, BrightnessInfo.HIGH_BRIGHTNESS_MODE_OFF,
+ TRANSITION_POINT));
+ listener.onDisplayChanged(DISPLAY_ID);
+ vote = director.getVote(DISPLAY_ID, Vote.PRIORITY_HIGH_BRIGHTNESS_MODE);
+ assertNull(vote);
+
+ // Turn on Sunlight HBM when HBM is unsupported; expect an invalid transition point and
+ // no vote.
+ when(mInjector.getBrightnessInfo(DISPLAY_ID)).thenReturn(
+ new BrightnessInfo(1.0f, 0.0f, 1.0f, BrightnessInfo.HIGH_BRIGHTNESS_MODE_SUNLIGHT,
+ HBM_TRANSITION_POINT_INVALID));
+ listener.onDisplayChanged(DISPLAY_ID);
+ vote = director.getVote(DISPLAY_ID, Vote.PRIORITY_HIGH_BRIGHTNESS_MODE);
+ assertNull(vote);
+
+ // Turn on HDR HBM when HBM is unsupported; expect an invalid transition point and
+ // no vote.
+ when(mInjector.getBrightnessInfo(DISPLAY_ID)).thenReturn(
+ new BrightnessInfo(1.0f, 0.0f, 1.0f, BrightnessInfo.HIGH_BRIGHTNESS_MODE_HDR,
+ HBM_TRANSITION_POINT_INVALID));
+ listener.onDisplayChanged(DISPLAY_ID);
+ vote = director.getVote(DISPLAY_ID, Vote.PRIORITY_HIGH_BRIGHTNESS_MODE);
+ assertNull(vote);
+
+ // Turn off HBM
+ when(mInjector.getBrightnessInfo(DISPLAY_ID)).thenReturn(
+ new BrightnessInfo(1.0f, 0.0f, 1.0f, BrightnessInfo.HIGH_BRIGHTNESS_MODE_OFF,
+ TRANSITION_POINT));
listener.onDisplayChanged(DISPLAY_ID);
vote = director.getVote(DISPLAY_ID, Vote.PRIORITY_HIGH_BRIGHTNESS_MODE);
assertNull(vote);
@@ -1600,7 +1737,7 @@
private void setHbmAndAssertRefreshRate(
DisplayModeDirector director, DisplayListener listener, int mode, float rr) {
when(mInjector.getBrightnessInfo(DISPLAY_ID))
- .thenReturn(new BrightnessInfo(1.0f, 0.0f, 1.0f, mode));
+ .thenReturn(new BrightnessInfo(1.0f, 0.0f, 1.0f, mode, TRANSITION_POINT));
listener.onDisplayChanged(DISPLAY_ID);
final Vote vote = director.getVote(DISPLAY_ID, Vote.PRIORITY_HIGH_BRIGHTNESS_MODE);
@@ -1679,7 +1816,8 @@
// Turn on HBM
when(mInjector.getBrightnessInfo(DISPLAY_ID)).thenReturn(
- new BrightnessInfo(1.0f, 0.0f, 1.0f, BrightnessInfo.HIGH_BRIGHTNESS_MODE_SUNLIGHT));
+ new BrightnessInfo(1.0f, 0.0f, 1.0f, BrightnessInfo.HIGH_BRIGHTNESS_MODE_SUNLIGHT,
+ TRANSITION_POINT));
listener.onDisplayChanged(DISPLAY_ID);
vote = director.getVote(DISPLAY_ID, Vote.PRIORITY_HIGH_BRIGHTNESS_MODE);
assertVoteForRefreshRate(vote, 60.f);
@@ -1834,11 +1972,14 @@
}
}
- private void setBrightness(int brightness) {
- Settings.System.putInt(mContext.getContentResolver(), Settings.System.SCREEN_BRIGHTNESS,
- brightness);
- mInjector.notifyBrightnessChanged();
- waitForIdleSync();
+ private void setBrightness(int brightness, int adjustedBrightness, DisplayListener listener) {
+ float floatBri = BrightnessSynchronizer.brightnessIntToFloat(brightness);
+ float floatAdjBri = BrightnessSynchronizer.brightnessIntToFloat(adjustedBrightness);
+
+ when(mInjector.getBrightnessInfo(DISPLAY_ID)).thenReturn(
+ new BrightnessInfo(floatBri, floatAdjBri, 0.0f, 1.0f,
+ BrightnessInfo.HIGH_BRIGHTNESS_MODE_OFF, TRANSITION_POINT));
+ listener.onDisplayChanged(DISPLAY_ID);
}
private void setPeakRefreshRate(float fps) {
@@ -1902,27 +2043,6 @@
}
@Override
- public void registerBrightnessObserver(@NonNull ContentResolver cr,
- @NonNull ContentObserver observer) {
- if (mBrightnessObserver != null) {
- throw new IllegalStateException("Tried to register a second brightness observer");
- }
- mBrightnessObserver = observer;
- }
-
- @Override
- public void unregisterBrightnessObserver(@NonNull ContentResolver cr,
- @NonNull ContentObserver observer) {
- mBrightnessObserver = null;
- }
-
- void notifyBrightnessChanged() {
- if (mBrightnessObserver != null) {
- mBrightnessObserver.dispatchChange(false /*selfChange*/, DISPLAY_BRIGHTNESS_URI);
- }
- }
-
- @Override
public void registerPeakRefreshRateObserver(@NonNull ContentResolver cr,
@NonNull ContentObserver observer) {
mPeakRefreshRateObserver = observer;
diff --git a/services/tests/servicestests/src/com/android/server/display/HighBrightnessModeControllerTest.java b/services/tests/servicestests/src/com/android/server/display/HighBrightnessModeControllerTest.java
index cc3591c8..aca8632 100644
--- a/services/tests/servicestests/src/com/android/server/display/HighBrightnessModeControllerTest.java
+++ b/services/tests/servicestests/src/com/android/server/display/HighBrightnessModeControllerTest.java
@@ -20,6 +20,8 @@
import static android.hardware.display.BrightnessInfo.HIGH_BRIGHTNESS_MODE_OFF;
import static android.hardware.display.BrightnessInfo.HIGH_BRIGHTNESS_MODE_SUNLIGHT;
+import static com.android.server.display.HighBrightnessModeController.HBM_TRANSITION_POINT_INVALID;
+
import static org.junit.Assert.assertEquals;
import static org.mockito.Mockito.eq;
import static org.mockito.Mockito.spy;
@@ -124,6 +126,7 @@
mInjectorMock, mHandler, DISPLAY_WIDTH, DISPLAY_HEIGHT, mDisplayToken, DEFAULT_MIN,
DEFAULT_MAX, null, () -> {}, mContextSpy);
assertState(hbmc, DEFAULT_MIN, DEFAULT_MAX, HIGH_BRIGHTNESS_MODE_OFF);
+ assertEquals(hbmc.getTransitionPoint(), HBM_TRANSITION_POINT_INVALID, 0.0f);
}
@Test
@@ -135,6 +138,7 @@
hbmc.setAutoBrightnessEnabled(true);
hbmc.onAmbientLuxChange(MINIMUM_LUX - 1); // below allowed range
assertState(hbmc, DEFAULT_MIN, DEFAULT_MAX, HIGH_BRIGHTNESS_MODE_OFF);
+ assertEquals(hbmc.getTransitionPoint(), HBM_TRANSITION_POINT_INVALID, 0.0f);
}
@Test
diff --git a/services/tests/servicestests/src/com/android/server/pm/permission/LegacyPermissionManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/pm/permission/LegacyPermissionManagerServiceTest.java
index acd3fca..3261dfa 100644
--- a/services/tests/servicestests/src/com/android/server/pm/permission/LegacyPermissionManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/permission/LegacyPermissionManagerServiceTest.java
@@ -125,7 +125,7 @@
public void checkDeviceIdentifierAccess_hasPrivilegedPermission_returnsGranted() {
// Apps with the READ_PRIVILEGED_PHONE_STATE permission should have access to device
// identifiers.
- setupCheckDeviceIdentifierAccessTest(SYSTEM_PID, SYSTEM_UID);
+ setupCheckDeviceIdentifierAccessTest(SYSTEM_PID, SYSTEM_UID, APP_UID);
when(mInjector.checkPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE,
APP_PID, APP_UID)).thenReturn(PackageManager.PERMISSION_GRANTED);
@@ -140,7 +140,7 @@
public void checkDeviceIdentifierAccess_hasAppOp_returnsGranted() {
// Apps that have been granted the READ_DEVICE_IDENTIFIERS appop should have access to
// device identifiers.
- setupCheckDeviceIdentifierAccessTest(SYSTEM_PID, SYSTEM_UID);
+ setupCheckDeviceIdentifierAccessTest(SYSTEM_PID, SYSTEM_UID, APP_UID);
when(mAppOpsManager.noteOpNoThrow(eq(AppOpsManager.OPSTR_READ_DEVICE_IDENTIFIERS),
eq(APP_UID), eq(mPackageName), any(), any())).thenReturn(
AppOpsManager.MODE_ALLOWED);
@@ -156,7 +156,7 @@
public void checkDeviceIdentifierAccess_hasDpmAccess_returnsGranted() {
// Apps that pass a DevicePolicyManager device / profile owner check should have access to
// device identifiers.
- setupCheckDeviceIdentifierAccessTest(SYSTEM_PID, SYSTEM_UID);
+ setupCheckDeviceIdentifierAccessTest(SYSTEM_PID, SYSTEM_UID, APP_UID);
when(mDevicePolicyManager.hasDeviceIdentifierAccess(mPackageName, APP_PID,
APP_UID)).thenReturn(true);
@@ -236,7 +236,7 @@
// both the permission and the appop must be granted. If the permission is granted but the
// appop is not then AppOpsManager#MODE_IGNORED should be returned to indicate that this
// should be a silent failure.
- setupCheckPhoneNumberAccessTest(SYSTEM_PID, SYSTEM_UID);
+ setupCheckPhoneNumberAccessTest(SYSTEM_PID, SYSTEM_UID, APP_UID);
setPackageTargetSdk(Build.VERSION_CODES.Q);
grantPermissionAndAppop(android.Manifest.permission.READ_PHONE_STATE, null);
@@ -256,7 +256,7 @@
// Apps targeting R+ with just the READ_PHONE_STATE permission granted should not have
// access to the phone number; PERMISSION_DENIED should be returned both with and without
// the appop granted since this check should be skipped for target SDK R+.
- setupCheckPhoneNumberAccessTest(SYSTEM_PID, SYSTEM_UID);
+ setupCheckPhoneNumberAccessTest(SYSTEM_PID, SYSTEM_UID, APP_UID);
grantPermissionAndAppop(android.Manifest.permission.READ_PHONE_STATE, null);
int resultWithoutAppop = mLegacyPermissionManagerService.checkPhoneNumberAccess(
@@ -319,12 +319,79 @@
assertEquals(PackageManager.PERMISSION_GRANTED, resultWithAppop);
}
+ @Test
+ public void checkPhoneNumberAccess_providedUidDoesNotMatchPackageUid_throwsException()
+ throws Exception {
+ // An app can directly interact with one of the services that accepts a package name and
+ // returns a protected resource via a direct binder transact. This app could then provide
+ // the name of another app that targets pre-R, then determine if the app is installed based
+ // on whether the service throws an exception or not. While the app can provide the package
+ // name of another app, it cannot specify the package uid which is passed to the
+ // LegacyPermissionManager using Binder#getCallingUid. Ultimately this uid should then be
+ // compared against the actual uid of the package to ensure information about packages
+ // installed on the device is not leaked.
+ setupCheckPhoneNumberAccessTest(SYSTEM_PID, SYSTEM_UID, APP_UID + 1);
+
+ assertThrows(SecurityException.class,
+ () -> mLegacyPermissionManagerService.checkPhoneNumberAccess(mPackageName,
+ CHECK_PHONE_NUMBER_MESSAGE, null, APP_PID, APP_UID));
+ }
+
+ @Test
+ public void checkPhoneNumberAccess_nullPackageNameSystemUid_returnsGranted() throws Exception {
+ // The platform can pass a null package name when checking if the platform itself has
+ // access to the device phone number(s) / identifier(s). This test ensures if a null package
+ // is provided, then the package uid check is skipped and the test is based on whether the
+ // the provided uid / pid has been granted the privileged permission.
+ setupCheckPhoneNumberAccessTest(SYSTEM_PID, SYSTEM_UID, -1);
+ when(mInjector.checkPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE,
+ SYSTEM_PID, SYSTEM_UID)).thenReturn(PackageManager.PERMISSION_GRANTED);
+
+ int result = mLegacyPermissionManagerService.checkPhoneNumberAccess(null,
+ CHECK_PHONE_NUMBER_MESSAGE, null, SYSTEM_PID, SYSTEM_UID);
+
+ assertEquals(PackageManager.PERMISSION_GRANTED, result);
+ }
+
+ @Test
+ public void checkPhoneNumberAccess_systemUidMismatchPackageUid_returnsGranted()
+ throws Exception {
+ // When the platform is checking device phone number / identifier access checks for other
+ // components on the platform, a uid less than the first application UID is provided; this
+ // test verifies the package uid check is skipped and access is still granted with the
+ // privileged permission.
+ int telephonyUid = SYSTEM_UID + 1;
+ int telephonyPid = SYSTEM_PID + 1;
+ setupCheckPhoneNumberAccessTest(SYSTEM_PID, SYSTEM_UID, -1);
+ when(mInjector.checkPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE,
+ telephonyPid, telephonyUid)).thenReturn(PackageManager.PERMISSION_GRANTED);
+
+ int result = mLegacyPermissionManagerService.checkPhoneNumberAccess(mPackageName,
+ CHECK_PHONE_NUMBER_MESSAGE, null, telephonyPid, telephonyUid);
+
+ assertEquals(PackageManager.PERMISSION_GRANTED, result);
+ }
+
/**
* Configures device identifier access tests to fail; tests verifying access should individually
* set an access check to succeed to verify access when that condition is met.
*/
private void setupCheckDeviceIdentifierAccessTest(int callingPid, int callingUid) {
- setupAccessTest(callingPid, callingUid);
+ setupCheckDeviceIdentifierAccessTest(callingPid, callingUid, callingUid);
+ }
+
+ /**
+ * Configures device identifier access tests to fail; tests verifying access should individually
+ * set an access check to succeed to verify access when that condition is met.
+ *
+ * <p>To prevent leaking package information, access checks for package UIDs >= {@link
+ * android.os.Process#FIRST_APPLICATION_UID} must ensure the provided uid matches the uid of
+ * the package being checked; to ensure this check is successful, this method accepts the
+ * {@code packageUid} to be used for the package being checked.
+ */
+ public void setupCheckDeviceIdentifierAccessTest(int callingPid, int callingUid,
+ int packageUid) {
+ setupAccessTest(callingPid, callingUid, packageUid);
when(mDevicePolicyManager.hasDeviceIdentifierAccess(anyString(), anyInt(),
anyInt())).thenReturn(false);
@@ -333,11 +400,26 @@
}
/**
- * Configures phone number access tests to fail; tests verifying access should individually set
- * an access check to succeed to verify access when that condition is met.
+ * Configures phone number access tests to fail; tests verifying access should individually
+ * set an access check to succeed to verify access when that condition is set.
+ *
*/
private void setupCheckPhoneNumberAccessTest(int callingPid, int callingUid) throws Exception {
- setupAccessTest(callingPid, callingUid);
+ setupCheckPhoneNumberAccessTest(callingPid, callingUid, callingUid);
+ }
+
+ /**
+ * Configures phone number access tests to fail; tests verifying access should individually set
+ * an access check to succeed to verify access when that condition is met.
+ *
+ * <p>To prevent leaking package information, access checks for package UIDs >= {@link
+ * android.os.Process#FIRST_APPLICATION_UID} must ensure the provided uid matches the uid of
+ * the package being checked; to ensure this check is successful, this method accepts the
+ * {@code packageUid} to be used for the package being checked.
+ */
+ private void setupCheckPhoneNumberAccessTest(int callingPid, int callingUid, int packageUid)
+ throws Exception {
+ setupAccessTest(callingPid, callingUid, packageUid);
setPackageTargetSdk(Build.VERSION_CODES.R);
}
@@ -345,9 +427,10 @@
* Configures the common mocks for any access tests using the provided {@code callingPid}
* and {@code callingUid}.
*/
- private void setupAccessTest(int callingPid, int callingUid) {
+ private void setupAccessTest(int callingPid, int callingUid, int packageUid) {
when(mInjector.getCallingPid()).thenReturn(callingPid);
when(mInjector.getCallingUid()).thenReturn(callingUid);
+ when(mInjector.getPackageUidForUser(anyString(), anyInt())).thenReturn(packageUid);
when(mInjector.checkPermission(anyString(), anyInt(), anyInt())).thenReturn(
PackageManager.PERMISSION_DENIED);
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityStartInterceptorTest.java b/services/tests/wmtests/src/com/android/server/wm/ActivityStartInterceptorTest.java
index a3ad09a..c103bc6 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityStartInterceptorTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityStartInterceptorTest.java
@@ -28,6 +28,7 @@
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.ArgumentMatchers.nullable;
+import android.annotation.Nullable;
import android.app.ActivityManagerInternal;
import android.app.KeyguardManager;
import android.app.admin.DevicePolicyManagerInternal;
@@ -42,6 +43,7 @@
import android.os.UserManager;
import android.platform.test.annotations.Presubmit;
import android.testing.DexmakerShareClassLoaderRule;
+import android.util.SparseArray;
import androidx.test.filters.SmallTest;
@@ -64,7 +66,7 @@
* Unit tests for {@link ActivityStartInterceptorTest}.
*
* Build/Install/Run:
- * atest WmTests:ActivityStartInterceptorTest
+ * atest WmTests:ActivityStartInterceptorTest
*/
@SmallTest
@Presubmit
@@ -114,6 +116,9 @@
private ActivityStartInterceptor mInterceptor;
private ActivityInfo mAInfo = new ActivityInfo();
+ private SparseArray<ActivityInterceptorCallback> mActivityInterceptorCallbacks =
+ new SparseArray<>();
+
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
@@ -157,6 +162,9 @@
TEST_USER_ID, TEST_PACKAGE_NAME, LOCK_TASK_LAUNCH_MODE_DEFAULT))
.thenReturn(true);
+ // Mock the activity start callbacks
+ when(mService.getActivityInterceptorCallbacks()).thenReturn(mActivityInterceptorCallbacks);
+
// Initialise activity info
mAInfo.applicationInfo = new ApplicationInfo();
mAInfo.packageName = mAInfo.applicationInfo.packageName = TEST_PACKAGE_NAME;
@@ -285,4 +293,38 @@
// THEN calling intercept returns false
assertFalse(mInterceptor.intercept(null, null, mAInfo, null, null, 0, 0, null));
}
+
+ public void addMockInterceptorCallback(@Nullable Intent intent) {
+ int size = mActivityInterceptorCallbacks.size();
+ mActivityInterceptorCallbacks.put(size, new ActivityInterceptorCallback() {
+ @Override
+ public Intent intercept(ActivityInterceptorInfo info) {
+ return intent;
+ }
+ });
+ }
+
+ @Test
+ public void testInterceptionCallback_singleCallback() {
+ addMockInterceptorCallback(new Intent("android.test.foo"));
+
+ assertTrue(mInterceptor.intercept(null, null, mAInfo, null, null, 0, 0, null));
+ assertEquals("android.test.foo", mInterceptor.mIntent.getAction());
+ }
+
+ @Test
+ public void testInterceptionCallback_singleCallbackReturnsNull() {
+ addMockInterceptorCallback(null);
+
+ assertFalse(mInterceptor.intercept(null, null, mAInfo, null, null, 0, 0, null));
+ }
+
+ @Test
+ public void testInterceptionCallback_fallbackToSecondCallback() {
+ addMockInterceptorCallback(null);
+ addMockInterceptorCallback(new Intent("android.test.second"));
+
+ assertTrue(mInterceptor.intercept(null, null, mAInfo, null, null, 0, 0, null));
+ assertEquals("android.test.second", mInterceptor.mIntent.getAction());
+ }
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityTaskManagerServiceTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityTaskManagerServiceTests.java
index 5de4fcb..5d1a068 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityTaskManagerServiceTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityTaskManagerServiceTests.java
@@ -26,6 +26,8 @@
import static com.android.dx.mockito.inline.extended.ExtendedMockito.mock;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
+import static com.android.server.wm.ActivityInterceptorCallback.FIRST_ORDERED_ID;
+import static com.android.server.wm.ActivityInterceptorCallback.LAST_ORDERED_ID;
import static com.android.server.wm.ActivityRecord.State.PAUSED;
import static com.android.server.wm.ActivityRecord.State.PAUSING;
import static com.android.server.wm.ActivityRecord.State.RESUMED;
@@ -42,12 +44,14 @@
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.when;
+import android.annotation.Nullable;
import android.app.Activity;
import android.app.ActivityManager;
import android.app.IApplicationThread;
import android.app.PictureInPictureParams;
import android.app.servertransaction.ClientTransaction;
import android.app.servertransaction.EnterPipRequestedItem;
+import android.content.Intent;
import android.content.pm.ActivityInfo;
import android.content.pm.ApplicationInfo;
import android.content.res.Configuration;
@@ -846,7 +850,64 @@
return wpc;
}
+ @Test(expected = IllegalArgumentException.class)
+ public void testRegisterActivityStartInterceptor_IndexTooSmall() {
+ mAtm.mInternal.registerActivityStartInterceptor(FIRST_ORDERED_ID - 1,
+ new ActivityInterceptorCallback() {
+ @Nullable
+ @Override
+ public Intent intercept(ActivityInterceptorInfo info) {
+ return null;
+ }
+ });
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void testRegisterActivityStartInterceptor_IndexTooLarge() {
+ mAtm.mInternal.registerActivityStartInterceptor(LAST_ORDERED_ID + 1,
+ new ActivityInterceptorCallback() {
+ @Nullable
+ @Override
+ public Intent intercept(ActivityInterceptorInfo info) {
+ return null;
+ }
+ });
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void testRegisterActivityStartInterceptor_DuplicateId() {
+ mAtm.mInternal.registerActivityStartInterceptor(FIRST_ORDERED_ID,
+ new ActivityInterceptorCallback() {
+ @Nullable
+ @Override
+ public Intent intercept(ActivityInterceptorInfo info) {
+ return null;
+ }
+ });
+ mAtm.mInternal.registerActivityStartInterceptor(FIRST_ORDERED_ID,
+ new ActivityInterceptorCallback() {
+ @Nullable
+ @Override
+ public Intent intercept(ActivityInterceptorInfo info) {
+ return null;
+ }
+ });
+ }
+
+ @Test
+ public void testRegisterActivityStartInterceptor() {
+ assertEquals(0, mAtm.getActivityInterceptorCallbacks().size());
+
+ mAtm.mInternal.registerActivityStartInterceptor(FIRST_ORDERED_ID,
+ new ActivityInterceptorCallback() {
+ @Nullable
+ @Override
+ public Intent intercept(ActivityInterceptorInfo info) {
+ return null;
+ }
+ });
+
+ assertEquals(1, mAtm.getActivityInterceptorCallbacks().size());
+ assertTrue(mAtm.getActivityInterceptorCallbacks().contains(FIRST_ORDERED_ID));
+ }
}
-
-
-
diff --git a/services/tests/wmtests/src/com/android/server/wm/AppTransitionControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/AppTransitionControllerTest.java
index 5fa76bb..82140f4 100644
--- a/services/tests/wmtests/src/com/android/server/wm/AppTransitionControllerTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/AppTransitionControllerTest.java
@@ -922,6 +922,40 @@
verify(mDisplayContent.mAppTransition).goodToGo(anyInt(), any());
}
+ @Test
+ public void testTransitionGoodToGoForTaskFragments_detachedApp() {
+ final TaskFragmentOrganizer organizer = new TaskFragmentOrganizer(Runnable::run);
+ final Task task = createTask(mDisplayContent);
+ final TaskFragment changeTaskFragment =
+ createTaskFragmentWithEmbeddedActivity(task, organizer);
+ final TaskFragment emptyTaskFragment = new TaskFragmentBuilder(mAtm)
+ .setParentTask(task)
+ .setOrganizer(organizer)
+ .build();
+ changeTaskFragment.getTopMostActivity().allDrawn = true;
+ // To make sure that having a detached activity won't cause any issue.
+ final ActivityRecord detachedActivity = createActivityRecord(task);
+ detachedActivity.removeImmediately();
+ assertNull(detachedActivity.getRootTask());
+ spyOn(mDisplayContent.mAppTransition);
+ spyOn(emptyTaskFragment);
+
+ prepareAndTriggerAppTransition(
+ null /* openingActivity */, detachedActivity, changeTaskFragment);
+
+ // Transition not ready because there is an empty non-finishing TaskFragment.
+ verify(mDisplayContent.mAppTransition, never()).goodToGo(anyInt(), any());
+
+ doReturn(true).when(emptyTaskFragment).hasChild();
+ emptyTaskFragment.remove(false /* withTransition */, "test");
+
+ mDisplayContent.mAppTransitionController.handleAppTransitionReady();
+
+ // Transition ready because the empty (no running activity) TaskFragment is requested to be
+ // removed.
+ verify(mDisplayContent.mAppTransition).goodToGo(anyInt(), any());
+ }
+
/** Registers remote animation for the organizer. */
private void setupTaskFragmentRemoteAnimation(TaskFragmentOrganizer organizer,
RemoteAnimationAdapter adapter) {
diff --git a/services/tests/wmtests/src/com/android/server/wm/AppTransitionTests.java b/services/tests/wmtests/src/com/android/server/wm/AppTransitionTests.java
index 405d714..fb8bc7b 100644
--- a/services/tests/wmtests/src/com/android/server/wm/AppTransitionTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/AppTransitionTests.java
@@ -35,6 +35,8 @@
import static com.android.dx.mockito.inline.extended.ExtendedMockito.doNothing;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
import static com.android.server.wm.WindowContainer.POSITION_TOP;
import static org.junit.Assert.assertEquals;
@@ -42,6 +44,7 @@
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.mock;
import android.graphics.Rect;
import android.os.Binder;
@@ -54,6 +57,7 @@
import android.view.IRemoteAnimationRunner;
import android.view.RemoteAnimationAdapter;
import android.view.RemoteAnimationTarget;
+import android.view.SurfaceControl;
import android.view.WindowManager;
import android.window.ITaskFragmentOrganizer;
import android.window.TaskFragmentOrganizer;
@@ -397,7 +401,9 @@
@Test
public void testActivityRecordReparentToTaskFragment() {
final ActivityRecord activity = createActivityRecord(mDc);
+ final SurfaceControl activityLeash = mock(SurfaceControl.class);
activity.setVisibility(true);
+ activity.setSurfaceControl(activityLeash);
final Task task = activity.getTask();
// Add a TaskFragment of half of the Task size.
@@ -412,15 +418,20 @@
final Rect taskBounds = new Rect();
task.getBounds(taskBounds);
taskFragment.setBounds(0, 0, taskBounds.right / 2, taskBounds.bottom);
+ spyOn(taskFragment);
assertTrue(mDc.mChangingContainers.isEmpty());
assertFalse(mDc.mAppTransition.isTransitionSet());
// Schedule app transition when reparent activity to a TaskFragment of different size.
+ final Rect startBounds = new Rect(activity.getBounds());
activity.reparent(taskFragment, POSITION_TOP);
- assertTrue(mDc.mChangingContainers.contains(activity));
+ // It should transit at TaskFragment level with snapshot on the activity surface.
+ verify(taskFragment).initializeChangeTransition(activity.getBounds(), activityLeash);
+ assertTrue(mDc.mChangingContainers.contains(taskFragment));
assertTrue(mDc.mAppTransition.containsTransitRequest(TRANSIT_CHANGE));
+ assertEquals(startBounds, taskFragment.mSurfaceFreezer.mFreezeBounds);
}
private class TestRemoteAnimationRunner implements IRemoteAnimationRunner {
diff --git a/services/tests/wmtests/src/com/android/server/wm/LetterboxTest.java b/services/tests/wmtests/src/com/android/server/wm/LetterboxTest.java
index 78946fc..1e86522 100644
--- a/services/tests/wmtests/src/com/android/server/wm/LetterboxTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/LetterboxTest.java
@@ -63,7 +63,7 @@
mLetterbox = new Letterbox(mSurfaces, StubTransaction::new,
() -> mAreCornersRounded, () -> Color.valueOf(mColor),
() -> mHasWallpaperBackground, () -> mBlurRadius, () -> mDarkScrimAlpha,
- /* doubleTapCallback= */ () -> {});
+ /* doubleTapCallback= */ x -> {});
mTransaction = spy(StubTransaction.class);
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowOrganizerTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowOrganizerTests.java
index 17ae2e8..a482bda 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowOrganizerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowOrganizerTests.java
@@ -80,6 +80,7 @@
import android.window.ITaskOrganizer;
import android.window.IWindowContainerTransactionCallback;
import android.window.StartingWindowInfo;
+import android.window.StartingWindowRemovalInfo;
import android.window.TaskAppearedInfo;
import android.window.WindowContainerToken;
import android.window.WindowContainerTransaction;
@@ -779,8 +780,7 @@
@Override
public void addStartingWindow(StartingWindowInfo info, IBinder appToken) { }
@Override
- public void removeStartingWindow(int taskId, SurfaceControl leash, Rect frame,
- boolean playRevealAnimation) { }
+ public void removeStartingWindow(StartingWindowRemovalInfo removalInfo) { }
@Override
public void copySplashScreenView(int taskId) { }
@Override
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java
index b17ea5e..e6ad68a 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java
@@ -18,6 +18,7 @@
import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
+import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW;
import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
import static android.view.InsetsState.ITYPE_IME;
@@ -835,8 +836,7 @@
WindowState sameTokenWindow = createWindow(null, TYPE_BASE_APPLICATION, mAppWindow.mToken,
"SameTokenWindow");
mDisplayContent.setImeLayeringTarget(mAppWindow);
- sameTokenWindow.mActivityRecord.getRootTask().setWindowingMode(
- WINDOWING_MODE_SPLIT_SCREEN_PRIMARY);
+ sameTokenWindow.mActivityRecord.getRootTask().setWindowingMode(WINDOWING_MODE_MULTI_WINDOW);
assertTrue(sameTokenWindow.needsRelativeLayeringToIme());
sameTokenWindow.removeImmediately();
assertFalse(sameTokenWindow.needsRelativeLayeringToIme());
@@ -848,8 +848,7 @@
WindowState sameTokenWindow = createWindow(null, TYPE_APPLICATION_STARTING,
mAppWindow.mToken, "SameTokenWindow");
mDisplayContent.setImeLayeringTarget(mAppWindow);
- sameTokenWindow.mActivityRecord.getRootTask().setWindowingMode(
- WINDOWING_MODE_SPLIT_SCREEN_PRIMARY);
+ sameTokenWindow.mActivityRecord.getRootTask().setWindowingMode(WINDOWING_MODE_MULTI_WINDOW);
assertFalse(sameTokenWindow.needsRelativeLayeringToIme());
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java b/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java
index 8ec1bd6c..b2d4eea 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java
@@ -77,7 +77,6 @@
import android.content.Intent;
import android.content.pm.ActivityInfo;
import android.content.pm.ApplicationInfo;
-import android.graphics.Rect;
import android.hardware.display.DisplayManager;
import android.os.Build;
import android.os.Bundle;
@@ -101,6 +100,7 @@
import android.view.WindowManager.DisplayImePolicy;
import android.window.ITransitionPlayer;
import android.window.StartingWindowInfo;
+import android.window.StartingWindowRemovalInfo;
import android.window.TaskFragmentOrganizer;
import android.window.TransitionInfo;
import android.window.TransitionRequestInfo;
@@ -1457,12 +1457,11 @@
}
}
@Override
- public void removeStartingWindow(int taskId, SurfaceControl leash, Rect frame,
- boolean playRevealAnimation) {
+ public void removeStartingWindow(StartingWindowRemovalInfo removalInfo) {
synchronized (mWMService.mGlobalLock) {
- final IBinder appToken = mTaskAppMap.get(taskId);
+ final IBinder appToken = mTaskAppMap.get(removalInfo.taskId);
if (appToken != null) {
- mTaskAppMap.remove(taskId);
+ mTaskAppMap.remove(removalInfo.taskId);
final ActivityRecord activity = mWMService.mRoot.getActivityRecord(
appToken);
WindowState win = mAppWindowMap.remove(appToken);
diff --git a/services/tests/wmtests/src/com/android/server/wm/ZOrderingTests.java b/services/tests/wmtests/src/com/android/server/wm/ZOrderingTests.java
index d967891..22ea3d5 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ZOrderingTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ZOrderingTests.java
@@ -20,6 +20,7 @@
import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
+import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW;
import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_SECONDARY;
@@ -37,6 +38,7 @@
import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
import static com.android.server.wm.WindowStateAnimator.PRESERVED_SURFACE_LAYER;
import static com.google.common.truth.Truth.assertThat;
@@ -493,4 +495,27 @@
assertZOrderGreaterThan(mTransaction, mNavBarWindow.mToken.getSurfaceControl(),
mDisplayContent.getImeContainer().getSurfaceControl());
}
+
+ @Test
+ public void testPopupWindowAndParentIsImeTarget_expectHigherThanIme_inMultiWindow() {
+ // Simulate the app window is in multi windowing mode and being IME target
+ mAppWindow.getConfiguration().windowConfiguration.setWindowingMode(
+ WINDOWING_MODE_MULTI_WINDOW);
+ mDisplayContent.setImeLayeringTarget(mAppWindow);
+ mDisplayContent.setImeInputTarget(mAppWindow);
+
+ // Create a popupWindow
+ assertWindowHigher(mImeWindow, mAppWindow);
+ final WindowState popupWindow = createWindow(mAppWindow, TYPE_APPLICATION_PANEL,
+ mDisplayContent, "PopupWindow");
+ spyOn(popupWindow);
+
+ mDisplayContent.assignChildLayers(mTransaction);
+
+ // Verify the surface layer of the popupWindow should higher than IME
+ verify(popupWindow).needsRelativeLayeringToIme();
+ assertThat(popupWindow.needsRelativeLayeringToIme()).isTrue();
+ assertZOrderGreaterThan(mTransaction, popupWindow.getSurfaceControl(),
+ mDisplayContent.getImeContainer().getSurfaceControl());
+ }
}
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/CommonAssertions.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/CommonAssertions.kt
index 2cc1943..562a0bd 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/CommonAssertions.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/CommonAssertions.kt
@@ -17,22 +17,29 @@
@file:JvmName("CommonAssertions")
package com.android.server.wm.flicker
-import android.content.ComponentName
import com.android.server.wm.flicker.helpers.WindowUtils
-import com.android.server.wm.traces.parser.windowmanager.WindowManagerStateHelper
+import com.android.server.wm.traces.common.FlickerComponentName
-val LAUNCHER_COMPONENT = ComponentName("com.google.android.apps.nexuslauncher",
+val LAUNCHER_COMPONENT = FlickerComponentName("com.google.android.apps.nexuslauncher",
"com.google.android.apps.nexuslauncher.NexusLauncherActivity")
+/**
+ * Checks that [FlickerComponentName.STATUS_BAR] window is visible and above the app windows in
+ * all WM trace entries
+ */
fun FlickerTestParameter.statusBarWindowIsVisible() {
assertWm {
- this.isAboveAppWindowVisible(WindowManagerStateHelper.STATUS_BAR_COMPONENT)
+ this.isAboveAppWindowVisible(FlickerComponentName.STATUS_BAR)
}
}
+/**
+ * Checks that [FlickerComponentName.NAV_BAR] window is visible and above the app windows in
+ * all WM trace entries
+ */
fun FlickerTestParameter.navBarWindowIsVisible() {
assertWm {
- this.isAboveAppWindowVisible(WindowManagerStateHelper.NAV_BAR_COMPONENT)
+ this.isAboveAppWindowVisible(FlickerComponentName.NAV_BAR)
}
}
@@ -69,21 +76,29 @@
}
}
+/**
+ * Checks that [FlickerComponentName.NAV_BAR] layer is visible at the start and end of the SF
+ * trace
+ */
fun FlickerTestParameter.navBarLayerIsVisible() {
assertLayersStart {
- this.isVisible(WindowManagerStateHelper.NAV_BAR_COMPONENT)
+ this.isVisible(FlickerComponentName.NAV_BAR)
}
assertLayersEnd {
- this.isVisible(WindowManagerStateHelper.NAV_BAR_COMPONENT)
+ this.isVisible(FlickerComponentName.NAV_BAR)
}
}
+/**
+ * Checks that [FlickerComponentName.STATUS_BAR] layer is visible at the start and end of the SF
+ * trace
+ */
fun FlickerTestParameter.statusBarLayerIsVisible() {
assertLayersStart {
- this.isVisible(WindowManagerStateHelper.STATUS_BAR_COMPONENT)
+ this.isVisible(FlickerComponentName.STATUS_BAR)
}
assertLayersEnd {
- this.isVisible(WindowManagerStateHelper.STATUS_BAR_COMPONENT)
+ this.isVisible(FlickerComponentName.STATUS_BAR)
}
}
@@ -96,10 +111,10 @@
val endingPos = WindowUtils.getNavigationBarPosition(endRotation)
assertLayersStart {
- this.visibleRegion(WindowManagerStateHelper.NAV_BAR_COMPONENT).coversExactly(startingPos)
+ this.visibleRegion(FlickerComponentName.NAV_BAR).coversExactly(startingPos)
}
assertLayersEnd {
- this.visibleRegion(WindowManagerStateHelper.NAV_BAR_COMPONENT).coversExactly(endingPos)
+ this.visibleRegion(FlickerComponentName.NAV_BAR).coversExactly(endingPos)
}
}
@@ -112,10 +127,10 @@
val endingPos = WindowUtils.getStatusBarPosition(endRotation)
assertLayersStart {
- this.visibleRegion(WindowManagerStateHelper.STATUS_BAR_COMPONENT).coversExactly(startingPos)
+ this.visibleRegion(FlickerComponentName.STATUS_BAR).coversExactly(startingPos)
}
assertLayersEnd {
- this.visibleRegion(WindowManagerStateHelper.STATUS_BAR_COMPONENT).coversExactly(endingPos)
+ this.visibleRegion(FlickerComponentName.STATUS_BAR).coversExactly(endingPos)
}
}
@@ -132,15 +147,15 @@
* (useful mostly for app launch)
*/
fun FlickerTestParameter.replacesLayer(
- originalLayer: ComponentName,
- newLayer: ComponentName,
+ originalLayer: FlickerComponentName,
+ newLayer: FlickerComponentName,
ignoreSnapshot: Boolean = false
) {
assertLayers {
val assertion = this.isVisible(originalLayer)
if (ignoreSnapshot) {
assertion.then()
- .isVisible(WindowManagerStateHelper.SNAPSHOT_COMPONENT, isOptional = true)
+ .isVisible(FlickerComponentName.SNAPSHOT, isOptional = true)
}
assertion.then().isVisible(newLayer)
}
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppBackButtonTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppBackButtonTest.kt
index 90c851d..9b34853 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppBackButtonTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppBackButtonTest.kt
@@ -23,7 +23,7 @@
import com.android.server.wm.flicker.FlickerParametersRunnerFactory
import com.android.server.wm.flicker.FlickerTestParameter
import com.android.server.wm.flicker.FlickerTestParameterFactory
-import com.android.server.wm.flicker.annotation.Group1
+import com.android.server.wm.flicker.annotation.Group4
import com.android.server.wm.flicker.dsl.FlickerBuilder
import org.junit.FixMethodOrder
import org.junit.Test
@@ -33,13 +33,38 @@
/**
* Test app closes by pressing back button
+ *
* To run this test: `atest FlickerTests:CloseAppBackButtonTest`
+ *
+ * Actions:
+ * Make sure no apps are running on the device
+ * Launch an app [testApp] and wait animation to complete
+ * Press back button
+ *
+ * To run only the presubmit assertions add: `--
+ * --module-arg FlickerTests:exclude-annotation:androidx.test.filters.FlakyTest
+ * --module-arg FlickerTests:include-annotation:android.platform.test.annotations.Presubmit`
+ *
+ * To run only the postsubmit assertions add: `--
+ * --module-arg FlickerTests:exclude-annotation:androidx.test.filters.FlakyTest
+ * --module-arg FlickerTests:include-annotation:android.platform.test.annotations.Postsubmit`
+ *
+ * To run only the flaky assertions add: `--
+ * --module-arg FlickerTests:include-annotation:androidx.test.filters.FlakyTest`
+ *
+ * Notes:
+ * 1. Some default assertions (e.g., nav bar, status bar and screen covered)
+ * are inherited [CloseAppTransition]
+ * 2. Part of the test setup occurs automatically via
+ * [com.android.server.wm.flicker.TransitionRunnerWithRules],
+ * including configuring navigation mode, initial orientation and ensuring no
+ * apps are running before setup
*/
@RequiresDevice
@RunWith(Parameterized::class)
@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-@Group1
+@Group4
class CloseAppBackButtonTest(testSpec: FlickerTestParameter) : CloseAppTransition(testSpec) {
override val transition: FlickerBuilder.(Map<String, Any?>) -> Unit
get() = {
@@ -50,14 +75,23 @@
}
}
+ /** {@inheritDoc} */
@FlakyTest
@Test
override fun navBarLayerRotatesAndScales() = super.navBarLayerRotatesAndScales()
+ /** {@inheritDoc} */
@Postsubmit
+ @Test
override fun navBarLayerIsVisible() = super.navBarLayerIsVisible()
companion object {
+ /**
+ * Creates the test configurations.
+ *
+ * See [FlickerTestParameterFactory.getConfigNonRotationTests] for configuring
+ * repetitions, screen orientation and navigation modes.
+ */
@Parameterized.Parameters(name = "{0}")
@JvmStatic
fun getParams(): List<FlickerTestParameter> {
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppHomeButtonTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppHomeButtonTest.kt
index e8391ed..e380794 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppHomeButtonTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppHomeButtonTest.kt
@@ -22,7 +22,7 @@
import com.android.server.wm.flicker.FlickerParametersRunnerFactory
import com.android.server.wm.flicker.FlickerTestParameter
import com.android.server.wm.flicker.FlickerTestParameterFactory
-import com.android.server.wm.flicker.annotation.Group1
+import com.android.server.wm.flicker.annotation.Group4
import com.android.server.wm.flicker.dsl.FlickerBuilder
import org.junit.FixMethodOrder
import org.junit.Test
@@ -31,14 +31,39 @@
import org.junit.runners.Parameterized
/**
- * Test app closes by pressing home button.
+ * Test app closes by pressing home button
+ *
* To run this test: `atest FlickerTests:CloseAppHomeButtonTest`
+ *
+ * Actions:
+ * Make sure no apps are running on the device
+ * Launch an app [testApp] and wait animation to complete
+ * Press home button
+ *
+ * To run only the presubmit assertions add: `--
+ * --module-arg FlickerTests:exclude-annotation:androidx.test.filters.FlakyTest
+ * --module-arg FlickerTests:include-annotation:android.platform.test.annotations.Presubmit`
+ *
+ * To run only the postsubmit assertions add: `--
+ * --module-arg FlickerTests:exclude-annotation:androidx.test.filters.FlakyTest
+ * --module-arg FlickerTests:include-annotation:android.platform.test.annotations.Postsubmit`
+ *
+ * To run only the flaky assertions add: `--
+ * --module-arg FlickerTests:include-annotation:androidx.test.filters.FlakyTest`
+ *
+ * Notes:
+ * 1. Some default assertions (e.g., nav bar, status bar and screen covered)
+ * are inherited [CloseAppTransition]
+ * 2. Part of the test setup occurs automatically via
+ * [com.android.server.wm.flicker.TransitionRunnerWithRules],
+ * including configuring navigation mode, initial orientation and ensuring no
+ * apps are running before setup
*/
@RequiresDevice
@RunWith(Parameterized::class)
@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-@Group1
+@Group4
class CloseAppHomeButtonTest(testSpec: FlickerTestParameter) : CloseAppTransition(testSpec) {
override val transition: FlickerBuilder.(Map<String, Any?>) -> Unit
get() = {
@@ -49,14 +74,23 @@
}
}
+ /** {@inheritDoc} */
@FlakyTest
@Test
override fun navBarLayerRotatesAndScales() = super.navBarLayerRotatesAndScales()
+ /** {@inheritDoc} */
@Postsubmit
+ @Test
override fun navBarLayerIsVisible() = super.navBarLayerIsVisible()
companion object {
+ /**
+ * Creates the test configurations.
+ *
+ * See [FlickerTestParameterFactory.getConfigNonRotationTests] for configuring
+ * repetitions, screen orientation and navigation modes.
+ */
@Parameterized.Parameters(name = "{0}")
@JvmStatic
fun getParams(): Collection<FlickerTestParameter> {
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppTransition.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppTransition.kt
index 1efb6da..0482619 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppTransition.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppTransition.kt
@@ -62,6 +62,10 @@
}
}
+ /**
+ * Entry point for the test runner. It will use this method to initialize and cache
+ * flicker executions
+ */
@FlickerBuilderProvider
fun buildFlicker(): FlickerBuilder {
return FlickerBuilder(instrumentation).apply {
@@ -69,42 +73,64 @@
}
}
+ /**
+ * Checks that the navigation bar window is visible during the whole transition
+ */
@Presubmit
@Test
open fun navBarWindowIsVisible() {
testSpec.navBarWindowIsVisible()
}
+ /**
+ * Checks that the status bar window is visible during the whole transition
+ */
@Presubmit
@Test
open fun statusBarWindowIsVisible() {
testSpec.statusBarWindowIsVisible()
}
+ /**
+ * Checks that the navigation bar layer is visible during the whole transition
+ */
@Presubmit
@Test
open fun navBarLayerIsVisible() {
testSpec.navBarLayerIsVisible()
}
+ /**
+ * Checks that the status bar layer is visible during the whole transition
+ */
@Presubmit
@Test
open fun statusBarLayerIsVisible() {
testSpec.statusBarLayerIsVisible()
}
+ /**
+ * Checks the position of the navigation bar at the start and end of the transition
+ */
@Presubmit
@Test
open fun navBarLayerRotatesAndScales() {
testSpec.navBarLayerRotatesAndScales(testSpec.config.startRotation, Surface.ROTATION_0)
}
+ /**
+ * Checks the position of the status bar at the start and end of the transition
+ */
@Presubmit
@Test
open fun statusBarLayerRotatesScales() {
testSpec.statusBarLayerRotatesScales(testSpec.config.startRotation, Surface.ROTATION_0)
}
+ /**
+ * Checks that all windows that are visible on the trace, are visible for at least 2
+ * consecutive entries.
+ */
@Presubmit
@Test
open fun visibleWindowsShownMoreThanOneConsecutiveEntry() {
@@ -113,6 +139,10 @@
}
}
+ /**
+ * Checks that all layers that are visible on the trace, are visible for at least 2
+ * consecutive entries.
+ */
@Presubmit
@Test
open fun visibleLayersShownMoreThanOneConsecutiveEntry() {
@@ -121,10 +151,17 @@
}
}
+ /**
+ * Checks that all parts of the screen are covered during the transition
+ */
@Presubmit
@Test
open fun entireScreenCovered() = testSpec.entireScreenCovered()
+ /**
+ * Checks that [testApp] is the top visible app window at the start of the transition and
+ * that it is replaced by [LAUNCHER_COMPONENT] during the transition
+ */
@Presubmit
@Test
open fun launcherReplacesAppWindowAsTopWindow() {
@@ -135,19 +172,26 @@
}
}
+ /**
+ * Checks that [LAUNCHER_COMPONENT] is invisible at the start of the transition and that
+ * it becomes visible during the transition
+ */
@Presubmit
@Test
open fun launcherWindowBecomesVisible() {
testSpec.assertWm {
- this.isAppWindowInvisible(LAUNCHER_COMPONENT)
+ this.isAppWindowNotOnTop(LAUNCHER_COMPONENT)
.then()
.isAppWindowOnTop(LAUNCHER_COMPONENT)
}
}
+ /**
+ * Checks that [LAUNCHER_COMPONENT] layer becomes visible when [testApp] becomes invisible
+ */
@Presubmit
@Test
open fun launcherLayerReplacesApp() {
testSpec.replacesLayer(testApp.component, LAUNCHER_COMPONENT)
}
-}
+}
\ No newline at end of file
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/FlickerExtensions.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/FlickerExtensions.kt
index fad25b4..75900df 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/FlickerExtensions.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/FlickerExtensions.kt
@@ -14,6 +14,7 @@
* limitations under the License.
*/
+@file:JvmName("FlickerExtensions")
package com.android.server.wm.flicker.helpers
import com.android.server.wm.flicker.Flicker
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/ImeAppAutoFocusHelper.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/ImeAppAutoFocusHelper.kt
index bd7c185..0b1748a 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/ImeAppAutoFocusHelper.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/ImeAppAutoFocusHelper.kt
@@ -17,9 +17,10 @@
package com.android.server.wm.flicker.helpers
import android.app.Instrumentation
-import android.content.ComponentName
import androidx.test.uiautomator.UiDevice
import com.android.server.wm.flicker.testapp.ActivityOptions
+import com.android.server.wm.traces.common.FlickerComponentName
+import com.android.server.wm.traces.parser.toFlickerComponent
import com.android.server.wm.traces.parser.windowmanager.WindowManagerStateHelper
class ImeAppAutoFocusHelper @JvmOverloads constructor(
@@ -27,7 +28,8 @@
private val rotation: Int,
private val imePackageName: String = IME_PACKAGE,
launcherName: String = ActivityOptions.IME_ACTIVITY_AUTO_FOCUS_LAUNCHER_NAME,
- component: ComponentName = ActivityOptions.IME_ACTIVITY_AUTO_FOCUS_COMPONENT_NAME
+ component: FlickerComponentName =
+ ActivityOptions.IME_ACTIVITY_AUTO_FOCUS_COMPONENT_NAME.toFlickerComponent()
) : ImeAppHelper(instr, launcherName, component) {
override fun openIME(
device: UiDevice,
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/ImeAppHelper.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/ImeAppHelper.kt
index d224af9..1c2164a 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/ImeAppHelper.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/ImeAppHelper.kt
@@ -17,19 +17,21 @@
package com.android.server.wm.flicker.helpers
import android.app.Instrumentation
-import android.content.ComponentName
import android.support.test.launcherhelper.ILauncherStrategy
import android.support.test.launcherhelper.LauncherStrategyFactory
import androidx.test.uiautomator.By
import androidx.test.uiautomator.UiDevice
import androidx.test.uiautomator.Until
import com.android.server.wm.flicker.testapp.ActivityOptions
+import com.android.server.wm.traces.common.FlickerComponentName
+import com.android.server.wm.traces.parser.toFlickerComponent
import com.android.server.wm.traces.parser.windowmanager.WindowManagerStateHelper
open class ImeAppHelper @JvmOverloads constructor(
instr: Instrumentation,
launcherName: String = ActivityOptions.IME_ACTIVITY_LAUNCHER_NAME,
- component: ComponentName = ActivityOptions.IME_ACTIVITY_COMPONENT_NAME,
+ component: FlickerComponentName =
+ ActivityOptions.IME_ACTIVITY_COMPONENT_NAME.toFlickerComponent(),
launcherStrategy: ILauncherStrategy = LauncherStrategyFactory
.getInstance(instr)
.launcherStrategy
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/NonResizeableAppHelper.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/NonResizeableAppHelper.kt
index 3074e28..f7ca5ce 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/NonResizeableAppHelper.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/NonResizeableAppHelper.kt
@@ -17,15 +17,17 @@
package com.android.server.wm.flicker.helpers
import android.app.Instrumentation
-import android.content.ComponentName
import android.support.test.launcherhelper.ILauncherStrategy
import android.support.test.launcherhelper.LauncherStrategyFactory
import com.android.server.wm.flicker.testapp.ActivityOptions
+import com.android.server.wm.traces.common.FlickerComponentName
+import com.android.server.wm.traces.parser.toFlickerComponent
class NonResizeableAppHelper @JvmOverloads constructor(
instr: Instrumentation,
launcherName: String = ActivityOptions.NON_RESIZEABLE_ACTIVITY_LAUNCHER_NAME,
- component: ComponentName = ActivityOptions.NON_RESIZEABLE_ACTIVITY_COMPONENT_NAME,
+ component: FlickerComponentName =
+ ActivityOptions.NON_RESIZEABLE_ACTIVITY_COMPONENT_NAME.toFlickerComponent(),
launcherStrategy: ILauncherStrategy = LauncherStrategyFactory
.getInstance(instr)
.launcherStrategy
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/SeamlessRotationAppHelper.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/SeamlessRotationAppHelper.kt
index 02be3cf..7bab981 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/SeamlessRotationAppHelper.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/SeamlessRotationAppHelper.kt
@@ -17,15 +17,17 @@
package com.android.server.wm.flicker.helpers
import android.app.Instrumentation
-import android.content.ComponentName
import android.support.test.launcherhelper.ILauncherStrategy
import android.support.test.launcherhelper.LauncherStrategyFactory
import com.android.server.wm.flicker.testapp.ActivityOptions
+import com.android.server.wm.traces.common.FlickerComponentName
+import com.android.server.wm.traces.parser.toFlickerComponent
class SeamlessRotationAppHelper @JvmOverloads constructor(
instr: Instrumentation,
launcherName: String = ActivityOptions.SEAMLESS_ACTIVITY_LAUNCHER_NAME,
- component: ComponentName = ActivityOptions.SEAMLESS_ACTIVITY_COMPONENT_NAME,
+ component: FlickerComponentName =
+ ActivityOptions.SEAMLESS_ACTIVITY_COMPONENT_NAME.toFlickerComponent(),
launcherStrategy: ILauncherStrategy = LauncherStrategyFactory
.getInstance(instr)
.launcherStrategy
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/SimpleAppHelper.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/SimpleAppHelper.kt
index d7cbaae..f6a8817 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/SimpleAppHelper.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/SimpleAppHelper.kt
@@ -17,15 +17,17 @@
package com.android.server.wm.flicker.helpers
import android.app.Instrumentation
-import android.content.ComponentName
import android.support.test.launcherhelper.ILauncherStrategy
import android.support.test.launcherhelper.LauncherStrategyFactory
import com.android.server.wm.flicker.testapp.ActivityOptions
+import com.android.server.wm.traces.common.FlickerComponentName
+import com.android.server.wm.traces.parser.toFlickerComponent
class SimpleAppHelper @JvmOverloads constructor(
instr: Instrumentation,
launcherName: String = ActivityOptions.SIMPLE_ACTIVITY_LAUNCHER_NAME,
- component: ComponentName = ActivityOptions.SIMPLE_ACTIVITY_AUTO_FOCUS_COMPONENT_NAME,
+ component: FlickerComponentName =
+ ActivityOptions.SIMPLE_ACTIVITY_AUTO_FOCUS_COMPONENT_NAME.toFlickerComponent(),
launcherStrategy: ILauncherStrategy = LauncherStrategyFactory
.getInstance(instr)
.launcherStrategy
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/TwoActivitiesAppHelper.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/TwoActivitiesAppHelper.kt
index 19fefb9..59e8dc8 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/TwoActivitiesAppHelper.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/TwoActivitiesAppHelper.kt
@@ -17,19 +17,21 @@
package com.android.server.wm.flicker.helpers
import android.app.Instrumentation
-import android.content.ComponentName
import android.support.test.launcherhelper.ILauncherStrategy
import android.support.test.launcherhelper.LauncherStrategyFactory
import androidx.test.uiautomator.By
import androidx.test.uiautomator.UiDevice
import androidx.test.uiautomator.Until
import com.android.server.wm.flicker.testapp.ActivityOptions
+import com.android.server.wm.traces.common.FlickerComponentName
+import com.android.server.wm.traces.parser.toFlickerComponent
import com.android.server.wm.traces.parser.windowmanager.WindowManagerStateHelper
class TwoActivitiesAppHelper @JvmOverloads constructor(
instr: Instrumentation,
launcherName: String = ActivityOptions.BUTTON_ACTIVITY_LAUNCHER_NAME,
- component: ComponentName = ActivityOptions.BUTTON_ACTIVITY_COMPONENT_NAME,
+ component: FlickerComponentName =
+ ActivityOptions.BUTTON_ACTIVITY_COMPONENT_NAME.toFlickerComponent(),
launcherStrategy: ILauncherStrategy = LauncherStrategyFactory
.getInstance(instr)
.launcherStrategy
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeAutoOpenWindowToAppTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeAutoOpenWindowToAppTest.kt
index d17e77d..3550536 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeAutoOpenWindowToAppTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeAutoOpenWindowToAppTest.kt
@@ -37,7 +37,7 @@
import com.android.server.wm.flicker.statusBarLayerIsVisible
import com.android.server.wm.flicker.statusBarLayerRotatesScales
import com.android.server.wm.flicker.statusBarWindowIsVisible
-import com.android.server.wm.traces.parser.windowmanager.WindowManagerStateHelper
+import com.android.server.wm.traces.common.FlickerComponentName
import org.junit.FixMethodOrder
import org.junit.Test
import org.junit.runner.RunWith
@@ -125,7 +125,7 @@
@Test
fun imeLayerVisibleStart() {
testSpec.assertLayersStart {
- this.isVisible(WindowManagerStateHelper.IME_COMPONENT)
+ this.isVisible(FlickerComponentName.IME)
}
}
@@ -133,7 +133,7 @@
@Test
fun imeLayerInvisibleEnd() {
testSpec.assertLayersEnd {
- this.isInvisible(WindowManagerStateHelper.IME_COMPONENT)
+ this.isInvisible(FlickerComponentName.IME)
}
}
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeAutoOpenWindowToHomeTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeAutoOpenWindowToHomeTest.kt
index 6f0f55a..f7f325e 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeAutoOpenWindowToHomeTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeAutoOpenWindowToHomeTest.kt
@@ -38,7 +38,7 @@
import com.android.server.wm.flicker.statusBarLayerIsVisible
import com.android.server.wm.flicker.statusBarLayerRotatesScales
import com.android.server.wm.flicker.statusBarWindowIsVisible
-import com.android.server.wm.traces.parser.windowmanager.WindowManagerStateHelper
+import com.android.server.wm.traces.common.FlickerComponentName
import org.junit.FixMethodOrder
import org.junit.Test
import org.junit.runner.RunWith
@@ -110,7 +110,7 @@
testSpec.assertWm {
this.isAppWindowOnTop(testApp.component)
.then()
- .appWindowNotOnTop(testApp.component)
+ .isAppWindowNotOnTop(testApp.component)
}
}
@@ -122,7 +122,7 @@
@Test
fun imeLayerVisibleStart() {
testSpec.assertLayersStart {
- this.isVisible(WindowManagerStateHelper.IME_COMPONENT)
+ this.isVisible(FlickerComponentName.IME)
}
}
@@ -130,7 +130,7 @@
@Test
fun imeLayerInvisibleEnd() {
testSpec.assertLayersEnd {
- this.isInvisible(WindowManagerStateHelper.IME_COMPONENT)
+ this.isInvisible(FlickerComponentName.IME)
}
}
@@ -173,8 +173,8 @@
fun visibleLayersShownMoreThanOneConsecutiveEntry() {
testSpec.assertLayers {
this.visibleLayersShownMoreThanOneConsecutiveEntry(listOf(
- WindowManagerStateHelper.IME_COMPONENT,
- WindowManagerStateHelper.SPLASH_SCREEN_COMPONENT))
+ FlickerComponentName.IME,
+ FlickerComponentName.SPLASH_SCREEN))
}
}
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeWindowToAppTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeWindowToAppTest.kt
index 6751439..11660df 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeWindowToAppTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeWindowToAppTest.kt
@@ -35,8 +35,8 @@
import com.android.server.wm.flicker.startRotation
import com.android.server.wm.flicker.statusBarLayerRotatesScales
import com.android.server.wm.flicker.statusBarWindowIsVisible
+import com.android.server.wm.traces.common.FlickerComponentName
import org.junit.Assume
-import com.android.server.wm.traces.parser.windowmanager.WindowManagerStateHelper
import org.junit.FixMethodOrder
import org.junit.Test
import org.junit.runner.RunWith
@@ -91,9 +91,9 @@
fun visibleWindowsShownMoreThanOneConsecutiveEntry() {
testSpec.assertWm {
this.visibleWindowsShownMoreThanOneConsecutiveEntry(listOf(
- WindowManagerStateHelper.IME_COMPONENT,
- WindowManagerStateHelper.SPLASH_SCREEN_COMPONENT,
- WindowManagerStateHelper.SNAPSHOT_COMPONENT))
+ FlickerComponentName.IME,
+ FlickerComponentName.SPLASH_SCREEN,
+ FlickerComponentName.SNAPSHOT))
}
}
@@ -165,4 +165,4 @@
.getConfigNonRotationTests(repetitions = 5)
}
}
-}
+}
\ No newline at end of file
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeWindowToHomeTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeWindowToHomeTest.kt
index 8aaf925..bb2ffbc 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeWindowToHomeTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeWindowToHomeTest.kt
@@ -37,7 +37,7 @@
import com.android.server.wm.flicker.startRotation
import com.android.server.wm.flicker.statusBarLayerRotatesScales
import com.android.server.wm.flicker.statusBarWindowIsVisible
-import com.android.server.wm.traces.parser.windowmanager.WindowManagerStateHelper
+import com.android.server.wm.traces.common.FlickerComponentName
import org.junit.FixMethodOrder
import org.junit.Test
import org.junit.runner.RunWith
@@ -95,9 +95,9 @@
fun visibleWindowsShownMoreThanOneConsecutiveEntry() {
testSpec.assertWm {
this.visibleWindowsShownMoreThanOneConsecutiveEntry(listOf(
- WindowManagerStateHelper.IME_COMPONENT,
- WindowManagerStateHelper.SPLASH_SCREEN_COMPONENT,
- WindowManagerStateHelper.SNAPSHOT_COMPONENT))
+ FlickerComponentName.IME,
+ FlickerComponentName.SPLASH_SCREEN,
+ FlickerComponentName.SNAPSHOT))
}
}
@@ -157,8 +157,8 @@
fun visibleLayersShownMoreThanOneConsecutiveEntry() {
testSpec.assertLayers {
this.visibleLayersShownMoreThanOneConsecutiveEntry(listOf(
- WindowManagerStateHelper.IME_COMPONENT,
- WindowManagerStateHelper.SPLASH_SCREEN_COMPONENT))
+ FlickerComponentName.IME,
+ FlickerComponentName.SPLASH_SCREEN))
}
}
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CommonAssertions.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CommonAssertions.kt
index 7659d94..ba78e25 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CommonAssertions.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CommonAssertions.kt
@@ -18,52 +18,52 @@
package com.android.server.wm.flicker.ime
import com.android.server.wm.flicker.FlickerTestParameter
-import com.android.server.wm.traces.parser.windowmanager.WindowManagerStateHelper
+import com.android.server.wm.traces.common.FlickerComponentName
fun FlickerTestParameter.imeLayerBecomesVisible() {
assertLayers {
- this.isInvisible(WindowManagerStateHelper.IME_COMPONENT)
+ this.isInvisible(FlickerComponentName.IME)
.then()
- .isVisible(WindowManagerStateHelper.IME_COMPONENT)
+ .isVisible(FlickerComponentName.IME)
}
}
fun FlickerTestParameter.imeLayerBecomesInvisible() {
assertLayers {
- this.isVisible(WindowManagerStateHelper.IME_COMPONENT)
+ this.isVisible(FlickerComponentName.IME)
.then()
- .isInvisible(WindowManagerStateHelper.IME_COMPONENT)
+ .isInvisible(FlickerComponentName.IME)
}
}
fun FlickerTestParameter.imeWindowIsAlwaysVisible(rotatesScreen: Boolean = false) {
if (rotatesScreen) {
assertWm {
- this.isNonAppWindowVisible(WindowManagerStateHelper.IME_COMPONENT)
+ this.isNonAppWindowVisible(FlickerComponentName.IME)
.then()
- .isNonAppWindowInvisible(WindowManagerStateHelper.IME_COMPONENT)
+ .isNonAppWindowInvisible(FlickerComponentName.IME)
.then()
- .isNonAppWindowVisible(WindowManagerStateHelper.IME_COMPONENT)
+ .isNonAppWindowVisible(FlickerComponentName.IME)
}
} else {
assertWm {
- this.isNonAppWindowVisible(WindowManagerStateHelper.IME_COMPONENT)
+ this.isNonAppWindowVisible(FlickerComponentName.IME)
}
}
}
fun FlickerTestParameter.imeWindowBecomesVisible() {
assertWm {
- this.isNonAppWindowInvisible(WindowManagerStateHelper.IME_COMPONENT)
+ this.isNonAppWindowInvisible(FlickerComponentName.IME)
.then()
- .isNonAppWindowVisible(WindowManagerStateHelper.IME_COMPONENT)
+ .isNonAppWindowVisible(FlickerComponentName.IME)
}
}
fun FlickerTestParameter.imeWindowBecomesInvisible() {
assertWm {
- this.isNonAppWindowVisible(WindowManagerStateHelper.IME_COMPONENT)
+ this.isNonAppWindowVisible(FlickerComponentName.IME)
.then()
- .isNonAppWindowInvisible(WindowManagerStateHelper.IME_COMPONENT)
+ .isNonAppWindowInvisible(FlickerComponentName.IME)
}
}
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/OpenImeWindowTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/OpenImeWindowTest.kt
index 665204b..44a27b1 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/OpenImeWindowTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/OpenImeWindowTest.kt
@@ -20,6 +20,7 @@
import android.platform.test.annotations.Presubmit
import android.view.Surface
import android.view.WindowManagerPolicyConstants
+import androidx.test.filters.FlakyTest
import androidx.test.filters.RequiresDevice
import androidx.test.platform.app.InstrumentationRegistry
import com.android.server.wm.flicker.FlickerBuilderProvider
@@ -142,7 +143,7 @@
}
}
- @Presubmit
+ @FlakyTest
@Test
fun visibleWindowsShownMoreThanOneConsecutiveEntry() {
testSpec.assertWm {
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ReOpenImeWindowTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ReOpenImeWindowTest.kt
index b37c404..7a01703 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ReOpenImeWindowTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ReOpenImeWindowTest.kt
@@ -17,7 +17,6 @@
package com.android.server.wm.flicker.ime
import android.app.Instrumentation
-import android.content.ComponentName
import android.os.SystemProperties
import android.platform.test.annotations.Presubmit
import android.view.Surface
@@ -43,7 +42,7 @@
import com.android.server.wm.flicker.statusBarLayerIsVisible
import com.android.server.wm.flicker.statusBarLayerRotatesScales
import com.android.server.wm.flicker.statusBarWindowIsVisible
-import com.android.server.wm.traces.parser.windowmanager.WindowManagerStateHelper
+import com.android.server.wm.traces.common.FlickerComponentName
import org.junit.Assume
import org.junit.FixMethodOrder
import org.junit.Test
@@ -104,12 +103,12 @@
@Presubmit
@Test
fun visibleWindowsShownMoreThanOneConsecutiveEntry() {
- val component = ComponentName("", "RecentTaskScreenshotSurface")
+ val component = FlickerComponentName("", "RecentTaskScreenshotSurface")
testSpec.assertWm {
this.visibleWindowsShownMoreThanOneConsecutiveEntry(
- ignoreWindows = listOf(WindowManagerStateHelper.SPLASH_SCREEN_COMPONENT,
- WindowManagerStateHelper.SNAPSHOT_COMPONENT,
- component)
+ ignoreWindows = listOf(FlickerComponentName.SPLASH_SCREEN,
+ FlickerComponentName.SNAPSHOT,
+ component)
)
}
}
@@ -138,9 +137,9 @@
// Since we log 1x per frame, sometimes the activity visibility and the app visibility
// are updated together, sometimes not, thus ignore activity check at the start
testSpec.assertWm {
- this.isAppWindowVisible(testApp.component, ignoreActivity = true)
+ this.isAppWindowVisible(testApp.component)
.then()
- .isAppWindowInvisible(testApp.component, ignoreActivity = true)
+ .isAppWindowInvisible(testApp.component)
.then()
.isAppWindowVisible(testApp.component)
}
@@ -155,7 +154,7 @@
// and the app visibility are updated together, sometimes not, thus ignore activity
// check at the start
testSpec.assertWm {
- this.isAppWindowVisible(testApp.component, ignoreActivity = true)
+ this.isAppWindowVisible(testApp.component)
}
}
@@ -177,11 +176,11 @@
fun imeLayerIsBecomesVisibleLegacy() {
Assume.assumeFalse(isShellTransitionsEnabled)
testSpec.assertLayers {
- this.isVisible(WindowManagerStateHelper.IME_COMPONENT)
+ this.isVisible(FlickerComponentName.IME)
.then()
- .isInvisible(WindowManagerStateHelper.IME_COMPONENT)
+ .isInvisible(FlickerComponentName.IME)
.then()
- .isVisible(WindowManagerStateHelper.IME_COMPONENT)
+ .isVisible(FlickerComponentName.IME)
}
}
@@ -190,7 +189,7 @@
fun imeLayerIsBecomesVisible() {
Assume.assumeTrue(isShellTransitionsEnabled)
testSpec.assertLayers {
- this.isVisible(WindowManagerStateHelper.IME_COMPONENT)
+ this.isVisible(FlickerComponentName.IME)
}
}
@@ -200,7 +199,7 @@
testSpec.assertLayers {
this.isVisible(LAUNCHER_COMPONENT)
.then()
- .isVisible(WindowManagerStateHelper.SNAPSHOT_COMPONENT, isOptional = true)
+ .isVisible(FlickerComponentName.SNAPSHOT, isOptional = true)
.then()
.isVisible(testApp.component)
}
@@ -223,11 +222,11 @@
fun visibleLayersShownMoreThanOneConsecutiveEntry() {
// depends on how much of the animation transactions are sent to SF at once
// sometimes this layer appears for 2-3 frames, sometimes for only 1
- val recentTaskComponent = ComponentName("", "RecentTaskScreenshotSurface")
+ val recentTaskComponent = FlickerComponentName("", "RecentTaskScreenshotSurface")
testSpec.assertLayers {
this.visibleLayersShownMoreThanOneConsecutiveEntry(
- listOf(WindowManagerStateHelper.SPLASH_SCREEN_COMPONENT,
- WindowManagerStateHelper.SNAPSHOT_COMPONENT, recentTaskComponent)
+ listOf(FlickerComponentName.SPLASH_SCREEN,
+ FlickerComponentName.SNAPSHOT, recentTaskComponent)
)
}
}
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/SwitchImeWindowsFromGestureNavTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/SwitchImeWindowsFromGestureNavTest.kt
index f9dd88e..4c506b0 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/SwitchImeWindowsFromGestureNavTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/SwitchImeWindowsFromGestureNavTest.kt
@@ -17,7 +17,6 @@
package com.android.server.wm.flicker.ime
import android.app.Instrumentation
-import android.content.ComponentName
import android.platform.test.annotations.Presubmit
import android.view.Surface
import android.view.WindowManagerPolicyConstants
@@ -28,7 +27,7 @@
import com.android.server.wm.flicker.FlickerParametersRunnerFactory
import com.android.server.wm.flicker.FlickerTestParameter
import com.android.server.wm.flicker.FlickerTestParameterFactory
-import com.android.server.wm.flicker.annotation.Group2
+import com.android.server.wm.flicker.annotation.Group4
import com.android.server.wm.flicker.helpers.ImeAppAutoFocusHelper
import com.android.server.wm.flicker.helpers.SimpleAppHelper
import com.android.server.wm.flicker.helpers.WindowUtils
@@ -36,7 +35,7 @@
import com.android.server.wm.flicker.navBarWindowIsVisible
import com.android.server.wm.flicker.startRotation
import com.android.server.wm.flicker.statusBarWindowIsVisible
-import com.android.server.wm.traces.parser.windowmanager.WindowManagerStateHelper
+import com.android.server.wm.traces.common.FlickerComponentName
import org.junit.FixMethodOrder
import org.junit.Test
@@ -52,7 +51,7 @@
@RunWith(Parameterized::class)
@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-@Group2
+@Group4
@Presubmit
class SwitchImeWindowsFromGestureNavTest(private val testSpec: FlickerTestParameter) {
private val instrumentation: Instrumentation = InstrumentationRegistry.getInstrumentation()
@@ -107,51 +106,52 @@
@Test
fun imeAppWindowVisibility() {
- val component = ComponentName(imeTestApp.`package`, "")
testSpec.assertWm {
- this.isAppWindowOnTop(component)
- .then()
- .isAppWindowVisible(component, ignoreActivity = true)
+ isAppWindowVisible(imeTestApp.component)
+ .then()
+ .isAppWindowVisible(testApp.component)
+ .then()
+ .isAppWindowVisible(imeTestApp.component)
}
}
@Test
fun navBarLayerIsVisibleAroundSwitching() {
testSpec.assertLayersStart {
- isVisible(WindowManagerStateHelper.NAV_BAR_COMPONENT)
+ isVisible(FlickerComponentName.NAV_BAR)
}
testSpec.assertLayersEnd {
- isVisible(WindowManagerStateHelper.NAV_BAR_COMPONENT)
+ isVisible(FlickerComponentName.NAV_BAR)
}
}
@Test
fun statusBarLayerIsVisibleAroundSwitching() {
testSpec.assertLayersStart {
- isVisible(WindowManagerStateHelper.STATUS_BAR_COMPONENT)
+ isVisible(FlickerComponentName.STATUS_BAR)
}
testSpec.assertLayersEnd {
- isVisible(WindowManagerStateHelper.STATUS_BAR_COMPONENT)
+ isVisible(FlickerComponentName.STATUS_BAR)
}
}
@Test
fun imeLayerIsVisibleWhenSwitchingToImeApp() {
testSpec.assertLayersStart {
- isVisible(WindowManagerStateHelper.IME_COMPONENT)
+ isVisible(FlickerComponentName.IME)
}
testSpec.assertLayersTag(TAG_IME_VISIBLE) {
- isVisible(WindowManagerStateHelper.IME_COMPONENT)
+ isVisible(FlickerComponentName.IME)
}
testSpec.assertLayersEnd {
- isVisible(WindowManagerStateHelper.IME_COMPONENT)
+ isVisible(FlickerComponentName.IME)
}
}
@Test
fun imeLayerIsInvisibleWhenSwitchingToTestApp() {
testSpec.assertLayersTag(TAG_IME_INVISIBLE) {
- isInvisible(WindowManagerStateHelper.IME_COMPONENT)
+ isInvisible(FlickerComponentName.IME)
}
}
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/ActivitiesTransitionTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/ActivitiesTransitionTest.kt
index 42c252e..f74a771 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/ActivitiesTransitionTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/ActivitiesTransitionTest.kt
@@ -27,10 +27,11 @@
import com.android.server.wm.flicker.FlickerTestParameterFactory
import com.android.server.wm.flicker.LAUNCHER_COMPONENT
import com.android.server.wm.flicker.repetitions
-import com.android.server.wm.flicker.annotation.Group1
+import com.android.server.wm.flicker.annotation.Group4
import com.android.server.wm.flicker.dsl.FlickerBuilder
import com.android.server.wm.flicker.helpers.TwoActivitiesAppHelper
import com.android.server.wm.flicker.testapp.ActivityOptions
+import com.android.server.wm.traces.parser.toFlickerComponent
import org.junit.FixMethodOrder
import org.junit.Test
import org.junit.runner.RunWith
@@ -39,17 +40,33 @@
/**
* Test the back and forward transition between 2 activities.
+ *
* To run this test: `atest FlickerTests:ActivitiesTransitionTest`
+ *
+ * Actions:
+ * Launch an app
+ * Launch a secondary activity within the app
+ * Close the secondary activity back to the initial one
+ *
+ * Notes:
+ * 1. Part of the test setup occurs automatically via
+ * [com.android.server.wm.flicker.TransitionRunnerWithRules],
+ * including configuring navigation mode, initial orientation and ensuring no
+ * apps are running before setup
*/
@RequiresDevice
@RunWith(Parameterized::class)
@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-@Group1
+@Group4
class ActivitiesTransitionTest(val testSpec: FlickerTestParameter) {
val instrumentation: Instrumentation = InstrumentationRegistry.getInstrumentation()
private val testApp: TwoActivitiesAppHelper = TwoActivitiesAppHelper(instrumentation)
+ /**
+ * Entry point for the test runner. It will use this method to initialize and cache
+ * flicker executions
+ */
@FlickerBuilderProvider
fun buildFlicker(): FlickerBuilder {
return FlickerBuilder(instrumentation).apply {
@@ -75,30 +92,52 @@
}
}
+ /**
+ * Checks that the [ActivityOptions.BUTTON_ACTIVITY_COMPONENT_NAME] activity is visible at
+ * the start of the transition, that
+ * [ActivityOptions.SIMPLE_ACTIVITY_AUTO_FOCUS_COMPONENT_NAME] becomes visible during the
+ * transition, and that [ActivityOptions.BUTTON_ACTIVITY_COMPONENT_NAME] is again visible
+ * at the end
+ */
@Presubmit
@Test
fun finishSubActivity() {
+ val buttonActivityComponent = ActivityOptions
+ .BUTTON_ACTIVITY_COMPONENT_NAME.toFlickerComponent()
+ val imeAutoFocusActivityComponent = ActivityOptions
+ .SIMPLE_ACTIVITY_AUTO_FOCUS_COMPONENT_NAME.toFlickerComponent()
testSpec.assertWm {
- this.isAppWindowOnTop(ActivityOptions.BUTTON_ACTIVITY_COMPONENT_NAME)
- .then()
- .isAppWindowOnTop(ActivityOptions.SIMPLE_ACTIVITY_AUTO_FOCUS_COMPONENT_NAME)
- .then()
- .isAppWindowOnTop(ActivityOptions.BUTTON_ACTIVITY_COMPONENT_NAME)
+ this.isAppWindowOnTop(buttonActivityComponent)
+ .then()
+ .isAppWindowOnTop(imeAutoFocusActivityComponent)
+ .then()
+ .isAppWindowOnTop(buttonActivityComponent)
}
}
+ /**
+ * Checks that all parts of the screen are covered during the transition
+ */
@Presubmit
@Test
fun entireScreenCovered() = testSpec.entireScreenCovered()
+ /**
+ * Checks that the [LAUNCHER_COMPONENT] window is not on top. The launcher cannot be
+ * asserted with `isAppWindowVisible` because it contains 2 windows with the exact same name,
+ * and both are never simultaneously visible
+ */
@Presubmit
@Test
- fun launcherWindowNotVisible() {
+ fun launcherWindowNotOnTop() {
testSpec.assertWm {
- this.isAppWindowInvisible(LAUNCHER_COMPONENT, ignoreActivity = true)
+ this.isAppWindowNotOnTop(LAUNCHER_COMPONENT)
}
}
+ /**
+ * Checks that the [LAUNCHER_COMPONENT] layer is never visible during the transition
+ */
@Presubmit
@Test
fun launcherLayerNotVisible() {
@@ -106,6 +145,12 @@
}
companion object {
+ /**
+ * Creates the test configurations.
+ *
+ * See [FlickerTestParameterFactory.getConfigNonRotationTests] for configuring
+ * repetitions, screen orientation and navigation modes.
+ */
@Parameterized.Parameters(name = "{0}")
@JvmStatic
fun getParams(): Collection<FlickerTestParameter> {
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppNonResizeableTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppNonResizeableTest.kt
index b717612..1bdc235 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppNonResizeableTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppNonResizeableTest.kt
@@ -16,7 +16,6 @@
package com.android.server.wm.flicker.launch
-import android.content.ComponentName
import android.platform.test.annotations.Postsubmit
import android.platform.test.annotations.Presubmit
import android.view.Surface
@@ -29,7 +28,7 @@
import com.android.server.wm.flicker.annotation.Group1
import com.android.server.wm.flicker.helpers.NonResizeableAppHelper
import com.android.server.wm.flicker.dsl.FlickerBuilder
-import com.android.server.wm.traces.parser.windowmanager.WindowManagerStateHelper
+import com.android.server.wm.traces.common.FlickerComponentName
import com.google.common.truth.Truth
import org.junit.FixMethodOrder
import org.junit.Test
@@ -61,7 +60,7 @@
@Group1
class OpenAppNonResizeableTest(testSpec: FlickerTestParameter) : OpenAppTransition(testSpec) {
override val testApp = NonResizeableAppHelper(instrumentation)
- private val colorFadComponent = ComponentName("", "ColorFade BLAST#")
+ private val colorFadComponent = FlickerComponentName("", "ColorFade BLAST#")
/**
* Defines the transition used to run the test
@@ -92,15 +91,15 @@
* Checks that the nav bar layer starts visible, becomes invisible during unlocking animation
* and becomes visible at the end
*/
- @Presubmit
+ @Postsubmit
@Test
fun navBarLayerVisibilityChanges() {
testSpec.assertLayers {
- this.isVisible(WindowManagerStateHelper.NAV_BAR_COMPONENT)
+ this.isVisible(FlickerComponentName.NAV_BAR)
.then()
- .isInvisible(WindowManagerStateHelper.NAV_BAR_COMPONENT)
+ .isInvisible(FlickerComponentName.NAV_BAR)
.then()
- .isVisible(WindowManagerStateHelper.NAV_BAR_COMPONENT)
+ .isVisible(FlickerComponentName.NAV_BAR)
}
}
@@ -133,10 +132,9 @@
testSpec.assertWm {
this.notContains(testApp.component)
.then()
- .isAppWindowInvisible(testApp.component,
- ignoreActivity = true, isOptional = true)
+ .isAppWindowInvisible(testApp.component, isOptional = true)
.then()
- .isAppWindowVisible(testApp.component, ignoreActivity = true)
+ .isAppWindowVisible(testApp.component)
}
}
@@ -147,7 +145,7 @@
@Test
fun appWindowBecomesVisibleAtEnd() {
testSpec.assertWmEnd {
- this.isVisible(testApp.component)
+ this.isAppWindowVisible(testApp.component)
}
}
@@ -159,11 +157,11 @@
@Test
fun navBarWindowsVisibilityChanges() {
testSpec.assertWm {
- this.isAboveAppWindowVisible(WindowManagerStateHelper.NAV_BAR_COMPONENT)
+ this.isAboveAppWindowVisible(FlickerComponentName.NAV_BAR)
.then()
- .isNonAppWindowInvisible(WindowManagerStateHelper.NAV_BAR_COMPONENT)
+ .isNonAppWindowInvisible(FlickerComponentName.NAV_BAR)
.then()
- .isAboveAppWindowVisible(WindowManagerStateHelper.NAV_BAR_COMPONENT)
+ .isAboveAppWindowVisible(FlickerComponentName.NAV_BAR)
}
}
@@ -173,6 +171,10 @@
override fun visibleWindowsShownMoreThanOneConsecutiveEntry() =
super.visibleWindowsShownMoreThanOneConsecutiveEntry()
+ @FlakyTest
+ @Test
+ override fun navBarLayerRotatesAndScales() = super.navBarLayerRotatesAndScales()
+
/** {@inheritDoc} */
@FlakyTest
@Test
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppTransition.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppTransition.kt
index 14d17f8..419d3e8 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppTransition.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppTransition.kt
@@ -39,10 +39,12 @@
import com.android.server.wm.flicker.statusBarLayerIsVisible
import com.android.server.wm.flicker.statusBarLayerRotatesScales
import com.android.server.wm.flicker.statusBarWindowIsVisible
-import com.android.server.wm.traces.parser.windowmanager.WindowManagerStateHelper.Companion.SNAPSHOT_COMPONENT
-import com.android.server.wm.traces.parser.windowmanager.WindowManagerStateHelper.Companion.SPLASH_SCREEN_COMPONENT
+import com.android.server.wm.traces.common.FlickerComponentName
import org.junit.Test
+/**
+ * Base class for app launch tests
+ */
abstract class OpenAppTransition(protected val testSpec: FlickerTestParameter) {
protected val instrumentation: Instrumentation = InstrumentationRegistry.getInstrumentation()
protected open val testApp: StandardAppHelper = SimpleAppHelper(instrumentation)
@@ -85,7 +87,7 @@
}
/**
- * Checks that the navigation bar layer is visible during the whole transition
+ * Checks that the navigation bar layer is visible at the start and end of the trace
*/
open fun navBarLayerIsVisible() {
testSpec.navBarLayerIsVisible()
@@ -110,7 +112,7 @@
}
/**
- * Checks that the status bar layer is visible during the whole transition
+ * Checks that the status bar layer is visible at the start and end of the trace
*/
@Presubmit
@Test
@@ -188,9 +190,9 @@
testSpec.assertWm {
this.isAppWindowOnTop(LAUNCHER_COMPONENT)
.then()
- .isAppWindowOnTop(SNAPSHOT_COMPONENT, isOptional = true)
+ .isAppWindowOnTop(FlickerComponentName.SNAPSHOT, isOptional = true)
.then()
- .isAppWindowOnTop(SPLASH_SCREEN_COMPONENT, isOptional = true)
+ .isAppWindowOnTop(FlickerComponentName.SPLASH_SCREEN, isOptional = true)
.then()
.isAppWindowOnTop(testApp.component)
}
@@ -202,9 +204,9 @@
*/
open fun launcherWindowBecomesInvisible() {
testSpec.assertWm {
- this.isAppWindowVisible(LAUNCHER_COMPONENT)
+ this.isAppWindowOnTop(LAUNCHER_COMPONENT)
.then()
- .isAppWindowInvisible(LAUNCHER_COMPONENT)
+ .isAppWindowNotOnTop(LAUNCHER_COMPONENT)
}
}
}
\ No newline at end of file
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/quickswitch/QuickSwitchBetweenTwoAppsBackTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/quickswitch/QuickSwitchBetweenTwoAppsBackTest.kt
index 035aac1..cdab681 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/quickswitch/QuickSwitchBetweenTwoAppsBackTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/quickswitch/QuickSwitchBetweenTwoAppsBackTest.kt
@@ -39,7 +39,7 @@
import com.android.server.wm.flicker.startRotation
import com.android.server.wm.flicker.statusBarLayerIsVisible
import com.android.server.wm.flicker.statusBarWindowIsVisible
-import com.android.server.wm.traces.parser.windowmanager.WindowManagerStateHelper.Companion.SNAPSHOT_COMPONENT
+import com.android.server.wm.traces.common.FlickerComponentName
import org.junit.FixMethodOrder
import org.junit.Test
import org.junit.runner.RunWith
@@ -189,9 +189,9 @@
testSpec.assertWm {
this.isAppWindowInvisible(testApp1.component)
.then()
- .isAppWindowVisible(SNAPSHOT_COMPONENT, isOptional = true)
+ .isAppWindowVisible(FlickerComponentName.SNAPSHOT, isOptional = true)
.then()
- .isAppWindowVisible(testApp1.component, ignoreActivity = true)
+ .isAppWindowVisible(testApp1.component)
}
}
@@ -217,7 +217,7 @@
@Test
fun app2WindowBecomesAndStaysInvisible() {
testSpec.assertWm {
- this.isAppWindowVisible(testApp2.component, ignoreActivity = true)
+ this.isAppWindowVisible(testApp2.component)
.then()
.isAppWindowInvisible(testApp2.component)
}
@@ -251,7 +251,7 @@
// TODO: Do we actually want to test this? Seems too implementation specific...
.isAppWindowVisible(LAUNCHER_COMPONENT, isOptional = true)
.then()
- .isAppWindowVisible(SNAPSHOT_COMPONENT, isOptional = true)
+ .isAppWindowVisible(FlickerComponentName.SNAPSHOT, isOptional = true)
.then()
.isAppWindowVisible(testApp1.component)
}
@@ -270,7 +270,7 @@
.then()
.isVisible(LAUNCHER_COMPONENT, isOptional = true)
.then()
- .isVisible(SNAPSHOT_COMPONENT, isOptional = true)
+ .isVisible(FlickerComponentName.SNAPSHOT, isOptional = true)
.then()
.isVisible(testApp1.component)
}
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/quickswitch/QuickSwitchBetweenTwoAppsForwardTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/quickswitch/QuickSwitchBetweenTwoAppsForwardTest.kt
new file mode 100644
index 0000000..d1a3fe4
--- /dev/null
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/quickswitch/QuickSwitchBetweenTwoAppsForwardTest.kt
@@ -0,0 +1,348 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.wm.flicker.quickswitch
+
+import android.app.Instrumentation
+import android.platform.test.annotations.Postsubmit
+import android.platform.test.annotations.RequiresDevice
+import android.view.Surface
+import android.view.WindowManagerPolicyConstants
+import androidx.test.platform.app.InstrumentationRegistry
+import com.android.server.wm.flicker.FlickerBuilderProvider
+import com.android.server.wm.flicker.FlickerParametersRunnerFactory
+import com.android.server.wm.flicker.FlickerTestParameter
+import com.android.server.wm.flicker.FlickerTestParameterFactory
+import com.android.server.wm.flicker.LAUNCHER_COMPONENT
+import com.android.server.wm.flicker.annotation.Group1
+import com.android.server.wm.flicker.dsl.FlickerBuilder
+import com.android.server.wm.flicker.helpers.NonResizeableAppHelper
+import com.android.server.wm.flicker.helpers.SimpleAppHelper
+import com.android.server.wm.flicker.helpers.WindowUtils
+import com.android.server.wm.flicker.helpers.isRotated
+import com.android.server.wm.flicker.navBarLayerIsVisible
+import com.android.server.wm.flicker.navBarLayerRotatesAndScales
+import com.android.server.wm.flicker.navBarWindowIsVisible
+import com.android.server.wm.flicker.repetitions
+import com.android.server.wm.flicker.startRotation
+import com.android.server.wm.flicker.statusBarLayerIsVisible
+import com.android.server.wm.flicker.statusBarWindowIsVisible
+import com.android.server.wm.traces.common.FlickerComponentName
+import org.junit.FixMethodOrder
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.MethodSorters
+import org.junit.runners.Parameterized
+
+/**
+ * Test quick switching back to previous app from last opened app
+ *
+ * To run this test: `atest FlickerTests:QuickSwitchBetweenTwoAppsBackTest`
+ *
+ * Actions:
+ * Launch an app [testApp1]
+ * Launch another app [testApp2]
+ * Swipe right from the bottom of the screen to quick switch back to the first app [testApp1]
+ * Swipe left from the bottom of the screen to quick switch forward to the second app [testApp2]
+ */
+@RequiresDevice
+@RunWith(Parameterized::class)
+@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
+@FixMethodOrder(MethodSorters.NAME_ASCENDING)
+@Group1
+class QuickSwitchBetweenTwoAppsForwardTest(private val testSpec: FlickerTestParameter) {
+ private val instrumentation: Instrumentation = InstrumentationRegistry.getInstrumentation()
+
+ private val testApp1 = SimpleAppHelper(instrumentation)
+ private val testApp2 = NonResizeableAppHelper(instrumentation)
+
+ private val startDisplayBounds = WindowUtils.getDisplayBounds(testSpec.config.startRotation)
+
+ @FlickerBuilderProvider
+ fun buildFlicker(): FlickerBuilder {
+ return FlickerBuilder(instrumentation).apply {
+ withTestName { testSpec.name }
+ repeat { testSpec.config.repetitions }
+ setup {
+ eachRun {
+ testApp1.launchViaIntent(wmHelper)
+ wmHelper.waitForFullScreenApp(testApp1.component)
+
+ testApp2.launchViaIntent(wmHelper)
+ wmHelper.waitForFullScreenApp(testApp2.component)
+
+ // Swipe right from bottom to quick switch back
+ // NOTE: We don't perform an edge-to-edge swipe but instead only swipe in the middle
+ // as to not accidentally trigger a swipe back or forward action which would result
+ // in the same behavior but not testing quick swap.
+ device.swipe(
+ startDisplayBounds.bounds.right / 3,
+ startDisplayBounds.bounds.bottom,
+ 2 * startDisplayBounds.bounds.right / 3,
+ startDisplayBounds.bounds.bottom,
+ if (testSpec.config.startRotation.isRotated()) 75 else 30
+ )
+
+ wmHelper.waitForFullScreenApp(testApp1.component)
+ wmHelper.waitForAppTransitionIdle()
+ }
+ }
+ transitions {
+ // Swipe left from bottom to quick switch forward
+ // NOTE: We don't perform an edge-to-edge swipe but instead only swipe in the middle
+ // as to not accidentally trigger a swipe back or forward action which would result
+ // in the same behavior but not testing quick swap.
+ device.swipe(
+ 2 * startDisplayBounds.bounds.right / 3,
+ startDisplayBounds.bounds.bottom,
+ startDisplayBounds.bounds.right / 3,
+ startDisplayBounds.bounds.bottom,
+ if (testSpec.config.startRotation.isRotated()) 75 else 30
+ )
+
+ wmHelper.waitForFullScreenApp(testApp2.component)
+ wmHelper.waitForAppTransitionIdle()
+ }
+
+ teardown {
+ test {
+ testApp1.exit()
+ testApp2.exit()
+ }
+ }
+ }
+ }
+
+ /**
+ * Checks that the transition starts with [testApp1]'s windows filling/covering exactly the
+ * entirety of the display.
+ */
+ @Postsubmit
+ @Test
+ fun startsWithApp1WindowsCoverFullScreen() {
+ testSpec.assertWmStart {
+ this.frameRegion(testApp1.component).coversExactly(startDisplayBounds)
+ }
+ }
+
+ /**
+ * Checks that the transition starts with [testApp1]'s layers filling/covering exactly the
+ * entirety of the display.
+ */
+ @Postsubmit
+ @Test
+ fun startsWithApp1LayersCoverFullScreen() {
+ testSpec.assertLayersStart {
+ this.visibleRegion(testApp1.component).coversExactly(startDisplayBounds)
+ }
+ }
+
+ /**
+ * Checks that the transition starts with [testApp1] being the top window.
+ */
+ @Postsubmit
+ @Test
+ fun startsWithApp1WindowBeingOnTop() {
+ testSpec.assertWmStart {
+ this.isAppWindowOnTop(testApp1.component)
+ }
+ }
+
+ /**
+ * Checks that [testApp2] windows fill the entire screen (i.e. is "fullscreen") at the end of the
+ * transition once we have fully quick switched from [testApp1] back to the [testApp2].
+ */
+ @Postsubmit
+ @Test
+ fun endsWithApp2WindowsCoveringFullScreen() {
+ testSpec.assertWmEnd {
+ this.frameRegion(testApp2.component).coversExactly(startDisplayBounds)
+ }
+ }
+
+ /**
+ * Checks that [testApp2] layers fill the entire screen (i.e. is "fullscreen") at the end of the
+ * transition once we have fully quick switched from [testApp1] back to the [testApp2].
+ */
+ @Postsubmit
+ @Test
+ fun endsWithApp2LayersCoveringFullScreen() {
+ testSpec.assertLayersEnd {
+ this.visibleRegion(testApp2.component).coversExactly(startDisplayBounds)
+ }
+ }
+
+ /**
+ * Checks that [testApp2] is the top window at the end of the transition once we have fully quick
+ * switched from [testApp1] back to the [testApp2].
+ */
+ @Postsubmit
+ @Test
+ fun endsWithApp2BeingOnTop() {
+ testSpec.assertWmEnd {
+ this.isAppWindowOnTop(testApp2.component)
+ }
+ }
+
+ /**
+ * Checks that [testApp2]'s window starts off invisible and becomes visible at some point before
+ * the end of the transition and then stays visible until the end of the transition.
+ */
+ @Postsubmit
+ @Test
+ fun app2WindowBecomesAndStaysVisible() {
+ testSpec.assertWm {
+ this.isAppWindowInvisible(testApp2.component)
+ .then()
+ .isAppWindowVisible(FlickerComponentName.SNAPSHOT, isOptional = true)
+ .then()
+ .isAppWindowVisible(testApp2.component)
+ }
+ }
+
+ /**
+ * Checks that [testApp2]'s layer starts off invisible and becomes visible at some point before
+ * the end of the transition and then stays visible until the end of the transition.
+ */
+ @Postsubmit
+ @Test
+ fun app2LayerBecomesAndStaysVisible() {
+ testSpec.assertLayers {
+ this.isInvisible(testApp2.component)
+ .then()
+ .isVisible(testApp2.component)
+ }
+ }
+
+ /**
+ * Checks that [testApp1]'s window starts off visible and becomes invisible at some point before
+ * the end of the transition and then stays invisible until the end of the transition.
+ */
+ @Postsubmit
+ @Test
+ fun app1WindowBecomesAndStaysInvisible() {
+ testSpec.assertWm {
+ this.isAppWindowVisible(testApp1.component)
+ .then()
+ .isAppWindowInvisible(testApp1.component)
+ }
+ }
+
+ /**
+ * Checks that [testApp1]'s layer starts off visible and becomes invisible at some point before
+ * the end of the transition and then stays invisible until the end of the transition.
+ */
+ @Postsubmit
+ @Test
+ fun app1LayerBecomesAndStaysInvisible() {
+ testSpec.assertLayers {
+ this.isVisible(testApp1.component)
+ .then()
+ .isInvisible(testApp1.component)
+ }
+ }
+
+ /**
+ * Checks that [testApp1]'s window is visible at least until [testApp2]'s window is visible.
+ * Ensures that at any point, either [testApp2] or [testApp1]'s windows are at least partially
+ * visible.
+ */
+ @Postsubmit
+ @Test
+ fun app2WindowIsVisibleOnceApp1WindowIsInvisible() {
+ testSpec.assertWm {
+ this.isAppWindowVisible(testApp1.component)
+ .then()
+ .isAppWindowVisible(LAUNCHER_COMPONENT, isOptional = true)
+ .then()
+ .isAppWindowVisible(FlickerComponentName.SNAPSHOT, isOptional = true)
+ .then()
+ .isAppWindowVisible(testApp2.component)
+ }
+ }
+
+ /**
+ * Checks that [testApp1]'s layer is visible at least until [testApp2]'s window is visible.
+ * Ensures that at any point, either [testApp2] or [testApp1]'s windows are at least partially
+ * visible.
+ */
+ @Postsubmit
+ @Test
+ fun app2LayerIsVisibleOnceApp1LayerIsInvisible() {
+ testSpec.assertLayers {
+ this.isVisible(testApp1.component)
+ .then()
+ .isVisible(LAUNCHER_COMPONENT, isOptional = true)
+ .then()
+ .isVisible(FlickerComponentName.SNAPSHOT, isOptional = true)
+ .then()
+ .isVisible(testApp2.component)
+ }
+ }
+
+ /**
+ * Checks that the navbar window is visible throughout the entire transition.
+ */
+ @Postsubmit
+ @Test
+ fun navBarWindowIsAlwaysVisible() = testSpec.navBarWindowIsVisible()
+
+ /**
+ * Checks that the navbar layer is visible throughout the entire transition.
+ */
+ @Postsubmit
+ @Test
+ fun navBarLayerAlwaysIsVisible() = testSpec.navBarLayerIsVisible()
+
+ /**
+ * Checks that the navbar is always in the right position and covers the expected region.
+ *
+ * NOTE: This doesn't check that the navbar is visible or not.
+ */
+ @Postsubmit
+ @Test
+ fun navbarIsAlwaysInRightPosition() =
+ testSpec.navBarLayerRotatesAndScales(testSpec.config.startRotation)
+
+ /**
+ * Checks that the status bar window is visible throughout the entire transition.
+ */
+ @Postsubmit
+ @Test
+ fun statusBarWindowIsAlwaysVisible() = testSpec.statusBarWindowIsVisible()
+
+ /**
+ * Checks that the status bar layer is visible throughout the entire transition.
+ */
+ @Postsubmit
+ @Test
+ fun statusBarLayerIsAlwaysVisible() = testSpec.statusBarLayerIsVisible()
+
+ companion object {
+ @Parameterized.Parameters(name = "{0}")
+ @JvmStatic
+ fun getParams(): Collection<FlickerTestParameter> {
+ return FlickerTestParameterFactory.getInstance()
+ .getConfigNonRotationTests(
+ repetitions = 5,
+ supportedNavigationModes = listOf(
+ WindowManagerPolicyConstants.NAV_BAR_MODE_GESTURAL_OVERLAY
+ ),
+ supportedRotations = listOf(Surface.ROTATION_0, Surface.ROTATION_90)
+ )
+ }
+ }
+}
\ No newline at end of file
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/quickswitch/QuickSwitchFromLauncherTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/quickswitch/QuickSwitchFromLauncherTest.kt
index ca8f8af..0389f7c 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/quickswitch/QuickSwitchFromLauncherTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/quickswitch/QuickSwitchFromLauncherTest.kt
@@ -27,7 +27,7 @@
import com.android.server.wm.flicker.FlickerTestParameter
import com.android.server.wm.flicker.FlickerTestParameterFactory
import com.android.server.wm.flicker.LAUNCHER_COMPONENT
-import com.android.server.wm.flicker.annotation.Group1
+import com.android.server.wm.flicker.annotation.Group4
import com.android.server.wm.flicker.dsl.FlickerBuilder
import com.android.server.wm.flicker.entireScreenCovered
import com.android.server.wm.flicker.helpers.SimpleAppHelper
@@ -38,7 +38,7 @@
import com.android.server.wm.flicker.startRotation
import com.android.server.wm.flicker.statusBarLayerIsVisible
import com.android.server.wm.flicker.statusBarWindowIsVisible
-import com.android.server.wm.traces.parser.windowmanager.WindowManagerStateHelper.Companion.SNAPSHOT_COMPONENT
+import com.android.server.wm.traces.common.FlickerComponentName
import org.junit.FixMethodOrder
import org.junit.Test
import org.junit.runner.RunWith
@@ -60,7 +60,7 @@
@RunWith(Parameterized::class)
@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-@Group1
+@Group4
class QuickSwitchFromLauncherTest(private val testSpec: FlickerTestParameter) {
private val instrumentation: Instrumentation = InstrumentationRegistry.getInstrumentation()
private val testApp = SimpleAppHelper(instrumentation)
@@ -145,7 +145,7 @@
@Test
fun startsWithHomeActivityFlaggedVisible() {
testSpec.assertWmStart {
- this.isHomeActivityVisible(true)
+ this.isHomeActivityVisible()
}
}
@@ -192,7 +192,7 @@
@Test
fun endsWithHomeActivityFlaggedInvisible() {
testSpec.assertWmEnd {
- this.isHomeActivityVisible(false)
+ this.isHomeActivityInvisible()
}
}
@@ -204,9 +204,9 @@
@Test
fun appWindowBecomesAndStaysVisible() {
testSpec.assertWm {
- this.isAppWindowInvisible(testApp.component, ignoreActivity = true)
+ this.isAppWindowInvisible(testApp.component)
.then()
- .isAppWindowVisible(testApp.component, ignoreActivity = true)
+ .isAppWindowVisible(testApp.component)
}
}
@@ -232,9 +232,9 @@
@Test
fun launcherWindowBecomesAndStaysInvisible() {
testSpec.assertWm {
- this.isAppWindowVisible(LAUNCHER_COMPONENT)
+ this.isAppWindowOnTop(LAUNCHER_COMPONENT)
.then()
- .isAppWindowInvisible(LAUNCHER_COMPONENT)
+ .isAppWindowNotOnTop(LAUNCHER_COMPONENT)
}
}
@@ -260,9 +260,9 @@
@Test
fun appWindowIsVisibleOnceLauncherWindowIsInvisible() {
testSpec.assertWm {
- this.isAppWindowVisible(LAUNCHER_COMPONENT)
+ this.isAppWindowOnTop(LAUNCHER_COMPONENT)
.then()
- .isAppWindowVisible(SNAPSHOT_COMPONENT)
+ .isAppWindowVisible(FlickerComponentName.SNAPSHOT)
.then()
.isAppWindowVisible(testApp.component)
}
@@ -278,7 +278,7 @@
testSpec.assertLayers {
this.isVisible(LAUNCHER_COMPONENT)
.then()
- .isVisible(SNAPSHOT_COMPONENT)
+ .isVisible(FlickerComponentName.SNAPSHOT)
.then()
.isVisible(testApp.component)
}
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/ChangeAppRotationTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/ChangeAppRotationTest.kt
index d57c6698..878821a 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/ChangeAppRotationTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/ChangeAppRotationTest.kt
@@ -30,7 +30,7 @@
import com.android.server.wm.flicker.statusBarLayerIsVisible
import com.android.server.wm.flicker.statusBarLayerRotatesScales
import com.android.server.wm.flicker.statusBarWindowIsVisible
-import com.android.server.wm.traces.parser.windowmanager.WindowManagerStateHelper.Companion.ROTATION_COMPONENT
+import com.android.server.wm.traces.common.FlickerComponentName
import org.junit.FixMethodOrder
import org.junit.Test
import org.junit.runner.RunWith
@@ -38,8 +38,39 @@
import org.junit.runners.Parameterized
/**
- * Cycle through supported app rotations.
+ * Test opening an app and cycling through app rotations
+ *
+ * Currently runs:
+ * 0 -> 90 degrees
+ * 90 -> 0 degrees
+ *
+ * Actions:
+ * Launch an app (via intent)
+ * Set initial device orientation
+ * Start tracing
+ * Change device orientation
+ * Stop tracing
+ *
* To run this test: `atest FlickerTests:ChangeAppRotationTest`
+ *
+ * To run only the presubmit assertions add: `--
+ * --module-arg FlickerTests:exclude-annotation:androidx.test.filters.FlakyTest
+ * --module-arg FlickerTests:include-annotation:android.platform.test.annotations.Presubmit`
+ *
+ * To run only the postsubmit assertions add: `--
+ * --module-arg FlickerTests:exclude-annotation:androidx.test.filters.FlakyTest
+ * --module-arg FlickerTests:include-annotation:android.platform.test.annotations.Postsubmit`
+ *
+ * To run only the flaky assertions add: `--
+ * --module-arg FlickerTests:include-annotation:androidx.test.filters.FlakyTest`
+ *
+ * Notes:
+ * 1. Some default assertions (e.g., nav bar, status bar and screen covered)
+ * are inherited [RotationTransition]
+ * 2. Part of the test setup occurs automatically via
+ * [com.android.server.wm.flicker.TransitionRunnerWithRules],
+ * including configuring navigation mode, initial orientation and ensuring no
+ * apps are running before setup
*/
@RequiresDevice
@RunWith(Parameterized::class)
@@ -60,36 +91,51 @@
}
}
+ /** {@inheritDoc} */
@FlakyTest(bugId = 190185577)
@Test
override fun focusDoesNotChange() {
super.focusDoesNotChange()
}
+ /**
+ * Checks that the [FlickerComponentName.ROTATION] layer appears during the transition,
+ * doesn't flicker, and disappears before the transition is complete
+ */
@Presubmit
@Test
- fun screenshotLayerBecomesInvisible() {
+ fun rotationLayerAppearsAndVanishes() {
testSpec.assertLayers {
this.isVisible(testApp.component)
.then()
- .isVisible(ROTATION_COMPONENT)
+ .isVisible(FlickerComponentName.ROTATION)
.then()
.isVisible(testApp.component)
}
}
+ /**
+ * Checks that the status bar window is visible and above the app windows in all WM
+ * trace entries
+ */
@Presubmit
@Test
fun statusBarWindowIsVisible() {
testSpec.statusBarWindowIsVisible()
}
+ /**
+ * Checks that the status bar layer is visible at the start and end of the transition
+ */
@Presubmit
@Test
fun statusBarLayerIsVisible() {
testSpec.statusBarLayerIsVisible()
}
+ /**
+ * Checks the position of the status bar at the start and end of the transition
+ */
@Presubmit
@Test
fun statusBarLayerRotatesScales() {
@@ -97,13 +143,26 @@
testSpec.config.startRotation, testSpec.config.endRotation)
}
+ /** {@inheritDoc} */
@FlakyTest
@Test
override fun navBarLayerRotatesAndScales() {
super.navBarLayerRotatesAndScales()
}
+ /** {@inheritDoc} */
+ @FlakyTest
+ @Test
+ override fun visibleLayersShownMoreThanOneConsecutiveEntry() =
+ super.visibleLayersShownMoreThanOneConsecutiveEntry()
+
companion object {
+ /**
+ * Creates the test configurations.
+ *
+ * See [FlickerTestParameterFactory.getConfigRotationTests] for configuring
+ * repetitions, screen orientation and navigation modes.
+ */
@Parameterized.Parameters(name = "{0}")
@JvmStatic
fun getParams(): Collection<FlickerTestParameter> {
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/RotationTransition.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/RotationTransition.kt
index 612ff9d..2b03396 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/RotationTransition.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/RotationTransition.kt
@@ -17,7 +17,6 @@
package com.android.server.wm.flicker.rotation
import android.app.Instrumentation
-import android.content.ComponentName
import android.platform.test.annotations.Presubmit
import androidx.test.platform.app.InstrumentationRegistry
import com.android.server.wm.flicker.FlickerBuilderProvider
@@ -31,9 +30,12 @@
import com.android.server.wm.flicker.navBarWindowIsVisible
import com.android.server.wm.flicker.entireScreenCovered
import com.android.server.wm.flicker.startRotation
-import com.android.server.wm.traces.parser.windowmanager.WindowManagerStateHelper
+import com.android.server.wm.traces.common.FlickerComponentName
import org.junit.Test
+/**
+ * Base class for app rotation tests
+ */
abstract class RotationTransition(protected val testSpec: FlickerTestParameter) {
protected abstract val testApp: StandardAppHelper
@@ -55,6 +57,10 @@
}
}
+ /**
+ * Entry point for the test runner. It will use this method to initialize and cache
+ * flicker executions
+ */
@FlickerBuilderProvider
fun buildFlicker(): FlickerBuilder {
return FlickerBuilder(instrumentation).apply {
@@ -62,18 +68,28 @@
}
}
+ /**
+ * Checks that the navigation bar window is visible and above the app windows in all WM
+ * trace entries
+ */
@Presubmit
@Test
open fun navBarWindowIsVisible() {
testSpec.navBarWindowIsVisible()
}
+ /**
+ * Checks that the navigation bar layer is visible at the start and end of the transition
+ */
@Presubmit
@Test
open fun navBarLayerIsVisible() {
testSpec.navBarLayerIsVisible()
}
+ /**
+ * Checks the position of the navigation bar at the start and end of the transition
+ */
@Presubmit
@Test
open fun navBarLayerRotatesAndScales() {
@@ -81,19 +97,27 @@
testSpec.config.startRotation, testSpec.config.endRotation)
}
+ /**
+ * Checks that all layers that are visible on the trace, are visible for at least 2
+ * consecutive entries.
+ */
@Presubmit
@Test
open fun visibleLayersShownMoreThanOneConsecutiveEntry() {
testSpec.assertLayers {
this.visibleLayersShownMoreThanOneConsecutiveEntry(
- ignoreLayers = listOf(WindowManagerStateHelper.SPLASH_SCREEN_COMPONENT,
- WindowManagerStateHelper.SNAPSHOT_COMPONENT,
- ComponentName("", "SecondaryHomeHandle")
+ ignoreLayers = listOf(FlickerComponentName.SPLASH_SCREEN,
+ FlickerComponentName.SNAPSHOT,
+ FlickerComponentName("", "SecondaryHomeHandle")
)
)
}
}
+ /**
+ * Checks that all windows that are visible on the trace, are visible for at least 2
+ * consecutive entries.
+ */
@Presubmit
@Test
open fun visibleWindowsShownMoreThanOneConsecutiveEntry() {
@@ -102,10 +126,16 @@
}
}
+ /**
+ * Checks that all parts of the screen are covered during the transition
+ */
@Presubmit
@Test
open fun entireScreenCovered() = testSpec.entireScreenCovered()
+ /**
+ * Checks that the focus doesn't change during animation
+ */
@Presubmit
@Test
open fun focusDoesNotChange() {
@@ -114,6 +144,9 @@
}
}
+ /**
+ * Checks that [testApp] layer covers the entire screen at the start of the transition
+ */
@Presubmit
@Test
open fun appLayerRotates_StartingPos() {
@@ -124,6 +157,9 @@
}
}
+ /**
+ * Checks that [testApp] layer covers the entire screen at the end of the transition
+ */
@Presubmit
@Test
open fun appLayerRotates_EndingPos() {
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/SeamlessAppRotationTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/SeamlessAppRotationTest.kt
index 48efe73..310f04b 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/SeamlessAppRotationTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/SeamlessAppRotationTest.kt
@@ -27,7 +27,7 @@
import com.android.server.wm.flicker.dsl.FlickerBuilder
import com.android.server.wm.flicker.helpers.SeamlessRotationAppHelper
import com.android.server.wm.flicker.testapp.ActivityOptions
-import com.android.server.wm.traces.parser.windowmanager.WindowManagerStateHelper
+import com.android.server.wm.traces.common.FlickerComponentName
import org.junit.FixMethodOrder
import org.junit.Test
import org.junit.runner.RunWith
@@ -35,8 +35,41 @@
import org.junit.runners.Parameterized
/**
- * Cycle through supported app rotations using seamless rotations.
+ * Test opening an app and cycling through app rotations using seamless rotations
+ *
+ * Currently runs:
+ * 0 -> 90 degrees
+ * 0 -> 90 degrees (with starved UI thread)
+ * 90 -> 0 degrees
+ * 90 -> 0 degrees (with starved UI thread)
+ *
+ * Actions:
+ * Launch an app in fullscreen and supporting seamless rotation (via intent)
+ * Set initial device orientation
+ * Start tracing
+ * Change device orientation
+ * Stop tracing
+ *
* To run this test: `atest FlickerTests:SeamlessAppRotationTest`
+ *
+ * To run only the presubmit assertions add: `--
+ * --module-arg FlickerTests:exclude-annotation:androidx.test.filters.FlakyTest
+ * --module-arg FlickerTests:include-annotation:android.platform.test.annotations.Presubmit`
+ *
+ * To run only the postsubmit assertions add: `--
+ * --module-arg FlickerTests:exclude-annotation:androidx.test.filters.FlakyTest
+ * --module-arg FlickerTests:include-annotation:android.platform.test.annotations.Postsubmit`
+ *
+ * To run only the flaky assertions add: `--
+ * --module-arg FlickerTests:include-annotation:androidx.test.filters.FlakyTest`
+ *
+ * Notes:
+ * 1. Some default assertions (e.g., nav bar, status bar and screen covered)
+ * are inherited [RotationTransition]
+ * 2. Part of the test setup occurs automatically via
+ * [com.android.server.wm.flicker.TransitionRunnerWithRules],
+ * including configuring navigation mode, initial orientation and ensuring no
+ * apps are running before setup
*/
@RequiresDevice
@RunWith(Parameterized::class)
@@ -61,6 +94,9 @@
}
}
+ /**
+ * Checks that [testApp] window is always in full screen
+ */
@Presubmit
@Test
fun appWindowFullScreen() {
@@ -75,6 +111,9 @@
}
}
+ /**
+ * Checks that [testApp] window is always with seamless rotation
+ */
@Presubmit
@Test
fun appWindowSeamlessRotation() {
@@ -90,6 +129,9 @@
}
}
+ /**
+ * Checks that [testApp] window is always visible
+ */
@Presubmit
@Test
fun appLayerAlwaysVisible() {
@@ -98,6 +140,9 @@
}
}
+ /**
+ * Checks that [testApp] layer covers the entire screen during the whole transition
+ */
@Presubmit
@Test
fun appLayerRotates() {
@@ -110,29 +155,36 @@
}
}
+ /**
+ * Checks that the [FlickerComponentName.STATUS_BAR] window is invisible during the whole
+ * transition
+ */
@Presubmit
@Test
fun statusBarWindowIsAlwaysInvisible() {
testSpec.assertWm {
- this.isAboveAppWindowInvisible(WindowManagerStateHelper.STATUS_BAR_COMPONENT)
+ this.isAboveAppWindowInvisible(FlickerComponentName.STATUS_BAR)
}
}
+ /**
+ * Checks that the [FlickerComponentName.STATUS_BAR] layer is invisible during the whole
+ * transition
+ */
@Presubmit
@Test
fun statusBarLayerIsAlwaysInvisible() {
testSpec.assertLayers {
- this.isInvisible(WindowManagerStateHelper.STATUS_BAR_COMPONENT)
+ this.isInvisible(FlickerComponentName.STATUS_BAR)
}
}
+ /** {@inheritDoc} */
@FlakyTest
@Test
override fun navBarLayerRotatesAndScales() = super.navBarLayerRotatesAndScales()
companion object {
- private val testFactory = FlickerTestParameterFactory.getInstance()
-
private val Map<String, Any?>.starveUiThread
get() = this.getOrDefault(ActivityOptions.EXTRA_STARVE_UI_THREAD, false) as Boolean
@@ -144,20 +196,34 @@
return config
}
+ /**
+ * Creates the test configurations for seamless rotation based on the default rotation
+ * tests from [FlickerTestParameterFactory.getConfigRotationTests], but adding an
+ * additional flag ([ActivityOptions.EXTRA_STARVE_UI_THREAD]) to indicate if the app
+ * should starve the UI thread of not
+ */
@JvmStatic
private fun getConfigurations(): List<FlickerTestParameter> {
- return testFactory.getConfigRotationTests(repetitions = 2).flatMap {
- val defaultRun = it.createConfig(starveUiThread = false)
- val busyUiRun = it.createConfig(starveUiThread = true)
- listOf(
- FlickerTestParameter(defaultRun),
- FlickerTestParameter(busyUiRun,
- name = "${FlickerTestParameter.defaultName(busyUiRun)}_BUSY_UI_THREAD"
+ return FlickerTestParameterFactory.getInstance()
+ .getConfigRotationTests(repetitions = 2)
+ .flatMap {
+ val defaultRun = it.createConfig(starveUiThread = false)
+ val busyUiRun = it.createConfig(starveUiThread = true)
+ listOf(
+ FlickerTestParameter(defaultRun),
+ FlickerTestParameter(busyUiRun,
+ name = "${FlickerTestParameter.defaultName(busyUiRun)}_BUSY_UI_THREAD"
+ )
)
- )
- }
+ }
}
+ /**
+ * Creates the test configurations.
+ *
+ * See [FlickerTestParameterFactory.getConfigRotationTests] for configuring
+ * repetitions, screen orientation and navigation modes.
+ */
@Parameterized.Parameters(name = "{0}")
@JvmStatic
fun getParams(): Collection<FlickerTestParameter> {