diff options
142 files changed, 2214 insertions, 3004 deletions
diff --git a/core/api/current.txt b/core/api/current.txt index c8a43db2f9c2..487e57d114c9 100644 --- a/core/api/current.txt +++ b/core/api/current.txt @@ -7216,8 +7216,8 @@ package android.app { method public android.content.Intent getCropAndSetWallpaperIntent(android.net.Uri); method public int getDesiredMinimumHeight(); method public int getDesiredMinimumWidth(); - method @RequiresPermission(android.Manifest.permission.READ_EXTERNAL_STORAGE) public android.graphics.drawable.Drawable getDrawable(); - method @RequiresPermission(android.Manifest.permission.READ_EXTERNAL_STORAGE) public android.graphics.drawable.Drawable getFastDrawable(); + method @Nullable @RequiresPermission(android.Manifest.permission.READ_EXTERNAL_STORAGE) public android.graphics.drawable.Drawable getDrawable(); + method @Nullable @RequiresPermission(android.Manifest.permission.READ_EXTERNAL_STORAGE) public android.graphics.drawable.Drawable getFastDrawable(); method public static android.app.WallpaperManager getInstance(android.content.Context); method @Nullable public android.app.WallpaperColors getWallpaperColors(int); method @RequiresPermission(android.Manifest.permission.READ_EXTERNAL_STORAGE) public android.os.ParcelFileDescriptor getWallpaperFile(int); @@ -7226,8 +7226,8 @@ package android.app { method public boolean hasResourceWallpaper(@RawRes int); method public boolean isSetWallpaperAllowed(); method public boolean isWallpaperSupported(); - method public android.graphics.drawable.Drawable peekDrawable(); - method @RequiresPermission(android.Manifest.permission.READ_EXTERNAL_STORAGE) public android.graphics.drawable.Drawable peekFastDrawable(); + method @Nullable public android.graphics.drawable.Drawable peekDrawable(); + method @Nullable @RequiresPermission(android.Manifest.permission.READ_EXTERNAL_STORAGE) public android.graphics.drawable.Drawable peekFastDrawable(); method public void removeOnColorsChangedListener(@NonNull android.app.WallpaperManager.OnColorsChangedListener); method public void sendWallpaperCommand(android.os.IBinder, String, int, int, int, android.os.Bundle); method @RequiresPermission(android.Manifest.permission.SET_WALLPAPER) public void setBitmap(android.graphics.Bitmap) throws java.io.IOException; diff --git a/core/api/test-current.txt b/core/api/test-current.txt index 93c0c4d7a024..70a23cdf106b 100644 --- a/core/api/test-current.txt +++ b/core/api/test-current.txt @@ -1139,6 +1139,7 @@ package android.hardware.camera2 { public final class CameraManager { method public String[] getCameraIdListNoLazy() throws android.hardware.camera2.CameraAccessException; method @RequiresPermission(allOf={android.Manifest.permission.SYSTEM_CAMERA, android.Manifest.permission.CAMERA}) public void openCamera(@NonNull String, int, @NonNull java.util.concurrent.Executor, @NonNull android.hardware.camera2.CameraDevice.StateCallback) throws android.hardware.camera2.CameraAccessException; + field public static final long OVERRIDE_FRONT_CAMERA_APP_COMPAT = 250678880L; // 0xef10e60L } public abstract static class CameraManager.AvailabilityCallback { diff --git a/core/java/android/app/WallpaperManager.java b/core/java/android/app/WallpaperManager.java index 3e6283e25aa5..162a9976f4f3 100644 --- a/core/java/android/app/WallpaperManager.java +++ b/core/java/android/app/WallpaperManager.java @@ -786,6 +786,7 @@ public class WallpaperManager { * is not able to access the wallpaper. */ @RequiresPermission(android.Manifest.permission.READ_EXTERNAL_STORAGE) + @Nullable public Drawable getDrawable() { final ColorManagementProxy cmProxy = getColorManagementProxy(); Bitmap bm = sGlobals.peekWallpaperBitmap(mContext, true, FLAG_SYSTEM, cmProxy); @@ -798,6 +799,29 @@ public class WallpaperManager { } /** + * Retrieve the requested wallpaper; if + * no wallpaper is set, the requested built-in static wallpaper is returned. + * This is returned as an + * abstract Drawable that you can install in a View to display whatever + * wallpaper the user has currently set. + * <p> + * This method can return null if the requested wallpaper is not available, if + * wallpapers are not supported in the current user, or if the calling app is not + * permitted to access the requested wallpaper. + * + * @param which The {@code FLAG_*} identifier of a valid wallpaper type. Throws + * IllegalArgumentException if an invalid wallpaper is requested. + * @return Returns a Drawable object that will draw the requested wallpaper, + * or {@code null} if the requested wallpaper does not exist or if the calling application + * is not able to access the wallpaper. + * @hide + */ + @RequiresPermission(android.Manifest.permission.READ_EXTERNAL_STORAGE) + @Nullable + public Drawable getDrawable(@SetWallpaperFlags int which) { + return getDrawable(); + } + /** * Obtain a drawable for the built-in static system wallpaper. */ public Drawable getBuiltInDrawable() { @@ -1018,6 +1042,7 @@ public class WallpaperManager { * @return Returns a Drawable object that will draw the wallpaper or a * null pointer if these is none. */ + @Nullable public Drawable peekDrawable() { final ColorManagementProxy cmProxy = getColorManagementProxy(); Bitmap bm = sGlobals.peekWallpaperBitmap(mContext, false, FLAG_SYSTEM, cmProxy); @@ -1030,6 +1055,23 @@ public class WallpaperManager { } /** + * Retrieve the requested wallpaper; if there is no wallpaper set, + * a null pointer is returned. This is returned as an + * abstract Drawable that you can install in a View to display whatever + * wallpaper the user has currently set. + * + * @param which The {@code FLAG_*} identifier of a valid wallpaper type. Throws + * IllegalArgumentException if an invalid wallpaper is requested. + * @return Returns a Drawable object that will draw the wallpaper or a null pointer if these + * is none. + * @hide + */ + @Nullable + public Drawable peekDrawable(@SetWallpaperFlags int which) { + return peekDrawable(); + } + + /** * Like {@link #getDrawable()}, but the returned Drawable has a number * of limitations to reduce its overhead as much as possible. It will * never scale the wallpaper (only centering it if the requested bounds @@ -1043,6 +1085,7 @@ public class WallpaperManager { * @return Returns a Drawable object that will draw the wallpaper. */ @RequiresPermission(android.Manifest.permission.READ_EXTERNAL_STORAGE) + @Nullable public Drawable getFastDrawable() { final ColorManagementProxy cmProxy = getColorManagementProxy(); Bitmap bm = sGlobals.peekWallpaperBitmap(mContext, true, FLAG_SYSTEM, cmProxy); @@ -1053,6 +1096,28 @@ public class WallpaperManager { } /** + * Like {@link #getFastDrawable(int)}, but the returned Drawable has a number + * of limitations to reduce its overhead as much as possible. It will + * never scale the wallpaper (only centering it if the requested bounds + * do match the bitmap bounds, which should not be typical), doesn't + * allow setting an alpha, color filter, or other attributes, etc. The + * bounds of the returned drawable will be initialized to the same bounds + * as the wallpaper, so normally you will not need to touch it. The + * drawable also assumes that it will be used in a context running in + * the same density as the screen (not in density compatibility mode). + * + * @param which The {@code FLAG_*} identifier of a valid wallpaper type. Throws + * IllegalArgumentException if an invalid wallpaper is requested. + * @return Returns a Drawable object that will draw the wallpaper. + * @hide + */ + @RequiresPermission(android.Manifest.permission.READ_EXTERNAL_STORAGE) + @Nullable + public Drawable getFastDrawable(@SetWallpaperFlags int which) { + return getFastDrawable(); + } + + /** * Like {@link #getFastDrawable()}, but if there is no wallpaper set, * a null pointer is returned. * @@ -1060,6 +1125,7 @@ public class WallpaperManager { * wallpaper or a null pointer if these is none. */ @RequiresPermission(android.Manifest.permission.READ_EXTERNAL_STORAGE) + @Nullable public Drawable peekFastDrawable() { final ColorManagementProxy cmProxy = getColorManagementProxy(); Bitmap bm = sGlobals.peekWallpaperBitmap(mContext, false, FLAG_SYSTEM, cmProxy); @@ -1070,6 +1136,22 @@ public class WallpaperManager { } /** + * Like {@link #getFastDrawable()}, but if there is no wallpaper set, + * a null pointer is returned. + * + * @param which The {@code FLAG_*} identifier of a valid wallpaper type. Throws + * IllegalArgumentException if an invalid wallpaper is requested. + * @return Returns an optimized Drawable object that will draw the + * wallpaper or a null pointer if these is none. + * @hide + */ + @RequiresPermission(android.Manifest.permission.READ_EXTERNAL_STORAGE) + @Nullable + public Drawable peekFastDrawable(@SetWallpaperFlags int which) { + return peekFastDrawable(); + } + + /** * Whether the wallpaper supports Wide Color Gamut or not. * @param which The wallpaper whose image file is to be retrieved. Must be a single * defined kind of wallpaper, either {@link #FLAG_SYSTEM} or {@link #FLAG_LOCK}. diff --git a/core/java/android/hardware/Camera.java b/core/java/android/hardware/Camera.java index 3bdd39f5d7d7..5291d2b73891 100644 --- a/core/java/android/hardware/Camera.java +++ b/core/java/android/hardware/Camera.java @@ -29,12 +29,14 @@ import android.annotation.SdkConstant; import android.annotation.SdkConstant.SdkConstantType; import android.app.ActivityThread; import android.app.AppOpsManager; +import android.app.compat.CompatChanges; import android.compat.annotation.UnsupportedAppUsage; import android.content.Context; import android.graphics.ImageFormat; import android.graphics.Point; import android.graphics.Rect; import android.graphics.SurfaceTexture; +import android.hardware.camera2.CameraManager; import android.media.AudioAttributes; import android.media.IAudioService; import android.os.Build; @@ -45,6 +47,7 @@ import android.os.Message; import android.os.Process; import android.os.RemoteException; import android.os.ServiceManager; +import android.os.SystemProperties; import android.renderscript.Allocation; import android.renderscript.Element; import android.renderscript.RSIllegalArgumentException; @@ -281,6 +284,14 @@ public class Camera { */ public native static int getNumberOfCameras(); + private static final boolean sLandscapeToPortrait = + SystemProperties.getBoolean(CameraManager.LANDSCAPE_TO_PORTRAIT_PROP, false); + + private static boolean shouldOverrideToPortrait() { + return CompatChanges.isChangeEnabled(CameraManager.OVERRIDE_FRONT_CAMERA_APP_COMPAT) + && sLandscapeToPortrait; + } + /** * Returns the information about a particular camera. * If {@link #getNumberOfCameras()} returns N, the valid id is 0 to N-1. @@ -290,7 +301,9 @@ public class Camera { * low-level failure). */ public static void getCameraInfo(int cameraId, CameraInfo cameraInfo) { - _getCameraInfo(cameraId, cameraInfo); + boolean overrideToPortrait = shouldOverrideToPortrait(); + + _getCameraInfo(cameraId, overrideToPortrait, cameraInfo); IBinder b = ServiceManager.getService(Context.AUDIO_SERVICE); IAudioService audioService = IAudioService.Stub.asInterface(b); try { @@ -303,7 +316,8 @@ public class Camera { Log.e(TAG, "Audio service is unavailable for queries"); } } - private native static void _getCameraInfo(int cameraId, CameraInfo cameraInfo); + private native static void _getCameraInfo(int cameraId, boolean overrideToPortrait, + CameraInfo cameraInfo); /** * Information about a camera @@ -484,8 +498,9 @@ public class Camera { mEventHandler = null; } + boolean overrideToPortrait = shouldOverrideToPortrait(); return native_setup(new WeakReference<Camera>(this), cameraId, - ActivityThread.currentOpPackageName()); + ActivityThread.currentOpPackageName(), overrideToPortrait); } /** used by Camera#open, Camera#open(int) */ @@ -555,7 +570,8 @@ public class Camera { } @UnsupportedAppUsage - private native int native_setup(Object cameraThis, int cameraId, String packageName); + private native int native_setup(Object cameraThis, int cameraId, String packageName, + boolean overrideToPortrait); private native final void native_release(); diff --git a/core/java/android/hardware/camera2/CameraManager.java b/core/java/android/hardware/camera2/CameraManager.java index dff2f7ed1cf3..7fed2200d606 100644 --- a/core/java/android/hardware/camera2/CameraManager.java +++ b/core/java/android/hardware/camera2/CameraManager.java @@ -23,6 +23,10 @@ import android.annotation.RequiresPermission; import android.annotation.SystemApi; import android.annotation.SystemService; import android.annotation.TestApi; +import android.app.compat.CompatChanges; +import android.compat.annotation.ChangeId; +import android.compat.annotation.Disabled; +import android.compat.annotation.Overridable; import android.content.Context; import android.content.pm.PackageManager; import android.graphics.Point; @@ -104,6 +108,24 @@ public final class CameraManager { private final boolean mHasOpenCloseListenerPermission; /** + * Force camera output to be rotated to portrait orientation on landscape cameras. + * Many apps do not handle this situation and display stretched images otherwise. + * @hide + */ + @ChangeId + @Overridable + @Disabled + @TestApi + public static final long OVERRIDE_FRONT_CAMERA_APP_COMPAT = 250678880L; + + /** + * System property for allowing the above + * @hide + */ + public static final String LANDSCAPE_TO_PORTRAIT_PROP = + "camera.enable_landscape_to_portrait"; + + /** * @hide */ public CameraManager(Context context) { @@ -520,7 +542,8 @@ public final class CameraManager { for (String physicalCameraId : physicalCameraIds) { CameraMetadataNative physicalCameraInfo = cameraService.getCameraCharacteristics(physicalCameraId, - mContext.getApplicationInfo().targetSdkVersion); + mContext.getApplicationInfo().targetSdkVersion, + /*overrideToPortrait*/false); StreamConfiguration[] configs = physicalCameraInfo.get( CameraCharacteristics. SCALER_PHYSICAL_CAMERA_MULTI_RESOLUTION_STREAM_CONFIGURATIONS); @@ -579,8 +602,9 @@ public final class CameraManager { try { Size displaySize = getDisplaySize(); + boolean overrideToPortrait = shouldOverrideToPortrait(); CameraMetadataNative info = cameraService.getCameraCharacteristics(cameraId, - mContext.getApplicationInfo().targetSdkVersion); + mContext.getApplicationInfo().targetSdkVersion, overrideToPortrait); try { info.setCameraId(Integer.parseInt(cameraId)); } catch (NumberFormatException e) { @@ -697,9 +721,12 @@ public final class CameraManager { ICameraService.ERROR_DISCONNECTED, "Camera service is currently unavailable"); } + + boolean overrideToPortrait = shouldOverrideToPortrait(); cameraUser = cameraService.connectDevice(callbacks, cameraId, - mContext.getOpPackageName(), mContext.getAttributionTag(), uid, - oomScoreOffset, mContext.getApplicationInfo().targetSdkVersion); + mContext.getOpPackageName(), mContext.getAttributionTag(), uid, + oomScoreOffset, mContext.getApplicationInfo().targetSdkVersion, + overrideToPortrait); } catch (ServiceSpecificException e) { if (e.errorCode == ICameraService.ERROR_DEPRECATED_HAL) { throw new AssertionError("Should've gone down the shim path"); @@ -1127,6 +1154,11 @@ public final class CameraManager { return CameraManagerGlobal.get().getTorchStrengthLevel(cameraId); } + private static boolean shouldOverrideToPortrait() { + return CompatChanges.isChangeEnabled(OVERRIDE_FRONT_CAMERA_APP_COMPAT) + && CameraManagerGlobal.sLandscapeToPortrait; + } + /** * A callback for camera devices becoming available or unavailable to open. * @@ -1573,6 +1605,9 @@ public final class CameraManager { public static final boolean sCameraServiceDisabled = SystemProperties.getBoolean("config.disable_cameraservice", false); + public static final boolean sLandscapeToPortrait = + SystemProperties.getBoolean(LANDSCAPE_TO_PORTRAIT_PROP, false); + public static CameraManagerGlobal get() { return gCameraManager; } diff --git a/core/java/android/hardware/display/AmbientDisplayConfiguration.java b/core/java/android/hardware/display/AmbientDisplayConfiguration.java index 8c71b363eb7b..47541ca16cda 100644 --- a/core/java/android/hardware/display/AmbientDisplayConfiguration.java +++ b/core/java/android/hardware/display/AmbientDisplayConfiguration.java @@ -40,6 +40,7 @@ public class AmbientDisplayConfiguration { private static final String TAG = "AmbientDisplayConfig"; private final Context mContext; private final boolean mAlwaysOnByDefault; + private final boolean mPickupGestureEnabledByDefault; /** Copied from android.provider.Settings.Secure since these keys are hidden. */ private static final String[] DOZE_SETTINGS = { @@ -65,6 +66,8 @@ public class AmbientDisplayConfiguration { public AmbientDisplayConfiguration(Context context) { mContext = context; mAlwaysOnByDefault = mContext.getResources().getBoolean(R.bool.config_dozeAlwaysOnEnabled); + mPickupGestureEnabledByDefault = + mContext.getResources().getBoolean(R.bool.config_dozePickupGestureEnabled); } /** @hide */ @@ -95,7 +98,8 @@ public class AmbientDisplayConfiguration { /** @hide */ public boolean pickupGestureEnabled(int user) { - return boolSettingDefaultOn(Settings.Secure.DOZE_PICK_UP_GESTURE, user) + return boolSetting(Settings.Secure.DOZE_PICK_UP_GESTURE, user, + mPickupGestureEnabledByDefault ? 1 : 0) && dozePickupSensorAvailable(); } diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java index d21ba014e166..550bf348d1c7 100644 --- a/core/java/android/view/ViewRootImpl.java +++ b/core/java/android/view/ViewRootImpl.java @@ -720,7 +720,7 @@ public final class ViewRootImpl implements ViewParent, private final InsetsState mTempInsets = new InsetsState(); private final InsetsSourceControl[] mTempControls = new InsetsSourceControl[SIZE]; private final WindowConfiguration mTempWinConfig = new WindowConfiguration(); - private float mInvSizeCompatScale = 1f; + private float mInvCompatScale = 1f; final ViewTreeObserver.InternalInsetsInfo mLastGivenInsets = new ViewTreeObserver.InternalInsetsInfo(); @@ -1119,11 +1119,11 @@ public final class ViewRootImpl implements ViewParent, private WindowConfiguration getCompatWindowConfiguration() { final WindowConfiguration winConfig = getConfiguration().windowConfiguration; - if (mInvSizeCompatScale == 1f) { + if (mInvCompatScale == 1f) { return winConfig; } mTempWinConfig.setTo(winConfig); - mTempWinConfig.scale(mInvSizeCompatScale); + mTempWinConfig.scale(mInvCompatScale); return mTempWinConfig; } @@ -1257,11 +1257,11 @@ public final class ViewRootImpl implements ViewParent, controlInsetsForCompatibility(mWindowAttributes); Rect attachedFrame = new Rect(); - final float[] sizeCompatScale = { 1f }; + final float[] compatScale = { 1f }; res = mWindowSession.addToDisplayAsUser(mWindow, mWindowAttributes, getHostVisibility(), mDisplay.getDisplayId(), userId, mInsetsController.getRequestedVisibilities(), inputChannel, mTempInsets, - mTempControls, attachedFrame, sizeCompatScale); + mTempControls, attachedFrame, compatScale); if (!attachedFrame.isValid()) { attachedFrame = null; } @@ -1271,8 +1271,8 @@ public final class ViewRootImpl implements ViewParent, mTranslator.translateRectInScreenToAppWindow(attachedFrame); } mTmpFrames.attachedFrame = attachedFrame; - mTmpFrames.sizeCompatScale = sizeCompatScale[0]; - mInvSizeCompatScale = 1f / sizeCompatScale[0]; + mTmpFrames.compatScale = compatScale[0]; + mInvCompatScale = 1f / compatScale[0]; } catch (RemoteException e) { mAdded = false; mView = null; @@ -1794,24 +1794,24 @@ public final class ViewRootImpl implements ViewParent, mTranslator.translateRectInScreenToAppWindow(displayFrame); mTranslator.translateRectInScreenToAppWindow(attachedFrame); } - final float sizeCompatScale = frames.sizeCompatScale; + final float compatScale = frames.compatScale; final boolean frameChanged = !mWinFrame.equals(frame); final boolean configChanged = !mLastReportedMergedConfiguration.equals(mergedConfiguration); final boolean attachedFrameChanged = LOCAL_LAYOUT && !Objects.equals(mTmpFrames.attachedFrame, attachedFrame); final boolean displayChanged = mDisplay.getDisplayId() != displayId; final boolean resizeModeChanged = mResizeMode != resizeMode; - final boolean sizeCompatScaleChanged = mTmpFrames.sizeCompatScale != sizeCompatScale; + final boolean compatScaleChanged = mTmpFrames.compatScale != compatScale; if (msg == MSG_RESIZED && !frameChanged && !configChanged && !attachedFrameChanged && !displayChanged && !resizeModeChanged && !forceNextWindowRelayout - && !sizeCompatScaleChanged) { + && !compatScaleChanged) { return; } mPendingDragResizing = resizeMode != RESIZE_MODE_INVALID; mResizeMode = resizeMode; - mTmpFrames.sizeCompatScale = sizeCompatScale; - mInvSizeCompatScale = 1f / sizeCompatScale; + mTmpFrames.compatScale = compatScale; + mInvCompatScale = 1f / compatScale; if (configChanged) { // If configuration changed - notify about that and, maybe, about move to display. @@ -8240,7 +8240,7 @@ public final class ViewRootImpl implements ViewParent, mTranslator.translateInsetsStateInScreenToAppWindow(mTempInsets); mTranslator.translateSourceControlsInScreenToAppWindow(mTempControls); } - mInvSizeCompatScale = 1f / mTmpFrames.sizeCompatScale; + mInvCompatScale = 1f / mTmpFrames.compatScale; mInsetsController.onStateChanged(mTempInsets); mInsetsController.onControlsChanged(mTempControls); diff --git a/core/java/android/window/ClientWindowFrames.java b/core/java/android/window/ClientWindowFrames.java index f274d1a15ba5..0ce076b6eb96 100644 --- a/core/java/android/window/ClientWindowFrames.java +++ b/core/java/android/window/ClientWindowFrames.java @@ -49,7 +49,7 @@ public class ClientWindowFrames implements Parcelable { public boolean isParentFrameClippedByDisplayCutout; - public float sizeCompatScale = 1f; + public float compatScale = 1f; public ClientWindowFrames() { } @@ -62,7 +62,7 @@ public class ClientWindowFrames implements Parcelable { attachedFrame = new Rect(other.attachedFrame); } isParentFrameClippedByDisplayCutout = other.isParentFrameClippedByDisplayCutout; - sizeCompatScale = other.sizeCompatScale; + compatScale = other.compatScale; } private ClientWindowFrames(Parcel in) { @@ -76,7 +76,7 @@ public class ClientWindowFrames implements Parcelable { parentFrame.readFromParcel(in); attachedFrame = in.readTypedObject(Rect.CREATOR); isParentFrameClippedByDisplayCutout = in.readBoolean(); - sizeCompatScale = in.readFloat(); + compatScale = in.readFloat(); } @Override @@ -86,7 +86,7 @@ public class ClientWindowFrames implements Parcelable { parentFrame.writeToParcel(dest, flags); dest.writeTypedObject(attachedFrame, flags); dest.writeBoolean(isParentFrameClippedByDisplayCutout); - dest.writeFloat(sizeCompatScale); + dest.writeFloat(compatScale); } @Override @@ -97,7 +97,7 @@ public class ClientWindowFrames implements Parcelable { + " parentFrame=" + parentFrame.toShortString(sb) + (attachedFrame != null ? " attachedFrame=" + attachedFrame.toShortString() : "") + (isParentFrameClippedByDisplayCutout ? " parentClippedByDisplayCutout" : "") - + (sizeCompatScale != 1f ? " sizeCompatScale=" + sizeCompatScale : "") + "}"; + + (compatScale != 1f ? " sizeCompatScale=" + compatScale : "") + "}"; } @Override diff --git a/core/java/android/window/TaskFragmentCreationParams.java b/core/java/android/window/TaskFragmentCreationParams.java index 81ab7836435d..c9ddf92d3740 100644 --- a/core/java/android/window/TaskFragmentCreationParams.java +++ b/core/java/android/window/TaskFragmentCreationParams.java @@ -20,6 +20,7 @@ import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED; import static android.app.WindowConfiguration.WindowingMode; import android.annotation.NonNull; +import android.annotation.Nullable; import android.annotation.TestApi; import android.graphics.Rect; import android.os.IBinder; @@ -57,14 +58,33 @@ public final class TaskFragmentCreationParams implements Parcelable { /** The initial windowing mode of the TaskFragment. Inherits from parent if not set. */ @WindowingMode - private int mWindowingMode = WINDOWING_MODE_UNDEFINED; + private final int mWindowingMode; + + /** + * The fragment token of the paired primary TaskFragment. + * When it is set, the new TaskFragment will be positioned right above the paired TaskFragment. + * Otherwise, the new TaskFragment will be positioned on the top of the Task by default. + * + * This is different from {@link WindowContainerTransaction#setAdjacentTaskFragments} as we may + * set this when the pair of TaskFragments are stacked, while adjacent is only set on the pair + * of TaskFragments that are in split. + * + * This is needed in case we need to launch a placeholder Activity to split below a transparent + * always-expand Activity. + */ + @Nullable + private final IBinder mPairedPrimaryFragmentToken; private TaskFragmentCreationParams( - @NonNull TaskFragmentOrganizerToken organizer, - @NonNull IBinder fragmentToken, @NonNull IBinder ownerToken) { + @NonNull TaskFragmentOrganizerToken organizer, @NonNull IBinder fragmentToken, + @NonNull IBinder ownerToken, @NonNull Rect initialBounds, + @WindowingMode int windowingMode, @Nullable IBinder pairedPrimaryFragmentToken) { mOrganizer = organizer; mFragmentToken = fragmentToken; mOwnerToken = ownerToken; + mInitialBounds.set(initialBounds); + mWindowingMode = windowingMode; + mPairedPrimaryFragmentToken = pairedPrimaryFragmentToken; } @NonNull @@ -92,12 +112,22 @@ public final class TaskFragmentCreationParams implements Parcelable { return mWindowingMode; } + /** + * TODO(b/232476698): remove the hide with adding CTS for this in next release. + * @hide + */ + @Nullable + public IBinder getPairedPrimaryFragmentToken() { + return mPairedPrimaryFragmentToken; + } + private TaskFragmentCreationParams(Parcel in) { mOrganizer = TaskFragmentOrganizerToken.CREATOR.createFromParcel(in); mFragmentToken = in.readStrongBinder(); mOwnerToken = in.readStrongBinder(); mInitialBounds.readFromParcel(in); mWindowingMode = in.readInt(); + mPairedPrimaryFragmentToken = in.readStrongBinder(); } /** @hide */ @@ -108,6 +138,7 @@ public final class TaskFragmentCreationParams implements Parcelable { dest.writeStrongBinder(mOwnerToken); mInitialBounds.writeToParcel(dest, flags); dest.writeInt(mWindowingMode); + dest.writeStrongBinder(mPairedPrimaryFragmentToken); } @NonNull @@ -132,6 +163,7 @@ public final class TaskFragmentCreationParams implements Parcelable { + " ownerToken=" + mOwnerToken + " initialBounds=" + mInitialBounds + " windowingMode=" + mWindowingMode + + " pairedFragmentToken=" + mPairedPrimaryFragmentToken + "}"; } @@ -159,6 +191,9 @@ public final class TaskFragmentCreationParams implements Parcelable { @WindowingMode private int mWindowingMode = WINDOWING_MODE_UNDEFINED; + @Nullable + private IBinder mPairedPrimaryFragmentToken; + public Builder(@NonNull TaskFragmentOrganizerToken organizer, @NonNull IBinder fragmentToken, @NonNull IBinder ownerToken) { mOrganizer = organizer; @@ -180,14 +215,29 @@ public final class TaskFragmentCreationParams implements Parcelable { return this; } + /** + * Sets the fragment token of the paired primary TaskFragment. + * When it is set, the new TaskFragment will be positioned right above the paired + * TaskFragment. Otherwise, the new TaskFragment will be positioned on the top of the Task + * by default. + * + * This is needed in case we need to launch a placeholder Activity to split below a + * transparent always-expand Activity. + * + * TODO(b/232476698): remove the hide with adding CTS for this in next release. + * @hide + */ + @NonNull + public Builder setPairedPrimaryFragmentToken(@Nullable IBinder fragmentToken) { + mPairedPrimaryFragmentToken = fragmentToken; + return this; + } + /** Constructs the options to create TaskFragment with. */ @NonNull public TaskFragmentCreationParams build() { - final TaskFragmentCreationParams result = new TaskFragmentCreationParams( - mOrganizer, mFragmentToken, mOwnerToken); - result.mInitialBounds.set(mInitialBounds); - result.mWindowingMode = mWindowingMode; - return result; + return new TaskFragmentCreationParams(mOrganizer, mFragmentToken, mOwnerToken, + mInitialBounds, mWindowingMode, mPairedPrimaryFragmentToken); } } } diff --git a/core/jni/android_hardware_Camera.cpp b/core/jni/android_hardware_Camera.cpp index 365a18d174c9..a8abe50a9755 100644 --- a/core/jni/android_hardware_Camera.cpp +++ b/core/jni/android_hardware_Camera.cpp @@ -529,9 +529,8 @@ static jint android_hardware_Camera_getNumberOfCameras(JNIEnv *env, jobject thiz return Camera::getNumberOfCameras(); } -static void android_hardware_Camera_getCameraInfo(JNIEnv *env, jobject thiz, - jint cameraId, jobject info_obj) -{ +static void android_hardware_Camera_getCameraInfo(JNIEnv *env, jobject thiz, jint cameraId, + jboolean overrideToPortrait, jobject info_obj) { CameraInfo cameraInfo; if (cameraId >= Camera::getNumberOfCameras() || cameraId < 0) { ALOGE("%s: Unknown camera ID %d", __FUNCTION__, cameraId); @@ -539,7 +538,7 @@ static void android_hardware_Camera_getCameraInfo(JNIEnv *env, jobject thiz, return; } - status_t rc = Camera::getCameraInfo(cameraId, &cameraInfo); + status_t rc = Camera::getCameraInfo(cameraId, overrideToPortrait, &cameraInfo); if (rc != NO_ERROR) { jniThrowRuntimeException(env, "Fail to get camera info"); return; @@ -555,9 +554,9 @@ static void android_hardware_Camera_getCameraInfo(JNIEnv *env, jobject thiz, } // connect to camera service -static jint android_hardware_Camera_native_setup(JNIEnv *env, jobject thiz, - jobject weak_this, jint cameraId, jstring clientPackageName) -{ +static jint android_hardware_Camera_native_setup(JNIEnv *env, jobject thiz, jobject weak_this, + jint cameraId, jstring clientPackageName, + jboolean overrideToPortrait) { // Convert jstring to String16 const char16_t *rawClientName = reinterpret_cast<const char16_t*>( env->GetStringChars(clientPackageName, NULL)); @@ -567,8 +566,9 @@ static jint android_hardware_Camera_native_setup(JNIEnv *env, jobject thiz, reinterpret_cast<const jchar*>(rawClientName)); int targetSdkVersion = android_get_application_target_sdk_version(); - sp<Camera> camera = Camera::connect(cameraId, clientName, Camera::USE_CALLING_UID, - Camera::USE_CALLING_PID, targetSdkVersion); + sp<Camera> camera = + Camera::connect(cameraId, clientName, Camera::USE_CALLING_UID, Camera::USE_CALLING_PID, + targetSdkVersion, overrideToPortrait); if (camera == NULL) { return -EACCES; } @@ -596,7 +596,7 @@ static jint android_hardware_Camera_native_setup(JNIEnv *env, jobject thiz, // Update default display orientation in case the sensor is reverse-landscape CameraInfo cameraInfo; - status_t rc = Camera::getCameraInfo(cameraId, &cameraInfo); + status_t rc = Camera::getCameraInfo(cameraId, overrideToPortrait, &cameraInfo); if (rc != NO_ERROR) { ALOGE("%s: getCameraInfo error: %d", __FUNCTION__, rc); return rc; @@ -1051,93 +1051,43 @@ static int32_t android_hardware_Camera_getAudioRestriction( //------------------------------------------------- static const JNINativeMethod camMethods[] = { - { "getNumberOfCameras", - "()I", - (void *)android_hardware_Camera_getNumberOfCameras }, - { "_getCameraInfo", - "(ILandroid/hardware/Camera$CameraInfo;)V", - (void*)android_hardware_Camera_getCameraInfo }, - { "native_setup", - "(Ljava/lang/Object;ILjava/lang/String;)I", - (void*)android_hardware_Camera_native_setup }, - { "native_release", - "()V", - (void*)android_hardware_Camera_release }, - { "setPreviewSurface", - "(Landroid/view/Surface;)V", - (void *)android_hardware_Camera_setPreviewSurface }, - { "setPreviewTexture", - "(Landroid/graphics/SurfaceTexture;)V", - (void *)android_hardware_Camera_setPreviewTexture }, - { "setPreviewCallbackSurface", - "(Landroid/view/Surface;)V", - (void *)android_hardware_Camera_setPreviewCallbackSurface }, - { "startPreview", - "()V", - (void *)android_hardware_Camera_startPreview }, - { "_stopPreview", - "()V", - (void *)android_hardware_Camera_stopPreview }, - { "previewEnabled", - "()Z", - (void *)android_hardware_Camera_previewEnabled }, - { "setHasPreviewCallback", - "(ZZ)V", - (void *)android_hardware_Camera_setHasPreviewCallback }, - { "_addCallbackBuffer", - "([BI)V", - (void *)android_hardware_Camera_addCallbackBuffer }, - { "native_autoFocus", - "()V", - (void *)android_hardware_Camera_autoFocus }, - { "native_cancelAutoFocus", - "()V", - (void *)android_hardware_Camera_cancelAutoFocus }, - { "native_takePicture", - "(I)V", - (void *)android_hardware_Camera_takePicture }, - { "native_setParameters", - "(Ljava/lang/String;)V", - (void *)android_hardware_Camera_setParameters }, - { "native_getParameters", - "()Ljava/lang/String;", - (void *)android_hardware_Camera_getParameters }, - { "reconnect", - "()V", - (void*)android_hardware_Camera_reconnect }, - { "lock", - "()V", - (void*)android_hardware_Camera_lock }, - { "unlock", - "()V", - (void*)android_hardware_Camera_unlock }, - { "startSmoothZoom", - "(I)V", - (void *)android_hardware_Camera_startSmoothZoom }, - { "stopSmoothZoom", - "()V", - (void *)android_hardware_Camera_stopSmoothZoom }, - { "setDisplayOrientation", - "(I)V", - (void *)android_hardware_Camera_setDisplayOrientation }, - { "_enableShutterSound", - "(Z)Z", - (void *)android_hardware_Camera_enableShutterSound }, - { "_startFaceDetection", - "(I)V", - (void *)android_hardware_Camera_startFaceDetection }, - { "_stopFaceDetection", - "()V", - (void *)android_hardware_Camera_stopFaceDetection}, - { "enableFocusMoveCallback", - "(I)V", - (void *)android_hardware_Camera_enableFocusMoveCallback}, - { "setAudioRestriction", - "(I)V", - (void *)android_hardware_Camera_setAudioRestriction}, - { "getAudioRestriction", - "()I", - (void *)android_hardware_Camera_getAudioRestriction}, + {"getNumberOfCameras", "()I", (void *)android_hardware_Camera_getNumberOfCameras}, + {"_getCameraInfo", "(IZLandroid/hardware/Camera$CameraInfo;)V", + (void *)android_hardware_Camera_getCameraInfo}, + {"native_setup", "(Ljava/lang/Object;ILjava/lang/String;Z)I", + (void *)android_hardware_Camera_native_setup}, + {"native_release", "()V", (void *)android_hardware_Camera_release}, + {"setPreviewSurface", "(Landroid/view/Surface;)V", + (void *)android_hardware_Camera_setPreviewSurface}, + {"setPreviewTexture", "(Landroid/graphics/SurfaceTexture;)V", + (void *)android_hardware_Camera_setPreviewTexture}, + {"setPreviewCallbackSurface", "(Landroid/view/Surface;)V", + (void *)android_hardware_Camera_setPreviewCallbackSurface}, + {"startPreview", "()V", (void *)android_hardware_Camera_startPreview}, + {"_stopPreview", "()V", (void *)android_hardware_Camera_stopPreview}, + {"previewEnabled", "()Z", (void *)android_hardware_Camera_previewEnabled}, + {"setHasPreviewCallback", "(ZZ)V", (void *)android_hardware_Camera_setHasPreviewCallback}, + {"_addCallbackBuffer", "([BI)V", (void *)android_hardware_Camera_addCallbackBuffer}, + {"native_autoFocus", "()V", (void *)android_hardware_Camera_autoFocus}, + {"native_cancelAutoFocus", "()V", (void *)android_hardware_Camera_cancelAutoFocus}, + {"native_takePicture", "(I)V", (void *)android_hardware_Camera_takePicture}, + {"native_setParameters", "(Ljava/lang/String;)V", + (void *)android_hardware_Camera_setParameters}, + {"native_getParameters", "()Ljava/lang/String;", + (void *)android_hardware_Camera_getParameters}, + {"reconnect", "()V", (void *)android_hardware_Camera_reconnect}, + {"lock", "()V", (void *)android_hardware_Camera_lock}, + {"unlock", "()V", (void *)android_hardware_Camera_unlock}, + {"startSmoothZoom", "(I)V", (void *)android_hardware_Camera_startSmoothZoom}, + {"stopSmoothZoom", "()V", (void *)android_hardware_Camera_stopSmoothZoom}, + {"setDisplayOrientation", "(I)V", (void *)android_hardware_Camera_setDisplayOrientation}, + {"_enableShutterSound", "(Z)Z", (void *)android_hardware_Camera_enableShutterSound}, + {"_startFaceDetection", "(I)V", (void *)android_hardware_Camera_startFaceDetection}, + {"_stopFaceDetection", "()V", (void *)android_hardware_Camera_stopFaceDetection}, + {"enableFocusMoveCallback", "(I)V", + (void *)android_hardware_Camera_enableFocusMoveCallback}, + {"setAudioRestriction", "(I)V", (void *)android_hardware_Camera_setAudioRestriction}, + {"getAudioRestriction", "()I", (void *)android_hardware_Camera_getAudioRestriction}, }; struct field { diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml index 0c707fce7c7e..18c29d196ada 100644 --- a/core/res/res/values/config.xml +++ b/core/res/res/values/config.xml @@ -2543,6 +2543,10 @@ states. --> <bool name="config_dozeAlwaysOnDisplayAvailable">false</bool> + <!-- Control whether the pickup gesture is enabled by default. This value will be used + during initialization when the setting is still null. --> + <bool name="config_dozePickupGestureEnabled">true</bool> + <!-- Control whether the always on display mode is enabled by default. This value will be used during initialization when the setting is still null. --> <bool name="config_dozeAlwaysOnEnabled">true</bool> diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml index 9722e79b700a..b9259218c2d3 100644 --- a/core/res/res/values/symbols.xml +++ b/core/res/res/values/symbols.xml @@ -3814,6 +3814,7 @@ <java-symbol type="string" name="config_cameraShutterSound" /> <java-symbol type="integer" name="config_autoGroupAtCount" /> <java-symbol type="bool" name="config_dozeAlwaysOnDisplayAvailable" /> + <java-symbol type="bool" name="config_dozePickupGestureEnabled" /> <java-symbol type="bool" name="config_dozeAlwaysOnEnabled" /> <java-symbol type="bool" name="config_dozeSupportsAodWallpaper" /> <java-symbol type="bool" name="config_displayBlanksAfterDoze" /> diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/JetpackTaskFragmentOrganizer.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/JetpackTaskFragmentOrganizer.java index d7d43aa19757..b910287aa535 100644 --- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/JetpackTaskFragmentOrganizer.java +++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/JetpackTaskFragmentOrganizer.java @@ -133,8 +133,18 @@ class JetpackTaskFragmentOrganizer extends TaskFragmentOrganizer { } // Create a TaskFragment for the secondary activity. - createTaskFragmentAndStartActivity(wct, secondaryFragmentToken, ownerToken, - secondaryFragmentBounds, windowingMode, activityIntent, + final TaskFragmentCreationParams fragmentOptions = new TaskFragmentCreationParams.Builder( + getOrganizerToken(), secondaryFragmentToken, ownerToken) + .setInitialBounds(secondaryFragmentBounds) + .setWindowingMode(windowingMode) + // Make sure to set the paired fragment token so that the new TaskFragment will be + // positioned right above the paired TaskFragment. + // This is needed in case we need to launch a placeholder Activity to split below a + // transparent always-expand Activity. + .setPairedPrimaryFragmentToken(launchingFragmentToken) + .build(); + createTaskFragment(wct, fragmentOptions); + wct.startActivityInTaskFragment(secondaryFragmentToken, ownerToken, activityIntent, activityOptions); // Set adjacent to each other so that the containers below will be invisible. @@ -173,8 +183,21 @@ class JetpackTaskFragmentOrganizer extends TaskFragmentOrganizer { */ void createTaskFragment(@NonNull WindowContainerTransaction wct, @NonNull IBinder fragmentToken, @NonNull IBinder ownerToken, @NonNull Rect bounds, @WindowingMode int windowingMode) { - final TaskFragmentCreationParams fragmentOptions = - createFragmentOptions(fragmentToken, ownerToken, bounds, windowingMode); + final TaskFragmentCreationParams fragmentOptions = new TaskFragmentCreationParams.Builder( + getOrganizerToken(), fragmentToken, ownerToken) + .setInitialBounds(bounds) + .setWindowingMode(windowingMode) + .build(); + createTaskFragment(wct, fragmentOptions); + } + + void createTaskFragment(@NonNull WindowContainerTransaction wct, + @NonNull TaskFragmentCreationParams fragmentOptions) { + if (mFragmentInfos.containsKey(fragmentOptions.getFragmentToken())) { + throw new IllegalArgumentException( + "There is an existing TaskFragment with fragmentToken=" + + fragmentOptions.getFragmentToken()); + } wct.createTaskFragment(fragmentOptions); } @@ -189,18 +212,6 @@ class JetpackTaskFragmentOrganizer extends TaskFragmentOrganizer { wct.reparentActivityToTaskFragment(fragmentToken, activity.getActivityToken()); } - /** - * @param ownerToken The token of the activity that creates this task fragment. It does not - * have to be a child of this task fragment, but must belong to the same task. - */ - private void createTaskFragmentAndStartActivity(@NonNull WindowContainerTransaction wct, - @NonNull IBinder fragmentToken, @NonNull IBinder ownerToken, @NonNull Rect bounds, - @WindowingMode int windowingMode, @NonNull Intent activityIntent, - @Nullable Bundle activityOptions) { - createTaskFragment(wct, fragmentToken, ownerToken, bounds, windowingMode); - wct.startActivityInTaskFragment(fragmentToken, ownerToken, activityIntent, activityOptions); - } - void setAdjacentTaskFragments(@NonNull WindowContainerTransaction wct, @NonNull IBinder primary, @Nullable IBinder secondary, @Nullable SplitRule splitRule) { WindowContainerTransaction.TaskFragmentAdjacentParams adjacentParams = null; @@ -238,22 +249,6 @@ class JetpackTaskFragmentOrganizer extends TaskFragmentOrganizer { wct.setCompanionTaskFragment(secondary, finishSecondaryWithPrimary ? primary : null); } - TaskFragmentCreationParams createFragmentOptions(@NonNull IBinder fragmentToken, - @NonNull IBinder ownerToken, @NonNull Rect bounds, @WindowingMode int windowingMode) { - if (mFragmentInfos.containsKey(fragmentToken)) { - throw new IllegalArgumentException( - "There is an existing TaskFragment with fragmentToken=" + fragmentToken); - } - - return new TaskFragmentCreationParams.Builder( - getOrganizerToken(), - fragmentToken, - ownerToken) - .setInitialBounds(bounds) - .setWindowingMode(windowingMode) - .build(); - } - void resizeTaskFragment(@NonNull WindowContainerTransaction wct, @NonNull IBinder fragmentToken, @Nullable Rect bounds) { if (!mFragmentInfos.containsKey(fragmentToken)) { 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 6f9a4ff8188a..d3dc05fb92d4 100644 --- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java +++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java @@ -1218,14 +1218,14 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen TaskFragmentContainer newContainer(@NonNull Activity pendingAppearedActivity, @NonNull Activity activityInTask, int taskId) { return newContainer(pendingAppearedActivity, null /* pendingAppearedIntent */, - activityInTask, taskId); + activityInTask, taskId, null /* pairedPrimaryContainer */); } @GuardedBy("mLock") TaskFragmentContainer newContainer(@NonNull Intent pendingAppearedIntent, @NonNull Activity activityInTask, int taskId) { return newContainer(null /* pendingAppearedActivity */, pendingAppearedIntent, - activityInTask, taskId); + activityInTask, taskId, null /* pairedPrimaryContainer */); } /** @@ -1237,10 +1237,13 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen * @param activityInTask activity in the same Task so that we can get the Task bounds * if needed. * @param taskId parent Task of the new TaskFragment. + * @param pairedPrimaryContainer the paired primary {@link TaskFragmentContainer}. When it is + * set, the new container will be added right above it. */ @GuardedBy("mLock") TaskFragmentContainer newContainer(@Nullable Activity pendingAppearedActivity, - @Nullable Intent pendingAppearedIntent, @NonNull Activity activityInTask, int taskId) { + @Nullable Intent pendingAppearedIntent, @NonNull Activity activityInTask, int taskId, + @Nullable TaskFragmentContainer pairedPrimaryContainer) { if (activityInTask == null) { throw new IllegalArgumentException("activityInTask must not be null,"); } @@ -1249,7 +1252,7 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen } final TaskContainer taskContainer = mTaskContainers.get(taskId); final TaskFragmentContainer container = new TaskFragmentContainer(pendingAppearedActivity, - pendingAppearedIntent, taskContainer, this); + pendingAppearedIntent, taskContainer, this, pairedPrimaryContainer); 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 5395fb2ef5ed..47253d388f0d 100644 --- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitPresenter.java +++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitPresenter.java @@ -36,6 +36,7 @@ import android.util.Size; import android.view.View; import android.view.WindowInsets; import android.view.WindowMetrics; +import android.window.TaskFragmentCreationParams; import android.window.WindowContainerTransaction; import androidx.annotation.GuardedBy; @@ -307,10 +308,13 @@ class SplitPresenter extends JetpackTaskFragmentOrganizer { } final int taskId = primaryContainer.getTaskId(); - final TaskFragmentContainer secondaryContainer = mController.newContainer(activityIntent, - launchingActivity, taskId); - final int windowingMode = mController.getTaskContainer(taskId) - .getWindowingModeForSplitTaskFragment(primaryRectBounds); + final TaskFragmentContainer secondaryContainer = mController.newContainer( + null /* pendingAppearedActivity */, activityIntent, launchingActivity, taskId, + // Pass in the primary container to make sure it is added right above the primary. + primaryContainer); + final TaskContainer taskContainer = mController.getTaskContainer(taskId); + final int windowingMode = taskContainer.getWindowingModeForSplitTaskFragment( + primaryRectBounds); mController.registerSplit(wct, primaryContainer, launchingActivity, secondaryContainer, rule, splitAttributes); startActivityToSide(wct, primaryContainer.getTaskFragmentToken(), primaryRectBounds, @@ -412,17 +416,18 @@ class SplitPresenter extends JetpackTaskFragmentOrganizer { } @Override - void createTaskFragment(@NonNull WindowContainerTransaction wct, @NonNull IBinder fragmentToken, - @NonNull IBinder ownerToken, @NonNull Rect bounds, @WindowingMode int windowingMode) { - final TaskFragmentContainer container = mController.getContainer(fragmentToken); + void createTaskFragment(@NonNull WindowContainerTransaction wct, + @NonNull TaskFragmentCreationParams fragmentOptions) { + final TaskFragmentContainer container = mController.getContainer( + fragmentOptions.getFragmentToken()); if (container == null) { throw new IllegalStateException( "Creating a task fragment that is not registered with controller."); } - container.setLastRequestedBounds(bounds); - container.setLastRequestedWindowingMode(windowingMode); - super.createTaskFragment(wct, fragmentToken, ownerToken, bounds, windowingMode); + container.setLastRequestedBounds(fragmentOptions.getInitialBounds()); + container.setLastRequestedWindowingMode(fragmentOptions.getWindowingMode()); + super.createTaskFragment(wct, fragmentOptions); } @Override diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentContainer.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentContainer.java index e31792ad718f..fcf0ac78af38 100644 --- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentContainer.java +++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentContainer.java @@ -118,10 +118,12 @@ class TaskFragmentContainer { /** * Creates a container with an existing activity that will be re-parented to it in a window * container transaction. + * @param pairedPrimaryContainer when it is set, the new container will be add right above it */ TaskFragmentContainer(@Nullable Activity pendingAppearedActivity, @Nullable Intent pendingAppearedIntent, @NonNull TaskContainer taskContainer, - @NonNull SplitController controller) { + @NonNull SplitController controller, + @Nullable TaskFragmentContainer pairedPrimaryContainer) { if ((pendingAppearedActivity == null && pendingAppearedIntent == null) || (pendingAppearedActivity != null && pendingAppearedIntent != null)) { throw new IllegalArgumentException( @@ -130,7 +132,16 @@ class TaskFragmentContainer { mController = controller; mToken = new Binder("TaskFragmentContainer"); mTaskContainer = taskContainer; - taskContainer.mContainers.add(this); + if (pairedPrimaryContainer != null) { + if (pairedPrimaryContainer.getTaskContainer() != taskContainer) { + throw new IllegalArgumentException( + "pairedPrimaryContainer must be in the same Task"); + } + final int primaryIndex = taskContainer.mContainers.indexOf(pairedPrimaryContainer); + taskContainer.mContainers.add(primaryIndex + 1, this); + } else { + taskContainer.mContainers.add(this); + } if (pendingAppearedActivity != null) { addPendingAppearedActivity(pendingAppearedActivity); } diff --git a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/JetpackTaskFragmentOrganizerTest.java b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/JetpackTaskFragmentOrganizerTest.java index 31aa09c902b1..bbb454d31c38 100644 --- a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/JetpackTaskFragmentOrganizerTest.java +++ b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/JetpackTaskFragmentOrganizerTest.java @@ -102,7 +102,7 @@ public class JetpackTaskFragmentOrganizerTest { public void testExpandTaskFragment() { final TaskContainer taskContainer = createTestTaskContainer(); final TaskFragmentContainer container = new TaskFragmentContainer(null /* activity */, - new Intent(), taskContainer, mSplitController); + new Intent(), taskContainer, mSplitController, null /* pairedPrimaryContainer */); final TaskFragmentInfo info = createMockInfo(container); mOrganizer.mFragmentInfos.put(container.getTaskFragmentToken(), info); container.setInfo(mTransaction, info); diff --git a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/SplitControllerTest.java b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/SplitControllerTest.java index 221c7640ef0b..6725dfdb94e8 100644 --- a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/SplitControllerTest.java +++ b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/SplitControllerTest.java @@ -169,7 +169,7 @@ public class SplitControllerTest { final TaskContainer taskContainer = createTestTaskContainer(); // tf1 has no running activity so is not active. final TaskFragmentContainer tf1 = new TaskFragmentContainer(null /* activity */, - new Intent(), taskContainer, mSplitController); + new Intent(), taskContainer, mSplitController, null /* pairedPrimaryContainer */); // tf2 has running activity so is active. final TaskFragmentContainer tf2 = mock(TaskFragmentContainer.class); doReturn(1).when(tf2).getRunningActivityCount(); @@ -375,7 +375,7 @@ public class SplitControllerTest { final Intent intent = new Intent(); final TaskContainer taskContainer = createTestTaskContainer(); final TaskFragmentContainer container = new TaskFragmentContainer(null /* activity */, - intent, taskContainer, mSplitController); + intent, taskContainer, mSplitController, null /* pairedPrimaryContainer */); final SplitController.ActivityStartMonitor monitor = mSplitController.getActivityStartMonitor(); @@ -609,7 +609,7 @@ public class SplitControllerTest { false /* isOnReparent */); assertFalse(result); - verify(mSplitController, never()).newContainer(any(), any(), any(), anyInt()); + verify(mSplitController, never()).newContainer(any(), any(), any(), anyInt(), any()); } @Test @@ -771,7 +771,7 @@ public class SplitControllerTest { false /* isOnReparent */); assertTrue(result); - verify(mSplitController, never()).newContainer(any(), any(), any(), anyInt()); + verify(mSplitController, never()).newContainer(any(), any(), any(), anyInt(), any()); verify(mSplitController, never()).registerSplit(any(), any(), any(), any(), any(), any()); } @@ -813,7 +813,7 @@ public class SplitControllerTest { false /* isOnReparent */); assertTrue(result); - verify(mSplitController, never()).newContainer(any(), any(), any(), anyInt()); + verify(mSplitController, never()).newContainer(any(), any(), any(), anyInt(), any()); verify(mSplitController, never()).registerSplit(any(), any(), any(), any(), any(), any()); } diff --git a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/TaskContainerTest.java b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/TaskContainerTest.java index 95328ce700e3..13e709271221 100644 --- a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/TaskContainerTest.java +++ b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/TaskContainerTest.java @@ -122,7 +122,7 @@ public class TaskContainerTest { assertTrue(taskContainer.isEmpty()); final TaskFragmentContainer tf = new TaskFragmentContainer(null /* activity */, - new Intent(), taskContainer, mController); + new Intent(), taskContainer, mController, null /* pairedPrimaryContainer */); assertFalse(taskContainer.isEmpty()); @@ -138,11 +138,11 @@ public class TaskContainerTest { assertNull(taskContainer.getTopTaskFragmentContainer()); final TaskFragmentContainer tf0 = new TaskFragmentContainer(null /* activity */, - new Intent(), taskContainer, mController); + new Intent(), taskContainer, mController, null /* pairedPrimaryContainer */); assertEquals(tf0, taskContainer.getTopTaskFragmentContainer()); final TaskFragmentContainer tf1 = new TaskFragmentContainer(null /* activity */, - new Intent(), taskContainer, mController); + new Intent(), taskContainer, mController, null /* pairedPrimaryContainer */); assertEquals(tf1, taskContainer.getTopTaskFragmentContainer()); } diff --git a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/TaskFragmentContainerTest.java b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/TaskFragmentContainerTest.java index 99f56b4ceb17..5c3ba72e2361 100644 --- a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/TaskFragmentContainerTest.java +++ b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/TaskFragmentContainerTest.java @@ -94,18 +94,21 @@ public class TaskFragmentContainerTest { // One of the activity and the intent must be non-null assertThrows(IllegalArgumentException.class, - () -> new TaskFragmentContainer(null, null, taskContainer, mController)); + () -> new TaskFragmentContainer(null, null, taskContainer, mController, + null /* pairedPrimaryContainer */)); // One of the activity and the intent must be null. assertThrows(IllegalArgumentException.class, - () -> new TaskFragmentContainer(mActivity, mIntent, taskContainer, mController)); + () -> new TaskFragmentContainer(mActivity, mIntent, taskContainer, mController, + null /* pairedPrimaryContainer */)); } @Test public void testFinish() { final TaskContainer taskContainer = createTestTaskContainer(); final TaskFragmentContainer container = new TaskFragmentContainer(mActivity, - null /* pendingAppearedIntent */, taskContainer, mController); + null /* pendingAppearedIntent */, taskContainer, mController, + null /* pairedPrimaryContainer */); doReturn(container).when(mController).getContainerWithActivity(mActivity); // Only remove the activity, but not clear the reference until appeared. @@ -137,12 +140,14 @@ public class TaskFragmentContainerTest { public void testFinish_notFinishActivityThatIsReparenting() { final TaskContainer taskContainer = createTestTaskContainer(); final TaskFragmentContainer container0 = new TaskFragmentContainer(mActivity, - null /* pendingAppearedIntent */, taskContainer, mController); + null /* pendingAppearedIntent */, taskContainer, mController, + null /* pairedPrimaryContainer */); final TaskFragmentInfo info = createMockTaskFragmentInfo(container0, mActivity); container0.setInfo(mTransaction, info); // Request to reparent the activity to a new TaskFragment. final TaskFragmentContainer container1 = new TaskFragmentContainer(mActivity, - null /* pendingAppearedIntent */, taskContainer, mController); + null /* pendingAppearedIntent */, taskContainer, mController, + null /* pairedPrimaryContainer */); doReturn(container1).when(mController).getContainerWithActivity(mActivity); final WindowContainerTransaction wct = new WindowContainerTransaction(); @@ -159,7 +164,8 @@ public class TaskFragmentContainerTest { final TaskContainer taskContainer = createTestTaskContainer(); // Pending activity should be cleared when it has appeared on server side. final TaskFragmentContainer pendingActivityContainer = new TaskFragmentContainer(mActivity, - null /* pendingAppearedIntent */, taskContainer, mController); + null /* pendingAppearedIntent */, taskContainer, mController, + null /* pairedPrimaryContainer */); assertTrue(pendingActivityContainer.mPendingAppearedActivities.contains( mActivity.getActivityToken())); @@ -172,7 +178,8 @@ public class TaskFragmentContainerTest { // Pending intent should be cleared when the container becomes non-empty. final TaskFragmentContainer pendingIntentContainer = new TaskFragmentContainer( - null /* pendingAppearedActivity */, mIntent, taskContainer, mController); + null /* pendingAppearedActivity */, mIntent, taskContainer, mController, + null /* pairedPrimaryContainer */); assertEquals(mIntent, pendingIntentContainer.getPendingAppearedIntent()); @@ -187,7 +194,7 @@ public class TaskFragmentContainerTest { public void testIsWaitingActivityAppear() { final TaskContainer taskContainer = createTestTaskContainer(); final TaskFragmentContainer container = new TaskFragmentContainer(null /* activity */, - mIntent, taskContainer, mController); + mIntent, taskContainer, mController, null /* pairedPrimaryContainer */); assertTrue(container.isWaitingActivityAppear()); @@ -209,7 +216,7 @@ public class TaskFragmentContainerTest { doNothing().when(mController).onTaskFragmentAppearEmptyTimeout(any(), any()); final TaskContainer taskContainer = createTestTaskContainer(); final TaskFragmentContainer container = new TaskFragmentContainer(null /* activity */, - mIntent, taskContainer, mController); + mIntent, taskContainer, mController, null /* pairedPrimaryContainer */); assertNull(container.mAppearEmptyTimeout); @@ -249,7 +256,7 @@ public class TaskFragmentContainerTest { public void testCollectNonFinishingActivities() { final TaskContainer taskContainer = createTestTaskContainer(); final TaskFragmentContainer container = new TaskFragmentContainer(null /* activity */, - mIntent, taskContainer, mController); + mIntent, taskContainer, mController, null /* pairedPrimaryContainer */); List<Activity> activities = container.collectNonFinishingActivities(); assertTrue(activities.isEmpty()); @@ -277,7 +284,7 @@ public class TaskFragmentContainerTest { public void testAddPendingActivity() { final TaskContainer taskContainer = createTestTaskContainer(); final TaskFragmentContainer container = new TaskFragmentContainer(null /* activity */, - mIntent, taskContainer, mController); + mIntent, taskContainer, mController, null /* pairedPrimaryContainer */); container.addPendingAppearedActivity(mActivity); assertEquals(1, container.collectNonFinishingActivities().size()); @@ -291,9 +298,9 @@ public class TaskFragmentContainerTest { public void testIsAbove() { final TaskContainer taskContainer = createTestTaskContainer(); final TaskFragmentContainer container0 = new TaskFragmentContainer(null /* activity */, - mIntent, taskContainer, mController); + mIntent, taskContainer, mController, null /* pairedPrimaryContainer */); final TaskFragmentContainer container1 = new TaskFragmentContainer(null /* activity */, - mIntent, taskContainer, mController); + mIntent, taskContainer, mController, null /* pairedPrimaryContainer */); assertTrue(container1.isAbove(container0)); assertFalse(container0.isAbove(container1)); @@ -303,7 +310,7 @@ public class TaskFragmentContainerTest { public void testGetBottomMostActivity() { final TaskContainer taskContainer = createTestTaskContainer(); final TaskFragmentContainer container = new TaskFragmentContainer(null /* activity */, - mIntent, taskContainer, mController); + mIntent, taskContainer, mController, null /* pairedPrimaryContainer */); container.addPendingAppearedActivity(mActivity); assertEquals(mActivity, container.getBottomMostActivity()); @@ -320,7 +327,7 @@ public class TaskFragmentContainerTest { public void testOnActivityDestroyed() { final TaskContainer taskContainer = createTestTaskContainer(mController); final TaskFragmentContainer container = new TaskFragmentContainer(null /* activity */, - mIntent, taskContainer, mController); + mIntent, taskContainer, mController, null /* pairedPrimaryContainer */); container.addPendingAppearedActivity(mActivity); final List<IBinder> activities = new ArrayList<>(); activities.add(mActivity.getActivityToken()); @@ -340,7 +347,7 @@ public class TaskFragmentContainerTest { // True if no info set. final TaskContainer taskContainer = createTestTaskContainer(); final TaskFragmentContainer container = new TaskFragmentContainer(null /* activity */, - mIntent, taskContainer, mController); + mIntent, taskContainer, mController, null /* pairedPrimaryContainer */); spyOn(taskContainer); doReturn(true).when(taskContainer).isVisible(); @@ -403,7 +410,7 @@ public class TaskFragmentContainerTest { public void testHasAppearedActivity() { final TaskContainer taskContainer = createTestTaskContainer(); final TaskFragmentContainer container = new TaskFragmentContainer(null /* activity */, - mIntent, taskContainer, mController); + mIntent, taskContainer, mController, null /* pairedPrimaryContainer */); container.addPendingAppearedActivity(mActivity); assertFalse(container.hasAppearedActivity(mActivity.getActivityToken())); @@ -420,7 +427,7 @@ public class TaskFragmentContainerTest { public void testHasPendingAppearedActivity() { final TaskContainer taskContainer = createTestTaskContainer(); final TaskFragmentContainer container = new TaskFragmentContainer(null /* activity */, - mIntent, taskContainer, mController); + mIntent, taskContainer, mController, null /* pairedPrimaryContainer */); container.addPendingAppearedActivity(mActivity); assertTrue(container.hasPendingAppearedActivity(mActivity.getActivityToken())); @@ -437,9 +444,9 @@ public class TaskFragmentContainerTest { public void testHasActivity() { final TaskContainer taskContainer = createTestTaskContainer(mController); final TaskFragmentContainer container1 = new TaskFragmentContainer(null /* activity */, - mIntent, taskContainer, mController); + mIntent, taskContainer, mController, null /* pairedPrimaryContainer */); final TaskFragmentContainer container2 = new TaskFragmentContainer(null /* activity */, - mIntent, taskContainer, mController); + mIntent, taskContainer, mController, null /* pairedPrimaryContainer */); // Activity is pending appeared on container2. container2.addPendingAppearedActivity(mActivity); @@ -472,6 +479,27 @@ public class TaskFragmentContainerTest { assertTrue(container2.hasActivity(mActivity.getActivityToken())); } + @Test + public void testNewContainerWithPairedPrimaryContainer() { + final TaskContainer taskContainer = createTestTaskContainer(); + final TaskFragmentContainer tf0 = new TaskFragmentContainer( + null /* pendingAppearedActivity */, new Intent(), taskContainer, mController, + null /* pairedPrimaryTaskFragment */); + final TaskFragmentContainer tf1 = new TaskFragmentContainer( + null /* pendingAppearedActivity */, new Intent(), taskContainer, mController, + null /* pairedPrimaryTaskFragment */); + taskContainer.mContainers.add(tf0); + taskContainer.mContainers.add(tf1); + + // When tf2 is created with using tf0 as pairedPrimaryContainer, tf2 should be inserted + // right above tf0. + final TaskFragmentContainer tf2 = new TaskFragmentContainer( + null /* pendingAppearedActivity */, new Intent(), taskContainer, mController, tf0); + assertEquals(0, taskContainer.indexOf(tf0)); + assertEquals(1, taskContainer.indexOf(tf2)); + assertEquals(2, taskContainer.indexOf(tf1)); + } + /** Creates a mock activity in the organizer process. */ private Activity createMockActivity() { final Activity activity = mock(Activity.class); diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleBadgeIconFactory.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleBadgeIconFactory.java index d3a9a672ec76..56b13b8dcd46 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleBadgeIconFactory.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleBadgeIconFactory.java @@ -19,7 +19,6 @@ package com.android.wm.shell.bubbles; import android.content.Context; import android.graphics.Bitmap; import android.graphics.Canvas; -import android.graphics.Color; import android.graphics.Path; import android.graphics.Rect; import android.graphics.drawable.AdaptiveIconDrawable; @@ -59,7 +58,8 @@ public class BubbleBadgeIconFactory extends BaseIconFactory { private class CircularRingDrawable extends CircularAdaptiveIcon { final int mImportantConversationColor; - final Rect mTempBounds = new Rect(); + final int mRingWidth; + final Rect mInnerBounds = new Rect(); final Drawable mDr; @@ -68,6 +68,8 @@ public class BubbleBadgeIconFactory extends BaseIconFactory { mDr = dr; mImportantConversationColor = mContext.getResources().getColor( R.color.important_conversation, null); + mRingWidth = mContext.getResources().getDimensionPixelSize( + com.android.internal.R.dimen.importance_ring_stroke_width); } @Override @@ -75,11 +77,10 @@ public class BubbleBadgeIconFactory extends BaseIconFactory { int save = canvas.save(); canvas.clipPath(getIconMask()); canvas.drawColor(mImportantConversationColor); - int ringStrokeWidth = mContext.getResources().getDimensionPixelSize( - com.android.internal.R.dimen.importance_ring_stroke_width); - mTempBounds.set(getBounds()); - mTempBounds.inset(ringStrokeWidth, ringStrokeWidth); - mDr.setBounds(mTempBounds); + mInnerBounds.set(getBounds()); + mInnerBounds.inset(mRingWidth, mRingWidth); + canvas.translate(mInnerBounds.left, mInnerBounds.top); + mDr.setBounds(0, 0, mInnerBounds.width(), mInnerBounds.height()); mDr.draw(canvas); canvas.restoreToCount(save); } @@ -106,7 +107,6 @@ public class BubbleBadgeIconFactory extends BaseIconFactory { int save = canvas.save(); canvas.clipPath(getIconMask()); - canvas.drawColor(Color.BLACK); Drawable d; if ((d = getBackground()) != null) { d.draw(canvas); diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java index ad0adc6c84c4..c743582c3264 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java @@ -692,7 +692,7 @@ public abstract class WMShellBaseModule { // Use optional-of-lazy for the dependency that this provider relies on. // Lazy ensures that this provider will not be the cause the dependency is created // when it will not be returned due to the condition below. - if (DesktopModeStatus.IS_SUPPORTED) { + if (DesktopModeStatus.isProto1Enabled()) { return desktopModeController.map(Lazy::get); } return Optional.empty(); @@ -709,7 +709,7 @@ public abstract class WMShellBaseModule { // Use optional-of-lazy for the dependency that this provider relies on. // Lazy ensures that this provider will not be the cause the dependency is created // when it will not be returned due to the condition below. - if (DesktopModeStatus.IS_SUPPORTED) { + if (DesktopModeStatus.isAnyEnabled()) { return desktopModeTaskRepository.map(Lazy::get); } return Optional.empty(); diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeController.java index 5824f51a1d33..7eb01a79f14f 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeController.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeController.java @@ -100,7 +100,7 @@ public class DesktopModeController implements RemoteCallable<DesktopModeControll mDesktopModeTaskRepository = desktopModeTaskRepository; mMainExecutor = mainExecutor; mSettingsObserver = new SettingsObserver(mContext, mainHandler); - if (DesktopModeStatus.isSupported()) { + if (DesktopModeStatus.isProto1Enabled()) { shellInit.addInitCallback(this::onInit, this); } } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeStatus.java b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeStatus.java index e3eb2b746969..67f4a1914c49 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeStatus.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeStatus.java @@ -33,17 +33,38 @@ public class DesktopModeStatus { /** * Flag to indicate whether desktop mode is available on the device */ - public static final boolean IS_SUPPORTED = SystemProperties.getBoolean( + private static final boolean IS_SUPPORTED = SystemProperties.getBoolean( "persist.wm.debug.desktop_mode", false); /** + * Flag to indicate whether desktop mode proto 2 is available on the device + */ + private static final boolean IS_PROTO2_ENABLED = SystemProperties.getBoolean( + "persist.wm.debug.desktop_mode_2", false); + + /** * Return {@code true} if desktop mode support is enabled */ - public static boolean isSupported() { + public static boolean isProto1Enabled() { return IS_SUPPORTED; } /** + * Return {@code true} is desktop windowing proto 2 is enabled + */ + public static boolean isProto2Enabled() { + return IS_PROTO2_ENABLED; + } + + /** + * Return {@code true} if proto 1 or 2 is enabled. + * Can be used to guard logic that is common for both prototypes. + */ + public static boolean isAnyEnabled() { + return isProto1Enabled() || isProto2Enabled(); + } + + /** * Check if desktop mode is active * * @return {@code true} if active @@ -61,5 +82,4 @@ public class DesktopModeStatus { return false; } } - } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/freeform/FreeformTaskListener.java b/libs/WindowManager/Shell/src/com/android/wm/shell/freeform/FreeformTaskListener.java index 8a9b74fd72b1..793bad86d873 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/freeform/FreeformTaskListener.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/freeform/FreeformTaskListener.java @@ -68,7 +68,7 @@ public class FreeformTaskListener implements ShellTaskOrganizer.TaskListener, private void onInit() { mShellTaskOrganizer.addListenerForType(this, TASK_LISTENER_TYPE_FREEFORM); - if (DesktopModeStatus.IS_SUPPORTED) { + if (DesktopModeStatus.isAnyEnabled()) { mShellTaskOrganizer.addFocusListener(this); } } @@ -90,7 +90,7 @@ public class FreeformTaskListener implements ShellTaskOrganizer.TaskListener, t.apply(); } - if (DesktopModeStatus.IS_SUPPORTED) { + if (DesktopModeStatus.isAnyEnabled()) { mDesktopModeTaskRepository.ifPresent(repository -> { repository.addOrMoveFreeformTaskToTop(taskInfo.taskId); if (taskInfo.isVisible) { @@ -110,7 +110,7 @@ public class FreeformTaskListener implements ShellTaskOrganizer.TaskListener, taskInfo.taskId); mTasks.remove(taskInfo.taskId); - if (DesktopModeStatus.IS_SUPPORTED) { + if (DesktopModeStatus.isAnyEnabled()) { mDesktopModeTaskRepository.ifPresent(repository -> { repository.removeFreeformTask(taskInfo.taskId); if (repository.removeActiveTask(taskInfo.taskId)) { @@ -134,7 +134,7 @@ public class FreeformTaskListener implements ShellTaskOrganizer.TaskListener, taskInfo.taskId); mWindowDecorationViewModel.onTaskInfoChanged(state.mTaskInfo); - if (DesktopModeStatus.IS_SUPPORTED) { + if (DesktopModeStatus.isAnyEnabled()) { mDesktopModeTaskRepository.ifPresent(repository -> { if (taskInfo.isVisible) { if (repository.addActiveTask(taskInfo.taskId)) { @@ -152,7 +152,7 @@ public class FreeformTaskListener implements ShellTaskOrganizer.TaskListener, ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TASK_ORG, "Freeform Task Focus Changed: #%d focused=%b", taskInfo.taskId, taskInfo.isFocused); - if (DesktopModeStatus.IS_SUPPORTED && taskInfo.isFocused) { + if (DesktopModeStatus.isAnyEnabled() && taskInfo.isFocused) { mDesktopModeTaskRepository.ifPresent(repository -> { repository.addOrMoveFreeformTaskToTop(taskInfo.taskId); }); diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransition.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransition.java index 85bad174194c..e6c7e101d078 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransition.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransition.java @@ -358,8 +358,10 @@ public class PipTransition extends PipTransitionController { WindowContainerTransaction wct = null; if (isOutPipDirection(direction)) { // Only need to reset surface properties. The server-side operations were already - // done at the start. - if (tx != null) { + // done at the start. But if it is running fixed rotation, there will be a seamless + // display transition later. So the last rotation transform needs to be kept to + // avoid flickering, and then the display transition will reset the transform. + if (tx != null && !mInFixedRotation) { mFinishTransaction.merge(tx); } } else { diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecorViewModel.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecorViewModel.java index 56554020f3cc..afefd5dc6344 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecorViewModel.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecorViewModel.java @@ -517,7 +517,7 @@ public class CaptionWindowDecorViewModel implements WindowDecorViewModel { private boolean shouldShowWindowDecor(RunningTaskInfo taskInfo) { if (taskInfo.getWindowingMode() == WINDOWING_MODE_FREEFORM) return true; - return DesktopModeStatus.IS_SUPPORTED + return DesktopModeStatus.isAnyEnabled() && taskInfo.getActivityType() == ACTIVITY_TYPE_STANDARD && mDisplayController.getDisplayContext(taskInfo.displayId) .getResources().getConfiguration().smallestScreenWidthDp >= 600; diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeControllerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeControllerTest.java index dad913300711..b3c9e238a614 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeControllerTest.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeControllerTest.java @@ -100,7 +100,7 @@ public class DesktopModeControllerTest extends ShellTestCase { @Before public void setUp() { mMockitoSession = mockitoSession().mockStatic(DesktopModeStatus.class).startMocking(); - when(DesktopModeStatus.isSupported()).thenReturn(true); + when(DesktopModeStatus.isProto1Enabled()).thenReturn(true); when(DesktopModeStatus.isActive(any())).thenReturn(true); mShellInit = Mockito.spy(new ShellInit(mTestExecutor)); @@ -129,7 +129,7 @@ public class DesktopModeControllerTest extends ShellTestCase { @Test public void instantiate_flagOff_doNotAddInitCallback() { - when(DesktopModeStatus.isSupported()).thenReturn(false); + when(DesktopModeStatus.isProto1Enabled()).thenReturn(false); clearInvocations(mShellInit); createController(); diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/integration/CameraBinderTest.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/integration/CameraBinderTest.java index a2eae2c9579a..2b7bcbee79fd 100644 --- a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/integration/CameraBinderTest.java +++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/integration/CameraBinderTest.java @@ -85,7 +85,8 @@ public class CameraBinderTest extends AndroidTestCase { public void testCameraInfo() throws Exception { for (int cameraId = 0; cameraId < mUtils.getGuessedNumCameras(); ++cameraId) { - CameraInfo info = mUtils.getCameraService().getCameraInfo(cameraId); + CameraInfo info = mUtils.getCameraService().getCameraInfo(cameraId, + /*overrideToPortrait*/false); assertTrue("Facing was not set for camera " + cameraId, info.info.facing != -1); assertTrue("Orientation was not set for camera " + cameraId, info.info.orientation != -1); @@ -159,7 +160,8 @@ public class CameraBinderTest extends AndroidTestCase { .connect(dummyCallbacks, cameraId, clientPackageName, ICameraService.USE_CALLING_UID, ICameraService.USE_CALLING_PID, - getContext().getApplicationInfo().targetSdkVersion); + getContext().getApplicationInfo().targetSdkVersion, + /*overrideToPortrait*/false); assertNotNull(String.format("Camera %s was null", cameraId), cameraUser); Log.v(TAG, String.format("Camera %s connected", cameraId)); @@ -264,7 +266,8 @@ public class CameraBinderTest extends AndroidTestCase { dummyCallbacks, String.valueOf(cameraId), clientPackageName, clientAttributionTag, ICameraService.USE_CALLING_UID, 0 /*oomScoreOffset*/, - getContext().getApplicationInfo().targetSdkVersion); + getContext().getApplicationInfo().targetSdkVersion, + /*overrideToPortrait*/false); assertNotNull(String.format("Camera %s was null", cameraId), cameraUser); Log.v(TAG, String.format("Camera %s connected", cameraId)); diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/integration/CameraDeviceBinderTest.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/integration/CameraDeviceBinderTest.java index 0890346db198..9d09dcc5c440 100644 --- a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/integration/CameraDeviceBinderTest.java +++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/integration/CameraDeviceBinderTest.java @@ -244,7 +244,8 @@ public class CameraDeviceBinderTest extends AndroidTestCase { mCameraUser = mUtils.getCameraService().connectDevice(mMockCb, mCameraId, clientPackageName, clientAttributionTag, ICameraService.USE_CALLING_UID, - /*oomScoreOffset*/0, getContext().getApplicationInfo().targetSdkVersion); + /*oomScoreOffset*/0, getContext().getApplicationInfo().targetSdkVersion, + /*overrideToPortrait*/false); assertNotNull(String.format("Camera %s was null", mCameraId), mCameraUser); mHandlerThread = new HandlerThread(TAG); mHandlerThread.start(); @@ -417,7 +418,7 @@ public class CameraDeviceBinderTest extends AndroidTestCase { @SmallTest public void testCameraCharacteristics() throws RemoteException { CameraMetadataNative info = mUtils.getCameraService().getCameraCharacteristics(mCameraId, - getContext().getApplicationInfo().targetSdkVersion); + getContext().getApplicationInfo().targetSdkVersion, /*overrideToPortrait*/false); assertFalse(info.isEmpty()); assertNotNull(info.get(CameraCharacteristics.SCALER_AVAILABLE_FORMATS)); diff --git a/packages/SystemUI/res-keyguard/layout/fgs_footer.xml b/packages/SystemUI/res-keyguard/layout/fgs_footer.xml deleted file mode 100644 index ee588f997ab8..000000000000 --- a/packages/SystemUI/res-keyguard/layout/fgs_footer.xml +++ /dev/null @@ -1,105 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- - Copyright (C) 2022 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. ---> -<!-- TODO(b/242040009): Remove this file. --> -<FrameLayout - xmlns:android="http://schemas.android.com/apk/res/android" - android:layout_width="0dp" - android:layout_height="@dimen/qs_security_footer_single_line_height" - android:layout_weight="1" - android:gravity="center" - android:clickable="true" - android:visibility="gone"> - - <LinearLayout - android:id="@+id/fgs_text_container" - android:layout_width="match_parent" - android:layout_height="match_parent" - android:layout_marginEnd="@dimen/qs_footer_action_inset" - android:background="@drawable/qs_security_footer_background" - android:layout_gravity="end" - android:gravity="center" - android:paddingHorizontal="@dimen/qs_footer_padding" - > - - <ImageView - android:id="@+id/primary_footer_icon" - android:layout_width="@dimen/qs_footer_icon_size" - android:layout_height="@dimen/qs_footer_icon_size" - android:gravity="start" - android:layout_marginEnd="12dp" - android:contentDescription="@null" - android:src="@drawable/ic_info_outline" - android:tint="?android:attr/textColorSecondary" /> - - <TextView - android:id="@+id/footer_text" - android:layout_width="0dp" - android:layout_height="wrap_content" - android:layout_weight="1" - android:maxLines="1" - android:ellipsize="end" - android:textAppearance="@style/TextAppearance.QS.SecurityFooter" - android:textColor="?android:attr/textColorSecondary"/> - - <ImageView - android:id="@+id/fgs_new" - android:layout_width="12dp" - android:layout_height="12dp" - android:scaleType="fitCenter" - android:src="@drawable/fgs_dot" - android:contentDescription="@string/fgs_dot_content_description" - /> - - <ImageView - android:id="@+id/footer_icon" - android:layout_width="@dimen/qs_footer_icon_size" - android:layout_height="@dimen/qs_footer_icon_size" - android:layout_marginStart="8dp" - android:contentDescription="@null" - android:src="@*android:drawable/ic_chevron_end" - android:autoMirrored="true" - android:tint="?android:attr/textColorSecondary" /> - </LinearLayout> - - <FrameLayout - android:id="@+id/fgs_number_container" - android:layout_width="@dimen/qs_footer_action_button_size" - android:layout_height="@dimen/qs_footer_action_button_size" - android:background="@drawable/qs_footer_action_circle" - android:focusable="true" - android:visibility="gone"> - - <TextView - android:id="@+id/fgs_number" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:textAppearance="@style/TextAppearance.QS.SecurityFooter" - android:layout_gravity="center" - android:textColor="?android:attr/textColorPrimary" - android:textSize="18sp"/> - <ImageView - android:id="@+id/fgs_collapsed_new" - android:layout_width="12dp" - android:layout_height="12dp" - android:scaleType="fitCenter" - android:layout_gravity="bottom|end" - android:src="@drawable/fgs_dot" - android:contentDescription="@string/fgs_dot_content_description" - /> - </FrameLayout> - -</FrameLayout>
\ No newline at end of file diff --git a/packages/SystemUI/res-keyguard/layout/footer_actions.xml b/packages/SystemUI/res-keyguard/layout/footer_actions.xml index 2261ae8d7714..4a2a1cb9dc6d 100644 --- a/packages/SystemUI/res-keyguard/layout/footer_actions.xml +++ b/packages/SystemUI/res-keyguard/layout/footer_actions.xml @@ -16,10 +16,8 @@ --> <!-- Action buttons for footer in QS/QQS, containing settings button, power off button etc --> -<!-- TODO(b/242040009): Clean up this file. --> -<com.android.systemui.qs.FooterActionsView +<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:androidprv="http://schemas.android.com/apk/prv/res/android" android:layout_width="match_parent" android:layout_height="@dimen/footer_actions_height" android:elevation="@dimen/qs_panel_elevation" @@ -28,74 +26,4 @@ android:background="@drawable/qs_footer_actions_background" android:gravity="center_vertical|end" android:layout_gravity="bottom" -> - - <LinearLayout - android:id="@+id/security_footers_container" - android:orientation="horizontal" - android:layout_height="@dimen/qs_footer_action_button_size" - android:layout_width="0dp" - android:layout_weight="1" - /> - - <!-- Negative margin equal to --> - <LinearLayout - android:layout_height="match_parent" - android:layout_width="wrap_content" - android:layout_marginEnd="@dimen/qs_footer_action_inset_negative" - > - - <com.android.systemui.statusbar.phone.MultiUserSwitch - android:id="@id/multi_user_switch" - android:layout_width="@dimen/qs_footer_action_button_size" - android:layout_height="@dimen/qs_footer_action_button_size" - android:background="@drawable/qs_footer_action_circle" - android:focusable="true"> - - <ImageView - android:id="@+id/multi_user_avatar" - android:layout_width="@dimen/qs_footer_icon_size" - android:layout_height="@dimen/qs_footer_icon_size" - android:layout_gravity="center" - android:scaleType="centerInside" /> - </com.android.systemui.statusbar.phone.MultiUserSwitch> - - <com.android.systemui.statusbar.AlphaOptimizedFrameLayout - android:id="@id/settings_button_container" - android:layout_width="@dimen/qs_footer_action_button_size" - android:layout_height="@dimen/qs_footer_action_button_size" - android:background="@drawable/qs_footer_action_circle" - android:clipChildren="false" - android:clipToPadding="false"> - - <com.android.systemui.statusbar.phone.SettingsButton - android:id="@+id/settings_button" - android:layout_width="@dimen/qs_footer_icon_size" - android:layout_height="@dimen/qs_footer_icon_size" - android:layout_gravity="center" - android:background="@android:color/transparent" - android:focusable="false" - android:clickable="false" - android:importantForAccessibility="yes" - android:contentDescription="@string/accessibility_quick_settings_settings" - android:scaleType="centerInside" - android:src="@drawable/ic_settings" - android:tint="?android:attr/textColorPrimary" /> - - </com.android.systemui.statusbar.AlphaOptimizedFrameLayout> - - <com.android.systemui.statusbar.AlphaOptimizedImageView - android:id="@id/pm_lite" - android:layout_width="@dimen/qs_footer_action_button_size" - android:layout_height="@dimen/qs_footer_action_button_size" - android:background="@drawable/qs_footer_action_circle_color" - android:clickable="true" - android:clipToPadding="false" - android:focusable="true" - android:padding="@dimen/qs_footer_icon_padding" - android:src="@*android:drawable/ic_lock_power_off" - android:contentDescription="@string/accessibility_quick_settings_power_menu" - android:tint="?androidprv:attr/textColorOnAccent" /> - - </LinearLayout> -</com.android.systemui.qs.FooterActionsView>
\ No newline at end of file +/>
\ No newline at end of file diff --git a/packages/SystemUI/res/layout/activity_rear_display_education.xml b/packages/SystemUI/res/layout/activity_rear_display_education.xml index 73d3f0236fff..f5fc48c70003 100644 --- a/packages/SystemUI/res/layout/activity_rear_display_education.xml +++ b/packages/SystemUI/res/layout/activity_rear_display_education.xml @@ -33,7 +33,7 @@ android:layout_width="@dimen/rear_display_animation_width" android:layout_height="@dimen/rear_display_animation_height" android:layout_gravity="center" - android:contentDescription="@null" + android:contentDescription="@string/rear_display_accessibility_folded_animation" android:scaleType="fitXY" app:lottie_rawRes="@raw/rear_display_folded" app:lottie_autoPlay="true" diff --git a/packages/SystemUI/res/layout/activity_rear_display_education_opened.xml b/packages/SystemUI/res/layout/activity_rear_display_education_opened.xml index 20b93d9bc40d..6de06f76ebbf 100644 --- a/packages/SystemUI/res/layout/activity_rear_display_education_opened.xml +++ b/packages/SystemUI/res/layout/activity_rear_display_education_opened.xml @@ -34,7 +34,7 @@ android:layout_width="@dimen/rear_display_animation_width" android:layout_height="@dimen/rear_display_animation_height" android:layout_gravity="center" - android:contentDescription="@null" + android:contentDescription="@string/rear_display_accessibility_unfolded_animation" android:scaleType="fitXY" app:lottie_rawRes="@raw/rear_display_turnaround" app:lottie_autoPlay="true" diff --git a/packages/SystemUI/res/layout/quick_settings_security_footer.xml b/packages/SystemUI/res/layout/quick_settings_security_footer.xml deleted file mode 100644 index 194f3dd5dc26..000000000000 --- a/packages/SystemUI/res/layout/quick_settings_security_footer.xml +++ /dev/null @@ -1,61 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- - Copyright (C) 2014 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. ---> -<!-- TODO(b/242040009): Remove this file. --> -<LinearLayout - xmlns:android="http://schemas.android.com/apk/res/android" - android:layout_width="0dp" - android:layout_height="@dimen/qs_security_footer_single_line_height" - android:layout_weight="1" - android:clickable="true" - android:orientation="horizontal" - android:paddingHorizontal="@dimen/qs_footer_padding" - android:gravity="center_vertical" - android:layout_gravity="center_vertical|center_horizontal" - android:layout_marginEnd="@dimen/qs_footer_action_inset" - android:background="@drawable/qs_security_footer_background" - > - - <ImageView - android:id="@+id/primary_footer_icon" - android:layout_width="@dimen/qs_footer_icon_size" - android:layout_height="@dimen/qs_footer_icon_size" - android:gravity="start" - android:layout_marginEnd="12dp" - android:contentDescription="@null" - android:tint="?android:attr/textColorSecondary" /> - - <TextView - android:id="@+id/footer_text" - android:layout_width="0dp" - android:layout_height="wrap_content" - android:layout_weight="1" - android:singleLine="true" - android:ellipsize="end" - android:textAppearance="@style/TextAppearance.QS.SecurityFooter" - android:textColor="?android:attr/textColorSecondary"/> - - <ImageView - android:id="@+id/footer_icon" - android:layout_width="@dimen/qs_footer_icon_size" - android:layout_height="@dimen/qs_footer_icon_size" - android:layout_marginStart="8dp" - android:contentDescription="@null" - android:src="@*android:drawable/ic_chevron_end" - android:autoMirrored="true" - android:tint="?android:attr/textColorSecondary" /> - -</LinearLayout> diff --git a/packages/SystemUI/res/layout/super_notification_shade.xml b/packages/SystemUI/res/layout/super_notification_shade.xml index bafdb11ab211..4abc1769ab54 100644 --- a/packages/SystemUI/res/layout/super_notification_shade.xml +++ b/packages/SystemUI/res/layout/super_notification_shade.xml @@ -98,16 +98,18 @@ android:singleLine="true" android:ellipsize="marquee" android:focusable="true" /> - <FrameLayout android:id="@+id/keyguard_bouncer_container" - android:layout_height="0dp" - android:layout_width="match_parent" - android:layout_weight="1" - android:background="@android:color/transparent" - android:visibility="invisible" - android:clipChildren="false" - android:clipToPadding="false" /> </LinearLayout> + <FrameLayout android:id="@+id/keyguard_bouncer_container" + android:paddingTop="@dimen/status_bar_height" + android:layout_height="match_parent" + android:layout_width="match_parent" + android:layout_weight="1" + android:background="@android:color/transparent" + android:visibility="invisible" + android:clipChildren="false" + android:clipToPadding="false" /> + <com.android.systemui.biometrics.AuthRippleView android:id="@+id/auth_ripple" android:layout_width="match_parent" diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml index c5bd976395ea..084a0e0cb62d 100644 --- a/packages/SystemUI/res/values/strings.xml +++ b/packages/SystemUI/res/values/strings.xml @@ -2755,4 +2755,8 @@ <string name="rear_display_bottom_sheet_description">Use the rear-facing camera for a wider photo with higher resolution.</string> <!-- Text for education page description to warn user that the display will turn off if the button is clicked. [CHAR_LIMIT=NONE] --> <string name="rear_display_bottom_sheet_warning"><b>✱ This screen will turn off</b></string> + <!-- Text for education page content description for folded animation. [CHAR_LIMIT=NONE] --> + <string name="rear_display_accessibility_folded_animation">Foldable device being unfolded</string> + <!-- Text for education page content description for unfolded animation. [CHAR_LIMIT=NONE] --> + <string name="rear_display_accessibility_unfolded_animation">Foldable device being flipped around</string> </resources> diff --git a/packages/SystemUI/src/com/android/systemui/util/condition/CombinedCondition.kt b/packages/SystemUI/shared/src/com/android/systemui/shared/condition/CombinedCondition.kt index da81d540f189..2d83458ec2f7 100644 --- a/packages/SystemUI/src/com/android/systemui/util/condition/CombinedCondition.kt +++ b/packages/SystemUI/shared/src/com/android/systemui/shared/condition/CombinedCondition.kt @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.systemui.util.condition +package com.android.systemui.shared.condition /** * A higher order [Condition] which combines multiple conditions with a specified diff --git a/packages/SystemUI/src/com/android/systemui/util/condition/Condition.java b/packages/SystemUI/shared/src/com/android/systemui/shared/condition/Condition.java index b39adefa238b..cc48090e1217 100644 --- a/packages/SystemUI/src/com/android/systemui/util/condition/Condition.java +++ b/packages/SystemUI/shared/src/com/android/systemui/shared/condition/Condition.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2021 The Android Open Source Project + * Copyright (C) 2022 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. @@ -14,13 +14,14 @@ * limitations under the License. */ -package com.android.systemui.util.condition; +package com.android.systemui.shared.condition; import android.util.Log; -import com.android.systemui.statusbar.policy.CallbackController; - -import org.jetbrains.annotations.NotNull; +import androidx.annotation.NonNull; +import androidx.lifecycle.Lifecycle; +import androidx.lifecycle.LifecycleEventObserver; +import androidx.lifecycle.LifecycleOwner; import java.lang.ref.WeakReference; import java.util.ArrayList; @@ -33,7 +34,7 @@ import java.util.List; * Base class for a condition that needs to be fulfilled in order for {@link Monitor} to inform * its callbacks. */ -public abstract class Condition implements CallbackController<Condition.Callback> { +public abstract class Condition { private final String mTag = getClass().getSimpleName(); private final ArrayList<WeakReference<Callback>> mCallbacks = new ArrayList<>(); @@ -79,8 +80,7 @@ public abstract class Condition implements CallbackController<Condition.Callback * Registers a callback to receive updates once started. This should be called before * {@link #start()}. Also triggers the callback immediately if already started. */ - @Override - public void addCallback(@NotNull Callback callback) { + public void addCallback(@NonNull Callback callback) { if (shouldLog()) Log.d(mTag, "adding callback"); mCallbacks.add(new WeakReference<>(callback)); @@ -96,8 +96,7 @@ public abstract class Condition implements CallbackController<Condition.Callback /** * Removes the provided callback from further receiving updates. */ - @Override - public void removeCallback(@NotNull Callback callback) { + public void removeCallback(@NonNull Callback callback) { if (shouldLog()) Log.d(mTag, "removing callback"); final Iterator<WeakReference<Callback>> iterator = mCallbacks.iterator(); while (iterator.hasNext()) { @@ -116,6 +115,29 @@ public abstract class Condition implements CallbackController<Condition.Callback } /** + * Wrapper to {@link #addCallback(Callback)} when a lifecycle is in the resumed state + * and {@link #removeCallback(Callback)} when not resumed automatically. + */ + public Callback observe(LifecycleOwner owner, Callback listener) { + return observe(owner.getLifecycle(), listener); + } + + /** + * Wrapper to {@link #addCallback(Callback)} when a lifecycle is in the resumed state + * and {@link #removeCallback(Condition.Callback)} when not resumed automatically. + */ + public Callback observe(Lifecycle lifecycle, Callback listener) { + lifecycle.addObserver((LifecycleEventObserver) (lifecycleOwner, event) -> { + if (event == Lifecycle.Event.ON_RESUME) { + addCallback(listener); + } else if (event == Lifecycle.Event.ON_PAUSE) { + removeCallback(listener); + } + }); + return listener; + } + + /** * Updates the value for whether the condition has been fulfilled, and sends an update if the * value changes and any callback is registered. * @@ -187,7 +209,7 @@ public abstract class Condition implements CallbackController<Condition.Callback * Creates a new condition which will only be true when both this condition and all the provided * conditions are true. */ - public Condition and(Collection<Condition> others) { + public Condition and(@NonNull Collection<Condition> others) { final List<Condition> conditions = new ArrayList<>(others); conditions.add(this); return new CombinedCondition(conditions, Evaluator.OP_AND); @@ -197,7 +219,7 @@ public abstract class Condition implements CallbackController<Condition.Callback * Creates a new condition which will only be true when both this condition and the provided * condition is true. */ - public Condition and(Condition other) { + public Condition and(@NonNull Condition other) { return new CombinedCondition(Arrays.asList(this, other), Evaluator.OP_AND); } @@ -205,7 +227,7 @@ public abstract class Condition implements CallbackController<Condition.Callback * Creates a new condition which will only be true when either this condition or any of the * provided conditions are true. */ - public Condition or(Collection<Condition> others) { + public Condition or(@NonNull Collection<Condition> others) { final List<Condition> conditions = new ArrayList<>(others); conditions.add(this); return new CombinedCondition(conditions, Evaluator.OP_OR); @@ -215,7 +237,7 @@ public abstract class Condition implements CallbackController<Condition.Callback * Creates a new condition which will only be true when either this condition or the provided * condition is true. */ - public Condition or(Condition other) { + public Condition or(@NonNull Condition other) { return new CombinedCondition(Arrays.asList(this, other), Evaluator.OP_OR); } diff --git a/packages/SystemUI/src/com/android/systemui/util/condition/Evaluator.kt b/packages/SystemUI/shared/src/com/android/systemui/shared/condition/Evaluator.kt index cf44e84a563f..23742c503ed3 100644 --- a/packages/SystemUI/src/com/android/systemui/util/condition/Evaluator.kt +++ b/packages/SystemUI/shared/src/com/android/systemui/shared/condition/Evaluator.kt @@ -1,4 +1,20 @@ -package com.android.systemui.util.condition +/* + * Copyright (C) 2022 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.shared.condition import android.annotation.IntDef diff --git a/packages/SystemUI/src/com/android/systemui/util/condition/Monitor.java b/packages/SystemUI/shared/src/com/android/systemui/shared/condition/Monitor.java index 24bc9078475f..95675cef9136 100644 --- a/packages/SystemUI/src/com/android/systemui/util/condition/Monitor.java +++ b/packages/SystemUI/shared/src/com/android/systemui/shared/condition/Monitor.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2021 The Android Open Source Project + * Copyright (C) 2022 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. @@ -14,14 +14,14 @@ * limitations under the License. */ -package com.android.systemui.util.condition; +package com.android.systemui.shared.condition; import android.util.ArraySet; import android.util.Log; -import com.android.systemui.dagger.qualifiers.Main; +import androidx.annotation.NonNull; -import org.jetbrains.annotations.NotNull; +import com.android.systemui.dagger.qualifiers.Main; import java.util.ArrayList; import java.util.Collections; @@ -100,7 +100,7 @@ public class Monitor { * @param subscription A {@link Subscription} detailing the desired conditions and callback. * @return A {@link Subscription.Token} that can be used to remove the subscription. */ - public Subscription.Token addSubscription(@NotNull Subscription subscription) { + public Subscription.Token addSubscription(@NonNull Subscription subscription) { final Subscription.Token token = new Subscription.Token(); final SubscriptionState state = new SubscriptionState(subscription); @@ -131,7 +131,7 @@ public class Monitor { * @param token The {@link Subscription.Token} returned when the {@link Subscription} was * originally added. */ - public void removeSubscription(@NotNull Subscription.Token token) { + public void removeSubscription(@NonNull Subscription.Token token) { mExecutor.execute(() -> { if (shouldLog()) Log.d(mTag, "removing subscription"); if (!mSubscriptions.containsKey(token)) { diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/utilities/PreviewPositionHelper.java b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/utilities/PreviewPositionHelper.java index c9ea79432360..5883b6c0e723 100644 --- a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/utilities/PreviewPositionHelper.java +++ b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/utilities/PreviewPositionHelper.java @@ -87,8 +87,8 @@ public class PreviewPositionHelper { taskPercent = mDesiredStagePosition != STAGE_POSITION_TOP_OR_LEFT ? mSplitBounds.topTaskPercent : (1 - (mSplitBounds.topTaskPercent + mSplitBounds.dividerHeightPercent)); - // Scale portrait height to that of (actual screen - taskbar inset) - fullscreenTaskHeight = (screenHeightPx) * taskPercent; + // Scale portrait height to that of the actual screen + fullscreenTaskHeight = screenHeightPx * taskPercent; if (mTaskbarInApp) { canvasScreenRatio = canvasHeight / fullscreenTaskHeight; } else { diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardAbsKeyInputViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardAbsKeyInputViewController.java index 860c8e3a9f77..7da27b1d6898 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardAbsKeyInputViewController.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardAbsKeyInputViewController.java @@ -260,7 +260,8 @@ public abstract class KeyguardAbsKeyInputViewController<T extends KeyguardAbsKey if (reason != PROMPT_REASON_NONE) { int promtReasonStringRes = mView.getPromptReasonStringRes(reason); if (promtReasonStringRes != 0) { - mMessageAreaController.setMessage(promtReasonStringRes); + mMessageAreaController.setMessage( + mView.getResources().getString(promtReasonStringRes), false); } } } diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardInputViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardInputViewController.java index 2e9ad5868eba..53b569af29e1 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardInputViewController.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardInputViewController.java @@ -143,7 +143,9 @@ public abstract class KeyguardInputViewController<T extends KeyguardInputView> public void startAppearAnimation() { if (TextUtils.isEmpty(mMessageAreaController.getMessage())) { - mMessageAreaController.setMessage(getInitialMessageResId()); + mMessageAreaController.setMessage( + mView.getResources().getString(getInitialMessageResId()), + /* animate= */ false); } mView.startAppearAnimation(); } diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardPINView.java b/packages/SystemUI/src/com/android/keyguard/KeyguardPINView.java index 5d86ccd5409e..67e3400670ba 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardPINView.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardPINView.java @@ -52,6 +52,7 @@ public class KeyguardPINView extends KeyguardPinBasedInputView { private int mYTransOffset; private View mBouncerMessageView; @DevicePostureInt private int mLastDevicePosture = DEVICE_POSTURE_UNKNOWN; + public static final long ANIMATION_DURATION = 650; public KeyguardPINView(Context context) { this(context, null); @@ -181,7 +182,7 @@ public class KeyguardPINView extends KeyguardPinBasedInputView { if (mAppearAnimator.isRunning()) { mAppearAnimator.cancel(); } - mAppearAnimator.setDuration(650); + mAppearAnimator.setDuration(ANIMATION_DURATION); mAppearAnimator.addUpdateListener(animation -> animate(animation.getAnimatedFraction())); mAppearAnimator.start(); } diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java index 8f3484a0c99b..5d7a6f122e69 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java @@ -36,8 +36,11 @@ import static com.android.systemui.plugins.FalsingManager.LOW_PENALTY; import static java.lang.Integer.max; +import android.animation.Animator; +import android.animation.AnimatorListenerAdapter; import android.animation.AnimatorSet; import android.animation.ObjectAnimator; +import android.animation.ValueAnimator; import android.app.Activity; import android.app.AlertDialog; import android.app.admin.DevicePolicyManager; @@ -967,11 +970,23 @@ public class KeyguardSecurityContainer extends ConstraintLayout { } mUserSwitcherViewGroup.setAlpha(0f); - ObjectAnimator alphaAnim = ObjectAnimator.ofFloat(mUserSwitcherViewGroup, View.ALPHA, - 1f); - alphaAnim.setInterpolator(Interpolators.ALPHA_IN); - alphaAnim.setDuration(500); - alphaAnim.start(); + ValueAnimator animator = ValueAnimator.ofFloat(0f, 1f); + int yTrans = mView.getResources().getDimensionPixelSize(R.dimen.pin_view_trans_y_entry); + animator.setInterpolator(Interpolators.STANDARD_DECELERATE); + animator.setDuration(650); + animator.addListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationEnd(Animator animation) { + mUserSwitcherViewGroup.setAlpha(1f); + mUserSwitcherViewGroup.setTranslationY(0f); + } + }); + animator.addUpdateListener(animation -> { + float value = (float) animation.getAnimatedValue(); + mUserSwitcherViewGroup.setAlpha(value); + mUserSwitcherViewGroup.setTranslationY(yTrans - yTrans * value); + }); + animator.start(); } @Override diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java index d1a59cc334ab..71d5bf57baf6 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java @@ -398,6 +398,9 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab private static final int HAL_ERROR_RETRY_MAX = 20; @VisibleForTesting + protected static final int HAL_POWER_PRESS_TIMEOUT = 50; // ms + + @VisibleForTesting protected final Runnable mFpCancelNotReceived = this::onFingerprintCancelNotReceived; private final Runnable mFaceCancelNotReceived = this::onFaceCancelNotReceived; @@ -918,7 +921,7 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab } } - private final Runnable mRetryFingerprintAuthentication = new Runnable() { + private final Runnable mRetryFingerprintAuthenticationAfterHwUnavailable = new Runnable() { @SuppressLint("MissingPermission") @Override public void run() { @@ -927,7 +930,8 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab updateFingerprintListeningState(BIOMETRIC_ACTION_UPDATE); } else if (mHardwareFingerprintUnavailableRetryCount < HAL_ERROR_RETRY_MAX) { mHardwareFingerprintUnavailableRetryCount++; - mHandler.postDelayed(mRetryFingerprintAuthentication, HAL_ERROR_RETRY_TIMEOUT); + mHandler.postDelayed(mRetryFingerprintAuthenticationAfterHwUnavailable, + HAL_ERROR_RETRY_TIMEOUT); } } }; @@ -957,12 +961,16 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab if (msgId == FingerprintManager.FINGERPRINT_ERROR_HW_UNAVAILABLE) { mLogger.logRetryAfterFpErrorWithDelay(msgId, errString, HAL_ERROR_RETRY_TIMEOUT); - mHandler.postDelayed(mRetryFingerprintAuthentication, HAL_ERROR_RETRY_TIMEOUT); + mHandler.postDelayed(mRetryFingerprintAuthenticationAfterHwUnavailable, + HAL_ERROR_RETRY_TIMEOUT); } if (msgId == FingerprintManager.BIOMETRIC_ERROR_POWER_PRESSED) { - mLogger.logRetryAfterFpErrorWithDelay(msgId, errString, 0); - updateFingerprintListeningState(BIOMETRIC_ACTION_START); + mLogger.logRetryAfterFpErrorWithDelay(msgId, errString, HAL_POWER_PRESS_TIMEOUT); + mHandler.postDelayed(() -> { + mLogger.d("Retrying fingerprint listening after power pressed error."); + updateFingerprintListeningState(BIOMETRIC_ACTION_START); + }, HAL_POWER_PRESS_TIMEOUT); } boolean lockedOutStateChanged = false; diff --git a/packages/SystemUI/src/com/android/keyguard/logging/KeyguardUpdateMonitorLogger.kt b/packages/SystemUI/src/com/android/keyguard/logging/KeyguardUpdateMonitorLogger.kt index b66ae286171a..ceebe4c69091 100644 --- a/packages/SystemUI/src/com/android/keyguard/logging/KeyguardUpdateMonitorLogger.kt +++ b/packages/SystemUI/src/com/android/keyguard/logging/KeyguardUpdateMonitorLogger.kt @@ -231,7 +231,7 @@ class KeyguardUpdateMonitorLogger @Inject constructor( int2 = delay str1 = "$errString" }, { - "Fingerprint retrying auth after $int2 ms due to($int1) -> $str1" + "Fingerprint scheduling retry auth after $int2 ms due to($int1) -> $str1" }) } diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java index 5a81bd3e01b6..419cf1faf1fd 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java +++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java @@ -96,6 +96,7 @@ import java.util.Set; import java.util.concurrent.Executor; import javax.inject.Inject; +import javax.inject.Provider; import kotlin.Unit; @@ -714,7 +715,7 @@ public class UdfpsController implements DozeReceiver, Dumpable { @NonNull SystemUIDialogManager dialogManager, @NonNull LatencyTracker latencyTracker, @NonNull ActivityLaunchAnimator activityLaunchAnimator, - @NonNull Optional<AlternateUdfpsTouchProvider> alternateTouchProvider, + @NonNull Optional<Provider<AlternateUdfpsTouchProvider>> alternateTouchProvider, @NonNull @BiometricsBackground Executor biometricsExecutor, @NonNull PrimaryBouncerInteractor primaryBouncerInteractor, @NonNull SinglePointerTouchProcessor singlePointerTouchProcessor) { @@ -746,7 +747,7 @@ public class UdfpsController implements DozeReceiver, Dumpable { mUnlockedScreenOffAnimationController = unlockedScreenOffAnimationController; mLatencyTracker = latencyTracker; mActivityLaunchAnimator = activityLaunchAnimator; - mAlternateTouchProvider = alternateTouchProvider.orElse(null); + mAlternateTouchProvider = alternateTouchProvider.map(Provider::get).orElse(null); mSensorProps = new FingerprintSensorPropertiesInternal( -1 /* sensorId */, SensorProperties.STRENGTH_CONVENIENCE, diff --git a/packages/SystemUI/src/com/android/systemui/controls/dagger/ControlsComponent.kt b/packages/SystemUI/src/com/android/systemui/controls/dagger/ControlsComponent.kt index 77d0496e43db..27466d4f58bc 100644 --- a/packages/SystemUI/src/com/android/systemui/controls/dagger/ControlsComponent.kt +++ b/packages/SystemUI/src/com/android/systemui/controls/dagger/ControlsComponent.kt @@ -19,7 +19,7 @@ package com.android.systemui.controls.dagger import android.content.Context import com.android.internal.widget.LockPatternUtils import com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_BOOT -import com.android.systemui.controls.ControlsSettingsRepository +import com.android.systemui.controls.settings.ControlsSettingsRepository import com.android.systemui.controls.controller.ControlsController import com.android.systemui.controls.controller.ControlsTileResourceConfiguration import com.android.systemui.controls.controller.ControlsTileResourceConfigurationImpl diff --git a/packages/SystemUI/src/com/android/systemui/controls/dagger/ControlsModule.kt b/packages/SystemUI/src/com/android/systemui/controls/dagger/ControlsModule.kt index 9ae605e30a83..6d6410de1a91 100644 --- a/packages/SystemUI/src/com/android/systemui/controls/dagger/ControlsModule.kt +++ b/packages/SystemUI/src/com/android/systemui/controls/dagger/ControlsModule.kt @@ -20,8 +20,8 @@ import android.app.Activity import android.content.pm.PackageManager import com.android.systemui.controls.ControlsMetricsLogger import com.android.systemui.controls.ControlsMetricsLoggerImpl -import com.android.systemui.controls.ControlsSettingsRepository -import com.android.systemui.controls.ControlsSettingsRepositoryImpl +import com.android.systemui.controls.settings.ControlsSettingsRepository +import com.android.systemui.controls.settings.ControlsSettingsRepositoryImpl import com.android.systemui.controls.controller.ControlsBindingController import com.android.systemui.controls.controller.ControlsBindingControllerImpl import com.android.systemui.controls.controller.ControlsController @@ -34,6 +34,8 @@ import com.android.systemui.controls.management.ControlsListingController import com.android.systemui.controls.management.ControlsListingControllerImpl import com.android.systemui.controls.management.ControlsProviderSelectorActivity import com.android.systemui.controls.management.ControlsRequestDialog +import com.android.systemui.controls.settings.ControlsSettingsDialogManager +import com.android.systemui.controls.settings.ControlsSettingsDialogManagerImpl import com.android.systemui.controls.ui.ControlActionCoordinator import com.android.systemui.controls.ui.ControlActionCoordinatorImpl import com.android.systemui.controls.ui.ControlsActivity @@ -90,6 +92,11 @@ abstract class ControlsModule { ): ControlsSettingsRepository @Binds + abstract fun provideDialogManager( + manager: ControlsSettingsDialogManagerImpl + ): ControlsSettingsDialogManager + + @Binds abstract fun provideMetricsLogger(logger: ControlsMetricsLoggerImpl): ControlsMetricsLogger @Binds diff --git a/packages/SystemUI/src/com/android/systemui/controls/settings/ControlsSettingsDialogManager.kt b/packages/SystemUI/src/com/android/systemui/controls/settings/ControlsSettingsDialogManager.kt new file mode 100644 index 000000000000..bb2e2d701aa0 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/controls/settings/ControlsSettingsDialogManager.kt @@ -0,0 +1,231 @@ +/* + * Copyright (C) 2022 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.controls.settings + +import android.app.AlertDialog +import android.content.Context +import android.content.Context.MODE_PRIVATE +import android.content.DialogInterface +import android.content.SharedPreferences +import android.provider.Settings +import androidx.annotation.VisibleForTesting +import com.android.systemui.R +import com.android.systemui.controls.settings.ControlsSettingsDialogManager.Companion.MAX_NUMBER_ATTEMPTS_CONTROLS_DIALOG +import com.android.systemui.controls.settings.ControlsSettingsDialogManager.Companion.PREFS_SETTINGS_DIALOG_ATTEMPTS +import com.android.systemui.dagger.SysUISingleton +import com.android.systemui.plugins.ActivityStarter +import com.android.systemui.settings.UserFileManager +import com.android.systemui.settings.UserTracker +import com.android.systemui.statusbar.phone.SystemUIDialog +import com.android.systemui.statusbar.policy.DeviceControlsControllerImpl +import com.android.systemui.util.settings.SecureSettings +import javax.inject.Inject + +/** + * Manager to display a dialog to prompt user to enable controls related Settings: + * + * * [Settings.Secure.LOCKSCREEN_SHOW_CONTROLS] + * * [Settings.Secure.LOCKSCREEN_ALLOW_TRIVIAL_CONTROLS] + */ +interface ControlsSettingsDialogManager { + + /** + * Shows the corresponding dialog. In order for a dialog to appear, the following must be true + * + * * At least one of the Settings in [ControlsSettingsRepository] are `false`. + * * The dialog has not been seen by the user too many times (as defined by + * [MAX_NUMBER_ATTEMPTS_CONTROLS_DIALOG]). + * + * When the dialogs are shown, the following outcomes are possible: + * * User cancels the dialog by clicking outside or going back: we register that the dialog was + * seen but the settings don't change. + * * User responds negatively to the dialog: we register that the user doesn't want to change + * the settings (dialog will not appear again) and the settings don't change. + * * User responds positively to the dialog: the settings are set to `true` and the dialog will + * not appear again. + * * SystemUI closes the dialogs (for example, the activity showing it is closed). In this case, + * we don't modify anything. + * + * Of those four scenarios, only the first three will cause [onAttemptCompleted] to be called. + * It will also be called if the dialogs are not shown. + */ + fun maybeShowDialog(activityContext: Context, onAttemptCompleted: () -> Unit) + + /** + * Closes the dialog without registering anything from the user. The state of the settings after + * this is called will be the same as before the dialogs were shown. + */ + fun closeDialog() + + companion object { + @VisibleForTesting internal const val MAX_NUMBER_ATTEMPTS_CONTROLS_DIALOG = 2 + @VisibleForTesting + internal const val PREFS_SETTINGS_DIALOG_ATTEMPTS = "show_settings_attempts" + } +} + +@SysUISingleton +class ControlsSettingsDialogManagerImpl +@VisibleForTesting +internal constructor( + private val secureSettings: SecureSettings, + private val userFileManager: UserFileManager, + private val controlsSettingsRepository: ControlsSettingsRepository, + private val userTracker: UserTracker, + private val activityStarter: ActivityStarter, + private val dialogProvider: (context: Context, theme: Int) -> AlertDialog +) : ControlsSettingsDialogManager { + + @Inject + constructor( + secureSettings: SecureSettings, + userFileManager: UserFileManager, + controlsSettingsRepository: ControlsSettingsRepository, + userTracker: UserTracker, + activityStarter: ActivityStarter + ) : this( + secureSettings, + userFileManager, + controlsSettingsRepository, + userTracker, + activityStarter, + { context, theme -> SettingsDialog(context, theme) } + ) + + private var dialog: AlertDialog? = null + private set + + private val showDeviceControlsInLockscreen: Boolean + get() = controlsSettingsRepository.canShowControlsInLockscreen.value + + private val allowTrivialControls: Boolean + get() = controlsSettingsRepository.allowActionOnTrivialControlsInLockscreen.value + + override fun maybeShowDialog(activityContext: Context, onAttemptCompleted: () -> Unit) { + closeDialog() + + val prefs = + userFileManager.getSharedPreferences( + DeviceControlsControllerImpl.PREFS_CONTROLS_FILE, + MODE_PRIVATE, + userTracker.userId + ) + val attempts = prefs.getInt(PREFS_SETTINGS_DIALOG_ATTEMPTS, 0) + if ( + attempts >= MAX_NUMBER_ATTEMPTS_CONTROLS_DIALOG || + (showDeviceControlsInLockscreen && allowTrivialControls) + ) { + onAttemptCompleted() + return + } + + val listener = DialogListener(prefs, attempts, onAttemptCompleted) + val d = + dialogProvider(activityContext, R.style.Theme_SystemUI_Dialog).apply { + setIcon(R.drawable.ic_warning) + setOnCancelListener(listener) + setNeutralButton(R.string.controls_settings_dialog_neutral_button, listener) + setPositiveButton(R.string.controls_settings_dialog_positive_button, listener) + if (showDeviceControlsInLockscreen) { + setTitle(R.string.controls_settings_trivial_controls_dialog_title) + setMessage(R.string.controls_settings_trivial_controls_dialog_message) + } else { + setTitle(R.string.controls_settings_show_controls_dialog_title) + setMessage(R.string.controls_settings_show_controls_dialog_message) + } + } + + SystemUIDialog.registerDismissListener(d) { dialog = null } + SystemUIDialog.setDialogSize(d) + SystemUIDialog.setShowForAllUsers(d, true) + dialog = d + d.show() + } + + private fun turnOnSettingSecurely(settings: List<String>) { + val action = + ActivityStarter.OnDismissAction { + settings.forEach { setting -> + secureSettings.putIntForUser(setting, 1, userTracker.userId) + } + true + } + activityStarter.dismissKeyguardThenExecute( + action, + /* cancel */ null, + /* afterKeyguardGone */ true + ) + } + + override fun closeDialog() { + dialog?.dismiss() + } + + private inner class DialogListener( + private val prefs: SharedPreferences, + private val attempts: Int, + private val onComplete: () -> Unit + ) : DialogInterface.OnClickListener, DialogInterface.OnCancelListener { + override fun onClick(dialog: DialogInterface?, which: Int) { + if (dialog == null) return + if (which == DialogInterface.BUTTON_POSITIVE) { + val settings = mutableListOf(Settings.Secure.LOCKSCREEN_ALLOW_TRIVIAL_CONTROLS) + if (!showDeviceControlsInLockscreen) { + settings.add(Settings.Secure.LOCKSCREEN_SHOW_CONTROLS) + } + turnOnSettingSecurely(settings) + } + if (attempts != MAX_NUMBER_ATTEMPTS_CONTROLS_DIALOG) { + prefs + .edit() + .putInt(PREFS_SETTINGS_DIALOG_ATTEMPTS, MAX_NUMBER_ATTEMPTS_CONTROLS_DIALOG) + .apply() + } + onComplete() + } + + override fun onCancel(dialog: DialogInterface?) { + if (dialog == null) return + if (attempts < MAX_NUMBER_ATTEMPTS_CONTROLS_DIALOG) { + prefs.edit().putInt(PREFS_SETTINGS_DIALOG_ATTEMPTS, attempts + 1).apply() + } + onComplete() + } + } + + private fun AlertDialog.setNeutralButton( + msgId: Int, + listener: DialogInterface.OnClickListener + ) { + setButton(DialogInterface.BUTTON_NEUTRAL, context.getText(msgId), listener) + } + + private fun AlertDialog.setPositiveButton( + msgId: Int, + listener: DialogInterface.OnClickListener + ) { + setButton(DialogInterface.BUTTON_POSITIVE, context.getText(msgId), listener) + } + + private fun AlertDialog.setMessage(msgId: Int) { + setMessage(context.getText(msgId)) + } + + /** This is necessary because the constructors are `protected`. */ + private class SettingsDialog(context: Context, theme: Int) : AlertDialog(context, theme) +} diff --git a/packages/SystemUI/src/com/android/systemui/controls/ControlsSettingsRepository.kt b/packages/SystemUI/src/com/android/systemui/controls/settings/ControlsSettingsRepository.kt index 3d10ab906f2f..df2831c6eb96 100644 --- a/packages/SystemUI/src/com/android/systemui/controls/ControlsSettingsRepository.kt +++ b/packages/SystemUI/src/com/android/systemui/controls/settings/ControlsSettingsRepository.kt @@ -15,7 +15,7 @@ * */ -package com.android.systemui.controls +package com.android.systemui.controls.settings import kotlinx.coroutines.flow.StateFlow diff --git a/packages/SystemUI/src/com/android/systemui/controls/ControlsSettingsRepositoryImpl.kt b/packages/SystemUI/src/com/android/systemui/controls/settings/ControlsSettingsRepositoryImpl.kt index 9dc422a09674..8e3b5109339c 100644 --- a/packages/SystemUI/src/com/android/systemui/controls/ControlsSettingsRepositoryImpl.kt +++ b/packages/SystemUI/src/com/android/systemui/controls/settings/ControlsSettingsRepositoryImpl.kt @@ -15,7 +15,7 @@ * */ -package com.android.systemui.controls +package com.android.systemui.controls.settings import android.provider.Settings import com.android.systemui.common.coroutine.ConflatedCallbackFlow.conflatedCallbackFlow diff --git a/packages/SystemUI/src/com/android/systemui/controls/ui/ControlActionCoordinatorImpl.kt b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlActionCoordinatorImpl.kt index 8472ca0731d7..99a10a33ab0f 100644 --- a/packages/SystemUI/src/com/android/systemui/controls/ui/ControlActionCoordinatorImpl.kt +++ b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlActionCoordinatorImpl.kt @@ -18,15 +18,13 @@ package com.android.systemui.controls.ui import android.annotation.AnyThread import android.annotation.MainThread -import android.app.AlertDialog +import android.app.Activity import android.app.Dialog import android.app.PendingIntent import android.content.Context import android.content.pm.PackageManager import android.content.pm.ResolveInfo -import android.os.UserHandle import android.os.VibrationEffect -import android.provider.Settings.Secure import android.service.controls.Control import android.service.controls.actions.BooleanAction import android.service.controls.actions.CommandAction @@ -34,39 +32,36 @@ import android.service.controls.actions.FloatAction import android.util.Log import android.view.HapticFeedbackConstants import com.android.internal.annotations.VisibleForTesting -import com.android.systemui.R import com.android.systemui.broadcast.BroadcastSender import com.android.systemui.controls.ControlsMetricsLogger -import com.android.systemui.controls.ControlsSettingsRepository +import com.android.systemui.controls.settings.ControlsSettingsDialogManager +import com.android.systemui.controls.settings.ControlsSettingsRepository import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dagger.qualifiers.Main +import com.android.systemui.flags.FeatureFlags +import com.android.systemui.flags.Flags import com.android.systemui.plugins.ActivityStarter -import com.android.systemui.settings.UserContextProvider import com.android.systemui.statusbar.VibratorHelper -import com.android.systemui.statusbar.phone.SystemUIDialog -import com.android.systemui.statusbar.policy.DeviceControlsControllerImpl.Companion.PREFS_CONTROLS_FILE -import com.android.systemui.statusbar.policy.DeviceControlsControllerImpl.Companion.PREFS_SETTINGS_DIALOG_ATTEMPTS import com.android.systemui.statusbar.policy.KeyguardStateController import com.android.systemui.util.concurrency.DelayableExecutor -import com.android.systemui.util.settings.SecureSettings import com.android.wm.shell.TaskViewFactory import java.util.Optional import javax.inject.Inject @SysUISingleton class ControlActionCoordinatorImpl @Inject constructor( - private val context: Context, - private val bgExecutor: DelayableExecutor, - @Main private val uiExecutor: DelayableExecutor, - private val activityStarter: ActivityStarter, - private val broadcastSender: BroadcastSender, - private val keyguardStateController: KeyguardStateController, - private val taskViewFactory: Optional<TaskViewFactory>, - private val controlsMetricsLogger: ControlsMetricsLogger, - private val vibrator: VibratorHelper, - private val secureSettings: SecureSettings, - private val userContextProvider: UserContextProvider, - private val controlsSettingsRepository: ControlsSettingsRepository, + private val context: Context, + private val bgExecutor: DelayableExecutor, + @Main private val uiExecutor: DelayableExecutor, + private val activityStarter: ActivityStarter, + private val broadcastSender: BroadcastSender, + private val keyguardStateController: KeyguardStateController, + private val taskViewFactory: Optional<TaskViewFactory>, + private val controlsMetricsLogger: ControlsMetricsLogger, + private val vibrator: VibratorHelper, + private val controlsSettingsRepository: ControlsSettingsRepository, + private val controlsSettingsDialogManager: ControlsSettingsDialogManager, + private val featureFlags: FeatureFlags, ) : ControlActionCoordinator { private var dialog: Dialog? = null private var pendingAction: Action? = null @@ -75,18 +70,26 @@ class ControlActionCoordinatorImpl @Inject constructor( get() = !keyguardStateController.isUnlocked() private val allowTrivialControls: Boolean get() = controlsSettingsRepository.allowActionOnTrivialControlsInLockscreen.value - private val showDeviceControlsInLockscreen: Boolean - get() = controlsSettingsRepository.canShowControlsInLockscreen.value override lateinit var activityContext: Context companion object { private const val RESPONSE_TIMEOUT_IN_MILLIS = 3000L - private const val MAX_NUMBER_ATTEMPTS_CONTROLS_DIALOG = 2 } override fun closeDialogs() { - dialog?.dismiss() - dialog = null + if (!featureFlags.isEnabled(Flags.USE_APP_PANELS)) { + controlsSettingsDialogManager.closeDialog() + } + val isActivityFinishing = + (activityContext as? Activity)?.let { it.isFinishing || it.isDestroyed } + if (isActivityFinishing == true) { + dialog = null + return + } + if (dialog?.isShowing == true) { + dialog?.dismiss() + dialog = null + } } override fun toggle(cvh: ControlViewHolder, templateId: String, isChecked: Boolean) { @@ -244,71 +247,9 @@ class ControlActionCoordinatorImpl @Inject constructor( if (action.authIsRequired) { return } - val prefs = userContextProvider.userContext.getSharedPreferences( - PREFS_CONTROLS_FILE, Context.MODE_PRIVATE) - val attempts = prefs.getInt(PREFS_SETTINGS_DIALOG_ATTEMPTS, 0) - if (attempts >= MAX_NUMBER_ATTEMPTS_CONTROLS_DIALOG || - (showDeviceControlsInLockscreen && allowTrivialControls)) { - return - } - val builder = AlertDialog - .Builder(activityContext, R.style.Theme_SystemUI_Dialog) - .setIcon(R.drawable.ic_warning) - .setOnCancelListener { - if (attempts < MAX_NUMBER_ATTEMPTS_CONTROLS_DIALOG) { - prefs.edit().putInt(PREFS_SETTINGS_DIALOG_ATTEMPTS, attempts + 1) - .commit() - } - true - } - .setNeutralButton(R.string.controls_settings_dialog_neutral_button) { _, _ -> - if (attempts != MAX_NUMBER_ATTEMPTS_CONTROLS_DIALOG) { - prefs.edit().putInt(PREFS_SETTINGS_DIALOG_ATTEMPTS, - MAX_NUMBER_ATTEMPTS_CONTROLS_DIALOG) - .commit() - } - true - } - - if (showDeviceControlsInLockscreen) { - dialog = builder - .setTitle(R.string.controls_settings_trivial_controls_dialog_title) - .setMessage(R.string.controls_settings_trivial_controls_dialog_message) - .setPositiveButton(R.string.controls_settings_dialog_positive_button) { _, _ -> - if (attempts != MAX_NUMBER_ATTEMPTS_CONTROLS_DIALOG) { - prefs.edit().putInt(PREFS_SETTINGS_DIALOG_ATTEMPTS, - MAX_NUMBER_ATTEMPTS_CONTROLS_DIALOG) - .commit() - } - secureSettings.putIntForUser(Secure.LOCKSCREEN_ALLOW_TRIVIAL_CONTROLS, 1, - UserHandle.USER_CURRENT) - true - } - .create() - } else { - dialog = builder - .setTitle(R.string.controls_settings_show_controls_dialog_title) - .setMessage(R.string.controls_settings_show_controls_dialog_message) - .setPositiveButton(R.string.controls_settings_dialog_positive_button) { _, _ -> - if (attempts != MAX_NUMBER_ATTEMPTS_CONTROLS_DIALOG) { - prefs.edit().putInt(PREFS_SETTINGS_DIALOG_ATTEMPTS, - MAX_NUMBER_ATTEMPTS_CONTROLS_DIALOG) - .commit() - } - secureSettings.putIntForUser(Secure.LOCKSCREEN_SHOW_CONTROLS, - 1, UserHandle.USER_CURRENT) - secureSettings.putIntForUser(Secure.LOCKSCREEN_ALLOW_TRIVIAL_CONTROLS, - 1, UserHandle.USER_CURRENT) - true - } - .create() + if (!featureFlags.isEnabled(Flags.USE_APP_PANELS)) { + controlsSettingsDialogManager.maybeShowDialog(activityContext) {} } - - SystemUIDialog.registerDismissListener(dialog) - SystemUIDialog.setDialogSize(dialog) - - dialog?.create() - dialog?.show() } @VisibleForTesting diff --git a/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsActivity.kt b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsActivity.kt index bd704c1ff086..5d611c4c8212 100644 --- a/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsActivity.kt +++ b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsActivity.kt @@ -32,8 +32,10 @@ import androidx.activity.ComponentActivity import com.android.systemui.R import com.android.systemui.broadcast.BroadcastDispatcher import com.android.systemui.controls.management.ControlsAnimations +import com.android.systemui.controls.settings.ControlsSettingsDialogManager import com.android.systemui.flags.FeatureFlags import com.android.systemui.flags.Flags +import com.android.systemui.statusbar.policy.KeyguardStateController import javax.inject.Inject /** @@ -47,7 +49,9 @@ class ControlsActivity @Inject constructor( private val uiController: ControlsUiController, private val broadcastDispatcher: BroadcastDispatcher, private val dreamManager: IDreamManager, - private val featureFlags: FeatureFlags + private val featureFlags: FeatureFlags, + private val controlsSettingsDialogManager: ControlsSettingsDialogManager, + private val keyguardStateController: KeyguardStateController ) : ComponentActivity() { private lateinit var parent: ViewGroup @@ -92,7 +96,13 @@ class ControlsActivity @Inject constructor( parent = requireViewById<ViewGroup>(R.id.global_actions_controls) parent.alpha = 0f - uiController.show(parent, { finishOrReturnToDream() }, this) + if (featureFlags.isEnabled(Flags.USE_APP_PANELS) && !keyguardStateController.isUnlocked) { + controlsSettingsDialogManager.maybeShowDialog(this) { + uiController.show(parent, { finishOrReturnToDream() }, this) + } + } else { + uiController.show(parent, { finishOrReturnToDream() }, this) + } ControlsAnimations.enterAnimation(parent).start() } @@ -124,6 +134,7 @@ class ControlsActivity @Inject constructor( mExitToDream = false uiController.hide() + controlsSettingsDialogManager.closeDialog() } override fun onDestroy() { diff --git a/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsUiControllerImpl.kt b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsUiControllerImpl.kt index a07c716bc8f9..fb678aa420bf 100644 --- a/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsUiControllerImpl.kt +++ b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsUiControllerImpl.kt @@ -49,7 +49,7 @@ import com.android.systemui.Dumpable import com.android.systemui.R import com.android.systemui.controls.ControlsMetricsLogger import com.android.systemui.controls.ControlsServiceInfo -import com.android.systemui.controls.ControlsSettingsRepository +import com.android.systemui.controls.settings.ControlsSettingsRepository import com.android.systemui.controls.CustomIconCache import com.android.systemui.controls.controller.ControlsController import com.android.systemui.controls.controller.StructureInfo diff --git a/packages/SystemUI/src/com/android/systemui/flags/Flags.kt b/packages/SystemUI/src/com/android/systemui/flags/Flags.kt index c322b129b908..ad6106b48328 100644 --- a/packages/SystemUI/src/com/android/systemui/flags/Flags.kt +++ b/packages/SystemUI/src/com/android/systemui/flags/Flags.kt @@ -71,6 +71,9 @@ object Flags { val NOTIFICATION_MEMORY_MONITOR_ENABLED = releasedFlag(112, "notification_memory_monitor_enabled") + val NOTIFICATION_MEMORY_LOGGING_ENABLED = + unreleasedFlag(119, "notification_memory_logging_enabled", teamfood = true) + // TODO(b/254512731): Tracking Bug @JvmField val NOTIFICATION_DISMISSAL_FADE = @@ -91,6 +94,7 @@ object Flags { unreleasedFlag(259217907, "notification_group_dismissal_animation", teamfood = true) // TODO(b/257506350): Tracking Bug + @JvmField val FSI_CHROME = unreleasedFlag(117, "fsi_chrome") @JvmField @@ -98,7 +102,7 @@ object Flags { unreleasedFlag(259395680, "simplified_appear_fraction", teamfood = true) // TODO(b/257315550): Tracking Bug - val NO_HUN_FOR_OLD_WHEN = unreleasedFlag(118, "no_hun_for_old_when") + val NO_HUN_FOR_OLD_WHEN = unreleasedFlag(118, "no_hun_for_old_when", teamfood = true) val FILTER_UNSEEN_NOTIFS_ON_KEYGUARD = unreleasedFlag(254647461, "filter_unseen_notifs_on_keyguard", teamfood = true) @@ -134,7 +138,8 @@ object Flags { * Whether the clock on a wide lock screen should use the new "stepping" animation for moving * the digits when the clock moves. */ - @JvmField val STEP_CLOCK_ANIMATION = unreleasedFlag(212, "step_clock_animation") + @JvmField + val STEP_CLOCK_ANIMATION = unreleasedFlag(212, "step_clock_animation", teamfood = true) /** * Migration from the legacy isDozing/dozeAmount paths to the new KeyguardTransitionRepository @@ -204,9 +209,6 @@ object Flags { "full_screen_user_switcher" ) - // TODO(b/254512678): Tracking Bug - @JvmField val NEW_FOOTER_ACTIONS = releasedFlag(507, "new_footer_actions") - // TODO(b/244064524): Tracking Bug @JvmField val QS_SECONDARY_DATA_SUB_INFO = diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java index 6ed555056cb1..306e92e6c96c 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java +++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java @@ -2214,6 +2214,9 @@ public class KeyguardViewMediator implements CoreStartable, Dumpable, case START_KEYGUARD_EXIT_ANIM: Trace.beginSection( "KeyguardViewMediator#handleMessage START_KEYGUARD_EXIT_ANIM"); + synchronized (KeyguardViewMediator.this) { + mHiding = true; + } StartKeyguardExitAnimParams params = (StartKeyguardExitAnimParams) msg.obj; mNotificationShadeWindowControllerLazy.get().batchApplyWindowLayoutParams( () -> { diff --git a/packages/SystemUI/src/com/android/systemui/qs/FgsManagerController.kt b/packages/SystemUI/src/com/android/systemui/qs/FgsManagerController.kt index 3c1077867582..930de1302c58 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/FgsManagerController.kt +++ b/packages/SystemUI/src/com/android/systemui/qs/FgsManagerController.kt @@ -122,10 +122,6 @@ interface FgsManagerController { /** Remove a [OnDialogDismissedListener]. */ fun removeOnDialogDismissedListener(listener: OnDialogDismissedListener) - /** Whether we should update the footer visibility. */ - // TODO(b/242040009): Remove this. - fun shouldUpdateFooterVisibility(): Boolean - @VisibleForTesting fun visibleButtonsCount(): Int @@ -375,8 +371,6 @@ class FgsManagerControllerImpl @Inject constructor( } } - override fun shouldUpdateFooterVisibility() = dialog == null - override fun showDialog(expandable: Expandable?) { synchronized(lock) { if (dialog == null) { diff --git a/packages/SystemUI/src/com/android/systemui/qs/FooterActionsController.kt b/packages/SystemUI/src/com/android/systemui/qs/FooterActionsController.kt index a9943e886339..b52233fc748b 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/FooterActionsController.kt +++ b/packages/SystemUI/src/com/android/systemui/qs/FooterActionsController.kt @@ -1,5 +1,5 @@ /* - * Copyright (C) 2021 The Android Open Source Project + * Copyright (C) 2022 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,261 +16,17 @@ package com.android.systemui.qs -import android.content.Intent -import android.content.res.Configuration -import android.os.Handler -import android.os.UserManager -import android.provider.Settings -import android.provider.Settings.Global.USER_SWITCHER_ENABLED -import android.view.View -import android.view.ViewGroup -import androidx.annotation.VisibleForTesting -import com.android.internal.jank.InteractionJankMonitor -import com.android.internal.logging.MetricsLogger -import com.android.internal.logging.UiEventLogger -import com.android.internal.logging.nano.MetricsProto -import com.android.keyguard.KeyguardUpdateMonitor -import com.android.systemui.R -import com.android.systemui.animation.ActivityLaunchAnimator -import com.android.systemui.animation.Expandable -import com.android.systemui.globalactions.GlobalActionsDialogLite -import com.android.systemui.plugins.ActivityStarter -import com.android.systemui.plugins.FalsingManager -import com.android.systemui.qs.dagger.QSFlagsModule.PM_LITE_ENABLED -import com.android.systemui.qs.dagger.QSScope -import com.android.systemui.settings.UserTracker -import com.android.systemui.statusbar.phone.MultiUserSwitchController -import com.android.systemui.statusbar.policy.ConfigurationController -import com.android.systemui.statusbar.policy.DeviceProvisionedController -import com.android.systemui.statusbar.policy.UserInfoController -import com.android.systemui.statusbar.policy.UserInfoController.OnUserInfoChangedListener -import com.android.systemui.util.LargeScreenUtils -import com.android.systemui.util.ViewController -import com.android.systemui.util.settings.GlobalSettings +import com.android.systemui.dagger.SysUISingleton import javax.inject.Inject -import javax.inject.Named -import javax.inject.Provider -/** - * Manages [FooterActionsView] behaviour, both when it's placed in QS or QQS (split shade). - * Main difference between QS and QQS behaviour is condition when buttons should be visible, - * determined by [buttonsVisibleState] - */ -@QSScope -// TODO(b/242040009): Remove this file. -internal class FooterActionsController @Inject constructor( - view: FooterActionsView, - multiUserSwitchControllerFactory: MultiUserSwitchController.Factory, - private val activityStarter: ActivityStarter, - private val userManager: UserManager, - private val userTracker: UserTracker, - private val userInfoController: UserInfoController, - private val deviceProvisionedController: DeviceProvisionedController, - private val securityFooterController: QSSecurityFooter, - private val fgsManagerFooterController: QSFgsManagerFooter, - private val falsingManager: FalsingManager, - private val metricsLogger: MetricsLogger, - private val globalActionsDialogProvider: Provider<GlobalActionsDialogLite>, - private val uiEventLogger: UiEventLogger, - @Named(PM_LITE_ENABLED) private val showPMLiteButton: Boolean, - private val globalSetting: GlobalSettings, - private val handler: Handler, - private val configurationController: ConfigurationController, -) : ViewController<FooterActionsView>(view) { - - private var globalActionsDialog: GlobalActionsDialogLite? = null - - private var lastExpansion = -1f - private var listening: Boolean = false - private var inSplitShade = false - - private val singleShadeAnimator by lazy { - // In single shade, the actions footer should only appear at the end of the expansion, - // so that it doesn't overlap with the notifications panel. - TouchAnimator.Builder().addFloat(mView, "alpha", 0f, 1f).setStartDelay(0.9f).build() - } - - private val splitShadeAnimator by lazy { - // The Actions footer view has its own background which is the same color as the qs panel's - // background. - // We don't want it to fade in at the same time as the rest of the panel, otherwise it is - // more opaque than the rest of the panel's background. Only applies to split shade. - val alphaAnimator = TouchAnimator.Builder().addFloat(mView, "alpha", 0f, 1f).build() - val bgAlphaAnimator = - TouchAnimator.Builder() - .addFloat(mView, "backgroundAlpha", 0f, 1f) - .setStartDelay(0.9f) - .build() - // In split shade, we want the actions footer to fade in exactly at the same time as the - // rest of the shade, as there is no overlap. - TouchAnimator.Builder() - .addFloat(alphaAnimator, "position", 0f, 1f) - .addFloat(bgAlphaAnimator, "position", 0f, 1f) - .build() - } - - private val animators: TouchAnimator - get() = if (inSplitShade) splitShadeAnimator else singleShadeAnimator - - var visible = true - set(value) { - field = value - updateVisibility() - } - - private val settingsButtonContainer: View = view.findViewById(R.id.settings_button_container) - private val securityFootersContainer: ViewGroup? = - view.findViewById(R.id.security_footers_container) - private val powerMenuLite: View = view.findViewById(R.id.pm_lite) - private val multiUserSwitchController = multiUserSwitchControllerFactory.create(view) - - @VisibleForTesting - internal val securityFootersSeparator = View(context).apply { visibility = View.GONE } - - private val onUserInfoChangedListener = OnUserInfoChangedListener { _, picture, _ -> - val isGuestUser: Boolean = userManager.isGuestUser(KeyguardUpdateMonitor.getCurrentUser()) - mView.onUserInfoChanged(picture, isGuestUser) - } - - private val multiUserSetting = - object : SettingObserver( - globalSetting, handler, USER_SWITCHER_ENABLED, userTracker.userId) { - override fun handleValueChanged(value: Int, observedChange: Boolean) { - if (observedChange) { - updateView() - } - } - } - - private val onClickListener = View.OnClickListener { v -> - // Don't do anything if the tap looks suspicious. - if (!visible || falsingManager.isFalseTap(FalsingManager.LOW_PENALTY)) { - return@OnClickListener - } - if (v === settingsButtonContainer) { - if (!deviceProvisionedController.isCurrentUserSetup) { - // If user isn't setup just unlock the device and dump them back at SUW. - activityStarter.postQSRunnableDismissingKeyguard {} - return@OnClickListener - } - metricsLogger.action(MetricsProto.MetricsEvent.ACTION_QS_EXPANDED_SETTINGS_LAUNCH) - startSettingsActivity() - } else if (v === powerMenuLite) { - uiEventLogger.log(GlobalActionsDialogLite.GlobalActionsEvent.GA_OPEN_QS) - globalActionsDialog?.showOrHideDialog(false, true, Expandable.fromView(powerMenuLite)) - } - } - - private val configurationListener = - object : ConfigurationController.ConfigurationListener { - override fun onConfigChanged(newConfig: Configuration?) { - updateResources() - } - } - - private fun updateResources() { - inSplitShade = LargeScreenUtils.shouldUseSplitNotificationShade(resources) - } - - override fun onInit() { - multiUserSwitchController.init() - securityFooterController.init() - fgsManagerFooterController.init() - } - - private fun updateVisibility() { - val previousVisibility = mView.visibility - mView.visibility = if (visible) View.VISIBLE else View.INVISIBLE - if (previousVisibility != mView.visibility) updateView() - } - - private fun startSettingsActivity() { - val animationController = settingsButtonContainer?.let { - ActivityLaunchAnimator.Controller.fromView( - it, - InteractionJankMonitor.CUJ_SHADE_APP_LAUNCH_FROM_SETTINGS_BUTTON) - } - activityStarter.startActivity(Intent(Settings.ACTION_SETTINGS), - true /* dismissShade */, animationController) - } - - @VisibleForTesting - public override fun onViewAttached() { - globalActionsDialog = globalActionsDialogProvider.get() - if (showPMLiteButton) { - powerMenuLite.visibility = View.VISIBLE - powerMenuLite.setOnClickListener(onClickListener) - } else { - powerMenuLite.visibility = View.GONE - } - settingsButtonContainer.setOnClickListener(onClickListener) - multiUserSetting.isListening = true - - val securityFooter = securityFooterController.view - securityFootersContainer?.addView(securityFooter) - val separatorWidth = resources.getDimensionPixelSize(R.dimen.qs_footer_action_inset) - securityFootersContainer?.addView(securityFootersSeparator, separatorWidth, 1) - - val fgsFooter = fgsManagerFooterController.view - securityFootersContainer?.addView(fgsFooter) - - val visibilityListener = - VisibilityChangedDispatcher.OnVisibilityChangedListener { visibility -> - if (securityFooter.visibility == View.VISIBLE && - fgsFooter.visibility == View.VISIBLE) { - securityFootersSeparator.visibility = View.VISIBLE - } else { - securityFootersSeparator.visibility = View.GONE - } - fgsManagerFooterController - .setCollapsed(securityFooter.visibility == View.VISIBLE) - } - securityFooterController.setOnVisibilityChangedListener(visibilityListener) - fgsManagerFooterController.setOnVisibilityChangedListener(visibilityListener) - - configurationController.addCallback(configurationListener) - - updateResources() - updateView() - } - - private fun updateView() { - mView.updateEverything(multiUserSwitchController.isMultiUserEnabled) - } - - override fun onViewDetached() { - globalActionsDialog?.destroy() - globalActionsDialog = null - setListening(false) - multiUserSetting.isListening = false - configurationController.removeCallback(configurationListener) - } - - fun setListening(listening: Boolean) { - if (this.listening == listening) { - return - } - this.listening = listening - if (this.listening) { - userInfoController.addCallback(onUserInfoChangedListener) - updateView() - } else { - userInfoController.removeCallback(onUserInfoChangedListener) - } - - fgsManagerFooterController.setListening(listening) - securityFooterController.setListening(listening) - } - - fun disable(state2: Int) { - mView.disable(state2, multiUserSwitchController.isMultiUserEnabled) - } - - fun setExpansion(headerExpansionFraction: Float) { - animators.setPosition(headerExpansionFraction) - } - - fun setKeyguardShowing(showing: Boolean) { - setExpansion(lastExpansion) +/** Controller for the footer actions. This manages the initialization of its dependencies. */ +@SysUISingleton +class FooterActionsController +@Inject +constructor( + private val fgsManagerController: FgsManagerController, +) { + fun init() { + fgsManagerController.init() } } diff --git a/packages/SystemUI/src/com/android/systemui/qs/FooterActionsView.kt b/packages/SystemUI/src/com/android/systemui/qs/FooterActionsView.kt deleted file mode 100644 index d602b0b27977..000000000000 --- a/packages/SystemUI/src/com/android/systemui/qs/FooterActionsView.kt +++ /dev/null @@ -1,135 +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.systemui.qs - -import android.app.StatusBarManager -import android.content.Context -import android.graphics.PorterDuff -import android.graphics.drawable.Drawable -import android.graphics.drawable.RippleDrawable -import android.os.UserManager -import android.util.AttributeSet -import android.util.Log -import android.view.MotionEvent -import android.view.View -import android.widget.ImageView -import android.widget.LinearLayout -import androidx.annotation.Keep -import com.android.settingslib.Utils -import com.android.settingslib.drawable.UserIconDrawable -import com.android.systemui.R -import com.android.systemui.statusbar.phone.MultiUserSwitch - -/** - * Quick Settings bottom buttons placed in footer (aka utility bar) - always visible in expanded QS, - * in split shade mode visible also in collapsed state. May contain up to 5 buttons: settings, - * edit tiles, power off and conditionally: user switch and tuner - */ -// TODO(b/242040009): Remove this file. -class FooterActionsView(context: Context?, attrs: AttributeSet?) : LinearLayout(context, attrs) { - private lateinit var settingsContainer: View - private lateinit var multiUserSwitch: MultiUserSwitch - private lateinit var multiUserAvatar: ImageView - - private var qsDisabled = false - private var expansionAmount = 0f - - /** - * Sets the alpha of the background of this view. - * - * Used from a [TouchAnimator] in the controller. - */ - var backgroundAlpha: Float = 1f - @Keep - set(value) { - field = value - background?.alpha = (value * 255).toInt() - } - @Keep get - - override fun onFinishInflate() { - super.onFinishInflate() - settingsContainer = findViewById(R.id.settings_button_container) - multiUserSwitch = findViewById(R.id.multi_user_switch) - multiUserAvatar = multiUserSwitch.findViewById(R.id.multi_user_avatar) - - // RenderThread is doing more harm than good when touching the header (to expand quick - // settings), so disable it for this view - if (settingsContainer.background is RippleDrawable) { - (settingsContainer.background as RippleDrawable).setForceSoftware(true) - } - importantForAccessibility = View.IMPORTANT_FOR_ACCESSIBILITY_YES - } - - fun disable( - state2: Int, - multiUserEnabled: Boolean - ) { - val disabled = state2 and StatusBarManager.DISABLE2_QUICK_SETTINGS != 0 - if (disabled == qsDisabled) return - qsDisabled = disabled - updateEverything(multiUserEnabled) - } - - fun updateEverything( - multiUserEnabled: Boolean - ) { - post { - updateVisibilities(multiUserEnabled) - updateClickabilities() - isClickable = false - } - } - - private fun updateClickabilities() { - multiUserSwitch.isClickable = multiUserSwitch.visibility == VISIBLE - settingsContainer.isClickable = settingsContainer.visibility == VISIBLE - } - - private fun updateVisibilities( - multiUserEnabled: Boolean - ) { - settingsContainer.visibility = if (qsDisabled) GONE else VISIBLE - multiUserSwitch.visibility = if (multiUserEnabled) VISIBLE else GONE - val isDemo = UserManager.isDeviceInDemoMode(context) - settingsContainer.visibility = if (isDemo) INVISIBLE else VISIBLE - } - - fun onUserInfoChanged(picture: Drawable?, isGuestUser: Boolean) { - var pictureToSet = picture - if (picture != null && isGuestUser && picture !is UserIconDrawable) { - pictureToSet = picture.constantState.newDrawable(resources).mutate() - pictureToSet.setColorFilter( - Utils.getColorAttrDefaultColor(mContext, android.R.attr.colorForeground), - PorterDuff.Mode.SRC_IN) - } - multiUserAvatar.setImageDrawable(pictureToSet) - } - - override fun onInterceptTouchEvent(ev: MotionEvent?): Boolean { - if (VERBOSE) Log.d(TAG, "FooterActionsView onInterceptTouchEvent ${ev?.string}") - return super.onInterceptTouchEvent(ev) - } - - override fun onTouchEvent(event: MotionEvent?): Boolean { - if (VERBOSE) Log.d(TAG, "FooterActionsView onTouchEvent ${event?.string}") - return super.onTouchEvent(event) - } -} -private const val TAG = "FooterActionsView" -private val VERBOSE = Log.isLoggable(TAG, Log.VERBOSE) -private val MotionEvent.string - get() = "($id): ($x,$y)" diff --git a/packages/SystemUI/src/com/android/systemui/qs/NewFooterActionsController.kt b/packages/SystemUI/src/com/android/systemui/qs/NewFooterActionsController.kt deleted file mode 100644 index 7c67d9f42b55..000000000000 --- a/packages/SystemUI/src/com/android/systemui/qs/NewFooterActionsController.kt +++ /dev/null @@ -1,33 +0,0 @@ -/* - * Copyright (C) 2022 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 - -import com.android.systemui.dagger.SysUISingleton -import javax.inject.Inject - -/** Controller for the footer actions. This manages the initialization of its dependencies. */ -@SysUISingleton -class NewFooterActionsController -@Inject -// TODO(b/242040009): Rename this to FooterActionsController. -constructor( - private val fgsManagerController: FgsManagerController, -) { - fun init() { - fgsManagerController.init() - } -} diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSContainerImpl.java b/packages/SystemUI/src/com/android/systemui/qs/QSContainerImpl.java index dc9dcc295e6e..0c242d9da25f 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/QSContainerImpl.java +++ b/packages/SystemUI/src/com/android/systemui/qs/QSContainerImpl.java @@ -215,7 +215,7 @@ public class QSContainerImpl extends FrameLayout implements Dumpable { // Some views are always full width or have dependent padding continue; } - if (!(view instanceof FooterActionsView)) { + if (view.getId() != R.id.qs_footer_actions) { // Only padding for FooterActionsView, no margin. That way, the background goes // all the way to the edge. LayoutParams lp = (LayoutParams) view.getLayoutParams(); diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSFgsManagerFooter.java b/packages/SystemUI/src/com/android/systemui/qs/QSFgsManagerFooter.java deleted file mode 100644 index b1b9dd721eaf..000000000000 --- a/packages/SystemUI/src/com/android/systemui/qs/QSFgsManagerFooter.java +++ /dev/null @@ -1,178 +0,0 @@ -/* - * Copyright (C) 2022 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; - -import static com.android.systemui.qs.dagger.QSFragmentModule.QS_FGS_MANAGER_FOOTER_VIEW; -import static com.android.systemui.util.PluralMessageFormaterKt.icuMessageFormat; - -import android.content.Context; -import android.view.View; -import android.view.ViewGroup; -import android.widget.ImageView; -import android.widget.LinearLayout; -import android.widget.TextView; - -import androidx.annotation.Nullable; - -import com.android.systemui.R; -import com.android.systemui.animation.Expandable; -import com.android.systemui.dagger.qualifiers.Background; -import com.android.systemui.dagger.qualifiers.Main; -import com.android.systemui.qs.dagger.QSScope; - -import java.util.concurrent.Executor; - -import javax.inject.Inject; -import javax.inject.Named; - -/** - * Footer entry point for the foreground service manager - */ -// TODO(b/242040009): Remove this file. -@QSScope -public class QSFgsManagerFooter implements View.OnClickListener, - FgsManagerController.OnDialogDismissedListener, - FgsManagerController.OnNumberOfPackagesChangedListener, - VisibilityChangedDispatcher { - - private final View mRootView; - private final TextView mFooterText; - private final Context mContext; - private final Executor mMainExecutor; - private final Executor mExecutor; - - private final FgsManagerController mFgsManagerController; - - private boolean mIsInitialized = false; - private int mNumPackages; - - private final View mTextContainer; - private final View mNumberContainer; - private final TextView mNumberView; - private final ImageView mDotView; - private final ImageView mCollapsedDotView; - - @Nullable - private VisibilityChangedDispatcher.OnVisibilityChangedListener mVisibilityChangedListener; - - @Inject - QSFgsManagerFooter(@Named(QS_FGS_MANAGER_FOOTER_VIEW) View rootView, - @Main Executor mainExecutor, @Background Executor executor, - FgsManagerController fgsManagerController) { - mRootView = rootView; - mFooterText = mRootView.findViewById(R.id.footer_text); - mTextContainer = mRootView.findViewById(R.id.fgs_text_container); - mNumberContainer = mRootView.findViewById(R.id.fgs_number_container); - mNumberView = mRootView.findViewById(R.id.fgs_number); - mDotView = mRootView.findViewById(R.id.fgs_new); - mCollapsedDotView = mRootView.findViewById(R.id.fgs_collapsed_new); - mContext = rootView.getContext(); - mMainExecutor = mainExecutor; - mExecutor = executor; - mFgsManagerController = fgsManagerController; - } - - /** - * Whether to show the footer in collapsed mode (just a number) or not (text). - * @param collapsed - */ - public void setCollapsed(boolean collapsed) { - mTextContainer.setVisibility(collapsed ? View.GONE : View.VISIBLE); - mNumberContainer.setVisibility(collapsed ? View.VISIBLE : View.GONE); - LinearLayout.LayoutParams lp = (LinearLayout.LayoutParams) mRootView.getLayoutParams(); - lp.width = collapsed ? ViewGroup.LayoutParams.WRAP_CONTENT : 0; - lp.weight = collapsed ? 0f : 1f; - mRootView.setLayoutParams(lp); - } - - public void init() { - if (mIsInitialized) { - return; - } - - mFgsManagerController.init(); - - mRootView.setOnClickListener(this); - - mIsInitialized = true; - } - - public void setListening(boolean listening) { - if (listening) { - mFgsManagerController.addOnDialogDismissedListener(this); - mFgsManagerController.addOnNumberOfPackagesChangedListener(this); - mNumPackages = mFgsManagerController.getNumRunningPackages(); - refreshState(); - } else { - mFgsManagerController.removeOnDialogDismissedListener(this); - mFgsManagerController.removeOnNumberOfPackagesChangedListener(this); - } - } - - @Override - public void setOnVisibilityChangedListener( - @Nullable OnVisibilityChangedListener onVisibilityChangedListener) { - mVisibilityChangedListener = onVisibilityChangedListener; - } - - @Override - public void onClick(View view) { - mFgsManagerController.showDialog(Expandable.fromView(view)); - } - - public void refreshState() { - mExecutor.execute(this::handleRefreshState); - } - - public View getView() { - return mRootView; - } - - public void handleRefreshState() { - mMainExecutor.execute(() -> { - CharSequence text = icuMessageFormat(mContext.getResources(), - R.string.fgs_manager_footer_label, mNumPackages); - mFooterText.setText(text); - mNumberView.setText(Integer.toString(mNumPackages)); - mNumberView.setContentDescription(text); - if (mFgsManagerController.shouldUpdateFooterVisibility()) { - mRootView.setVisibility(mNumPackages > 0 - && mFgsManagerController.isAvailable().getValue() ? View.VISIBLE - : View.GONE); - int dotVis = mFgsManagerController.getShowFooterDot().getValue() - && mFgsManagerController.getNewChangesSinceDialogWasDismissed() - ? View.VISIBLE : View.GONE; - mDotView.setVisibility(dotVis); - mCollapsedDotView.setVisibility(dotVis); - if (mVisibilityChangedListener != null) { - mVisibilityChangedListener.onVisibilityChanged(mRootView.getVisibility()); - } - } - }); - } - - @Override - public void onDialogDismissed() { - refreshState(); - } - - @Override - public void onNumberOfPackagesChanged(int numPackages) { - mNumPackages = numPackages; - refreshState(); - } -} diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java b/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java index 71ab457659fe..f8fb4e89111e 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java +++ b/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java @@ -33,6 +33,7 @@ import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.view.ViewTreeObserver; +import android.widget.LinearLayout; import androidx.annotation.FloatRange; import androidx.annotation.Nullable; @@ -48,7 +49,6 @@ import com.android.systemui.animation.Interpolators; import com.android.systemui.animation.ShadeInterpolation; import com.android.systemui.dump.DumpManager; import com.android.systemui.flags.FeatureFlags; -import com.android.systemui.flags.Flags; import com.android.systemui.media.controls.ui.MediaHost; import com.android.systemui.plugins.FalsingManager; import com.android.systemui.plugins.qs.QS; @@ -114,7 +114,7 @@ public class QSFragment extends LifecycleFragment implements QS, CommandQueue.Ca private final QSFragmentDisableFlagsLogger mQsFragmentDisableFlagsLogger; private final QSTileHost mHost; private final FeatureFlags mFeatureFlags; - private final NewFooterActionsController mNewFooterActionsController; + private final FooterActionsController mFooterActionsController; private final FooterActionsViewModel.Factory mFooterActionsViewModelFactory; private final ListeningAndVisibilityLifecycleOwner mListeningAndVisibilityLifecycleOwner; private boolean mShowCollapsedOnKeyguard; @@ -132,9 +132,6 @@ public class QSFragment extends LifecycleFragment implements QS, CommandQueue.Ca private QSPanelController mQSPanelController; private QuickQSPanelController mQuickQSPanelController; private QSCustomizerController mQSCustomizerController; - @Nullable - private FooterActionsController mQSFooterActionController; - @Nullable private FooterActionsViewModel mQSFooterActionsViewModel; @Nullable private ScrollListener mScrollListener; @@ -185,7 +182,7 @@ public class QSFragment extends LifecycleFragment implements QS, CommandQueue.Ca QSFragmentComponent.Factory qsComponentFactory, QSFragmentDisableFlagsLogger qsFragmentDisableFlagsLogger, FalsingManager falsingManager, DumpManager dumpManager, FeatureFlags featureFlags, - NewFooterActionsController newFooterActionsController, + FooterActionsController footerActionsController, FooterActionsViewModel.Factory footerActionsViewModelFactory) { mRemoteInputQuickSettingsDisabler = remoteInputQsDisabler; mQsMediaHost = qsMediaHost; @@ -199,7 +196,7 @@ public class QSFragment extends LifecycleFragment implements QS, CommandQueue.Ca mStatusBarStateController = statusBarStateController; mDumpManager = dumpManager; mFeatureFlags = featureFlags; - mNewFooterActionsController = newFooterActionsController; + mFooterActionsController = footerActionsController; mFooterActionsViewModelFactory = footerActionsViewModelFactory; mListeningAndVisibilityLifecycleOwner = new ListeningAndVisibilityLifecycleOwner(); } @@ -226,18 +223,12 @@ public class QSFragment extends LifecycleFragment implements QS, CommandQueue.Ca mQSPanelController.init(); mQuickQSPanelController.init(); - if (mFeatureFlags.isEnabled(Flags.NEW_FOOTER_ACTIONS)) { - mQSFooterActionsViewModel = mFooterActionsViewModelFactory.create(/* lifecycleOwner */ - this); - FooterActionsView footerActionsView = view.findViewById(R.id.qs_footer_actions); - FooterActionsViewBinder.bind(footerActionsView, mQSFooterActionsViewModel, - mListeningAndVisibilityLifecycleOwner); - - mNewFooterActionsController.init(); - } else { - mQSFooterActionController = qsFragmentComponent.getQSFooterActionController(); - mQSFooterActionController.init(); - } + mQSFooterActionsViewModel = mFooterActionsViewModelFactory.create(/* lifecycleOwner */ + this); + LinearLayout footerActionsView = view.findViewById(R.id.qs_footer_actions); + FooterActionsViewBinder.bind(footerActionsView, mQSFooterActionsViewModel, + mListeningAndVisibilityLifecycleOwner); + mFooterActionsController.init(); mQSPanelScrollView = view.findViewById(R.id.expanded_qs_scroll_view); mQSPanelScrollView.addOnLayoutChangeListener( @@ -436,9 +427,6 @@ public class QSFragment extends LifecycleFragment implements QS, CommandQueue.Ca mContainer.disable(state1, state2, animate); mHeader.disable(state1, state2, animate); mFooter.disable(state1, state2, animate); - if (mQSFooterActionController != null) { - mQSFooterActionController.disable(state2); - } updateQsState(); } @@ -457,11 +445,7 @@ public class QSFragment extends LifecycleFragment implements QS, CommandQueue.Ca boolean footerVisible = qsPanelVisible && (mQsExpanded || !keyguardShowing || mHeaderAnimating || mShowCollapsedOnKeyguard); mFooter.setVisibility(footerVisible ? View.VISIBLE : View.INVISIBLE); - if (mQSFooterActionController != null) { - mQSFooterActionController.setVisible(footerVisible); - } else { - mQSFooterActionsViewModel.onVisibilityChangeRequested(footerVisible); - } + mQSFooterActionsViewModel.onVisibilityChangeRequested(footerVisible); mFooter.setExpanded((keyguardShowing && !mHeaderAnimating && !mShowCollapsedOnKeyguard) || (mQsExpanded && !mStackScrollerOverscrolling)); mQSPanelController.setVisibility(qsPanelVisible ? View.VISIBLE : View.INVISIBLE); @@ -534,9 +518,6 @@ public class QSFragment extends LifecycleFragment implements QS, CommandQueue.Ca } mFooter.setKeyguardShowing(keyguardShowing); - if (mQSFooterActionController != null) { - mQSFooterActionController.setKeyguardShowing(keyguardShowing); - } updateQsState(); } @@ -552,9 +533,6 @@ public class QSFragment extends LifecycleFragment implements QS, CommandQueue.Ca if (DEBUG) Log.d(TAG, "setListening " + listening); mListening = listening; mQSContainerImplController.setListening(listening && mQsVisible); - if (mQSFooterActionController != null) { - mQSFooterActionController.setListening(listening && mQsVisible); - } mListeningAndVisibilityLifecycleOwner.updateState(); updateQsPanelControllerListening(); } @@ -665,12 +643,8 @@ public class QSFragment extends LifecycleFragment implements QS, CommandQueue.Ca mFooter.setExpansion(onKeyguardAndExpanded ? 1 : expansion); float footerActionsExpansion = onKeyguardAndExpanded ? 1 : mInSplitShade ? alphaProgress : expansion; - if (mQSFooterActionController != null) { - mQSFooterActionController.setExpansion(footerActionsExpansion); - } else { - mQSFooterActionsViewModel.onQuickSettingsExpansionChanged(footerActionsExpansion, - mInSplitShade); - } + mQSFooterActionsViewModel.onQuickSettingsExpansionChanged(footerActionsExpansion, + mInSplitShade); mQSPanelController.setRevealExpansion(expansion); mQSPanelController.getTileLayout().setExpansion(expansion, proposedTranslation); mQuickQSPanelController.getTileLayout().setExpansion(expansion, proposedTranslation); @@ -835,11 +809,7 @@ public class QSFragment extends LifecycleFragment implements QS, CommandQueue.Ca boolean customizing = isCustomizing(); mQSPanelScrollView.setVisibility(!customizing ? View.VISIBLE : View.INVISIBLE); mFooter.setVisibility(!customizing ? View.VISIBLE : View.INVISIBLE); - if (mQSFooterActionController != null) { - mQSFooterActionController.setVisible(!customizing); - } else { - mQSFooterActionsViewModel.onVisibilityChangeRequested(!customizing); - } + mQSFooterActionsViewModel.onVisibilityChangeRequested(!customizing); mHeader.setVisibility(!customizing ? View.VISIBLE : View.INVISIBLE); // Let the panel know the position changed and it needs to update where notifications // and whatnot are. @@ -927,6 +897,11 @@ public class QSFragment extends LifecycleFragment implements QS, CommandQueue.Ca updateShowCollapsedOnKeyguard(); } + @VisibleForTesting + public ListeningAndVisibilityLifecycleOwner getListeningAndVisibilityLifecycleOwner() { + return mListeningAndVisibilityLifecycleOwner; + } + @Override public void dump(PrintWriter pw, String[] args) { IndentingPrintWriter indentingPw = new IndentingPrintWriter(pw, /* singleIndent= */ " "); @@ -994,7 +969,8 @@ public class QSFragment extends LifecycleFragment implements QS, CommandQueue.Ca * - STARTED when mListening == true && mQsVisible == false. * - RESUMED when mListening == true && mQsVisible == true. */ - private class ListeningAndVisibilityLifecycleOwner implements LifecycleOwner { + @VisibleForTesting + class ListeningAndVisibilityLifecycleOwner implements LifecycleOwner { private final LifecycleRegistry mLifecycleRegistry = new LifecycleRegistry(this); private boolean mDestroyed = false; diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSSecurityFooter.java b/packages/SystemUI/src/com/android/systemui/qs/QSSecurityFooter.java deleted file mode 100644 index 6c1e95645550..000000000000 --- a/packages/SystemUI/src/com/android/systemui/qs/QSSecurityFooter.java +++ /dev/null @@ -1,262 +0,0 @@ -/* - * Copyright (C) 2014 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; - -import static com.android.systemui.qs.dagger.QSFragmentModule.QS_SECURITY_FOOTER_VIEW; - -import android.app.admin.DevicePolicyEventLogger; -import android.app.admin.DevicePolicyManager; -import android.content.BroadcastReceiver; -import android.content.Context; -import android.content.Intent; -import android.content.IntentFilter; -import android.content.res.Resources; -import android.os.Handler; -import android.os.Looper; -import android.os.Message; -import android.os.UserHandle; -import android.util.Log; -import android.view.View; -import android.view.View.OnClickListener; -import android.widget.ImageView; -import android.widget.TextView; - -import androidx.annotation.Nullable; - -import com.android.internal.util.FrameworkStatsLog; -import com.android.systemui.FontSizeUtils; -import com.android.systemui.R; -import com.android.systemui.animation.Expandable; -import com.android.systemui.broadcast.BroadcastDispatcher; -import com.android.systemui.common.shared.model.Icon; -import com.android.systemui.dagger.qualifiers.Background; -import com.android.systemui.dagger.qualifiers.Main; -import com.android.systemui.qs.dagger.QSScope; -import com.android.systemui.qs.footer.domain.model.SecurityButtonConfig; -import com.android.systemui.security.data.model.SecurityModel; -import com.android.systemui.statusbar.policy.SecurityController; -import com.android.systemui.util.ViewController; - -import javax.inject.Inject; -import javax.inject.Named; - -/** ViewController for the footer actions. */ -// TODO(b/242040009): Remove this class. -@QSScope -public class QSSecurityFooter extends ViewController<View> - implements OnClickListener, VisibilityChangedDispatcher { - protected static final String TAG = "QSSecurityFooter"; - - private final TextView mFooterText; - private final ImageView mPrimaryFooterIcon; - private Context mContext; - private final Callback mCallback = new Callback(); - private final SecurityController mSecurityController; - private final Handler mMainHandler; - private final BroadcastDispatcher mBroadcastDispatcher; - private final QSSecurityFooterUtils mQSSecurityFooterUtils; - - protected H mHandler; - - private boolean mIsVisible; - private boolean mIsClickable; - @Nullable - private CharSequence mFooterTextContent = null; - private Icon mFooterIcon; - - @Nullable - private VisibilityChangedDispatcher.OnVisibilityChangedListener mVisibilityChangedListener; - - private final BroadcastReceiver mReceiver = new BroadcastReceiver() { - @Override - public void onReceive(Context context, Intent intent) { - if (intent.getAction().equals( - DevicePolicyManager.ACTION_SHOW_DEVICE_MONITORING_DIALOG)) { - showDeviceMonitoringDialog(); - } - } - }; - - @Inject - QSSecurityFooter(@Named(QS_SECURITY_FOOTER_VIEW) View rootView, - @Main Handler mainHandler, SecurityController securityController, - @Background Looper bgLooper, BroadcastDispatcher broadcastDispatcher, - QSSecurityFooterUtils qSSecurityFooterUtils) { - super(rootView); - mFooterText = mView.findViewById(R.id.footer_text); - mPrimaryFooterIcon = mView.findViewById(R.id.primary_footer_icon); - mFooterIcon = new Icon.Resource( - R.drawable.ic_info_outline, /* contentDescription= */ null); - mContext = rootView.getContext(); - mSecurityController = securityController; - mMainHandler = mainHandler; - mHandler = new H(bgLooper); - mBroadcastDispatcher = broadcastDispatcher; - mQSSecurityFooterUtils = qSSecurityFooterUtils; - } - - @Override - protected void onViewAttached() { - // Use background handler, as it's the same thread that handleClick is called on. - mBroadcastDispatcher.registerReceiverWithHandler(mReceiver, - new IntentFilter(DevicePolicyManager.ACTION_SHOW_DEVICE_MONITORING_DIALOG), - mHandler, UserHandle.ALL); - mView.setOnClickListener(this); - } - - @Override - protected void onViewDetached() { - mBroadcastDispatcher.unregisterReceiver(mReceiver); - mView.setOnClickListener(null); - } - - public void setListening(boolean listening) { - if (listening) { - mSecurityController.addCallback(mCallback); - refreshState(); - } else { - mSecurityController.removeCallback(mCallback); - } - } - - @Override - public void setOnVisibilityChangedListener( - @Nullable OnVisibilityChangedListener onVisibilityChangedListener) { - mVisibilityChangedListener = onVisibilityChangedListener; - } - - public void onConfigurationChanged() { - FontSizeUtils.updateFontSize(mFooterText, R.dimen.qs_tile_text_size); - Resources r = mContext.getResources(); - - int padding = r.getDimensionPixelSize(R.dimen.qs_footer_padding); - mView.setPaddingRelative(padding, 0, padding, 0); - mView.setBackground(mContext.getDrawable(R.drawable.qs_security_footer_background)); - } - - public View getView() { - return mView; - } - - public boolean hasFooter() { - return mView.getVisibility() != View.GONE; - } - - @Override - public void onClick(View v) { - if (!hasFooter()) return; - mHandler.sendEmptyMessage(H.CLICK); - } - - private void handleClick() { - showDeviceMonitoringDialog(); - DevicePolicyEventLogger - .createEvent(FrameworkStatsLog.DEVICE_POLICY_EVENT__EVENT_ID__DO_USER_INFO_CLICKED) - .write(); - } - - // TODO(b/242040009): Remove this. - public void showDeviceMonitoringDialog() { - mQSSecurityFooterUtils.showDeviceMonitoringDialog(mContext, Expandable.fromView(mView)); - } - - public void refreshState() { - mHandler.sendEmptyMessage(H.REFRESH_STATE); - } - - private void handleRefreshState() { - SecurityModel securityModel = SecurityModel.create(mSecurityController); - SecurityButtonConfig buttonConfig = mQSSecurityFooterUtils.getButtonConfig(securityModel); - - if (buttonConfig == null) { - mIsVisible = false; - } else { - mIsVisible = true; - mIsClickable = buttonConfig.isClickable(); - mFooterTextContent = buttonConfig.getText(); - mFooterIcon = buttonConfig.getIcon(); - } - - // Update the UI. - mMainHandler.post(mUpdatePrimaryIcon); - mMainHandler.post(mUpdateDisplayState); - } - - private final Runnable mUpdatePrimaryIcon = new Runnable() { - @Override - public void run() { - if (mFooterIcon instanceof Icon.Loaded) { - mPrimaryFooterIcon.setImageDrawable(((Icon.Loaded) mFooterIcon).getDrawable()); - } else if (mFooterIcon instanceof Icon.Resource) { - mPrimaryFooterIcon.setImageResource(((Icon.Resource) mFooterIcon).getRes()); - } - } - }; - - private final Runnable mUpdateDisplayState = new Runnable() { - @Override - public void run() { - if (mFooterTextContent != null) { - mFooterText.setText(mFooterTextContent); - } - mView.setVisibility(mIsVisible ? View.VISIBLE : View.GONE); - if (mVisibilityChangedListener != null) { - mVisibilityChangedListener.onVisibilityChanged(mView.getVisibility()); - } - - if (mIsVisible && mIsClickable) { - mView.setClickable(true); - mView.findViewById(R.id.footer_icon).setVisibility(View.VISIBLE); - } else { - mView.setClickable(false); - mView.findViewById(R.id.footer_icon).setVisibility(View.GONE); - } - } - }; - - private class Callback implements SecurityController.SecurityControllerCallback { - @Override - public void onStateChanged() { - refreshState(); - } - } - - private class H extends Handler { - private static final int CLICK = 0; - private static final int REFRESH_STATE = 1; - - private H(Looper looper) { - super(looper); - } - - @Override - public void handleMessage(Message msg) { - String name = null; - try { - if (msg.what == REFRESH_STATE) { - name = "handleRefreshState"; - handleRefreshState(); - } else if (msg.what == CLICK) { - name = "handleClick"; - handleClick(); - } - } catch (Throwable t) { - final String error = "Error in " + name; - Log.w(TAG, error, t); - } - } - } -} diff --git a/packages/SystemUI/src/com/android/systemui/qs/dagger/QSFragmentModule.java b/packages/SystemUI/src/com/android/systemui/qs/dagger/QSFragmentModule.java index aa505fb0b6bd..01eb636f75d6 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/dagger/QSFragmentModule.java +++ b/packages/SystemUI/src/com/android/systemui/qs/dagger/QSFragmentModule.java @@ -28,7 +28,6 @@ import com.android.systemui.battery.BatteryMeterView; import com.android.systemui.dagger.qualifiers.RootView; import com.android.systemui.plugins.qs.QS; import com.android.systemui.privacy.OngoingPrivacyChip; -import com.android.systemui.qs.FooterActionsView; import com.android.systemui.qs.QSContainerImpl; import com.android.systemui.qs.QSFooter; import com.android.systemui.qs.QSFooterView; @@ -51,8 +50,6 @@ import dagger.Provides; */ @Module public interface QSFragmentModule { - String QS_FGS_MANAGER_FOOTER_VIEW = "qs_fgs_manager_footer"; - String QS_SECURITY_FOOTER_VIEW = "qs_security_footer"; String QS_USING_MEDIA_PLAYER = "qs_using_media_player"; String QS_USING_COLLAPSED_LANDSCAPE_MEDIA = "qs_using_collapsed_landscape_media"; @@ -119,16 +116,6 @@ public interface QSFragmentModule { return view.findViewById(R.id.qs_footer); } - /** - * Provides a {@link FooterActionsView}. - * - * This will replace a ViewStub either in {@link QSFooterView} or in {@link QSContainerImpl}. - */ - @Provides - static FooterActionsView providesQSFooterActionsView(@RootView View view) { - return view.findViewById(R.id.qs_footer_actions); - } - /** */ @Provides @QSScope @@ -146,18 +133,6 @@ public interface QSFragmentModule { /** */ @Provides - @QSScope - @Named(QS_SECURITY_FOOTER_VIEW) - static View providesQSSecurityFooterView( - @QSThemedContext LayoutInflater layoutInflater, - FooterActionsView footerActionsView - ) { - return layoutInflater.inflate(R.layout.quick_settings_security_footer, footerActionsView, - false); - } - - /** */ - @Provides @Named(QS_USING_MEDIA_PLAYER) static boolean providesQSUsingMediaPlayer(Context context) { return useQsMediaPlayer(context); @@ -183,15 +158,4 @@ public interface QSFragmentModule { static StatusIconContainer providesStatusIconContainer(QuickStatusBarHeader qsHeader) { return qsHeader.findViewById(R.id.statusIcons); } - - /** */ - @Provides - @QSScope - @Named(QS_FGS_MANAGER_FOOTER_VIEW) - static View providesQSFgsManagerFooterView( - @QSThemedContext LayoutInflater layoutInflater, - FooterActionsView footerActionsView - ) { - return layoutInflater.inflate(R.layout.fgs_footer, footerActionsView, false); - } }
\ No newline at end of file diff --git a/packages/SystemUI/src/com/android/systemui/qs/footer/ui/binder/FooterActionsViewBinder.kt b/packages/SystemUI/src/com/android/systemui/qs/footer/ui/binder/FooterActionsViewBinder.kt index 3e39c8ee62f1..6db3c9941b78 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/footer/ui/binder/FooterActionsViewBinder.kt +++ b/packages/SystemUI/src/com/android/systemui/qs/footer/ui/binder/FooterActionsViewBinder.kt @@ -35,35 +35,31 @@ import com.android.systemui.animation.Expandable import com.android.systemui.common.ui.binder.IconViewBinder import com.android.systemui.lifecycle.repeatWhenAttached import com.android.systemui.people.ui.view.PeopleViewBinder.bind -import com.android.systemui.qs.FooterActionsView import com.android.systemui.qs.footer.ui.viewmodel.FooterActionsButtonViewModel import com.android.systemui.qs.footer.ui.viewmodel.FooterActionsForegroundServicesButtonViewModel import com.android.systemui.qs.footer.ui.viewmodel.FooterActionsSecurityButtonViewModel import com.android.systemui.qs.footer.ui.viewmodel.FooterActionsViewModel +import kotlin.math.roundToInt import kotlinx.coroutines.flow.collect import kotlinx.coroutines.launch /** A ViewBinder for [FooterActionsViewBinder]. */ object FooterActionsViewBinder { - /** - * Create a [FooterActionsView] that can later be [bound][bind] to a [FooterActionsViewModel]. - */ + /** Create a view that can later be [bound][bind] to a [FooterActionsViewModel]. */ @JvmStatic - fun create(context: Context): FooterActionsView { + fun create(context: Context): LinearLayout { return LayoutInflater.from(context).inflate(R.layout.footer_actions, /* root= */ null) - as FooterActionsView + as LinearLayout } /** Bind [view] to [viewModel]. */ @JvmStatic fun bind( - view: FooterActionsView, + view: LinearLayout, viewModel: FooterActionsViewModel, qsVisibilityLifecycleOwner: LifecycleOwner, ) { - // Remove all children of the FooterActionsView that are used by the old implementation. - // TODO(b/242040009): Clean up the XML once the old implementation is removed. - view.removeAllViews() + view.importantForAccessibility = View.IMPORTANT_FOR_ACCESSIBILITY_YES // Add the views used by this new implementation. val context = view.context @@ -117,7 +113,11 @@ object FooterActionsViewBinder { } launch { viewModel.alpha.collect { view.alpha = it } } - launch { viewModel.backgroundAlpha.collect { view.backgroundAlpha = it } } + launch { + viewModel.backgroundAlpha.collect { + view.background?.alpha = (it * 255).roundToInt() + } + } } // Listen for model changes only when QS are visible. diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotView.java b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotView.java index fae938d542f1..9c7718d0e001 100644 --- a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotView.java +++ b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotView.java @@ -881,6 +881,13 @@ public class ScreenshotView extends FrameLayout implements } void addQuickShareChip(Notification.Action quickShareAction) { + if (mQuickShareChip != null) { + mSmartChips.remove(mQuickShareChip); + mActionsView.removeView(mQuickShareChip); + } + if (mPendingInteraction == PendingInteraction.QUICK_SHARE) { + mPendingInteraction = null; + } if (mPendingInteraction == null) { LayoutInflater inflater = LayoutInflater.from(mContext); mQuickShareChip = (OverlayActionChip) inflater.inflate( diff --git a/packages/SystemUI/src/com/android/systemui/security/data/model/SecurityModel.kt b/packages/SystemUI/src/com/android/systemui/security/data/model/SecurityModel.kt index 50af260684f8..1cf5a8fe5f97 100644 --- a/packages/SystemUI/src/com/android/systemui/security/data/model/SecurityModel.kt +++ b/packages/SystemUI/src/com/android/systemui/security/data/model/SecurityModel.kt @@ -17,6 +17,7 @@ package com.android.systemui.security.data.model import android.graphics.drawable.Drawable +import androidx.annotation.VisibleForTesting import com.android.systemui.dagger.qualifiers.Background import com.android.systemui.statusbar.policy.SecurityController import kotlinx.coroutines.CoroutineDispatcher @@ -55,8 +56,8 @@ data class SecurityModel( * Important: This method should be called from a background thread as this will do a lot of * binder calls. */ - // TODO(b/242040009): Remove this. @JvmStatic + @VisibleForTesting fun create(securityController: SecurityController): SecurityModel { val deviceAdminInfo = if (securityController.isParentalControlsEnabled) { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/MultiUserSwitchController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/MultiUserSwitchController.java deleted file mode 100644 index 5e2a7c8ca540..000000000000 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/MultiUserSwitchController.java +++ /dev/null @@ -1,171 +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.systemui.statusbar.phone; - -import static com.android.systemui.DejankUtils.whitelistIpcs; - -import android.content.Intent; -import android.os.UserHandle; -import android.os.UserManager; -import android.view.View; -import android.view.ViewGroup; - -import com.android.systemui.R; -import com.android.systemui.animation.ActivityLaunchAnimator; -import com.android.systemui.animation.Expandable; -import com.android.systemui.flags.FeatureFlags; -import com.android.systemui.flags.Flags; -import com.android.systemui.plugins.ActivityStarter; -import com.android.systemui.plugins.FalsingManager; -import com.android.systemui.qs.FooterActionsView; -import com.android.systemui.qs.dagger.QSScope; -import com.android.systemui.qs.user.UserSwitchDialogController; -import com.android.systemui.statusbar.policy.BaseUserSwitcherAdapter; -import com.android.systemui.statusbar.policy.UserSwitcherController; -import com.android.systemui.user.UserSwitcherActivity; -import com.android.systemui.util.ViewController; - -import javax.inject.Inject; - -/** View Controller for {@link MultiUserSwitch}. */ -// TODO(b/242040009): Remove this file. -public class MultiUserSwitchController extends ViewController<MultiUserSwitch> { - private final UserManager mUserManager; - private final UserSwitcherController mUserSwitcherController; - private final FalsingManager mFalsingManager; - private final UserSwitchDialogController mUserSwitchDialogController; - private final ActivityStarter mActivityStarter; - private final FeatureFlags mFeatureFlags; - - private BaseUserSwitcherAdapter mUserListener; - - private final View.OnClickListener mOnClickListener = new View.OnClickListener() { - @Override - public void onClick(View v) { - if (mFalsingManager.isFalseTap(FalsingManager.LOW_PENALTY)) { - return; - } - - if (mFeatureFlags.isEnabled(Flags.FULL_SCREEN_USER_SWITCHER)) { - Intent intent = new Intent(v.getContext(), UserSwitcherActivity.class); - intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_NEW_TASK); - - mActivityStarter.startActivity(intent, true /* dismissShade */, - ActivityLaunchAnimator.Controller.fromView(v, null), - true /* showOverlockscreenwhenlocked */, UserHandle.SYSTEM); - } else { - mUserSwitchDialogController.showDialog(v.getContext(), Expandable.fromView(v)); - } - } - }; - - @QSScope - public static class Factory { - private final UserManager mUserManager; - private final UserSwitcherController mUserSwitcherController; - private final FalsingManager mFalsingManager; - private final UserSwitchDialogController mUserSwitchDialogController; - private final ActivityStarter mActivityStarter; - private final FeatureFlags mFeatureFlags; - - @Inject - public Factory(UserManager userManager, UserSwitcherController userSwitcherController, - FalsingManager falsingManager, - UserSwitchDialogController userSwitchDialogController, FeatureFlags featureFlags, - ActivityStarter activityStarter) { - mUserManager = userManager; - mUserSwitcherController = userSwitcherController; - mFalsingManager = falsingManager; - mUserSwitchDialogController = userSwitchDialogController; - mActivityStarter = activityStarter; - mFeatureFlags = featureFlags; - } - - public MultiUserSwitchController create(FooterActionsView view) { - return new MultiUserSwitchController(view.findViewById(R.id.multi_user_switch), - mUserManager, mUserSwitcherController, - mFalsingManager, mUserSwitchDialogController, mFeatureFlags, - mActivityStarter); - } - } - - private MultiUserSwitchController(MultiUserSwitch view, UserManager userManager, - UserSwitcherController userSwitcherController, - FalsingManager falsingManager, UserSwitchDialogController userSwitchDialogController, - FeatureFlags featureFlags, ActivityStarter activityStarter) { - super(view); - mUserManager = userManager; - mUserSwitcherController = userSwitcherController; - mFalsingManager = falsingManager; - mUserSwitchDialogController = userSwitchDialogController; - mFeatureFlags = featureFlags; - mActivityStarter = activityStarter; - } - - @Override - protected void onInit() { - registerListener(); - mView.refreshContentDescription(getCurrentUser()); - } - - @Override - protected void onViewAttached() { - mView.setOnClickListener(mOnClickListener); - } - - @Override - protected void onViewDetached() { - mView.setOnClickListener(null); - } - - private void registerListener() { - if (mUserManager.isUserSwitcherEnabled() && mUserListener == null) { - - final UserSwitcherController controller = mUserSwitcherController; - if (controller != null) { - mUserListener = new BaseUserSwitcherAdapter(controller) { - @Override - public void notifyDataSetChanged() { - mView.refreshContentDescription(getCurrentUser()); - } - - @Override - public View getView(int position, View convertView, ViewGroup parent) { - return null; - } - }; - mView.refreshContentDescription(getCurrentUser()); - } - } - } - - private String getCurrentUser() { - // TODO(b/138661450) - if (whitelistIpcs(() -> mUserManager.isUserSwitcherEnabled())) { - return mUserSwitcherController.getCurrentUserName(); - } - - return null; - } - - /** Returns true if view should be made visible. */ - public boolean isMultiUserEnabled() { - // TODO(b/138661450) Move IPC calls to background - return whitelistIpcs(() -> mUserManager.isUserSwitcherEnabled( - getResources().getBoolean(R.bool.qs_show_user_switcher_for_single_user))); - } -} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/DeviceControlsControllerImpl.kt b/packages/SystemUI/src/com/android/systemui/statusbar/policy/DeviceControlsControllerImpl.kt index 6c66f0bb1e47..341eb3b0425c 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/DeviceControlsControllerImpl.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/DeviceControlsControllerImpl.kt @@ -68,7 +68,6 @@ public class DeviceControlsControllerImpl @Inject constructor( internal const val PREFS_CONTROLS_SEEDING_COMPLETED = "SeedingCompleted" const val PREFS_CONTROLS_FILE = "controls_prefs" - internal const val PREFS_SETTINGS_DIALOG_ATTEMPTS = "show_settings_attempts" private const val SEEDING_MAX = 2 } diff --git a/packages/SystemUI/src/com/android/systemui/wallpapers/ImageWallpaper.java b/packages/SystemUI/src/com/android/systemui/wallpapers/ImageWallpaper.java index ad97ef4a79bc..ef43702e97b8 100644 --- a/packages/SystemUI/src/com/android/systemui/wallpapers/ImageWallpaper.java +++ b/packages/SystemUI/src/com/android/systemui/wallpapers/ImageWallpaper.java @@ -45,7 +45,6 @@ import androidx.annotation.NonNull; import com.android.internal.annotations.VisibleForTesting; import com.android.systemui.dagger.qualifiers.Background; -import com.android.systemui.dagger.qualifiers.Main; import com.android.systemui.flags.FeatureFlags; import com.android.systemui.util.concurrency.DelayableExecutor; import com.android.systemui.wallpapers.canvas.WallpaperLocalColorExtractor; @@ -57,7 +56,6 @@ import java.io.IOException; import java.io.PrintWriter; import java.util.ArrayList; import java.util.List; -import java.util.concurrent.Executor; import javax.inject.Inject; @@ -88,17 +86,12 @@ public class ImageWallpaper extends WallpaperService { private final DelayableExecutor mBackgroundExecutor; private static final int DELAY_UNLOAD_BITMAP = 2000; - @Main - private final Executor mMainExecutor; - @Inject public ImageWallpaper(FeatureFlags featureFlags, - @Background DelayableExecutor backgroundExecutor, - @Main Executor mainExecutor) { + @Background DelayableExecutor backgroundExecutor) { super(); mFeatureFlags = featureFlags; mBackgroundExecutor = backgroundExecutor; - mMainExecutor = mainExecutor; } @Override @@ -662,13 +655,9 @@ public class ImageWallpaper extends WallpaperService { loadWallpaperAndDrawFrameInternal(); } else { mBitmapUsages++; - - // drawing is done on the main thread - mMainExecutor.execute(() -> { - drawFrameOnCanvas(mBitmap); - reportEngineShown(false); - unloadBitmapIfNotUsed(); - }); + drawFrameOnCanvas(mBitmap); + reportEngineShown(false); + unloadBitmapIfNotUsedInternal(); } } @@ -706,11 +695,15 @@ public class ImageWallpaper extends WallpaperService { private void unloadBitmapIfNotUsedSynchronized() { synchronized (mLock) { - mBitmapUsages -= 1; - if (mBitmapUsages <= 0) { - mBitmapUsages = 0; - unloadBitmapInternal(); - } + unloadBitmapIfNotUsedInternal(); + } + } + + private void unloadBitmapIfNotUsedInternal() { + mBitmapUsages -= 1; + if (mBitmapUsages <= 0) { + mBitmapUsages = 0; + unloadBitmapInternal(); } } diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardAbsKeyInputViewControllerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardAbsKeyInputViewControllerTest.java index 8bbaf3dff1e5..4903d31f89f4 100644 --- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardAbsKeyInputViewControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardAbsKeyInputViewControllerTest.java @@ -19,6 +19,7 @@ package com.android.keyguard; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.never; import static org.mockito.Mockito.reset; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verifyZeroInteractions; @@ -87,6 +88,7 @@ public class KeyguardAbsKeyInputViewControllerTest extends SysuiTestCase { when(mAbsKeyInputView.isAttachedToWindow()).thenReturn(true); when(mAbsKeyInputView.requireViewById(R.id.bouncer_message_area)) .thenReturn(mKeyguardMessageArea); + when(mAbsKeyInputView.getResources()).thenReturn(getContext().getResources()); mKeyguardAbsKeyInputViewController = new KeyguardAbsKeyInputViewController(mAbsKeyInputView, mKeyguardUpdateMonitor, mSecurityMode, mLockPatternUtils, mKeyguardSecurityCallback, mKeyguardMessageAreaControllerFactory, mLatencyTracker, mFalsingCollector, @@ -125,4 +127,22 @@ public class KeyguardAbsKeyInputViewControllerTest extends SysuiTestCase { verifyZeroInteractions(mKeyguardSecurityCallback); verifyZeroInteractions(mKeyguardMessageAreaController); } + + @Test + public void onPromptReasonNone_doesNotSetMessage() { + mKeyguardAbsKeyInputViewController.showPromptReason(0); + verify(mKeyguardMessageAreaController, never()).setMessage( + getContext().getResources().getString(R.string.kg_prompt_reason_restart_password), + false); + } + + @Test + public void onPromptReason_setsMessage() { + when(mAbsKeyInputView.getPromptReasonStringRes(1)).thenReturn( + R.string.kg_prompt_reason_restart_password); + mKeyguardAbsKeyInputViewController.showPromptReason(1); + verify(mKeyguardMessageAreaController).setMessage( + getContext().getResources().getString(R.string.kg_prompt_reason_restart_password), + false); + } } diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPasswordViewControllerTest.kt b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPasswordViewControllerTest.kt index d20be56d6c6b..d91279399341 100644 --- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPasswordViewControllerTest.kt +++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPasswordViewControllerTest.kt @@ -30,64 +30,54 @@ import com.android.systemui.util.concurrency.DelayableExecutor import org.junit.Before import org.junit.Test import org.junit.runner.RunWith +import org.mockito.ArgumentMatchers.anyBoolean +import org.mockito.ArgumentMatchers.anyString import org.mockito.Mock import org.mockito.Mockito -import org.mockito.Mockito.`when` import org.mockito.Mockito.never import org.mockito.Mockito.verify +import org.mockito.Mockito.`when` import org.mockito.MockitoAnnotations @SmallTest @RunWith(AndroidTestingRunner::class) @TestableLooper.RunWithLooper class KeyguardPasswordViewControllerTest : SysuiTestCase() { - @Mock - private lateinit var keyguardPasswordView: KeyguardPasswordView - @Mock - private lateinit var passwordEntry: EditText - @Mock - lateinit var keyguardUpdateMonitor: KeyguardUpdateMonitor - @Mock - lateinit var securityMode: KeyguardSecurityModel.SecurityMode - @Mock - lateinit var lockPatternUtils: LockPatternUtils - @Mock - lateinit var keyguardSecurityCallback: KeyguardSecurityCallback - @Mock - lateinit var messageAreaControllerFactory: KeyguardMessageAreaController.Factory - @Mock - lateinit var latencyTracker: LatencyTracker - @Mock - lateinit var inputMethodManager: InputMethodManager - @Mock - lateinit var emergencyButtonController: EmergencyButtonController - @Mock - lateinit var mainExecutor: DelayableExecutor - @Mock - lateinit var falsingCollector: FalsingCollector - @Mock - lateinit var keyguardViewController: KeyguardViewController - @Mock - private lateinit var mKeyguardMessageArea: BouncerKeyguardMessageArea - @Mock - private lateinit var mKeyguardMessageAreaController: - KeyguardMessageAreaController<BouncerKeyguardMessageArea> + @Mock private lateinit var keyguardPasswordView: KeyguardPasswordView + @Mock private lateinit var passwordEntry: EditText + @Mock lateinit var keyguardUpdateMonitor: KeyguardUpdateMonitor + @Mock lateinit var securityMode: KeyguardSecurityModel.SecurityMode + @Mock lateinit var lockPatternUtils: LockPatternUtils + @Mock lateinit var keyguardSecurityCallback: KeyguardSecurityCallback + @Mock lateinit var messageAreaControllerFactory: KeyguardMessageAreaController.Factory + @Mock lateinit var latencyTracker: LatencyTracker + @Mock lateinit var inputMethodManager: InputMethodManager + @Mock lateinit var emergencyButtonController: EmergencyButtonController + @Mock lateinit var mainExecutor: DelayableExecutor + @Mock lateinit var falsingCollector: FalsingCollector + @Mock lateinit var keyguardViewController: KeyguardViewController + @Mock private lateinit var mKeyguardMessageArea: BouncerKeyguardMessageArea + @Mock + private lateinit var mKeyguardMessageAreaController: + KeyguardMessageAreaController<BouncerKeyguardMessageArea> - private lateinit var keyguardPasswordViewController: KeyguardPasswordViewController + private lateinit var keyguardPasswordViewController: KeyguardPasswordViewController - @Before - fun setup() { - MockitoAnnotations.initMocks(this) - Mockito.`when`( - keyguardPasswordView - .requireViewById<BouncerKeyguardMessageArea>(R.id.bouncer_message_area) - ).thenReturn(mKeyguardMessageArea) - Mockito.`when`(messageAreaControllerFactory.create(mKeyguardMessageArea)) - .thenReturn(mKeyguardMessageAreaController) - Mockito.`when`(keyguardPasswordView.passwordTextViewId).thenReturn(R.id.passwordEntry) - Mockito.`when`(keyguardPasswordView.findViewById<EditText>(R.id.passwordEntry) - ).thenReturn(passwordEntry) - keyguardPasswordViewController = KeyguardPasswordViewController( + @Before + fun setup() { + MockitoAnnotations.initMocks(this) + Mockito.`when`( + keyguardPasswordView.requireViewById<BouncerKeyguardMessageArea>( + R.id.bouncer_message_area)) + .thenReturn(mKeyguardMessageArea) + Mockito.`when`(messageAreaControllerFactory.create(mKeyguardMessageArea)) + .thenReturn(mKeyguardMessageAreaController) + Mockito.`when`(keyguardPasswordView.passwordTextViewId).thenReturn(R.id.passwordEntry) + Mockito.`when`(keyguardPasswordView.findViewById<EditText>(R.id.passwordEntry)) + .thenReturn(passwordEntry) + `when`(keyguardPasswordView.resources).thenReturn(context.resources) + keyguardPasswordViewController = + KeyguardPasswordViewController( keyguardPasswordView, keyguardUpdateMonitor, securityMode, @@ -100,51 +90,48 @@ class KeyguardPasswordViewControllerTest : SysuiTestCase() { mainExecutor, mContext.resources, falsingCollector, - keyguardViewController - ) - } + keyguardViewController) + } - @Test - fun testFocusWhenBouncerIsShown() { - Mockito.`when`(keyguardViewController.isBouncerShowing).thenReturn(true) - Mockito.`when`(keyguardPasswordView.isShown).thenReturn(true) - keyguardPasswordViewController.onResume(KeyguardSecurityView.VIEW_REVEALED) - keyguardPasswordView.post { - verify(keyguardPasswordView).requestFocus() - verify(keyguardPasswordView).showKeyboard() - } + @Test + fun testFocusWhenBouncerIsShown() { + Mockito.`when`(keyguardViewController.isBouncerShowing).thenReturn(true) + Mockito.`when`(keyguardPasswordView.isShown).thenReturn(true) + keyguardPasswordViewController.onResume(KeyguardSecurityView.VIEW_REVEALED) + keyguardPasswordView.post { + verify(keyguardPasswordView).requestFocus() + verify(keyguardPasswordView).showKeyboard() } + } - @Test - fun testDoNotFocusWhenBouncerIsHidden() { - Mockito.`when`(keyguardViewController.isBouncerShowing).thenReturn(false) - Mockito.`when`(keyguardPasswordView.isShown).thenReturn(true) - keyguardPasswordViewController.onResume(KeyguardSecurityView.VIEW_REVEALED) - verify(keyguardPasswordView, never()).requestFocus() - } + @Test + fun testDoNotFocusWhenBouncerIsHidden() { + Mockito.`when`(keyguardViewController.isBouncerShowing).thenReturn(false) + Mockito.`when`(keyguardPasswordView.isShown).thenReturn(true) + keyguardPasswordViewController.onResume(KeyguardSecurityView.VIEW_REVEALED) + verify(keyguardPasswordView, never()).requestFocus() + } - @Test - fun testHideKeyboardWhenOnPause() { - keyguardPasswordViewController.onPause() - keyguardPasswordView.post { - verify(keyguardPasswordView).clearFocus() - verify(keyguardPasswordView).hideKeyboard() - } + @Test + fun testHideKeyboardWhenOnPause() { + keyguardPasswordViewController.onPause() + keyguardPasswordView.post { + verify(keyguardPasswordView).clearFocus() + verify(keyguardPasswordView).hideKeyboard() } + } - @Test - fun startAppearAnimation() { - keyguardPasswordViewController.startAppearAnimation() - verify(mKeyguardMessageAreaController).setMessage(R.string.keyguard_enter_your_password) - } + @Test + fun startAppearAnimation() { + keyguardPasswordViewController.startAppearAnimation() + verify(mKeyguardMessageAreaController) + .setMessage(context.resources.getString(R.string.keyguard_enter_your_password), false) + } - @Test - fun startAppearAnimation_withExistingMessage() { - `when`(mKeyguardMessageAreaController.message).thenReturn("Unlock to continue.") - keyguardPasswordViewController.startAppearAnimation() - verify( - mKeyguardMessageAreaController, - never() - ).setMessage(R.string.keyguard_enter_your_password) - } + @Test + fun startAppearAnimation_withExistingMessage() { + `when`(mKeyguardMessageAreaController.message).thenReturn("Unlock to continue.") + keyguardPasswordViewController.startAppearAnimation() + verify(mKeyguardMessageAreaController, never()).setMessage(anyString(), anyBoolean()) + } } diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPatternViewControllerTest.kt b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPatternViewControllerTest.kt index b3d1c8f909d8..85dbdb8330a3 100644 --- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPatternViewControllerTest.kt +++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPatternViewControllerTest.kt @@ -30,97 +30,93 @@ import com.android.systemui.statusbar.policy.DevicePostureController import org.junit.Before import org.junit.Test import org.junit.runner.RunWith +import org.mockito.ArgumentMatchers.anyBoolean +import org.mockito.ArgumentMatchers.anyString import org.mockito.Mock +import org.mockito.Mockito.never import org.mockito.Mockito.verify import org.mockito.Mockito.`when` -import org.mockito.Mockito.never import org.mockito.MockitoAnnotations @SmallTest @RunWith(AndroidTestingRunner::class) @TestableLooper.RunWithLooper class KeyguardPatternViewControllerTest : SysuiTestCase() { - @Mock - private lateinit var mKeyguardPatternView: KeyguardPatternView + @Mock private lateinit var mKeyguardPatternView: KeyguardPatternView - @Mock - private lateinit var mKeyguardUpdateMonitor: KeyguardUpdateMonitor + @Mock private lateinit var mKeyguardUpdateMonitor: KeyguardUpdateMonitor - @Mock - private lateinit var mSecurityMode: KeyguardSecurityModel.SecurityMode + @Mock private lateinit var mSecurityMode: KeyguardSecurityModel.SecurityMode - @Mock - private lateinit var mLockPatternUtils: LockPatternUtils + @Mock private lateinit var mLockPatternUtils: LockPatternUtils - @Mock - private lateinit var mKeyguardSecurityCallback: KeyguardSecurityCallback + @Mock private lateinit var mKeyguardSecurityCallback: KeyguardSecurityCallback - @Mock - private lateinit var mLatencyTracker: LatencyTracker - private var mFalsingCollector: FalsingCollector = FalsingCollectorFake() + @Mock private lateinit var mLatencyTracker: LatencyTracker + private var mFalsingCollector: FalsingCollector = FalsingCollectorFake() - @Mock - private lateinit var mEmergencyButtonController: EmergencyButtonController + @Mock private lateinit var mEmergencyButtonController: EmergencyButtonController - @Mock - private lateinit - var mKeyguardMessageAreaControllerFactory: KeyguardMessageAreaController.Factory + @Mock + private lateinit var mKeyguardMessageAreaControllerFactory: KeyguardMessageAreaController.Factory - @Mock - private lateinit var mKeyguardMessageArea: BouncerKeyguardMessageArea + @Mock private lateinit var mKeyguardMessageArea: BouncerKeyguardMessageArea - @Mock - private lateinit var mKeyguardMessageAreaController: - KeyguardMessageAreaController<BouncerKeyguardMessageArea> + @Mock + private lateinit var mKeyguardMessageAreaController: + KeyguardMessageAreaController<BouncerKeyguardMessageArea> - @Mock - private lateinit var mLockPatternView: LockPatternView + @Mock private lateinit var mLockPatternView: LockPatternView - @Mock - private lateinit var mPostureController: DevicePostureController + @Mock private lateinit var mPostureController: DevicePostureController - private lateinit var mKeyguardPatternViewController: KeyguardPatternViewController + private lateinit var mKeyguardPatternViewController: KeyguardPatternViewController - @Before - fun setup() { - MockitoAnnotations.initMocks(this) - `when`(mKeyguardPatternView.isAttachedToWindow).thenReturn(true) - `when`(mKeyguardPatternView - .requireViewById<BouncerKeyguardMessageArea>(R.id.bouncer_message_area)) - .thenReturn(mKeyguardMessageArea) - `when`(mKeyguardPatternView.findViewById<LockPatternView>(R.id.lockPatternView)) - .thenReturn(mLockPatternView) - `when`(mKeyguardMessageAreaControllerFactory.create(mKeyguardMessageArea)) - .thenReturn(mKeyguardMessageAreaController) - mKeyguardPatternViewController = KeyguardPatternViewController( + @Before + fun setup() { + MockitoAnnotations.initMocks(this) + `when`(mKeyguardPatternView.isAttachedToWindow).thenReturn(true) + `when`( + mKeyguardPatternView.requireViewById<BouncerKeyguardMessageArea>( + R.id.bouncer_message_area)) + .thenReturn(mKeyguardMessageArea) + `when`(mKeyguardPatternView.findViewById<LockPatternView>(R.id.lockPatternView)) + .thenReturn(mLockPatternView) + `when`(mKeyguardMessageAreaControllerFactory.create(mKeyguardMessageArea)) + .thenReturn(mKeyguardMessageAreaController) + `when`(mKeyguardPatternView.resources).thenReturn(context.resources) + mKeyguardPatternViewController = + KeyguardPatternViewController( mKeyguardPatternView, - mKeyguardUpdateMonitor, mSecurityMode, mLockPatternUtils, mKeyguardSecurityCallback, - mLatencyTracker, mFalsingCollector, mEmergencyButtonController, - mKeyguardMessageAreaControllerFactory, mPostureController - ) - } - - @Test - fun onPause_resetsText() { - mKeyguardPatternViewController.init() - mKeyguardPatternViewController.onPause() - verify(mKeyguardMessageAreaController).setMessage(R.string.keyguard_enter_your_pattern) - } - - - @Test - fun startAppearAnimation() { - mKeyguardPatternViewController.startAppearAnimation() - verify(mKeyguardMessageAreaController).setMessage(R.string.keyguard_enter_your_pattern) - } - - @Test - fun startAppearAnimation_withExistingMessage() { - `when`(mKeyguardMessageAreaController.message).thenReturn("Unlock to continue.") - mKeyguardPatternViewController.startAppearAnimation() - verify( - mKeyguardMessageAreaController, - never() - ).setMessage(R.string.keyguard_enter_your_password) - } + mKeyguardUpdateMonitor, + mSecurityMode, + mLockPatternUtils, + mKeyguardSecurityCallback, + mLatencyTracker, + mFalsingCollector, + mEmergencyButtonController, + mKeyguardMessageAreaControllerFactory, + mPostureController) + } + + @Test + fun onPause_resetsText() { + mKeyguardPatternViewController.init() + mKeyguardPatternViewController.onPause() + verify(mKeyguardMessageAreaController).setMessage(R.string.keyguard_enter_your_pattern) + } + + @Test + fun startAppearAnimation() { + mKeyguardPatternViewController.startAppearAnimation() + verify(mKeyguardMessageAreaController) + .setMessage(context.resources.getString(R.string.keyguard_enter_your_pattern), false) + } + + @Test + fun startAppearAnimation_withExistingMessage() { + `when`(mKeyguardMessageAreaController.message).thenReturn("Unlock to continue.") + mKeyguardPatternViewController.startAppearAnimation() + verify(mKeyguardMessageAreaController, never()).setMessage(anyString(), anyBoolean()) + } } diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPinViewControllerTest.kt b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPinViewControllerTest.kt index 8bcfe6f2b6f5..cdb7bbb9f823 100644 --- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPinViewControllerTest.kt +++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPinViewControllerTest.kt @@ -31,10 +31,13 @@ import com.android.systemui.statusbar.policy.DevicePostureController import org.junit.Before import org.junit.Test import org.junit.runner.RunWith +import org.mockito.ArgumentMatchers.anyBoolean +import org.mockito.ArgumentMatchers.anyString import org.mockito.Mock import org.mockito.Mockito import org.mockito.Mockito.any import org.mockito.Mockito.verify +import org.mockito.Mockito.`when` import org.mockito.MockitoAnnotations @SmallTest @@ -79,6 +82,7 @@ class KeyguardPinViewControllerTest : SysuiTestCase() { keyguardMessageAreaControllerFactory.create(any(KeyguardMessageArea::class.java)) ) .thenReturn(keyguardMessageAreaController) + `when`(keyguardPinView.resources).thenReturn(context.resources) pinViewController = KeyguardPinViewController( keyguardPinView, @@ -98,14 +102,14 @@ class KeyguardPinViewControllerTest : SysuiTestCase() { @Test fun startAppearAnimation() { pinViewController.startAppearAnimation() - verify(keyguardMessageAreaController).setMessage(R.string.keyguard_enter_your_pin) + verify(keyguardMessageAreaController) + .setMessage(context.resources.getString(R.string.keyguard_enter_your_pin), false) } @Test fun startAppearAnimation_withExistingMessage() { Mockito.`when`(keyguardMessageAreaController.message).thenReturn("Unlock to continue.") pinViewController.startAppearAnimation() - verify(keyguardMessageAreaController, Mockito.never()) - .setMessage(R.string.keyguard_enter_your_password) + verify(keyguardMessageAreaController, Mockito.never()).setMessage(anyString(), anyBoolean()) } } diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java index 0e92a2904436..40542d25689d 100644 --- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java +++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java @@ -30,6 +30,7 @@ import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STR import static com.android.keyguard.FaceAuthApiRequestReason.NOTIFICATION_PANEL_CLICKED; import static com.android.keyguard.KeyguardUpdateMonitor.BIOMETRIC_STATE_CANCELLING_RESTARTING; import static com.android.keyguard.KeyguardUpdateMonitor.DEFAULT_CANCEL_SIGNAL_TIMEOUT; +import static com.android.keyguard.KeyguardUpdateMonitor.HAL_POWER_PRESS_TIMEOUT; import static com.android.keyguard.KeyguardUpdateMonitor.getCurrentUser; import static com.google.common.truth.Truth.assertThat; @@ -855,12 +856,21 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase { } @Test - public void testFingerprintPowerPressed_restartsFingerprintListeningStateImmediately() { + public void testFingerprintPowerPressed_restartsFingerprintListeningStateWithDelay() { mKeyguardUpdateMonitor.mFingerprintAuthenticationCallback .onAuthenticationError(FingerprintManager.BIOMETRIC_ERROR_POWER_PRESSED, ""); - verify(mFingerprintManager).authenticate(any(), any(), any(), any(), anyInt(), anyInt(), - anyInt()); + // THEN doesn't authenticate immediately + verify(mFingerprintManager, never()).authenticate(any(), + any(), any(), any(), anyInt(), anyInt(), anyInt()); + + // WHEN all messages (with delays) are processed + mTestableLooper.moveTimeForward(HAL_POWER_PRESS_TIMEOUT); + mTestableLooper.processAllMessages(); + + // THEN fingerprint manager attempts to authenticate again + verify(mFingerprintManager).authenticate(any(), + any(), any(), any(), anyInt(), anyInt(), anyInt()); } @Test diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerTest.java index b267a5c23a49..a94f3427eebe 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerTest.java @@ -110,6 +110,8 @@ import java.util.ArrayList; import java.util.List; import java.util.Optional; +import javax.inject.Provider; + @SmallTest @RunWith(AndroidTestingRunner.class) @RunWithLooper(setAsMainLooper = true) @@ -261,6 +263,7 @@ public class UdfpsControllerTest extends SysuiTestCase { initUdfpsController(true /* hasAlternateTouchProvider */); } + private void initUdfpsController(boolean hasAlternateTouchProvider) { initUdfpsController(mOpticalProps, hasAlternateTouchProvider); } @@ -270,8 +273,10 @@ public class UdfpsControllerTest extends SysuiTestCase { reset(mFingerprintManager); reset(mScreenLifecycle); - final Optional<AlternateUdfpsTouchProvider> alternateTouchProvider = - hasAlternateTouchProvider ? Optional.of(mAlternateTouchProvider) : Optional.empty(); + final Optional<Provider<AlternateUdfpsTouchProvider>> alternateTouchProvider = + hasAlternateTouchProvider ? Optional.of( + (Provider<AlternateUdfpsTouchProvider>) () -> mAlternateTouchProvider) + : Optional.empty(); mUdfpsController = new UdfpsController(mContext, new FakeExecution(), mLayoutInflater, mFingerprintManager, mWindowManager, mStatusBarStateController, mFgExecutor, @@ -1140,7 +1145,7 @@ public class UdfpsControllerTest extends SysuiTestCase { } @Test - public void onTouch_withNewTouchDetection_shouldCallOldFingerprintManagerPath() + public void onTouch_withNewTouchDetection_shouldCallNewFingerprintManagerPath() throws RemoteException { final NormalizedTouchData touchData = new NormalizedTouchData(0, 0f, 0f, 0f, 0f, 0f, 0L, 0L); diff --git a/packages/SystemUI/tests/src/com/android/systemui/controls/controller/ControlActionCoordinatorImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/controls/controller/ControlActionCoordinatorImplTest.kt index 1d00d6b05568..16fb50c15af6 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/controls/controller/ControlActionCoordinatorImplTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/controls/controller/ControlActionCoordinatorImplTest.kt @@ -16,21 +16,19 @@ package com.android.systemui.controls.ui -import android.content.Context -import android.content.SharedPreferences import android.test.suitebuilder.annotation.SmallTest import android.testing.AndroidTestingRunner import com.android.systemui.SysuiTestCase import com.android.systemui.broadcast.BroadcastSender import com.android.systemui.controls.ControlsMetricsLogger -import com.android.systemui.controls.FakeControlsSettingsRepository +import com.android.systemui.controls.settings.ControlsSettingsDialogManager +import com.android.systemui.controls.settings.FakeControlsSettingsRepository +import com.android.systemui.flags.FeatureFlags +import com.android.systemui.flags.Flags import com.android.systemui.plugins.ActivityStarter -import com.android.systemui.settings.UserContextProvider import com.android.systemui.statusbar.VibratorHelper -import com.android.systemui.statusbar.policy.DeviceControlsControllerImpl import com.android.systemui.statusbar.policy.KeyguardStateController import com.android.systemui.util.concurrency.DelayableExecutor -import com.android.systemui.util.settings.SecureSettings import com.android.wm.shell.TaskViewFactory import org.junit.Before import org.junit.Test @@ -40,8 +38,8 @@ import org.mockito.Mock import org.mockito.Mockito import org.mockito.Mockito.`when` import org.mockito.Mockito.anyBoolean +import org.mockito.Mockito.doNothing import org.mockito.Mockito.doReturn -import org.mockito.Mockito.mock import org.mockito.Mockito.never import org.mockito.Mockito.reset import org.mockito.Mockito.spy @@ -71,9 +69,9 @@ class ControlActionCoordinatorImplTest : SysuiTestCase() { @Mock private lateinit var metricsLogger: ControlsMetricsLogger @Mock - private lateinit var secureSettings: SecureSettings + private lateinit var featureFlags: FeatureFlags @Mock - private lateinit var userContextProvider: UserContextProvider + private lateinit var controlsSettingsDialogManager: ControlsSettingsDialogManager companion object { fun <T> any(): T = Mockito.any<T>() @@ -103,23 +101,16 @@ class ControlActionCoordinatorImplTest : SysuiTestCase() { taskViewFactory, metricsLogger, vibratorHelper, - secureSettings, - userContextProvider, - controlsSettingsRepository + controlsSettingsRepository, + controlsSettingsDialogManager, + featureFlags )) - - val userContext = mock(Context::class.java) - val pref = mock(SharedPreferences::class.java) - `when`(userContextProvider.userContext).thenReturn(userContext) - `when`(userContext.getSharedPreferences( - DeviceControlsControllerImpl.PREFS_CONTROLS_FILE, Context.MODE_PRIVATE)) - .thenReturn(pref) - // Just return 2 so we don't test any Dialog logic which requires a launched activity. - `when`(pref.getInt(DeviceControlsControllerImpl.PREFS_SETTINGS_DIALOG_ATTEMPTS, 0)) - .thenReturn(2) + coordinator.activityContext = mContext `when`(cvh.cws.ci.controlId).thenReturn(ID) `when`(cvh.cws.control?.isAuthRequired()).thenReturn(true) + `when`(featureFlags.isEnabled(Flags.USE_APP_PANELS)).thenReturn(false) + action = spy(coordinator.Action(ID, {}, false, true)) doReturn(action).`when`(coordinator).createAction(any(), any(), anyBoolean(), anyBoolean()) } @@ -160,15 +151,31 @@ class ControlActionCoordinatorImplTest : SysuiTestCase() { doReturn(action).`when`(coordinator).createAction(any(), any(), anyBoolean(), anyBoolean()) `when`(keyguardStateController.isShowing()).thenReturn(true) - `when`(keyguardStateController.isUnlocked()).thenReturn(false) coordinator.toggle(cvh, "", true) verify(coordinator).bouncerOrRun(action) + verify(controlsSettingsDialogManager).maybeShowDialog(any(), any()) verify(action).invoke() } @Test + fun testToggleWhenLockedDoesNotTriggerDialog_featureFlagEnabled() { + `when`(featureFlags.isEnabled(Flags.USE_APP_PANELS)).thenReturn(true) + action = spy(coordinator.Action(ID, {}, false, false)) + doReturn(action).`when`(coordinator).createAction(any(), any(), anyBoolean(), anyBoolean()) + + `when`(keyguardStateController.isShowing()).thenReturn(true) + `when`(keyguardStateController.isUnlocked()).thenReturn(false) + doNothing().`when`(controlsSettingsDialogManager).maybeShowDialog(any(), any()) + + coordinator.toggle(cvh, "", true) + + verify(coordinator).bouncerOrRun(action) + verify(controlsSettingsDialogManager, never()).maybeShowDialog(any(), any()) + } + + @Test fun testToggleDoesNotRunsWhenLockedAndAuthRequired() { action = spy(coordinator.Action(ID, {}, false, true)) doReturn(action).`when`(coordinator).createAction(any(), any(), anyBoolean(), anyBoolean()) diff --git a/packages/SystemUI/tests/src/com/android/systemui/controls/dagger/ControlsComponentTest.kt b/packages/SystemUI/tests/src/com/android/systemui/controls/dagger/ControlsComponentTest.kt index 48fc46b7e730..9144b13c7f3e 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/controls/dagger/ControlsComponentTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/controls/dagger/ControlsComponentTest.kt @@ -22,7 +22,7 @@ import com.android.internal.widget.LockPatternUtils import com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_NOT_REQUIRED import com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_BOOT import com.android.systemui.SysuiTestCase -import com.android.systemui.controls.FakeControlsSettingsRepository +import com.android.systemui.controls.settings.FakeControlsSettingsRepository import com.android.systemui.controls.controller.ControlsController import com.android.systemui.controls.controller.ControlsTileResourceConfiguration import com.android.systemui.controls.management.ControlsListingController diff --git a/packages/SystemUI/tests/src/com/android/systemui/controls/settings/ControlsSettingsDialogManagerImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/controls/settings/ControlsSettingsDialogManagerImplTest.kt new file mode 100644 index 000000000000..0c9986d82447 --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/controls/settings/ControlsSettingsDialogManagerImplTest.kt @@ -0,0 +1,328 @@ +/* + * Copyright (C) 2022 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.controls.settings + +import android.content.DialogInterface +import android.content.SharedPreferences +import android.database.ContentObserver +import android.provider.Settings.Secure.LOCKSCREEN_ALLOW_TRIVIAL_CONTROLS +import android.provider.Settings.Secure.LOCKSCREEN_SHOW_CONTROLS +import android.testing.AndroidTestingRunner +import android.testing.TestableLooper +import androidx.test.filters.SmallTest +import com.android.systemui.SysuiTestCase +import com.android.systemui.controls.settings.ControlsSettingsDialogManager.Companion.PREFS_SETTINGS_DIALOG_ATTEMPTS +import com.android.systemui.plugins.ActivityStarter +import com.android.systemui.settings.UserFileManager +import com.android.systemui.settings.UserTracker +import com.android.systemui.statusbar.policy.DeviceControlsControllerImpl +import com.android.systemui.util.FakeSharedPreferences +import com.android.systemui.util.TestableAlertDialog +import com.android.systemui.util.mockito.any +import com.android.systemui.util.mockito.eq +import com.android.systemui.util.mockito.nullable +import com.android.systemui.util.settings.FakeSettings +import com.google.common.truth.Truth.assertThat +import org.junit.After +import org.junit.Before +import org.junit.Test +import org.junit.runner.RunWith +import org.mockito.ArgumentMatchers.anyBoolean +import org.mockito.Mock +import org.mockito.Mockito.anyInt +import org.mockito.Mockito.never +import org.mockito.Mockito.verify +import org.mockito.Mockito.`when` +import org.mockito.MockitoAnnotations + +@SmallTest +@RunWith(AndroidTestingRunner::class) +@TestableLooper.RunWithLooper +class ControlsSettingsDialogManagerImplTest : SysuiTestCase() { + + companion object { + private const val SETTING_SHOW = LOCKSCREEN_SHOW_CONTROLS + private const val SETTING_ACTION = LOCKSCREEN_ALLOW_TRIVIAL_CONTROLS + private const val MAX_NUMBER_ATTEMPTS_CONTROLS_DIALOG = 2 + } + + @Mock private lateinit var userFileManager: UserFileManager + @Mock private lateinit var userTracker: UserTracker + @Mock private lateinit var activityStarter: ActivityStarter + @Mock private lateinit var completedRunnable: () -> Unit + + private lateinit var controlsSettingsRepository: FakeControlsSettingsRepository + private lateinit var sharedPreferences: FakeSharedPreferences + private lateinit var secureSettings: FakeSettings + + private lateinit var underTest: ControlsSettingsDialogManagerImpl + + private var dialog: TestableAlertDialog? = null + + @Before + fun setUp() { + MockitoAnnotations.initMocks(this) + + controlsSettingsRepository = FakeControlsSettingsRepository() + sharedPreferences = FakeSharedPreferences() + secureSettings = FakeSettings() + + `when`(userTracker.userId).thenReturn(0) + secureSettings.userId = userTracker.userId + `when`( + userFileManager.getSharedPreferences( + eq(DeviceControlsControllerImpl.PREFS_CONTROLS_FILE), + anyInt(), + anyInt() + ) + ) + .thenReturn(sharedPreferences) + + `when`(activityStarter.dismissKeyguardThenExecute(any(), nullable(), anyBoolean())) + .thenAnswer { (it.arguments[0] as ActivityStarter.OnDismissAction).onDismiss() } + + attachRepositoryToSettings() + underTest = + ControlsSettingsDialogManagerImpl( + secureSettings, + userFileManager, + controlsSettingsRepository, + userTracker, + activityStarter + ) { context, _ -> TestableAlertDialog(context).also { dialog = it } } + } + + @After + fun tearDown() { + underTest.closeDialog() + } + + @Test + fun dialogNotShownIfPrefsAtMaximum() { + sharedPreferences.putAttempts(MAX_NUMBER_ATTEMPTS_CONTROLS_DIALOG) + + underTest.maybeShowDialog(context, completedRunnable) + + assertThat(dialog?.isShowing ?: false).isFalse() + verify(completedRunnable).invoke() + } + + @Test + fun dialogNotShownIfSettingsAreTrue() { + sharedPreferences.putAttempts(0) + secureSettings.putBool(SETTING_SHOW, true) + secureSettings.putBool(SETTING_ACTION, true) + + underTest.maybeShowDialog(context, completedRunnable) + + assertThat(dialog?.isShowing ?: false).isFalse() + verify(completedRunnable).invoke() + } + + @Test + fun dialogShownIfAllowTrivialControlsFalse() { + sharedPreferences.putAttempts(0) + secureSettings.putBool(SETTING_SHOW, true) + secureSettings.putBool(SETTING_ACTION, false) + + underTest.maybeShowDialog(context, completedRunnable) + + assertThat(dialog?.isShowing ?: false).isTrue() + } + + @Test + fun dialogDispossedAfterClosing() { + sharedPreferences.putAttempts(0) + secureSettings.putBool(SETTING_SHOW, true) + secureSettings.putBool(SETTING_ACTION, false) + + underTest.maybeShowDialog(context, completedRunnable) + underTest.closeDialog() + + assertThat(dialog?.isShowing ?: false).isFalse() + } + + @Test + fun dialogNeutralButtonDoesntChangeSetting() { + sharedPreferences.putAttempts(0) + secureSettings.putBool(SETTING_SHOW, true) + secureSettings.putBool(SETTING_ACTION, false) + + underTest.maybeShowDialog(context, completedRunnable) + clickButton(DialogInterface.BUTTON_NEUTRAL) + + assertThat(secureSettings.getBool(SETTING_ACTION, false)).isFalse() + } + + @Test + fun dialogNeutralButtonPutsMaxAttempts() { + sharedPreferences.putAttempts(0) + secureSettings.putBool(SETTING_SHOW, true) + secureSettings.putBool(SETTING_ACTION, false) + + underTest.maybeShowDialog(context, completedRunnable) + clickButton(DialogInterface.BUTTON_NEUTRAL) + + assertThat(sharedPreferences.getInt(PREFS_SETTINGS_DIALOG_ATTEMPTS, 0)) + .isEqualTo(MAX_NUMBER_ATTEMPTS_CONTROLS_DIALOG) + } + + @Test + fun dialogNeutralButtonCallsOnComplete() { + sharedPreferences.putAttempts(0) + secureSettings.putBool(SETTING_SHOW, true) + secureSettings.putBool(SETTING_ACTION, false) + + underTest.maybeShowDialog(context, completedRunnable) + clickButton(DialogInterface.BUTTON_NEUTRAL) + + verify(completedRunnable).invoke() + } + + @Test + fun dialogPositiveButtonChangesSetting() { + sharedPreferences.putAttempts(0) + secureSettings.putBool(SETTING_SHOW, true) + secureSettings.putBool(SETTING_ACTION, false) + + underTest.maybeShowDialog(context, completedRunnable) + clickButton(DialogInterface.BUTTON_POSITIVE) + + assertThat(secureSettings.getBool(SETTING_ACTION, false)).isTrue() + } + + @Test + fun dialogPositiveButtonPutsMaxAttempts() { + sharedPreferences.putAttempts(0) + secureSettings.putBool(SETTING_SHOW, true) + secureSettings.putBool(SETTING_ACTION, false) + + underTest.maybeShowDialog(context, completedRunnable) + clickButton(DialogInterface.BUTTON_POSITIVE) + + assertThat(sharedPreferences.getInt(PREFS_SETTINGS_DIALOG_ATTEMPTS, 0)) + .isEqualTo(MAX_NUMBER_ATTEMPTS_CONTROLS_DIALOG) + } + + @Test + fun dialogPositiveButtonCallsOnComplete() { + sharedPreferences.putAttempts(0) + secureSettings.putBool(SETTING_SHOW, true) + secureSettings.putBool(SETTING_ACTION, false) + + underTest.maybeShowDialog(context, completedRunnable) + clickButton(DialogInterface.BUTTON_POSITIVE) + + verify(completedRunnable).invoke() + } + + @Test + fun dialogCancelDoesntChangeSetting() { + sharedPreferences.putAttempts(0) + secureSettings.putBool(SETTING_SHOW, true) + secureSettings.putBool(SETTING_ACTION, false) + + underTest.maybeShowDialog(context, completedRunnable) + dialog?.cancel() + + assertThat(secureSettings.getBool(SETTING_ACTION, false)).isFalse() + } + + @Test + fun dialogCancelPutsOneExtraAttempt() { + val attempts = 0 + sharedPreferences.putAttempts(attempts) + secureSettings.putBool(SETTING_SHOW, true) + secureSettings.putBool(SETTING_ACTION, false) + + underTest.maybeShowDialog(context, completedRunnable) + dialog?.cancel() + + assertThat(sharedPreferences.getInt(PREFS_SETTINGS_DIALOG_ATTEMPTS, 0)) + .isEqualTo(attempts + 1) + } + + @Test + fun dialogCancelCallsOnComplete() { + sharedPreferences.putAttempts(0) + secureSettings.putBool(SETTING_SHOW, true) + secureSettings.putBool(SETTING_ACTION, false) + + underTest.maybeShowDialog(context, completedRunnable) + dialog?.cancel() + + verify(completedRunnable).invoke() + } + + @Test + fun closeDialogDoesNotCallOnComplete() { + sharedPreferences.putAttempts(0) + secureSettings.putBool(SETTING_SHOW, true) + secureSettings.putBool(SETTING_ACTION, false) + + underTest.maybeShowDialog(context, completedRunnable) + underTest.closeDialog() + + verify(completedRunnable, never()).invoke() + } + + @Test + fun dialogPositiveWithBothSettingsFalseTogglesBothSettings() { + sharedPreferences.putAttempts(0) + secureSettings.putBool(SETTING_SHOW, false) + secureSettings.putBool(SETTING_ACTION, false) + + underTest.maybeShowDialog(context, completedRunnable) + clickButton(DialogInterface.BUTTON_POSITIVE) + + assertThat(secureSettings.getBool(SETTING_SHOW)).isTrue() + assertThat(secureSettings.getBool(SETTING_ACTION)).isTrue() + } + + private fun clickButton(which: Int) { + dialog?.clickButton(which) + } + + private fun attachRepositoryToSettings() { + secureSettings.registerContentObserver( + SETTING_SHOW, + object : ContentObserver(null) { + override fun onChange(selfChange: Boolean) { + controlsSettingsRepository.setCanShowControlsInLockscreen( + secureSettings.getBool(SETTING_SHOW, false) + ) + } + } + ) + + secureSettings.registerContentObserver( + SETTING_ACTION, + object : ContentObserver(null) { + override fun onChange(selfChange: Boolean) { + controlsSettingsRepository.setAllowActionOnTrivialControlsInLockscreen( + secureSettings.getBool(SETTING_ACTION, false) + ) + } + } + ) + } + + private fun SharedPreferences.putAttempts(value: Int) { + edit().putInt(PREFS_SETTINGS_DIALOG_ATTEMPTS, value).commit() + } +} diff --git a/packages/SystemUI/tests/src/com/android/systemui/controls/ControlsSettingsRepositoryImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/controls/settings/ControlsSettingsRepositoryImplTest.kt index 4b88b44c3f03..b904ac14e707 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/controls/ControlsSettingsRepositoryImplTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/controls/settings/ControlsSettingsRepositoryImplTest.kt @@ -15,7 +15,7 @@ * */ -package com.android.systemui.controls +package com.android.systemui.controls.settings import android.content.pm.UserInfo import android.provider.Settings diff --git a/packages/SystemUI/tests/src/com/android/systemui/controls/FakeControlsSettingsRepository.kt b/packages/SystemUI/tests/src/com/android/systemui/controls/settings/FakeControlsSettingsRepository.kt index 8a1bed20e700..b6628db14235 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/controls/FakeControlsSettingsRepository.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/controls/settings/FakeControlsSettingsRepository.kt @@ -15,7 +15,7 @@ * */ -package com.android.systemui.controls +package com.android.systemui.controls.settings import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.asStateFlow diff --git a/packages/SystemUI/tests/src/com/android/systemui/controls/ui/ControlsUiControllerImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/controls/ui/ControlsUiControllerImplTest.kt index d965e337f47a..779788aa0075 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/controls/ui/ControlsUiControllerImplTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/controls/ui/ControlsUiControllerImplTest.kt @@ -35,10 +35,10 @@ import com.android.systemui.SysuiTestCase import com.android.systemui.controls.ControlsMetricsLogger import com.android.systemui.controls.ControlsServiceInfo import com.android.systemui.controls.CustomIconCache -import com.android.systemui.controls.FakeControlsSettingsRepository import com.android.systemui.controls.controller.ControlsController import com.android.systemui.controls.controller.StructureInfo import com.android.systemui.controls.management.ControlsListingController +import com.android.systemui.controls.settings.FakeControlsSettingsRepository import com.android.systemui.dump.DumpManager import com.android.systemui.plugins.ActivityStarter import com.android.systemui.settings.UserFileManager diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTest.java b/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTest.java index d17e3744edc6..798839dcc1f6 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTest.java @@ -16,6 +16,7 @@ package com.android.systemui.keyguard; +import static android.view.WindowManager.TRANSIT_OLD_KEYGUARD_GOING_AWAY; import static android.view.WindowManagerPolicyConstants.OFF_BECAUSE_OF_USER; import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_DPM_LOCK_NOW; @@ -34,6 +35,7 @@ import static org.mockito.Mockito.never; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; +import android.app.IActivityManager; import android.app.admin.DevicePolicyManager; import android.app.trust.TrustManager; import android.os.PowerManager; @@ -41,6 +43,11 @@ import android.os.PowerManager.WakeLock; import android.telephony.TelephonyManager; import android.testing.AndroidTestingRunner; import android.testing.TestableLooper; +import android.view.IRemoteAnimationFinishedCallback; +import android.view.RemoteAnimationTarget; +import android.view.View; +import android.view.ViewRootImpl; +import android.view.WindowManager; import androidx.test.filters.SmallTest; @@ -52,21 +59,27 @@ import com.android.keyguard.KeyguardUpdateMonitor; import com.android.keyguard.mediator.ScreenOnCoordinator; import com.android.systemui.SysuiTestCase; import com.android.systemui.animation.ActivityLaunchAnimator; +import com.android.systemui.biometrics.AuthController; import com.android.systemui.broadcast.BroadcastDispatcher; import com.android.systemui.classifier.FalsingCollectorFake; +import com.android.systemui.colorextraction.SysuiColorExtractor; import com.android.systemui.dreams.DreamOverlayStateController; import com.android.systemui.dump.DumpManager; import com.android.systemui.navigationbar.NavigationModeController; import com.android.systemui.settings.UserTracker; +import com.android.systemui.shade.NotificationShadeWindowControllerImpl; import com.android.systemui.shade.ShadeController; +import com.android.systemui.shade.ShadeExpansionStateManager; import com.android.systemui.statusbar.NotificationShadeDepthController; import com.android.systemui.statusbar.NotificationShadeWindowController; import com.android.systemui.statusbar.SysuiStatusBarStateController; import com.android.systemui.statusbar.phone.CentralSurfaces; import com.android.systemui.statusbar.phone.DozeParameters; +import com.android.systemui.statusbar.phone.KeyguardBypassController; import com.android.systemui.statusbar.phone.ScreenOffAnimationController; import com.android.systemui.statusbar.phone.ScrimController; import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager; +import com.android.systemui.statusbar.policy.ConfigurationController; import com.android.systemui.statusbar.policy.KeyguardStateController; import com.android.systemui.statusbar.policy.UserSwitcherController; import com.android.systemui.util.DeviceConfigProxy; @@ -80,8 +93,6 @@ import org.junit.runner.RunWith; import org.mockito.Mock; import org.mockito.MockitoAnnotations; -import dagger.Lazy; - @RunWith(AndroidTestingRunner.class) @TestableLooper.RunWithLooper @SmallTest @@ -96,11 +107,15 @@ public class KeyguardViewMediatorTest extends SysuiTestCase { private @Mock BroadcastDispatcher mBroadcastDispatcher; private @Mock DismissCallbackRegistry mDismissCallbackRegistry; private @Mock DumpManager mDumpManager; + private @Mock WindowManager mWindowManager; + private @Mock IActivityManager mActivityManager; + private @Mock ConfigurationController mConfigurationController; private @Mock PowerManager mPowerManager; private @Mock TrustManager mTrustManager; private @Mock UserSwitcherController mUserSwitcherController; private @Mock NavigationModeController mNavigationModeController; private @Mock KeyguardDisplayManager mKeyguardDisplayManager; + private @Mock KeyguardBypassController mKeyguardBypassController; private @Mock DozeParameters mDozeParameters; private @Mock SysuiStatusBarStateController mStatusBarStateController; private @Mock KeyguardStateController mKeyguardStateController; @@ -110,10 +125,13 @@ public class KeyguardViewMediatorTest extends SysuiTestCase { private @Mock InteractionJankMonitor mInteractionJankMonitor; private @Mock ScreenOnCoordinator mScreenOnCoordinator; private @Mock ShadeController mShadeController; - private @Mock Lazy<NotificationShadeWindowController> mNotificationShadeWindowControllerLazy; + private NotificationShadeWindowController mNotificationShadeWindowController; private @Mock DreamOverlayStateController mDreamOverlayStateController; private @Mock ActivityLaunchAnimator mActivityLaunchAnimator; private @Mock ScrimController mScrimController; + private @Mock SysuiColorExtractor mColorExtractor; + private @Mock AuthController mAuthController; + private @Mock ShadeExpansionStateManager mShadeExpansionStateManager; private DeviceConfigProxy mDeviceConfig = new DeviceConfigProxyFake(); private FakeExecutor mUiBgExecutor = new FakeExecutor(new FakeSystemClock()); @@ -130,6 +148,14 @@ public class KeyguardViewMediatorTest extends SysuiTestCase { when(mPowerManager.newWakeLock(anyInt(), any())).thenReturn(mock(WakeLock.class)); when(mInteractionJankMonitor.begin(any(), anyInt())).thenReturn(true); when(mInteractionJankMonitor.end(anyInt())).thenReturn(true); + final ViewRootImpl testViewRoot = mock(ViewRootImpl.class); + when(testViewRoot.getView()).thenReturn(mock(View.class)); + when(mStatusBarKeyguardViewManager.getViewRootImpl()).thenReturn(testViewRoot); + mNotificationShadeWindowController = new NotificationShadeWindowControllerImpl(mContext, + mWindowManager, mActivityManager, mDozeParameters, mStatusBarStateController, + mConfigurationController, mViewMediator, mKeyguardBypassController, + mColorExtractor, mDumpManager, mKeyguardStateController, + mScreenOffAnimationController, mAuthController, mShadeExpansionStateManager); createAndStartViewMediator(); } @@ -287,6 +313,23 @@ public class KeyguardViewMediatorTest extends SysuiTestCase { verify(mCentralSurfaces).updateIsKeyguard(); } + @Test + @TestableLooper.RunWithLooper(setAsMainLooper = true) + public void testStartKeyguardExitAnimation_expectSurfaceBehindRemoteAnimation() { + RemoteAnimationTarget[] apps = new RemoteAnimationTarget[]{ + mock(RemoteAnimationTarget.class) + }; + RemoteAnimationTarget[] wallpapers = new RemoteAnimationTarget[]{ + mock(RemoteAnimationTarget.class) + }; + IRemoteAnimationFinishedCallback callback = mock(IRemoteAnimationFinishedCallback.class); + + mViewMediator.startKeyguardExitAnimation(TRANSIT_OLD_KEYGUARD_GOING_AWAY, apps, wallpapers, + null, callback); + TestableLooper.get(this).processAllMessages(); + assertTrue(mViewMediator.isAnimatingBetweenKeyguardAndSurfaceBehind()); + } + private void createAndStartViewMediator() { mViewMediator = new KeyguardViewMediator( mContext, @@ -315,7 +358,7 @@ public class KeyguardViewMediatorTest extends SysuiTestCase { mInteractionJankMonitor, mDreamOverlayStateController, () -> mShadeController, - mNotificationShadeWindowControllerLazy, + () -> mNotificationShadeWindowController, () -> mActivityLaunchAnimator, () -> mScrimController); mViewMediator.start(); diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/FooterActionsControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/FooterActionsControllerTest.kt deleted file mode 100644 index 2ba8782c6d02..000000000000 --- a/packages/SystemUI/tests/src/com/android/systemui/qs/FooterActionsControllerTest.kt +++ /dev/null @@ -1,440 +0,0 @@ -package com.android.systemui.qs - -import android.content.Intent -import android.os.Handler -import android.os.UserManager -import android.provider.Settings -import android.testing.AndroidTestingRunner -import android.testing.TestableLooper -import android.testing.ViewUtils -import android.view.LayoutInflater -import android.view.View -import android.view.ViewGroup -import androidx.test.filters.SmallTest -import com.android.internal.logging.MetricsLogger -import com.android.internal.logging.UiEventLogger -import com.android.internal.logging.testing.FakeMetricsLogger -import com.android.systemui.R -import com.android.systemui.animation.ActivityLaunchAnimator -import com.android.systemui.classifier.FalsingManagerFake -import com.android.systemui.globalactions.GlobalActionsDialogLite -import com.android.systemui.plugins.ActivityStarter -import com.android.systemui.settings.UserTracker -import com.android.systemui.statusbar.phone.MultiUserSwitchController -import com.android.systemui.statusbar.policy.DeviceProvisionedController -import com.android.systemui.statusbar.policy.FakeConfigurationController -import com.android.systemui.statusbar.policy.UserInfoController -import com.android.systemui.util.mockito.capture -import com.android.systemui.util.settings.FakeSettings -import com.android.systemui.utils.leaks.LeakCheckedTest -import com.google.common.truth.Expect -import com.google.common.truth.Truth.assertThat -import javax.inject.Provider -import org.junit.After -import org.junit.Before -import org.junit.Rule -import org.junit.Test -import org.junit.runner.RunWith -import org.mockito.ArgumentCaptor -import org.mockito.ArgumentMatchers.any -import org.mockito.ArgumentMatchers.anyBoolean -import org.mockito.Captor -import org.mockito.Mock -import org.mockito.Mockito -import org.mockito.Mockito.atLeastOnce -import org.mockito.Mockito.clearInvocations -import org.mockito.Mockito.never -import org.mockito.Mockito.reset -import org.mockito.Mockito.verify -import org.mockito.Mockito.`when` as whenever -import org.mockito.MockitoAnnotations - -@SmallTest -@TestableLooper.RunWithLooper(setAsMainLooper = true) -@RunWith(AndroidTestingRunner::class) -class FooterActionsControllerTest : LeakCheckedTest() { - - @get:Rule var expect: Expect = Expect.create() - - @Mock private lateinit var userManager: UserManager - @Mock private lateinit var userTracker: UserTracker - @Mock private lateinit var activityStarter: ActivityStarter - @Mock private lateinit var deviceProvisionedController: DeviceProvisionedController - @Mock private lateinit var userInfoController: UserInfoController - @Mock private lateinit var multiUserSwitchControllerFactory: MultiUserSwitchController.Factory - @Mock private lateinit var multiUserSwitchController: MultiUserSwitchController - @Mock private lateinit var globalActionsDialogProvider: Provider<GlobalActionsDialogLite> - @Mock private lateinit var globalActionsDialog: GlobalActionsDialogLite - @Mock private lateinit var uiEventLogger: UiEventLogger - @Mock private lateinit var securityFooterController: QSSecurityFooter - @Mock private lateinit var fgsManagerController: QSFgsManagerFooter - @Captor - private lateinit var visibilityChangedCaptor: - ArgumentCaptor<VisibilityChangedDispatcher.OnVisibilityChangedListener> - - private lateinit var controller: FooterActionsController - - private val configurationController = FakeConfigurationController() - private val metricsLogger: MetricsLogger = FakeMetricsLogger() - private val falsingManager: FalsingManagerFake = FalsingManagerFake() - private lateinit var view: FooterActionsView - private lateinit var testableLooper: TestableLooper - private lateinit var fakeSettings: FakeSettings - private lateinit var securityFooter: View - private lateinit var fgsFooter: View - - @Before - fun setUp() { - // We want to make sure testable resources are always used - context.ensureTestableResources() - - MockitoAnnotations.initMocks(this) - testableLooper = TestableLooper.get(this) - fakeSettings = FakeSettings() - - whenever(multiUserSwitchControllerFactory.create(any())) - .thenReturn(multiUserSwitchController) - whenever(globalActionsDialogProvider.get()).thenReturn(globalActionsDialog) - - securityFooter = View(mContext) - fgsFooter = View(mContext) - - whenever(securityFooterController.view).thenReturn(securityFooter) - whenever(fgsManagerController.view).thenReturn(fgsFooter) - - view = inflateView() - - controller = constructFooterActionsController(view) - controller.init() - ViewUtils.attachView(view) - // View looper is the testable looper associated with the test - testableLooper.processAllMessages() - } - - @After - fun tearDown() { - if (view.isAttachedToWindow) { - ViewUtils.detachView(view) - } - } - - @Test - fun testInitializesControllers() { - verify(multiUserSwitchController).init() - verify(fgsManagerController).init() - verify(securityFooterController).init() - } - - @Test - fun testLogPowerMenuClick() { - controller.visible = true - falsingManager.setFalseTap(false) - - view.findViewById<View>(R.id.pm_lite).performClick() - // Verify clicks are logged - verify(uiEventLogger, Mockito.times(1)) - .log(GlobalActionsDialogLite.GlobalActionsEvent.GA_OPEN_QS) - } - - @Test - fun testSettings() { - val captor = ArgumentCaptor.forClass(Intent::class.java) - whenever(deviceProvisionedController.isCurrentUserSetup).thenReturn(true) - view.findViewById<View>(R.id.settings_button_container).performClick() - - verify(activityStarter) - .startActivity(capture(captor), anyBoolean(), any<ActivityLaunchAnimator.Controller>()) - - assertThat(captor.value.action).isEqualTo(Settings.ACTION_SETTINGS) - } - - @Test - fun testSettings_UserNotSetup() { - whenever(deviceProvisionedController.isCurrentUserSetup).thenReturn(false) - view.findViewById<View>(R.id.settings_button_container).performClick() - // Verify Settings wasn't launched. - verify(activityStarter, never()) - .startActivity(any(), anyBoolean(), any<ActivityLaunchAnimator.Controller>()) - } - - @Test - fun testMultiUserSwitchUpdatedWhenExpansionStarts() { - // When expansion starts, listening is set to true - val multiUserSwitch = view.requireViewById<View>(R.id.multi_user_switch) - - assertThat(multiUserSwitch.visibility).isNotEqualTo(View.VISIBLE) - - whenever(multiUserSwitchController.isMultiUserEnabled).thenReturn(true) - - controller.setListening(true) - testableLooper.processAllMessages() - - assertThat(multiUserSwitch.visibility).isEqualTo(View.VISIBLE) - } - - @Test - fun testMultiUserSwitchUpdatedWhenSettingChanged() { - // Always listening to setting while View is attached - testableLooper.processAllMessages() - - val multiUserSwitch = view.requireViewById<View>(R.id.multi_user_switch) - assertThat(multiUserSwitch.visibility).isNotEqualTo(View.VISIBLE) - - // The setting is only used as an indicator for whether the view should refresh. The actual - // value of the setting is ignored; isMultiUserEnabled is the source of truth - whenever(multiUserSwitchController.isMultiUserEnabled).thenReturn(true) - - // Changing the value of USER_SWITCHER_ENABLED should cause the view to update - fakeSettings.putIntForUser(Settings.Global.USER_SWITCHER_ENABLED, 1, userTracker.userId) - testableLooper.processAllMessages() - - assertThat(multiUserSwitch.visibility).isEqualTo(View.VISIBLE) - } - - @Test - fun testMultiUserSettingNotListenedAfterDetach() { - testableLooper.processAllMessages() - - val multiUserSwitch = view.requireViewById<View>(R.id.multi_user_switch) - assertThat(multiUserSwitch.visibility).isNotEqualTo(View.VISIBLE) - - ViewUtils.detachView(view) - - // The setting is only used as an indicator for whether the view should refresh. The actual - // value of the setting is ignored; isMultiUserEnabled is the source of truth - whenever(multiUserSwitchController.isMultiUserEnabled).thenReturn(true) - - // Changing the value of USER_SWITCHER_ENABLED should cause the view to update - fakeSettings.putIntForUser(Settings.Global.USER_SWITCHER_ENABLED, 1, userTracker.userId) - testableLooper.processAllMessages() - - assertThat(multiUserSwitch.visibility).isNotEqualTo(View.VISIBLE) - } - - @Test - fun testCleanUpGAD() { - reset(globalActionsDialogProvider) - // We are creating a new controller, so detach the views from it - (securityFooter.parent as ViewGroup).removeView(securityFooter) - (fgsFooter.parent as ViewGroup).removeView(fgsFooter) - - whenever(globalActionsDialogProvider.get()).thenReturn(globalActionsDialog) - val view = inflateView() - controller = constructFooterActionsController(view) - controller.init() - verify(globalActionsDialogProvider, never()).get() - - // GAD is constructed during attachment - ViewUtils.attachView(view) - testableLooper.processAllMessages() - verify(globalActionsDialogProvider).get() - - ViewUtils.detachView(view) - testableLooper.processAllMessages() - verify(globalActionsDialog).destroy() - } - - @Test - fun testSeparatorVisibility_noneVisible_gone() { - verify(securityFooterController) - .setOnVisibilityChangedListener(capture(visibilityChangedCaptor)) - val listener = visibilityChangedCaptor.value - val separator = controller.securityFootersSeparator - - setVisibilities(securityFooterVisible = false, fgsFooterVisible = false, listener) - assertThat(separator.visibility).isEqualTo(View.GONE) - } - - @Test - fun testSeparatorVisibility_onlySecurityFooterVisible_gone() { - verify(securityFooterController) - .setOnVisibilityChangedListener(capture(visibilityChangedCaptor)) - val listener = visibilityChangedCaptor.value - val separator = controller.securityFootersSeparator - - setVisibilities(securityFooterVisible = true, fgsFooterVisible = false, listener) - assertThat(separator.visibility).isEqualTo(View.GONE) - } - - @Test - fun testSeparatorVisibility_onlyFgsFooterVisible_gone() { - verify(securityFooterController) - .setOnVisibilityChangedListener(capture(visibilityChangedCaptor)) - val listener = visibilityChangedCaptor.value - val separator = controller.securityFootersSeparator - - setVisibilities(securityFooterVisible = false, fgsFooterVisible = true, listener) - assertThat(separator.visibility).isEqualTo(View.GONE) - } - - @Test - fun testSeparatorVisibility_bothVisible_visible() { - verify(securityFooterController) - .setOnVisibilityChangedListener(capture(visibilityChangedCaptor)) - val listener = visibilityChangedCaptor.value - val separator = controller.securityFootersSeparator - - setVisibilities(securityFooterVisible = true, fgsFooterVisible = true, listener) - assertThat(separator.visibility).isEqualTo(View.VISIBLE) - } - - @Test - fun testFgsFooterCollapsed() { - verify(securityFooterController) - .setOnVisibilityChangedListener(capture(visibilityChangedCaptor)) - val listener = visibilityChangedCaptor.value - - val booleanCaptor = ArgumentCaptor.forClass(Boolean::class.java) - - clearInvocations(fgsManagerController) - setVisibilities(securityFooterVisible = false, fgsFooterVisible = true, listener) - verify(fgsManagerController, atLeastOnce()).setCollapsed(capture(booleanCaptor)) - assertThat(booleanCaptor.allValues.last()).isFalse() - - clearInvocations(fgsManagerController) - setVisibilities(securityFooterVisible = true, fgsFooterVisible = true, listener) - verify(fgsManagerController, atLeastOnce()).setCollapsed(capture(booleanCaptor)) - assertThat(booleanCaptor.allValues.last()).isTrue() - } - - @Test - fun setExpansion_inSplitShade_alphaFollowsExpansion() { - enableSplitShade() - - controller.setExpansion(0f) - expect.that(view.alpha).isEqualTo(0f) - - controller.setExpansion(0.25f) - expect.that(view.alpha).isEqualTo(0.25f) - - controller.setExpansion(0.5f) - expect.that(view.alpha).isEqualTo(0.5f) - - controller.setExpansion(0.75f) - expect.that(view.alpha).isEqualTo(0.75f) - - controller.setExpansion(1f) - expect.that(view.alpha).isEqualTo(1f) - } - - @Test - fun setExpansion_inSplitShade_backgroundAlphaFollowsExpansion_with_0_9_delay() { - enableSplitShade() - - controller.setExpansion(0f) - expect.that(view.backgroundAlphaFraction).isEqualTo(0f) - - controller.setExpansion(0.5f) - expect.that(view.backgroundAlphaFraction).isEqualTo(0f) - - controller.setExpansion(0.9f) - expect.that(view.backgroundAlphaFraction).isEqualTo(0f) - - controller.setExpansion(0.91f) - expect.that(view.backgroundAlphaFraction).isWithin(FLOAT_TOLERANCE).of(0.1f) - - controller.setExpansion(0.95f) - expect.that(view.backgroundAlphaFraction).isWithin(FLOAT_TOLERANCE).of(0.5f) - - controller.setExpansion(1f) - expect.that(view.backgroundAlphaFraction).isEqualTo(1f) - } - - @Test - fun setExpansion_inSingleShade_alphaFollowsExpansion_with_0_9_delay() { - disableSplitShade() - - controller.setExpansion(0f) - expect.that(view.alpha).isEqualTo(0f) - - controller.setExpansion(0.5f) - expect.that(view.alpha).isEqualTo(0f) - - controller.setExpansion(0.9f) - expect.that(view.alpha).isEqualTo(0f) - - controller.setExpansion(0.91f) - expect.that(view.alpha).isWithin(FLOAT_TOLERANCE).of(0.1f) - - controller.setExpansion(0.95f) - expect.that(view.alpha).isWithin(FLOAT_TOLERANCE).of(0.5f) - - controller.setExpansion(1f) - expect.that(view.alpha).isEqualTo(1f) - } - - @Test - fun setExpansion_inSingleShade_backgroundAlphaAlways1() { - disableSplitShade() - - controller.setExpansion(0f) - expect.that(view.backgroundAlphaFraction).isEqualTo(1f) - - controller.setExpansion(0.5f) - expect.that(view.backgroundAlphaFraction).isEqualTo(1f) - - controller.setExpansion(1f) - expect.that(view.backgroundAlphaFraction).isEqualTo(1f) - } - - private fun setVisibilities( - securityFooterVisible: Boolean, - fgsFooterVisible: Boolean, - listener: VisibilityChangedDispatcher.OnVisibilityChangedListener - ) { - securityFooter.visibility = if (securityFooterVisible) View.VISIBLE else View.GONE - listener.onVisibilityChanged(securityFooter.visibility) - fgsFooter.visibility = if (fgsFooterVisible) View.VISIBLE else View.GONE - listener.onVisibilityChanged(fgsFooter.visibility) - } - - private fun inflateView(): FooterActionsView { - return LayoutInflater.from(context).inflate(R.layout.footer_actions, null) - as FooterActionsView - } - - private fun constructFooterActionsController(view: FooterActionsView): FooterActionsController { - return FooterActionsController( - view, - multiUserSwitchControllerFactory, - activityStarter, - userManager, - userTracker, - userInfoController, - deviceProvisionedController, - securityFooterController, - fgsManagerController, - falsingManager, - metricsLogger, - globalActionsDialogProvider, - uiEventLogger, - showPMLiteButton = true, - fakeSettings, - Handler(testableLooper.looper), - configurationController) - } - - private fun enableSplitShade() { - setSplitShadeEnabled(true) - } - - private fun disableSplitShade() { - setSplitShadeEnabled(false) - } - - private fun setSplitShadeEnabled(enabled: Boolean) { - overrideResource(R.bool.config_use_split_notification_shade, enabled) - configurationController.notifyConfigurationChanged() - } -} - -private const val FLOAT_TOLERANCE = 0.01f - -private val View.backgroundAlphaFraction: Float? - get() { - return if (background != null) { - background.alpha / 255f - } else { - null - } - } diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/QSFragmentTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/QSFragmentTest.java index aedb9354e6db..ffe918d36d6f 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/qs/QSFragmentTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/qs/QSFragmentTest.java @@ -25,6 +25,7 @@ import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyBoolean; import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.ArgumentMatchers.nullable; import static org.mockito.Mockito.clearInvocations; import static org.mockito.Mockito.doAnswer; import static org.mockito.Mockito.mock; @@ -32,6 +33,7 @@ import static org.mockito.Mockito.spy; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; +import android.annotation.Nullable; import android.app.Fragment; import android.content.Context; import android.graphics.Rect; @@ -42,6 +44,7 @@ import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; +import androidx.lifecycle.Lifecycle; import androidx.test.filters.SmallTest; import com.android.keyguard.BouncerPanelExpansionCalculator; @@ -50,12 +53,12 @@ import com.android.systemui.SysuiBaseFragmentTest; import com.android.systemui.animation.ShadeInterpolation; import com.android.systemui.dump.DumpManager; import com.android.systemui.flags.FakeFeatureFlags; -import com.android.systemui.flags.Flags; import com.android.systemui.media.controls.ui.MediaHost; import com.android.systemui.plugins.FalsingManager; import com.android.systemui.qs.customize.QSCustomizerController; import com.android.systemui.qs.dagger.QSFragmentComponent; import com.android.systemui.qs.external.TileServiceRequestController; +import com.android.systemui.qs.footer.ui.binder.FooterActionsViewBinder; import com.android.systemui.qs.footer.ui.viewmodel.FooterActionsViewModel; import com.android.systemui.statusbar.CommandQueue; import com.android.systemui.statusbar.StatusBarState; @@ -99,6 +102,8 @@ public class QSFragmentTest extends SysuiBaseFragmentTest { @Mock private QSAnimator mQSAnimator; @Mock private SysuiStatusBarStateController mStatusBarStateController; @Mock private QSSquishinessController mSquishinessController; + @Mock private FooterActionsViewModel mFooterActionsViewModel; + @Mock private FooterActionsViewModel.Factory mFooterActionsViewModelFactory; private View mQsFragmentView; public QSFragmentTest() { @@ -245,7 +250,8 @@ public class QSFragmentTest extends SysuiBaseFragmentTest { fragment.setQsExpansion(expansion, panelExpansionFraction, proposedTranslation, squishinessFraction); - verify(mQSFooterActionController).setExpansion(panelExpansionFraction); + verify(mFooterActionsViewModel).onQuickSettingsExpansionChanged( + panelExpansionFraction, /* isInSplitShade= */ true); } @Test @@ -262,7 +268,8 @@ public class QSFragmentTest extends SysuiBaseFragmentTest { fragment.setQsExpansion(expansion, panelExpansionFraction, proposedTranslation, squishinessFraction); - verify(mQSFooterActionController).setExpansion(expansion); + verify(mFooterActionsViewModel).onQuickSettingsExpansionChanged( + expansion, /* isInSplitShade= */ false); } @Test @@ -379,6 +386,13 @@ public class QSFragmentTest extends SysuiBaseFragmentTest { assertThat(mQsFragmentView.getTranslationY()).isEqualTo(0); } + private Lifecycle.State getListeningAndVisibilityLifecycleState() { + return getFragment() + .getListeningAndVisibilityLifecycleOwner() + .getLifecycle() + .getCurrentState(); + } + @Test public void setListeningFalse_notVisible() { QSFragment fragment = resumeAndGetFragment(); @@ -387,7 +401,7 @@ public class QSFragmentTest extends SysuiBaseFragmentTest { fragment.setListening(false); verify(mQSContainerImplController).setListening(false); - verify(mQSFooterActionController).setListening(false); + assertThat(getListeningAndVisibilityLifecycleState()).isEqualTo(Lifecycle.State.CREATED); verify(mQSPanelController).setListening(eq(false), anyBoolean()); } @@ -399,7 +413,7 @@ public class QSFragmentTest extends SysuiBaseFragmentTest { fragment.setListening(true); verify(mQSContainerImplController).setListening(false); - verify(mQSFooterActionController).setListening(false); + assertThat(getListeningAndVisibilityLifecycleState()).isEqualTo(Lifecycle.State.STARTED); verify(mQSPanelController).setListening(eq(false), anyBoolean()); } @@ -411,7 +425,7 @@ public class QSFragmentTest extends SysuiBaseFragmentTest { fragment.setListening(false); verify(mQSContainerImplController).setListening(false); - verify(mQSFooterActionController).setListening(false); + assertThat(getListeningAndVisibilityLifecycleState()).isEqualTo(Lifecycle.State.CREATED); verify(mQSPanelController).setListening(eq(false), anyBoolean()); } @@ -423,7 +437,7 @@ public class QSFragmentTest extends SysuiBaseFragmentTest { fragment.setListening(true); verify(mQSContainerImplController).setListening(true); - verify(mQSFooterActionController).setListening(true); + assertThat(getListeningAndVisibilityLifecycleState()).isEqualTo(Lifecycle.State.RESUMED); verify(mQSPanelController).setListening(eq(true), anyBoolean()); } @@ -480,7 +494,6 @@ public class QSFragmentTest extends SysuiBaseFragmentTest { setUpOther(); FakeFeatureFlags featureFlags = new FakeFeatureFlags(); - featureFlags.set(Flags.NEW_FOOTER_ACTIONS, false); return new QSFragment( new RemoteInputQuickSettingsDisabler( context, commandQueue, mock(ConfigurationController.class)), @@ -495,8 +508,8 @@ public class QSFragmentTest extends SysuiBaseFragmentTest { mFalsingManager, mock(DumpManager.class), featureFlags, - mock(NewFooterActionsController.class), - mock(FooterActionsViewModel.Factory.class)); + mock(FooterActionsController.class), + mFooterActionsViewModelFactory); } private void setUpOther() { @@ -505,6 +518,7 @@ public class QSFragmentTest extends SysuiBaseFragmentTest { when(mQSContainerImplController.getView()).thenReturn(mContainer); when(mQSPanelController.getTileLayout()).thenReturn(mQQsTileLayout); when(mQuickQSPanelController.getTileLayout()).thenReturn(mQsTileLayout); + when(mFooterActionsViewModelFactory.create(any())).thenReturn(mFooterActionsViewModel); } private void setUpMedia() { @@ -519,15 +533,40 @@ public class QSFragmentTest extends SysuiBaseFragmentTest { .thenReturn(mQSPanelScrollView); when(mQsFragmentView.findViewById(R.id.header)).thenReturn(mHeader); when(mQsFragmentView.findViewById(android.R.id.edit)).thenReturn(new View(mContext)); + when(mQsFragmentView.findViewById(R.id.qs_footer_actions)).thenAnswer( + invocation -> FooterActionsViewBinder.create(mContext)); } private void setUpInflater() { + LayoutInflater realInflater = LayoutInflater.from(mContext); + when(mLayoutInflater.cloneInContext(any(Context.class))).thenReturn(mLayoutInflater); - when(mLayoutInflater.inflate(anyInt(), any(ViewGroup.class), anyBoolean())) - .thenReturn(mQsFragmentView); + when(mLayoutInflater.inflate(anyInt(), nullable(ViewGroup.class), anyBoolean())) + .thenAnswer((invocation) -> inflate(realInflater, (int) invocation.getArgument(0), + (ViewGroup) invocation.getArgument(1), + (boolean) invocation.getArgument(2))); + when(mLayoutInflater.inflate(anyInt(), nullable(ViewGroup.class))) + .thenAnswer((invocation) -> inflate(realInflater, (int) invocation.getArgument(0), + (ViewGroup) invocation.getArgument(1))); mContext.addMockSystemService(Context.LAYOUT_INFLATER_SERVICE, mLayoutInflater); } + private View inflate(LayoutInflater realInflater, int layoutRes, @Nullable ViewGroup root) { + return inflate(realInflater, layoutRes, root, root != null); + } + + private View inflate(LayoutInflater realInflater, int layoutRes, @Nullable ViewGroup root, + boolean attachToRoot) { + if (layoutRes == R.layout.footer_actions + || layoutRes == R.layout.footer_actions_text_button + || layoutRes == R.layout.footer_actions_number_button + || layoutRes == R.layout.footer_actions_icon_button) { + return realInflater.inflate(layoutRes, root, attachToRoot); + } + + return mQsFragmentView; + } + private void setupQsComponent() { when(mQsComponentFactory.create(any(QSFragment.class))).thenReturn(mQsFragmentComponent); when(mQsFragmentComponent.getQSPanelController()).thenReturn(mQSPanelController); diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/QSSecurityFooterTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/QSSecurityFooterTest.java index 906c20b1d032..c656d6dd1a35 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/qs/QSSecurityFooterTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/qs/QSSecurityFooterTest.java @@ -17,23 +17,24 @@ package com.android.systemui.qs; import static android.app.admin.DevicePolicyManager.DEVICE_OWNER_TYPE_DEFAULT; import static android.app.admin.DevicePolicyManager.DEVICE_OWNER_TYPE_FINANCED; +import static com.google.common.truth.Truth.assertThat; + import static junit.framework.Assert.assertEquals; import static junit.framework.Assert.assertNotNull; +import static junit.framework.Assert.assertNull; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; import static org.mockito.Matchers.any; import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.never; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; +import android.annotation.IdRes; import android.app.AlertDialog; import android.app.admin.DevicePolicyManager; -import android.content.BroadcastReceiver; import android.content.ComponentName; import android.content.DialogInterface; -import android.content.Intent; import android.content.pm.UserInfo; import android.graphics.drawable.Drawable; import android.graphics.drawable.VectorDrawable; @@ -42,27 +43,27 @@ import android.os.Looper; import android.provider.Settings; import android.test.suitebuilder.annotation.SmallTest; import android.testing.AndroidTestingRunner; -import android.testing.LayoutInflaterBuilder; -import android.testing.TestableImageView; import android.testing.TestableLooper; import android.testing.TestableLooper.RunWithLooper; -import android.testing.ViewUtils; import android.text.SpannableStringBuilder; import android.view.LayoutInflater; import android.view.View; -import android.view.ViewGroup; -import android.widget.FrameLayout; import android.widget.TextView; +import androidx.annotation.Nullable; + import com.android.systemui.R; import com.android.systemui.SysuiTestCase; import com.android.systemui.animation.DialogLaunchAnimator; +import com.android.systemui.animation.Expandable; import com.android.systemui.broadcast.BroadcastDispatcher; +import com.android.systemui.common.shared.model.Icon; import com.android.systemui.plugins.ActivityStarter; +import com.android.systemui.qs.footer.domain.model.SecurityButtonConfig; +import com.android.systemui.security.data.model.SecurityModel; import com.android.systemui.settings.UserTracker; import com.android.systemui.statusbar.policy.SecurityController; -import org.junit.After; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; @@ -71,8 +72,6 @@ import org.mockito.Mock; import org.mockito.Mockito; import org.mockito.MockitoAnnotations; -import java.util.concurrent.atomic.AtomicInteger; - /* * Compile and run the whole SystemUI test suite: runtest --path frameworks/base/packages/SystemUI/tests @@ -96,11 +95,6 @@ public class QSSecurityFooterTest extends SysuiTestCase { new ComponentName("TestDPC", "Test"); private static final int DEFAULT_ICON_ID = R.drawable.ic_info_outline; - private ViewGroup mRootView; - private ViewGroup mSecurityFooterView; - private TextView mFooterText; - private TestableImageView mPrimaryFooterIcon; - private QSSecurityFooter mFooter; private QSSecurityFooterUtils mFooterUtils; @Mock private SecurityController mSecurityController; @@ -122,58 +116,53 @@ public class QSSecurityFooterTest extends SysuiTestCase { Looper looper = mTestableLooper.getLooper(); Handler mainHandler = new Handler(looper); when(mUserTracker.getUserInfo()).thenReturn(mock(UserInfo.class)); - mSecurityFooterView = (ViewGroup) new LayoutInflaterBuilder(mContext) - .replace("ImageView", TestableImageView.class) - .build().inflate(R.layout.quick_settings_security_footer, null, false); mFooterUtils = new QSSecurityFooterUtils(getContext(), getContext().getSystemService(DevicePolicyManager.class), mUserTracker, mainHandler, mActivityStarter, mSecurityController, looper, mDialogLaunchAnimator); - mFooter = new QSSecurityFooter(mSecurityFooterView, mainHandler, mSecurityController, - looper, mBroadcastDispatcher, mFooterUtils); - mFooterText = mSecurityFooterView.findViewById(R.id.footer_text); - mPrimaryFooterIcon = mSecurityFooterView.findViewById(R.id.primary_footer_icon); when(mSecurityController.getDeviceOwnerComponentOnAnyUser()) .thenReturn(DEVICE_OWNER_COMPONENT); when(mSecurityController.getDeviceOwnerType(DEVICE_OWNER_COMPONENT)) .thenReturn(DEVICE_OWNER_TYPE_DEFAULT); + } - // mSecurityFooterView must have a ViewGroup parent so that - // DialogLaunchAnimator.Controller.fromView() does not return null. - mRootView = new FrameLayout(mContext); - mRootView.addView(mSecurityFooterView); - ViewUtils.attachView(mRootView); + @Nullable + private SecurityButtonConfig getButtonConfig() { + SecurityModel securityModel = SecurityModel.create(mSecurityController); + return mFooterUtils.getButtonConfig(securityModel); + } + + private void assertIsDefaultIcon(Icon icon) { + assertIsIconResource(icon, DEFAULT_ICON_ID); + } - mFooter.init(); + private void assertIsIconResource(Icon icon, @IdRes int res) { + assertThat(icon).isInstanceOf(Icon.Resource.class); + assertEquals(res, ((Icon.Resource) icon).getRes()); } - @After - public void tearDown() { - ViewUtils.detachView(mRootView); + private void assertIsIconDrawable(Icon icon, Drawable drawable) { + assertThat(icon).isInstanceOf(Icon.Loaded.class); + assertEquals(drawable, ((Icon.Loaded) icon).getDrawable()); } @Test public void testUnmanaged() { when(mSecurityController.isDeviceManaged()).thenReturn(false); when(mSecurityController.isProfileOwnerOfOrganizationOwnedDevice()).thenReturn(false); - mFooter.refreshState(); - - TestableLooper.get(this).processAllMessages(); - assertEquals(View.GONE, mSecurityFooterView.getVisibility()); + assertNull(getButtonConfig()); } @Test public void testManagedNoOwnerName() { when(mSecurityController.isDeviceManaged()).thenReturn(true); when(mSecurityController.getDeviceOwnerOrganizationName()).thenReturn(null); - mFooter.refreshState(); - TestableLooper.get(this).processAllMessages(); + SecurityButtonConfig buttonConfig = getButtonConfig(); + assertNotNull(buttonConfig); assertEquals(mContext.getString(R.string.quick_settings_disclosure_management), - mFooterText.getText()); - assertEquals(View.VISIBLE, mSecurityFooterView.getVisibility()); - assertEquals(View.VISIBLE, mPrimaryFooterIcon.getVisibility()); - assertEquals(DEFAULT_ICON_ID, mPrimaryFooterIcon.getLastImageResource()); + buttonConfig.getText()); + assertIsDefaultIcon(buttonConfig.getIcon()); } @Test @@ -181,15 +170,13 @@ public class QSSecurityFooterTest extends SysuiTestCase { when(mSecurityController.isDeviceManaged()).thenReturn(true); when(mSecurityController.getDeviceOwnerOrganizationName()) .thenReturn(MANAGING_ORGANIZATION); - mFooter.refreshState(); - TestableLooper.get(this).processAllMessages(); + SecurityButtonConfig buttonConfig = getButtonConfig(); + assertNotNull(buttonConfig); assertEquals(mContext.getString(R.string.quick_settings_disclosure_named_management, - MANAGING_ORGANIZATION), - mFooterText.getText()); - assertEquals(View.VISIBLE, mSecurityFooterView.getVisibility()); - assertEquals(View.VISIBLE, mPrimaryFooterIcon.getVisibility()); - assertEquals(DEFAULT_ICON_ID, mPrimaryFooterIcon.getLastImageResource()); + MANAGING_ORGANIZATION), + buttonConfig.getText()); + assertIsDefaultIcon(buttonConfig.getIcon()); } @Test @@ -200,15 +187,13 @@ public class QSSecurityFooterTest extends SysuiTestCase { when(mSecurityController.getDeviceOwnerType(DEVICE_OWNER_COMPONENT)) .thenReturn(DEVICE_OWNER_TYPE_FINANCED); - mFooter.refreshState(); - - TestableLooper.get(this).processAllMessages(); + SecurityButtonConfig buttonConfig = getButtonConfig(); + assertNotNull(buttonConfig); assertEquals(mContext.getString( - R.string.quick_settings_financed_disclosure_named_management, - MANAGING_ORGANIZATION), mFooterText.getText()); - assertEquals(View.VISIBLE, mSecurityFooterView.getVisibility()); - assertEquals(View.VISIBLE, mPrimaryFooterIcon.getVisibility()); - assertEquals(DEFAULT_ICON_ID, mPrimaryFooterIcon.getLastImageResource()); + R.string.quick_settings_financed_disclosure_named_management, + MANAGING_ORGANIZATION), + buttonConfig.getText()); + assertIsDefaultIcon(buttonConfig.getIcon()); } @Test @@ -220,21 +205,16 @@ public class QSSecurityFooterTest extends SysuiTestCase { when(mUserTracker.getUserInfo()).thenReturn(mockUserInfo); Settings.Global.putInt(mContext.getContentResolver(), Settings.Global.DEVICE_DEMO_MODE, 1); - mFooter.refreshState(); - - TestableLooper.get(this).processAllMessages(); - assertEquals(View.GONE, mSecurityFooterView.getVisibility()); + assertNull(getButtonConfig()); } @Test public void testUntappableView_profileOwnerOfOrgOwnedDevice() { when(mSecurityController.isProfileOwnerOfOrganizationOwnedDevice()).thenReturn(true); - mFooter.refreshState(); - - TestableLooper.get(this).processAllMessages(); - assertFalse(mSecurityFooterView.isClickable()); - assertEquals(View.GONE, mSecurityFooterView.findViewById(R.id.footer_icon).getVisibility()); + SecurityButtonConfig buttonConfig = getButtonConfig(); + assertNotNull(buttonConfig); + assertFalse(buttonConfig.isClickable()); } @Test @@ -244,12 +224,9 @@ public class QSSecurityFooterTest extends SysuiTestCase { when(mSecurityController.isWorkProfileOn()).thenReturn(true); when(mSecurityController.hasWorkProfile()).thenReturn(true); - mFooter.refreshState(); - - TestableLooper.get(this).processAllMessages(); - assertTrue(mSecurityFooterView.isClickable()); - assertEquals(View.VISIBLE, - mSecurityFooterView.findViewById(R.id.footer_icon).getVisibility()); + SecurityButtonConfig buttonConfig = getButtonConfig(); + assertNotNull(buttonConfig); + assertTrue(buttonConfig.isClickable()); } @Test @@ -258,35 +235,31 @@ public class QSSecurityFooterTest extends SysuiTestCase { when(mSecurityController.isNetworkLoggingEnabled()).thenReturn(true); when(mSecurityController.isWorkProfileOn()).thenReturn(false); - mFooter.refreshState(); - - TestableLooper.get(this).processAllMessages(); - assertFalse(mSecurityFooterView.isClickable()); - assertEquals(View.GONE, mSecurityFooterView.findViewById(R.id.footer_icon).getVisibility()); + SecurityButtonConfig buttonConfig = getButtonConfig(); + assertNotNull(buttonConfig); + assertFalse(buttonConfig.isClickable()); } @Test public void testNetworkLoggingEnabled_deviceOwner() { when(mSecurityController.isDeviceManaged()).thenReturn(true); when(mSecurityController.isNetworkLoggingEnabled()).thenReturn(true); - mFooter.refreshState(); - TestableLooper.get(this).processAllMessages(); + SecurityButtonConfig buttonConfig = getButtonConfig(); + assertNotNull(buttonConfig); assertEquals(mContext.getString(R.string.quick_settings_disclosure_management_monitoring), - mFooterText.getText()); - assertEquals(View.VISIBLE, mPrimaryFooterIcon.getVisibility()); - assertEquals(DEFAULT_ICON_ID, mPrimaryFooterIcon.getLastImageResource()); + buttonConfig.getText()); + assertIsDefaultIcon(buttonConfig.getIcon()); // Same situation, but with organization name set when(mSecurityController.getDeviceOwnerOrganizationName()) .thenReturn(MANAGING_ORGANIZATION); - mFooter.refreshState(); - - TestableLooper.get(this).processAllMessages(); + buttonConfig = getButtonConfig(); + assertNotNull(buttonConfig); assertEquals(mContext.getString( - R.string.quick_settings_disclosure_named_management_monitoring, - MANAGING_ORGANIZATION), - mFooterText.getText()); + R.string.quick_settings_disclosure_named_management_monitoring, + MANAGING_ORGANIZATION), + buttonConfig.getText()); } @Test @@ -294,12 +267,12 @@ public class QSSecurityFooterTest extends SysuiTestCase { when(mSecurityController.hasWorkProfile()).thenReturn(true); when(mSecurityController.isNetworkLoggingEnabled()).thenReturn(true); when(mSecurityController.isWorkProfileOn()).thenReturn(true); - mFooter.refreshState(); - TestableLooper.get(this).processAllMessages(); + SecurityButtonConfig buttonConfig = getButtonConfig(); + assertNotNull(buttonConfig); assertEquals(mContext.getString( - R.string.quick_settings_disclosure_managed_profile_network_activity), - mFooterText.getText()); + R.string.quick_settings_disclosure_managed_profile_network_activity), + buttonConfig.getText()); } @Test @@ -307,21 +280,19 @@ public class QSSecurityFooterTest extends SysuiTestCase { when(mSecurityController.hasWorkProfile()).thenReturn(true); when(mSecurityController.isNetworkLoggingEnabled()).thenReturn(true); when(mSecurityController.isWorkProfileOn()).thenReturn(false); - mFooter.refreshState(); - TestableLooper.get(this).processAllMessages(); - assertEquals("", mFooterText.getText()); + assertNull(getButtonConfig()); } @Test public void testManagedCACertsInstalled() { when(mSecurityController.isDeviceManaged()).thenReturn(true); when(mSecurityController.hasCACertInCurrentUser()).thenReturn(true); - mFooter.refreshState(); - TestableLooper.get(this).processAllMessages(); + SecurityButtonConfig buttonConfig = getButtonConfig(); + assertNotNull(buttonConfig); assertEquals(mContext.getString(R.string.quick_settings_disclosure_management_monitoring), - mFooterText.getText()); + buttonConfig.getText()); } @Test @@ -329,25 +300,23 @@ public class QSSecurityFooterTest extends SysuiTestCase { when(mSecurityController.isDeviceManaged()).thenReturn(true); when(mSecurityController.isVpnEnabled()).thenReturn(true); when(mSecurityController.getPrimaryVpnName()).thenReturn(VPN_PACKAGE); - mFooter.refreshState(); - TestableLooper.get(this).processAllMessages(); + SecurityButtonConfig buttonConfig = getButtonConfig(); + assertNotNull(buttonConfig); assertEquals(mContext.getString(R.string.quick_settings_disclosure_management_named_vpn, - VPN_PACKAGE), - mFooterText.getText()); - assertEquals(View.VISIBLE, mPrimaryFooterIcon.getVisibility()); - assertEquals(R.drawable.stat_sys_vpn_ic, mPrimaryFooterIcon.getLastImageResource()); + VPN_PACKAGE), + buttonConfig.getText()); + assertIsIconResource(buttonConfig.getIcon(), R.drawable.stat_sys_vpn_ic); // Same situation, but with organization name set when(mSecurityController.getDeviceOwnerOrganizationName()) .thenReturn(MANAGING_ORGANIZATION); - mFooter.refreshState(); - - TestableLooper.get(this).processAllMessages(); + buttonConfig = getButtonConfig(); + assertNotNull(buttonConfig); assertEquals(mContext.getString( - R.string.quick_settings_disclosure_named_management_named_vpn, - MANAGING_ORGANIZATION, VPN_PACKAGE), - mFooterText.getText()); + R.string.quick_settings_disclosure_named_management_named_vpn, + MANAGING_ORGANIZATION, VPN_PACKAGE), + buttonConfig.getText()); } @Test @@ -356,23 +325,21 @@ public class QSSecurityFooterTest extends SysuiTestCase { when(mSecurityController.isVpnEnabled()).thenReturn(true); when(mSecurityController.getPrimaryVpnName()).thenReturn(VPN_PACKAGE); when(mSecurityController.getWorkProfileVpnName()).thenReturn(VPN_PACKAGE_2); - mFooter.refreshState(); - TestableLooper.get(this).processAllMessages(); + SecurityButtonConfig buttonConfig = getButtonConfig(); + assertNotNull(buttonConfig); assertEquals(mContext.getString(R.string.quick_settings_disclosure_management_vpns), - mFooterText.getText()); - assertEquals(View.VISIBLE, mPrimaryFooterIcon.getVisibility()); - assertEquals(R.drawable.stat_sys_vpn_ic, mPrimaryFooterIcon.getLastImageResource()); + buttonConfig.getText()); + assertIsIconResource(buttonConfig.getIcon(), R.drawable.stat_sys_vpn_ic); // Same situation, but with organization name set when(mSecurityController.getDeviceOwnerOrganizationName()) .thenReturn(MANAGING_ORGANIZATION); - mFooter.refreshState(); - - TestableLooper.get(this).processAllMessages(); + buttonConfig = getButtonConfig(); + assertNotNull(buttonConfig); assertEquals(mContext.getString(R.string.quick_settings_disclosure_named_management_vpns, MANAGING_ORGANIZATION), - mFooterText.getText()); + buttonConfig.getText()); } @Test @@ -381,13 +348,12 @@ public class QSSecurityFooterTest extends SysuiTestCase { when(mSecurityController.isNetworkLoggingEnabled()).thenReturn(true); when(mSecurityController.isVpnEnabled()).thenReturn(true); when(mSecurityController.getPrimaryVpnName()).thenReturn("VPN Test App"); - mFooter.refreshState(); - TestableLooper.get(this).processAllMessages(); - assertEquals(View.VISIBLE, mPrimaryFooterIcon.getVisibility()); - assertEquals(R.drawable.stat_sys_vpn_ic, mPrimaryFooterIcon.getLastImageResource()); + SecurityButtonConfig buttonConfig = getButtonConfig(); + assertNotNull(buttonConfig); + assertIsIconResource(buttonConfig.getIcon(), R.drawable.stat_sys_vpn_ic); assertEquals(mContext.getString(R.string.quick_settings_disclosure_management_monitoring), - mFooterText.getText()); + buttonConfig.getText()); } @Test @@ -395,24 +361,23 @@ public class QSSecurityFooterTest extends SysuiTestCase { when(mSecurityController.isDeviceManaged()).thenReturn(false); when(mSecurityController.hasCACertInWorkProfile()).thenReturn(true); when(mSecurityController.isWorkProfileOn()).thenReturn(true); - mFooter.refreshState(); - TestableLooper.get(this).processAllMessages(); - assertEquals(DEFAULT_ICON_ID, mPrimaryFooterIcon.getLastImageResource()); + SecurityButtonConfig buttonConfig = getButtonConfig(); + assertNotNull(buttonConfig); + assertIsDefaultIcon(buttonConfig.getIcon()); assertEquals(mContext.getString( R.string.quick_settings_disclosure_managed_profile_monitoring), - mFooterText.getText()); + buttonConfig.getText()); // Same situation, but with organization name set when(mSecurityController.getWorkProfileOrganizationName()) .thenReturn(MANAGING_ORGANIZATION); - mFooter.refreshState(); - - TestableLooper.get(this).processAllMessages(); + buttonConfig = getButtonConfig(); + assertNotNull(buttonConfig); assertEquals(mContext.getString( R.string.quick_settings_disclosure_named_managed_profile_monitoring, MANAGING_ORGANIZATION), - mFooterText.getText()); + buttonConfig.getText()); } @Test @@ -420,22 +385,20 @@ public class QSSecurityFooterTest extends SysuiTestCase { when(mSecurityController.isDeviceManaged()).thenReturn(false); when(mSecurityController.hasCACertInWorkProfile()).thenReturn(true); when(mSecurityController.isWorkProfileOn()).thenReturn(false); - mFooter.refreshState(); - TestableLooper.get(this).processAllMessages(); - assertEquals("", mFooterText.getText()); + assertNull(getButtonConfig()); } @Test public void testCACertsInstalled() { when(mSecurityController.isDeviceManaged()).thenReturn(false); when(mSecurityController.hasCACertInCurrentUser()).thenReturn(true); - mFooter.refreshState(); - TestableLooper.get(this).processAllMessages(); - assertEquals(DEFAULT_ICON_ID, mPrimaryFooterIcon.getLastImageResource()); + SecurityButtonConfig buttonConfig = getButtonConfig(); + assertNotNull(buttonConfig); + assertIsDefaultIcon(buttonConfig.getIcon()); assertEquals(mContext.getString(R.string.quick_settings_disclosure_monitoring), - mFooterText.getText()); + buttonConfig.getText()); } @Test @@ -443,12 +406,12 @@ public class QSSecurityFooterTest extends SysuiTestCase { when(mSecurityController.isVpnEnabled()).thenReturn(true); when(mSecurityController.getPrimaryVpnName()).thenReturn(VPN_PACKAGE); when(mSecurityController.getWorkProfileVpnName()).thenReturn(VPN_PACKAGE_2); - mFooter.refreshState(); - TestableLooper.get(this).processAllMessages(); - assertEquals(R.drawable.stat_sys_vpn_ic, mPrimaryFooterIcon.getLastImageResource()); + SecurityButtonConfig buttonConfig = getButtonConfig(); + assertNotNull(buttonConfig); + assertIsIconResource(buttonConfig.getIcon(), R.drawable.stat_sys_vpn_ic); assertEquals(mContext.getString(R.string.quick_settings_disclosure_vpns), - mFooterText.getText()); + buttonConfig.getText()); } @Test @@ -456,14 +419,14 @@ public class QSSecurityFooterTest extends SysuiTestCase { when(mSecurityController.isVpnEnabled()).thenReturn(true); when(mSecurityController.getWorkProfileVpnName()).thenReturn(VPN_PACKAGE_2); when(mSecurityController.isWorkProfileOn()).thenReturn(true); - mFooter.refreshState(); - TestableLooper.get(this).processAllMessages(); - assertEquals(R.drawable.stat_sys_vpn_ic, mPrimaryFooterIcon.getLastImageResource()); + SecurityButtonConfig buttonConfig = getButtonConfig(); + assertNotNull(buttonConfig); + assertIsIconResource(buttonConfig.getIcon(), R.drawable.stat_sys_vpn_ic); assertEquals(mContext.getString( R.string.quick_settings_disclosure_managed_profile_named_vpn, VPN_PACKAGE_2), - mFooterText.getText()); + buttonConfig.getText()); } @Test @@ -471,22 +434,19 @@ public class QSSecurityFooterTest extends SysuiTestCase { when(mSecurityController.isVpnEnabled()).thenReturn(true); when(mSecurityController.getWorkProfileVpnName()).thenReturn(VPN_PACKAGE_2); when(mSecurityController.isWorkProfileOn()).thenReturn(false); - mFooter.refreshState(); - TestableLooper.get(this).processAllMessages(); - assertEquals("", mFooterText.getText()); + assertNull(getButtonConfig()); } @Test public void testProfileOwnerOfOrganizationOwnedDeviceNoName() { when(mSecurityController.isProfileOwnerOfOrganizationOwnedDevice()).thenReturn(true); - mFooter.refreshState(); - TestableLooper.get(this).processAllMessages(); - + SecurityButtonConfig buttonConfig = getButtonConfig(); + assertNotNull(buttonConfig); assertEquals(mContext.getString( R.string.quick_settings_disclosure_management), - mFooterText.getText()); + buttonConfig.getText()); } @Test @@ -495,35 +455,33 @@ public class QSSecurityFooterTest extends SysuiTestCase { when(mSecurityController.getWorkProfileOrganizationName()) .thenReturn(MANAGING_ORGANIZATION); - mFooter.refreshState(); - TestableLooper.get(this).processAllMessages(); - + SecurityButtonConfig buttonConfig = getButtonConfig(); + assertNotNull(buttonConfig); assertEquals(mContext.getString( R.string.quick_settings_disclosure_named_management, MANAGING_ORGANIZATION), - mFooterText.getText()); + buttonConfig.getText()); } @Test public void testVpnEnabled() { when(mSecurityController.isVpnEnabled()).thenReturn(true); when(mSecurityController.getPrimaryVpnName()).thenReturn(VPN_PACKAGE); - mFooter.refreshState(); - TestableLooper.get(this).processAllMessages(); - assertEquals(R.drawable.stat_sys_vpn_ic, mPrimaryFooterIcon.getLastImageResource()); + SecurityButtonConfig buttonConfig = getButtonConfig(); + assertNotNull(buttonConfig); + assertIsIconResource(buttonConfig.getIcon(), R.drawable.stat_sys_vpn_ic); assertEquals(mContext.getString(R.string.quick_settings_disclosure_named_vpn, VPN_PACKAGE), - mFooterText.getText()); + buttonConfig.getText()); when(mSecurityController.hasWorkProfile()).thenReturn(true); - mFooter.refreshState(); - - TestableLooper.get(this).processAllMessages(); + buttonConfig = getButtonConfig(); + assertNotNull(buttonConfig); assertEquals(mContext.getString( R.string.quick_settings_disclosure_personal_profile_named_vpn, VPN_PACKAGE), - mFooterText.getText()); + buttonConfig.getText()); } @Test @@ -687,19 +645,6 @@ public class QSSecurityFooterTest extends SysuiTestCase { } @Test - public void testNoClickWhenGone() { - mFooter.refreshState(); - - TestableLooper.get(this).processAllMessages(); - - assertFalse(mFooter.hasFooter()); - mFooter.onClick(mFooter.getView()); - - // Proxy for dialog being created - verify(mDialogLaunchAnimator, never()).showFromView(any(), any()); - } - - @Test public void testParentalControls() { // Make sure the security footer is visible, so that the images are updated. when(mSecurityController.isProfileOwnerOfOrganizationOwnedDevice()).thenReturn(true); @@ -707,29 +652,26 @@ public class QSSecurityFooterTest extends SysuiTestCase { // We use the default icon when there is no admin icon. when(mSecurityController.getIcon(any())).thenReturn(null); - mFooter.refreshState(); - TestableLooper.get(this).processAllMessages(); + SecurityButtonConfig buttonConfig = getButtonConfig(); assertEquals(mContext.getString(R.string.quick_settings_disclosure_parental_controls), - mFooterText.getText()); - assertEquals(DEFAULT_ICON_ID, mPrimaryFooterIcon.getLastImageResource()); + buttonConfig.getText()); + assertIsDefaultIcon(buttonConfig.getIcon()); Drawable testDrawable = new VectorDrawable(); when(mSecurityController.getIcon(any())).thenReturn(testDrawable); assertNotNull(mSecurityController.getIcon(null)); - mFooter.refreshState(); - TestableLooper.get(this).processAllMessages(); - + buttonConfig = getButtonConfig(); + assertNotNull(buttonConfig); assertEquals(mContext.getString(R.string.quick_settings_disclosure_parental_controls), - mFooterText.getText()); - assertEquals(View.VISIBLE, mPrimaryFooterIcon.getVisibility()); - assertEquals(testDrawable, mPrimaryFooterIcon.getDrawable()); + buttonConfig.getText()); + assertIsIconDrawable(buttonConfig.getIcon(), testDrawable); // Ensure the primary icon is back to default after parental controls are gone when(mSecurityController.isParentalControlsEnabled()).thenReturn(false); - mFooter.refreshState(); - TestableLooper.get(this).processAllMessages(); - assertEquals(DEFAULT_ICON_ID, mPrimaryFooterIcon.getLastImageResource()); + buttonConfig = getButtonConfig(); + assertNotNull(buttonConfig); + assertIsDefaultIcon(buttonConfig.getIcon()); } @Test @@ -743,16 +685,6 @@ public class QSSecurityFooterTest extends SysuiTestCase { } @Test - public void testDialogUsesDialogLauncher() { - when(mSecurityController.isDeviceManaged()).thenReturn(true); - mFooter.onClick(mSecurityFooterView); - - mTestableLooper.processAllMessages(); - - verify(mDialogLaunchAnimator).show(any(), any()); - } - - @Test public void testCreateDialogViewForFinancedDevice() { when(mSecurityController.isDeviceManaged()).thenReturn(true); when(mSecurityController.getDeviceOwnerOrganizationName()) @@ -782,7 +714,10 @@ public class QSSecurityFooterTest extends SysuiTestCase { when(mSecurityController.getDeviceOwnerType(DEVICE_OWNER_COMPONENT)) .thenReturn(DEVICE_OWNER_TYPE_FINANCED); - mFooter.showDeviceMonitoringDialog(); + Expandable expandable = mock(Expandable.class); + when(expandable.dialogLaunchController(any())).thenReturn( + mock(DialogLaunchAnimator.Controller.class)); + mFooterUtils.showDeviceMonitoringDialog(getContext(), expandable); ArgumentCaptor<AlertDialog> dialogCaptor = ArgumentCaptor.forClass(AlertDialog.class); mTestableLooper.processAllMessages(); @@ -797,47 +732,6 @@ public class QSSecurityFooterTest extends SysuiTestCase { dialog.dismiss(); } - @Test - public void testVisibilityListener() { - final AtomicInteger lastVisibility = new AtomicInteger(-1); - VisibilityChangedDispatcher.OnVisibilityChangedListener listener = lastVisibility::set; - - mFooter.setOnVisibilityChangedListener(listener); - - when(mSecurityController.isDeviceManaged()).thenReturn(true); - mFooter.refreshState(); - mTestableLooper.processAllMessages(); - assertEquals(View.VISIBLE, lastVisibility.get()); - - when(mSecurityController.isDeviceManaged()).thenReturn(false); - mFooter.refreshState(); - mTestableLooper.processAllMessages(); - assertEquals(View.GONE, lastVisibility.get()); - } - - @Test - public void testBroadcastShowsDialog() { - // Setup dialog content - when(mSecurityController.isDeviceManaged()).thenReturn(true); - when(mSecurityController.getDeviceOwnerOrganizationName()) - .thenReturn(MANAGING_ORGANIZATION); - when(mSecurityController.getDeviceOwnerType(DEVICE_OWNER_COMPONENT)) - .thenReturn(DEVICE_OWNER_TYPE_FINANCED); - - ArgumentCaptor<BroadcastReceiver> captor = ArgumentCaptor.forClass(BroadcastReceiver.class); - verify(mBroadcastDispatcher).registerReceiverWithHandler(captor.capture(), any(), any(), - any()); - - // Pretend view is not attached anymore. - mRootView.removeView(mSecurityFooterView); - captor.getValue().onReceive(mContext, - new Intent(DevicePolicyManager.ACTION_SHOW_DEVICE_MONITORING_DIALOG)); - mTestableLooper.processAllMessages(); - - assertTrue(mFooterUtils.getDialog().isShowing()); - mFooterUtils.getDialog().dismiss(); - } - private CharSequence addLink(CharSequence description) { final SpannableStringBuilder message = new SpannableStringBuilder(); message.append(description); diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/footer/ui/viewmodel/FooterActionsViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/footer/ui/viewmodel/FooterActionsViewModelTest.kt index 47afa70fa84b..01411c9e7f04 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/qs/footer/ui/viewmodel/FooterActionsViewModelTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/qs/footer/ui/viewmodel/FooterActionsViewModelTest.kt @@ -385,4 +385,86 @@ class FooterActionsViewModelTest : SysuiTestCase() { underTest.onVisibilityChangeRequested(visible = true) assertThat(underTest.isVisible.value).isTrue() } + + @Test + fun alpha_inSplitShade_followsExpansion() { + val underTest = utils.footerActionsViewModel() + + underTest.onQuickSettingsExpansionChanged(0f, isInSplitShade = true) + assertThat(underTest.alpha.value).isEqualTo(0f) + + underTest.onQuickSettingsExpansionChanged(0.25f, isInSplitShade = true) + assertThat(underTest.alpha.value).isEqualTo(0.25f) + + underTest.onQuickSettingsExpansionChanged(0.5f, isInSplitShade = true) + assertThat(underTest.alpha.value).isEqualTo(0.5f) + + underTest.onQuickSettingsExpansionChanged(0.75f, isInSplitShade = true) + assertThat(underTest.alpha.value).isEqualTo(0.75f) + + underTest.onQuickSettingsExpansionChanged(1f, isInSplitShade = true) + assertThat(underTest.alpha.value).isEqualTo(1f) + } + + @Test + fun backgroundAlpha_inSplitShade_followsExpansion_with_0_99_delay() { + val underTest = utils.footerActionsViewModel() + val floatTolerance = 0.01f + + underTest.onQuickSettingsExpansionChanged(0f, isInSplitShade = true) + assertThat(underTest.backgroundAlpha.value).isEqualTo(0f) + + underTest.onQuickSettingsExpansionChanged(0.5f, isInSplitShade = true) + assertThat(underTest.backgroundAlpha.value).isEqualTo(0f) + + underTest.onQuickSettingsExpansionChanged(0.9f, isInSplitShade = true) + assertThat(underTest.backgroundAlpha.value).isEqualTo(0f) + + underTest.onQuickSettingsExpansionChanged(0.991f, isInSplitShade = true) + assertThat(underTest.backgroundAlpha.value).isWithin(floatTolerance).of(0.1f) + + underTest.onQuickSettingsExpansionChanged(0.995f, isInSplitShade = true) + assertThat(underTest.backgroundAlpha.value).isWithin(floatTolerance).of(0.5f) + + underTest.onQuickSettingsExpansionChanged(1f, isInSplitShade = true) + assertThat(underTest.backgroundAlpha.value).isEqualTo(1f) + } + + @Test + fun alpha_inSingleShade_followsExpansion_with_0_9_delay() { + val underTest = utils.footerActionsViewModel() + val floatTolerance = 0.01f + + underTest.onQuickSettingsExpansionChanged(0f, isInSplitShade = false) + assertThat(underTest.alpha.value).isEqualTo(0f) + + underTest.onQuickSettingsExpansionChanged(0.5f, isInSplitShade = false) + assertThat(underTest.alpha.value).isEqualTo(0f) + + underTest.onQuickSettingsExpansionChanged(0.9f, isInSplitShade = false) + assertThat(underTest.alpha.value).isEqualTo(0f) + + underTest.onQuickSettingsExpansionChanged(0.91f, isInSplitShade = false) + assertThat(underTest.alpha.value).isWithin(floatTolerance).of(0.1f) + + underTest.onQuickSettingsExpansionChanged(0.95f, isInSplitShade = false) + assertThat(underTest.alpha.value).isWithin(floatTolerance).of(0.5f) + + underTest.onQuickSettingsExpansionChanged(1f, isInSplitShade = false) + assertThat(underTest.alpha.value).isEqualTo(1f) + } + + @Test + fun backgroundAlpha_inSingleShade_always1() { + val underTest = utils.footerActionsViewModel() + + underTest.onQuickSettingsExpansionChanged(0f, isInSplitShade = false) + assertThat(underTest.backgroundAlpha.value).isEqualTo(1f) + + underTest.onQuickSettingsExpansionChanged(0.5f, isInSplitShade = false) + assertThat(underTest.backgroundAlpha.value).isEqualTo(1f) + + underTest.onQuickSettingsExpansionChanged(1f, isInSplitShade = false) + assertThat(underTest.backgroundAlpha.value).isEqualTo(1f) + } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerTest.java index 56a840cae267..0302dade0a8c 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerTest.java @@ -173,6 +173,7 @@ import com.android.wm.shell.animation.FlingAnimationUtils; import org.junit.After; import org.junit.Before; +import org.junit.Ignore; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.ArgumentCaptor; @@ -611,6 +612,7 @@ public class NotificationPanelViewControllerTest extends SysuiTestCase { } @Test + @Ignore("b/261472011 - Test appears inconsistent across environments") public void getVerticalSpaceForLockscreenNotifications_useLockIconBottomPadding_returnsSpaceAvailable() { setBottomPadding(/* stackScrollLayoutBottom= */ 180, /* lockIconPadding= */ 20, @@ -622,6 +624,7 @@ public class NotificationPanelViewControllerTest extends SysuiTestCase { } @Test + @Ignore("b/261472011 - Test appears inconsistent across environments") public void getVerticalSpaceForLockscreenNotifications_useIndicationBottomPadding_returnsSpaceAvailable() { setBottomPadding(/* stackScrollLayoutBottom= */ 180, /* lockIconPadding= */ 0, @@ -633,6 +636,7 @@ public class NotificationPanelViewControllerTest extends SysuiTestCase { } @Test + @Ignore("b/261472011 - Test appears inconsistent across environments") public void getVerticalSpaceForLockscreenNotifications_useAmbientBottomPadding_returnsSpaceAvailable() { setBottomPadding(/* stackScrollLayoutBottom= */ 180, /* lockIconPadding= */ 0, diff --git a/packages/SystemUI/tests/src/com/android/systemui/util/condition/ConditionMonitorTest.java b/packages/SystemUI/tests/src/com/android/systemui/shared/condition/ConditionMonitorTest.java index 17d81c8338cb..7693fee0a1c4 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/util/condition/ConditionMonitorTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/shared/condition/ConditionMonitorTest.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2021 The Android Open Source Project + * Copyright (C) 2022 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. @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.systemui.util.condition; +package com.android.systemui.shared.condition; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.eq; @@ -68,13 +68,15 @@ public class ConditionMonitorTest extends SysuiTestCase { mConditionMonitor = new Monitor(mExecutor); } - public Monitor.Subscription.Builder getDefaultBuilder(Monitor.Callback callback) { + public Monitor.Subscription.Builder getDefaultBuilder( + Monitor.Callback callback) { return new Monitor.Subscription.Builder(callback) .addConditions(mConditions); } private Condition createMockCondition() { - final Condition condition = Mockito.mock(Condition.class); + final Condition condition = Mockito.mock( + Condition.class); when(condition.isConditionSet()).thenReturn(true); return condition; } @@ -83,11 +85,14 @@ public class ConditionMonitorTest extends SysuiTestCase { public void testOverridingCondition() { final Condition overridingCondition = createMockCondition(); final Condition regularCondition = createMockCondition(); - final Monitor.Callback callback = Mockito.mock(Monitor.Callback.class); + final Monitor.Callback callback = Mockito.mock( + Monitor.Callback.class); - final Monitor.Callback referenceCallback = Mockito.mock(Monitor.Callback.class); + final Monitor.Callback referenceCallback = Mockito.mock( + Monitor.Callback.class); - final Monitor monitor = new Monitor(mExecutor); + final Monitor + monitor = new Monitor(mExecutor); monitor.addSubscription(getDefaultBuilder(callback) .addCondition(overridingCondition) @@ -136,9 +141,11 @@ public class ConditionMonitorTest extends SysuiTestCase { final Condition overridingCondition = createMockCondition(); final Condition overridingCondition2 = createMockCondition(); final Condition regularCondition = createMockCondition(); - final Monitor.Callback callback = Mockito.mock(Monitor.Callback.class); + final Monitor.Callback callback = Mockito.mock( + Monitor.Callback.class); - final Monitor monitor = new Monitor(mExecutor); + final Monitor + monitor = new Monitor(mExecutor); monitor.addSubscription(getDefaultBuilder(callback) .addCondition(overridingCondition) @@ -211,9 +218,11 @@ public class ConditionMonitorTest extends SysuiTestCase { public void addCallback_addSecondCallback_reportWithExistingValue() { final Monitor.Callback callback1 = mock(Monitor.Callback.class); - final Condition condition = mock(Condition.class); + final Condition condition = mock( + Condition.class); when(condition.isConditionMet()).thenReturn(true); - final Monitor monitor = new Monitor(mExecutor); + final Monitor + monitor = new Monitor(mExecutor); monitor.addSubscription(new Monitor.Subscription.Builder(callback1) .addCondition(condition) .build()); @@ -229,8 +238,10 @@ public class ConditionMonitorTest extends SysuiTestCase { @Test public void addCallback_noConditions_reportAllConditionsMet() { - final Monitor monitor = new Monitor(mExecutor); - final Monitor.Callback callback = mock(Monitor.Callback.class); + final Monitor + monitor = new Monitor(mExecutor); + final Monitor.Callback callback = mock( + Monitor.Callback.class); monitor.addSubscription(new Monitor.Subscription.Builder(callback).build()); mExecutor.runAllReady(); @@ -239,8 +250,10 @@ public class ConditionMonitorTest extends SysuiTestCase { @Test public void removeCallback_noFailureOnDoubleRemove() { - final Condition condition = mock(Condition.class); - final Monitor monitor = new Monitor(mExecutor); + final Condition condition = mock( + Condition.class); + final Monitor + monitor = new Monitor(mExecutor); final Monitor.Callback callback = mock(Monitor.Callback.class); final Monitor.Subscription.Token token = monitor.addSubscription( @@ -255,8 +268,10 @@ public class ConditionMonitorTest extends SysuiTestCase { @Test public void removeCallback_shouldNoLongerReceiveUpdate() { - final Condition condition = mock(Condition.class); - final Monitor monitor = new Monitor(mExecutor); + final Condition condition = mock( + Condition.class); + final Monitor + monitor = new Monitor(mExecutor); final Monitor.Callback callback = mock(Monitor.Callback.class); final Monitor.Subscription.Token token = monitor.addSubscription( diff --git a/packages/SystemUI/tests/src/com/android/systemui/util/condition/ConditionTest.java b/packages/SystemUI/tests/src/com/android/systemui/shared/condition/ConditionTest.java index 28788647dd58..8443221e8b7a 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/util/condition/ConditionTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/shared/condition/ConditionTest.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2021 The Android Open Source Project + * Copyright (C) 2022 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. @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.systemui.util.condition; +package com.android.systemui.shared.condition; import static com.google.common.truth.Truth.assertThat; @@ -47,16 +47,20 @@ public class ConditionTest extends SysuiTestCase { @Test public void addCallback_addFirstCallback_triggerStart() { - final Condition.Callback callback = mock(Condition.Callback.class); + final Condition.Callback callback = mock( + Condition.Callback.class); mCondition.addCallback(callback); verify(mCondition).start(); } @Test public void addCallback_addMultipleCallbacks_triggerStartOnlyOnce() { - final Condition.Callback callback1 = mock(Condition.Callback.class); - final Condition.Callback callback2 = mock(Condition.Callback.class); - final Condition.Callback callback3 = mock(Condition.Callback.class); + final Condition.Callback callback1 = mock( + Condition.Callback.class); + final Condition.Callback callback2 = mock( + Condition.Callback.class); + final Condition.Callback callback3 = mock( + Condition.Callback.class); mCondition.addCallback(callback1); mCondition.addCallback(callback2); @@ -67,12 +71,14 @@ public class ConditionTest extends SysuiTestCase { @Test public void addCallback_alreadyStarted_triggerUpdate() { - final Condition.Callback callback1 = mock(Condition.Callback.class); + final Condition.Callback callback1 = mock( + Condition.Callback.class); mCondition.addCallback(callback1); mCondition.fakeUpdateCondition(true); - final Condition.Callback callback2 = mock(Condition.Callback.class); + final Condition.Callback callback2 = mock( + Condition.Callback.class); mCondition.addCallback(callback2); verify(callback2).onConditionChanged(mCondition); assertThat(mCondition.isConditionMet()).isTrue(); @@ -80,7 +86,8 @@ public class ConditionTest extends SysuiTestCase { @Test public void removeCallback_removeLastCallback_triggerStop() { - final Condition.Callback callback = mock(Condition.Callback.class); + final Condition.Callback callback = mock( + Condition.Callback.class); mCondition.addCallback(callback); verify(mCondition, never()).stop(); @@ -92,7 +99,8 @@ public class ConditionTest extends SysuiTestCase { public void updateCondition_falseToTrue_reportTrue() { mCondition.fakeUpdateCondition(false); - final Condition.Callback callback = mock(Condition.Callback.class); + final Condition.Callback callback = mock( + Condition.Callback.class); mCondition.addCallback(callback); mCondition.fakeUpdateCondition(true); @@ -104,7 +112,8 @@ public class ConditionTest extends SysuiTestCase { public void updateCondition_trueToFalse_reportFalse() { mCondition.fakeUpdateCondition(true); - final Condition.Callback callback = mock(Condition.Callback.class); + final Condition.Callback callback = mock( + Condition.Callback.class); mCondition.addCallback(callback); mCondition.fakeUpdateCondition(false); @@ -116,7 +125,8 @@ public class ConditionTest extends SysuiTestCase { public void updateCondition_trueToTrue_reportNothing() { mCondition.fakeUpdateCondition(true); - final Condition.Callback callback = mock(Condition.Callback.class); + final Condition.Callback callback = mock( + Condition.Callback.class); mCondition.addCallback(callback); mCondition.fakeUpdateCondition(true); @@ -127,7 +137,8 @@ public class ConditionTest extends SysuiTestCase { public void updateCondition_falseToFalse_reportNothing() { mCondition.fakeUpdateCondition(false); - final Condition.Callback callback = mock(Condition.Callback.class); + final Condition.Callback callback = mock( + Condition.Callback.class); mCondition.addCallback(callback); mCondition.fakeUpdateCondition(false); @@ -149,7 +160,8 @@ public class ConditionTest extends SysuiTestCase { final Condition combinedCondition = mCondition.or( new FakeCondition(/* initialValue= */ false)); - final Condition.Callback callback = mock(Condition.Callback.class); + final Condition.Callback callback = mock( + Condition.Callback.class); combinedCondition.addCallback(callback); assertThat(combinedCondition.isConditionSet()).isTrue(); @@ -164,7 +176,8 @@ public class ConditionTest extends SysuiTestCase { final Condition combinedCondition = mCondition.or( new FakeCondition(/* initialValue= */ true)); - final Condition.Callback callback = mock(Condition.Callback.class); + final Condition.Callback callback = mock( + Condition.Callback.class); combinedCondition.addCallback(callback); assertThat(combinedCondition.isConditionSet()).isTrue(); @@ -179,7 +192,8 @@ public class ConditionTest extends SysuiTestCase { final Condition combinedCondition = mCondition.or( new FakeCondition(/* initialValue= */ true)); - final Condition.Callback callback = mock(Condition.Callback.class); + final Condition.Callback callback = mock( + Condition.Callback.class); combinedCondition.addCallback(callback); assertThat(combinedCondition.isConditionSet()).isTrue(); @@ -195,7 +209,8 @@ public class ConditionTest extends SysuiTestCase { final Condition combinedCondition = mCondition.or( new FakeCondition(/* initialValue= */ null)); - final Condition.Callback callback = mock(Condition.Callback.class); + final Condition.Callback callback = mock( + Condition.Callback.class); combinedCondition.addCallback(callback); assertThat(combinedCondition.isConditionSet()).isTrue(); @@ -211,7 +226,8 @@ public class ConditionTest extends SysuiTestCase { final Condition combinedCondition = mCondition.or( new FakeCondition(/* initialValue= */ null)); - final Condition.Callback callback = mock(Condition.Callback.class); + final Condition.Callback callback = mock( + Condition.Callback.class); combinedCondition.addCallback(callback); assertThat(combinedCondition.isConditionSet()).isFalse(); @@ -226,7 +242,8 @@ public class ConditionTest extends SysuiTestCase { final Condition combinedCondition = mCondition.and( new FakeCondition(/* initialValue= */ false)); - final Condition.Callback callback = mock(Condition.Callback.class); + final Condition.Callback callback = mock( + Condition.Callback.class); combinedCondition.addCallback(callback); assertThat(combinedCondition.isConditionSet()).isTrue(); @@ -241,7 +258,8 @@ public class ConditionTest extends SysuiTestCase { final Condition combinedCondition = mCondition.and( new FakeCondition(/* initialValue= */ true)); - final Condition.Callback callback = mock(Condition.Callback.class); + final Condition.Callback callback = mock( + Condition.Callback.class); combinedCondition.addCallback(callback); assertThat(combinedCondition.isConditionSet()).isTrue(); @@ -256,7 +274,8 @@ public class ConditionTest extends SysuiTestCase { final Condition combinedCondition = mCondition.and( new FakeCondition(/* initialValue= */ false)); - final Condition.Callback callback = mock(Condition.Callback.class); + final Condition.Callback callback = mock( + Condition.Callback.class); combinedCondition.addCallback(callback); assertThat(combinedCondition.isConditionSet()).isTrue(); @@ -272,7 +291,8 @@ public class ConditionTest extends SysuiTestCase { final Condition combinedCondition = mCondition.and( new FakeCondition(/* initialValue= */ null)); - final Condition.Callback callback = mock(Condition.Callback.class); + final Condition.Callback callback = mock( + Condition.Callback.class); combinedCondition.addCallback(callback); assertThat(combinedCondition.isConditionSet()).isFalse(); @@ -288,7 +308,8 @@ public class ConditionTest extends SysuiTestCase { final Condition combinedCondition = mCondition.and( new FakeCondition(/* initialValue= */ null)); - final Condition.Callback callback = mock(Condition.Callback.class); + final Condition.Callback callback = mock( + Condition.Callback.class); combinedCondition.addCallback(callback); assertThat(combinedCondition.isConditionSet()).isTrue(); diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/util/condition/FakeCondition.java b/packages/SystemUI/tests/src/com/android/systemui/shared/condition/FakeCondition.java index 07ed1102e990..55a6d39d4644 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/util/condition/FakeCondition.java +++ b/packages/SystemUI/tests/src/com/android/systemui/shared/condition/FakeCondition.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2021 The Android Open Source Project + * Copyright (C) 2022 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. @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.systemui.util.condition; +package com.android.systemui.shared.condition; /** * Fake implementation of {@link Condition}, and provides a way for tests to update diff --git a/packages/SystemUI/tests/src/com/android/systemui/wallpapers/ImageWallpaperTest.java b/packages/SystemUI/tests/src/com/android/systemui/wallpapers/ImageWallpaperTest.java index 379bb28ae032..30dc0d20600f 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/wallpapers/ImageWallpaperTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/wallpapers/ImageWallpaperTest.java @@ -108,7 +108,6 @@ public class ImageWallpaperTest extends SysuiTestCase { private FeatureFlags mFeatureFlags; FakeSystemClock mFakeSystemClock = new FakeSystemClock(); - FakeExecutor mFakeMainExecutor = new FakeExecutor(mFakeSystemClock); FakeExecutor mFakeBackgroundExecutor = new FakeExecutor(mFakeSystemClock); private CountDownLatch mEventCountdown; @@ -163,7 +162,7 @@ public class ImageWallpaperTest extends SysuiTestCase { } private ImageWallpaper createImageWallpaper() { - return new ImageWallpaper(mFeatureFlags, mFakeBackgroundExecutor, mFakeMainExecutor) { + return new ImageWallpaper(mFeatureFlags, mFakeBackgroundExecutor) { @Override public Engine onCreateEngine() { return new GLEngine(mHandler) { @@ -242,7 +241,7 @@ public class ImageWallpaperTest extends SysuiTestCase { private ImageWallpaper createImageWallpaperCanvas() { - return new ImageWallpaper(mFeatureFlags, mFakeBackgroundExecutor, mFakeMainExecutor) { + return new ImageWallpaper(mFeatureFlags, mFakeBackgroundExecutor) { @Override public Engine onCreateEngine() { return new CanvasEngine() { @@ -315,11 +314,10 @@ public class ImageWallpaperTest extends SysuiTestCase { assertThat(mFakeBackgroundExecutor.numPending()).isAtLeast(1); int n = 0; - while (mFakeBackgroundExecutor.numPending() + mFakeMainExecutor.numPending() >= 1) { + while (mFakeBackgroundExecutor.numPending() >= 1) { n++; assertThat(n).isAtMost(10); mFakeBackgroundExecutor.runNextReady(); - mFakeMainExecutor.runNextReady(); mFakeSystemClock.advanceTime(1000); } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/FakeFgsManagerController.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/FakeFgsManagerController.kt index b31f119b7a7c..ced7955100f7 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/FakeFgsManagerController.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/FakeFgsManagerController.kt @@ -77,7 +77,5 @@ class FakeFgsManagerController( dialogDismissedListeners.remove(listener) } - override fun shouldUpdateFooterVisibility(): Boolean = false - override fun visibleButtonsCount(): Int = 0 } diff --git a/packages/SystemUI/unfold/src/com/android/systemui/unfold/progress/PhysicsBasedUnfoldTransitionProgressProvider.kt b/packages/SystemUI/unfold/src/com/android/systemui/unfold/progress/PhysicsBasedUnfoldTransitionProgressProvider.kt index b56818693124..52fb0a79a2bb 100644 --- a/packages/SystemUI/unfold/src/com/android/systemui/unfold/progress/PhysicsBasedUnfoldTransitionProgressProvider.kt +++ b/packages/SystemUI/unfold/src/com/android/systemui/unfold/progress/PhysicsBasedUnfoldTransitionProgressProvider.kt @@ -31,6 +31,7 @@ import com.android.systemui.unfold.updates.FOLD_UPDATE_UNFOLDED_SCREEN_AVAILABLE import com.android.systemui.unfold.updates.FoldStateProvider import com.android.systemui.unfold.updates.FoldStateProvider.FoldUpdate import com.android.systemui.unfold.updates.FoldStateProvider.FoldUpdatesListener +import com.android.systemui.unfold.updates.name /** Maps fold updates to unfold transition progress using DynamicAnimation. */ class PhysicsBasedUnfoldTransitionProgressProvider( @@ -117,7 +118,7 @@ class PhysicsBasedUnfoldTransitionProgressProvider( } if (DEBUG) { - Log.d(TAG, "onFoldUpdate = $update") + Log.d(TAG, "onFoldUpdate = ${update.name()}") Trace.traceCounter(Trace.TRACE_TAG_APP, "fold_update", update) } } diff --git a/services/core/java/com/android/server/wm/ActivityMetricsLogger.java b/services/core/java/com/android/server/wm/ActivityMetricsLogger.java index d2413f015003..55a4cc79abf0 100644 --- a/services/core/java/com/android/server/wm/ActivityMetricsLogger.java +++ b/services/core/java/com/android/server/wm/ActivityMetricsLogger.java @@ -726,7 +726,7 @@ class ActivityMetricsLogger { // visible such as after the top task is finished. for (int i = mTransitionInfoList.size() - 2; i >= 0; i--) { final TransitionInfo prevInfo = mTransitionInfoList.get(i); - if (prevInfo.mIsDrawn || !prevInfo.mLastLaunchedActivity.mVisibleRequested) { + if (prevInfo.mIsDrawn || !prevInfo.mLastLaunchedActivity.isVisibleRequested()) { scheduleCheckActivityToBeDrawn(prevInfo.mLastLaunchedActivity, 0 /* delay */); } } @@ -851,7 +851,7 @@ class ActivityMetricsLogger { return; } if (DEBUG_METRICS) { - Slog.i(TAG, "notifyVisibilityChanged " + r + " visible=" + r.mVisibleRequested + Slog.i(TAG, "notifyVisibilityChanged " + r + " visible=" + r.isVisibleRequested() + " state=" + r.getState() + " finishing=" + r.finishing); } if (r.isState(ActivityRecord.State.RESUMED) && r.mDisplayContent.isSleeping()) { @@ -860,7 +860,7 @@ class ActivityMetricsLogger { // the tracking of launch event. return; } - if (!r.mVisibleRequested || r.finishing) { + if (!r.isVisibleRequested() || r.finishing) { // Check if the tracker can be cancelled because the last launched activity may be // no longer visible. scheduleCheckActivityToBeDrawn(r, 0 /* delay */); @@ -893,7 +893,7 @@ class ActivityMetricsLogger { // activities in this task may be finished, invisible or drawn, so the transition event // should be cancelled. if (t != null && t.forAllActivities( - a -> a.mVisibleRequested && !a.isReportedDrawn() && !a.finishing)) { + a -> a.isVisibleRequested() && !a.isReportedDrawn() && !a.finishing)) { return; } diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java index e19e921852ff..313f5e205add 100644 --- a/services/core/java/com/android/server/wm/ActivityRecord.java +++ b/services/core/java/com/android/server/wm/ActivityRecord.java @@ -800,7 +800,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A // it will sometimes be true a little earlier: when the activity record has // been shown, but is still waiting for its app transition to execute // before making its windows shown. - boolean mVisibleRequested; + private boolean mVisibleRequested; // Last visibility state we reported to the app token. boolean reportedVisible; @@ -3632,7 +3632,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A // implied that the current finishing activity should be added into stopping list rather // than destroy immediately. final boolean isNextNotYetVisible = next != null - && (!next.nowVisible || !next.mVisibleRequested); + && (!next.nowVisible || !next.isVisibleRequested()); // Clear last paused activity to ensure top activity can be resumed during sleeping. if (isNextNotYetVisible && mDisplayContent.isSleeping() @@ -4450,7 +4450,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A void transferStartingWindowFromHiddenAboveTokenIfNeeded() { task.forAllActivities(fromActivity -> { if (fromActivity == this) return true; - return !fromActivity.mVisibleRequested && transferStartingWindow(fromActivity); + return !fromActivity.isVisibleRequested() && transferStartingWindow(fromActivity); }); } @@ -5115,7 +5115,8 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A * This is the only place that writes {@link #mVisibleRequested} (except unit test). The caller * outside of this class should use {@link #setVisibility}. */ - private void setVisibleRequested(boolean visible) { + @VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE) + void setVisibleRequested(boolean visible) { if (visible == mVisibleRequested) { return; } @@ -6561,7 +6562,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A if (associatedTask == null) { removeStartingWindow(); } else if (associatedTask.getActivity( - r -> r.mVisibleRequested && !r.firstWindowDrawn) == null) { + r -> r.isVisibleRequested() && !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) { @@ -7940,8 +7941,8 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A } @Override - float getSizeCompatScale() { - return hasSizeCompatBounds() ? mSizeCompatScale : super.getSizeCompatScale(); + float getCompatScale() { + return hasSizeCompatBounds() ? mSizeCompatScale : super.getCompatScale(); } @Override diff --git a/services/core/java/com/android/server/wm/ActivityServiceConnectionsHolder.java b/services/core/java/com/android/server/wm/ActivityServiceConnectionsHolder.java index 30c7b232fcc8..0859d40c0fd1 100644 --- a/services/core/java/com/android/server/wm/ActivityServiceConnectionsHolder.java +++ b/services/core/java/com/android/server/wm/ActivityServiceConnectionsHolder.java @@ -92,7 +92,7 @@ public class ActivityServiceConnectionsHolder<T> { public boolean isActivityVisible() { synchronized (mService.mGlobalLock) { - return mActivity.mVisibleRequested || mActivity.isState(RESUMED, PAUSING); + return mActivity.isVisibleRequested() || mActivity.isState(RESUMED, PAUSING); } } diff --git a/services/core/java/com/android/server/wm/ActivityStartController.java b/services/core/java/com/android/server/wm/ActivityStartController.java index e1c3cbf94bd4..4663662c5585 100644 --- a/services/core/java/com/android/server/wm/ActivityStartController.java +++ b/services/core/java/com/android/server/wm/ActivityStartController.java @@ -558,7 +558,7 @@ public class ActivityStartController { if (rootTask == null) return false; final RemoteTransition remote = options.getRemoteTransition(); final ActivityRecord r = rootTask.topRunningActivity(); - if (r == null || r.mVisibleRequested || !r.attachedToProcess() || remote == null + if (r == null || r.isVisibleRequested() || !r.attachedToProcess() || remote == null || !r.mActivityComponent.equals(intent.getComponent()) // Recents keeps invisible while device is locked. || r.mDisplayContent.isKeyguardLocked()) { diff --git a/services/core/java/com/android/server/wm/ActivityStarter.java b/services/core/java/com/android/server/wm/ActivityStarter.java index 27869c7cd44b..4fa7095b3282 100644 --- a/services/core/java/com/android/server/wm/ActivityStarter.java +++ b/services/core/java/com/android/server/wm/ActivityStarter.java @@ -2928,7 +2928,7 @@ class ActivityStarter { // If the activity is visible in multi-windowing mode, it may already be on // the top (visible to user but not the global top), then the result code // should be START_DELIVERED_TO_TOP instead of START_TASK_TO_FRONT. - final boolean wasTopOfVisibleRootTask = intentActivity.mVisibleRequested + final boolean wasTopOfVisibleRootTask = intentActivity.isVisibleRequested() && intentActivity.inMultiWindowMode() && intentActivity == mTargetRootTask.topRunningActivity(); // We only want to move to the front, if we aren't going to launch on a diff --git a/services/core/java/com/android/server/wm/BackNavigationController.java b/services/core/java/com/android/server/wm/BackNavigationController.java index 028d4b3e0871..adb88060528a 100644 --- a/services/core/java/com/android/server/wm/BackNavigationController.java +++ b/services/core/java/com/android/server/wm/BackNavigationController.java @@ -319,7 +319,7 @@ class BackNavigationController { // launch-behind to bump its visibility for the duration of the back gesture. prevActivity = prevTask.getTopNonFinishingActivity(); if (prevActivity != null) { - if (!prevActivity.mVisibleRequested) { + if (!prevActivity.isVisibleRequested()) { prevActivity.setVisibility(true); } prevActivity.mLaunchTaskBehind = true; @@ -492,7 +492,7 @@ class BackNavigationController { private void prepareBackToHomeTransition(ActivityRecord currentActivity, Task homeTask) { final DisplayContent dc = currentActivity.getDisplayContent(); final ActivityRecord homeActivity = homeTask.getTopNonFinishingActivity(); - if (!homeActivity.mVisibleRequested) { + if (!homeActivity.isVisibleRequested()) { homeActivity.setVisibility(true); } homeActivity.mLaunchTaskBehind = true; diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java index b33a83dac6e5..459949cffdf1 100644 --- a/services/core/java/com/android/server/wm/DisplayContent.java +++ b/services/core/java/com/android/server/wm/DisplayContent.java @@ -861,11 +861,11 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp final ActivityRecord activity = w.mActivityRecord; if (gone) Slog.v(TAG, " GONE: mViewVisibility=" + w.mViewVisibility + " mRelayoutCalled=" + w.mRelayoutCalled + " visible=" + w.mToken.isVisible() - + " visibleRequested=" + (activity != null && activity.mVisibleRequested) + + " visibleRequested=" + (activity != null && activity.isVisibleRequested()) + " parentHidden=" + w.isParentWindowHidden()); else Slog.v(TAG, " VIS: mViewVisibility=" + w.mViewVisibility + " mRelayoutCalled=" + w.mRelayoutCalled + " visible=" + w.mToken.isVisible() - + " visibleRequested=" + (activity != null && activity.mVisibleRequested) + + " visibleRequested=" + (activity != null && activity.isVisibleRequested()) + " parentHidden=" + w.isParentWindowHidden()); } @@ -1662,7 +1662,7 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp .notifyTaskRequestedOrientationChanged(task.mTaskId, orientation); } // The orientation source may not be the top if it uses SCREEN_ORIENTATION_BEHIND. - final ActivityRecord topCandidate = !r.mVisibleRequested ? topRunningActivity() : r; + final ActivityRecord topCandidate = !r.isVisibleRequested() ? topRunningActivity() : r; if (handleTopActivityLaunchingInDifferentOrientation( topCandidate, r, true /* checkOpening */)) { // Display orientation should be deferred until the top fixed rotation is finished. @@ -2660,7 +2660,7 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp mWmService.mWindowsChanged = true; // If the transition finished callback cannot match the token for some reason, make sure the // rotated state is cleared if it is already invisible. - if (mFixedRotationLaunchingApp != null && !mFixedRotationLaunchingApp.mVisibleRequested + if (mFixedRotationLaunchingApp != null && !mFixedRotationLaunchingApp.isVisibleRequested() && !mFixedRotationLaunchingApp.isVisible() && !mDisplayRotation.isRotatingSeamlessly()) { clearFixedRotationLaunchingApp(); diff --git a/services/core/java/com/android/server/wm/EnsureActivitiesVisibleHelper.java b/services/core/java/com/android/server/wm/EnsureActivitiesVisibleHelper.java index 7bb036d0b1e0..bd837940dfb2 100644 --- a/services/core/java/com/android/server/wm/EnsureActivitiesVisibleHelper.java +++ b/services/core/java/com/android/server/wm/EnsureActivitiesVisibleHelper.java @@ -191,7 +191,7 @@ class EnsureActivitiesVisibleHelper { if (!r.attachedToProcess()) { makeVisibleAndRestartIfNeeded(mStarting, mConfigChanges, isTop, resumeTopActivity && isTop, r); - } else if (r.mVisibleRequested) { + } else if (r.isVisibleRequested()) { // If this activity is already visible, then there is nothing to do here. if (DEBUG_VISIBILITY) { Slog.v(TAG_VISIBILITY, "Skipping: already visible at " + r); @@ -244,7 +244,7 @@ class EnsureActivitiesVisibleHelper { // invisible. If the app is already visible, it must have died while it was visible. In this // case, we'll show the dead window but will not restart the app. Otherwise we could end up // thrashing. - if (!isTop && r.mVisibleRequested && !r.isState(INITIALIZING)) { + if (!isTop && r.isVisibleRequested() && !r.isState(INITIALIZING)) { return; } @@ -256,7 +256,7 @@ class EnsureActivitiesVisibleHelper { if (r != starting) { r.startFreezingScreenLocked(configChanges); } - if (!r.mVisibleRequested || r.mLaunchTaskBehind) { + if (!r.isVisibleRequested() || r.mLaunchTaskBehind) { if (DEBUG_VISIBILITY) { Slog.v(TAG_VISIBILITY, "Starting and making visible: " + r); } diff --git a/services/core/java/com/android/server/wm/LetterboxUiController.java b/services/core/java/com/android/server/wm/LetterboxUiController.java index 76ee733cbb27..bcea6f4db1dc 100644 --- a/services/core/java/com/android/server/wm/LetterboxUiController.java +++ b/services/core/java/com/android/server/wm/LetterboxUiController.java @@ -576,9 +576,8 @@ final class LetterboxUiController { // Rounded corners should be displayed above the taskbar. bounds.bottom = Math.min(bounds.bottom, getTaskbarInsetsSource(mainWindow).getFrame().top); - if (mActivityRecord.inSizeCompatMode() - && mActivityRecord.getSizeCompatScale() < 1.0f) { - bounds.scale(1.0f / mActivityRecord.getSizeCompatScale()); + if (mActivityRecord.inSizeCompatMode() && mActivityRecord.getCompatScale() < 1.0f) { + bounds.scale(1.0f / mActivityRecord.getCompatScale()); } } diff --git a/services/core/java/com/android/server/wm/RecentsAnimation.java b/services/core/java/com/android/server/wm/RecentsAnimation.java index ffe3374e6658..be9058840492 100644 --- a/services/core/java/com/android/server/wm/RecentsAnimation.java +++ b/services/core/java/com/android/server/wm/RecentsAnimation.java @@ -112,7 +112,7 @@ class RecentsAnimation implements RecentsAnimationCallbacks, OnRootTaskOrderChan mTargetActivityType); ActivityRecord targetActivity = getTargetActivity(targetRootTask); if (targetActivity != null) { - if (targetActivity.mVisibleRequested || targetActivity.isTopRunningActivity()) { + if (targetActivity.isVisibleRequested() || targetActivity.isTopRunningActivity()) { // The activity is ready. return; } @@ -195,7 +195,7 @@ class RecentsAnimation implements RecentsAnimationCallbacks, OnRootTaskOrderChan // Send launch hint if we are actually launching the target. If it's already visible // (shouldn't happen in general) we don't need to send it. - if (targetActivity == null || !targetActivity.mVisibleRequested) { + if (targetActivity == null || !targetActivity.isVisibleRequested()) { mService.mRootWindowContainer.startPowerModeLaunchIfNeeded( true /* forceSend */, targetActivity); } diff --git a/services/core/java/com/android/server/wm/RootWindowContainer.java b/services/core/java/com/android/server/wm/RootWindowContainer.java index 6c6fbee885f2..2507a8d5af3a 100644 --- a/services/core/java/com/android/server/wm/RootWindowContainer.java +++ b/services/core/java/com/android/server/wm/RootWindowContainer.java @@ -2626,7 +2626,7 @@ class RootWindowContainer extends WindowContainer<DisplayContent> final ArrayList<Task> addedTasks = new ArrayList<>(); forAllActivities((r) -> { final Task task = r.getTask(); - if (r.mVisibleRequested && r.mStartingData == null && !addedTasks.contains(task)) { + if (r.isVisibleRequested() && r.mStartingData == null && !addedTasks.contains(task)) { r.showStartingWindow(true /*taskSwitch*/); addedTasks.add(task); } @@ -2651,7 +2651,7 @@ class RootWindowContainer extends WindowContainer<DisplayContent> forAllLeafTasks(task -> { final int oldRank = task.mLayerRank; final ActivityRecord r = task.topRunningActivityLocked(); - if (r != null && r.mVisibleRequested) { + if (r != null && r.isVisibleRequested()) { task.mLayerRank = ++mTmpTaskLayerRank; } else { task.mLayerRank = Task.LAYER_RANK_INVISIBLE; diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java index 51eec03855a5..c2604f1476c4 100644 --- a/services/core/java/com/android/server/wm/Task.java +++ b/services/core/java/com/android/server/wm/Task.java @@ -1930,7 +1930,6 @@ class Task extends TaskFragment { mTaskSupervisor.scheduleUpdateMultiWindowMode(this); } - final int newWinMode = getWindowingMode(); if (shouldStartChangeTransition(prevWinMode, mTmpPrevBounds)) { initializeChangeTransition(mTmpPrevBounds); } @@ -1944,16 +1943,15 @@ class Task extends TaskFragment { } } - if (pipChanging && wasInPictureInPicture) { + if (pipChanging && wasInPictureInPicture + && !mTransitionController.isShellTransitionsEnabled()) { // If the top activity is changing from PiP to fullscreen with fixed rotation, // clear the crop and rotation matrix of task because fixed rotation will handle // the transformation on activity level. This also avoids flickering caused by the // latency of fullscreen task organizer configuring the surface. final ActivityRecord r = topRunningActivity(); if (r != null && mDisplayContent.isFixedRotationLaunchingApp(r)) { - getSyncTransaction().setWindowCrop(mSurfaceControl, null) - .setCornerRadius(mSurfaceControl, 0f) - .setMatrix(mSurfaceControl, Matrix.IDENTITY_MATRIX, new float[9]); + resetSurfaceControlTransforms(); } } @@ -2474,7 +2472,7 @@ class Task extends TaskFragment { final String myReason = reason + " adjustFocusToNextFocusableTask"; final ActivityRecord top = focusableTask.topRunningActivity(); - if (focusableTask.isActivityTypeHome() && (top == null || !top.mVisibleRequested)) { + if (focusableTask.isActivityTypeHome() && (top == null || !top.isVisibleRequested())) { // If we will be focusing on the root home task next and its current top activity isn't // visible, then use the move the root home task to top to make the activity visible. focusableTask.getDisplayArea().moveHomeActivityToTop(myReason); @@ -2786,7 +2784,7 @@ class Task extends TaskFragment { */ private static void getMaxVisibleBounds(ActivityRecord token, Rect out, boolean[] foundTop) { // skip hidden (or about to hide) apps - if (token.mIsExiting || !token.isClientVisible() || !token.mVisibleRequested) { + if (token.mIsExiting || !token.isClientVisible() || !token.isVisibleRequested()) { return; } final WindowState win = token.findMainWindow(); @@ -3098,7 +3096,7 @@ class Task extends TaskFragment { * this activity. */ ActivityRecord getTopVisibleActivity() { - return getActivity((r) -> !r.mIsExiting && r.isClientVisible() && r.mVisibleRequested); + return getActivity((r) -> !r.mIsExiting && r.isClientVisible() && r.isVisibleRequested()); } /** @@ -5806,7 +5804,7 @@ class Task extends TaskFragment { ActivityRecord r, ActivityRecord starting, String packageName) { if (r.info.packageName.equals(packageName)) { r.forceNewConfig = true; - if (starting != null && r == starting && r.mVisibleRequested) { + if (starting != null && r == starting && r.isVisibleRequested()) { r.startFreezingScreenLocked(CONFIG_SCREEN_LAYOUT); } } diff --git a/services/core/java/com/android/server/wm/TaskFragment.java b/services/core/java/com/android/server/wm/TaskFragment.java index 454f338045d1..ae4f8947cc86 100644 --- a/services/core/java/com/android/server/wm/TaskFragment.java +++ b/services/core/java/com/android/server/wm/TaskFragment.java @@ -352,7 +352,7 @@ class TaskFragment extends WindowContainer<WindowContainer> { } void process(ActivityRecord start, boolean preserveWindow) { - if (start == null || !start.mVisibleRequested) { + if (start == null || !start.isVisibleRequested()) { return; } reset(preserveWindow); @@ -1363,7 +1363,7 @@ class TaskFragment extends WindowContainer<WindowContainer> { if (next.attachedToProcess()) { if (DEBUG_SWITCH) { Slog.v(TAG_SWITCH, "Resume running: " + next + " stopped=" + next.stopped - + " visibleRequested=" + next.mVisibleRequested); + + " visibleRequested=" + next.isVisibleRequested()); } // If the previous activity is translucent, force a visibility update of @@ -1377,7 +1377,7 @@ class TaskFragment extends WindowContainer<WindowContainer> { || mLastPausedActivity != null && !mLastPausedActivity.occludesParent(); // This activity is now becoming visible. - if (!next.mVisibleRequested || next.stopped || lastActivityTranslucent) { + if (!next.isVisibleRequested() || next.stopped || lastActivityTranslucent) { next.app.addToPendingTop(); next.setVisibility(true); } @@ -1428,7 +1428,7 @@ class TaskFragment extends WindowContainer<WindowContainer> { // Do over! mTaskSupervisor.scheduleResumeTopActivities(); } - if (!next.mVisibleRequested || next.stopped) { + if (!next.isVisibleRequested() || next.stopped) { next.setVisibility(true); } next.completeResumeLocked(); @@ -1739,7 +1739,7 @@ class TaskFragment extends WindowContainer<WindowContainer> { } else if (prev.hasProcess()) { ProtoLog.v(WM_DEBUG_STATES, "Enqueue pending stop if needed: %s " + "wasStopping=%b visibleRequested=%b", prev, wasStopping, - prev.mVisibleRequested); + prev.isVisibleRequested()); if (prev.deferRelaunchUntilPaused) { // Complete the deferred relaunch that was waiting for pause to complete. ProtoLog.v(WM_DEBUG_STATES, "Re-launching after pause: %s", prev); @@ -1749,7 +1749,7 @@ class TaskFragment extends WindowContainer<WindowContainer> { // We can't clobber it, because the stop confirmation will not be handled. // We don't need to schedule another stop, we only need to let it happen. prev.setState(STOPPING, "completePausedLocked"); - } else if (!prev.mVisibleRequested || shouldSleepOrShutDownActivities()) { + } else if (!prev.isVisibleRequested() || shouldSleepOrShutDownActivities()) { // Clear out any deferred client hide we might currently have. prev.setDeferHidingClient(false); // If we were visible then resumeTopActivities will release resources before diff --git a/services/core/java/com/android/server/wm/Transition.java b/services/core/java/com/android/server/wm/Transition.java index 6b14dba6eb81..c874747b0b5a 100644 --- a/services/core/java/com/android/server/wm/Transition.java +++ b/services/core/java/com/android/server/wm/Transition.java @@ -643,7 +643,7 @@ class Transition implements BLASTSyncEngine.TransactionReadyListener { // No need to clip the display in case seeing the clipped content when during the // display rotation. No need to clip activities because they rely on clipping on // task layers. - if (target.asDisplayContent() != null || target.asActivityRecord() != null) { + if (target.asTaskFragment() == null) { t.setCrop(targetLeash, null /* crop */); } else { // Crop to the resolved override bounds. @@ -993,7 +993,7 @@ class Transition implements BLASTSyncEngine.TransactionReadyListener { // show here in the same way that we manually hide in finishTransaction. for (int i = mParticipants.size() - 1; i >= 0; --i) { final ActivityRecord ar = mParticipants.valueAt(i).asActivityRecord(); - if (ar == null || !ar.mVisibleRequested) continue; + if (ar == null || !ar.isVisibleRequested()) continue; transaction.show(ar.getSurfaceControl()); // Also manually show any non-reported parents. This is necessary in a few cases @@ -1274,7 +1274,7 @@ class Transition implements BLASTSyncEngine.TransactionReadyListener { ArrayMap<WindowContainer, Integer> reasons = new ArrayMap<>(); for (int i = mParticipants.size() - 1; i >= 0; --i) { ActivityRecord r = mParticipants.valueAt(i).asActivityRecord(); - if (r == null || !r.mVisibleRequested) continue; + if (r == null || !r.isVisibleRequested()) continue; int transitionReason = APP_TRANSITION_WINDOWS_DRAWN; // At this point, r is "ready", but if it's not "ALL ready" then it is probably only // ready due to starting-window. diff --git a/services/core/java/com/android/server/wm/WallpaperController.java b/services/core/java/com/android/server/wm/WallpaperController.java index 920b1bad48d1..1d25dbc0d533 100644 --- a/services/core/java/com/android/server/wm/WallpaperController.java +++ b/services/core/java/com/android/server/wm/WallpaperController.java @@ -726,9 +726,9 @@ class WallpaperController { } final boolean newTargetHidden = wallpaperTarget.mActivityRecord != null - && !wallpaperTarget.mActivityRecord.mVisibleRequested; + && !wallpaperTarget.mActivityRecord.isVisibleRequested(); final boolean oldTargetHidden = prevWallpaperTarget.mActivityRecord != null - && !prevWallpaperTarget.mActivityRecord.mVisibleRequested; + && !prevWallpaperTarget.mActivityRecord.isVisibleRequested(); ProtoLog.v(WM_DEBUG_WALLPAPER, "Animating wallpapers: " + "old: %s hidden=%b new: %s hidden=%b", diff --git a/services/core/java/com/android/server/wm/WallpaperWindowToken.java b/services/core/java/com/android/server/wm/WallpaperWindowToken.java index 8fdaec613ad5..bf6c4fb7220a 100644 --- a/services/core/java/com/android/server/wm/WallpaperWindowToken.java +++ b/services/core/java/com/android/server/wm/WallpaperWindowToken.java @@ -139,7 +139,7 @@ class WallpaperWindowToken extends WindowToken { bac.linkFixedRotationTransformIfNeeded(this); } else if ((wallpaperTarget.mActivityRecord == null // Ignore invisible activity because it may be moving to background. - || wallpaperTarget.mActivityRecord.mVisibleRequested) + || wallpaperTarget.mActivityRecord.isVisibleRequested()) && wallpaperTarget.mToken.hasFixedRotationTransform()) { // If the wallpaper target has a fixed rotation, we want the wallpaper to follow its // rotation diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java index 68b853dd2695..b3a7754bc2d5 100644 --- a/services/core/java/com/android/server/wm/WindowManagerService.java +++ b/services/core/java/com/android/server/wm/WindowManagerService.java @@ -1883,7 +1883,7 @@ public class WindowManagerService extends IWindowManager.Stub // Make this invalid which indicates a null attached frame. outAttachedFrame.set(0, 0, -1, -1); } - outSizeCompatScale[0] = win.getSizeCompatScaleForClient(); + outSizeCompatScale[0] = win.getCompatScaleForClient(); } Binder.restoreCallingIdentity(origId); @@ -8866,7 +8866,7 @@ public class WindowManagerService extends IWindowManager.Stub outInsetsState.set(state, true /* copySources */); if (WindowState.hasCompatScale(attrs, token, overrideScale)) { final float compatScale = token != null && token.hasSizeCompatBounds() - ? token.getSizeCompatScale() * overrideScale + ? token.getCompatScale() * overrideScale : overrideScale; outInsetsState.scale(1f / compatScale); } diff --git a/services/core/java/com/android/server/wm/WindowOrganizerController.java b/services/core/java/com/android/server/wm/WindowOrganizerController.java index dc1d50c435b9..cd777092d438 100644 --- a/services/core/java/com/android/server/wm/WindowOrganizerController.java +++ b/services/core/java/com/android/server/wm/WindowOrganizerController.java @@ -1891,7 +1891,18 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub // actions. taskFragment.setTaskFragmentOrganizer(creationParams.getOrganizer(), ownerActivity.getUid(), ownerActivity.info.processName); - ownerTask.addChild(taskFragment, POSITION_TOP); + final int position; + if (creationParams.getPairedPrimaryFragmentToken() != null) { + // When there is a paired primary TaskFragment, we want to place the new TaskFragment + // right above the paired one to make sure there is no other window in between. + final TaskFragment pairedPrimaryTaskFragment = getTaskFragment( + creationParams.getPairedPrimaryFragmentToken()); + final int pairedPosition = ownerTask.mChildren.indexOf(pairedPrimaryTaskFragment); + position = pairedPosition != -1 ? pairedPosition + 1 : POSITION_TOP; + } else { + position = POSITION_TOP; + } + ownerTask.addChild(taskFragment, position); taskFragment.setWindowingMode(creationParams.getWindowingMode()); taskFragment.setBounds(creationParams.getInitialBounds()); mLaunchTaskFragments.put(creationParams.getFragmentToken(), taskFragment); diff --git a/services/core/java/com/android/server/wm/WindowProcessController.java b/services/core/java/com/android/server/wm/WindowProcessController.java index 202fe558f938..682918b1e7dd 100644 --- a/services/core/java/com/android/server/wm/WindowProcessController.java +++ b/services/core/java/com/android/server/wm/WindowProcessController.java @@ -751,7 +751,7 @@ public class WindowProcessController extends ConfigurationContainer<Configuratio // - no longer visible OR // - not focusable (in PiP mode for instance) if (topDisplay == null - || !mPreQTopResumedActivity.mVisibleRequested + || !mPreQTopResumedActivity.isVisibleRequested() || !mPreQTopResumedActivity.isFocusable()) { canUpdate = true; } @@ -860,7 +860,7 @@ public class WindowProcessController extends ConfigurationContainer<Configuratio // to those activities that are part of the package whose app-specific settings changed if (packageName.equals(r.packageName) && r.applyAppSpecificConfig(nightMode, localesOverride) - && r.mVisibleRequested) { + && r.isVisibleRequested()) { r.ensureActivityConfiguration(0 /* globalChanges */, true /* preserveWindow */); } } @@ -942,7 +942,7 @@ public class WindowProcessController extends ConfigurationContainer<Configuratio } // Don't consider any activities that are currently not in a state where they // can be destroyed. - if (r.mVisibleRequested || !r.stopped || !r.hasSavedState() || !r.isDestroyable() + if (r.isVisibleRequested() || !r.stopped || !r.hasSavedState() || !r.isDestroyable() || r.isState(STARTED, RESUMED, PAUSING, PAUSED, STOPPING)) { if (DEBUG_RELEASE) Slog.d(TAG_RELEASE, "Not releasing in-use activity: " + r); continue; @@ -988,7 +988,7 @@ public class WindowProcessController extends ConfigurationContainer<Configuratio final int displayId = r.getDisplayId(); final Context c = root.getDisplayUiContext(displayId); - if (c != null && r.mVisibleRequested && !displayContexts.contains(c)) { + if (c != null && r.isVisibleRequested() && !displayContexts.contains(c)) { displayContexts.add(c); } } @@ -1056,7 +1056,7 @@ public class WindowProcessController extends ConfigurationContainer<Configuratio if (task != null && task.mLayerRank != Task.LAYER_RANK_INVISIBLE) { stateFlags |= ACTIVITY_STATE_FLAG_HAS_ACTIVITY_IN_VISIBLE_TASK; } - if (r.mVisibleRequested) { + if (r.isVisibleRequested()) { if (r.isState(RESUMED)) { stateFlags |= ACTIVITY_STATE_FLAG_HAS_RESUMED; } @@ -1268,7 +1268,7 @@ public class WindowProcessController extends ConfigurationContainer<Configuratio } for (int i = activities.size() - 1; i >= 0; i--) { final ActivityRecord r = activities.get(i); - if (r.mVisibleRequested || r.isVisible()) { + if (r.isVisibleRequested() || r.isVisible()) { // While an activity launches a new activity, it's possible that the old activity // is already requested to be hidden (mVisibleRequested=false), but this visibility // is not yet committed, so isVisible()=true. @@ -1489,7 +1489,7 @@ public class WindowProcessController extends ConfigurationContainer<Configuratio Configuration overrideConfig = new Configuration(r.getRequestedOverrideConfiguration()); overrideConfig.assetsSeq = assetSeq; r.onRequestedOverrideConfigurationChanged(overrideConfig); - if (r.mVisibleRequested) { + if (r.isVisibleRequested()) { r.ensureActivityConfiguration(0, true); } } diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java index 45606f965858..2f55d1824c07 100644 --- a/services/core/java/com/android/server/wm/WindowState.java +++ b/services/core/java/com/android/server/wm/WindowState.java @@ -475,7 +475,7 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP // Current transformation being applied. float mGlobalScale = 1f; float mInvGlobalScale = 1f; - float mSizeCompatScale = 1f; + float mCompatScale = 1f; final float mOverrideScale; float mHScale = 1f, mVScale = 1f; float mLastHScale = 1f, mLastVScale = 1f; @@ -1254,21 +1254,21 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP void updateGlobalScale() { if (hasCompatScale()) { - mSizeCompatScale = (mOverrideScale == 1f || mToken.hasSizeCompatBounds()) - ? mToken.getSizeCompatScale() + mCompatScale = (mOverrideScale == 1f || mToken.hasSizeCompatBounds()) + ? mToken.getCompatScale() : 1f; - mGlobalScale = mSizeCompatScale * mOverrideScale; + mGlobalScale = mCompatScale * mOverrideScale; mInvGlobalScale = 1f / mGlobalScale; return; } - mGlobalScale = mInvGlobalScale = mSizeCompatScale = 1f; + mGlobalScale = mInvGlobalScale = mCompatScale = 1f; } - float getSizeCompatScaleForClient() { - // If the size compat scale is because of the size compat bounds, we only scale down its - // coordinates at the server side without letting the client know. - return mToken.hasSizeCompatBounds() ? 1f : mSizeCompatScale; + float getCompatScaleForClient() { + // If this window in the size compat mode. The scaling is fully controlled at the server + // side. The client doesn't need to take it into account. + return mToken.hasSizeCompatBounds() ? 1f : mCompatScale; } /** @@ -1956,7 +1956,7 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP */ // TODO: Can we consolidate this with #isVisible() or have a more appropriate name for this? boolean isWinVisibleLw() { - return (mActivityRecord == null || mActivityRecord.mVisibleRequested + return (mActivityRecord == null || mActivityRecord.isVisibleRequested() || mActivityRecord.isAnimating(TRANSITION | PARENTS)) && isVisible(); } @@ -1993,7 +1993,7 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP final ActivityRecord atoken = mActivityRecord; return (mHasSurface || (!mRelayoutCalled && mViewVisibility == View.VISIBLE)) && isVisibleByPolicy() && !isParentWindowHidden() - && (atoken == null || atoken.mVisibleRequested) + && (atoken == null || atoken.isVisibleRequested()) && !mAnimatingExit && !mDestroying; } @@ -2100,7 +2100,7 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP boolean isDisplayed() { final ActivityRecord atoken = mActivityRecord; return isDrawn() && isVisibleByPolicy() - && ((!isParentWindowHidden() && (atoken == null || atoken.mVisibleRequested)) + && ((!isParentWindowHidden() && (atoken == null || atoken.isVisibleRequested())) || isAnimating(TRANSITION | PARENTS)); } @@ -2122,7 +2122,7 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP // a layout since they can request relayout when client visibility is false. // TODO (b/157682066) investigate if we can clean up isVisible || (atoken == null && !(wouldBeVisibleIfPolicyIgnored() && isVisibleByPolicy())) - || (atoken != null && !atoken.mVisibleRequested) + || (atoken != null && !atoken.isVisibleRequested()) || isParentWindowGoneForLayout() || (mAnimatingExit && !isAnimatingLw()) || mDestroying; @@ -2169,7 +2169,7 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP return; } if (mActivityRecord != null) { - if (!mActivityRecord.mVisibleRequested) return; + if (!mActivityRecord.isVisibleRequested()) return; if (mActivityRecord.allDrawn) { // The allDrawn of activity is reset when the visibility is changed to visible, so // the content should be ready if allDrawn is set. @@ -2742,7 +2742,7 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP + " exiting=" + mAnimatingExit + " destroying=" + mDestroying); if (mActivityRecord != null) { Slog.i(TAG_WM, " mActivityRecord.visibleRequested=" - + mActivityRecord.mVisibleRequested); + + mActivityRecord.isVisibleRequested()); } } } @@ -3218,7 +3218,7 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP } return !mActivityRecord.getTask().getRootTask().shouldIgnoreInput() - && mActivityRecord.mVisibleRequested; + && mActivityRecord.isVisibleRequested(); } /** @@ -3867,14 +3867,14 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP } } - outFrames.sizeCompatScale = getSizeCompatScaleForClient(); + outFrames.compatScale = getCompatScaleForClient(); // Note: in the cases where the window is tied to an activity, we should not send a // configuration update when the window has requested to be hidden. Doing so can lead to // the client erroneously accepting a configuration that would have otherwise caused an // activity restart. We instead hand back the last reported {@link MergedConfiguration}. if (useLatestConfig || (relayoutVisible && (mActivityRecord == null - || mActivityRecord.mVisibleRequested))) { + || mActivityRecord.isVisibleRequested()))) { final Configuration globalConfig = getProcessGlobalConfiguration(); final Configuration overrideConfig = getMergedOverrideConfiguration(); outMergedConfiguration.setConfiguration(globalConfig, overrideConfig); @@ -4722,7 +4722,7 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP + " during animation: policyVis=" + isVisibleByPolicy() + " parentHidden=" + isParentWindowHidden() + " tok.visibleRequested=" - + (mActivityRecord != null && mActivityRecord.mVisibleRequested) + + (mActivityRecord != null && mActivityRecord.isVisibleRequested()) + " tok.visible=" + (mActivityRecord != null && mActivityRecord.isVisible()) + " animating=" + isAnimating(TRANSITION | PARENTS) + " tok animating=" @@ -5195,7 +5195,7 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP + " pv=" + isVisibleByPolicy() + " mDrawState=" + mWinAnimator.mDrawState + " ph=" + isParentWindowHidden() - + " th=" + (mActivityRecord != null && mActivityRecord.mVisibleRequested) + + " th=" + (mActivityRecord != null && mActivityRecord.isVisibleRequested()) + " a=" + isAnimating(TRANSITION | PARENTS)); } } diff --git a/services/core/java/com/android/server/wm/WindowToken.java b/services/core/java/com/android/server/wm/WindowToken.java index 805559035ef9..e6323315db4a 100644 --- a/services/core/java/com/android/server/wm/WindowToken.java +++ b/services/core/java/com/android/server/wm/WindowToken.java @@ -258,7 +258,7 @@ class WindowToken extends WindowContainer<WindowState> { * @return The scale for applications running in compatibility mode. Multiply the size in the * application by this scale will be the size in the screen. */ - float getSizeCompatScale() { + float getCompatScale() { return mDisplayContent.mCompatibleScreenScale; } @@ -588,9 +588,7 @@ class WindowToken extends WindowContainer<WindowState> { .setCallsite("WindowToken.getOrCreateFixedRotationLeash") .build(); t.setPosition(leash, mLastSurfacePosition.x, mLastSurfacePosition.y); - t.show(leash); t.reparent(getSurfaceControl(), leash); - t.setAlpha(getSurfaceControl(), 1.f); mFixedRotationTransformLeash = leash; updateSurfaceRotation(t, rotation, mFixedRotationTransformLeash); return mFixedRotationTransformLeash; diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityMetricsLaunchObserverTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityMetricsLaunchObserverTests.java index 5b909a343a59..14eeaa57ddd7 100644 --- a/services/tests/wmtests/src/com/android/server/wm/ActivityMetricsLaunchObserverTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/ActivityMetricsLaunchObserverTests.java @@ -99,7 +99,7 @@ public class ActivityMetricsLaunchObserverTests extends WindowTestsBase { .setComponent(createRelative(DEFAULT_COMPONENT_PACKAGE_NAME, "TopActivity")) .build(); // becomes invisible when covered by mTopActivity - mTrampolineActivity.mVisibleRequested = false; + mTrampolineActivity.setVisibleRequested(false); } private <T> T verifyAsync(T mock) { @@ -228,7 +228,7 @@ public class ActivityMetricsLaunchObserverTests extends WindowTestsBase { public void testOnActivityLaunchCancelled_hasDrawn() { onActivityLaunched(mTopActivity); - mTopActivity.mVisibleRequested = true; + mTopActivity.setVisibleRequested(true); doReturn(true).when(mTopActivity).isReportedDrawn(); // Cannot time already-visible activities. @@ -251,7 +251,7 @@ public class ActivityMetricsLaunchObserverTests extends WindowTestsBase { notifyActivityLaunching(noDrawnActivity.intent); notifyAndVerifyActivityLaunched(noDrawnActivity); - noDrawnActivity.mVisibleRequested = false; + noDrawnActivity.setVisibleRequested(false); mActivityMetricsLogger.notifyVisibilityChanged(noDrawnActivity); verifyAsync(mLaunchObserver).onActivityLaunchCancelled(eqLastStartedId(noDrawnActivity)); @@ -279,7 +279,7 @@ public class ActivityMetricsLaunchObserverTests extends WindowTestsBase { clearInvocations(mLaunchObserver); mLaunchTopByTrampoline = true; - mTopActivity.mVisibleRequested = false; + mTopActivity.setVisibleRequested(false); notifyActivityLaunching(mTopActivity.intent); // It should schedule a message with UNKNOWN_VISIBILITY_CHECK_DELAY_MS to check whether // the launch event is still valid. @@ -307,7 +307,7 @@ public class ActivityMetricsLaunchObserverTests extends WindowTestsBase { // Create an invisible event that should be cancelled after the next event starts. final ActivityRecord prev = new ActivityBuilder(mAtm).setCreateTask(true).build(); onActivityLaunched(prev); - prev.mVisibleRequested = false; + prev.setVisibleRequested(false); mActivityOptions = ActivityOptions.makeBasic(); mActivityOptions.setSourceInfo(SourceInfo.TYPE_LAUNCHER, SystemClock.uptimeMillis() - 10); @@ -540,7 +540,7 @@ public class ActivityMetricsLaunchObserverTests extends WindowTestsBase { @Test public void testConsecutiveLaunchWithDifferentWindowingMode() { mTopActivity.setWindowingMode(WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW); - mTrampolineActivity.mVisibleRequested = true; + mTrampolineActivity.setVisibleRequested(true); onActivityLaunched(mTrampolineActivity); mActivityMetricsLogger.notifyActivityLaunching(mTopActivity.intent, mTrampolineActivity /* caller */, mTrampolineActivity.getUid()); diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java index 53e0b2b7a615..17ec19d3e617 100644 --- a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java @@ -917,7 +917,7 @@ public class ActivityRecordTests extends WindowTestsBase { // Prepare the activity record to be ready for immediate removal. It should be invisible and // have no process. Otherwise, request to finish it will send a message to client first. activity.setState(STOPPED, "test"); - activity.mVisibleRequested = false; + activity.setVisibleRequested(false); activity.nowVisible = false; // Set process to 'null' to allow immediate removal, but don't call mActivity.setProcess() - // this will cause NPE when updating task's process. @@ -927,7 +927,7 @@ public class ActivityRecordTests extends WindowTestsBase { // next activity reports idle to destroy it. final ActivityRecord topActivity = new ActivityBuilder(mAtm) .setTask(activity.getTask()).build(); - topActivity.mVisibleRequested = true; + topActivity.setVisibleRequested(true); topActivity.nowVisible = true; topActivity.setState(RESUMED, "test"); @@ -1082,7 +1082,7 @@ public class ActivityRecordTests extends WindowTestsBase { final ActivityRecord activity = createActivityWithTask(); clearInvocations(activity.mDisplayContent); activity.finishing = false; - activity.mVisibleRequested = true; + activity.setVisibleRequested(true); activity.setState(RESUMED, "test"); activity.finishIfPossible("test", false /* oomAdj */); @@ -1099,7 +1099,7 @@ public class ActivityRecordTests extends WindowTestsBase { final ActivityRecord activity = createActivityWithTask(); clearInvocations(activity.mDisplayContent); activity.finishing = false; - activity.mVisibleRequested = true; + activity.setVisibleRequested(true); activity.setState(PAUSED, "test"); activity.finishIfPossible("test", false /* oomAdj */); @@ -1118,7 +1118,7 @@ public class ActivityRecordTests extends WindowTestsBase { // Put an activity on top of test activity to make it invisible and prevent us from // accidentally resuming the topmost one again. new ActivityBuilder(mAtm).build(); - activity.mVisibleRequested = false; + activity.setVisibleRequested(false); activity.setState(STOPPED, "test"); activity.finishIfPossible("test", false /* oomAdj */); @@ -1136,7 +1136,7 @@ public class ActivityRecordTests extends WindowTestsBase { final TestTransitionPlayer testPlayer = registerTestTransitionPlayer(); final ActivityRecord activity = createActivityWithTask(); activity.finishing = false; - activity.mVisibleRequested = true; + activity.setVisibleRequested(true); activity.setState(RESUMED, "test"); activity.finishIfPossible("test", false /* oomAdj */); @@ -1273,7 +1273,7 @@ public class ActivityRecordTests extends WindowTestsBase { final ActivityRecord currentTop = createActivityWithTask(); final Task task = currentTop.getTask(); - currentTop.mVisibleRequested = currentTop.nowVisible = true; + currentTop.setVisibleRequested(currentTop.nowVisible = true); // Simulates that {@code currentTop} starts an existing activity from background (so its // state is stopped) and the starting flow just goes to place it at top. @@ -1300,7 +1300,7 @@ public class ActivityRecordTests extends WindowTestsBase { final ActivityRecord bottomActivity = createActivityWithTask(); final ActivityRecord topActivity = new ActivityBuilder(mAtm) .setTask(bottomActivity.getTask()).build(); - topActivity.mVisibleRequested = true; + topActivity.setVisibleRequested(true); // simulating bottomActivity as a trampoline activity. bottomActivity.setState(RESUMED, "test"); bottomActivity.finishIfPossible("test", false); @@ -1316,13 +1316,13 @@ public class ActivityRecordTests extends WindowTestsBase { final ActivityRecord activity = createActivityWithTask(); final ActivityRecord topActivity = new ActivityBuilder(mAtm) .setTask(activity.getTask()).build(); - topActivity.mVisibleRequested = true; + topActivity.setVisibleRequested(true); topActivity.nowVisible = true; topActivity.finishing = true; topActivity.setState(PAUSED, "true"); // Mark the bottom activity as not visible, so that we will wait for it before removing // the top one. - activity.mVisibleRequested = false; + activity.setVisibleRequested(false); activity.nowVisible = false; activity.setState(STOPPED, "test"); @@ -1346,13 +1346,13 @@ public class ActivityRecordTests extends WindowTestsBase { final ActivityRecord topActivity = createActivityWithTask(); mDisplayContent.setIsSleeping(true); doReturn(true).when(activity).shouldBeVisible(); - topActivity.mVisibleRequested = false; + topActivity.setVisibleRequested(false); topActivity.nowVisible = false; topActivity.finishing = true; topActivity.setState(STOPPED, "true"); // Mark the activity behind (on a separate task) as not visible - activity.mVisibleRequested = false; + activity.setVisibleRequested(false); activity.nowVisible = false; activity.setState(STOPPED, "test"); @@ -1370,13 +1370,13 @@ public class ActivityRecordTests extends WindowTestsBase { final ActivityRecord activity = createActivityWithTask(); final ActivityRecord topActivity = new ActivityBuilder(mAtm) .setTask(activity.getTask()).build(); - topActivity.mVisibleRequested = false; + topActivity.setVisibleRequested(false); topActivity.nowVisible = false; topActivity.finishing = true; topActivity.setState(STOPPED, "true"); // Mark the bottom activity as not visible, so that we would wait for it before removing // the top one. - activity.mVisibleRequested = false; + activity.setVisibleRequested(false); activity.nowVisible = false; activity.setState(STOPPED, "test"); @@ -1394,12 +1394,12 @@ public class ActivityRecordTests extends WindowTestsBase { final ActivityRecord activity = createActivityWithTask(); final ActivityRecord topActivity = new ActivityBuilder(mAtm) .setTask(activity.getTask()).build(); - topActivity.mVisibleRequested = true; + topActivity.setVisibleRequested(true); topActivity.nowVisible = true; topActivity.finishing = true; topActivity.setState(PAUSED, "true"); // Mark the bottom activity as already visible, so that there is no need to wait for it. - activity.mVisibleRequested = true; + activity.setVisibleRequested(true); activity.nowVisible = true; activity.setState(RESUMED, "test"); @@ -1417,12 +1417,12 @@ public class ActivityRecordTests extends WindowTestsBase { final ActivityRecord activity = createActivityWithTask(); final ActivityRecord topActivity = new ActivityBuilder(mAtm) .setTask(activity.getTask()).build(); - topActivity.mVisibleRequested = false; + topActivity.setVisibleRequested(false); topActivity.nowVisible = false; topActivity.finishing = true; topActivity.setState(STOPPED, "true"); // Mark the bottom activity as already visible, so that there is no need to wait for it. - activity.mVisibleRequested = true; + activity.setVisibleRequested(true); activity.nowVisible = true; activity.setState(RESUMED, "test"); @@ -1440,12 +1440,12 @@ public class ActivityRecordTests extends WindowTestsBase { final ActivityRecord activity = createActivityWithTask(); final ActivityRecord topActivity = new ActivityBuilder(mAtm) .setTask(activity.getTask()).build(); - topActivity.mVisibleRequested = true; + topActivity.setVisibleRequested(true); topActivity.nowVisible = true; topActivity.finishing = true; topActivity.setState(PAUSED, "true"); // Mark the bottom activity as already visible, so that there is no need to wait for it. - activity.mVisibleRequested = true; + activity.setVisibleRequested(true); activity.nowVisible = true; activity.setState(RESUMED, "test"); @@ -1454,7 +1454,7 @@ public class ActivityRecordTests extends WindowTestsBase { final Task stack = new TaskBuilder(mSupervisor).setCreateActivity(true).build(); final ActivityRecord focusedActivity = stack.getTopMostActivity(); focusedActivity.nowVisible = true; - focusedActivity.mVisibleRequested = true; + focusedActivity.setVisibleRequested(true); focusedActivity.setState(RESUMED, "test"); stack.setResumedActivity(focusedActivity, "test"); @@ -1476,7 +1476,7 @@ public class ActivityRecordTests extends WindowTestsBase { int displayId = activity.getDisplayId(); doReturn(true).when(keyguardController).isKeyguardLocked(eq(displayId)); final ActivityRecord topActivity = new ActivityBuilder(mAtm).setTask(task).build(); - topActivity.mVisibleRequested = true; + topActivity.setVisibleRequested(true); topActivity.nowVisible = true; topActivity.setState(RESUMED, "true"); doCallRealMethod().when(mRootWindowContainer).ensureActivitiesVisible( @@ -1515,12 +1515,12 @@ public class ActivityRecordTests extends WindowTestsBase { final ActivityRecord activity = createActivityWithTask(); final Task task = activity.getTask(); final ActivityRecord firstActivity = new ActivityBuilder(mAtm).setTask(task).build(); - firstActivity.mVisibleRequested = false; + firstActivity.setVisibleRequested(false); firstActivity.nowVisible = false; firstActivity.setState(STOPPED, "test"); final ActivityRecord secondActivity = new ActivityBuilder(mAtm).setTask(task).build(); - secondActivity.mVisibleRequested = true; + secondActivity.setVisibleRequested(true); secondActivity.nowVisible = true; secondActivity.setState(secondActivityState, "test"); @@ -1530,7 +1530,7 @@ public class ActivityRecordTests extends WindowTestsBase { } else { translucentActivity = new ActivityBuilder(mAtm).setTask(task).build(); } - translucentActivity.mVisibleRequested = true; + translucentActivity.setVisibleRequested(true); translucentActivity.nowVisible = true; translucentActivity.setState(RESUMED, "test"); @@ -1546,7 +1546,7 @@ public class ActivityRecordTests extends WindowTestsBase { // Finish the first activity firstActivity.finishing = true; - firstActivity.mVisibleRequested = true; + firstActivity.setVisibleRequested(true); firstActivity.completeFinishing("test"); verify(firstActivity.mDisplayContent, times(2)).ensureActivitiesVisible(null /* starting */, 0 /* configChanges */ , false /* preserveWindows */, @@ -1614,7 +1614,7 @@ public class ActivityRecordTests extends WindowTestsBase { }, true /* traverseTopToBottom */); activity.setState(STARTED, "test"); activity.finishing = true; - activity.mVisibleRequested = true; + activity.setVisibleRequested(true); // Try to finish the last activity above the home stack. activity.completeFinishing("test"); @@ -1909,7 +1909,7 @@ public class ActivityRecordTests extends WindowTestsBase { // Simulate that the activity requests the same orientation as display. activity.setOrientation(display.getConfiguration().orientation); // Skip the real freezing. - activity.mVisibleRequested = false; + activity.setVisibleRequested(false); clearInvocations(activity); activity.onCancelFixedRotationTransform(originalRotation); // The implementation of cancellation must be executed. @@ -2536,7 +2536,7 @@ public class ActivityRecordTests extends WindowTestsBase { activity.setOccludesParent(true); activity.setVisible(false); - activity.mVisibleRequested = false; + activity.setVisibleRequested(false); // Can not specify orientation if app isn't visible even though it occludes parent. assertEquals(SCREEN_ORIENTATION_UNSET, activity.getOrientation()); // Can specify orientation if the current orientation candidate is orientation behind. @@ -2913,7 +2913,7 @@ public class ActivityRecordTests extends WindowTestsBase { task.addChild(taskFragment2, POSITION_TOP); final ActivityRecord activity2 = new ActivityBuilder(mAtm) .setResizeMode(ActivityInfo.RESIZE_MODE_UNRESIZEABLE).build(); - activity2.mVisibleRequested = true; + activity2.setVisibleRequested(true); taskFragment2.addChild(activity2); assertTrue(activity2.isResizeable()); activity1.reparent(taskFragment1, POSITION_TOP); @@ -3059,7 +3059,7 @@ public class ActivityRecordTests extends WindowTestsBase { .setCreateTask(true).build(); // By default, activity is visible. assertTrue(activity.isVisible()); - assertTrue(activity.mVisibleRequested); + assertTrue(activity.isVisibleRequested()); assertTrue(activity.mDisplayContent.mOpeningApps.contains(activity)); assertFalse(activity.mDisplayContent.mClosingApps.contains(activity)); @@ -3068,7 +3068,7 @@ public class ActivityRecordTests extends WindowTestsBase { // until we verify no logic relies on this behavior, we'll keep this as is. activity.setVisibility(true); assertTrue(activity.isVisible()); - assertTrue(activity.mVisibleRequested); + assertTrue(activity.isVisibleRequested()); assertTrue(activity.mDisplayContent.mOpeningApps.contains(activity)); assertFalse(activity.mDisplayContent.mClosingApps.contains(activity)); } @@ -3079,7 +3079,7 @@ public class ActivityRecordTests extends WindowTestsBase { .setCreateTask(true).build(); // By default, activity is visible. assertTrue(activity.isVisible()); - assertTrue(activity.mVisibleRequested); + assertTrue(activity.isVisibleRequested()); assertTrue(activity.mDisplayContent.mOpeningApps.contains(activity)); assertFalse(activity.mDisplayContent.mClosingApps.contains(activity)); @@ -3087,7 +3087,7 @@ public class ActivityRecordTests extends WindowTestsBase { // animation should be applied on this activity. activity.setVisibility(false); assertTrue(activity.isVisible()); - assertFalse(activity.mVisibleRequested); + assertFalse(activity.isVisibleRequested()); assertFalse(activity.mDisplayContent.mOpeningApps.contains(activity)); assertTrue(activity.mDisplayContent.mClosingApps.contains(activity)); } @@ -3099,7 +3099,7 @@ public class ActivityRecordTests extends WindowTestsBase { // Activiby is invisible. However ATMS requests it to become visible, since this is a top // activity. assertFalse(activity.isVisible()); - assertTrue(activity.mVisibleRequested); + assertTrue(activity.isVisibleRequested()); assertTrue(activity.mDisplayContent.mOpeningApps.contains(activity)); assertFalse(activity.mDisplayContent.mClosingApps.contains(activity)); @@ -3107,7 +3107,7 @@ public class ActivityRecordTests extends WindowTestsBase { // animation should be applied on this activity. activity.setVisibility(true); assertFalse(activity.isVisible()); - assertTrue(activity.mVisibleRequested); + assertTrue(activity.isVisibleRequested()); assertTrue(activity.mDisplayContent.mOpeningApps.contains(activity)); assertFalse(activity.mDisplayContent.mClosingApps.contains(activity)); @@ -3130,7 +3130,7 @@ public class ActivityRecordTests extends WindowTestsBase { // Activiby is invisible. However ATMS requests it to become visible, since this is a top // activity. assertFalse(activity.isVisible()); - assertTrue(activity.mVisibleRequested); + assertTrue(activity.isVisibleRequested()); assertTrue(activity.mDisplayContent.mOpeningApps.contains(activity)); assertFalse(activity.mDisplayContent.mClosingApps.contains(activity)); @@ -3138,7 +3138,7 @@ public class ActivityRecordTests extends WindowTestsBase { // transition should be applied on this activity. activity.setVisibility(false); assertFalse(activity.isVisible()); - assertFalse(activity.mVisibleRequested); + assertFalse(activity.isVisibleRequested()); assertFalse(activity.mDisplayContent.mOpeningApps.contains(activity)); assertFalse(activity.mDisplayContent.mClosingApps.contains(activity)); } @@ -3555,12 +3555,12 @@ public class ActivityRecordTests extends WindowTestsBase { activity.reparent(taskFragment, POSITION_TOP); // Ensure the activity visibility is updated even it is not shown to current user. - activity.mVisibleRequested = true; + activity.setVisibleRequested(true); doReturn(false).when(activity).showToCurrentUser(); spyOn(taskFragment); doReturn(false).when(taskFragment).shouldBeVisible(any()); display.ensureActivitiesVisible(null, 0, false, false); - assertFalse(activity.mVisibleRequested); + assertFalse(activity.isVisibleRequested()); } @Test diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java index 6fe2d2cbe9d4..b4ffc2adbeea 100644 --- a/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java @@ -505,7 +505,9 @@ public class ActivityStarterTests extends WindowTestsBase { .setCreateActivity(true) .build() .getTopMostActivity(); - splitPrimaryActivity.mVisibleRequested = splitSecondActivity.mVisibleRequested = true; + + splitPrimaryActivity.setVisibleRequested(true); + splitSecondActivity.setVisibleRequested(true); assertEquals(splitOrg.mPrimary, splitPrimaryActivity.getRootTask()); assertEquals(splitOrg.mSecondary, splitSecondActivity.getRootTask()); @@ -518,7 +520,7 @@ public class ActivityStarterTests extends WindowTestsBase { .setCreateActivity(true).build().getTopMostActivity(); final ActivityRecord translucentActivity = new TaskBuilder(mSupervisor) .setCreateActivity(true).build().getTopMostActivity(); - assertTrue(activity.mVisibleRequested); + assertTrue(activity.isVisibleRequested()); final ActivityStarter starter = prepareStarter(FLAG_ACTIVITY_NEW_TASK, false /* mockGetRootTask */); @@ -919,7 +921,7 @@ public class ActivityStarterTests extends WindowTestsBase { ACTIVITY_TYPE_STANDARD, false /* onTop */)); // Activity should start invisible since we are bringing it to front. singleTaskActivity.setVisible(false); - singleTaskActivity.mVisibleRequested = false; + singleTaskActivity.setVisibleRequested(false); // Create another activity on top of the secondary display. final Task topStack = secondaryTaskContainer.createRootTask(WINDOWING_MODE_FULLSCREEN, @@ -1137,7 +1139,7 @@ public class ActivityStarterTests extends WindowTestsBase { final ActivityStarter starter = prepareStarter(0 /* flags */); final ActivityRecord target = new ActivityBuilder(mAtm).setCreateTask(true).build(); starter.mStartActivity = target; - target.mVisibleRequested = false; + target.setVisibleRequested(false); target.setTurnScreenOn(true); // Assume the flag was consumed by relayout. target.setCurrentLaunchCanTurnScreenOn(false); @@ -1458,10 +1460,10 @@ public class ActivityStarterTests extends WindowTestsBase { final ActivityRecord activityTop = new ActivityBuilder(mAtm).setTask(task).build(); activityBot.setVisible(false); - activityBot.mVisibleRequested = false; + activityBot.setVisibleRequested(false); assertTrue(activityTop.isVisible()); - assertTrue(activityTop.mVisibleRequested); + assertTrue(activityTop.isVisibleRequested()); final ActivityStarter starter = prepareStarter(FLAG_ACTIVITY_REORDER_TO_FRONT | FLAG_ACTIVITY_NEW_TASK, false /* mockGetRootTask */); 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 2fccd64bceca..368b750967c0 100644 --- a/services/tests/wmtests/src/com/android/server/wm/ActivityTaskManagerServiceTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/ActivityTaskManagerServiceTests.java @@ -344,7 +344,7 @@ public class ActivityTaskManagerServiceTests extends WindowTestsBase { // Assume the activity is finishing and hidden because it was crashed. activity.finishing = true; - activity.mVisibleRequested = false; + activity.setVisibleRequested(false); activity.setVisible(false); activity.getTask().setPausingActivity(activity); homeActivity.setState(PAUSED, "test"); 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 43e79f9cbb15..f72933a697ae 100644 --- a/services/tests/wmtests/src/com/android/server/wm/AppTransitionControllerTest.java +++ b/services/tests/wmtests/src/com/android/server/wm/AppTransitionControllerTest.java @@ -122,7 +122,7 @@ public class AppTransitionControllerTest extends WindowTestsBase { final ActivityRecord top = createActivityRecord(task); top.setState(ActivityRecord.State.RESUMED, "test"); behind.setState(ActivityRecord.State.STARTED, "test"); - behind.mVisibleRequested = true; + behind.setVisibleRequested(true); task.removeActivities("test", false /* excludingTaskOverlay */); assertFalse(mDisplayContent.mAppTransition.isReady()); @@ -294,7 +294,7 @@ public class AppTransitionControllerTest extends WindowTestsBase { final ActivityRecord activity2 = createActivityRecord(mDisplayContent); activity2.setVisible(false); - activity2.mVisibleRequested = false; + activity2.setVisibleRequested(false); final ArraySet<ActivityRecord> opening = new ArraySet<>(); opening.add(activity1); @@ -319,12 +319,12 @@ public class AppTransitionControllerTest extends WindowTestsBase { // +- [Task2] - [ActivityRecord2] (opening, visible) final ActivityRecord activity1 = createActivityRecord(mDisplayContent); activity1.setVisible(true); - activity1.mVisibleRequested = true; + activity1.setVisibleRequested(true); activity1.mRequestForceTransition = true; final ActivityRecord activity2 = createActivityRecord(mDisplayContent); activity2.setVisible(false); - activity2.mVisibleRequested = false; + activity2.setVisibleRequested(false); activity2.mRequestForceTransition = true; final ArraySet<ActivityRecord> opening = new ArraySet<>(); @@ -391,7 +391,7 @@ public class AppTransitionControllerTest extends WindowTestsBase { final ActivityRecord activity2 = createActivityRecord(mDisplayContent); activity2.setVisible(false); - activity2.mVisibleRequested = false; + activity2.setVisibleRequested(false); attrs.setTitle("AppWindow2"); final TestWindowState appWindow2 = createWindowState(attrs, activity2); appWindow2.mWillReplaceWindow = true; @@ -424,17 +424,17 @@ public class AppTransitionControllerTest extends WindowTestsBase { // +- [ActivityRecord4] (invisible) final ActivityRecord activity1 = createActivityRecord(mDisplayContent); activity1.setVisible(false); - activity1.mVisibleRequested = true; + activity1.setVisibleRequested(true); final ActivityRecord activity2 = createActivityRecord(mDisplayContent, activity1.getTask()); activity2.setVisible(false); - activity2.mVisibleRequested = false; + activity2.setVisibleRequested(false); final ActivityRecord activity3 = createActivityRecord(mDisplayContent); final ActivityRecord activity4 = createActivityRecord(mDisplayContent, activity3.getTask()); activity4.setVisible(false); - activity4.mVisibleRequested = false; + activity4.setVisibleRequested(false); final ArraySet<ActivityRecord> opening = new ArraySet<>(); opening.add(activity1); @@ -459,7 +459,7 @@ public class AppTransitionControllerTest extends WindowTestsBase { // +- [ActivityRecord2] (closing, visible) final ActivityRecord activity1 = createActivityRecord(mDisplayContent); activity1.setVisible(false); - activity1.mVisibleRequested = true; + activity1.setVisibleRequested(true); final ActivityRecord activity2 = createActivityRecord(mDisplayContent, activity1.getTask()); @@ -490,7 +490,7 @@ public class AppTransitionControllerTest extends WindowTestsBase { final ActivityRecord activity1 = createActivityRecord(mDisplayContent); activity1.setVisible(false); - activity1.mVisibleRequested = true; + activity1.setVisibleRequested(true); activity1.setOccludesParent(false); final ActivityRecord activity2 = createActivityRecord(mDisplayContent, @@ -528,13 +528,13 @@ public class AppTransitionControllerTest extends WindowTestsBase { final ActivityRecord activity1 = createActivityRecord(mDisplayContent); activity1.setVisible(false); - activity1.mVisibleRequested = true; + activity1.setVisibleRequested(true); activity1.setOccludesParent(false); final ActivityRecord activity2 = createActivityRecord(mDisplayContent, activity1.getTask()); activity2.setVisible(false); - activity2.mVisibleRequested = true; + activity2.setVisibleRequested(true); final ActivityRecord activity3 = createActivityRecord(mDisplayContent); activity3.setOccludesParent(false); @@ -567,7 +567,7 @@ public class AppTransitionControllerTest extends WindowTestsBase { final Task parentTask = createTask(mDisplayContent); final ActivityRecord activity1 = createActivityRecordWithParentTask(parentTask); activity1.setVisible(false); - activity1.mVisibleRequested = true; + activity1.setVisibleRequested(true); final ActivityRecord activity2 = createActivityRecordWithParentTask(parentTask); final ArraySet<ActivityRecord> opening = new ArraySet<>(); @@ -600,10 +600,10 @@ public class AppTransitionControllerTest extends WindowTestsBase { splitRoot1.setAdjacentTaskFragment(splitRoot2); final ActivityRecord activity1 = createActivityRecordWithParentTask(splitRoot1); activity1.setVisible(false); - activity1.mVisibleRequested = true; + activity1.setVisibleRequested(true); final ActivityRecord activity2 = createActivityRecordWithParentTask(splitRoot2); activity2.setVisible(false); - activity2.mVisibleRequested = true; + activity2.setVisibleRequested(true); final ArraySet<ActivityRecord> opening = new ArraySet<>(); opening.add(activity1); @@ -626,13 +626,13 @@ public class AppTransitionControllerTest extends WindowTestsBase { false /* createEmbeddedTask */); final ActivityRecord activity1 = taskFragment1.getTopMostActivity(); activity1.setVisible(false); - activity1.mVisibleRequested = true; + activity1.setVisibleRequested(true); final TaskFragment taskFragment2 = createTaskFragmentWithParentTask(parentTask, false /* createEmbeddedTask */); final ActivityRecord activity2 = taskFragment2.getTopMostActivity(); activity2.setVisible(true); - activity2.mVisibleRequested = false; + activity2.setVisibleRequested(false); final ArraySet<ActivityRecord> opening = new ArraySet<>(); opening.add(activity1); @@ -657,13 +657,13 @@ public class AppTransitionControllerTest extends WindowTestsBase { true /* createEmbeddedTask */); final ActivityRecord activity1 = taskFragment1.getTopMostActivity(); activity1.setVisible(false); - activity1.mVisibleRequested = true; + activity1.setVisibleRequested(true); final TaskFragment taskFragment2 = createTaskFragmentWithParentTask(parentTask, true /* createEmbeddedTask */); final ActivityRecord activity2 = taskFragment2.getTopMostActivity(); activity2.setVisible(true); - activity2.mVisibleRequested = false; + activity2.setVisibleRequested(false); final ArraySet<ActivityRecord> opening = new ArraySet<>(); opening.add(activity1); @@ -688,11 +688,11 @@ public class AppTransitionControllerTest extends WindowTestsBase { false /* createEmbeddedTask */); final ActivityRecord activity1 = taskFragment1.getTopMostActivity(); activity1.setVisible(false); - activity1.mVisibleRequested = true; + activity1.setVisibleRequested(true); final ActivityRecord activity2 = createActivityRecord(mDisplayContent); activity2.setVisible(true); - activity2.mVisibleRequested = false; + activity2.setVisibleRequested(false); final ArraySet<ActivityRecord> opening = new ArraySet<>(); opening.add(activity1); @@ -718,11 +718,11 @@ public class AppTransitionControllerTest extends WindowTestsBase { false /* createEmbeddedTask */); final ActivityRecord activity1 = taskFragment1.getTopMostActivity(); activity1.setVisible(true); - activity1.mVisibleRequested = false; + activity1.setVisibleRequested(false); final ActivityRecord activity2 = createActivityRecord(mDisplayContent); activity2.setVisible(false); - activity2.mVisibleRequested = true; + activity2.setVisibleRequested(true); final ArraySet<ActivityRecord> opening = new ArraySet<>(); opening.add(activity2); @@ -745,13 +745,13 @@ public class AppTransitionControllerTest extends WindowTestsBase { // +- [Task2] (embedded) - [ActivityRecord2] (opening, invisible) final ActivityRecord activity1 = createActivityRecord(mDisplayContent); activity1.setVisible(false); - activity1.mVisibleRequested = true; + activity1.setVisibleRequested(true); final Task task2 = createTask(mDisplayContent); task2.mRemoveWithTaskOrganizer = true; final ActivityRecord activity2 = createActivityRecord(task2); activity2.setVisible(false); - activity2.mVisibleRequested = true; + activity2.setVisibleRequested(true); final ArraySet<ActivityRecord> opening = new ArraySet<>(); opening.add(activity1); @@ -779,7 +779,7 @@ public class AppTransitionControllerTest extends WindowTestsBase { final ActivityRecord activity1 = createActivityRecord(task); activity1.setVisible(false); - activity1.mVisibleRequested = true; + activity1.setVisibleRequested(true); final ActivityRecord activity2 = createActivityRecord(task); final ArraySet<ActivityRecord> opening = new ArraySet<>(); @@ -1295,6 +1295,8 @@ public class AppTransitionControllerTest extends WindowTestsBase { @Test public void testTransitionGoodToGoForTaskFragments_detachedApp() { final TaskFragmentOrganizer organizer = new TaskFragmentOrganizer(Runnable::run); + final ITaskFragmentOrganizer iOrganizer = getITaskFragmentOrganizer(organizer); + mAtm.mTaskFragmentOrganizerController.registerOrganizer(iOrganizer); final Task task = createTask(mDisplayContent); final TaskFragment changeTaskFragment = createTaskFragmentWithEmbeddedActivity(task, organizer); 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 8cfe503d61d7..32c3a49907be 100644 --- a/services/tests/wmtests/src/com/android/server/wm/AppTransitionTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/AppTransitionTests.java @@ -420,11 +420,11 @@ public class AppTransitionTests extends WindowTestsBase { // Simulate activity1 launches activity2. final ActivityRecord activity1 = createActivityRecord(task); activity1.setVisible(true); - activity1.mVisibleRequested = false; + activity1.setVisibleRequested(false); activity1.allDrawn = true; final ActivityRecord activity2 = createActivityRecord(task); activity2.setVisible(false); - activity2.mVisibleRequested = true; + activity2.setVisibleRequested(true); activity2.allDrawn = true; dc.mClosingApps.add(activity1); diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java index 63f4f5f5b9fb..98e68ca99720 100644 --- a/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java @@ -588,7 +588,7 @@ public class DisplayContentTests extends WindowTestsBase { assertEquals(window1, mWm.mRoot.getTopFocusedDisplayContent().mCurrentFocus); // Make sure top focused display not changed if there is a focused app. - window1.mActivityRecord.mVisibleRequested = false; + window1.mActivityRecord.setVisibleRequested(false); window1.getDisplayContent().setFocusedApp(window1.mActivityRecord); updateFocusedWindow(); assertTrue(!window1.isFocused()); @@ -1104,7 +1104,7 @@ public class DisplayContentTests extends WindowTestsBase { public void testOrientationBehind() { final ActivityRecord prev = new ActivityBuilder(mAtm).setCreateTask(true) .setScreenOrientation(getRotatedOrientation(mDisplayContent)).build(); - prev.mVisibleRequested = false; + prev.setVisibleRequested(false); final ActivityRecord top = new ActivityBuilder(mAtm).setCreateTask(true) .setScreenOrientation(SCREEN_ORIENTATION_BEHIND).build(); assertNotEquals(WindowConfiguration.ROTATION_UNDEFINED, diff --git a/services/tests/wmtests/src/com/android/server/wm/DualDisplayAreaGroupPolicyTest.java b/services/tests/wmtests/src/com/android/server/wm/DualDisplayAreaGroupPolicyTest.java index 2956c14155b9..3ab4495bd7ca 100644 --- a/services/tests/wmtests/src/com/android/server/wm/DualDisplayAreaGroupPolicyTest.java +++ b/services/tests/wmtests/src/com/android/server/wm/DualDisplayAreaGroupPolicyTest.java @@ -206,7 +206,7 @@ public class DualDisplayAreaGroupPolicyTest extends WindowTestsBase { assertThat(newTaskBounds).isEqualTo(newDagBounds); // Activity config bounds is unchanged, size compat bounds is (860x[860x860/1200=616]) - assertThat(mFirstActivity.getSizeCompatScale()).isLessThan(1f); + assertThat(mFirstActivity.getCompatScale()).isLessThan(1f); assertThat(activityConfigBounds.width()).isEqualTo(activityBounds.width()); assertThat(activityConfigBounds.height()).isEqualTo(activityBounds.height()); assertThat(activitySizeCompatBounds.height()).isEqualTo(newTaskBounds.height()); diff --git a/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationControllerTest.java index 8546763aebec..48084743afde 100644 --- a/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationControllerTest.java +++ b/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationControllerTest.java @@ -172,12 +172,12 @@ public class RecentsAnimationControllerTest extends WindowTestsBase { // executed. final ActivityRecord activity1 = createActivityRecord(task); activity1.setVisible(true); - activity1.mVisibleRequested = false; + activity1.setVisibleRequested(false); activity1.addWindow(createWindowState(new LayoutParams(TYPE_BASE_APPLICATION), activity1)); final ActivityRecord activity2 = createActivityRecord(task); activity2.setVisible(false); - activity2.mVisibleRequested = true; + activity2.setVisibleRequested(true); mDefaultDisplay.getConfiguration().windowConfiguration.setRotation( mDefaultDisplay.getRotation()); diff --git a/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationTest.java b/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationTest.java index a1d6a5006fef..95623f0ab490 100644 --- a/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationTest.java +++ b/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationTest.java @@ -111,14 +111,14 @@ public class RecentsAnimationTest extends WindowTestsBase { RecentsAnimationCallbacks recentsAnimation = startRecentsActivity( mRecentsComponent, true /* getRecentsAnimation */); // The launch-behind state should make the recents activity visible. - assertTrue(recentActivity.mVisibleRequested); + assertTrue(recentActivity.isVisibleRequested()); assertEquals(ActivityTaskManagerService.DEMOTE_TOP_REASON_ANIMATING_RECENTS, mAtm.mDemoteTopAppReasons); // Simulate the animation is cancelled without changing the stack order. recentsAnimation.onAnimationFinished(REORDER_KEEP_IN_PLACE, false /* sendUserLeaveHint */); // The non-top recents activity should be invisible by the restored launch-behind state. - assertFalse(recentActivity.mVisibleRequested); + assertFalse(recentActivity.isVisibleRequested()); assertEquals(0, mAtm.mDemoteTopAppReasons); } @@ -163,7 +163,7 @@ public class RecentsAnimationTest extends WindowTestsBase { // The activity is started in background so it should be invisible and will be stopped. assertThat(recentsActivity).isNotNull(); assertThat(mSupervisor.mStoppingActivities).contains(recentsActivity); - assertFalse(recentsActivity.mVisibleRequested); + assertFalse(recentsActivity.isVisibleRequested()); // Assume it is stopped to test next use case. recentsActivity.activityStopped(null /* newIcicle */, null /* newPersistentState */, @@ -359,7 +359,7 @@ public class RecentsAnimationTest extends WindowTestsBase { true); // Ensure we find the task for the right user and it is made visible - assertTrue(otherUserHomeActivity.mVisibleRequested); + assertTrue(otherUserHomeActivity.isVisibleRequested()); } private void startRecentsActivity() { diff --git a/services/tests/wmtests/src/com/android/server/wm/RootTaskTests.java b/services/tests/wmtests/src/com/android/server/wm/RootTaskTests.java index b46e90da3944..db26b272b635 100644 --- a/services/tests/wmtests/src/com/android/server/wm/RootTaskTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/RootTaskTests.java @@ -1068,7 +1068,7 @@ public class RootTaskTests extends WindowTestsBase { activity.app = null; overlayActivity.app = null; // Simulate the process is dead - activity.mVisibleRequested = false; + activity.setVisibleRequested(false); activity.setState(DESTROYED, "Test"); assertEquals(2, task.getChildCount()); @@ -1205,7 +1205,7 @@ public class RootTaskTests extends WindowTestsBase { // There is still an activity1 in rootTask1 so the activity2 should be added to finishing // list that will be destroyed until idle. - rootTask2.getTopNonFinishingActivity().mVisibleRequested = true; + rootTask2.getTopNonFinishingActivity().setVisibleRequested(true); final ActivityRecord activity2 = finishTopActivity(rootTask2); assertEquals(STOPPING, activity2.getState()); assertThat(mSupervisor.mStoppingActivities).contains(activity2); @@ -1410,7 +1410,7 @@ public class RootTaskTests extends WindowTestsBase { new ActivityBuilder(mAtm).setTask(task).build(); // The scenario we are testing is when the app isn't visible yet. nonTopVisibleActivity.setVisible(false); - nonTopVisibleActivity.mVisibleRequested = false; + nonTopVisibleActivity.setVisibleRequested(false); doReturn(false).when(nonTopVisibleActivity).attachedToProcess(); doReturn(true).when(nonTopVisibleActivity).shouldBeVisibleUnchecked(); doNothing().when(mSupervisor).startSpecificActivity(any(), anyBoolean(), diff --git a/services/tests/wmtests/src/com/android/server/wm/RootWindowContainerTests.java b/services/tests/wmtests/src/com/android/server/wm/RootWindowContainerTests.java index 64c1e05da2cd..b89643cba116 100644 --- a/services/tests/wmtests/src/com/android/server/wm/RootWindowContainerTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/RootWindowContainerTests.java @@ -172,7 +172,7 @@ public class RootWindowContainerTests extends WindowTestsBase { public void testTaskLayerRank() { final Task rootTask = new TaskBuilder(mSupervisor).build(); final Task task1 = new TaskBuilder(mSupervisor).setParentTaskFragment(rootTask).build(); - new ActivityBuilder(mAtm).setTask(task1).build().mVisibleRequested = true; + new ActivityBuilder(mAtm).setTask(task1).build().setVisibleRequested(true); mWm.mRoot.rankTaskLayers(); assertEquals(1, task1.mLayerRank); @@ -180,7 +180,7 @@ public class RootWindowContainerTests extends WindowTestsBase { assertEquals(Task.LAYER_RANK_INVISIBLE, rootTask.mLayerRank); final Task task2 = new TaskBuilder(mSupervisor).build(); - new ActivityBuilder(mAtm).setTask(task2).build().mVisibleRequested = true; + new ActivityBuilder(mAtm).setTask(task2).build().setVisibleRequested(true); mWm.mRoot.rankTaskLayers(); // Note that ensureActivitiesVisible is disabled in SystemServicesTestRule, so both the diff --git a/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java b/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java index 06eea298600c..babad4d4d744 100644 --- a/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java @@ -167,7 +167,7 @@ public class SizeCompatTests extends WindowTestsBase { public void testRestartProcessIfVisible() { setUpDisplaySizeWithApp(1000, 2500); doNothing().when(mSupervisor).scheduleRestartTimeout(mActivity); - mActivity.mVisibleRequested = true; + mActivity.setVisibleRequested(true); mActivity.setSavedState(null /* savedState */); mActivity.setState(RESUMED, "testRestart"); prepareUnresizable(mActivity, 1.5f /* maxAspect */, SCREEN_ORIENTATION_UNSPECIFIED); @@ -551,7 +551,7 @@ public class SizeCompatTests extends WindowTestsBase { resizeDisplay(display, 900, 1800); mActivity.setState(STOPPED, "testSizeCompatMode"); - mActivity.mVisibleRequested = false; + mActivity.setVisibleRequested(false); mActivity.visibleIgnoringKeyguard = false; mActivity.app.setReportedProcState(ActivityManager.PROCESS_STATE_CACHED_ACTIVITY); mActivity.app.computeProcessActivityState(); @@ -603,7 +603,7 @@ public class SizeCompatTests extends WindowTestsBase { // Make the activity resizable again by restarting it clearInvocations(mTask); mActivity.info.resizeMode = RESIZE_MODE_RESIZEABLE; - mActivity.mVisibleRequested = true; + mActivity.setVisibleRequested(true); mActivity.restartProcessIfVisible(); // The full lifecycle isn't hooked up so manually set state to resumed mActivity.setState(RESUMED, "testHandleActivitySizeCompatModeChanged"); @@ -3185,7 +3185,7 @@ public class SizeCompatTests extends WindowTestsBase { task.mResizeMode = activity.info.resizeMode; task.getRootActivity().info.resizeMode = activity.info.resizeMode; } - activity.mVisibleRequested = true; + activity.setVisibleRequested(true); if (maxAspect >= 0) { activity.info.setMaxAspectRatio(maxAspect); } @@ -3205,7 +3205,7 @@ public class SizeCompatTests extends WindowTestsBase { /** Asserts that the size of activity is larger than its parent so it is scaling. */ private void assertScaled() { assertTrue(mActivity.inSizeCompatMode()); - assertNotEquals(1f, mActivity.getSizeCompatScale(), 0.0001f /* delta */); + assertNotEquals(1f, mActivity.getCompatScale(), 0.0001f /* delta */); } /** Asserts that the activity is best fitted in the parent. */ diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskDisplayAreaTests.java b/services/tests/wmtests/src/com/android/server/wm/TaskDisplayAreaTests.java index 7f09606d1c3a..e660db57fb69 100644 --- a/services/tests/wmtests/src/com/android/server/wm/TaskDisplayAreaTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/TaskDisplayAreaTests.java @@ -398,7 +398,7 @@ public class TaskDisplayAreaTests extends WindowTestsBase { .setParentTask(rootHomeTask).setCreateTask(true).build(); } homeActivity.setVisible(false); - homeActivity.mVisibleRequested = true; + homeActivity.setVisibleRequested(true); assertFalse(rootHomeTask.isVisible()); assertEquals(defaultTaskDisplayArea.getOrientation(), rootHomeTask.getOrientation()); diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskFragmentOrganizerControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/TaskFragmentOrganizerControllerTest.java index 2b493145f854..db65f49465bc 100644 --- a/services/tests/wmtests/src/com/android/server/wm/TaskFragmentOrganizerControllerTest.java +++ b/services/tests/wmtests/src/com/android/server/wm/TaskFragmentOrganizerControllerTest.java @@ -370,7 +370,8 @@ public class TaskFragmentOrganizerControllerTest extends WindowTestsBase { mController.onActivityReparentedToTask(activity); mController.dispatchPendingEvents(); - assertTaskFragmentParentInfoChangedTransaction(task); + // There will not be TaskFragmentParentInfoChanged because Task visible request is changed + // before the organized TaskFragment is added to the Task. assertActivityReparentedToTaskTransaction(task.mTaskId, activity.intent, activity.token); } @@ -552,10 +553,9 @@ public class TaskFragmentOrganizerControllerTest extends WindowTestsBase { @Test public void testApplyTransaction_enforceHierarchyChange_createTaskFragment() { final ActivityRecord ownerActivity = createActivityRecord(mDisplayContent); - final IBinder fragmentToken = new Binder(); // Allow organizer to create TaskFragment and start/reparent activity to TaskFragment. - createTaskFragmentFromOrganizer(mTransaction, ownerActivity, fragmentToken); + createTaskFragmentFromOrganizer(mTransaction, ownerActivity, mFragmentToken); mTransaction.startActivityInTaskFragment( mFragmentToken, null /* callerToken */, new Intent(), null /* activityOptions */); mTransaction.reparentActivityToTaskFragment(mFragmentToken, mock(IBinder.class)); @@ -564,7 +564,8 @@ public class TaskFragmentOrganizerControllerTest extends WindowTestsBase { assertApplyTransactionAllowed(mTransaction); // Successfully created a TaskFragment. - final TaskFragment taskFragment = mWindowOrganizerController.getTaskFragment(fragmentToken); + final TaskFragment taskFragment = mWindowOrganizerController.getTaskFragment( + mFragmentToken); assertNotNull(taskFragment); assertEquals(ownerActivity.getTask(), taskFragment.getTask()); } @@ -703,6 +704,40 @@ public class TaskFragmentOrganizerControllerTest extends WindowTestsBase { } @Test + public void testApplyTransaction_createTaskFragment_withPairedPrimaryFragmentToken() { + final Task task = createTask(mDisplayContent); + mTaskFragment = new TaskFragmentBuilder(mAtm) + .setParentTask(task) + .setFragmentToken(mFragmentToken) + .createActivityCount(1) + .build(); + mWindowOrganizerController.mLaunchTaskFragments.put(mFragmentToken, mTaskFragment); + final ActivityRecord activityOnTop = createActivityRecord(task); + final int uid = Binder.getCallingUid(); + activityOnTop.info.applicationInfo.uid = uid; + activityOnTop.getTask().effectiveUid = uid; + final IBinder fragmentToken1 = new Binder(); + final TaskFragmentCreationParams params = new TaskFragmentCreationParams.Builder( + mOrganizerToken, fragmentToken1, activityOnTop.token) + .setPairedPrimaryFragmentToken(mFragmentToken) + .build(); + mTransaction.setTaskFragmentOrganizer(mIOrganizer); + mTransaction.createTaskFragment(params); + assertApplyTransactionAllowed(mTransaction); + + // Successfully created a TaskFragment. + final TaskFragment taskFragment = mWindowOrganizerController.getTaskFragment( + fragmentToken1); + assertNotNull(taskFragment); + // The new TaskFragment should be positioned right above the paired TaskFragment. + assertEquals(task.mChildren.indexOf(mTaskFragment) + 1, + task.mChildren.indexOf(taskFragment)); + // The top activity should remain on top. + assertEquals(task.mChildren.indexOf(taskFragment) + 1, + task.mChildren.indexOf(activityOnTop)); + } + + @Test public void testApplyTransaction_enforceHierarchyChange_reparentChildren() { doReturn(true).when(mTaskFragment).isAttached(); @@ -1159,6 +1194,7 @@ public class TaskFragmentOrganizerControllerTest extends WindowTestsBase { doReturn(false).when(task).shouldBeVisible(any()); // Dispatch the initial event in the Task to update the Task visibility to the organizer. + clearInvocations(mOrganizer); mController.onTaskFragmentAppeared(mIOrganizer, taskFragment); mController.dispatchPendingEvents(); verify(mOrganizer).onTransactionReady(any()); diff --git a/services/tests/wmtests/src/com/android/server/wm/TransitionTests.java b/services/tests/wmtests/src/com/android/server/wm/TransitionTests.java index 999523f3ea19..3fd9dfe8f882 100644 --- a/services/tests/wmtests/src/com/android/server/wm/TransitionTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/TransitionTests.java @@ -134,8 +134,8 @@ public class TransitionTests extends WindowTestsBase { changes.put(closing, new Transition.ChangeInfo(true /* vis */, true /* exChg */)); fillChangeMap(changes, newTask); // End states. - closing.mVisibleRequested = false; - opening.mVisibleRequested = true; + closing.setVisibleRequested(false); + opening.setVisibleRequested(true); final int transit = transition.mType; int flags = 0; @@ -198,9 +198,9 @@ public class TransitionTests extends WindowTestsBase { changes.put(closing, new Transition.ChangeInfo(true /* vis */, true /* exChg */)); fillChangeMap(changes, newTask); // End states. - closing.mVisibleRequested = false; - opening.mVisibleRequested = true; - opening2.mVisibleRequested = true; + closing.setVisibleRequested(false); + opening.setVisibleRequested(true); + opening2.setVisibleRequested(true); final int transit = transition.mType; int flags = 0; @@ -247,8 +247,8 @@ public class TransitionTests extends WindowTestsBase { fillChangeMap(changes, tda); // End states. - showing.mVisibleRequested = true; - showing2.mVisibleRequested = true; + showing.setVisibleRequested(true); + showing2.setVisibleRequested(true); final int transit = transition.mType; int flags = 0; @@ -283,16 +283,16 @@ public class TransitionTests extends WindowTestsBase { final Task openTask = createTask(mDisplayContent); final ActivityRecord opening = createActivityRecord(openTask); - opening.mVisibleRequested = false; // starts invisible + opening.setVisibleRequested(false); // starts invisible final Task closeTask = createTask(mDisplayContent); final ActivityRecord closing = createActivityRecord(closeTask); - closing.mVisibleRequested = true; // starts visible + closing.setVisibleRequested(true); // starts visible transition.collectExistenceChange(openTask); transition.collect(opening); transition.collect(closing); - opening.mVisibleRequested = true; - closing.mVisibleRequested = false; + opening.setVisibleRequested(true); + closing.setVisibleRequested(false); ArrayList<WindowContainer> targets = Transition.calculateTargets( transition.mParticipants, transition.mChanges); @@ -320,7 +320,7 @@ public class TransitionTests extends WindowTestsBase { WINDOWING_MODE_FREEFORM, ACTIVITY_TYPE_STANDARD); final ActivityRecord act = createActivityRecord(tasks[i]); // alternate so that the transition doesn't get promoted to the display area - act.mVisibleRequested = (i % 2) == 0; // starts invisible + act.setVisibleRequested((i % 2) == 0); // starts invisible } // doesn't matter which order collected since participants is a set @@ -328,7 +328,7 @@ public class TransitionTests extends WindowTestsBase { transition.collectExistenceChange(tasks[i]); final ActivityRecord act = tasks[i].getTopMostActivity(); transition.collect(act); - tasks[i].getTopMostActivity().mVisibleRequested = (i % 2) != 0; + tasks[i].getTopMostActivity().setVisibleRequested((i % 2) != 0); } ArrayList<WindowContainer> targets = Transition.calculateTargets( @@ -357,7 +357,7 @@ public class TransitionTests extends WindowTestsBase { WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD); final ActivityRecord act = createActivityRecord(tasks[i]); // alternate so that the transition doesn't get promoted to the display area - act.mVisibleRequested = (i % 2) == 0; // starts invisible + act.setVisibleRequested((i % 2) == 0); // starts invisible act.visibleIgnoringKeyguard = (i % 2) == 0; if (i == showWallpaperTask) { doReturn(true).when(act).showWallpaper(); @@ -378,7 +378,7 @@ public class TransitionTests extends WindowTestsBase { transition.collectExistenceChange(tasks[i]); final ActivityRecord act = tasks[i].getTopMostActivity(); transition.collect(act); - tasks[i].getTopMostActivity().mVisibleRequested = (i % 2) != 0; + tasks[i].getTopMostActivity().setVisibleRequested((i % 2) != 0); } ArrayList<WindowContainer> targets = Transition.calculateTargets( @@ -414,9 +414,9 @@ public class TransitionTests extends WindowTestsBase { changes.put(closing, new Transition.ChangeInfo(true /* vis */, true /* exChg */)); fillChangeMap(changes, topTask); // End states. - showing.mVisibleRequested = true; - closing.mVisibleRequested = false; - hiding.mVisibleRequested = false; + showing.setVisibleRequested(true); + closing.setVisibleRequested(false); + hiding.setVisibleRequested(false); participants.add(belowTask); participants.add(hiding); @@ -446,9 +446,9 @@ public class TransitionTests extends WindowTestsBase { changes.put(closing, new Transition.ChangeInfo(true /* vis */, false /* exChg */)); fillChangeMap(changes, topTask); // End states. - showing.mVisibleRequested = true; - opening.mVisibleRequested = true; - closing.mVisibleRequested = false; + showing.setVisibleRequested(true); + opening.setVisibleRequested(true); + closing.setVisibleRequested(false); participants.add(belowTask); participants.add(showing); @@ -528,19 +528,19 @@ public class TransitionTests extends WindowTestsBase { @Test public void testOpenActivityInTheSameTaskWithDisplayChange() { final ActivityRecord closing = createActivityRecord(mDisplayContent); - closing.mVisibleRequested = true; + closing.setVisibleRequested(true); final Task task = closing.getTask(); makeTaskOrganized(task); final ActivityRecord opening = createActivityRecord(task); - opening.mVisibleRequested = false; + opening.setVisibleRequested(false); makeDisplayAreaOrganized(mDisplayContent.getDefaultTaskDisplayArea(), mDisplayContent); final WindowContainer<?>[] wcs = { closing, opening, task, mDisplayContent }; final Transition transition = createTestTransition(TRANSIT_OPEN); for (WindowContainer<?> wc : wcs) { transition.collect(wc); } - closing.mVisibleRequested = false; - opening.mVisibleRequested = true; + closing.setVisibleRequested(false); + opening.setVisibleRequested(true); final int newRotation = mDisplayContent.getWindowConfiguration().getRotation() + 1; for (WindowContainer<?> wc : wcs) { wc.getWindowConfiguration().setRotation(newRotation); @@ -583,9 +583,9 @@ public class TransitionTests extends WindowTestsBase { changes.put(changeInChange, new Transition.ChangeInfo(true /* vis */, false /* exChg */)); fillChangeMap(changes, openTask); // End states. - changeInChange.mVisibleRequested = true; - openInOpen.mVisibleRequested = true; - openInChange.mVisibleRequested = true; + changeInChange.setVisibleRequested(true); + openInOpen.setVisibleRequested(true); + openInChange.setVisibleRequested(true); final int transit = transition.mType; int flags = 0; @@ -641,8 +641,8 @@ public class TransitionTests extends WindowTestsBase { changes.put(closing, new Transition.ChangeInfo(true /* vis */, false /* exChg */)); fillChangeMap(changes, newTask); // End states. - closing.mVisibleRequested = true; - opening.mVisibleRequested = true; + closing.setVisibleRequested(true); + opening.setVisibleRequested(true); final int transit = transition.mType; int flags = 0; @@ -682,8 +682,8 @@ public class TransitionTests extends WindowTestsBase { changes.put(closing, new Transition.ChangeInfo(true /* vis */, false /* exChg */)); fillChangeMap(changes, newTask); // End states. - closing.mVisibleRequested = true; - opening.mVisibleRequested = true; + closing.setVisibleRequested(true); + opening.setVisibleRequested(true); final int transit = transition.mType; int flags = 0; @@ -959,7 +959,7 @@ public class TransitionTests extends WindowTestsBase { home.mTransitionController.requestStartTransition(transition, home.getTask(), null /* remoteTransition */, null /* displayChange */); transition.collectExistenceChange(home); - home.mVisibleRequested = true; + home.setVisibleRequested(true); mDisplayContent.setFixedRotationLaunchingAppUnchecked(home); doReturn(true).when(home).hasFixedRotationTransform(any()); player.startTransition(); @@ -995,12 +995,12 @@ public class TransitionTests extends WindowTestsBase { // Start out with task2 visible and set up a transition that closes task2 and opens task1 final Task task1 = createTask(mDisplayContent); final ActivityRecord activity1 = createActivityRecord(task1); - activity1.mVisibleRequested = false; + activity1.setVisibleRequested(false); activity1.setVisible(false); final Task task2 = createTask(mDisplayContent); makeTaskOrganized(task1, task2); final ActivityRecord activity2 = createActivityRecord(task1); - activity2.mVisibleRequested = true; + activity2.setVisibleRequested(true); activity2.setVisible(true); openTransition.collectExistenceChange(task1); @@ -1008,9 +1008,9 @@ public class TransitionTests extends WindowTestsBase { openTransition.collectExistenceChange(task2); openTransition.collectExistenceChange(activity2); - activity1.mVisibleRequested = true; + activity1.setVisibleRequested(true); activity1.setVisible(true); - activity2.mVisibleRequested = false; + activity2.setVisibleRequested(false); // Using abort to force-finish the sync (since we can't wait for drawing in unit test). // We didn't call abort on the transition itself, so it will still run onTransactionReady @@ -1026,8 +1026,8 @@ public class TransitionTests extends WindowTestsBase { closeTransition.collectExistenceChange(task2); closeTransition.collectExistenceChange(activity2); - activity1.mVisibleRequested = false; - activity2.mVisibleRequested = true; + activity1.setVisibleRequested(false); + activity2.setVisibleRequested(true); openTransition.finishTransition(); @@ -1069,12 +1069,12 @@ public class TransitionTests extends WindowTestsBase { // Start out with task2 visible and set up a transition that closes task2 and opens task1 final Task task1 = createTask(mDisplayContent); final ActivityRecord activity1 = createActivityRecord(task1); - activity1.mVisibleRequested = false; + activity1.setVisibleRequested(false); activity1.setVisible(false); final Task task2 = createTask(mDisplayContent); makeTaskOrganized(task1, task2); final ActivityRecord activity2 = createActivityRecord(task2); - activity2.mVisibleRequested = true; + activity2.setVisibleRequested(true); activity2.setVisible(true); openTransition.collectExistenceChange(task1); @@ -1082,9 +1082,9 @@ public class TransitionTests extends WindowTestsBase { openTransition.collectExistenceChange(task2); openTransition.collectExistenceChange(activity2); - activity1.mVisibleRequested = true; + activity1.setVisibleRequested(true); activity1.setVisible(true); - activity2.mVisibleRequested = false; + activity2.setVisibleRequested(false); // Using abort to force-finish the sync (since we can't wait for drawing in unit test). // We didn't call abort on the transition itself, so it will still run onTransactionReady @@ -1104,8 +1104,8 @@ public class TransitionTests extends WindowTestsBase { closeTransition.collectExistenceChange(activity2); closeTransition.setTransientLaunch(activity2, null /* restoreBelow */); - activity1.mVisibleRequested = false; - activity2.mVisibleRequested = true; + activity1.setVisibleRequested(false); + activity2.setVisibleRequested(true); activity2.setVisible(true); // Using abort to force-finish the sync (since we obviously can't wait for drawing). @@ -1163,8 +1163,8 @@ public class TransitionTests extends WindowTestsBase { changes.put(activity0, new Transition.ChangeInfo(true /* vis */, false /* exChg */)); changes.put(activity1, new Transition.ChangeInfo(false /* vis */, false /* exChg */)); // End states. - activity0.mVisibleRequested = false; - activity1.mVisibleRequested = true; + activity0.setVisibleRequested(false); + activity1.setVisibleRequested(true); participants.add(activity0); participants.add(activity1); @@ -1207,9 +1207,9 @@ public class TransitionTests extends WindowTestsBase { changes.put(nonEmbeddedActivity, new Transition.ChangeInfo(true /* vis */, false /* exChg */)); // End states. - closingActivity.mVisibleRequested = false; - openingActivity.mVisibleRequested = true; - nonEmbeddedActivity.mVisibleRequested = false; + closingActivity.setVisibleRequested(false); + openingActivity.setVisibleRequested(true); + nonEmbeddedActivity.setVisibleRequested(false); participants.add(closingActivity); participants.add(openingActivity); @@ -1252,8 +1252,8 @@ public class TransitionTests extends WindowTestsBase { false /* exChg */)); changes.put(embeddedTf, new Transition.ChangeInfo(false /* vis */, true /* exChg */)); // End states. - nonEmbeddedActivity.mVisibleRequested = false; - embeddedActivity.mVisibleRequested = true; + nonEmbeddedActivity.setVisibleRequested(false); + embeddedActivity.setVisibleRequested(true); embeddedTf.setBounds(new Rect(0, 0, 500, 500)); participants.add(nonEmbeddedActivity); @@ -1282,11 +1282,11 @@ public class TransitionTests extends WindowTestsBase { final ActivityRecord activity = createActivityRecord(task); // Start states: set bounds to make sure the start bounds is ignored if it is not visible. activity.getConfiguration().windowConfiguration.setBounds(new Rect(0, 0, 250, 500)); - activity.mVisibleRequested = false; + activity.setVisibleRequested(false); changes.put(activity, new Transition.ChangeInfo(activity)); // End states: reset bounds to fill Task. activity.getConfiguration().windowConfiguration.setBounds(taskBounds); - activity.mVisibleRequested = true; + activity.setVisibleRequested(true); participants.add(activity); final ArrayList<WindowContainer> targets = Transition.calculateTargets( @@ -1310,11 +1310,11 @@ public class TransitionTests extends WindowTestsBase { task.getConfiguration().windowConfiguration.setBounds(taskBounds); final ActivityRecord activity = createActivityRecord(task); // Start states: fills Task without override. - activity.mVisibleRequested = true; + activity.setVisibleRequested(true); changes.put(activity, new Transition.ChangeInfo(activity)); // End states: set bounds to make sure the start bounds is ignored if it is not visible. activity.getConfiguration().windowConfiguration.setBounds(new Rect(0, 0, 250, 500)); - activity.mVisibleRequested = false; + activity.setVisibleRequested(false); participants.add(activity); final ArrayList<WindowContainer> targets = Transition.calculateTargets( @@ -1337,12 +1337,12 @@ public class TransitionTests extends WindowTestsBase { final Task lastParent = createTask(mDisplayContent); final Task newParent = createTask(mDisplayContent); final ActivityRecord activity = createActivityRecord(lastParent); - activity.mVisibleRequested = true; + activity.setVisibleRequested(true); // Skip manipulate the SurfaceControl. doNothing().when(activity).setDropInputMode(anyInt()); changes.put(activity, new Transition.ChangeInfo(activity)); activity.reparent(newParent, POSITION_TOP); - activity.mVisibleRequested = false; + activity.setVisibleRequested(false); participants.add(activity); final ArrayList<WindowContainer> targets = Transition.calculateTargets( @@ -1362,7 +1362,7 @@ public class TransitionTests extends WindowTestsBase { final Task task = createTask(mDisplayContent); task.setBounds(new Rect(0, 0, 2000, 1000)); final ActivityRecord activity = createActivityRecord(task); - activity.mVisibleRequested = true; + activity.setVisibleRequested(true); // Skip manipulate the SurfaceControl. doNothing().when(activity).setDropInputMode(anyInt()); final TaskFragmentOrganizer organizer = new TaskFragmentOrganizer(Runnable::run); @@ -1410,13 +1410,13 @@ public class TransitionTests extends WindowTestsBase { task.setTaskDescription(taskDescription); // Start states: - embeddedActivity.mVisibleRequested = true; - nonEmbeddedActivity.mVisibleRequested = false; + embeddedActivity.setVisibleRequested(true); + nonEmbeddedActivity.setVisibleRequested(false); changes.put(embeddedTf, new Transition.ChangeInfo(embeddedTf)); changes.put(nonEmbeddedActivity, new Transition.ChangeInfo(nonEmbeddedActivity)); // End states: - embeddedActivity.mVisibleRequested = false; - nonEmbeddedActivity.mVisibleRequested = true; + embeddedActivity.setVisibleRequested(false); + nonEmbeddedActivity.setVisibleRequested(true); participants.add(embeddedTf); participants.add(nonEmbeddedActivity); @@ -1529,7 +1529,7 @@ public class TransitionTests extends WindowTestsBase { final ActivityRecord activity = createActivityRecord(lastParent); doReturn(true).when(lastParent).shouldRemoveSelfOnLastChildRemoval(); doNothing().when(activity).setDropInputMode(anyInt()); - activity.mVisibleRequested = true; + activity.setVisibleRequested(true); final Transition transition = new Transition(TRANSIT_CHANGE, 0 /* flags */, activity.mTransitionController, mWm.mSyncEngine); diff --git a/services/tests/wmtests/src/com/android/server/wm/UnknownAppVisibilityControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/UnknownAppVisibilityControllerTest.java index 45e114130420..2fccb88ad8de 100644 --- a/services/tests/wmtests/src/com/android/server/wm/UnknownAppVisibilityControllerTest.java +++ b/services/tests/wmtests/src/com/android/server/wm/UnknownAppVisibilityControllerTest.java @@ -95,7 +95,7 @@ public class UnknownAppVisibilityControllerTest extends WindowTestsBase { final ActivityRecord activity = createNonAttachedActivityRecord(mDisplayContent); mDisplayContent.mUnknownAppVisibilityController.notifyLaunched(activity); activity.finishing = true; - activity.mVisibleRequested = true; + activity.setVisibleRequested(true); activity.setVisibility(false, false); assertTrue(mDisplayContent.mUnknownAppVisibilityController.allResolved()); } diff --git a/services/tests/wmtests/src/com/android/server/wm/WallpaperControllerTests.java b/services/tests/wmtests/src/com/android/server/wm/WallpaperControllerTests.java index aab70b5f9004..06a79f47de55 100644 --- a/services/tests/wmtests/src/com/android/server/wm/WallpaperControllerTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/WallpaperControllerTests.java @@ -311,12 +311,12 @@ public class WallpaperControllerTests extends WindowTestsBase { r.applyFixedRotationTransform(mDisplayContent.getDisplayInfo(), mDisplayContent.mDisplayFrames, mDisplayContent.getConfiguration()); // Invisible requested activity should not share its rotation transform. - r.mVisibleRequested = false; + r.setVisibleRequested(false); mDisplayContent.mWallpaperController.adjustWallpaperWindows(); assertFalse(wallpaperToken.hasFixedRotationTransform()); // Wallpaper should link the transform of its target. - r.mVisibleRequested = true; + r.setVisibleRequested(true); mDisplayContent.mWallpaperController.adjustWallpaperWindows(); assertEquals(appWin, mDisplayContent.mWallpaperController.getWallpaperTarget()); assertTrue(r.hasFixedRotationTransform()); diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowManagerServiceTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowManagerServiceTests.java index b0d7ed660837..7ca358a12b36 100644 --- a/services/tests/wmtests/src/com/android/server/wm/WindowManagerServiceTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/WindowManagerServiceTests.java @@ -191,7 +191,7 @@ public class WindowManagerServiceTests extends WindowTestsBase { win.mViewVisibility = View.VISIBLE; win.mHasSurface = true; win.mActivityRecord.mAppStopped = true; - win.mActivityRecord.mVisibleRequested = false; + win.mActivityRecord.setVisibleRequested(false); win.mActivityRecord.setVisible(false); mWm.mWindowMap.put(win.mClient.asBinder(), win); final int w = 100; 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 8deb2825c4f9..df3b306d6739 100644 --- a/services/tests/wmtests/src/com/android/server/wm/WindowOrganizerTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/WindowOrganizerTests.java @@ -994,7 +994,7 @@ public class WindowOrganizerTests extends WindowTestsBase { final Task task = createTask(rootTaskController1); final WindowState w = createAppWindow(task, TYPE_APPLICATION, "Enlightened Window"); - w.mActivityRecord.mVisibleRequested = true; + w.mActivityRecord.setVisibleRequested(true); w.mActivityRecord.setVisible(true); BLASTSyncEngine bse = new BLASTSyncEngine(mWm); diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowProcessControllerTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowProcessControllerTests.java index 3abf7ce665ae..8bd414856394 100644 --- a/services/tests/wmtests/src/com/android/server/wm/WindowProcessControllerTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/WindowProcessControllerTests.java @@ -324,7 +324,7 @@ public class WindowProcessControllerTests extends WindowTestsBase { @Test public void testComputeOomAdjFromActivities() { final ActivityRecord activity = createActivityRecord(mWpc); - activity.mVisibleRequested = true; + activity.setVisibleRequested(true); final int[] callbackResult = { 0 }; final int visible = 1; final int paused = 2; @@ -359,7 +359,7 @@ public class WindowProcessControllerTests extends WindowTestsBase { assertEquals(visible, callbackResult[0]); callbackResult[0] = 0; - activity.mVisibleRequested = false; + activity.setVisibleRequested(false); activity.setState(PAUSED, "test"); mWpc.computeOomAdjFromActivities(callback); assertEquals(paused, callbackResult[0]); @@ -380,7 +380,7 @@ public class WindowProcessControllerTests extends WindowTestsBase { final VisibleActivityProcessTracker tracker = mAtm.mVisibleActivityProcessTracker; spyOn(tracker); final ActivityRecord activity = createActivityRecord(mWpc); - activity.mVisibleRequested = true; + activity.setVisibleRequested(true); activity.setState(STARTED, "test"); verify(tracker).onAnyActivityVisible(mWpc); @@ -398,7 +398,7 @@ public class WindowProcessControllerTests extends WindowTestsBase { assertTrue(mWpc.hasForegroundActivities()); activity.setVisibility(false); - activity.mVisibleRequested = false; + activity.setVisibleRequested(false); activity.setState(STOPPED, "test"); verify(tracker).onAllActivitiesInvisible(mWpc); @@ -413,7 +413,7 @@ public class WindowProcessControllerTests extends WindowTestsBase { @Test public void testTopActivityUiModeChangeScheduleConfigChange() { final ActivityRecord activity = createActivityRecord(mWpc); - activity.mVisibleRequested = true; + activity.setVisibleRequested(true); doReturn(true).when(activity).applyAppSpecificConfig(anyInt(), any()); mWpc.updateAppSpecificSettingsForAllActivitiesInPackage(DEFAULT_COMPONENT_PACKAGE_NAME, Configuration.UI_MODE_NIGHT_YES, LocaleList.forLanguageTags("en-XA")); @@ -423,7 +423,7 @@ public class WindowProcessControllerTests extends WindowTestsBase { @Test public void testTopActivityUiModeChangeForDifferentPackage_noScheduledConfigChange() { final ActivityRecord activity = createActivityRecord(mWpc); - activity.mVisibleRequested = true; + activity.setVisibleRequested(true); mWpc.updateAppSpecificSettingsForAllActivitiesInPackage("com.different.package", Configuration.UI_MODE_NIGHT_YES, LocaleList.forLanguageTags("en-XA")); verify(activity, never()).applyAppSpecificConfig(anyInt(), any()); 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 04d873453b2d..650286a8b111 100644 --- a/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java @@ -263,7 +263,7 @@ public class WindowStateTests extends WindowTestsBase { // Verify that app window can still be IME target as long as it is visible (even if // it is going to become invisible). - appWindow.mActivityRecord.mVisibleRequested = false; + appWindow.mActivityRecord.setVisibleRequested(false); assertTrue(appWindow.canBeImeTarget()); // Make windows invisible @@ -717,7 +717,7 @@ public class WindowStateTests extends WindowTestsBase { // No need to wait for a window of invisible activity even if the window has surface. final WindowState invisibleApp = mAppWindow; - invisibleApp.mActivityRecord.mVisibleRequested = false; + invisibleApp.mActivityRecord.setVisibleRequested(false); invisibleApp.mActivityRecord.allDrawn = false; outWaitingForDrawn.clear(); invisibleApp.requestDrawIfNeeded(outWaitingForDrawn); @@ -735,7 +735,7 @@ public class WindowStateTests extends WindowTestsBase { assertFalse(startingApp.getOrientationChanging()); // Even if the display is frozen, invisible requested window should not be affected. - startingApp.mActivityRecord.mVisibleRequested = false; + startingApp.mActivityRecord.setVisibleRequested(false); mWm.startFreezingDisplay(0, 0, mDisplayContent); doReturn(true).when(mWm.mPolicy).isScreenOn(); startingApp.getWindowFrames().setInsetsChanged(true); @@ -810,7 +810,7 @@ public class WindowStateTests extends WindowTestsBase { final WindowState win = createWindow(null /* parent */, TYPE_APPLICATION, embeddedActivity, "App window"); doReturn(true).when(embeddedActivity).isVisible(); - embeddedActivity.mVisibleRequested = true; + embeddedActivity.setVisibleRequested(true); makeWindowVisible(win); win.mLayoutSeq = win.getDisplayContent().mLayoutSeq; // Set the bounds twice: @@ -835,7 +835,7 @@ public class WindowStateTests extends WindowTestsBase { @Test public void testCantReceiveTouchWhenAppTokenHiddenRequested() { final WindowState win0 = createWindow(null, TYPE_APPLICATION, "win0"); - win0.mActivityRecord.mVisibleRequested = false; + win0.mActivityRecord.setVisibleRequested(false); assertFalse(win0.canReceiveTouchInput()); } 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 894ba3e95261..268aa3e5f5b4 100644 --- a/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java +++ b/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java @@ -715,7 +715,7 @@ class WindowTestsBase extends SystemServiceTestsBase { activity.onDisplayChanged(dc); activity.setOccludesParent(true); activity.setVisible(true); - activity.mVisibleRequested = true; + activity.setVisibleRequested(true); } static TaskFragment createTaskFragmentWithParentTask(@NonNull Task parentTask) { @@ -1209,7 +1209,7 @@ class WindowTestsBase extends SystemServiceTestsBase { mTask.moveToFront("createActivity"); } if (mVisible) { - activity.mVisibleRequested = true; + activity.setVisibleRequested(true); activity.setVisible(true); } } |