Merge "Revert "Provide IconProvider in sysui dagger"" into sc-v2-dev
diff --git a/core/api/current.txt b/core/api/current.txt
index 1d03370..f46c809 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -1298,7 +1298,7 @@
field public static final int shortcutLongLabel = 16844074; // 0x101052a
field public static final int shortcutShortLabel = 16844073; // 0x1010529
field public static final int shouldDisableView = 16843246; // 0x10101ee
- field public static final int shouldUseDefaultDisplayStateChangeTransition;
+ field public static final int shouldUseDefaultUnfoldTransition;
field public static final int showAsAction = 16843481; // 0x10102d9
field public static final int showDefault = 16843258; // 0x10101fa
field public static final int showDividers = 16843561; // 0x1010329
@@ -6927,7 +6927,7 @@
method public android.graphics.drawable.Drawable loadIcon(android.content.pm.PackageManager);
method public CharSequence loadLabel(android.content.pm.PackageManager);
method public android.graphics.drawable.Drawable loadThumbnail(android.content.pm.PackageManager);
- method public boolean shouldUseDefaultDisplayStateChangeTransition();
+ method public boolean shouldUseDefaultUnfoldTransition();
method public boolean supportsMultipleDisplays();
method public void writeToParcel(android.os.Parcel, int);
field @NonNull public static final android.os.Parcelable.Creator<android.app.WallpaperInfo> CREATOR;
@@ -17949,6 +17949,7 @@
field @NonNull public static final android.hardware.camera2.CameraCharacteristics.Key<int[]> EDGE_AVAILABLE_EDGE_MODES;
field @NonNull public static final android.hardware.camera2.CameraCharacteristics.Key<java.lang.Boolean> FLASH_INFO_AVAILABLE;
field @NonNull public static final android.hardware.camera2.CameraCharacteristics.Key<int[]> HOT_PIXEL_AVAILABLE_HOT_PIXEL_MODES;
+ field @NonNull public static final android.hardware.camera2.CameraCharacteristics.Key<android.hardware.camera2.params.DeviceStateOrientationMap> INFO_DEVICE_STATE_ORIENTATION_MAP;
field @NonNull public static final android.hardware.camera2.CameraCharacteristics.Key<java.lang.Integer> INFO_SUPPORTED_HARDWARE_LEVEL;
field @NonNull public static final android.hardware.camera2.CameraCharacteristics.Key<java.lang.String> INFO_VERSION;
field @NonNull public static final android.hardware.camera2.CameraCharacteristics.Key<android.util.Size[]> JPEG_AVAILABLE_THUMBNAIL_SIZES;
@@ -18643,6 +18644,12 @@
method public android.util.Rational getElement(int, int);
}
+ public final class DeviceStateOrientationMap {
+ method public int getSensorOrientation(long);
+ field public static final long FOLDED = 4L; // 0x4L
+ field public static final long NORMAL = 0L; // 0x0L
+ }
+
public final class ExtensionSessionConfiguration {
ctor public ExtensionSessionConfiguration(int, @NonNull java.util.List<android.hardware.camera2.params.OutputConfiguration>, @NonNull java.util.concurrent.Executor, @NonNull android.hardware.camera2.CameraExtensionSession.StateCallback);
method @NonNull public java.util.concurrent.Executor getExecutor();
@@ -46905,15 +46912,15 @@
}
@UiThread public interface AttachedSurfaceControl {
- method public default void addOnSurfaceTransformHintChangedListener(@NonNull android.view.AttachedSurfaceControl.OnSurfaceTransformHintChangedListener);
+ method public default void addOnBufferTransformHintChangedListener(@NonNull android.view.AttachedSurfaceControl.OnBufferTransformHintChangedListener);
method public boolean applyTransactionOnDraw(@NonNull android.view.SurfaceControl.Transaction);
method @Nullable public android.view.SurfaceControl.Transaction buildReparentTransaction(@NonNull android.view.SurfaceControl);
- method public default int getSurfaceTransformHint();
- method public default void removeOnSurfaceTransformHintChangedListener(@NonNull android.view.AttachedSurfaceControl.OnSurfaceTransformHintChangedListener);
+ method public default int getBufferTransformHint();
+ method public default void removeOnBufferTransformHintChangedListener(@NonNull android.view.AttachedSurfaceControl.OnBufferTransformHintChangedListener);
}
- @UiThread public static interface AttachedSurfaceControl.OnSurfaceTransformHintChangedListener {
- method public void onSurfaceTransformHintChanged(int);
+ @UiThread public static interface AttachedSurfaceControl.OnBufferTransformHintChangedListener {
+ method public void onBufferTransformHintChanged(int);
}
public final class Choreographer {
@@ -48403,6 +48410,12 @@
method public void readFromParcel(android.os.Parcel);
method public void release();
method public void writeToParcel(android.os.Parcel, int);
+ field public static final int BUFFER_TRANSFORM_IDENTITY = 0; // 0x0
+ field public static final int BUFFER_TRANSFORM_MIRROR_HORIZONTAL = 1; // 0x1
+ field public static final int BUFFER_TRANSFORM_MIRROR_VERTICAL = 2; // 0x2
+ field public static final int BUFFER_TRANSFORM_ROTATE_180 = 3; // 0x3
+ field public static final int BUFFER_TRANSFORM_ROTATE_270 = 7; // 0x7
+ field public static final int BUFFER_TRANSFORM_ROTATE_90 = 4; // 0x4
field @NonNull public static final android.os.Parcelable.Creator<android.view.SurfaceControl> CREATOR;
}
diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java
index 22091fc..d229fd0 100644
--- a/core/java/android/app/Activity.java
+++ b/core/java/android/app/Activity.java
@@ -77,7 +77,6 @@
import android.os.Looper;
import android.os.Parcelable;
import android.os.PersistableBundle;
-import android.os.PowerManager;
import android.os.Process;
import android.os.RemoteException;
import android.os.ServiceManager.ServiceNotFoundException;
@@ -8804,9 +8803,7 @@
* the activity is visible after the screen is turned on when the lockscreen is up. In addition,
* if this flag is set and the activity calls {@link
* KeyguardManager#requestDismissKeyguard(Activity, KeyguardManager.KeyguardDismissCallback)}
- * the screen will turn on. If the screen is off and device is not secured, this flag can turn
- * screen on and dismiss keyguard to make this activity visible and resume, which can be used to
- * replace {@link PowerManager#ACQUIRE_CAUSES_WAKEUP}
+ * the screen will turn on.
*
* @param turnScreenOn {@code true} to turn on the screen; {@code false} otherwise.
*
diff --git a/core/java/android/app/WallpaperInfo.java b/core/java/android/app/WallpaperInfo.java
index a969b10..99d4064 100644
--- a/core/java/android/app/WallpaperInfo.java
+++ b/core/java/android/app/WallpaperInfo.java
@@ -81,7 +81,7 @@
final int mContextDescriptionResource;
final boolean mShowMetadataInPreview;
final boolean mSupportsAmbientMode;
- final boolean mShouldUseDefaultDisplayStateChangeTransition;
+ final boolean mShouldUseDefaultUnfoldTransition;
final String mSettingsSliceUri;
final boolean mSupportMultipleDisplays;
@@ -146,9 +146,9 @@
mSupportsAmbientMode = sa.getBoolean(
com.android.internal.R.styleable.Wallpaper_supportsAmbientMode,
false);
- mShouldUseDefaultDisplayStateChangeTransition = sa.getBoolean(
+ mShouldUseDefaultUnfoldTransition = sa.getBoolean(
com.android.internal.R.styleable
- .Wallpaper_shouldUseDefaultDisplayStateChangeTransition, true);
+ .Wallpaper_shouldUseDefaultUnfoldTransition, true);
mSettingsSliceUri = sa.getString(
com.android.internal.R.styleable.Wallpaper_settingsSliceUri);
mSupportMultipleDisplays = sa.getBoolean(
@@ -175,7 +175,7 @@
mSupportsAmbientMode = source.readInt() != 0;
mSettingsSliceUri = source.readString();
mSupportMultipleDisplays = source.readInt() != 0;
- mShouldUseDefaultDisplayStateChangeTransition = source.readInt() != 0;
+ mShouldUseDefaultUnfoldTransition = source.readInt() != 0;
mService = ResolveInfo.CREATOR.createFromParcel(source);
}
@@ -400,24 +400,24 @@
/**
* Returns whether this wallpaper should receive default zooming updates when the device
- * changes its display state (e.g. when folding or unfolding a foldable device).
+ * changes its state (e.g. when folding or unfolding a foldable device).
* If set to false the wallpaper will not receive zoom events when changing the device state,
* so it can implement its own transition instead.
* <p>
* This corresponds to the value {@link
- * android.R.styleable#Wallpaper_shouldUseDefaultDisplayStateChangeTransition} in the
+ * android.R.styleable#Wallpaper_shouldUseDefaultUnfoldTransition} in the
* XML description of the wallpaper.
* <p>
* The default value is {@code true}.
*
- * @see android.R.styleable#Wallpaper_shouldUseDefaultDisplayStateChangeTransition
+ * @see android.R.styleable#Wallpaper_shouldUseDefaultUnfoldTransition
* @return {@code true} if wallpaper should receive default device state change
* transition updates
*
- * @attr ref android.R.styleable#Wallpaper_shouldUseDefaultDisplayStateChangeTransition
+ * @attr ref android.R.styleable#Wallpaper_shouldUseDefaultUnfoldTransition
*/
- public boolean shouldUseDefaultDisplayStateChangeTransition() {
- return mShouldUseDefaultDisplayStateChangeTransition;
+ public boolean shouldUseDefaultUnfoldTransition() {
+ return mShouldUseDefaultUnfoldTransition;
}
public void dump(Printer pw, String prefix) {
@@ -450,7 +450,7 @@
dest.writeInt(mSupportsAmbientMode ? 1 : 0);
dest.writeString(mSettingsSliceUri);
dest.writeInt(mSupportMultipleDisplays ? 1 : 0);
- dest.writeInt(mShouldUseDefaultDisplayStateChangeTransition ? 1 : 0);
+ dest.writeInt(mShouldUseDefaultUnfoldTransition ? 1 : 0);
mService.writeToParcel(dest, flags);
}
diff --git a/core/java/android/hardware/camera2/CameraCharacteristics.java b/core/java/android/hardware/camera2/CameraCharacteristics.java
index e0138c5..ddac22c 100644
--- a/core/java/android/hardware/camera2/CameraCharacteristics.java
+++ b/core/java/android/hardware/camera2/CameraCharacteristics.java
@@ -22,15 +22,18 @@
import android.hardware.camera2.impl.CameraMetadataNative;
import android.hardware.camera2.impl.PublicKey;
import android.hardware.camera2.impl.SyntheticKey;
+import android.hardware.camera2.params.DeviceStateOrientationMap;
import android.hardware.camera2.params.RecommendedStreamConfigurationMap;
import android.hardware.camera2.params.SessionConfiguration;
import android.hardware.camera2.utils.TypeReference;
import android.os.Build;
+import android.util.Log;
import android.util.Rational;
+import com.android.internal.annotations.GuardedBy;
+
import java.util.ArrayList;
import java.util.Collections;
-import java.util.HashSet;
import java.util.List;
import java.util.Set;
@@ -202,8 +205,25 @@
private List<CaptureResult.Key<?>> mAvailableResultKeys;
private ArrayList<RecommendedStreamConfigurationMap> mRecommendedConfigurations;
+ private final Object mLock = new Object();
+ @GuardedBy("mLock")
+ private boolean mFoldedDeviceState;
+
+ private final CameraManager.DeviceStateListener mFoldStateListener =
+ new CameraManager.DeviceStateListener() {
+ @Override
+ public final void onDeviceStateChanged(boolean folded) {
+ synchronized (mLock) {
+ mFoldedDeviceState = folded;
+ }
+ }};
+
+ private static final String TAG = "CameraCharacteristics";
+
/**
* Takes ownership of the passed-in properties object
+ *
+ * @param properties Camera properties.
* @hide
*/
public CameraCharacteristics(CameraMetadataNative properties) {
@@ -220,6 +240,41 @@
}
/**
+ * Return the device state listener for this Camera characteristics instance
+ */
+ CameraManager.DeviceStateListener getDeviceStateListener() { return mFoldStateListener; }
+
+ /**
+ * Overrides the property value
+ *
+ * <p>Check whether a given property value needs to be overridden in some specific
+ * case.</p>
+ *
+ * @param key The characteristics field to override.
+ * @return The value of overridden property, or {@code null} if the property doesn't need an
+ * override.
+ */
+ @Nullable
+ private <T> T overrideProperty(Key<T> key) {
+ if (CameraCharacteristics.SENSOR_ORIENTATION.equals(key) && (mFoldStateListener != null) &&
+ (mProperties.get(CameraCharacteristics.INFO_DEVICE_STATE_ORIENTATIONS) != null)) {
+ DeviceStateOrientationMap deviceStateOrientationMap =
+ mProperties.get(CameraCharacteristics.INFO_DEVICE_STATE_ORIENTATION_MAP);
+ synchronized (mLock) {
+ Integer ret = deviceStateOrientationMap.getSensorOrientation(mFoldedDeviceState ?
+ DeviceStateOrientationMap.FOLDED : DeviceStateOrientationMap.NORMAL);
+ if (ret >= 0) {
+ return (T) ret;
+ } else {
+ Log.w(TAG, "No valid device state to orientation mapping! Using default!");
+ }
+ }
+ }
+
+ return null;
+ }
+
+ /**
* Get a camera characteristics field value.
*
* <p>The field definitions can be
@@ -235,7 +290,8 @@
*/
@Nullable
public <T> T get(Key<T> key) {
- return mProperties.get(key);
+ T propertyOverride = overrideProperty(key);
+ return (propertyOverride != null) ? propertyOverride : mProperties.get(key);
}
/**
@@ -3993,11 +4049,26 @@
* upright on the device screen in its native orientation.</p>
* <p>Also defines the direction of rolling shutter readout, which is from top to bottom in
* the sensor's coordinate system.</p>
+ * <p>Starting with Android API level 32, camera clients that query the orientation via
+ * {@link android.hardware.camera2.CameraCharacteristics#get } on foldable devices which
+ * include logical cameras can receive a value that can dynamically change depending on the
+ * device/fold state.
+ * Clients are advised to not cache or store the orientation value of such logical sensors.
+ * In case repeated queries to CameraCharacteristics are not preferred, then clients can
+ * also access the entire mapping from device state to sensor orientation in
+ * {@link android.hardware.camera2.params.DeviceStateOrientationMap }.
+ * Do note that a dynamically changing sensor orientation value in camera characteristics
+ * will not be the best way to establish the orientation per frame. Clients that want to
+ * know the sensor orientation of a particular captured frame should query the
+ * {@link CaptureResult#LOGICAL_MULTI_CAMERA_ACTIVE_PHYSICAL_ID android.logicalMultiCamera.activePhysicalId} from the corresponding capture result and
+ * check the respective physical camera orientation.</p>
* <p><b>Units</b>: Degrees of clockwise rotation; always a multiple of
* 90</p>
* <p><b>Range of valid values:</b><br>
* 0, 90, 180, 270</p>
* <p>This key is available on all devices.</p>
+ *
+ * @see CaptureResult#LOGICAL_MULTI_CAMERA_ACTIVE_PHYSICAL_ID
*/
@PublicKey
@NonNull
@@ -4307,6 +4378,46 @@
new Key<String>("android.info.version", String.class);
/**
+ * <p>This lists the mapping between a device folding state and
+ * specific camera sensor orientation for logical cameras on a foldable device.</p>
+ * <p>Logical cameras on foldable devices can support sensors with different orientation
+ * values. The orientation value may need to change depending on the specific folding
+ * state. Information about the mapping between the device folding state and the
+ * sensor orientation can be obtained in
+ * {@link android.hardware.camera2.params.DeviceStateOrientationMap }.
+ * Device state orientation maps are optional and maybe present on devices that support
+ * {@link CaptureRequest#SCALER_ROTATE_AND_CROP android.scaler.rotateAndCrop}.</p>
+ * <p><b>Optional</b> - The value for this key may be {@code null} on some devices.</p>
+ * <p><b>Limited capability</b> -
+ * Present on all camera devices that report being at least {@link CameraCharacteristics#INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED HARDWARE_LEVEL_LIMITED} devices in the
+ * {@link CameraCharacteristics#INFO_SUPPORTED_HARDWARE_LEVEL android.info.supportedHardwareLevel} key</p>
+ *
+ * @see CameraCharacteristics#INFO_SUPPORTED_HARDWARE_LEVEL
+ * @see CaptureRequest#SCALER_ROTATE_AND_CROP
+ */
+ @PublicKey
+ @NonNull
+ @SyntheticKey
+ public static final Key<android.hardware.camera2.params.DeviceStateOrientationMap> INFO_DEVICE_STATE_ORIENTATION_MAP =
+ new Key<android.hardware.camera2.params.DeviceStateOrientationMap>("android.info.deviceStateOrientationMap", android.hardware.camera2.params.DeviceStateOrientationMap.class);
+
+ /**
+ * <p>HAL must populate the array with
+ * (hardware::camera::provider::V2_5::DeviceState, sensorOrientation) pairs for each
+ * supported device state bitwise combination.</p>
+ * <p><b>Units</b>: (device fold state, sensor orientation) x n</p>
+ * <p><b>Optional</b> - The value for this key may be {@code null} on some devices.</p>
+ * <p><b>Limited capability</b> -
+ * Present on all camera devices that report being at least {@link CameraCharacteristics#INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED HARDWARE_LEVEL_LIMITED} devices in the
+ * {@link CameraCharacteristics#INFO_SUPPORTED_HARDWARE_LEVEL android.info.supportedHardwareLevel} key</p>
+ *
+ * @see CameraCharacteristics#INFO_SUPPORTED_HARDWARE_LEVEL
+ * @hide
+ */
+ public static final Key<long[]> INFO_DEVICE_STATE_ORIENTATIONS =
+ new Key<long[]>("android.info.deviceStateOrientations", long[].class);
+
+ /**
* <p>The maximum number of frames that can occur after a request
* (different than the previous) has been submitted, and before the
* result's state becomes synchronized.</p>
diff --git a/core/java/android/hardware/camera2/CameraManager.java b/core/java/android/hardware/camera2/CameraManager.java
index 5833b3d..9bb901f 100644
--- a/core/java/android/hardware/camera2/CameraManager.java
+++ b/core/java/android/hardware/camera2/CameraManager.java
@@ -30,15 +30,19 @@
import android.hardware.camera2.impl.CameraDeviceImpl;
import android.hardware.camera2.impl.CameraInjectionSessionImpl;
import android.hardware.camera2.impl.CameraMetadataNative;
+import android.hardware.camera2.params.DeviceStateOrientationMap;
import android.hardware.camera2.params.ExtensionSessionConfiguration;
import android.hardware.camera2.params.SessionConfiguration;
import android.hardware.camera2.params.StreamConfiguration;
import android.hardware.camera2.utils.CameraIdAndSessionConfiguration;
import android.hardware.camera2.utils.ConcurrentCameraIdCombination;
+import android.hardware.devicestate.DeviceStateManager;
import android.hardware.display.DisplayManager;
import android.os.Binder;
import android.os.DeadObjectException;
import android.os.Handler;
+import android.os.HandlerExecutor;
+import android.os.HandlerThread;
import android.os.IBinder;
import android.os.RemoteException;
import android.os.ServiceManager;
@@ -50,6 +54,10 @@
import android.util.Size;
import android.view.Display;
+import com.android.internal.annotations.GuardedBy;
+import com.android.internal.util.ArrayUtils;
+
+import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Comparator;
@@ -97,6 +105,80 @@
synchronized(mLock) {
mContext = context;
}
+
+ mHandlerThread = new HandlerThread(TAG);
+ mHandlerThread.start();
+ mHandler = new Handler(mHandlerThread.getLooper());
+ mFoldStateListener = new FoldStateListener(context);
+ context.getSystemService(DeviceStateManager.class)
+ .registerCallback(new HandlerExecutor(mHandler), mFoldStateListener);
+ }
+
+ private HandlerThread mHandlerThread;
+ private Handler mHandler;
+ private FoldStateListener mFoldStateListener;
+ @GuardedBy("mLock")
+ private ArrayList<WeakReference<DeviceStateListener>> mDeviceStateListeners = new ArrayList<>();
+ private boolean mFoldedDeviceState;
+
+ /**
+ * @hide
+ */
+ public interface DeviceStateListener {
+ void onDeviceStateChanged(boolean folded);
+ }
+
+ private final class FoldStateListener implements DeviceStateManager.DeviceStateCallback {
+ private final int[] mFoldedDeviceStates;
+
+ public FoldStateListener(Context context) {
+ mFoldedDeviceStates = context.getResources().getIntArray(
+ com.android.internal.R.array.config_foldedDeviceStates);
+ }
+
+ private void handleStateChange(int state) {
+ boolean folded = ArrayUtils.contains(mFoldedDeviceStates, state);
+ synchronized (mLock) {
+ mFoldedDeviceState = folded;
+ ArrayList<WeakReference<DeviceStateListener>> invalidListeners = new ArrayList<>();
+ for (WeakReference<DeviceStateListener> listener : mDeviceStateListeners) {
+ DeviceStateListener callback = listener.get();
+ if (callback != null) {
+ callback.onDeviceStateChanged(folded);
+ } else {
+ invalidListeners.add(listener);
+ }
+ }
+ if (!invalidListeners.isEmpty()) {
+ mDeviceStateListeners.removeAll(invalidListeners);
+ }
+ }
+ }
+
+ @Override
+ public final void onBaseStateChanged(int state) {
+ handleStateChange(state);
+ }
+
+ @Override
+ public final void onStateChanged(int state) {
+ handleStateChange(state);
+ }
+ }
+
+ /**
+ * Register a {@link CameraCharacteristics} device state listener
+ *
+ * @param chars Camera characteristics that need to receive device state updates
+ *
+ * @hide
+ */
+ public void registerDeviceStateListener(@NonNull CameraCharacteristics chars) {
+ synchronized (mLock) {
+ DeviceStateListener listener = chars.getDeviceStateListener();
+ listener.onDeviceStateChanged(mFoldedDeviceState);
+ mDeviceStateListeners.add(new WeakReference<>(listener));
+ }
}
/**
@@ -504,6 +586,7 @@
"Camera service is currently unavailable", e);
}
}
+ registerDeviceStateListener(characteristics);
return characteristics;
}
@@ -1327,8 +1410,7 @@
private ICameraService mCameraService;
// Singleton, don't allow construction
- private CameraManagerGlobal() {
- }
+ private CameraManagerGlobal() { }
public static final boolean sCameraServiceDisabled =
SystemProperties.getBoolean("config.disable_cameraservice", false);
diff --git a/core/java/android/hardware/camera2/CaptureFailure.java b/core/java/android/hardware/camera2/CaptureFailure.java
index 20ca4a3..032ed7e 100644
--- a/core/java/android/hardware/camera2/CaptureFailure.java
+++ b/core/java/android/hardware/camera2/CaptureFailure.java
@@ -59,7 +59,7 @@
private final CaptureRequest mRequest;
private final int mReason;
- private final boolean mDropped;
+ private final boolean mWasImageCaptured;
private final int mSequenceId;
private final long mFrameNumber;
private final String mErrorPhysicalCameraId;
@@ -68,10 +68,11 @@
* @hide
*/
public CaptureFailure(CaptureRequest request, int reason,
- boolean dropped, int sequenceId, long frameNumber, String errorPhysicalCameraId) {
+ boolean wasImageCaptured, int sequenceId, long frameNumber,
+ String errorPhysicalCameraId) {
mRequest = request;
mReason = reason;
- mDropped = dropped;
+ mWasImageCaptured = wasImageCaptured;
mSequenceId = sequenceId;
mFrameNumber = frameNumber;
mErrorPhysicalCameraId = errorPhysicalCameraId;
@@ -141,7 +142,7 @@
* @return boolean True if the image was captured, false otherwise.
*/
public boolean wasImageCaptured() {
- return !mDropped;
+ return mWasImageCaptured;
}
/**
diff --git a/core/java/android/hardware/camera2/impl/CameraAdvancedExtensionSessionImpl.java b/core/java/android/hardware/camera2/impl/CameraAdvancedExtensionSessionImpl.java
index 8da6551..b8443fb 100644
--- a/core/java/android/hardware/camera2/impl/CameraAdvancedExtensionSessionImpl.java
+++ b/core/java/android/hardware/camera2/impl/CameraAdvancedExtensionSessionImpl.java
@@ -873,21 +873,19 @@
@Override
public int submitBurst(List<Request> requests, IRequestCallback callback) {
int seqId = -1;
- synchronized (mInterfaceLock) {
- try {
- CaptureCallbackHandler captureCallback = new CaptureCallbackHandler(callback);
- ArrayList<CaptureRequest> captureRequests = new ArrayList<>();
- for (Request request : requests) {
- captureRequests.add(initializeCaptureRequest(mCameraDevice, request,
- mCameraConfigMap));
- }
- seqId = mCaptureSession.captureBurstRequests(captureRequests,
- new CameraExtensionUtils.HandlerExecutor(mHandler), captureCallback);
- } catch (CameraAccessException e) {
- Log.e(TAG, "Failed to submit capture requests!");
- } catch (IllegalStateException e) {
- Log.e(TAG, "Capture session closed!");
+ try {
+ CaptureCallbackHandler captureCallback = new CaptureCallbackHandler(callback);
+ ArrayList<CaptureRequest> captureRequests = new ArrayList<>();
+ for (Request request : requests) {
+ captureRequests.add(initializeCaptureRequest(mCameraDevice, request,
+ mCameraConfigMap));
}
+ seqId = mCaptureSession.captureBurstRequests(captureRequests,
+ new CameraExtensionUtils.HandlerExecutor(mHandler), captureCallback);
+ } catch (CameraAccessException e) {
+ Log.e(TAG, "Failed to submit capture requests!");
+ } catch (IllegalStateException e) {
+ Log.e(TAG, "Capture session closed!");
}
return seqId;
@@ -896,18 +894,16 @@
@Override
public int setRepeating(Request request, IRequestCallback callback) {
int seqId = -1;
- synchronized (mInterfaceLock) {
- try {
- CaptureRequest repeatingRequest = initializeCaptureRequest(mCameraDevice,
- request, mCameraConfigMap);
- CaptureCallbackHandler captureCallback = new CaptureCallbackHandler(callback);
- seqId = mCaptureSession.setSingleRepeatingRequest(repeatingRequest,
- new CameraExtensionUtils.HandlerExecutor(mHandler), captureCallback);
- } catch (CameraAccessException e) {
- Log.e(TAG, "Failed to enable repeating request!");
- } catch (IllegalStateException e) {
- Log.e(TAG, "Capture session closed!");
- }
+ try {
+ CaptureRequest repeatingRequest = initializeCaptureRequest(mCameraDevice,
+ request, mCameraConfigMap);
+ CaptureCallbackHandler captureCallback = new CaptureCallbackHandler(callback);
+ seqId = mCaptureSession.setSingleRepeatingRequest(repeatingRequest,
+ new CameraExtensionUtils.HandlerExecutor(mHandler), captureCallback);
+ } catch (CameraAccessException e) {
+ Log.e(TAG, "Failed to enable repeating request!");
+ } catch (IllegalStateException e) {
+ Log.e(TAG, "Capture session closed!");
}
return seqId;
@@ -915,27 +911,23 @@
@Override
public void abortCaptures() {
- synchronized (mInterfaceLock) {
- try {
- mCaptureSession.abortCaptures();
- } catch (CameraAccessException e) {
- Log.e(TAG, "Failed during capture abort!");
- } catch (IllegalStateException e) {
- Log.e(TAG, "Capture session closed!");
- }
+ try {
+ mCaptureSession.abortCaptures();
+ } catch (CameraAccessException e) {
+ Log.e(TAG, "Failed during capture abort!");
+ } catch (IllegalStateException e) {
+ Log.e(TAG, "Capture session closed!");
}
}
@Override
public void stopRepeating() {
- synchronized (mInterfaceLock) {
- try {
- mCaptureSession.stopRepeating();
- } catch (CameraAccessException e) {
- Log.e(TAG, "Failed during repeating capture stop!");
- } catch (IllegalStateException e) {
- Log.e(TAG, "Capture session closed!");
- }
+ try {
+ mCaptureSession.stopRepeating();
+ } catch (CameraAccessException e) {
+ Log.e(TAG, "Failed during repeating capture stop!");
+ } catch (IllegalStateException e) {
+ Log.e(TAG, "Capture session closed!");
}
}
}
diff --git a/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java b/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java
index 4708f3e..8864939 100644
--- a/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java
+++ b/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java
@@ -1867,7 +1867,7 @@
final CaptureFailure failure = new CaptureFailure(
request,
reason,
- /*dropped*/ mayHaveBuffers,
+ mayHaveBuffers,
requestId,
frameNumber,
errorPhysicalCameraId);
diff --git a/core/java/android/hardware/camera2/impl/CameraMetadataNative.java b/core/java/android/hardware/camera2/impl/CameraMetadataNative.java
index 196134b..3745022 100644
--- a/core/java/android/hardware/camera2/impl/CameraMetadataNative.java
+++ b/core/java/android/hardware/camera2/impl/CameraMetadataNative.java
@@ -50,6 +50,7 @@
import android.hardware.camera2.marshal.impl.MarshalQueryableStreamConfigurationDuration;
import android.hardware.camera2.marshal.impl.MarshalQueryableString;
import android.hardware.camera2.params.Capability;
+import android.hardware.camera2.params.DeviceStateOrientationMap;
import android.hardware.camera2.params.Face;
import android.hardware.camera2.params.HighSpeedVideoConfiguration;
import android.hardware.camera2.params.LensShadingMap;
@@ -754,7 +755,7 @@
});
sGetCommandMap.put(
CaptureResult.STATISTICS_LENS_SHADING_CORRECTION_MAP.getNativeKey(),
- new GetCommand() {
+ new GetCommand() {
@Override
@SuppressWarnings("unchecked")
public <T> T getValue(CameraMetadataNative metadata, Key<T> key) {
@@ -762,6 +763,15 @@
}
});
sGetCommandMap.put(
+ CameraCharacteristics.INFO_DEVICE_STATE_ORIENTATION_MAP.getNativeKey(),
+ new GetCommand() {
+ @Override
+ @SuppressWarnings("unchecked")
+ public <T> T getValue(CameraMetadataNative metadata, Key<T> key) {
+ return (T) metadata.getDeviceStateOrientationMap();
+ }
+ });
+ sGetCommandMap.put(
CaptureResult.STATISTICS_OIS_SAMPLES.getNativeKey(),
new GetCommand() {
@Override
@@ -994,6 +1004,18 @@
return map;
}
+ private DeviceStateOrientationMap getDeviceStateOrientationMap() {
+ long[] mapArray = getBase(CameraCharacteristics.INFO_DEVICE_STATE_ORIENTATIONS);
+
+ // Do not warn if map is null while s is not. This is valid.
+ if (mapArray == null) {
+ return null;
+ }
+
+ DeviceStateOrientationMap map = new DeviceStateOrientationMap(mapArray);
+ return map;
+ }
+
private Location getGpsLocation() {
String processingMethod = get(CaptureResult.JPEG_GPS_PROCESSING_METHOD);
double[] coords = get(CaptureResult.JPEG_GPS_COORDINATES);
diff --git a/core/java/android/hardware/camera2/params/DeviceStateOrientationMap.java b/core/java/android/hardware/camera2/params/DeviceStateOrientationMap.java
new file mode 100644
index 0000000..3907f04
--- /dev/null
+++ b/core/java/android/hardware/camera2/params/DeviceStateOrientationMap.java
@@ -0,0 +1,155 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.camera2.params;
+
+import android.annotation.LongDef;
+import android.hardware.camera2.CameraCharacteristics;
+import android.hardware.camera2.utils.HashCodeHelpers;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.Objects;
+
+/**
+ * Immutable class that maps the device fold state to sensor orientation.
+ *
+ * <p>Some {@link CameraCharacteristics#REQUEST_AVAILABLE_CAPABILITIES_LOGICAL_MULTI_CAMERA logical}
+ * cameras on foldables can include physical sensors with different sensor orientation
+ * values. As a result, the values of the logical camera device can potentially change depending
+ * on the device fold state.</p>
+ *
+ * <p>The device fold state to sensor orientation map will contain information about the
+ * respective logical camera sensor orientation given a device state. Clients
+ * can query the mapping for all possible supported folded states.
+ *
+ * @see CameraCharacteristics#SENSOR_ORIENTATION
+ */
+public final class DeviceStateOrientationMap {
+ /**
+ * Needs to be kept in sync with the HIDL/AIDL DeviceState
+ */
+
+ /**
+ * The device is in its normal physical configuration. This is the default if the
+ * device does not support multiple different states.
+ */
+ public static final long NORMAL = 0;
+
+ /**
+ * The device is folded. If not set, the device is unfolded or does not
+ * support folding.
+ *
+ * The exact point when this status change happens during the folding
+ * operation is device-specific.
+ */
+ public static final long FOLDED = 1 << 2;
+
+ /** @hide */
+ @Retention(RetentionPolicy.SOURCE)
+ @LongDef(prefix = {"DEVICE_STATE"}, value =
+ {NORMAL,
+ FOLDED })
+ public @interface DeviceState {};
+
+ private final HashMap<Long, Integer> mDeviceStateOrientationMap = new HashMap<>();
+
+ /**
+ * Create a new immutable DeviceStateOrientationMap instance.
+ *
+ * <p>This constructor takes over the array; do not write to the array afterwards.</p>
+ *
+ * @param elements
+ * An array of elements describing the map
+ *
+ * @throws IllegalArgumentException
+ * if the {@code elements} array length is invalid, not divisible by 2 or contains
+ * invalid element values
+ * @throws NullPointerException
+ * if {@code elements} is {@code null}
+ *
+ * @hide
+ */
+ public DeviceStateOrientationMap(final long[] elements) {
+ mElements = Objects.requireNonNull(elements, "elements must not be null");
+ if ((elements.length % 2) != 0) {
+ throw new IllegalArgumentException("Device state orientation map length " +
+ elements.length + " is not even!");
+ }
+
+ for (int i = 0; i < elements.length; i += 2) {
+ if ((elements[i+1] % 90) != 0) {
+ throw new IllegalArgumentException("Sensor orientation not divisible by 90: " +
+ elements[i+1]);
+ }
+
+ mDeviceStateOrientationMap.put(elements[i], Math.toIntExact(elements[i + 1]));
+ }
+ }
+
+ /**
+ * Return the logical camera sensor orientation given a specific device fold state.
+ *
+ * @param deviceState Device fold state
+ *
+ * @return Valid {@link android.hardware.camera2.CameraCharacteristics#SENSOR_ORIENTATION} for
+ * any supported device fold state
+ *
+ * @throws IllegalArgumentException if the given device state is invalid
+ */
+ public int getSensorOrientation(@DeviceState long deviceState) {
+ if (!mDeviceStateOrientationMap.containsKey(deviceState)) {
+ throw new IllegalArgumentException("Invalid device state: " + deviceState);
+ }
+
+ return mDeviceStateOrientationMap.get(deviceState);
+ }
+
+ /**
+ * Check if this DeviceStateOrientationMap is equal to another DeviceStateOrientationMap.
+ *
+ * <p>Two device state orientation maps are equal if and only if all of their elements are
+ * {@link Object#equals equal}.</p>
+ *
+ * @return {@code true} if the objects were equal, {@code false} otherwise
+ */
+ @Override
+ public boolean equals(final Object obj) {
+ if (obj == null) {
+ return false;
+ }
+ if (this == obj) {
+ return true;
+ }
+ if (obj instanceof DeviceStateOrientationMap) {
+ final DeviceStateOrientationMap other = (DeviceStateOrientationMap) obj;
+ return Arrays.equals(mElements, other.mElements);
+ }
+ return false;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public int hashCode() {
+ return HashCodeHelpers.hashCodeGeneric(mElements);
+ }
+
+ private final long[] mElements;
+}
diff --git a/core/java/android/inputmethodservice/InputMethodService.java b/core/java/android/inputmethodservice/InputMethodService.java
index c0f0081..9721279 100644
--- a/core/java/android/inputmethodservice/InputMethodService.java
+++ b/core/java/android/inputmethodservice/InputMethodService.java
@@ -790,11 +790,6 @@
return;
}
- if (Trace.isEnabled()) {
- Binder.enableTracing();
- } else {
- Binder.disableTracing();
- }
Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "IMS.showSoftInput");
ImeTracing.getInstance().triggerServiceDump(
"InputMethodService.InputMethodImpl#showSoftInput", InputMethodService.this,
diff --git a/core/java/android/view/AttachedSurfaceControl.java b/core/java/android/view/AttachedSurfaceControl.java
index b2fc9a0..69af2a5 100644
--- a/core/java/android/view/AttachedSurfaceControl.java
+++ b/core/java/android/view/AttachedSurfaceControl.java
@@ -18,6 +18,7 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.UiThread;
+import android.hardware.HardwareBuffer;
/**
* Provides an interface to the root-Surface of a View Hierarchy or Window. This
@@ -84,41 +85,43 @@
* Note, when using ANativeWindow APIs in conjunction with a NativeActivity Surface or
* SurfaceView Surface, the buffer producer will already have access to the transform hint and
* no additional work is needed.
+ *
+ * @see HardwareBuffer
*/
- default @Surface.Rotation int getSurfaceTransformHint() {
- return Surface.ROTATION_0;
+ default @SurfaceControl.BufferTransform int getBufferTransformHint() {
+ return SurfaceControl.BUFFER_TRANSFORM_IDENTITY;
}
/**
- * Surface transform hint change listener.
- * @see #getSurfaceTransformHint
+ * Buffer transform hint change listener.
+ * @see #getBufferTransformHint
*/
@UiThread
- interface OnSurfaceTransformHintChangedListener {
+ interface OnBufferTransformHintChangedListener {
/**
* @param hint new surface transform hint
- * @see #getSurfaceTransformHint
+ * @see #getBufferTransformHint
*/
- void onSurfaceTransformHintChanged(@Surface.Rotation int hint);
+ void onBufferTransformHintChanged(@SurfaceControl.BufferTransform int hint);
}
/**
- * Registers a surface transform hint changed listener to receive notifications about when
+ * Registers a {@link OnBufferTransformHintChangedListener} to receive notifications about when
* the transform hint changes.
*
- * @see #getSurfaceTransformHint
- * @see #removeOnSurfaceTransformHintChangedListener
+ * @see #getBufferTransformHint
+ * @see #removeOnBufferTransformHintChangedListener
*/
- default void addOnSurfaceTransformHintChangedListener(
- @NonNull OnSurfaceTransformHintChangedListener listener) {
+ default void addOnBufferTransformHintChangedListener(
+ @NonNull OnBufferTransformHintChangedListener listener) {
}
/**
- * Unregisters a surface transform hint changed listener.
+ * Unregisters a {@link OnBufferTransformHintChangedListener}.
*
- * @see #addOnSurfaceTransformHintChangedListener
+ * @see #addOnBufferTransformHintChangedListener
*/
- default void removeOnSurfaceTransformHintChangedListener(
- @NonNull OnSurfaceTransformHintChangedListener listener) {
+ default void removeOnBufferTransformHintChangedListener(
+ @NonNull OnBufferTransformHintChangedListener listener) {
}
}
diff --git a/core/java/android/view/SurfaceControl.java b/core/java/android/view/SurfaceControl.java
index 5b8dc40..f1434c3 100644
--- a/core/java/android/view/SurfaceControl.java
+++ b/core/java/android/view/SurfaceControl.java
@@ -23,6 +23,7 @@
import static android.graphics.Matrix.MTRANS_X;
import static android.graphics.Matrix.MTRANS_Y;
import static android.view.SurfaceControlProto.HASH_CODE;
+import static android.view.SurfaceControlProto.LAYER_ID;
import static android.view.SurfaceControlProto.NAME;
import android.annotation.FloatRange;
@@ -242,6 +243,77 @@
private static native int nativeGetGPUContextPriority();
private static native void nativeSetTransformHint(long nativeObject, int transformHint);
private static native int nativeGetTransformHint(long nativeObject);
+ private static native int nativeGetLayerId(long nativeObject);
+
+ /**
+ * Transforms that can be applied to buffers as they are displayed to a window.
+ *
+ * Supported transforms are any combination of horizontal mirror, vertical mirror, and
+ * clock-wise 90 degree rotation, in that order. Rotations of 180 and 270 degrees are made up
+ * of those basic transforms.
+ * Mirrors {@code ANativeWindowTransform} definitions.
+ * @hide
+ */
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef(prefix = {"BUFFER_TRANSFORM_"},
+ value = {BUFFER_TRANSFORM_IDENTITY, BUFFER_TRANSFORM_MIRROR_HORIZONTAL,
+ BUFFER_TRANSFORM_MIRROR_VERTICAL, BUFFER_TRANSFORM_ROTATE_90,
+ BUFFER_TRANSFORM_ROTATE_180, BUFFER_TRANSFORM_ROTATE_270,
+ BUFFER_TRANSFORM_MIRROR_HORIZONTAL | BUFFER_TRANSFORM_ROTATE_90,
+ BUFFER_TRANSFORM_MIRROR_VERTICAL | BUFFER_TRANSFORM_ROTATE_90})
+ public @interface BufferTransform {
+ }
+
+ /**
+ * Identity transform.
+ *
+ * These transforms that can be applied to buffers as they are displayed to a window.
+ * @see HardwareBuffer
+ *
+ * Supported transforms are any combination of horizontal mirror, vertical mirror, and
+ * clock-wise 90 degree rotation, in that order. Rotations of 180 and 270 degrees are
+ * made up of those basic transforms.
+ */
+ public static final int BUFFER_TRANSFORM_IDENTITY = 0x00;
+ /**
+ * Mirror horizontally. Can be combined with {@link #BUFFER_TRANSFORM_MIRROR_VERTICAL}
+ * and {@link #BUFFER_TRANSFORM_ROTATE_90}.
+ */
+ public static final int BUFFER_TRANSFORM_MIRROR_HORIZONTAL = 0x01;
+ /**
+ * Mirror vertically. Can be combined with {@link #BUFFER_TRANSFORM_MIRROR_HORIZONTAL}
+ * and {@link #BUFFER_TRANSFORM_ROTATE_90}.
+ */
+ public static final int BUFFER_TRANSFORM_MIRROR_VERTICAL = 0x02;
+ /**
+ * Rotate 90 degrees clock-wise. Can be combined with {@link
+ * #BUFFER_TRANSFORM_MIRROR_HORIZONTAL} and {@link #BUFFER_TRANSFORM_MIRROR_VERTICAL}.
+ */
+ public static final int BUFFER_TRANSFORM_ROTATE_90 = 0x04;
+ /**
+ * Rotate 180 degrees clock-wise. Cannot be combined with other transforms.
+ */
+ public static final int BUFFER_TRANSFORM_ROTATE_180 =
+ BUFFER_TRANSFORM_MIRROR_HORIZONTAL | BUFFER_TRANSFORM_MIRROR_VERTICAL;
+ /**
+ * Rotate 270 degrees clock-wise. Cannot be combined with other transforms.
+ */
+ public static final int BUFFER_TRANSFORM_ROTATE_270 =
+ BUFFER_TRANSFORM_ROTATE_180 | BUFFER_TRANSFORM_ROTATE_90;
+
+ /**
+ * @hide
+ */
+ public static @BufferTransform int rotationToBufferTransform(@Surface.Rotation int rotation) {
+ switch (rotation) {
+ case Surface.ROTATION_0: return BUFFER_TRANSFORM_IDENTITY;
+ case Surface.ROTATION_90: return BUFFER_TRANSFORM_ROTATE_90;
+ case Surface.ROTATION_180: return BUFFER_TRANSFORM_ROTATE_180;
+ case Surface.ROTATION_270: return BUFFER_TRANSFORM_ROTATE_270;
+ }
+ Log.e(TAG, "Trying to convert unknown rotation=" + rotation);
+ return BUFFER_TRANSFORM_IDENTITY;
+ }
@Nullable
@GuardedBy("mLock")
@@ -357,8 +429,6 @@
@GuardedBy("mLock")
private int mHeight;
- private int mTransformHint;
-
private WeakReference<View> mLocalOwnerView;
static GlobalTransactionWrapper sGlobalTransaction;
@@ -1541,6 +1611,7 @@
final long token = proto.start(fieldId);
proto.write(HASH_CODE, System.identityHashCode(this));
proto.write(NAME, mName);
+ proto.write(LAYER_ID, getLayerId());
proto.end(token);
}
@@ -3675,4 +3746,15 @@
public void setTransformHint(@Surface.Rotation int transformHint) {
nativeSetTransformHint(mNativeObject, transformHint);
}
+
+ /**
+ * @hide
+ */
+ public int getLayerId() {
+ if (mNativeObject != 0) {
+ return nativeGetLayerId(mNativeObject);
+ }
+
+ return -1;
+ }
}
diff --git a/core/java/android/view/SurfaceView.java b/core/java/android/view/SurfaceView.java
index 60bb99d..63757e3 100644
--- a/core/java/android/view/SurfaceView.java
+++ b/core/java/android/view/SurfaceView.java
@@ -1104,7 +1104,7 @@
|| mWindowSpaceTop != mLocation[1];
final boolean layoutSizeChanged = getWidth() != mScreenRect.width()
|| getHeight() != mScreenRect.height();
- final boolean hintChanged = (viewRoot.getSurfaceTransformHint() != mTransformHint)
+ final boolean hintChanged = (viewRoot.getBufferTransformHint() != mTransformHint)
&& mRequestedVisible;
if (creating || formatChanged || sizeChanged || visibleChanged ||
@@ -1130,7 +1130,7 @@
mSurfaceHeight = myHeight;
mFormat = mRequestedFormat;
mLastWindowVisibility = mWindowVisibility;
- mTransformHint = viewRoot.getSurfaceTransformHint();
+ mTransformHint = viewRoot.getBufferTransformHint();
mScreenRect.left = mWindowSpaceLeft;
mScreenRect.top = mWindowSpaceTop;
@@ -1362,7 +1362,7 @@
if (mBlastBufferQueue != null) {
mBlastBufferQueue.destroy();
}
- mTransformHint = viewRoot.getSurfaceTransformHint();
+ mTransformHint = viewRoot.getBufferTransformHint();
mBlastSurfaceControl.setTransformHint(mTransformHint);
mBlastBufferQueue = new BLASTBufferQueue(name, mBlastSurfaceControl, mSurfaceWidth,
mSurfaceHeight, mFormat);
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index c45b27a..8b791a4 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -312,7 +312,7 @@
static final ArrayList<Runnable> sFirstDrawHandlers = new ArrayList<>();
static boolean sFirstDrawComplete = false;
- private ArrayList<OnSurfaceTransformHintChangedListener> mTransformHintListeners =
+ private ArrayList<OnBufferTransformHintChangedListener> mTransformHintListeners =
new ArrayList<>();
private @Surface.Rotation int mPreviousTransformHint = Surface.ROTATION_0;
/**
@@ -7872,7 +7872,8 @@
int transformHint = mSurfaceControl.getTransformHint();
if (mPreviousTransformHint != transformHint) {
mPreviousTransformHint = transformHint;
- dispatchTransformHintChanged(transformHint);
+ dispatchTransformHintChanged(
+ SurfaceControl.rotationToBufferTransform(transformHint));
}
} else {
destroySurface();
@@ -10499,38 +10500,38 @@
}
@Override
- public @Surface.Rotation int getSurfaceTransformHint() {
- return mSurfaceControl.getTransformHint();
+ public @SurfaceControl.BufferTransform int getBufferTransformHint() {
+ return SurfaceControl.rotationToBufferTransform(mSurfaceControl.getTransformHint());
}
@Override
- public void addOnSurfaceTransformHintChangedListener(
- OnSurfaceTransformHintChangedListener listener) {
+ public void addOnBufferTransformHintChangedListener(
+ OnBufferTransformHintChangedListener listener) {
Objects.requireNonNull(listener);
if (mTransformHintListeners.contains(listener)) {
throw new IllegalArgumentException(
- "attempt to call addOnSurfaceTransformHintChangedListener() "
+ "attempt to call addOnBufferTransformHintChangedListener() "
+ "with a previously registered listener");
}
mTransformHintListeners.add(listener);
}
@Override
- public void removeOnSurfaceTransformHintChangedListener(
- OnSurfaceTransformHintChangedListener listener) {
+ public void removeOnBufferTransformHintChangedListener(
+ OnBufferTransformHintChangedListener listener) {
Objects.requireNonNull(listener);
mTransformHintListeners.remove(listener);
}
- private void dispatchTransformHintChanged(@Surface.Rotation int hint) {
+ private void dispatchTransformHintChanged(@SurfaceControl.BufferTransform int hint) {
if (mTransformHintListeners.isEmpty()) {
return;
}
- ArrayList<OnSurfaceTransformHintChangedListener> listeners =
- (ArrayList<OnSurfaceTransformHintChangedListener>) mTransformHintListeners.clone();
+ ArrayList<OnBufferTransformHintChangedListener> listeners =
+ (ArrayList<OnBufferTransformHintChangedListener>) mTransformHintListeners.clone();
for (int i = 0; i < listeners.size(); i++) {
- OnSurfaceTransformHintChangedListener listener = listeners.get(i);
- listener.onSurfaceTransformHintChanged(hint);
+ OnBufferTransformHintChangedListener listener = listeners.get(i);
+ listener.onBufferTransformHintChanged(hint);
}
}
}
diff --git a/core/java/android/view/translation/UiTranslationController.java b/core/java/android/view/translation/UiTranslationController.java
index 60402eb..aa73ed7 100644
--- a/core/java/android/view/translation/UiTranslationController.java
+++ b/core/java/android/view/translation/UiTranslationController.java
@@ -426,15 +426,19 @@
continue;
}
mActivity.runOnUiThread(() -> {
+ ViewTranslationCallback callback = view.getViewTranslationCallback();
if (view.getViewTranslationResponse() != null
&& view.getViewTranslationResponse().equals(response)) {
- if (DEBUG) {
- Log.d(TAG, "Duplicate ViewTranslationResponse for " + autofillId
- + ". Ignoring.");
+ if (callback instanceof TextViewTranslationCallback) {
+ if (((TextViewTranslationCallback) callback).isShowingTranslation()) {
+ if (DEBUG) {
+ Log.d(TAG, "Duplicate ViewTranslationResponse for " + autofillId
+ + ". Ignoring.");
+ }
+ return;
+ }
}
- return;
}
- ViewTranslationCallback callback = view.getViewTranslationCallback();
if (callback == null) {
if (view instanceof TextView) {
// developer doesn't provide their override, we set the default TextView
diff --git a/core/java/android/widget/TextViewTranslationCallback.java b/core/java/android/widget/TextViewTranslationCallback.java
index 152405b..4a78f3e 100644
--- a/core/java/android/widget/TextViewTranslationCallback.java
+++ b/core/java/android/widget/TextViewTranslationCallback.java
@@ -64,6 +64,12 @@
*/
@Override
public boolean onShowTranslation(@NonNull View view) {
+ if (mIsShowingTranslation) {
+ if (DEBUG) {
+ Log.d(TAG, view + " is already showing translated text.");
+ }
+ return false;
+ }
ViewTranslationResponse response = view.getViewTranslationResponse();
if (response == null) {
Log.e(TAG, "onShowTranslation() shouldn't be called before "
@@ -152,7 +158,7 @@
return true;
}
- boolean isShowingTranslation() {
+ public boolean isShowingTranslation() {
return mIsShowingTranslation;
}
diff --git a/core/java/com/android/internal/os/AmbientDisplayPowerCalculator.java b/core/java/com/android/internal/os/AmbientDisplayPowerCalculator.java
index 0307268..93baa19 100644
--- a/core/java/com/android/internal/os/AmbientDisplayPowerCalculator.java
+++ b/core/java/com/android/internal/os/AmbientDisplayPowerCalculator.java
@@ -16,6 +16,8 @@
package com.android.internal.os;
+import static com.android.internal.os.PowerProfile.POWER_GROUP_DISPLAY_AMBIENT;
+
import android.os.BatteryConsumer;
import android.os.BatteryStats;
import android.os.BatteryUsageStats;
@@ -32,8 +34,9 @@
private final UsageBasedPowerEstimator mPowerEstimator;
public AmbientDisplayPowerCalculator(PowerProfile powerProfile) {
+ // TODO(b/200239964): update to support multidisplay.
mPowerEstimator = new UsageBasedPowerEstimator(
- powerProfile.getAveragePower(PowerProfile.POWER_AMBIENT_DISPLAY));
+ powerProfile.getAveragePowerForOrdinal(POWER_GROUP_DISPLAY_AMBIENT, 0));
}
/**
diff --git a/core/java/com/android/internal/os/PowerProfile.java b/core/java/com/android/internal/os/PowerProfile.java
index add2304..4d19b35 100644
--- a/core/java/com/android/internal/os/PowerProfile.java
+++ b/core/java/com/android/internal/os/PowerProfile.java
@@ -17,10 +17,12 @@
package com.android.internal.os;
+import android.annotation.StringDef;
import android.compat.annotation.UnsupportedAppUsage;
import android.content.Context;
import android.content.res.Resources;
import android.content.res.XmlResourceParser;
+import android.util.Slog;
import android.util.proto.ProtoOutputStream;
import com.android.internal.annotations.VisibleForTesting;
@@ -30,6 +32,8 @@
import org.xmlpull.v1.XmlPullParserException;
import java.io.IOException;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
import java.util.HashMap;
@@ -40,6 +44,8 @@
*/
public class PowerProfile {
+ public static final String TAG = "PowerProfile";
+
/*
* POWER_CPU_SUSPEND: Power consumption when CPU is in power collapse mode.
* POWER_CPU_IDLE: Power consumption when CPU is awake (when a wake lock is held). This should
@@ -145,12 +151,18 @@
/**
* Power consumption when screen is in doze/ambient/always-on mode, including backlight power.
+ *
+ * @deprecated Use {@link #POWER_GROUP_DISPLAY_AMBIENT} instead.
*/
+ @Deprecated
public static final String POWER_AMBIENT_DISPLAY = "ambient.on";
/**
* Power consumption when screen is on, not including the backlight power.
+ *
+ * @deprecated Use {@link #POWER_GROUP_DISPLAY_SCREEN_ON} instead.
*/
+ @Deprecated
@UnsupportedAppUsage
public static final String POWER_SCREEN_ON = "screen.on";
@@ -175,7 +187,10 @@
/**
* Power consumption at full backlight brightness. If the backlight is at
* 50% brightness, then this should be multiplied by 0.5
+ *
+ * @deprecated Use {@link #POWER_GROUP_DISPLAY_SCREEN_FULL} instead.
*/
+ @Deprecated
@UnsupportedAppUsage
public static final String POWER_SCREEN_FULL = "screen.full";
@@ -221,6 +236,29 @@
public static final String POWER_BATTERY_CAPACITY = "battery.capacity";
/**
+ * Power consumption when a screen is in doze/ambient/always-on mode, including backlight power.
+ */
+ public static final String POWER_GROUP_DISPLAY_AMBIENT = "ambient.on.display";
+
+ /**
+ * Power consumption when a screen is on, not including the backlight power.
+ */
+ public static final String POWER_GROUP_DISPLAY_SCREEN_ON = "screen.on.display";
+
+ /**
+ * Power consumption of a screen at full backlight brightness.
+ */
+ public static final String POWER_GROUP_DISPLAY_SCREEN_FULL = "screen.full.display";
+
+ @StringDef(prefix = { "POWER_GROUP_" }, value = {
+ POWER_GROUP_DISPLAY_AMBIENT,
+ POWER_GROUP_DISPLAY_SCREEN_ON,
+ POWER_GROUP_DISPLAY_SCREEN_FULL,
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface PowerGroup {}
+
+ /**
* A map from Power Use Item to its power consumption.
*/
static final HashMap<String, Double> sPowerItemMap = new HashMap<>();
@@ -255,6 +293,7 @@
readPowerValuesFromXml(context, forTest);
}
initCpuClusters();
+ initDisplays();
}
}
@@ -424,6 +463,58 @@
return 0;
}
+ private int mNumDisplays;
+
+ private void initDisplays() {
+ // Figure out how many displays are listed in the power profile.
+ mNumDisplays = 0;
+ while (!Double.isNaN(
+ getAveragePowerForOrdinal(POWER_GROUP_DISPLAY_AMBIENT, mNumDisplays, Double.NaN))
+ || !Double.isNaN(
+ getAveragePowerForOrdinal(POWER_GROUP_DISPLAY_SCREEN_ON, mNumDisplays, Double.NaN))
+ || !Double.isNaN(
+ getAveragePowerForOrdinal(POWER_GROUP_DISPLAY_SCREEN_FULL, mNumDisplays,
+ Double.NaN))) {
+ mNumDisplays++;
+ }
+
+ // Handle legacy display power constants.
+ final Double deprecatedAmbientDisplay = sPowerItemMap.get(POWER_AMBIENT_DISPLAY);
+ boolean legacy = false;
+ if (deprecatedAmbientDisplay != null && mNumDisplays == 0) {
+ final String key = getOrdinalPowerType(POWER_GROUP_DISPLAY_AMBIENT, 0);
+ Slog.w(TAG, POWER_AMBIENT_DISPLAY + " is deprecated! Use " + key + " instead.");
+ sPowerItemMap.put(key, deprecatedAmbientDisplay);
+ legacy = true;
+ }
+
+ final Double deprecatedScreenOn = sPowerItemMap.get(POWER_SCREEN_ON);
+ if (deprecatedScreenOn != null && mNumDisplays == 0) {
+ final String key = getOrdinalPowerType(POWER_GROUP_DISPLAY_SCREEN_ON, 0);
+ Slog.w(TAG, POWER_SCREEN_ON + " is deprecated! Use " + key + " instead.");
+ sPowerItemMap.put(key, deprecatedScreenOn);
+ legacy = true;
+ }
+
+ final Double deprecatedScreenFull = sPowerItemMap.get(POWER_SCREEN_FULL);
+ if (deprecatedScreenFull != null && mNumDisplays == 0) {
+ final String key = getOrdinalPowerType(POWER_GROUP_DISPLAY_SCREEN_FULL, 0);
+ Slog.w(TAG, POWER_SCREEN_FULL + " is deprecated! Use " + key + " instead.");
+ sPowerItemMap.put(key, deprecatedScreenFull);
+ legacy = true;
+ }
+ if (legacy) {
+ mNumDisplays = 1;
+ }
+ }
+
+ /**
+ * Returns the number built in displays on the device as defined in the power_profile.xml.
+ */
+ public int getNumDisplays() {
+ return mNumDisplays;
+ }
+
/**
* Returns the number of memory bandwidth buckets defined in power_profile.xml, or a
* default value if the subsystem has no recorded value.
@@ -496,6 +587,32 @@
}
/**
+ * Returns the average current in mA consumed by an ordinaled subsystem, or the given
+ * default value if the subsystem has no recorded value.
+ *
+ * @param group the subsystem {@link PowerGroup}.
+ * @param ordinal which entity in the {@link PowerGroup}.
+ * @param defaultValue the value to return if the subsystem has no recorded value.
+ * @return the average current in milliAmps.
+ */
+ public double getAveragePowerForOrdinal(@PowerGroup String group, int ordinal,
+ double defaultValue) {
+ final String type = getOrdinalPowerType(group, ordinal);
+ return getAveragePowerOrDefault(type, defaultValue);
+ }
+
+ /**
+ * Returns the average current in mA consumed by an ordinaled subsystem.
+ *
+ * @param group the subsystem {@link PowerGroup}.
+ * @param ordinal which entity in the {@link PowerGroup}.
+ * @return the average current in milliAmps.
+ */
+ public double getAveragePowerForOrdinal(@PowerGroup String group, int ordinal) {
+ return getAveragePowerForOrdinal(group, ordinal, 0);
+ }
+
+ /**
* Returns the battery capacity, if available, in milli Amp Hours. If not available,
* it returns zero.
*
@@ -682,4 +799,9 @@
}
}
}
+
+ // Creates the key for an ordinaled power constant from the group and ordinal.
+ private static String getOrdinalPowerType(@PowerGroup String group, int ordinal) {
+ return group + ordinal;
+ }
}
diff --git a/core/java/com/android/internal/os/ScreenPowerCalculator.java b/core/java/com/android/internal/os/ScreenPowerCalculator.java
index 1b3bc23..72ad4e7 100644
--- a/core/java/com/android/internal/os/ScreenPowerCalculator.java
+++ b/core/java/com/android/internal/os/ScreenPowerCalculator.java
@@ -16,6 +16,9 @@
package com.android.internal.os;
+import static com.android.internal.os.PowerProfile.POWER_GROUP_DISPLAY_SCREEN_FULL;
+import static com.android.internal.os.PowerProfile.POWER_GROUP_DISPLAY_SCREEN_ON;
+
import android.os.BatteryConsumer;
import android.os.BatteryStats;
import android.os.BatteryUsageStats;
@@ -50,10 +53,11 @@
}
public ScreenPowerCalculator(PowerProfile powerProfile) {
+ // TODO(b/200239964): update to support multidisplay.
mScreenOnPowerEstimator = new UsageBasedPowerEstimator(
- powerProfile.getAveragePower(PowerProfile.POWER_SCREEN_ON));
+ powerProfile.getAveragePowerForOrdinal(POWER_GROUP_DISPLAY_SCREEN_ON, 0));
mScreenFullPowerEstimator = new UsageBasedPowerEstimator(
- powerProfile.getAveragePower(PowerProfile.POWER_SCREEN_FULL));
+ powerProfile.getAveragePowerForOrdinal(POWER_GROUP_DISPLAY_SCREEN_FULL, 0));
}
@Override
diff --git a/core/java/com/android/internal/protolog/ProtoLogGroup.java b/core/java/com/android/internal/protolog/ProtoLogGroup.java
index db019a67..954204f 100644
--- a/core/java/com/android/internal/protolog/ProtoLogGroup.java
+++ b/core/java/com/android/internal/protolog/ProtoLogGroup.java
@@ -84,6 +84,7 @@
Consts.TAG_WM),
WM_DEBUG_LAYER_MIRRORING(Consts.ENABLE_DEBUG, Consts.ENABLE_LOG_TO_PROTO_DEBUG, true,
Consts.TAG_WM),
+ WM_DEBUG_WALLPAPER(Consts.ENABLE_DEBUG, Consts.ENABLE_LOG_TO_PROTO_DEBUG, true, Consts.TAG_WM),
TEST_GROUP(true, true, false, "WindowManagerProtoLogTest");
private final boolean mEnabled;
diff --git a/core/jni/android_view_SurfaceControl.cpp b/core/jni/android_view_SurfaceControl.cpp
index 5ce43df..65ff7c7 100644
--- a/core/jni/android_view_SurfaceControl.cpp
+++ b/core/jni/android_view_SurfaceControl.cpp
@@ -1827,6 +1827,12 @@
return toRotationInt(ui::Transform::toRotation((transformHintRotationFlags)));
}
+static jint nativeGetLayerId(JNIEnv* env, jclass clazz, jlong nativeSurfaceControl) {
+ sp<SurfaceControl> surface(reinterpret_cast<SurfaceControl*>(nativeSurfaceControl));
+
+ return surface->getLayerId();
+}
+
// ----------------------------------------------------------------------------
static const JNINativeMethod sSurfaceControlMethods[] = {
@@ -2026,6 +2032,8 @@
(void*)nativeSetTrustedOverlay },
{"nativeSetDropInputMode", "(JJI)V",
(void*)nativeSetDropInputMode },
+ {"nativeGetLayerId", "(J)I",
+ (void*)nativeGetLayerId },
// clang-format on
};
diff --git a/core/proto/android/server/windowmanagerservice.proto b/core/proto/android/server/windowmanagerservice.proto
index 0121bff..4af9d75 100644
--- a/core/proto/android/server/windowmanagerservice.proto
+++ b/core/proto/android/server/windowmanagerservice.proto
@@ -480,6 +480,7 @@
optional SurfaceAnimatorProto surface_animator = 4;
repeated WindowContainerChildProto children = 5;
optional IdentifierProto identifier = 6;
+ optional .android.view.SurfaceControlProto surface_control = 7;
}
/* represents a generic child of a WindowContainer */
diff --git a/core/proto/android/view/surfacecontrol.proto b/core/proto/android/view/surfacecontrol.proto
index cbb243b..5a5f035 100644
--- a/core/proto/android/view/surfacecontrol.proto
+++ b/core/proto/android/view/surfacecontrol.proto
@@ -29,4 +29,5 @@
optional int32 hash_code = 1;
optional string name = 2 [ (android.privacy).dest = DEST_EXPLICIT ];
+ optional int32 layerId = 3;
}
diff --git a/core/res/res/values-bg/strings.xml b/core/res/res/values-bg/strings.xml
index 01ebe06..cdd27d4 100644
--- a/core/res/res/values-bg/strings.xml
+++ b/core/res/res/values-bg/strings.xml
@@ -2097,7 +2097,7 @@
<string name="notification_feedback_indicator_promoted" msgid="9030204303764698640">"Това известие бе класирано по-високо. Докоснете, за да изпратите отзиви."</string>
<string name="notification_feedback_indicator_demoted" msgid="8880309924296450875">"Това известие бе класирано по-ниско. Докоснете, за да изпратите отзиви."</string>
<string name="nas_upgrade_notification_title" msgid="8436359459300146555">"Подобрени известия"</string>
- <string name="nas_upgrade_notification_content" msgid="5157550369837103337">"Предложените действия и отговори вече се предоставят от функцията за подобрени известия. Адаптивните известия за Android вече не се поддържат."</string>
+ <string name="nas_upgrade_notification_content" msgid="5157550369837103337">"Предложените действия и отговори вече се предоставят от функцията за подобрени известия. Адаптивните известия за Android вече не се поддържат."</string>
<string name="nas_upgrade_notification_enable_action" msgid="3046406808378726874">"OK"</string>
<string name="nas_upgrade_notification_disable_action" msgid="3794833210043497982">"Изключване"</string>
<string name="nas_upgrade_notification_learn_more_action" msgid="7011130656195423947">"Научете повече"</string>
diff --git a/core/res/res/values-de/strings.xml b/core/res/res/values-de/strings.xml
index 67b2ee4..a974e90 100644
--- a/core/res/res/values-de/strings.xml
+++ b/core/res/res/values-de/strings.xml
@@ -336,7 +336,7 @@
<string name="capability_desc_canControlMagnification" msgid="2206586716709254805">"Legt die Zoom-Stufe und -Position auf dem Display fest."</string>
<string name="capability_title_canPerformGestures" msgid="9106545062106728987">"Touch-Gesten möglich"</string>
<string name="capability_desc_canPerformGestures" msgid="6619457251067929726">"Tippen, Wischen, Zusammenziehen und andere Touch-Gesten möglich."</string>
- <string name="capability_title_canCaptureFingerprintGestures" msgid="1189053104594608091">"Gesten auf dem Fingerabdrucksensor"</string>
+ <string name="capability_title_canCaptureFingerprintGestures" msgid="1189053104594608091">"Gesten auf dem Fingerabdrucksensor"</string>
<string name="capability_desc_canCaptureFingerprintGestures" msgid="6861869337457461274">"Erfasst Touch-Gesten auf dem Fingerabdrucksensor des Geräts."</string>
<string name="capability_title_canTakeScreenshot" msgid="3895812893130071930">"Screenshot erstellen"</string>
<string name="capability_desc_canTakeScreenshot" msgid="7762297374317934052">"Es kann ein Screenshot des Displays erstellt werden."</string>
diff --git a/core/res/res/values-mcc334-mnc020-as/strings.xml b/core/res/res/values-mcc334-mnc020-as/strings.xml
index 25b074e..43f5fb1 100644
--- a/core/res/res/values-mcc334-mnc020-as/strings.xml
+++ b/core/res/res/values-mcc334-mnc020-as/strings.xml
@@ -20,10 +20,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="config_pdp_reject_dialog_title" msgid="41208110171880430"></string>
- <!-- no translation found for config_pdp_reject_user_authentication_failed (4683454131283459978) -->
- <skip />
- <!-- no translation found for config_pdp_reject_service_not_subscribed (9021140729932308119) -->
- <skip />
- <!-- no translation found for config_pdp_reject_multi_conn_to_same_pdn_not_allowed (3838388706348367865) -->
- <skip />
+ <string name="config_pdp_reject_user_authentication_failed" msgid="4683454131283459978">"বিশ্বাসযোগ্যতা প্ৰমাণীকৰণ বিফল হৈছে -29-"</string>
+ <string name="config_pdp_reject_service_not_subscribed" msgid="9021140729932308119">"সেৱাটো ছাবস্ক্ৰাইব কৰা নাই -33-"</string>
+ <string name="config_pdp_reject_multi_conn_to_same_pdn_not_allowed" msgid="3838388706348367865">"এটা APNৰ বাবে একাধিক PDN সংযোগৰ অনুমতি নাই -55-"</string>
</resources>
diff --git a/core/res/res/values-mcc334-mnc020-bn/strings.xml b/core/res/res/values-mcc334-mnc020-bn/strings.xml
index 25b074e..f58aea7 100644
--- a/core/res/res/values-mcc334-mnc020-bn/strings.xml
+++ b/core/res/res/values-mcc334-mnc020-bn/strings.xml
@@ -20,10 +20,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="config_pdp_reject_dialog_title" msgid="41208110171880430"></string>
- <!-- no translation found for config_pdp_reject_user_authentication_failed (4683454131283459978) -->
- <skip />
- <!-- no translation found for config_pdp_reject_service_not_subscribed (9021140729932308119) -->
- <skip />
- <!-- no translation found for config_pdp_reject_multi_conn_to_same_pdn_not_allowed (3838388706348367865) -->
- <skip />
+ <string name="config_pdp_reject_user_authentication_failed" msgid="4683454131283459978">"যাচাই করা যায়নি -29-"</string>
+ <string name="config_pdp_reject_service_not_subscribed" msgid="9021140729932308119">"পরিষেবা সাবস্ক্রাইব করা হয়নি -33-"</string>
+ <string name="config_pdp_reject_multi_conn_to_same_pdn_not_allowed" msgid="3838388706348367865">"নির্দিষ্ট কোনও APN-এর জন্য একাধিক PDN কানেকশন অনুমোদিত নয় -55-"</string>
</resources>
diff --git a/core/res/res/values-mcc334-mnc020-fr-rCA/strings.xml b/core/res/res/values-mcc334-mnc020-fr-rCA/strings.xml
index 25b074e..19794da 100644
--- a/core/res/res/values-mcc334-mnc020-fr-rCA/strings.xml
+++ b/core/res/res/values-mcc334-mnc020-fr-rCA/strings.xml
@@ -20,10 +20,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="config_pdp_reject_dialog_title" msgid="41208110171880430"></string>
- <!-- no translation found for config_pdp_reject_user_authentication_failed (4683454131283459978) -->
- <skip />
- <!-- no translation found for config_pdp_reject_service_not_subscribed (9021140729932308119) -->
- <skip />
- <!-- no translation found for config_pdp_reject_multi_conn_to_same_pdn_not_allowed (3838388706348367865) -->
- <skip />
+ <string name="config_pdp_reject_user_authentication_failed" msgid="4683454131283459978">"ÉCHEC DE L\'AUTHENTIFICATION -29-"</string>
+ <string name="config_pdp_reject_service_not_subscribed" msgid="9021140729932308119">"NON ABONNÉ AU SERVICE -33-"</string>
+ <string name="config_pdp_reject_multi_conn_to_same_pdn_not_allowed" msgid="3838388706348367865">"Connexions PDN multiples interdites pour un APN donné -55-"</string>
</resources>
diff --git a/core/res/res/values-mcc334-mnc020-gu/strings.xml b/core/res/res/values-mcc334-mnc020-gu/strings.xml
index 25b074e..5faef6f 100644
--- a/core/res/res/values-mcc334-mnc020-gu/strings.xml
+++ b/core/res/res/values-mcc334-mnc020-gu/strings.xml
@@ -20,10 +20,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="config_pdp_reject_dialog_title" msgid="41208110171880430"></string>
- <!-- no translation found for config_pdp_reject_user_authentication_failed (4683454131283459978) -->
- <skip />
- <!-- no translation found for config_pdp_reject_service_not_subscribed (9021140729932308119) -->
- <skip />
- <!-- no translation found for config_pdp_reject_multi_conn_to_same_pdn_not_allowed (3838388706348367865) -->
- <skip />
+ <string name="config_pdp_reject_user_authentication_failed" msgid="4683454131283459978">"પ્રમાણીકરણ નિષ્ફળ થયું -29-"</string>
+ <string name="config_pdp_reject_service_not_subscribed" msgid="9021140729932308119">"સેવા સબ્સ્ક્રાઇબ કરી નથી -33-"</string>
+ <string name="config_pdp_reject_multi_conn_to_same_pdn_not_allowed" msgid="3838388706348367865">"આપેલા APN માટે એક કરતાં વધારે PDN કનેક્શનની મંજૂરી નથી -55-"</string>
</resources>
diff --git a/core/res/res/values-mcc334-mnc020-ur/strings.xml b/core/res/res/values-mcc334-mnc020-ur/strings.xml
index 25b074e..bab5589 100644
--- a/core/res/res/values-mcc334-mnc020-ur/strings.xml
+++ b/core/res/res/values-mcc334-mnc020-ur/strings.xml
@@ -20,10 +20,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="config_pdp_reject_dialog_title" msgid="41208110171880430"></string>
- <!-- no translation found for config_pdp_reject_user_authentication_failed (4683454131283459978) -->
- <skip />
- <!-- no translation found for config_pdp_reject_service_not_subscribed (9021140729932308119) -->
- <skip />
- <!-- no translation found for config_pdp_reject_multi_conn_to_same_pdn_not_allowed (3838388706348367865) -->
- <skip />
+ <string name="config_pdp_reject_user_authentication_failed" msgid="4683454131283459978">"توثیق کی ناکامی -29-"</string>
+ <string name="config_pdp_reject_service_not_subscribed" msgid="9021140729932308119">"سروس کو سبسکرائب نہیں کیا -33-"</string>
+ <string name="config_pdp_reject_multi_conn_to_same_pdn_not_allowed" msgid="3838388706348367865">"ایک دیئے گئے APN کے لیے متعدد PDN کنکشنز کی اجازت نہیں ہے -55-"</string>
</resources>
diff --git a/core/res/res/values-mcc334-mnc020-zh-rCN/strings.xml b/core/res/res/values-mcc334-mnc020-zh-rCN/strings.xml
index 25b074e..a8fbe74c 100644
--- a/core/res/res/values-mcc334-mnc020-zh-rCN/strings.xml
+++ b/core/res/res/values-mcc334-mnc020-zh-rCN/strings.xml
@@ -20,10 +20,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="config_pdp_reject_dialog_title" msgid="41208110171880430"></string>
- <!-- no translation found for config_pdp_reject_user_authentication_failed (4683454131283459978) -->
- <skip />
- <!-- no translation found for config_pdp_reject_service_not_subscribed (9021140729932308119) -->
- <skip />
- <!-- no translation found for config_pdp_reject_multi_conn_to_same_pdn_not_allowed (3838388706348367865) -->
- <skip />
+ <string name="config_pdp_reject_user_authentication_failed" msgid="4683454131283459978">"身份验证失败 -29-"</string>
+ <string name="config_pdp_reject_service_not_subscribed" msgid="9021140729932308119">"未订阅服务 -33-"</string>
+ <string name="config_pdp_reject_multi_conn_to_same_pdn_not_allowed" msgid="3838388706348367865">"指定的 APN 不能有多个 PDN 连接 -55-"</string>
</resources>
diff --git a/core/res/res/values-mr/strings.xml b/core/res/res/values-mr/strings.xml
index 4bb06bb..eaed8c8 100644
--- a/core/res/res/values-mr/strings.xml
+++ b/core/res/res/values-mr/strings.xml
@@ -100,7 +100,7 @@
<string name="peerTtyModeHco" msgid="5626377160840915617">"समवयस्क व्यक्तीने TTY मोड HCO ची विनंती केली"</string>
<string name="peerTtyModeVco" msgid="572208600818270944">"समवयस्क व्यक्तीने TTY मोड VCO ची विनंती केली"</string>
<string name="peerTtyModeOff" msgid="2420380956369226583">"समवयस्क व्यक्तीने TTY मोड बंद ची विनंती केली"</string>
- <string name="serviceClassVoice" msgid="2065556932043454987">"Voice"</string>
+ <string name="serviceClassVoice" msgid="2065556932043454987">"व्हॉइस"</string>
<string name="serviceClassData" msgid="4148080018967300248">"डेटा"</string>
<string name="serviceClassFAX" msgid="2561653371698904118">"फॅक्स"</string>
<string name="serviceClassSMS" msgid="1547664561704509004">"SMS"</string>
@@ -306,7 +306,7 @@
<string name="permgroupdesc_contacts" msgid="9163927941244182567">"आपल्या संपर्कांवर प्रवेश"</string>
<string name="permgrouplab_location" msgid="1858277002233964394">"स्थान"</string>
<string name="permgroupdesc_location" msgid="1995955142118450685">"या डिव्हाइसच्या स्थानावर प्रवेश"</string>
- <string name="permgrouplab_calendar" msgid="6426860926123033230">"Calendar"</string>
+ <string name="permgrouplab_calendar" msgid="6426860926123033230">"कॅलेंडर"</string>
<string name="permgroupdesc_calendar" msgid="6762751063361489379">"आपल्या कॅलेंडरवर प्रवेश"</string>
<string name="permgrouplab_sms" msgid="795737735126084874">"SMS"</string>
<string name="permgroupdesc_sms" msgid="5726462398070064542">"SMS मेसेज पाठवणे आणि पाहणे हे"</string>
diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml
index 77820d1..ab39152 100644
--- a/core/res/res/values/attrs.xml
+++ b/core/res/res/values/attrs.xml
@@ -8372,7 +8372,7 @@
<attr name="supportsAmbientMode" format="boolean" />
<!-- Indicates that this wallpaper service should receive zoom transition updates when
- changing the display state of the device (e.g. when folding or unfolding
+ changing the structural state of the device (e.g. when folding or unfolding
a foldable device). When this value is set to true
{@link android.service.wallpaper.WallpaperService.Engine} could receive zoom updates
before or after changing the device state. Wallpapers receive zoom updates using
@@ -8381,8 +8381,8 @@
{@link android.service.wallpaper.WallpaperService.Engine} is created and not destroyed.
Default value is true.
Corresponds to
- {@link android.app.WallpaperInfo#shouldUseDefaultDisplayStateChangeTransition()} -->
- <attr name="shouldUseDefaultDisplayStateChangeTransition" format="boolean" />
+ {@link android.app.WallpaperInfo#shouldUseDefaultUnfoldTransition()} -->
+ <attr name="shouldUseDefaultUnfoldTransition" format="boolean" />
<!-- Uri that specifies a settings Slice for this wallpaper. -->
<attr name="settingsSliceUri" format="string"/>
diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml
index 462b188..7d48904 100644
--- a/core/res/res/values/public.xml
+++ b/core/res/res/values/public.xml
@@ -3221,7 +3221,7 @@
<eat-comment />
<staging-public-group type="attr" first-id="0x01ff0000">
- <public name="shouldUseDefaultDisplayStateChangeTransition" />
+ <public name="shouldUseDefaultUnfoldTransition" />
</staging-public-group>
<staging-public-group type="id" first-id="0x01fe0000">
diff --git a/core/res/res/xml/power_profile.xml b/core/res/res/xml/power_profile.xml
index 166edca..d310736 100644
--- a/core/res/res/xml/power_profile.xml
+++ b/core/res/res/xml/power_profile.xml
@@ -27,9 +27,33 @@
are totally dependent on the platform and can vary
significantly, so should be measured on the shipping platform
with a power meter. -->
- <item name="ambient.on">0.1</item> <!-- ~100mA -->
- <item name="screen.on">0.1</item> <!-- ~100mA -->
- <item name="screen.full">0.1</item> <!-- ~100mA -->
+
+ <!-- Display related values. -->
+ <!-- Average battery current draw of display0 while in ambient mode, including backlight.
+ There must be one of these for each display, labeled:
+ ambient.on.display0, ambient.on.display1, etc...
+
+ Each display suffix number should match it's ordinal in its display device config.
+ -->
+ <item name="ambient.on.display0">0.1</item> <!-- ~100mA -->
+ <!-- Average battery current draw of display0 while on without backlight.
+ There must be one of these for each display, labeled:
+ screen.on.display0, screen.on.display1, etc...
+
+ Each display suffix number should match it's ordinal in its display device config.
+ -->
+ <item name="screen.on.display0">0.1</item> <!-- ~100mA -->
+ <!-- Average battery current draw of the backlight at full brightness.
+ The full current draw of display N at full brightness should be the sum of screen.on.displayN
+ and screen.full.displayN
+
+ There must be one of these for each display, labeled:
+ screen.full.display0, screen.full.display1, etc...
+
+ Each display suffix number should match it's ordinal in its display device config.
+ -->
+ <item name="screen.full.display0">0.1</item> <!-- ~100mA -->
+
<item name="bluetooth.active">0.1</item> <!-- Bluetooth data transfer, ~10mA -->
<item name="bluetooth.on">0.1</item> <!-- Bluetooth on & connectable, but not connected, ~0.1mA -->
<item name="wifi.on">0.1</item> <!-- ~3mA -->
diff --git a/core/tests/coretests/src/com/android/internal/os/AmbientDisplayPowerCalculatorTest.java b/core/tests/coretests/src/com/android/internal/os/AmbientDisplayPowerCalculatorTest.java
index 79f7a5c..d76037e 100644
--- a/core/tests/coretests/src/com/android/internal/os/AmbientDisplayPowerCalculatorTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/AmbientDisplayPowerCalculatorTest.java
@@ -16,6 +16,8 @@
package com.android.internal.os;
+import static com.android.internal.os.PowerProfile.POWER_GROUP_DISPLAY_AMBIENT;
+
import static com.google.common.truth.Truth.assertThat;
import android.os.BatteryConsumer;
@@ -36,7 +38,7 @@
@Rule
public final BatteryUsageStatsRule mStatsRule = new BatteryUsageStatsRule()
- .setAveragePower(PowerProfile.POWER_AMBIENT_DISPLAY, 10.0);
+ .setAveragePowerForOrdinal(POWER_GROUP_DISPLAY_AMBIENT, 0, 10.0);
@Test
public void testMeasuredEnergyBasedModel() {
diff --git a/core/tests/coretests/src/com/android/internal/os/BatteryUsageStatsRule.java b/core/tests/coretests/src/com/android/internal/os/BatteryUsageStatsRule.java
index 083090c..ab38f01 100644
--- a/core/tests/coretests/src/com/android/internal/os/BatteryUsageStatsRule.java
+++ b/core/tests/coretests/src/com/android/internal/os/BatteryUsageStatsRule.java
@@ -110,6 +110,14 @@
return this;
}
+ public BatteryUsageStatsRule setAveragePowerForOrdinal(String group, int ordinal,
+ double value) {
+ when(mPowerProfile.getAveragePowerForOrdinal(group, ordinal)).thenReturn(value);
+ when(mPowerProfile.getAveragePowerForOrdinal(eq(group), eq(ordinal),
+ anyDouble())).thenReturn(value);
+ return this;
+ }
+
/** Call only after setting the power profile information. */
public BatteryUsageStatsRule initMeasuredEnergyStatsLocked() {
return initMeasuredEnergyStatsLocked(new String[0]);
diff --git a/core/tests/coretests/src/com/android/internal/os/PowerProfileTest.java b/core/tests/coretests/src/com/android/internal/os/PowerProfileTest.java
index 5862368..88ee405 100644
--- a/core/tests/coretests/src/com/android/internal/os/PowerProfileTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/PowerProfileTest.java
@@ -17,6 +17,10 @@
package com.android.internal.os;
+import static com.android.internal.os.PowerProfile.POWER_GROUP_DISPLAY_AMBIENT;
+import static com.android.internal.os.PowerProfile.POWER_GROUP_DISPLAY_SCREEN_FULL;
+import static com.android.internal.os.PowerProfile.POWER_GROUP_DISPLAY_SCREEN_ON;
+
import androidx.test.InstrumentationRegistry;
import androidx.test.filters.SmallTest;
@@ -53,7 +57,12 @@
assertEquals(4, mProfile.getNumSpeedStepsInCpuCluster(1));
assertEquals(60.0, mProfile.getAveragePowerForCpuCore(1, 3));
assertEquals(3000.0, mProfile.getBatteryCapacity());
- assertEquals(0.5, mProfile.getAveragePower(PowerProfile.POWER_AMBIENT_DISPLAY));
+ assertEquals(0.5,
+ mProfile.getAveragePowerForOrdinal(POWER_GROUP_DISPLAY_AMBIENT, 0));
+ assertEquals(100.0,
+ mProfile.getAveragePowerForOrdinal(POWER_GROUP_DISPLAY_SCREEN_ON, 0));
+ assertEquals(800.0,
+ mProfile.getAveragePowerForOrdinal(POWER_GROUP_DISPLAY_SCREEN_FULL, 0));
assertEquals(100.0, mProfile.getAveragePower(PowerProfile.POWER_AUDIO));
assertEquals(150.0, mProfile.getAveragePower(PowerProfile.POWER_VIDEO));
}
diff --git a/core/tests/coretests/src/com/android/internal/os/ScreenPowerCalculatorTest.java b/core/tests/coretests/src/com/android/internal/os/ScreenPowerCalculatorTest.java
index c695fc9..50e0a15 100644
--- a/core/tests/coretests/src/com/android/internal/os/ScreenPowerCalculatorTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/ScreenPowerCalculatorTest.java
@@ -16,6 +16,9 @@
package com.android.internal.os;
+import static com.android.internal.os.PowerProfile.POWER_GROUP_DISPLAY_SCREEN_FULL;
+import static com.android.internal.os.PowerProfile.POWER_GROUP_DISPLAY_SCREEN_ON;
+
import static com.google.common.truth.Truth.assertThat;
import android.app.ActivityManager;
@@ -42,8 +45,8 @@
@Rule
public final BatteryUsageStatsRule mStatsRule = new BatteryUsageStatsRule()
- .setAveragePower(PowerProfile.POWER_SCREEN_ON, 36.0)
- .setAveragePower(PowerProfile.POWER_SCREEN_FULL, 48.0);
+ .setAveragePowerForOrdinal(POWER_GROUP_DISPLAY_SCREEN_ON, 0, 36.0)
+ .setAveragePowerForOrdinal(POWER_GROUP_DISPLAY_SCREEN_FULL, 0, 48.0);
@Test
public void testMeasuredEnergyBasedModel() {
diff --git a/data/etc/car/com.android.car.carlauncher.xml b/data/etc/car/com.android.car.carlauncher.xml
index 33f885a..53d02a4 100644
--- a/data/etc/car/com.android.car.carlauncher.xml
+++ b/data/etc/car/com.android.car.carlauncher.xml
@@ -24,5 +24,6 @@
<permission name="android.permission.MEDIA_CONTENT_CONTROL"/>
<permission name="android.permission.PACKAGE_USAGE_STATS"/>
<permission name="android.car.permission.ACCESS_CAR_PROJECTION_STATUS"/>
+ <permission name="android.car.permission.CONTROL_CAR_APP_LAUNCH"/>
</privapp-permissions>
</permissions>
diff --git a/data/etc/car/com.google.android.car.kitchensink.xml b/data/etc/car/com.google.android.car.kitchensink.xml
index 8705067..ab162dd5 100644
--- a/data/etc/car/com.google.android.car.kitchensink.xml
+++ b/data/etc/car/com.google.android.car.kitchensink.xml
@@ -74,6 +74,8 @@
<permission name="android.car.permission.CAR_TEST_SERVICE"/>
<permission name="android.car.permission.CAR_UX_RESTRICTIONS_CONFIGURATION"/>
<permission name="android.car.permission.CAR_VENDOR_EXTENSION"/>
+ <!-- use for AndroidCarApiTest -->
+ <permission name="android.car.permission.CONTROL_CAR_APP_LAUNCH"/>
<permission name="android.car.permission.CONTROL_CAR_CLIMATE"/>
<permission name="android.car.permission.CONTROL_CAR_DOORS"/>
<permission name="android.car.permission.CONTROL_CAR_EXTERIOR_LIGHTS"/>
diff --git a/data/etc/services.core.protolog.json b/data/etc/services.core.protolog.json
index 909ca39..9573607 100644
--- a/data/etc/services.core.protolog.json
+++ b/data/etc/services.core.protolog.json
@@ -595,6 +595,12 @@
"group": "WM_DEBUG_ORIENTATION",
"at": "com\/android\/server\/wm\/DisplayContent.java"
},
+ "-1478175541": {
+ "message": "No longer animating wallpaper targets!",
+ "level": "VERBOSE",
+ "group": "WM_DEBUG_WALLPAPER",
+ "at": "com\/android\/server\/wm\/WallpaperController.java"
+ },
"-1474602871": {
"message": "Launch on display check: disallow launch on virtual display for not-embedded activity.",
"level": "DEBUG",
@@ -1621,6 +1627,12 @@
"group": "WM_DEBUG_TASKS",
"at": "com\/android\/server\/wm\/RootWindowContainer.java"
},
+ "-360208282": {
+ "message": "Animating wallpapers: old: %s hidden=%b new: %s hidden=%b",
+ "level": "VERBOSE",
+ "group": "WM_DEBUG_WALLPAPER",
+ "at": "com\/android\/server\/wm\/WallpaperController.java"
+ },
"-354571697": {
"message": "Existence Changed in transition %d: %s",
"level": "VERBOSE",
@@ -1675,6 +1687,12 @@
"group": "WM_DEBUG_LAYER_MIRRORING",
"at": "com\/android\/server\/wm\/DisplayContent.java"
},
+ "-304728471": {
+ "message": "New wallpaper: target=%s prev=%s",
+ "level": "DEBUG",
+ "group": "WM_DEBUG_WALLPAPER",
+ "at": "com\/android\/server\/wm\/WallpaperController.java"
+ },
"-302468788": {
"message": "Expected target rootTask=%s to be top most but found rootTask=%s",
"level": "WARN",
@@ -1693,6 +1711,12 @@
"group": "WM_ERROR",
"at": "com\/android\/server\/wm\/WindowManagerService.java"
},
+ "-275077723": {
+ "message": "New animation: %s old animation: %s",
+ "level": "VERBOSE",
+ "group": "WM_DEBUG_WALLPAPER",
+ "at": "com\/android\/server\/wm\/WallpaperController.java"
+ },
"-262984451": {
"message": "Relaunch failed %s",
"level": "INFO",
@@ -1747,6 +1771,12 @@
"group": "WM_DEBUG_LAYER_MIRRORING",
"at": "com\/android\/server\/wm\/DisplayContent.java"
},
+ "-182877285": {
+ "message": "Wallpaper layer changed: assigning layers + relayout",
+ "level": "VERBOSE",
+ "group": "WM_DEBUG_WALLPAPER",
+ "at": "com\/android\/server\/wm\/DisplayContent.java"
+ },
"-177040661": {
"message": "Start rotation animation. customAnim=%s, mCurRotation=%s, mOriginalRotation=%s",
"level": "DEBUG",
@@ -2005,6 +2035,12 @@
"group": "WM_DEBUG_STARTING_WINDOW",
"at": "com\/android\/server\/wm\/ActivityRecord.java"
},
+ "114070759": {
+ "message": "New wallpaper target: %s prevTarget: %s caller=%s",
+ "level": "VERBOSE",
+ "group": "WM_DEBUG_WALLPAPER",
+ "at": "com\/android\/server\/wm\/WallpaperController.java"
+ },
"115358443": {
"message": "Focus changing: %s -> %s",
"level": "INFO",
@@ -2347,6 +2383,12 @@
"group": "WM_DEBUG_RESIZE",
"at": "com\/android\/server\/wm\/WindowState.java"
},
+ "422634333": {
+ "message": "First draw done in potential wallpaper target %s",
+ "level": "VERBOSE",
+ "group": "WM_DEBUG_WALLPAPER",
+ "at": "com\/android\/server\/wm\/DisplayContent.java"
+ },
"424524729": {
"message": "Attempted to add wallpaper window with unknown token %s. Aborting.",
"level": "WARN",
@@ -2371,12 +2413,6 @@
"group": "WM_ERROR",
"at": "com\/android\/server\/wm\/WindowManagerService.java"
},
- "457951957": {
- "message": "\tNot visible=%s",
- "level": "DEBUG",
- "group": "WM_DEBUG_REMOTE_ANIMATIONS",
- "at": "com\/android\/server\/wm\/WallpaperAnimationAdapter.java"
- },
"463993897": {
"message": "Aborted waiting for drawn: %s",
"level": "WARN",
@@ -2425,6 +2461,12 @@
"group": "WM_SHOW_TRANSACTIONS",
"at": "com\/android\/server\/wm\/WindowContainerThumbnail.java"
},
+ "535103992": {
+ "message": "Wallpaper may change! Adjusting",
+ "level": "VERBOSE",
+ "group": "WM_DEBUG_WALLPAPER",
+ "at": "com\/android\/server\/wm\/RootWindowContainer.java"
+ },
"539077569": {
"message": "Clear freezing of %s force=%b",
"level": "VERBOSE",
@@ -2653,6 +2695,12 @@
"group": "WM_DEBUG_STATES",
"at": "com\/android\/server\/wm\/ActivityRecord.java"
},
+ "733466617": {
+ "message": "Wallpaper token %s visible=%b",
+ "level": "DEBUG",
+ "group": "WM_DEBUG_WALLPAPER",
+ "at": "com\/android\/server\/wm\/WallpaperWindowToken.java"
+ },
"736692676": {
"message": "Config is relaunching %s",
"level": "VERBOSE",
@@ -2989,6 +3037,12 @@
"group": "WM_DEBUG_APP_TRANSITIONS",
"at": "com\/android\/server\/wm\/DisplayContent.java"
},
+ "1178653181": {
+ "message": "Old wallpaper still the target.",
+ "level": "VERBOSE",
+ "group": "WM_DEBUG_WALLPAPER",
+ "at": "com\/android\/server\/wm\/WallpaperController.java"
+ },
"1186730970": {
"message": " no common mode yet, so set it",
"level": "VERBOSE",
@@ -3667,6 +3721,12 @@
"group": "WM_SHOW_TRANSACTIONS",
"at": "com\/android\/server\/wm\/WindowAnimator.java"
},
+ "1984843251": {
+ "message": "Hiding wallpaper %s from %s target=%s prev=%s callers=%s",
+ "level": "DEBUG",
+ "group": "WM_DEBUG_WALLPAPER",
+ "at": "com\/android\/server\/wm\/WallpaperController.java"
+ },
"1995093920": {
"message": "Checking to restart %s: changed=0x%s, handles=0x%s, mLastReportedConfiguration=%s",
"level": "VERBOSE",
@@ -3697,6 +3757,12 @@
"group": "WM_DEBUG_REMOTE_ANIMATIONS",
"at": "com\/android\/server\/wm\/RemoteAnimationController.java"
},
+ "2024493888": {
+ "message": "\tWallpaper of display=%s is not visible",
+ "level": "DEBUG",
+ "group": "WM_DEBUG_REMOTE_ANIMATIONS",
+ "at": "com\/android\/server\/wm\/WallpaperAnimationAdapter.java"
+ },
"2028163120": {
"message": "applyAnimation: anim=%s nextAppTransition=ANIM_SCALE_UP transit=%s isEntrance=%s Callers=%s",
"level": "VERBOSE",
@@ -3721,12 +3787,6 @@
"group": "WM_DEBUG_APP_TRANSITIONS",
"at": "com\/android\/server\/wm\/AppTransitionController.java"
},
- "2057434754": {
- "message": "\tvisible=%s",
- "level": "DEBUG",
- "group": "WM_DEBUG_REMOTE_ANIMATIONS",
- "at": "com\/android\/server\/wm\/WallpaperAnimationAdapter.java"
- },
"2060978050": {
"message": "moveWindowTokenToDisplay: Attempted to move token: %s to non-exiting displayId=%d",
"level": "WARN",
@@ -3867,6 +3927,9 @@
"WM_DEBUG_TASKS": {
"tag": "WindowManager"
},
+ "WM_DEBUG_WALLPAPER": {
+ "tag": "WindowManager"
+ },
"WM_DEBUG_WINDOW_INSETS": {
"tag": "WindowManager"
},
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentAnimationAdapter.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentAnimationAdapter.java
index 06f6228..194b633 100644
--- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentAnimationAdapter.java
+++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentAnimationAdapter.java
@@ -16,7 +16,8 @@
package androidx.window.extensions.embedding;
-import android.graphics.Point;
+import static android.graphics.Matrix.MSCALE_X;
+
import android.graphics.Rect;
import android.view.Choreographer;
import android.view.RemoteAnimationTarget;
@@ -25,58 +26,151 @@
import android.view.animation.Transformation;
import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
/**
* Wrapper to handle the TaskFragment animation update in one {@link SurfaceControl.Transaction}.
+ *
+ * The base adapter can be used for {@link RemoteAnimationTarget} that is simple open/close.
*/
class TaskFragmentAnimationAdapter {
- private final Animation mAnimation;
- private final RemoteAnimationTarget mTarget;
- private final SurfaceControl mLeash;
- private final boolean mSizeChanged;
- private final Point mPosition;
- private final Transformation mTransformation = new Transformation();
- private final float[] mMatrix = new float[9];
- private final float[] mVecs = new float[4];
- private final Rect mRect = new Rect();
+ final Animation mAnimation;
+ final RemoteAnimationTarget mTarget;
+ final SurfaceControl mLeash;
+
+ final Transformation mTransformation = new Transformation();
+ final float[] mMatrix = new float[9];
private boolean mIsFirstFrame = true;
TaskFragmentAnimationAdapter(@NonNull Animation animation,
@NonNull RemoteAnimationTarget target) {
- this(animation, target, target.leash, false /* sizeChanged */, null /* position */);
+ this(animation, target, target.leash);
}
/**
- * @param sizeChanged whether the surface size needs to be changed.
+ * @param leash the surface to animate.
*/
TaskFragmentAnimationAdapter(@NonNull Animation animation,
- @NonNull RemoteAnimationTarget target, @NonNull SurfaceControl leash,
- boolean sizeChanged, @Nullable Point position) {
+ @NonNull RemoteAnimationTarget target, @NonNull SurfaceControl leash) {
mAnimation = animation;
mTarget = target;
mLeash = leash;
- mSizeChanged = sizeChanged;
- mPosition = position != null
- ? position
- : new Point(target.localBounds.left, target.localBounds.top);
}
/** Called on frame update. */
- void onAnimationUpdate(@NonNull SurfaceControl.Transaction t, long currentPlayTime) {
+ final void onAnimationUpdate(@NonNull SurfaceControl.Transaction t, long currentPlayTime) {
if (mIsFirstFrame) {
t.show(mLeash);
mIsFirstFrame = false;
}
- currentPlayTime = Math.min(currentPlayTime, mAnimation.getDuration());
- mAnimation.getTransformation(currentPlayTime, mTransformation);
- mTransformation.getMatrix().postTranslate(mPosition.x, mPosition.y);
+ // Extract the transformation to the current time.
+ mAnimation.getTransformation(Math.min(currentPlayTime, mAnimation.getDuration()),
+ mTransformation);
+ t.setFrameTimelineVsync(Choreographer.getInstance().getVsyncId());
+ onAnimationUpdateInner(t);
+ }
+
+ /** To be overridden by subclasses to adjust the animation surface change. */
+ void onAnimationUpdateInner(@NonNull SurfaceControl.Transaction t) {
+ mTransformation.getMatrix().postTranslate(
+ mTarget.localBounds.left, mTarget.localBounds.top);
t.setMatrix(mLeash, mTransformation.getMatrix(), mMatrix);
t.setAlpha(mLeash, mTransformation.getAlpha());
- t.setFrameTimelineVsync(Choreographer.getInstance().getVsyncId());
+ }
- if (mSizeChanged) {
+ /** Called after animation finished. */
+ final void onAnimationEnd(@NonNull SurfaceControl.Transaction t) {
+ onAnimationUpdate(t, mAnimation.getDuration());
+ }
+
+ final long getDurationHint() {
+ return mAnimation.computeDurationHint();
+ }
+
+ /**
+ * Should be used when the {@link RemoteAnimationTarget} is in split with others, and want to
+ * animate together as one. This adapter will offset the animation leash to make the animate of
+ * two windows look like a single window.
+ */
+ static class SplitAdapter extends TaskFragmentAnimationAdapter {
+ private final boolean mIsLeftHalf;
+ private final int mWholeAnimationWidth;
+
+ /**
+ * @param isLeftHalf whether this is the left half of the animation.
+ * @param wholeAnimationWidth the whole animation windows width.
+ */
+ SplitAdapter(@NonNull Animation animation, @NonNull RemoteAnimationTarget target,
+ boolean isLeftHalf, int wholeAnimationWidth) {
+ super(animation, target);
+ mIsLeftHalf = isLeftHalf;
+ mWholeAnimationWidth = wholeAnimationWidth;
+ if (wholeAnimationWidth == 0) {
+ throw new IllegalArgumentException("SplitAdapter must provide wholeAnimationWidth");
+ }
+ }
+
+ @Override
+ void onAnimationUpdateInner(@NonNull SurfaceControl.Transaction t) {
+ float posX = mTarget.localBounds.left;
+ final float posY = mTarget.localBounds.top;
+ // This window is half of the whole animation window. Offset left/right to make it
+ // look as one with the other half.
+ mTransformation.getMatrix().getValues(mMatrix);
+ final int targetWidth = mTarget.localBounds.width();
+ final float scaleX = mMatrix[MSCALE_X];
+ final float totalOffset = mWholeAnimationWidth * (1 - scaleX) / 2;
+ final float curOffset = targetWidth * (1 - scaleX) / 2;
+ final float offsetDiff = totalOffset - curOffset;
+ if (mIsLeftHalf) {
+ posX += offsetDiff;
+ } else {
+ posX -= offsetDiff;
+ }
+ mTransformation.getMatrix().postTranslate(posX, posY);
+ t.setMatrix(mLeash, mTransformation.getMatrix(), mMatrix);
+ t.setAlpha(mLeash, mTransformation.getAlpha());
+ }
+ }
+
+ /**
+ * Should be used for the animation of the snapshot of a {@link RemoteAnimationTarget} that has
+ * size change.
+ */
+ static class SnapshotAdapter extends TaskFragmentAnimationAdapter {
+
+ SnapshotAdapter(@NonNull Animation animation, @NonNull RemoteAnimationTarget target) {
+ // Start leash is the snapshot of the starting surface.
+ super(animation, target, target.startLeash);
+ }
+
+ @Override
+ void onAnimationUpdateInner(@NonNull SurfaceControl.Transaction t) {
+ // Snapshot should always be placed at the top left of the animation leash.
+ mTransformation.getMatrix().postTranslate(0, 0);
+ t.setMatrix(mLeash, mTransformation.getMatrix(), mMatrix);
+ t.setAlpha(mLeash, mTransformation.getAlpha());
+ }
+ }
+
+ /**
+ * Should be used for the animation of the {@link RemoteAnimationTarget} that has size change.
+ */
+ static class BoundsChangeAdapter extends TaskFragmentAnimationAdapter {
+ private final float[] mVecs = new float[4];
+ private final Rect mRect = new Rect();
+
+ BoundsChangeAdapter(@NonNull Animation animation, @NonNull RemoteAnimationTarget target) {
+ super(animation, target);
+ }
+
+ @Override
+ void onAnimationUpdateInner(@NonNull SurfaceControl.Transaction t) {
+ mTransformation.getMatrix().postTranslate(
+ mTarget.localBounds.left, mTarget.localBounds.top);
+ t.setMatrix(mLeash, mTransformation.getMatrix(), mMatrix);
+ t.setAlpha(mLeash, mTransformation.getAlpha());
+
// The following applies an inverse scale to the clip-rect so that it crops "after" the
// scale instead of before.
mVecs[1] = mVecs[2] = 0;
@@ -92,13 +186,4 @@
t.setWindowCrop(mLeash, mRect);
}
}
-
- /** Called after animation finished. */
- void onAnimationEnd(@NonNull SurfaceControl.Transaction t) {
- onAnimationUpdate(t, mAnimation.getDuration());
- }
-
- long getDurationHint() {
- return mAnimation.computeDurationHint();
- }
}
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentAnimationController.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentAnimationController.java
index 6579766..535dac1 100644
--- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentAnimationController.java
+++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentAnimationController.java
@@ -29,7 +29,6 @@
class TaskFragmentAnimationController {
private static final String TAG = "TaskFragAnimationCtrl";
- // TODO(b/196173550) turn off when finalize
static final boolean DEBUG = false;
private final TaskFragmentOrganizer mOrganizer;
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentAnimationRunner.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentAnimationRunner.java
index 3980d07..412559e 100644
--- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentAnimationRunner.java
+++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentAnimationRunner.java
@@ -23,7 +23,7 @@
import android.animation.Animator;
import android.animation.ValueAnimator;
-import android.graphics.Point;
+import android.graphics.Rect;
import android.os.Handler;
import android.os.Looper;
import android.os.RemoteException;
@@ -40,6 +40,7 @@
import java.util.ArrayList;
import java.util.List;
+import java.util.function.BiFunction;
/** To run the TaskFragment animations. */
class TaskFragmentAnimationRunner extends IRemoteAnimationRunner.Stub {
@@ -167,42 +168,86 @@
private List<TaskFragmentAnimationAdapter> createOpenAnimationAdapters(
@NonNull RemoteAnimationTarget[] targets) {
- final List<TaskFragmentAnimationAdapter> adapters = new ArrayList<>();
- for (RemoteAnimationTarget target : targets) {
- final Animation animation =
- mAnimationSpec.loadOpenAnimation(target.mode != MODE_CLOSING /* isEnter */);
- adapters.add(new TaskFragmentAnimationAdapter(animation, target));
- }
- return adapters;
+ return createOpenCloseAnimationAdapters(targets,
+ mAnimationSpec::loadOpenAnimation);
}
private List<TaskFragmentAnimationAdapter> createCloseAnimationAdapters(
@NonNull RemoteAnimationTarget[] targets) {
- final List<TaskFragmentAnimationAdapter> adapters = new ArrayList<>();
+ return createOpenCloseAnimationAdapters(targets,
+ mAnimationSpec::loadCloseAnimation);
+ }
+
+ private List<TaskFragmentAnimationAdapter> createOpenCloseAnimationAdapters(
+ @NonNull RemoteAnimationTarget[] targets,
+ @NonNull BiFunction<RemoteAnimationTarget, Rect, Animation> animationProvider) {
+ // We need to know if the target window is only a partial of the whole animation screen.
+ // If so, we will need to adjust it to make the whole animation screen looks like one.
+ final List<RemoteAnimationTarget> openingTargets = new ArrayList<>();
+ final List<RemoteAnimationTarget> closingTargets = new ArrayList<>();
+ final Rect openingWholeScreenBounds = new Rect();
+ final Rect closingWholeScreenBounds = new Rect();
for (RemoteAnimationTarget target : targets) {
- final Animation animation =
- mAnimationSpec.loadCloseAnimation(target.mode != MODE_CLOSING /* isEnter */);
- adapters.add(new TaskFragmentAnimationAdapter(animation, target));
+ if (target.mode != MODE_CLOSING) {
+ openingTargets.add(target);
+ openingWholeScreenBounds.union(target.localBounds);
+ } else {
+ closingTargets.add(target);
+ closingWholeScreenBounds.union(target.localBounds);
+ }
+ }
+
+ final List<TaskFragmentAnimationAdapter> adapters = new ArrayList<>();
+ for (RemoteAnimationTarget target : openingTargets) {
+ adapters.add(createOpenCloseAnimationAdapter(target, animationProvider,
+ openingWholeScreenBounds));
+ }
+ for (RemoteAnimationTarget target : closingTargets) {
+ adapters.add(createOpenCloseAnimationAdapter(target, animationProvider,
+ closingWholeScreenBounds));
}
return adapters;
}
+ private TaskFragmentAnimationAdapter createOpenCloseAnimationAdapter(
+ @NonNull RemoteAnimationTarget target,
+ @NonNull BiFunction<RemoteAnimationTarget, Rect, Animation> animationProvider,
+ @NonNull Rect wholeAnimationBounds) {
+ final Animation animation = animationProvider.apply(target, wholeAnimationBounds);
+ final Rect targetBounds = target.localBounds;
+ if (targetBounds.left == wholeAnimationBounds.left
+ && targetBounds.right != wholeAnimationBounds.right) {
+ // This is the left split of the whole animation window.
+ return new TaskFragmentAnimationAdapter.SplitAdapter(animation, target,
+ true /* isLeftHalf */, wholeAnimationBounds.width());
+ } else if (targetBounds.left != wholeAnimationBounds.left
+ && targetBounds.right == wholeAnimationBounds.right) {
+ // This is the right split of the whole animation window.
+ return new TaskFragmentAnimationAdapter.SplitAdapter(animation, target,
+ false /* isLeftHalf */, wholeAnimationBounds.width());
+ }
+ // Open/close window that fills the whole animation.
+ return new TaskFragmentAnimationAdapter(animation, target);
+ }
+
private List<TaskFragmentAnimationAdapter> createChangeAnimationAdapters(
@NonNull RemoteAnimationTarget[] targets) {
final List<TaskFragmentAnimationAdapter> adapters = new ArrayList<>();
for (RemoteAnimationTarget target : targets) {
if (target.startBounds != null) {
+ // This is the target with bounds change.
final Animation[] animations =
mAnimationSpec.createChangeBoundsChangeAnimations(target);
- // The snapshot surface will always be at (0, 0) of its parent.
- adapters.add(new TaskFragmentAnimationAdapter(animations[0], target,
- target.startLeash, false /* sizeChanged */, new Point(0, 0)));
- // The end surface will have size change for scaling.
- adapters.add(new TaskFragmentAnimationAdapter(animations[1], target,
- target.leash, true /* sizeChanged */, null /* position */));
+ // Adapter for the starting snapshot leash.
+ adapters.add(new TaskFragmentAnimationAdapter.SnapshotAdapter(
+ animations[0], target));
+ // Adapter for the ending bounds changed leash.
+ adapters.add(new TaskFragmentAnimationAdapter.BoundsChangeAdapter(
+ animations[1], target));
continue;
}
+ // These are the other targets that don't have bounds change in the same transition.
final Animation animation;
if (target.hasAnimatingParent) {
// No-op if it will be covered by the changing parent window.
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentAnimationSpec.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentAnimationSpec.java
index 11a79b2..c0908a5 100644
--- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentAnimationSpec.java
+++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentAnimationSpec.java
@@ -176,18 +176,28 @@
return new Animation[]{startSet, endSet};
}
- Animation loadOpenAnimation(boolean isEnter) {
- // TODO(b/196173550) We need to customize the animation to handle two open window as one.
- return mTransitionAnimation.loadDefaultAnimationAttr(isEnter
+ Animation loadOpenAnimation(@NonNull RemoteAnimationTarget target,
+ @NonNull Rect wholeAnimationBounds) {
+ final boolean isEnter = target.mode != MODE_CLOSING;
+ final Animation animation = mTransitionAnimation.loadDefaultAnimationAttr(isEnter
? R.styleable.WindowAnimation_activityOpenEnterAnimation
: R.styleable.WindowAnimation_activityOpenExitAnimation);
+ animation.initialize(target.localBounds.width(), target.localBounds.height(),
+ wholeAnimationBounds.width(), wholeAnimationBounds.height());
+ animation.scaleCurrentDuration(mTransitionAnimationScaleSetting);
+ return animation;
}
- Animation loadCloseAnimation(boolean isEnter) {
- // TODO(b/196173550) We need to customize the animation to handle two open window as one.
- return mTransitionAnimation.loadDefaultAnimationAttr(isEnter
+ Animation loadCloseAnimation(@NonNull RemoteAnimationTarget target,
+ @NonNull Rect wholeAnimationBounds) {
+ final boolean isEnter = target.mode != MODE_CLOSING;
+ final Animation animation = mTransitionAnimation.loadDefaultAnimationAttr(isEnter
? R.styleable.WindowAnimation_activityCloseEnterAnimation
: R.styleable.WindowAnimation_activityCloseExitAnimation);
+ animation.initialize(target.localBounds.width(), target.localBounds.height(),
+ wholeAnimationBounds.width(), wholeAnimationBounds.height());
+ animation.scaleCurrentDuration(mTransitionAnimationScaleSetting);
+ return animation;
}
private class SettingsObserver extends ContentObserver {
diff --git a/libs/WindowManager/Shell/Android.bp b/libs/WindowManager/Shell/Android.bp
index 9aaef3b..3ba1a34 100644
--- a/libs/WindowManager/Shell/Android.bp
+++ b/libs/WindowManager/Shell/Android.bp
@@ -39,6 +39,14 @@
}
filegroup {
+ name: "wm_shell_util-sources",
+ srcs: [
+ "src/com/android/wm/shell/util/**/*.java",
+ ],
+ path: "src",
+}
+
+filegroup {
name: "wm_shell-aidls",
srcs: [
"src/**/*.aidl",
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleData.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleData.java
index b48bda3..bef26bf 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleData.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleData.java
@@ -558,6 +558,8 @@
}
Bubble bubbleToRemove = mBubbles.get(indexToRemove);
bubbleToRemove.stopInflation();
+ overflowBubble(reason, bubbleToRemove);
+
if (mBubbles.size() == 1) {
if (hasOverflowBubbles() && (mPositioner.showingInTaskbar() || isExpanded())) {
// No more active bubbles but we have stuff in the overflow -- select that view
@@ -581,8 +583,6 @@
mStateChange.orderChanged |= repackAll();
}
- overflowBubble(reason, bubbleToRemove);
-
// Note: If mBubbles.isEmpty(), then mSelectedBubble is now null.
if (Objects.equals(mSelectedBubble, bubbleToRemove)) {
// Move selection to the new bubble at the same position.
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/fullscreen/FullscreenUnfoldController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/fullscreen/FullscreenUnfoldController.java
index 08ab85c..fc1b704 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/fullscreen/FullscreenUnfoldController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/fullscreen/FullscreenUnfoldController.java
@@ -16,9 +16,6 @@
package com.android.wm.shell.fullscreen;
-import static android.graphics.Color.blue;
-import static android.graphics.Color.green;
-import static android.graphics.Color.red;
import static android.util.MathUtils.lerp;
import static android.view.Display.DEFAULT_DISPLAY;
@@ -36,12 +33,11 @@
import android.view.SurfaceControl;
import com.android.internal.policy.ScreenDecorationsUtils;
-import com.android.wm.shell.R;
-import com.android.wm.shell.RootTaskDisplayAreaOrganizer;
import com.android.wm.shell.common.DisplayInsetsController;
import com.android.wm.shell.common.DisplayInsetsController.OnInsetsChangedListener;
import com.android.wm.shell.unfold.ShellUnfoldProgressProvider;
import com.android.wm.shell.unfold.ShellUnfoldProgressProvider.UnfoldListener;
+import com.android.wm.shell.unfold.UnfoldBackgroundController;
import java.util.concurrent.Executor;
@@ -59,21 +55,17 @@
private static final float VERTICAL_START_MARGIN = 0.03f;
private static final float END_SCALE = 1f;
private static final float START_SCALE = END_SCALE - VERTICAL_START_MARGIN * 2;
- private static final int BACKGROUND_LAYER_Z_INDEX = -1;
- private final Context mContext;
private final Executor mExecutor;
private final ShellUnfoldProgressProvider mProgressProvider;
- private final RootTaskDisplayAreaOrganizer mRootTaskDisplayAreaOrganizer;
private final DisplayInsetsController mDisplayInsetsController;
private final SparseArray<AnimationContext> mAnimationContextByTaskId = new SparseArray<>();
+ private final UnfoldBackgroundController mBackgroundController;
- private SurfaceControl mBackgroundLayer;
private InsetsSource mTaskbarInsetsSource;
private final float mWindowCornerRadiusPx;
- private final float[] mBackgroundColor;
private final float mExpandedTaskBarHeight;
private final SurfaceControl.Transaction mTransaction = new SurfaceControl.Transaction();
@@ -81,19 +73,17 @@
public FullscreenUnfoldController(
@NonNull Context context,
@NonNull Executor executor,
+ @NonNull UnfoldBackgroundController backgroundController,
@NonNull ShellUnfoldProgressProvider progressProvider,
- @NonNull RootTaskDisplayAreaOrganizer rootTaskDisplayAreaOrganizer,
@NonNull DisplayInsetsController displayInsetsController
) {
- mContext = context;
mExecutor = executor;
- mRootTaskDisplayAreaOrganizer = rootTaskDisplayAreaOrganizer;
mProgressProvider = progressProvider;
mDisplayInsetsController = displayInsetsController;
mWindowCornerRadiusPx = ScreenDecorationsUtils.getWindowCornerRadius(context);
mExpandedTaskBarHeight = context.getResources().getDimensionPixelSize(
com.android.internal.R.dimen.taskbar_frame_height);
- mBackgroundColor = getBackgroundColor();
+ mBackgroundController = backgroundController;
}
/**
@@ -108,7 +98,7 @@
public void onStateChangeProgress(float progress) {
if (mAnimationContextByTaskId.size() == 0) return;
- ensureBackground();
+ mBackgroundController.ensureBackground(mTransaction);
for (int i = mAnimationContextByTaskId.size() - 1; i >= 0; i--) {
final AnimationContext context = mAnimationContextByTaskId.valueAt(i);
@@ -135,7 +125,7 @@
resetSurface(context);
}
- removeBackground();
+ mBackgroundController.removeBackground(mTransaction);
mTransaction.apply();
}
@@ -178,7 +168,7 @@
}
if (mAnimationContextByTaskId.size() == 0) {
- removeBackground();
+ mBackgroundController.removeBackground(mTransaction);
}
mTransaction.apply();
@@ -194,39 +184,6 @@
(float) context.mTaskInfo.positionInParent.y);
}
- private void ensureBackground() {
- if (mBackgroundLayer != null) return;
-
- SurfaceControl.Builder colorLayerBuilder = new SurfaceControl.Builder()
- .setName("app-unfold-background")
- .setCallsite("AppUnfoldTransitionController")
- .setColorLayer();
- mRootTaskDisplayAreaOrganizer.attachToDisplayArea(DEFAULT_DISPLAY, colorLayerBuilder);
- mBackgroundLayer = colorLayerBuilder.build();
-
- mTransaction
- .setColor(mBackgroundLayer, mBackgroundColor)
- .show(mBackgroundLayer)
- .setLayer(mBackgroundLayer, BACKGROUND_LAYER_Z_INDEX);
- }
-
- private void removeBackground() {
- if (mBackgroundLayer == null) return;
- if (mBackgroundLayer.isValid()) {
- mTransaction.remove(mBackgroundLayer);
- }
- mBackgroundLayer = null;
- }
-
- private float[] getBackgroundColor() {
- int colorInt = mContext.getResources().getColor(R.color.unfold_transition_background);
- return new float[]{
- (float) red(colorInt) / 255.0F,
- (float) green(colorInt) / 255.0F,
- (float) blue(colorInt) / 255.0F
- };
- }
-
private class AnimationContext {
final SurfaceControl mLeash;
final Rect mStartCropRect = new Rect();
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/MainStage.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/MainStage.java
index d0998eb..7f82ebd 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/MainStage.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/MainStage.java
@@ -18,6 +18,7 @@
import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW;
+import android.annotation.Nullable;
import android.graphics.Rect;
import android.view.SurfaceSession;
import android.window.WindowContainerToken;
@@ -38,8 +39,10 @@
MainStage(ShellTaskOrganizer taskOrganizer, int displayId,
StageListenerCallbacks callbacks, SyncTransactionQueue syncQueue,
- SurfaceSession surfaceSession) {
- super(taskOrganizer, displayId, callbacks, syncQueue, surfaceSession);
+ SurfaceSession surfaceSession,
+ @Nullable StageTaskUnfoldController stageTaskUnfoldController) {
+ super(taskOrganizer, displayId, callbacks, syncQueue, surfaceSession,
+ stageTaskUnfoldController);
}
boolean isActive() {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SideStage.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SideStage.java
index 0e7ccd3..dc8fb9f 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SideStage.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SideStage.java
@@ -46,8 +46,10 @@
SideStage(Context context, ShellTaskOrganizer taskOrganizer, int displayId,
StageListenerCallbacks callbacks, SyncTransactionQueue syncQueue,
- SurfaceSession surfaceSession) {
- super(taskOrganizer, displayId, callbacks, syncQueue, surfaceSession);
+ SurfaceSession surfaceSession,
+ @Nullable StageTaskUnfoldController stageTaskUnfoldController) {
+ super(taskOrganizer, displayId, callbacks, syncQueue, surfaceSession,
+ stageTaskUnfoldController);
mContext = context;
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java
index ac68b3b..0d52719 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java
@@ -68,8 +68,11 @@
import java.io.PrintWriter;
import java.util.Arrays;
+import java.util.Optional;
import java.util.concurrent.Executor;
+import javax.inject.Provider;
+
/**
* Class manages split-screen multitasking mode and implements the main interface
* {@link SplitScreen}.
@@ -91,6 +94,7 @@
private final Transitions mTransitions;
private final TransactionPool mTransactionPool;
private final SplitscreenEventLogger mLogger;
+ private final Provider<Optional<StageTaskUnfoldController>> mUnfoldControllerProvider;
private StageCoordinator mStageCoordinator;
@@ -99,7 +103,8 @@
RootTaskDisplayAreaOrganizer rootTDAOrganizer,
ShellExecutor mainExecutor, DisplayImeController displayImeController,
DisplayInsetsController displayInsetsController,
- Transitions transitions, TransactionPool transactionPool) {
+ Transitions transitions, TransactionPool transactionPool,
+ Provider<Optional<StageTaskUnfoldController>> unfoldControllerProvider) {
mTaskOrganizer = shellTaskOrganizer;
mSyncQueue = syncQueue;
mContext = context;
@@ -109,6 +114,7 @@
mDisplayInsetsController = displayInsetsController;
mTransitions = transitions;
mTransactionPool = transactionPool;
+ mUnfoldControllerProvider = unfoldControllerProvider;
mLogger = new SplitscreenEventLogger();
}
@@ -131,7 +137,8 @@
// TODO: Multi-display
mStageCoordinator = new StageCoordinator(mContext, DEFAULT_DISPLAY, mSyncQueue,
mRootTDAOrganizer, mTaskOrganizer, mDisplayImeController,
- mDisplayInsetsController, mTransitions, mTransactionPool, mLogger);
+ mDisplayInsetsController, mTransitions, mTransactionPool, mLogger,
+ mUnfoldControllerProvider);
}
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java
index d7a6cff..414b4e4 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java
@@ -95,6 +95,9 @@
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.List;
+import java.util.Optional;
+
+import javax.inject.Provider;
/**
* Coordinates the staging (visibility, sizing, ...) of the split-screen {@link MainStage} and
@@ -121,8 +124,10 @@
private final MainStage mMainStage;
private final StageListenerImpl mMainStageListener = new StageListenerImpl();
+ private final StageTaskUnfoldController mMainUnfoldController;
private final SideStage mSideStage;
private final StageListenerImpl mSideStageListener = new StageListenerImpl();
+ private final StageTaskUnfoldController mSideUnfoldController;
@SplitPosition
private int mSideStagePosition = SPLIT_POSITION_BOTTOM_OR_RIGHT;
@@ -179,26 +184,32 @@
RootTaskDisplayAreaOrganizer rootTDAOrganizer, ShellTaskOrganizer taskOrganizer,
DisplayImeController displayImeController,
DisplayInsetsController displayInsetsController, Transitions transitions,
- TransactionPool transactionPool, SplitscreenEventLogger logger) {
+ TransactionPool transactionPool, SplitscreenEventLogger logger,
+ Provider<Optional<StageTaskUnfoldController>> unfoldControllerProvider) {
mContext = context;
mDisplayId = displayId;
mSyncQueue = syncQueue;
mRootTDAOrganizer = rootTDAOrganizer;
mTaskOrganizer = taskOrganizer;
mLogger = logger;
+ mMainUnfoldController = unfoldControllerProvider.get().orElse(null);
+ mSideUnfoldController = unfoldControllerProvider.get().orElse(null);
+
mMainStage = new MainStage(
mTaskOrganizer,
mDisplayId,
mMainStageListener,
mSyncQueue,
- mSurfaceSession);
+ mSurfaceSession,
+ mMainUnfoldController);
mSideStage = new SideStage(
mContext,
mTaskOrganizer,
mDisplayId,
mSideStageListener,
mSyncQueue,
- mSurfaceSession);
+ mSurfaceSession,
+ mSideUnfoldController);
mDisplayImeController = displayImeController;
mDisplayInsetsController = displayInsetsController;
mDisplayInsetsController.addInsetsChangedListener(mDisplayId, mSideStage);
@@ -218,7 +229,8 @@
MainStage mainStage, SideStage sideStage, DisplayImeController displayImeController,
DisplayInsetsController displayInsetsController, SplitLayout splitLayout,
Transitions transitions, TransactionPool transactionPool,
- SplitscreenEventLogger logger) {
+ SplitscreenEventLogger logger,
+ Provider<Optional<StageTaskUnfoldController>> unfoldControllerProvider) {
mContext = context;
mDisplayId = displayId;
mSyncQueue = syncQueue;
@@ -232,6 +244,8 @@
mSplitLayout = splitLayout;
mSplitTransitions = new SplitScreenTransitions(transactionPool, transitions,
mOnTransitionAnimationComplete);
+ mMainUnfoldController = unfoldControllerProvider.get().orElse(null);
+ mSideUnfoldController = unfoldControllerProvider.get().orElse(null);
mLogger = logger;
transitions.addHandler(this);
}
@@ -460,6 +474,7 @@
onLayoutChanged(mSplitLayout);
} else {
updateWindowBounds(mSplitLayout, wct);
+ updateUnfoldBounds();
}
}
}
@@ -515,6 +530,9 @@
mSideStage.removeAllTasks(wct, childrenToTop == mSideStage);
mMainStage.deactivate(wct, childrenToTop == mMainStage);
mTaskOrganizer.applyTransaction(wct);
+ mSyncQueue.runInSync(t -> t
+ .setWindowCrop(mMainStage.mRootLeash, null)
+ .setWindowCrop(mSideStage.mRootLeash, null));
// Hide divider and reset its position.
setDividerVisibility(false);
mSplitLayout.resetDividerPosition();
@@ -603,6 +621,11 @@
final SplitScreen.SplitScreenListener l = mListeners.get(i);
l.onSplitVisibilityChanged(mDividerVisible);
}
+
+ if (mMainUnfoldController != null && mSideUnfoldController != null) {
+ mMainUnfoldController.onSplitVisibilityChanged(mDividerVisible);
+ mSideUnfoldController.onSplitVisibilityChanged(mDividerVisible);
+ }
}
private void onStageRootTaskAppeared(StageListenerImpl stageListener) {
@@ -641,6 +664,7 @@
mDividerVisible = visible;
if (visible) {
mSplitLayout.init();
+ updateUnfoldBounds();
} else {
mSplitLayout.release();
}
@@ -784,12 +808,20 @@
public void onLayoutChanged(SplitLayout layout) {
final WindowContainerTransaction wct = new WindowContainerTransaction();
updateWindowBounds(layout, wct);
+ updateUnfoldBounds();
mSyncQueue.queue(wct);
mSyncQueue.runInSync(t -> updateSurfaceBounds(layout, t));
mSideStage.setOutlineVisibility(true);
mLogger.logResize(mSplitLayout.getDividerPositionAsFraction());
}
+ private void updateUnfoldBounds() {
+ if (mMainUnfoldController != null && mSideUnfoldController != null) {
+ mMainUnfoldController.onLayoutChanged(getMainStageBounds());
+ mSideUnfoldController.onLayoutChanged(getSideStageBounds());
+ }
+ }
+
/**
* Populates `wct` with operations that match the split windows to the current layout.
* To match relevant surfaces, make sure to call updateSurfaceBounds after `wct` is applied
@@ -846,6 +878,11 @@
mDisplayAreaInfo.configuration, this, mParentContainerCallbacks,
mDisplayImeController, mTaskOrganizer);
mDisplayInsetsController.addInsetsChangedListener(mDisplayId, mSplitLayout);
+
+ if (mMainUnfoldController != null && mSideUnfoldController != null) {
+ mMainUnfoldController.init();
+ mSideUnfoldController.init();
+ }
}
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageTaskListener.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageTaskListener.java
index 4140332..84d570f 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageTaskListener.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageTaskListener.java
@@ -24,6 +24,7 @@
import static com.android.wm.shell.transition.Transitions.ENABLE_SHELL_TRANSITIONS;
import android.annotation.CallSuper;
+import android.annotation.Nullable;
import android.app.ActivityManager;
import android.graphics.Point;
import android.graphics.Rect;
@@ -80,12 +81,16 @@
protected SparseArray<ActivityManager.RunningTaskInfo> mChildrenTaskInfo = new SparseArray<>();
private final SparseArray<SurfaceControl> mChildrenLeashes = new SparseArray<>();
+ private final StageTaskUnfoldController mStageTaskUnfoldController;
+
StageTaskListener(ShellTaskOrganizer taskOrganizer, int displayId,
StageListenerCallbacks callbacks, SyncTransactionQueue syncQueue,
- SurfaceSession surfaceSession) {
+ SurfaceSession surfaceSession,
+ @Nullable StageTaskUnfoldController stageTaskUnfoldController) {
mCallbacks = callbacks;
mSyncQueue = syncQueue;
mSurfaceSession = surfaceSession;
+ mStageTaskUnfoldController = stageTaskUnfoldController;
taskOrganizer.createRootTask(displayId, WINDOWING_MODE_MULTI_WINDOW, this);
}
@@ -158,6 +163,10 @@
throw new IllegalArgumentException(this + "\n Unknown task: " + taskInfo
+ "\n mRootTaskInfo: " + mRootTaskInfo);
}
+
+ if (mStageTaskUnfoldController != null) {
+ mStageTaskUnfoldController.onTaskAppeared(taskInfo, leash);
+ }
}
@Override
@@ -210,6 +219,10 @@
throw new IllegalArgumentException(this + "\n Unknown task: " + taskInfo
+ "\n mRootTaskInfo: " + mRootTaskInfo);
}
+
+ if (mStageTaskUnfoldController != null) {
+ mStageTaskUnfoldController.onTaskVanished(taskInfo);
+ }
}
@Override
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageTaskUnfoldController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageTaskUnfoldController.java
new file mode 100644
index 0000000..e904f6a
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageTaskUnfoldController.java
@@ -0,0 +1,224 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell.splitscreen;
+
+import static android.view.Display.DEFAULT_DISPLAY;
+
+import android.animation.RectEvaluator;
+import android.animation.TypeEvaluator;
+import android.annotation.NonNull;
+import android.app.ActivityManager;
+import android.content.Context;
+import android.graphics.Rect;
+import android.util.SparseArray;
+import android.view.InsetsSource;
+import android.view.InsetsState;
+import android.view.SurfaceControl;
+
+import com.android.internal.policy.ScreenDecorationsUtils;
+import com.android.wm.shell.common.DisplayInsetsController;
+import com.android.wm.shell.common.DisplayInsetsController.OnInsetsChangedListener;
+import com.android.wm.shell.common.TransactionPool;
+import com.android.wm.shell.unfold.ShellUnfoldProgressProvider;
+import com.android.wm.shell.unfold.ShellUnfoldProgressProvider.UnfoldListener;
+import com.android.wm.shell.unfold.UnfoldBackgroundController;
+
+import java.util.concurrent.Executor;
+
+/**
+ * Controls transformations of the split screen task surfaces in response
+ * to the unfolding/folding action on foldable devices
+ */
+public class StageTaskUnfoldController implements UnfoldListener, OnInsetsChangedListener {
+
+ private static final TypeEvaluator<Rect> RECT_EVALUATOR = new RectEvaluator(new Rect());
+ private static final float CROPPING_START_MARGIN_FRACTION = 0.05f;
+
+ private final SparseArray<AnimationContext> mAnimationContextByTaskId = new SparseArray<>();
+ private final ShellUnfoldProgressProvider mUnfoldProgressProvider;
+ private final DisplayInsetsController mDisplayInsetsController;
+ private final UnfoldBackgroundController mBackgroundController;
+ private final Executor mExecutor;
+ private final int mExpandedTaskBarHeight;
+ private final float mWindowCornerRadiusPx;
+ private final Rect mStageBounds = new Rect();
+ private final TransactionPool mTransactionPool;
+
+ private InsetsSource mTaskbarInsetsSource;
+ private boolean mBothStagesVisible;
+
+ public StageTaskUnfoldController(@NonNull Context context,
+ @NonNull TransactionPool transactionPool,
+ @NonNull ShellUnfoldProgressProvider unfoldProgressProvider,
+ @NonNull DisplayInsetsController displayInsetsController,
+ @NonNull UnfoldBackgroundController backgroundController,
+ @NonNull Executor executor) {
+ mUnfoldProgressProvider = unfoldProgressProvider;
+ mTransactionPool = transactionPool;
+ mExecutor = executor;
+ mBackgroundController = backgroundController;
+ mDisplayInsetsController = displayInsetsController;
+ mWindowCornerRadiusPx = ScreenDecorationsUtils.getWindowCornerRadius(context);
+ mExpandedTaskBarHeight = context.getResources().getDimensionPixelSize(
+ com.android.internal.R.dimen.taskbar_frame_height);
+ }
+
+ /**
+ * Initializes the controller, starts listening for the external events
+ */
+ public void init() {
+ mUnfoldProgressProvider.addListener(mExecutor, this);
+ mDisplayInsetsController.addInsetsChangedListener(DEFAULT_DISPLAY, this);
+ }
+
+ @Override
+ public void insetsChanged(InsetsState insetsState) {
+ mTaskbarInsetsSource = insetsState.getSource(InsetsState.ITYPE_EXTRA_NAVIGATION_BAR);
+ for (int i = mAnimationContextByTaskId.size() - 1; i >= 0; i--) {
+ AnimationContext context = mAnimationContextByTaskId.valueAt(i);
+ context.update();
+ }
+ }
+
+ /**
+ * Called when split screen task appeared
+ * @param taskInfo info for the appeared task
+ * @param leash surface leash for the appeared task
+ */
+ public void onTaskAppeared(ActivityManager.RunningTaskInfo taskInfo, SurfaceControl leash) {
+ AnimationContext context = new AnimationContext(leash);
+ mAnimationContextByTaskId.put(taskInfo.taskId, context);
+ }
+
+ /**
+ * Called when a split screen task vanished
+ * @param taskInfo info for the vanished task
+ */
+ public void onTaskVanished(ActivityManager.RunningTaskInfo taskInfo) {
+ AnimationContext context = mAnimationContextByTaskId.get(taskInfo.taskId);
+ if (context != null) {
+ final SurfaceControl.Transaction transaction = mTransactionPool.acquire();
+ resetSurface(transaction, context);
+ transaction.apply();
+ mTransactionPool.release(transaction);
+ }
+ mAnimationContextByTaskId.remove(taskInfo.taskId);
+ }
+
+ @Override
+ public void onStateChangeProgress(float progress) {
+ if (mAnimationContextByTaskId.size() == 0 || !mBothStagesVisible) return;
+
+ final SurfaceControl.Transaction transaction = mTransactionPool.acquire();
+ mBackgroundController.ensureBackground(transaction);
+
+ for (int i = mAnimationContextByTaskId.size() - 1; i >= 0; i--) {
+ AnimationContext context = mAnimationContextByTaskId.valueAt(i);
+
+ context.mCurrentCropRect.set(RECT_EVALUATOR
+ .evaluate(progress, context.mStartCropRect, context.mEndCropRect));
+
+ transaction.setWindowCrop(context.mLeash, context.mCurrentCropRect)
+ .setCornerRadius(context.mLeash, mWindowCornerRadiusPx);
+ }
+
+ transaction.apply();
+
+ mTransactionPool.release(transaction);
+ }
+
+ @Override
+ public void onStateChangeFinished() {
+ resetTransformations();
+ }
+
+ /**
+ * Called when split screen visibility changes
+ * @param bothStagesVisible true if both stages of the split screen are visible
+ */
+ public void onSplitVisibilityChanged(boolean bothStagesVisible) {
+ mBothStagesVisible = bothStagesVisible;
+ if (!bothStagesVisible) {
+ resetTransformations();
+ }
+ }
+
+ /**
+ * Called when split screen stage bounds changed
+ * @param bounds new bounds for this stage
+ */
+ public void onLayoutChanged(Rect bounds) {
+ mStageBounds.set(bounds);
+
+ for (int i = mAnimationContextByTaskId.size() - 1; i >= 0; i--) {
+ final AnimationContext context = mAnimationContextByTaskId.valueAt(i);
+ context.update();
+ }
+ }
+
+ private void resetTransformations() {
+ final SurfaceControl.Transaction transaction = mTransactionPool.acquire();
+
+ for (int i = mAnimationContextByTaskId.size() - 1; i >= 0; i--) {
+ final AnimationContext context = mAnimationContextByTaskId.valueAt(i);
+ resetSurface(transaction, context);
+ }
+ mBackgroundController.removeBackground(transaction);
+ transaction.apply();
+
+ mTransactionPool.release(transaction);
+ }
+
+ private void resetSurface(SurfaceControl.Transaction transaction, AnimationContext context) {
+ transaction
+ .setWindowCrop(context.mLeash, null)
+ .setCornerRadius(context.mLeash, 0.0F);
+ }
+
+ private class AnimationContext {
+ final SurfaceControl mLeash;
+ final Rect mStartCropRect = new Rect();
+ final Rect mEndCropRect = new Rect();
+ final Rect mCurrentCropRect = new Rect();
+
+ private AnimationContext(SurfaceControl leash) {
+ this.mLeash = leash;
+ update();
+ }
+
+ private void update() {
+ mStartCropRect.set(mStageBounds);
+
+ if (mTaskbarInsetsSource != null) {
+ // Only insets the cropping window with taskbar when taskbar is expanded
+ if (mTaskbarInsetsSource.getFrame().height() >= mExpandedTaskBarHeight) {
+ mStartCropRect.inset(mTaskbarInsetsSource
+ .calculateVisibleInsets(mStartCropRect));
+ }
+ }
+
+ // Offset to surface coordinates as layout bounds are in screen coordinates
+ mStartCropRect.offsetTo(0, 0);
+
+ mEndCropRect.set(mStartCropRect);
+
+ int maxSize = Math.max(mEndCropRect.width(), mEndCropRect.height());
+ int margin = (int) (maxSize * CROPPING_START_MARGIN_FRACTION);
+ mStartCropRect.inset(margin, margin, margin, margin);
+ }
+ }
+}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java
index b673d48..663d647 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java
@@ -41,6 +41,7 @@
import static android.window.TransitionInfo.FLAG_SHOW_WALLPAPER;
import static android.window.TransitionInfo.FLAG_STARTING_WINDOW_TRANSFER_RECIPIENT;
import static android.window.TransitionInfo.FLAG_TRANSLUCENT;
+import static android.window.TransitionInfo.isIndependent;
import static com.android.internal.policy.TransitionAnimation.WALLPAPER_TRANSITION_CLOSE;
import static com.android.internal.policy.TransitionAnimation.WALLPAPER_TRANSITION_INTRA_CLOSE;
@@ -70,6 +71,7 @@
import android.view.animation.Transformation;
import android.window.TransitionInfo;
import android.window.TransitionRequestInfo;
+import android.window.WindowContainerToken;
import android.window.WindowContainerTransaction;
import com.android.internal.R;
@@ -82,6 +84,7 @@
import com.android.wm.shell.common.ShellExecutor;
import com.android.wm.shell.common.TransactionPool;
import com.android.wm.shell.protolog.ShellProtoLogGroup;
+import com.android.wm.shell.util.CounterRotator;
import java.util.ArrayList;
@@ -269,9 +272,16 @@
final ArrayList<Animator> animations = new ArrayList<>();
mAnimations.put(transition, animations);
+ final ArrayMap<WindowContainerToken, CounterRotator> counterRotators = new ArrayMap<>();
+
final Runnable onAnimFinish = () -> {
if (!animations.isEmpty()) return;
+ for (int i = 0; i < counterRotators.size(); ++i) {
+ counterRotators.valueAt(i).cleanUp(info.getRootLeash());
+ }
+ counterRotators.clear();
+
if (mRotationAnimation != null) {
mRotationAnimation.kill();
mRotationAnimation = null;
@@ -285,16 +295,44 @@
for (int i = info.getChanges().size() - 1; i >= 0; --i) {
final TransitionInfo.Change change = info.getChanges().get(i);
- if (info.getType() == TRANSIT_CHANGE && change.getMode() == TRANSIT_CHANGE
- && (change.getFlags() & FLAG_IS_DISPLAY) != 0) {
- boolean isSeamless = isRotationSeamless(info, mDisplayController);
- final int anim = getRotationAnimation(info);
- if (!(isSeamless || anim == ROTATION_ANIMATION_JUMPCUT)) {
- mRotationAnimation = new ScreenRotationAnimation(mContext, mSurfaceSession,
- mTransactionPool, startTransaction, change, info.getRootLeash());
- mRotationAnimation.startAnimation(animations, onAnimFinish,
- mTransitionAnimationScaleSetting, mMainExecutor, mAnimExecutor);
- continue;
+ if (change.getMode() == TRANSIT_CHANGE && (change.getFlags() & FLAG_IS_DISPLAY) != 0) {
+ int rotateDelta = change.getEndRotation() - change.getStartRotation();
+ int displayW = change.getEndAbsBounds().width();
+ int displayH = change.getEndAbsBounds().height();
+ if (info.getType() == TRANSIT_CHANGE) {
+ boolean isSeamless = isRotationSeamless(info, mDisplayController);
+ final int anim = getRotationAnimation(info);
+ if (!(isSeamless || anim == ROTATION_ANIMATION_JUMPCUT)) {
+ mRotationAnimation = new ScreenRotationAnimation(mContext, mSurfaceSession,
+ mTransactionPool, startTransaction, change, info.getRootLeash());
+ mRotationAnimation.startAnimation(animations, onAnimFinish,
+ mTransitionAnimationScaleSetting, mMainExecutor, mAnimExecutor);
+ continue;
+ }
+ } else {
+ // opening/closing an app into a new orientation. Counter-rotate all
+ // "going-away" things since they are still in the old orientation.
+ for (int j = info.getChanges().size() - 1; j >= 0; --j) {
+ final TransitionInfo.Change innerChange = info.getChanges().get(j);
+ if (!Transitions.isClosingType(innerChange.getMode())
+ || !isIndependent(innerChange, info)
+ || innerChange.getParent() == null) {
+ continue;
+ }
+ CounterRotator crot = counterRotators.get(innerChange.getParent());
+ if (crot == null) {
+ crot = new CounterRotator();
+ crot.setup(startTransaction,
+ info.getChange(innerChange.getParent()).getLeash(),
+ rotateDelta, displayW, displayH);
+ if (crot.getSurface() != null) {
+ int layer = info.getChanges().size() - j;
+ startTransaction.setLayer(crot.getSurface(), layer);
+ }
+ counterRotators.put(innerChange.getParent(), crot);
+ }
+ crot.addChild(startTransaction, innerChange.getLeash());
+ }
}
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/unfold/UnfoldBackgroundController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/unfold/UnfoldBackgroundController.java
new file mode 100644
index 0000000..9faf454
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/unfold/UnfoldBackgroundController.java
@@ -0,0 +1,89 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell.unfold;
+
+import static android.graphics.Color.blue;
+import static android.graphics.Color.green;
+import static android.graphics.Color.red;
+import static android.view.Display.DEFAULT_DISPLAY;
+
+import android.annotation.NonNull;
+import android.content.Context;
+import android.view.SurfaceControl;
+
+import com.android.wm.shell.R;
+import com.android.wm.shell.RootTaskDisplayAreaOrganizer;
+
+/**
+ * Controls background color layer for the unfold animations
+ */
+public class UnfoldBackgroundController {
+
+ private static final int BACKGROUND_LAYER_Z_INDEX = -1;
+
+ private final RootTaskDisplayAreaOrganizer mRootTaskDisplayAreaOrganizer;
+ private final float[] mBackgroundColor;
+ private SurfaceControl mBackgroundLayer;
+
+ public UnfoldBackgroundController(
+ @NonNull Context context,
+ @NonNull RootTaskDisplayAreaOrganizer rootTaskDisplayAreaOrganizer) {
+ mRootTaskDisplayAreaOrganizer = rootTaskDisplayAreaOrganizer;
+ mBackgroundColor = getBackgroundColor(context);
+ }
+
+ /**
+ * Ensures that unfold animation background color layer is present,
+ * @param transaction where we should add the background if it is not added
+ */
+ public void ensureBackground(@NonNull SurfaceControl.Transaction transaction) {
+ if (mBackgroundLayer != null) return;
+
+ SurfaceControl.Builder colorLayerBuilder = new SurfaceControl.Builder()
+ .setName("app-unfold-background")
+ .setCallsite("AppUnfoldTransitionController")
+ .setColorLayer();
+ mRootTaskDisplayAreaOrganizer.attachToDisplayArea(DEFAULT_DISPLAY, colorLayerBuilder);
+ mBackgroundLayer = colorLayerBuilder.build();
+
+ transaction
+ .setColor(mBackgroundLayer, mBackgroundColor)
+ .show(mBackgroundLayer)
+ .setLayer(mBackgroundLayer, BACKGROUND_LAYER_Z_INDEX);
+ }
+
+ /**
+ * Ensures that the background is not visible
+ * @param transaction as part of which the removal will happen if needed
+ */
+ public void removeBackground(@NonNull SurfaceControl.Transaction transaction) {
+ if (mBackgroundLayer == null) return;
+ if (mBackgroundLayer.isValid()) {
+ transaction.remove(mBackgroundLayer);
+ }
+ mBackgroundLayer = null;
+ }
+
+ private float[] getBackgroundColor(Context context) {
+ int colorInt = context.getResources().getColor(R.color.unfold_transition_background);
+ return new float[]{
+ (float) red(colorInt) / 255.0F,
+ (float) green(colorInt) / 255.0F,
+ (float) blue(colorInt) / 255.0F
+ };
+ }
+}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/util/CounterRotator.java b/libs/WindowManager/Shell/src/com/android/wm/shell/util/CounterRotator.java
new file mode 100644
index 0000000..b9b6716
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/util/CounterRotator.java
@@ -0,0 +1,87 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell.util;
+
+import android.view.SurfaceControl;
+
+import java.util.ArrayList;
+
+/**
+ * Utility class that takes care of counter-rotating surfaces during a transition animation.
+ */
+public class CounterRotator {
+ SurfaceControl mSurface = null;
+ ArrayList<SurfaceControl> mRotateChildren = null;
+
+ /** Gets the surface with the counter-rotation. */
+ public SurfaceControl getSurface() {
+ return mSurface;
+ }
+
+ /**
+ * Sets up this rotator.
+ *
+ * @param rotateDelta is the forward rotation change (the rotation the display is making).
+ * @param displayW (and H) Is the size of the rotating display.
+ */
+ public void setup(SurfaceControl.Transaction t, SurfaceControl parent, int rotateDelta,
+ float displayW, float displayH) {
+ if (rotateDelta == 0) return;
+ mRotateChildren = new ArrayList<>();
+ // We want to counter-rotate, so subtract from 4
+ rotateDelta = 4 - (rotateDelta + 4) % 4;
+ mSurface = new SurfaceControl.Builder()
+ .setName("Transition Unrotate")
+ .setContainerLayer()
+ .setParent(parent)
+ .build();
+ // column-major
+ if (rotateDelta == 1) {
+ t.setMatrix(mSurface, 0, 1, -1, 0);
+ t.setPosition(mSurface, displayW, 0);
+ } else if (rotateDelta == 2) {
+ t.setMatrix(mSurface, -1, 0, 0, -1);
+ t.setPosition(mSurface, displayW, displayH);
+ } else if (rotateDelta == 3) {
+ t.setMatrix(mSurface, 0, -1, 1, 0);
+ t.setPosition(mSurface, 0, displayH);
+ }
+ t.show(mSurface);
+ }
+
+ /**
+ * Add a surface that needs to be counter-rotate.
+ */
+ public void addChild(SurfaceControl.Transaction t, SurfaceControl child) {
+ if (mSurface == null) return;
+ t.reparent(child, mSurface);
+ mRotateChildren.add(child);
+ }
+
+ /**
+ * Clean-up. This undoes any reparenting and effectively stops the counter-rotation.
+ */
+ public void cleanUp(SurfaceControl rootLeash) {
+ if (mSurface == null) return;
+ SurfaceControl.Transaction t = new SurfaceControl.Transaction();
+ for (int i = mRotateChildren.size() - 1; i >= 0; --i) {
+ t.reparent(mRotateChildren.get(i), rootLeash);
+ }
+ t.remove(mSurface);
+ t.apply();
+ }
+}
diff --git a/libs/WindowManager/Shell/tests/flicker/AndroidManifest.xml b/libs/WindowManager/Shell/tests/flicker/AndroidManifest.xml
index e6d32ff..06df9568 100644
--- a/libs/WindowManager/Shell/tests/flicker/AndroidManifest.xml
+++ b/libs/WindowManager/Shell/tests/flicker/AndroidManifest.xml
@@ -42,6 +42,9 @@
<uses-permission android:name="android.permission.MEDIA_CONTENT_CONTROL"/>
<!-- ATM.removeRootTasksWithActivityTypes() -->
<uses-permission android:name="android.permission.MANAGE_ACTIVITY_TASKS" />
+ <!-- Enable bubble notification-->
+ <uses-permission android:name="android.permission.STATUS_BAR_SERVICE" />
+
<!-- Allow the test to write directly to /sdcard/ -->
<application android:requestLegacyExternalStorage="true">
<uses-library android:name="android.test.runner"/>
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/CommonAssertions.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/CommonAssertions.kt
index b36468b..c07f0eb 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/CommonAssertions.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/CommonAssertions.kt
@@ -14,13 +14,14 @@
* limitations under the License.
*/
+@file:JvmName("CommonAssertions")
package com.android.wm.shell.flicker
-import android.content.ComponentName
import android.graphics.Region
import android.view.Surface
import com.android.server.wm.flicker.FlickerTestParameter
import com.android.server.wm.flicker.helpers.WindowUtils
+import com.android.server.wm.traces.common.FlickerComponentName
fun FlickerTestParameter.appPairsDividerIsVisibleAtEnd() {
assertLayersEnd {
@@ -72,7 +73,7 @@
fun FlickerTestParameter.appPairsPrimaryBoundsIsVisibleAtEnd(
rotation: Int,
- primaryComponent: ComponentName
+ primaryComponent: FlickerComponentName
) {
assertLayersEnd {
val dividerRegion = layer(APP_PAIR_SPLIT_DIVIDER_COMPONENT).visibleRegion.region
@@ -83,7 +84,7 @@
fun FlickerTestParameter.dockedStackPrimaryBoundsIsVisibleAtEnd(
rotation: Int,
- primaryComponent: ComponentName
+ primaryComponent: FlickerComponentName
) {
assertLayersEnd {
val dividerRegion = layer(DOCKED_STACK_DIVIDER_COMPONENT).visibleRegion.region
@@ -94,7 +95,7 @@
fun FlickerTestParameter.appPairsSecondaryBoundsIsVisibleAtEnd(
rotation: Int,
- secondaryComponent: ComponentName
+ secondaryComponent: FlickerComponentName
) {
assertLayersEnd {
val dividerRegion = layer(APP_PAIR_SPLIT_DIVIDER_COMPONENT).visibleRegion.region
@@ -105,7 +106,7 @@
fun FlickerTestParameter.dockedStackSecondaryBoundsIsVisibleAtEnd(
rotation: Int,
- secondaryComponent: ComponentName
+ secondaryComponent: FlickerComponentName
) {
assertLayersEnd {
val dividerRegion = layer(DOCKED_STACK_DIVIDER_COMPONENT).visibleRegion.region
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/CommonConstants.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/CommonConstants.kt
index ff1a6e6..40891f3 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/CommonConstants.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/CommonConstants.kt
@@ -17,8 +17,8 @@
@file:JvmName("CommonConstants")
package com.android.wm.shell.flicker
-import android.content.ComponentName
+import com.android.server.wm.traces.common.FlickerComponentName
const val SYSTEM_UI_PACKAGE_NAME = "com.android.systemui"
-val APP_PAIR_SPLIT_DIVIDER_COMPONENT = ComponentName("", "AppPairSplitDivider#")
-val DOCKED_STACK_DIVIDER_COMPONENT = ComponentName("", "DockedStackDivider#")
\ No newline at end of file
+val APP_PAIR_SPLIT_DIVIDER_COMPONENT = FlickerComponentName("", "AppPairSplitDivider#")
+val DOCKED_STACK_DIVIDER_COMPONENT = FlickerComponentName("", "DockedStackDivider#")
\ No newline at end of file
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/WaitUtils.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/WaitUtils.kt
index a6d6735..b63d9ff 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/WaitUtils.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/WaitUtils.kt
@@ -14,6 +14,7 @@
* limitations under the License.
*/
+@file:JvmName("WaitUtils")
package com.android.wm.shell.flicker
import android.os.SystemClock
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTestCannotPairNonResizeableApps.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTestCannotPairNonResizeableApps.kt
index 19374ed..038be9c 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTestCannotPairNonResizeableApps.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTestCannotPairNonResizeableApps.kt
@@ -100,8 +100,8 @@
"Non resizeable app not initialized"
}
testSpec.assertWmEnd {
- isVisible(nonResizeableApp.component)
- isInvisible(primaryApp.component)
+ isAppWindowVisible(nonResizeableApp.component)
+ isAppWindowInvisible(primaryApp.component)
}
}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTestPairPrimaryAndSecondaryApps.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTestPairPrimaryAndSecondaryApps.kt
index 46ee892..bbc6b2d 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTestPairPrimaryAndSecondaryApps.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTestPairPrimaryAndSecondaryApps.kt
@@ -77,8 +77,8 @@
@Test
fun bothAppWindowsVisible() {
testSpec.assertWmEnd {
- isVisible(primaryApp.component)
- isVisible(secondaryApp.component)
+ isAppWindowVisible(primaryApp.component)
+ isAppWindowVisible(secondaryApp.component)
}
}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTestSupportPairNonResizeableApps.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTestSupportPairNonResizeableApps.kt
index f7ced71..bb784a8 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTestSupportPairNonResizeableApps.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTestSupportPairNonResizeableApps.kt
@@ -100,8 +100,8 @@
"Non resizeable app not initialized"
}
testSpec.assertWmEnd {
- isVisible(nonResizeableApp.component)
- isVisible(primaryApp.component)
+ isAppWindowVisible(nonResizeableApp.component)
+ isAppWindowVisible(primaryApp.component)
}
}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTestUnpairPrimaryAndSecondaryApps.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTestUnpairPrimaryAndSecondaryApps.kt
index 3debdd3..a1a4db1 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTestUnpairPrimaryAndSecondaryApps.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTestUnpairPrimaryAndSecondaryApps.kt
@@ -81,8 +81,8 @@
@Test
fun bothAppWindowsInvisible() {
testSpec.assertWmEnd {
- isInvisible(primaryApp.component)
- isInvisible(secondaryApp.component)
+ isAppWindowInvisible(primaryApp.component)
+ isAppWindowInvisible(secondaryApp.component)
}
}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTransition.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTransition.kt
index cdf89a5..9e20bbb 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTransition.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTransition.kt
@@ -20,13 +20,11 @@
import android.content.Context
import android.platform.test.annotations.Presubmit
import android.system.helpers.ActivityHelper
-import android.view.Surface
import androidx.test.filters.FlakyTest
import androidx.test.platform.app.InstrumentationRegistry
import com.android.server.wm.flicker.FlickerBuilderProvider
import com.android.server.wm.flicker.FlickerTestParameter
import com.android.server.wm.flicker.dsl.FlickerBuilder
-import com.android.server.wm.flicker.endRotation
import com.android.server.wm.flicker.helpers.isRotated
import com.android.server.wm.flicker.helpers.setRotation
import com.android.server.wm.flicker.helpers.wakeUpAndGoToHomeScreen
@@ -38,6 +36,7 @@
import com.android.server.wm.flicker.statusBarLayerIsVisible
import com.android.server.wm.flicker.statusBarLayerRotatesScales
import com.android.server.wm.flicker.statusBarWindowIsVisible
+import com.android.server.wm.traces.parser.toFlickerComponent
import com.android.wm.shell.flicker.helpers.AppPairsHelper
import com.android.wm.shell.flicker.helpers.BaseAppHelper
import com.android.wm.shell.flicker.helpers.MultiWindowHelper.Companion.getDevEnableNonResizableMultiWindow
@@ -55,7 +54,7 @@
protected val activityHelper = ActivityHelper.getInstance()
protected val appPairsHelper = AppPairsHelper(instrumentation,
Components.SplitScreenActivity.LABEL,
- Components.SplitScreenActivity.COMPONENT)
+ Components.SplitScreenActivity.COMPONENT.toFlickerComponent())
protected val primaryApp = SplitScreenHelper.getPrimary(instrumentation)
protected val secondaryApp = SplitScreenHelper.getSecondary(instrumentation)
@@ -178,15 +177,9 @@
@Presubmit
@Test
- open fun navBarLayerRotatesAndScales() {
- testSpec.navBarLayerRotatesAndScales(Surface.ROTATION_0,
- testSpec.config.endRotation)
- }
+ open fun navBarLayerRotatesAndScales() = testSpec.navBarLayerRotatesAndScales()
@Presubmit
@Test
- open fun statusBarLayerRotatesScales() {
- testSpec.statusBarLayerRotatesScales(Surface.ROTATION_0,
- testSpec.config.endRotation)
- }
+ open fun statusBarLayerRotatesScales() = testSpec.statusBarLayerRotatesScales()
}
\ No newline at end of file
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/RotateTwoLaunchedAppsInAppPairsMode.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/RotateTwoLaunchedAppsInAppPairsMode.kt
index 3e782e6..56a2531 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/RotateTwoLaunchedAppsInAppPairsMode.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/RotateTwoLaunchedAppsInAppPairsMode.kt
@@ -73,8 +73,8 @@
@Test
fun bothAppWindowsVisible() {
testSpec.assertWmEnd {
- isVisible(primaryApp.component)
- .isVisible(secondaryApp.component)
+ isAppWindowVisible(primaryApp.component)
+ isAppWindowVisible(secondaryApp.component)
}
}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/RotateTwoLaunchedAppsRotateAndEnterAppPairsMode.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/RotateTwoLaunchedAppsRotateAndEnterAppPairsMode.kt
index ee28c7a..0699a4f 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/RotateTwoLaunchedAppsRotateAndEnterAppPairsMode.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/RotateTwoLaunchedAppsRotateAndEnterAppPairsMode.kt
@@ -85,8 +85,8 @@
@Test
fun bothAppWindowsVisible() {
testSpec.assertWmEnd {
- isVisible(primaryApp.component)
- isVisible(secondaryApp.component)
+ isAppWindowVisible(primaryApp.component)
+ isAppWindowVisible(secondaryApp.component)
}
}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/BaseBubbleScreen.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/BaseBubbleScreen.kt
new file mode 100644
index 0000000..322d8b5
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/BaseBubbleScreen.kt
@@ -0,0 +1,118 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell.flicker.bubble
+
+import android.app.INotificationManager
+import android.app.Instrumentation
+import android.app.NotificationManager
+import android.content.Context
+import android.os.ServiceManager
+import android.view.Surface
+import androidx.test.filters.FlakyTest
+import androidx.test.platform.app.InstrumentationRegistry
+import androidx.test.uiautomator.By
+import androidx.test.uiautomator.UiObject2
+import androidx.test.uiautomator.Until
+import com.android.server.wm.flicker.FlickerBuilderProvider
+import com.android.server.wm.flicker.FlickerTestParameter
+import com.android.server.wm.flicker.FlickerTestParameterFactory
+import com.android.server.wm.flicker.dsl.FlickerBuilder
+import com.android.server.wm.flicker.helpers.SYSTEMUI_PACKAGE
+import com.android.server.wm.flicker.repetitions
+import com.android.wm.shell.flicker.helpers.LaunchBubbleHelper
+import org.junit.Test
+import org.junit.runners.Parameterized
+
+/**
+ * Base configurations for Bubble flicker tests
+ */
+abstract class BaseBubbleScreen(protected val testSpec: FlickerTestParameter) {
+
+ protected val instrumentation: Instrumentation = InstrumentationRegistry.getInstrumentation()
+ protected val context: Context = instrumentation.context
+ protected val testApp = LaunchBubbleHelper(instrumentation)
+
+ protected val notifyManager = INotificationManager.Stub.asInterface(
+ ServiceManager.getService(Context.NOTIFICATION_SERVICE))
+
+ protected val packageManager = context.getPackageManager()
+ protected val uid = packageManager.getApplicationInfo(
+ testApp.component.packageName, 0).uid
+
+ protected lateinit var addBubbleBtn: UiObject2
+ protected lateinit var cancelAllBtn: UiObject2
+
+ protected abstract val transition: FlickerBuilder.(Map<String, Any?>) -> Unit
+
+ @JvmOverloads
+ protected open fun buildTransition(
+ extraSpec: FlickerBuilder.(Map<String, Any?>) -> Unit = {}
+ ): FlickerBuilder.(Map<String, Any?>) -> Unit {
+ return { configuration ->
+
+ setup {
+ test {
+ notifyManager.setBubblesAllowed(testApp.component.packageName,
+ uid, NotificationManager.BUBBLE_PREFERENCE_ALL)
+ testApp.launchViaIntent(wmHelper)
+ addBubbleBtn = device.wait(Until.findObject(
+ By.text("Add Bubble")), FIND_OBJECT_TIMEOUT)
+ cancelAllBtn = device.wait(Until.findObject(
+ By.text("Cancel All Bubble")), FIND_OBJECT_TIMEOUT)
+ }
+ }
+
+ teardown {
+ notifyManager.setBubblesAllowed(testApp.component.packageName,
+ uid, NotificationManager.BUBBLE_PREFERENCE_NONE)
+ testApp.exit()
+ }
+
+ extraSpec(this, configuration)
+ }
+ }
+
+ @FlakyTest
+ @Test
+ fun testAppIsAlwaysVisible() {
+ testSpec.assertLayers {
+ this.isVisible(testApp.component)
+ }
+ }
+
+ @FlickerBuilderProvider
+ fun buildFlicker(): FlickerBuilder {
+ return FlickerBuilder(instrumentation).apply {
+ repeat { testSpec.config.repetitions }
+ transition(this, testSpec.config)
+ }
+ }
+
+ companion object {
+ @Parameterized.Parameters(name = "{0}")
+ @JvmStatic
+ fun getParams(): List<FlickerTestParameter> {
+ return FlickerTestParameterFactory.getInstance()
+ .getConfigNonRotationTests(supportedRotations = listOf(Surface.ROTATION_0),
+ repetitions = 5)
+ }
+
+ const val FIND_OBJECT_TIMEOUT = 2000L
+ const val SYSTEM_UI_PACKAGE = SYSTEMUI_PACKAGE
+ const val BUBBLE_RES_NAME = "bubble_view"
+ }
+}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/DismissBubbleScreen.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/DismissBubbleScreen.kt
new file mode 100644
index 0000000..bfdcb36
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/DismissBubbleScreen.kt
@@ -0,0 +1,65 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell.flicker.bubble
+
+import android.content.Context
+import android.graphics.Point
+import android.util.DisplayMetrics
+import android.view.WindowManager
+import androidx.test.filters.RequiresDevice
+import androidx.test.uiautomator.By
+import androidx.test.uiautomator.Until
+import com.android.server.wm.flicker.FlickerParametersRunnerFactory
+import com.android.server.wm.flicker.FlickerTestParameter
+import com.android.server.wm.flicker.annotation.Group4
+import com.android.server.wm.flicker.dsl.FlickerBuilder
+import org.junit.runner.RunWith
+import org.junit.runners.Parameterized
+
+/**
+ * Test launching a new activity from bubble.
+ *
+ * To run this test: `atest WMShellFlickerTests:DismissBubbleScreen`
+ *
+ * Actions:
+ * Dismiss a bubble notification
+ */
+@RequiresDevice
+@RunWith(Parameterized::class)
+@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
+@Group4
+class DismissBubbleScreen(testSpec: FlickerTestParameter) : BaseBubbleScreen(testSpec) {
+
+ val wm = context.getSystemService(Context.WINDOW_SERVICE) as WindowManager
+ val displaySize = DisplayMetrics()
+
+ override val transition: FlickerBuilder.(Map<String, Any?>) -> Unit
+ get() = buildTransition() {
+ setup {
+ eachRun {
+ addBubbleBtn?.run { addBubbleBtn.click() } ?: error("Add Bubble not found")
+ }
+ }
+ transitions {
+ wm?.run { wm.getDefaultDisplay().getMetrics(displaySize) } ?: error("WM not found")
+ val dist = Point((displaySize.widthPixels / 2), displaySize.heightPixels)
+ val showBubble = device.wait(Until.findObject(
+ By.res(SYSTEM_UI_PACKAGE, BUBBLE_RES_NAME)), FIND_OBJECT_TIMEOUT)
+ showBubble?.run { drag(dist, 1000) } ?: error("Show bubble not found")
+ }
+ }
+}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/ExpandBubbleScreen.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/ExpandBubbleScreen.kt
new file mode 100644
index 0000000..42eeadf
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/ExpandBubbleScreen.kt
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell.flicker.bubble
+
+import androidx.test.filters.RequiresDevice
+import androidx.test.uiautomator.By
+import androidx.test.uiautomator.Until
+import com.android.server.wm.flicker.FlickerParametersRunnerFactory
+import com.android.server.wm.flicker.FlickerTestParameter
+import com.android.server.wm.flicker.annotation.Group4
+import com.android.server.wm.flicker.dsl.FlickerBuilder
+import org.junit.runner.RunWith
+import org.junit.runners.Parameterized
+
+/**
+ * Test launching a new activity from bubble.
+ *
+ * To run this test: `atest WMShellFlickerTests:ExpandBubbleScreen`
+ *
+ * Actions:
+ * Launch an app and enable app's bubble notification
+ * Send a bubble notification
+ * The activity for the bubble is launched
+ */
+@RequiresDevice
+@RunWith(Parameterized::class)
+@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
+@Group4
+class ExpandBubbleScreen(testSpec: FlickerTestParameter) : BaseBubbleScreen(testSpec) {
+
+ override val transition: FlickerBuilder.(Map<String, Any?>) -> Unit
+ get() = buildTransition() {
+ setup {
+ test {
+ addBubbleBtn?.run { addBubbleBtn.click() } ?: error("Bubble widget not found")
+ }
+ }
+ transitions {
+ val showBubble = device.wait(Until.findObject(
+ By.res("com.android.systemui", "bubble_view")), FIND_OBJECT_TIMEOUT)
+ showBubble?.run { showBubble.click() } ?: error("Bubble notify not found")
+ device.pressBack()
+ }
+ }
+}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/LaunchBubbleScreen.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/LaunchBubbleScreen.kt
new file mode 100644
index 0000000..47e8c0c
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/LaunchBubbleScreen.kt
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell.flicker.bubble
+
+import androidx.test.filters.RequiresDevice
+import com.android.server.wm.flicker.FlickerParametersRunnerFactory
+import com.android.server.wm.flicker.FlickerTestParameter
+import com.android.server.wm.flicker.annotation.Group4
+import com.android.server.wm.flicker.dsl.FlickerBuilder
+import org.junit.runner.RunWith
+import org.junit.runners.Parameterized
+
+/**
+ * Test creating a bubble notification
+ *
+ * To run this test: `atest WMShellFlickerTests:LaunchBubbleScreen`
+ *
+ * Actions:
+ * Launch an app and enable app's bubble notification
+ * Send a bubble notification
+ */
+@RequiresDevice
+@RunWith(Parameterized::class)
+@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
+@Group4
+class LaunchBubbleScreen(testSpec: FlickerTestParameter) : BaseBubbleScreen(testSpec) {
+
+ override val transition: FlickerBuilder.(Map<String, Any?>) -> Unit
+ get() = buildTransition() {
+ transitions {
+ addBubbleBtn?.run { addBubbleBtn.click() } ?: error("Bubble widget not found")
+ }
+ }
+}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/MultiBubblesScreen.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/MultiBubblesScreen.kt
new file mode 100644
index 0000000..194e28f
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/MultiBubblesScreen.kt
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell.flicker.bubble
+
+import android.os.SystemClock
+import androidx.test.filters.RequiresDevice
+import androidx.test.uiautomator.By
+import androidx.test.uiautomator.Until
+import com.android.server.wm.flicker.FlickerParametersRunnerFactory
+import com.android.server.wm.flicker.FlickerTestParameter
+import com.android.server.wm.flicker.annotation.Group4
+import com.android.server.wm.flicker.dsl.FlickerBuilder
+import org.junit.runner.RunWith
+import org.junit.runners.Parameterized
+
+/**
+ * Test launching a new activity from bubble.
+ *
+ * To run this test: `atest WMShellFlickerTests:MultiBubblesScreen`
+ *
+ * Actions:
+ * Switch in different bubble notifications
+ */
+@RequiresDevice
+@RunWith(Parameterized::class)
+@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
+@Group4
+class MultiBubblesScreen(testSpec: FlickerTestParameter) : BaseBubbleScreen(testSpec) {
+
+ override val transition: FlickerBuilder.(Map<String, Any?>) -> Unit
+ get() = buildTransition() {
+ setup {
+ test {
+ for (i in 1..3) {
+ addBubbleBtn?.run { addBubbleBtn.click() } ?: error("Add Bubble not found")
+ }
+ val showBubble = device.wait(Until.findObject(
+ By.res(SYSTEM_UI_PACKAGE, BUBBLE_RES_NAME)), FIND_OBJECT_TIMEOUT)
+ showBubble?.run { showBubble.click() } ?: error("Show bubble not found")
+ SystemClock.sleep(1000)
+ }
+ }
+ transitions {
+ val bubbles = device.wait(Until.findObjects(
+ By.res(SYSTEM_UI_PACKAGE, BUBBLE_RES_NAME)), FIND_OBJECT_TIMEOUT)
+ for (entry in bubbles) {
+ entry?.run { entry.click() } ?: error("Bubble not found")
+ SystemClock.sleep(1000)
+ }
+ }
+ }
+}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/AppPairsHelper.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/AppPairsHelper.kt
index 5a438af..623055f6 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/AppPairsHelper.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/AppPairsHelper.kt
@@ -17,15 +17,15 @@
package com.android.wm.shell.flicker.helpers
import android.app.Instrumentation
-import android.content.ComponentName
import android.graphics.Region
import com.android.server.wm.flicker.Flicker
import com.android.server.wm.flicker.helpers.WindowUtils
+import com.android.server.wm.traces.common.FlickerComponentName
class AppPairsHelper(
instrumentation: Instrumentation,
activityLabel: String,
- component: ComponentName
+ component: FlickerComponentName
) : BaseAppHelper(instrumentation, activityLabel, component) {
fun getPrimaryBounds(dividerBounds: Region): android.graphics.Region {
val primaryAppBounds = Region(0, 0, dividerBounds.bounds.right,
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/BaseAppHelper.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/BaseAppHelper.kt
index f15044e..57bc0d5 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/BaseAppHelper.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/BaseAppHelper.kt
@@ -17,7 +17,6 @@
package com.android.wm.shell.flicker.helpers
import android.app.Instrumentation
-import android.content.ComponentName
import android.content.pm.PackageManager.FEATURE_LEANBACK
import android.content.pm.PackageManager.FEATURE_LEANBACK_ONLY
import android.os.SystemProperties
@@ -28,13 +27,13 @@
import androidx.test.uiautomator.Until
import com.android.compatibility.common.util.SystemUtil
import com.android.server.wm.flicker.helpers.StandardAppHelper
-import com.android.server.wm.traces.parser.toWindowName
+import com.android.server.wm.traces.common.FlickerComponentName
import java.io.IOException
abstract class BaseAppHelper(
instrumentation: Instrumentation,
launcherName: String,
- component: ComponentName
+ component: FlickerComponentName
) : StandardAppHelper(
instrumentation,
launcherName,
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/FixedAppHelper.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/FixedAppHelper.kt
index b4ae187..471e010 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/FixedAppHelper.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/FixedAppHelper.kt
@@ -17,10 +17,11 @@
package com.android.wm.shell.flicker.helpers
import android.app.Instrumentation
+import com.android.server.wm.traces.parser.toFlickerComponent
import com.android.wm.shell.flicker.testapp.Components
class FixedAppHelper(instrumentation: Instrumentation) : BaseAppHelper(
instrumentation,
Components.FixedActivity.LABEL,
- Components.FixedActivity.COMPONENT
+ Components.FixedActivity.COMPONENT.toFlickerComponent()
)
\ No newline at end of file
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/ImeAppHelper.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/ImeAppHelper.kt
index 086e8b7..0f00ede 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/ImeAppHelper.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/ImeAppHelper.kt
@@ -21,13 +21,14 @@
import androidx.test.uiautomator.UiDevice
import androidx.test.uiautomator.Until
import com.android.server.wm.flicker.helpers.FIND_TIMEOUT
+import com.android.server.wm.traces.parser.toFlickerComponent
import com.android.server.wm.traces.parser.windowmanager.WindowManagerStateHelper
import com.android.wm.shell.flicker.testapp.Components
open class ImeAppHelper(instrumentation: Instrumentation) : BaseAppHelper(
instrumentation,
Components.ImeActivity.LABEL,
- Components.ImeActivity.COMPONENT
+ Components.ImeActivity.COMPONENT.toFlickerComponent()
) {
/**
* Opens the IME and wait for it to be displayed
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/LaunchBubbleHelper.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/LaunchBubbleHelper.kt
new file mode 100644
index 0000000..6695c17
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/LaunchBubbleHelper.kt
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell.flicker.helpers
+
+import android.app.Instrumentation
+import com.android.server.wm.traces.parser.toFlickerComponent
+import com.android.wm.shell.flicker.testapp.Components
+
+class LaunchBubbleHelper(instrumentation: Instrumentation) : BaseAppHelper(
+ instrumentation,
+ Components.LaunchBubbleActivity.LABEL,
+ Components.LaunchBubbleActivity.COMPONENT.toFlickerComponent()
+) {
+
+ companion object {
+ const val TEST_REPETITIONS = 1
+ const val TIMEOUT_MS = 3_000L
+ }
+}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/MultiWindowHelper.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/MultiWindowHelper.kt
index 7f99e62..12ccbaf 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/MultiWindowHelper.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/MultiWindowHelper.kt
@@ -17,14 +17,14 @@
package com.android.wm.shell.flicker.helpers
import android.app.Instrumentation
-import android.content.ComponentName
import android.content.Context
import android.provider.Settings
+import com.android.server.wm.traces.common.FlickerComponentName
class MultiWindowHelper(
instrumentation: Instrumentation,
activityLabel: String,
- componentsInfo: ComponentName
+ componentsInfo: FlickerComponentName
) : BaseAppHelper(instrumentation, activityLabel, componentsInfo) {
companion object {
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/PipAppHelper.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/PipAppHelper.kt
index 1529f5b..2357b0d 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/PipAppHelper.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/PipAppHelper.kt
@@ -26,6 +26,7 @@
import androidx.test.uiautomator.Until
import com.android.server.wm.flicker.helpers.FIND_TIMEOUT
import com.android.server.wm.flicker.helpers.SYSTEMUI_PACKAGE
+import com.android.server.wm.traces.parser.toFlickerComponent
import com.android.server.wm.traces.parser.windowmanager.WindowManagerStateHelper
import com.android.wm.shell.flicker.pip.tv.closeTvPipWindow
import com.android.wm.shell.flicker.pip.tv.isFocusedOrHasFocusedChild
@@ -34,7 +35,7 @@
class PipAppHelper(instrumentation: Instrumentation) : BaseAppHelper(
instrumentation,
Components.PipActivity.LABEL,
- Components.PipActivity.COMPONENT
+ Components.PipActivity.COMPONENT.toFlickerComponent()
) {
private val mediaSessionManager: MediaSessionManager
get() = context.getSystemService(MediaSessionManager::class.java)
@@ -129,7 +130,7 @@
}
/**
- * Expands the pip window and dismisses it by clicking on the X button.
+ * Taps the pip window and dismisses it by clicking on the X button.
*/
fun closePipWindow(wmHelper: WindowManagerStateHelper) {
if (isTelevision) {
@@ -137,9 +138,12 @@
} else {
val windowRect = getWindowRect(wmHelper)
uiDevice.click(windowRect.centerX(), windowRect.centerY())
- val exitPipObject = uiDevice.findObject(By.res(SYSTEMUI_PACKAGE, "dismiss"))
+ // search and interact with the dismiss button
+ val dismissSelector = By.res(SYSTEMUI_PACKAGE, "dismiss")
+ uiDevice.wait(Until.hasObject(dismissSelector), FIND_TIMEOUT)
+ val dismissPipObject = uiDevice.findObject(dismissSelector)
?: error("PIP window dismiss button not found")
- val dismissButtonBounds = exitPipObject.visibleBounds
+ val dismissButtonBounds = dismissPipObject.visibleBounds
uiDevice.click(dismissButtonBounds.centerX(), dismissButtonBounds.centerY())
}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/SimpleAppHelper.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/SimpleAppHelper.kt
index ba13e38..4d0fbc4 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/SimpleAppHelper.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/SimpleAppHelper.kt
@@ -17,10 +17,11 @@
package com.android.wm.shell.flicker.helpers
import android.app.Instrumentation
+import com.android.server.wm.traces.parser.toFlickerComponent
import com.android.wm.shell.flicker.testapp.Components
class SimpleAppHelper(instrumentation: Instrumentation) : BaseAppHelper(
instrumentation,
Components.SimpleActivity.LABEL,
- Components.SimpleActivity.COMPONENT
+ Components.SimpleActivity.COMPONENT.toFlickerComponent()
)
\ No newline at end of file
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/SplitScreenHelper.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/SplitScreenHelper.kt
index 2d996ca..0ec9b2d 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/SplitScreenHelper.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/SplitScreenHelper.kt
@@ -17,14 +17,15 @@
package com.android.wm.shell.flicker.helpers
import android.app.Instrumentation
-import android.content.ComponentName
import android.content.res.Resources
+import com.android.server.wm.traces.common.FlickerComponentName
+import com.android.server.wm.traces.parser.toFlickerComponent
import com.android.wm.shell.flicker.testapp.Components
class SplitScreenHelper(
instrumentation: Instrumentation,
activityLabel: String,
- componentsInfo: ComponentName
+ componentsInfo: FlickerComponentName
) : BaseAppHelper(instrumentation, activityLabel, componentsInfo) {
companion object {
@@ -39,16 +40,16 @@
fun getPrimary(instrumentation: Instrumentation): SplitScreenHelper =
SplitScreenHelper(instrumentation,
Components.SplitScreenActivity.LABEL,
- Components.SplitScreenActivity.COMPONENT)
+ Components.SplitScreenActivity.COMPONENT.toFlickerComponent())
fun getSecondary(instrumentation: Instrumentation): SplitScreenHelper =
SplitScreenHelper(instrumentation,
Components.SplitScreenSecondaryActivity.LABEL,
- Components.SplitScreenSecondaryActivity.COMPONENT)
+ Components.SplitScreenSecondaryActivity.COMPONENT.toFlickerComponent())
fun getNonResizeable(instrumentation: Instrumentation): SplitScreenHelper =
SplitScreenHelper(instrumentation,
Components.NonResizeableActivity.LABEL,
- Components.NonResizeableActivity.COMPONENT)
+ Components.NonResizeableActivity.COMPONENT.toFlickerComponent())
}
}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/EnterSplitScreenDockActivity.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/EnterSplitScreenDockActivity.kt
index 508e939..bd44d08 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/EnterSplitScreenDockActivity.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/EnterSplitScreenDockActivity.kt
@@ -16,7 +16,6 @@
package com.android.wm.shell.flicker.legacysplitscreen
-import android.content.ComponentName
import android.platform.test.annotations.Presubmit
import android.view.Surface
import android.view.WindowManagerPolicyConstants
@@ -25,13 +24,13 @@
import com.android.server.wm.flicker.FlickerParametersRunnerFactory
import com.android.server.wm.flicker.FlickerTestParameter
import com.android.server.wm.flicker.FlickerTestParameterFactory
-import com.android.server.wm.flicker.annotation.Group1
+import com.android.server.wm.flicker.annotation.Group4
import com.android.server.wm.flicker.dsl.FlickerBuilder
import com.android.server.wm.flicker.helpers.launchSplitScreen
import com.android.server.wm.flicker.navBarWindowIsVisible
import com.android.server.wm.flicker.startRotation
import com.android.server.wm.flicker.statusBarWindowIsVisible
-import com.android.server.wm.traces.parser.windowmanager.WindowManagerStateHelper
+import com.android.server.wm.traces.common.FlickerComponentName
import com.android.wm.shell.flicker.dockedStackDividerBecomesVisible
import com.android.wm.shell.flicker.dockedStackPrimaryBoundsIsVisibleAtEnd
import com.android.wm.shell.flicker.helpers.SplitScreenHelper
@@ -50,7 +49,7 @@
@RunWith(Parameterized::class)
@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-@Group1
+@Group4
class EnterSplitScreenDockActivity(
testSpec: FlickerTestParameter
) : LegacySplitScreenTransition(testSpec) {
@@ -62,10 +61,10 @@
}
}
- override val ignoredWindows: List<ComponentName>
+ override val ignoredWindows: List<FlickerComponentName>
get() = listOf(LAUNCHER_COMPONENT, LIVE_WALLPAPER_COMPONENT,
- splitScreenApp.component, WindowManagerStateHelper.SPLASH_SCREEN_COMPONENT,
- WindowManagerStateHelper.SNAPSHOT_COMPONENT, LAUNCHER_COMPONENT)
+ splitScreenApp.component, FlickerComponentName.SPLASH_SCREEN,
+ FlickerComponentName.SNAPSHOT, LAUNCHER_COMPONENT)
@Presubmit
@Test
@@ -89,7 +88,7 @@
@Test
fun appWindowIsVisible() {
testSpec.assertWmEnd {
- isVisible(splitScreenApp.component)
+ isAppWindowVisible(splitScreenApp.component)
}
}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/EnterSplitScreenFromDetachedRecentTask.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/EnterSplitScreenFromDetachedRecentTask.kt
index 12f3909..625d48b 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/EnterSplitScreenFromDetachedRecentTask.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/EnterSplitScreenFromDetachedRecentTask.kt
@@ -16,16 +16,16 @@
package com.android.wm.shell.flicker.legacysplitscreen
-import android.content.ComponentName
import android.platform.test.annotations.Presubmit
import android.view.Surface
import androidx.test.filters.RequiresDevice
import com.android.server.wm.flicker.FlickerParametersRunnerFactory
import com.android.server.wm.flicker.FlickerTestParameter
import com.android.server.wm.flicker.FlickerTestParameterFactory
+import com.android.server.wm.flicker.annotation.Group4
import com.android.server.wm.flicker.dsl.FlickerBuilder
import com.android.server.wm.flicker.helpers.launchSplitScreen
-import com.android.server.wm.traces.parser.windowmanager.WindowManagerStateHelper
+import com.android.server.wm.traces.common.FlickerComponentName
import com.android.wm.shell.flicker.dockedStackDividerIsVisibleAtEnd
import com.android.wm.shell.flicker.helpers.SplitScreenHelper
import org.junit.FixMethodOrder
@@ -43,6 +43,7 @@
@RunWith(Parameterized::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
+@Group4
class EnterSplitScreenFromDetachedRecentTask(
testSpec: FlickerTestParameter
) : LegacySplitScreenTransition(testSpec) {
@@ -62,10 +63,10 @@
}
}
- override val ignoredWindows: List<ComponentName>
+ override val ignoredWindows: List<FlickerComponentName>
get() = listOf(LAUNCHER_COMPONENT,
- WindowManagerStateHelper.SPLASH_SCREEN_COMPONENT,
- WindowManagerStateHelper.SNAPSHOT_COMPONENT,
+ FlickerComponentName.SPLASH_SCREEN,
+ FlickerComponentName.SNAPSHOT,
splitScreenApp.component)
@Presubmit
@@ -76,7 +77,7 @@
@Test
fun appWindowIsVisible() {
testSpec.assertWmEnd {
- isVisible(splitScreenApp.component)
+ isAppWindowVisible(splitScreenApp.component)
}
}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/EnterSplitScreenLaunchToSide.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/EnterSplitScreenLaunchToSide.kt
index ac85c48..2ed2806 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/EnterSplitScreenLaunchToSide.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/EnterSplitScreenLaunchToSide.kt
@@ -16,21 +16,20 @@
package com.android.wm.shell.flicker.legacysplitscreen
-import android.content.ComponentName
import android.platform.test.annotations.Presubmit
import android.view.Surface
import androidx.test.filters.RequiresDevice
import com.android.server.wm.flicker.FlickerParametersRunnerFactory
import com.android.server.wm.flicker.FlickerTestParameter
import com.android.server.wm.flicker.FlickerTestParameterFactory
-import com.android.server.wm.flicker.annotation.Group1
+import com.android.server.wm.flicker.annotation.Group4
import com.android.server.wm.flicker.dsl.FlickerBuilder
import com.android.server.wm.flicker.helpers.launchSplitScreen
import com.android.server.wm.flicker.helpers.reopenAppFromOverview
import com.android.server.wm.flicker.navBarWindowIsVisible
import com.android.server.wm.flicker.startRotation
import com.android.server.wm.flicker.statusBarWindowIsVisible
-import com.android.server.wm.traces.parser.windowmanager.WindowManagerStateHelper
+import com.android.server.wm.traces.common.FlickerComponentName
import com.android.wm.shell.flicker.dockedStackDividerBecomesVisible
import com.android.wm.shell.flicker.dockedStackPrimaryBoundsIsVisibleAtEnd
import com.android.wm.shell.flicker.dockedStackSecondaryBoundsIsVisibleAtEnd
@@ -49,7 +48,7 @@
@RunWith(Parameterized::class)
@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-@Group1
+@Group4
class EnterSplitScreenLaunchToSide(
testSpec: FlickerTestParameter
) : LegacySplitScreenTransition(testSpec) {
@@ -62,10 +61,10 @@
}
}
- override val ignoredWindows: List<ComponentName>
+ override val ignoredWindows: List<FlickerComponentName>
get() = listOf(LAUNCHER_COMPONENT, splitScreenApp.component,
- secondaryApp.component, WindowManagerStateHelper.SPLASH_SCREEN_COMPONENT,
- WindowManagerStateHelper.SNAPSHOT_COMPONENT)
+ secondaryApp.component, FlickerComponentName.SPLASH_SCREEN,
+ FlickerComponentName.SNAPSHOT)
@Presubmit
@Test
@@ -92,7 +91,7 @@
// Because we log WM once per frame, sometimes the activity and the window
// become visible in the same entry, sometimes not, thus it is not possible to
// assert the visibility of the activity here
- this.isAppWindowInvisible(secondaryApp.component, ignoreActivity = true)
+ this.isAppWindowInvisible(secondaryApp.component)
.then()
// during re-parenting, the window may disappear and reappear from the
// trace, this occurs because we log only 1x per frame
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/EnterSplitScreenNotSupportNonResizable.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/EnterSplitScreenNotSupportNonResizable.kt
index 964af23..ee6cf34 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/EnterSplitScreenNotSupportNonResizable.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/EnterSplitScreenNotSupportNonResizable.kt
@@ -16,17 +16,16 @@
package com.android.wm.shell.flicker.legacysplitscreen
-import android.content.ComponentName
import android.platform.test.annotations.Presubmit
import android.view.Surface
import androidx.test.filters.RequiresDevice
import com.android.server.wm.flicker.FlickerParametersRunnerFactory
import com.android.server.wm.flicker.FlickerTestParameter
import com.android.server.wm.flicker.FlickerTestParameterFactory
-import com.android.server.wm.flicker.annotation.Group1
+import com.android.server.wm.flicker.annotation.Group4
import com.android.server.wm.flicker.dsl.FlickerBuilder
import com.android.server.wm.flicker.helpers.canSplitScreen
-import com.android.server.wm.traces.parser.windowmanager.WindowManagerStateHelper
+import com.android.server.wm.traces.common.FlickerComponentName
import com.android.wm.shell.flicker.dockedStackDividerNotExistsAtEnd
import com.android.wm.shell.flicker.helpers.MultiWindowHelper.Companion.resetMultiWindowConfig
import com.android.wm.shell.flicker.helpers.MultiWindowHelper.Companion.setSupportsNonResizableMultiWindow
@@ -51,7 +50,7 @@
@RunWith(Parameterized::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
-@Group1
+@Group4
class EnterSplitScreenNotSupportNonResizable(
testSpec: FlickerTestParameter
) : LegacySplitScreenTransition(testSpec) {
@@ -71,10 +70,10 @@
}
}
- override val ignoredWindows: List<ComponentName>
+ override val ignoredWindows: List<FlickerComponentName>
get() = listOf(LAUNCHER_COMPONENT,
- WindowManagerStateHelper.SPLASH_SCREEN_COMPONENT,
- WindowManagerStateHelper.SNAPSHOT_COMPONENT,
+ FlickerComponentName.SPLASH_SCREEN,
+ FlickerComponentName.SNAPSHOT,
nonResizeableApp.component,
splitScreenApp.component)
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/EnterSplitScreenSupportNonResizable.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/EnterSplitScreenSupportNonResizable.kt
index 1b8afa6..163b6ffda 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/EnterSplitScreenSupportNonResizable.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/EnterSplitScreenSupportNonResizable.kt
@@ -16,7 +16,6 @@
package com.android.wm.shell.flicker.legacysplitscreen
-import android.content.ComponentName
import android.platform.test.annotations.Presubmit
import android.view.Surface
import androidx.test.filters.RequiresDevice
@@ -26,7 +25,7 @@
import com.android.server.wm.flicker.annotation.Group2
import com.android.server.wm.flicker.dsl.FlickerBuilder
import com.android.server.wm.flicker.helpers.launchSplitScreen
-import com.android.server.wm.traces.parser.windowmanager.WindowManagerStateHelper
+import com.android.server.wm.traces.common.FlickerComponentName
import com.android.wm.shell.flicker.dockedStackDividerIsVisibleAtEnd
import com.android.wm.shell.flicker.helpers.MultiWindowHelper.Companion.resetMultiWindowConfig
import com.android.wm.shell.flicker.helpers.MultiWindowHelper.Companion.setSupportsNonResizableMultiWindow
@@ -68,12 +67,12 @@
}
}
- override val ignoredWindows: List<ComponentName>
+ override val ignoredWindows: List<FlickerComponentName>
get() = listOf(LAUNCHER_COMPONENT,
- WindowManagerStateHelper.SPLASH_SCREEN_COMPONENT,
- WindowManagerStateHelper.SNAPSHOT_COMPONENT,
- nonResizeableApp.component,
- splitScreenApp.component)
+ FlickerComponentName.SPLASH_SCREEN,
+ FlickerComponentName.SNAPSHOT,
+ nonResizeableApp.component,
+ splitScreenApp.component)
@Before
override fun setup() {
@@ -95,7 +94,7 @@
@Test
fun appWindowIsVisible() {
testSpec.assertWmEnd {
- isVisible(nonResizeableApp.component)
+ isAppWindowVisible(nonResizeableApp.component)
}
}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/ExitLegacySplitScreenFromBottom.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/ExitLegacySplitScreenFromBottom.kt
index 247965f..2b629b0 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/ExitLegacySplitScreenFromBottom.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/ExitLegacySplitScreenFromBottom.kt
@@ -16,7 +16,6 @@
package com.android.wm.shell.flicker.legacysplitscreen
-import android.content.ComponentName
import android.platform.test.annotations.Postsubmit
import android.view.Surface
import androidx.test.filters.FlakyTest
@@ -30,7 +29,7 @@
import com.android.server.wm.flicker.helpers.launchSplitScreen
import com.android.server.wm.flicker.navBarWindowIsVisible
import com.android.server.wm.flicker.statusBarWindowIsVisible
-import com.android.server.wm.traces.parser.windowmanager.WindowManagerStateHelper
+import com.android.server.wm.traces.common.FlickerComponentName
import com.android.wm.shell.flicker.DOCKED_STACK_DIVIDER_COMPONENT
import com.android.wm.shell.flicker.helpers.SplitScreenHelper
import org.junit.FixMethodOrder
@@ -70,10 +69,10 @@
}
}
- override val ignoredWindows: List<ComponentName>
- get() = listOf(LAUNCHER_COMPONENT, WindowManagerStateHelper.SPLASH_SCREEN_COMPONENT,
+ override val ignoredWindows: List<FlickerComponentName>
+ get() = listOf(LAUNCHER_COMPONENT, FlickerComponentName.SPLASH_SCREEN,
splitScreenApp.component, secondaryApp.component,
- WindowManagerStateHelper.SNAPSHOT_COMPONENT)
+ FlickerComponentName.SNAPSHOT)
@Postsubmit
@Test
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/ExitPrimarySplitScreenShowSecondaryFullscreen.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/ExitPrimarySplitScreenShowSecondaryFullscreen.kt
index ff34364..95fe3be 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/ExitPrimarySplitScreenShowSecondaryFullscreen.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/ExitPrimarySplitScreenShowSecondaryFullscreen.kt
@@ -16,7 +16,6 @@
package com.android.wm.shell.flicker.legacysplitscreen
-import android.content.ComponentName
import android.platform.test.annotations.Presubmit
import android.view.Surface
import androidx.test.filters.FlakyTest
@@ -30,7 +29,7 @@
import com.android.server.wm.flicker.helpers.reopenAppFromOverview
import com.android.server.wm.flicker.navBarWindowIsVisible
import com.android.server.wm.flicker.statusBarWindowIsVisible
-import com.android.server.wm.traces.parser.windowmanager.WindowManagerStateHelper
+import com.android.server.wm.traces.common.FlickerComponentName
import com.android.wm.shell.flicker.dockedStackDividerNotExistsAtEnd
import com.android.wm.shell.flicker.helpers.SplitScreenHelper
import org.junit.FixMethodOrder
@@ -70,10 +69,10 @@
}
}
- override val ignoredWindows: List<ComponentName>
- get() = listOf(LAUNCHER_COMPONENT, WindowManagerStateHelper.SPLASH_SCREEN_COMPONENT,
+ override val ignoredWindows: List<FlickerComponentName>
+ get() = listOf(LAUNCHER_COMPONENT, FlickerComponentName.SPLASH_SCREEN,
splitScreenApp.component, secondaryApp.component,
- WindowManagerStateHelper.SNAPSHOT_COMPONENT)
+ FlickerComponentName.SNAPSHOT)
@Presubmit
@Test
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/LegacySplitScreenFromIntentNotSupportNonResizable.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/LegacySplitScreenFromIntentNotSupportNonResizable.kt
index 95e4085..f7d628d 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/LegacySplitScreenFromIntentNotSupportNonResizable.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/LegacySplitScreenFromIntentNotSupportNonResizable.kt
@@ -16,7 +16,6 @@
package com.android.wm.shell.flicker.legacysplitscreen
-import android.content.ComponentName
import android.platform.test.annotations.Presubmit
import android.view.Surface
import androidx.test.filters.RequiresDevice
@@ -26,7 +25,7 @@
import com.android.server.wm.flicker.annotation.Group2
import com.android.server.wm.flicker.dsl.FlickerBuilder
import com.android.server.wm.flicker.helpers.launchSplitScreen
-import com.android.server.wm.traces.parser.windowmanager.WindowManagerStateHelper
+import com.android.server.wm.traces.common.FlickerComponentName
import com.android.wm.shell.flicker.DOCKED_STACK_DIVIDER_COMPONENT
import com.android.wm.shell.flicker.dockedStackDividerNotExistsAtEnd
import com.android.wm.shell.flicker.helpers.MultiWindowHelper.Companion.resetMultiWindowConfig
@@ -69,11 +68,11 @@
}
}
- override val ignoredWindows: List<ComponentName>
+ override val ignoredWindows: List<FlickerComponentName>
get() = listOf(DOCKED_STACK_DIVIDER_COMPONENT, LAUNCHER_COMPONENT, LETTERBOX_COMPONENT,
- nonResizeableApp.component, splitScreenApp.component,
- WindowManagerStateHelper.SPLASH_SCREEN_COMPONENT,
- WindowManagerStateHelper.SNAPSHOT_COMPONENT)
+ nonResizeableApp.component, splitScreenApp.component,
+ FlickerComponentName.SPLASH_SCREEN,
+ FlickerComponentName.SNAPSHOT)
@Before
override fun setup() {
@@ -120,12 +119,12 @@
// when the activity gets PAUSED the window may still be marked as visible
// it will be updated in the next log entry. This occurs because we record 1x
// per frame, thus ignore activity check here
- this.isAppWindowVisible(splitScreenApp.component, ignoreActivity = true)
+ this.isAppWindowVisible(splitScreenApp.component)
.then()
// immediately after the window (after onResume and before perform relayout)
// the activity is invisible. This may or not be logged, since we record 1x
// per frame, thus ignore activity check here
- .isAppWindowInvisible(splitScreenApp.component, ignoreActivity = true)
+ .isAppWindowInvisible(splitScreenApp.component)
}
}
@@ -142,13 +141,12 @@
.then()
// we log once per frame, upon logging, window may be visible or not depending
// on what was processed until that moment. Both behaviors are correct
- .isAppWindowInvisible(nonResizeableApp.component,
- ignoreActivity = true, isOptional = true)
+ .isAppWindowInvisible(nonResizeableApp.component, isOptional = true)
.then()
// immediately after the window (after onResume and before perform relayout)
// the activity is invisible. This may or not be logged, since we record 1x
// per frame, thus ignore activity check here
- .isAppWindowVisible(nonResizeableApp.component, ignoreActivity = true)
+ .isAppWindowVisible(nonResizeableApp.component)
}
}
@@ -159,7 +157,7 @@
@Test
fun nonResizableAppWindowBecomesVisibleAtEnd() {
testSpec.assertWmEnd {
- this.isVisible(nonResizeableApp.component)
+ isAppWindowVisible(nonResizeableApp.component)
}
}
@@ -171,8 +169,8 @@
@Test
fun onlyNonResizableAppWindowIsVisibleAtEnd() {
testSpec.assertWmEnd {
- isInvisible(splitScreenApp.component)
- isVisible(nonResizeableApp.component)
+ isAppWindowInvisible(splitScreenApp.component)
+ isAppWindowVisible(nonResizeableApp.component)
}
}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/LegacySplitScreenFromIntentSupportNonResizable.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/LegacySplitScreenFromIntentSupportNonResizable.kt
index 65346aa..a5c6571 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/LegacySplitScreenFromIntentSupportNonResizable.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/LegacySplitScreenFromIntentSupportNonResizable.kt
@@ -16,7 +16,6 @@
package com.android.wm.shell.flicker.legacysplitscreen
-import android.content.ComponentName
import android.platform.test.annotations.Presubmit
import android.view.Surface
import androidx.test.filters.RequiresDevice
@@ -26,7 +25,7 @@
import com.android.server.wm.flicker.annotation.Group2
import com.android.server.wm.flicker.dsl.FlickerBuilder
import com.android.server.wm.flicker.helpers.launchSplitScreen
-import com.android.server.wm.traces.parser.windowmanager.WindowManagerStateHelper
+import com.android.server.wm.traces.common.FlickerComponentName
import com.android.wm.shell.flicker.DOCKED_STACK_DIVIDER_COMPONENT
import com.android.wm.shell.flicker.dockedStackDividerIsVisibleAtEnd
import com.android.wm.shell.flicker.helpers.MultiWindowHelper.Companion.resetMultiWindowConfig
@@ -69,11 +68,11 @@
}
}
- override val ignoredWindows: List<ComponentName>
+ override val ignoredWindows: List<FlickerComponentName>
get() = listOf(DOCKED_STACK_DIVIDER_COMPONENT, LAUNCHER_COMPONENT, LETTERBOX_COMPONENT,
nonResizeableApp.component, splitScreenApp.component,
- WindowManagerStateHelper.SPLASH_SCREEN_COMPONENT,
- WindowManagerStateHelper.SNAPSHOT_COMPONENT)
+ FlickerComponentName.SPLASH_SCREEN,
+ FlickerComponentName.SNAPSHOT)
@Before
override fun setup() {
@@ -110,13 +109,12 @@
.then()
// we log once per frame, upon logging, window may be visible or not depending
// on what was processed until that moment. Both behaviors are correct
- .isAppWindowInvisible(nonResizeableApp.component,
- ignoreActivity = true, isOptional = true)
+ .isAppWindowInvisible(nonResizeableApp.component, isOptional = true)
.then()
// immediately after the window (after onResume and before perform relayout)
// the activity is invisible. This may or not be logged, since we record 1x
// per frame, thus ignore activity check here
- .isAppWindowVisible(nonResizeableApp.component, ignoreActivity = true)
+ .isAppWindowVisible(nonResizeableApp.component)
}
}
@@ -128,8 +126,8 @@
@Test
fun bothAppsWindowsAreVisibleAtEnd() {
testSpec.assertWmEnd {
- isVisible(splitScreenApp.component)
- isVisible(nonResizeableApp.component)
+ isAppWindowVisible(splitScreenApp.component)
+ isAppWindowVisible(nonResizeableApp.component)
}
}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/LegacySplitScreenFromRecentNotSupportNonResizable.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/LegacySplitScreenFromRecentNotSupportNonResizable.kt
index 547341a..6f486b0 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/LegacySplitScreenFromRecentNotSupportNonResizable.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/LegacySplitScreenFromRecentNotSupportNonResizable.kt
@@ -16,7 +16,6 @@
package com.android.wm.shell.flicker.legacysplitscreen
-import android.content.ComponentName
import android.platform.test.annotations.Postsubmit
import android.platform.test.annotations.Presubmit
import android.view.Surface
@@ -28,7 +27,7 @@
import com.android.server.wm.flicker.dsl.FlickerBuilder
import com.android.server.wm.flicker.helpers.launchSplitScreen
import com.android.server.wm.flicker.helpers.reopenAppFromOverview
-import com.android.server.wm.traces.parser.windowmanager.WindowManagerStateHelper
+import com.android.server.wm.traces.common.FlickerComponentName
import com.android.wm.shell.flicker.DOCKED_STACK_DIVIDER_COMPONENT
import com.android.wm.shell.flicker.dockedStackDividerNotExistsAtEnd
import com.android.wm.shell.flicker.helpers.MultiWindowHelper.Companion.resetMultiWindowConfig
@@ -71,11 +70,11 @@
}
}
- override val ignoredWindows: List<ComponentName>
+ override val ignoredWindows: List<FlickerComponentName>
get() = listOf(DOCKED_STACK_DIVIDER_COMPONENT, LAUNCHER_COMPONENT, LETTERBOX_COMPONENT,
- TOAST_COMPONENT, splitScreenApp.component, nonResizeableApp.component,
- WindowManagerStateHelper.SPLASH_SCREEN_COMPONENT,
- WindowManagerStateHelper.SNAPSHOT_COMPONENT)
+ TOAST_COMPONENT, splitScreenApp.component, nonResizeableApp.component,
+ FlickerComponentName.SPLASH_SCREEN,
+ FlickerComponentName.SNAPSHOT)
@Before
override fun setup() {
@@ -116,12 +115,12 @@
// when the activity gets PAUSED the window may still be marked as visible
// it will be updated in the next log entry. This occurs because we record 1x
// per frame, thus ignore activity check here
- this.isAppWindowVisible(splitScreenApp.component, ignoreActivity = true)
+ this.isAppWindowVisible(splitScreenApp.component)
.then()
// immediately after the window (after onResume and before perform relayout)
// the activity is invisible. This may or not be logged, since we record 1x
// per frame, thus ignore activity check here
- .isAppWindowInvisible(splitScreenApp.component, ignoreActivity = true)
+ .isAppWindowInvisible(splitScreenApp.component)
}
}
@@ -143,8 +142,8 @@
@Test
fun onlyNonResizableAppWindowIsVisibleAtEnd() {
testSpec.assertWmEnd {
- isInvisible(splitScreenApp.component)
- isVisible(nonResizeableApp.component)
+ isAppWindowInvisible(splitScreenApp.component)
+ isAppWindowVisible(nonResizeableApp.component)
}
}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/LegacySplitScreenFromRecentSupportNonResizable.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/LegacySplitScreenFromRecentSupportNonResizable.kt
index 3f86658..f03c927 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/LegacySplitScreenFromRecentSupportNonResizable.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/LegacySplitScreenFromRecentSupportNonResizable.kt
@@ -16,7 +16,6 @@
package com.android.wm.shell.flicker.legacysplitscreen
-import android.content.ComponentName
import android.platform.test.annotations.Presubmit
import android.view.Surface
import androidx.test.filters.RequiresDevice
@@ -27,7 +26,7 @@
import com.android.server.wm.flicker.dsl.FlickerBuilder
import com.android.server.wm.flicker.helpers.launchSplitScreen
import com.android.server.wm.flicker.helpers.reopenAppFromOverview
-import com.android.server.wm.traces.parser.windowmanager.WindowManagerStateHelper
+import com.android.server.wm.traces.common.FlickerComponentName
import com.android.wm.shell.flicker.DOCKED_STACK_DIVIDER_COMPONENT
import com.android.wm.shell.flicker.dockedStackDividerIsVisibleAtEnd
import com.android.wm.shell.flicker.helpers.MultiWindowHelper.Companion.resetMultiWindowConfig
@@ -70,11 +69,11 @@
}
}
- override val ignoredWindows: List<ComponentName>
+ override val ignoredWindows: List<FlickerComponentName>
get() = listOf(DOCKED_STACK_DIVIDER_COMPONENT, LAUNCHER_COMPONENT, LETTERBOX_COMPONENT,
- TOAST_COMPONENT, splitScreenApp.component, nonResizeableApp.component,
- WindowManagerStateHelper.SPLASH_SCREEN_COMPONENT,
- WindowManagerStateHelper.SNAPSHOT_COMPONENT)
+ TOAST_COMPONENT, splitScreenApp.component, nonResizeableApp.component,
+ FlickerComponentName.SPLASH_SCREEN,
+ FlickerComponentName.SNAPSHOT)
@Before
override fun setup() {
@@ -107,7 +106,7 @@
// Because we log WM once per frame, sometimes the activity and the window
// become visible in the same entry, sometimes not, thus it is not possible to
// assert the visibility of the activity here
- this.isAppWindowInvisible(nonResizeableApp.component, ignoreActivity = true)
+ this.isAppWindowInvisible(nonResizeableApp.component)
.then()
// during re-parenting, the window may disappear and reappear from the
// trace, this occurs because we log only 1x per frame
@@ -129,8 +128,8 @@
@Test
fun bothAppsWindowsAreVisibleAtEnd() {
testSpec.assertWmEnd {
- isVisible(splitScreenApp.component)
- isVisible(nonResizeableApp.component)
+ isAppWindowVisible(splitScreenApp.component)
+ isAppWindowVisible(nonResizeableApp.component)
}
}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/LegacySplitScreenToLauncher.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/LegacySplitScreenToLauncher.kt
index 5fb6f1f..2ccd03b 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/LegacySplitScreenToLauncher.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/LegacySplitScreenToLauncher.kt
@@ -16,7 +16,6 @@
package com.android.wm.shell.flicker.legacysplitscreen
-import android.content.ComponentName
import android.platform.test.annotations.Postsubmit
import android.platform.test.annotations.Presubmit
import android.view.Surface
@@ -39,7 +38,7 @@
import com.android.server.wm.flicker.statusBarLayerIsVisible
import com.android.server.wm.flicker.statusBarLayerRotatesScales
import com.android.server.wm.flicker.statusBarWindowIsVisible
-import com.android.server.wm.traces.parser.windowmanager.WindowManagerStateHelper
+import com.android.server.wm.traces.common.FlickerComponentName
import com.android.wm.shell.flicker.dockedStackDividerBecomesInvisible
import com.android.wm.shell.flicker.helpers.SimpleAppHelper
import org.junit.FixMethodOrder
@@ -86,9 +85,9 @@
}
}
- override val ignoredWindows: List<ComponentName>
- get() = listOf(LAUNCHER_COMPONENT, WindowManagerStateHelper.SPLASH_SCREEN_COMPONENT,
- WindowManagerStateHelper.SNAPSHOT_COMPONENT)
+ override val ignoredWindows: List<FlickerComponentName>
+ get() = listOf(LAUNCHER_COMPONENT, FlickerComponentName.SPLASH_SCREEN,
+ FlickerComponentName.SNAPSHOT)
@Presubmit
@Test
@@ -108,13 +107,11 @@
@Presubmit
@Test
- fun navBarLayerRotatesAndScales() =
- testSpec.navBarLayerRotatesAndScales(testSpec.config.endRotation)
+ fun navBarLayerRotatesAndScales() = testSpec.navBarLayerRotatesAndScales()
@Presubmit
@Test
- fun statusBarLayerRotatesScales() =
- testSpec.statusBarLayerRotatesScales(testSpec.config.endRotation)
+ fun statusBarLayerRotatesScales() = testSpec.statusBarLayerRotatesScales()
@Presubmit
@Test
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/LegacySplitScreenTransition.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/LegacySplitScreenTransition.kt
index 3117693..661c8b6 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/LegacySplitScreenTransition.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/LegacySplitScreenTransition.kt
@@ -17,7 +17,6 @@
package com.android.wm.shell.flicker.legacysplitscreen
import android.app.Instrumentation
-import android.content.ComponentName
import android.content.Context
import android.support.test.launcherhelper.LauncherStrategyFactory
import android.view.Surface
@@ -32,7 +31,7 @@
import com.android.server.wm.flicker.helpers.wakeUpAndGoToHomeScreen
import com.android.server.wm.flicker.repetitions
import com.android.server.wm.flicker.startRotation
-import com.android.server.wm.traces.parser.windowmanager.WindowManagerStateHelper
+import com.android.server.wm.traces.common.FlickerComponentName
import com.android.wm.shell.flicker.helpers.BaseAppHelper.Companion.isShellTransitionsEnabled
import com.android.wm.shell.flicker.helpers.MultiWindowHelper.Companion.getDevEnableNonResizableMultiWindow
import com.android.wm.shell.flicker.helpers.MultiWindowHelper.Companion.setDevEnableNonResizableMultiWindow
@@ -50,7 +49,7 @@
protected val splitScreenApp = SplitScreenHelper.getPrimary(instrumentation)
protected val secondaryApp = SplitScreenHelper.getSecondary(instrumentation)
protected val nonResizeableApp = SplitScreenHelper.getNonResizeable(instrumentation)
- protected val LAUNCHER_COMPONENT = ComponentName("",
+ protected val LAUNCHER_COMPONENT = FlickerComponentName("",
LauncherStrategyFactory.getInstance(instrumentation)
.launcherStrategy.supportedLauncherPackage)
private var prevDevEnableNonResizableMultiWindow = 0
@@ -79,9 +78,9 @@
*
* b/182720234
*/
- open val ignoredWindows: List<ComponentName> = listOf(
- WindowManagerStateHelper.SPLASH_SCREEN_COMPONENT,
- WindowManagerStateHelper.SNAPSHOT_COMPONENT)
+ open val ignoredWindows: List<FlickerComponentName> = listOf(
+ FlickerComponentName.SPLASH_SCREEN,
+ FlickerComponentName.SNAPSHOT)
protected open val transition: FlickerBuilder.(Map<String, Any?>) -> Unit
get() = { configuration ->
@@ -148,9 +147,9 @@
}
companion object {
- internal val LIVE_WALLPAPER_COMPONENT = ComponentName("",
+ internal val LIVE_WALLPAPER_COMPONENT = FlickerComponentName("",
"com.breel.wallpapers18.soundviz.wallpaper.variations.SoundVizWallpaperV2")
- internal val LETTERBOX_COMPONENT = ComponentName("", "Letterbox")
- internal val TOAST_COMPONENT = ComponentName("", "Toast")
+ internal val LETTERBOX_COMPONENT = FlickerComponentName("", "Letterbox")
+ internal val TOAST_COMPONENT = FlickerComponentName("", "Toast")
}
}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/OpenAppToLegacySplitScreen.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/OpenAppToLegacySplitScreen.kt
index a7ac6a7..34eff80 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/OpenAppToLegacySplitScreen.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/OpenAppToLegacySplitScreen.kt
@@ -16,7 +16,6 @@
package com.android.wm.shell.flicker.legacysplitscreen
-import android.content.ComponentName
import android.platform.test.annotations.Presubmit
import android.view.Surface
import androidx.test.filters.FlakyTest
@@ -29,7 +28,7 @@
import com.android.server.wm.flicker.entireScreenCovered
import com.android.server.wm.flicker.helpers.launchSplitScreen
import com.android.server.wm.flicker.statusBarLayerIsVisible
-import com.android.server.wm.traces.parser.windowmanager.WindowManagerStateHelper
+import com.android.server.wm.traces.common.FlickerComponentName
import com.android.wm.shell.flicker.appPairsDividerBecomesVisible
import com.android.wm.shell.flicker.helpers.SplitScreenHelper
import org.junit.FixMethodOrder
@@ -59,10 +58,10 @@
}
}
- override val ignoredWindows: List<ComponentName>
+ override val ignoredWindows: List<FlickerComponentName>
get() = listOf(LAUNCHER_COMPONENT, splitScreenApp.component,
- WindowManagerStateHelper.SPLASH_SCREEN_COMPONENT,
- WindowManagerStateHelper.SNAPSHOT_COMPONENT)
+ FlickerComponentName.SPLASH_SCREEN,
+ FlickerComponentName.SNAPSHOT)
@FlakyTest
@Test
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/ResizeLegacySplitScreen.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/ResizeLegacySplitScreen.kt
index cd15051..58e1def 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/ResizeLegacySplitScreen.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/ResizeLegacySplitScreen.kt
@@ -27,7 +27,6 @@
import com.android.server.wm.flicker.FlickerTestParameterFactory
import com.android.server.wm.flicker.annotation.Group2
import com.android.server.wm.flicker.dsl.FlickerBuilder
-import com.android.server.wm.flicker.endRotation
import com.android.server.wm.flicker.entireScreenCovered
import com.android.server.wm.flicker.helpers.ImeAppHelper
import com.android.server.wm.flicker.helpers.WindowUtils
@@ -42,6 +41,7 @@
import com.android.server.wm.flicker.statusBarLayerIsVisible
import com.android.server.wm.flicker.statusBarLayerRotatesScales
import com.android.server.wm.flicker.statusBarWindowIsVisible
+import com.android.server.wm.traces.parser.toFlickerComponent
import com.android.wm.shell.flicker.DOCKED_STACK_DIVIDER_COMPONENT
import com.android.wm.shell.flicker.helpers.SimpleAppHelper
import com.android.wm.shell.flicker.testapp.Components
@@ -110,7 +110,7 @@
@Test
fun topAppWindowIsAlwaysVisible() {
testSpec.assertWm {
- this.isAppWindowVisible(Components.SimpleActivity.COMPONENT)
+ this.isAppWindowVisible(Components.SimpleActivity.COMPONENT.toFlickerComponent())
}
}
@@ -118,7 +118,7 @@
@Test
fun bottomAppWindowIsAlwaysVisible() {
testSpec.assertWm {
- this.isAppWindowVisible(Components.ImeActivity.COMPONENT)
+ this.isAppWindowVisible(Components.ImeActivity.COMPONENT.toFlickerComponent())
}
}
@@ -132,24 +132,22 @@
fun entireScreenCovered() = testSpec.entireScreenCovered()
@Test
- fun navBarLayerRotatesAndScales() =
- testSpec.navBarLayerRotatesAndScales(testSpec.config.endRotation)
+ fun navBarLayerRotatesAndScales() = testSpec.navBarLayerRotatesAndScales()
@Test
- fun statusBarLayerRotatesScales() =
- testSpec.statusBarLayerRotatesScales(testSpec.config.endRotation)
+ fun statusBarLayerRotatesScales() = testSpec.statusBarLayerRotatesScales()
@Test
fun topAppLayerIsAlwaysVisible() {
testSpec.assertLayers {
- this.isVisible(Components.SimpleActivity.COMPONENT)
+ this.isVisible(Components.SimpleActivity.COMPONENT.toFlickerComponent())
}
}
@Test
fun bottomAppLayerIsAlwaysVisible() {
testSpec.assertLayers {
- this.isVisible(Components.ImeActivity.COMPONENT)
+ this.isVisible(Components.ImeActivity.COMPONENT.toFlickerComponent())
}
}
@@ -174,8 +172,10 @@
dividerBounds.bottom - WindowUtils.dockedStackDividerInset,
displayBounds.right,
displayBounds.bottom - WindowUtils.navigationBarHeight)
- visibleRegion(Components.SimpleActivity.COMPONENT).coversExactly(topAppBounds)
- visibleRegion(Components.ImeActivity.COMPONENT).coversExactly(bottomAppBounds)
+ visibleRegion(Components.SimpleActivity.COMPONENT.toFlickerComponent())
+ .coversExactly(topAppBounds)
+ visibleRegion(Components.ImeActivity.COMPONENT.toFlickerComponent())
+ .coversExactly(bottomAppBounds)
}
}
@@ -194,8 +194,10 @@
displayBounds.right,
displayBounds.bottom - WindowUtils.navigationBarHeight)
- visibleRegion(Components.SimpleActivity.COMPONENT).coversExactly(topAppBounds)
- visibleRegion(Components.ImeActivity.COMPONENT).coversExactly(bottomAppBounds)
+ visibleRegion(Components.SimpleActivity.COMPONENT.toFlickerComponent())
+ .coversExactly(topAppBounds)
+ visibleRegion(Components.ImeActivity.COMPONENT.toFlickerComponent())
+ .coversExactly(bottomAppBounds)
}
}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/RotateOneLaunchedAppAndEnterSplitScreen.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/RotateOneLaunchedAppAndEnterSplitScreen.kt
index 8a2b55b..8a50bc0 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/RotateOneLaunchedAppAndEnterSplitScreen.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/RotateOneLaunchedAppAndEnterSplitScreen.kt
@@ -25,7 +25,6 @@
import com.android.server.wm.flicker.FlickerTestParameterFactory
import com.android.server.wm.flicker.annotation.Group2
import com.android.server.wm.flicker.dsl.FlickerBuilder
-import com.android.server.wm.flicker.endRotation
import com.android.server.wm.flicker.helpers.launchSplitScreen
import com.android.server.wm.flicker.helpers.setRotation
import com.android.server.wm.flicker.navBarLayerRotatesAndScales
@@ -75,15 +74,11 @@
@Presubmit
@Test
- fun navBarLayerRotatesAndScales() =
- testSpec.navBarLayerRotatesAndScales(testSpec.config.startRotation,
- testSpec.config.endRotation)
+ fun navBarLayerRotatesAndScales() = testSpec.navBarLayerRotatesAndScales()
@Presubmit
@Test
- fun statusBarLayerRotatesScales() =
- testSpec.statusBarLayerRotatesScales(testSpec.config.startRotation,
- testSpec.config.endRotation)
+ fun statusBarLayerRotatesScales() = testSpec.statusBarLayerRotatesScales()
@Presubmit
@Test
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/RotateOneLaunchedAppInSplitScreenMode.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/RotateOneLaunchedAppInSplitScreenMode.kt
index b325157..84676a9 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/RotateOneLaunchedAppInSplitScreenMode.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/RotateOneLaunchedAppInSplitScreenMode.kt
@@ -25,7 +25,6 @@
import com.android.server.wm.flicker.FlickerTestParameterFactory
import com.android.server.wm.flicker.annotation.Group2
import com.android.server.wm.flicker.dsl.FlickerBuilder
-import com.android.server.wm.flicker.endRotation
import com.android.server.wm.flicker.helpers.launchSplitScreen
import com.android.server.wm.flicker.helpers.setRotation
import com.android.server.wm.flicker.navBarLayerRotatesAndScales
@@ -74,13 +73,11 @@
@Presubmit
@Test
- fun navBarLayerRotatesAndScales() = testSpec.navBarLayerRotatesAndScales(
- testSpec.config.startRotation, testSpec.config.endRotation)
+ fun navBarLayerRotatesAndScales() = testSpec.navBarLayerRotatesAndScales()
@Presubmit
@Test
- fun statusBarLayerRotatesScales() = testSpec.statusBarLayerRotatesScales(
- testSpec.config.startRotation, testSpec.config.endRotation)
+ fun statusBarLayerRotatesScales() = testSpec.statusBarLayerRotatesScales()
@Presubmit
@Test
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/RotateTwoLaunchedAppAndEnterSplitScreen.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/RotateTwoLaunchedAppAndEnterSplitScreen.kt
index 2be6936..2abdca9 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/RotateTwoLaunchedAppAndEnterSplitScreen.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/RotateTwoLaunchedAppAndEnterSplitScreen.kt
@@ -24,7 +24,6 @@
import com.android.server.wm.flicker.FlickerTestParameterFactory
import com.android.server.wm.flicker.annotation.Group2
import com.android.server.wm.flicker.dsl.FlickerBuilder
-import com.android.server.wm.flicker.endRotation
import com.android.server.wm.flicker.helpers.launchSplitScreen
import com.android.server.wm.flicker.helpers.reopenAppFromOverview
import com.android.server.wm.flicker.helpers.setRotation
@@ -83,14 +82,11 @@
@Presubmit
@Test
- fun navBarLayerRotatesAndScales() =
- testSpec.navBarLayerRotatesAndScales(testSpec.config.startRotation,
- testSpec.config.endRotation)
+ fun navBarLayerRotatesAndScales() = testSpec.navBarLayerRotatesAndScales()
@Presubmit
@Test
- fun statusBarLayerRotatesScales() = testSpec.statusBarLayerRotatesScales(
- testSpec.config.startRotation, testSpec.config.endRotation)
+ fun statusBarLayerRotatesScales() = testSpec.statusBarLayerRotatesScales()
@Presubmit
@Test
@@ -101,7 +97,7 @@
// Because we log WM once per frame, sometimes the activity and the window
// become visible in the same entry, sometimes not, thus it is not possible to
// assert the visibility of the activity here
- this.isAppWindowInvisible(secondaryApp.component, ignoreActivity = true)
+ this.isAppWindowInvisible(secondaryApp.component)
.then()
// during re-parenting, the window may disappear and reappear from the
// trace, this occurs because we log only 1x per frame
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/RotateTwoLaunchedAppInSplitScreenMode.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/RotateTwoLaunchedAppInSplitScreenMode.kt
index 5782f14..fe9b9f5 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/RotateTwoLaunchedAppInSplitScreenMode.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/RotateTwoLaunchedAppInSplitScreenMode.kt
@@ -25,7 +25,6 @@
import com.android.server.wm.flicker.FlickerTestParameterFactory
import com.android.server.wm.flicker.annotation.Group2
import com.android.server.wm.flicker.dsl.FlickerBuilder
-import com.android.server.wm.flicker.endRotation
import com.android.server.wm.flicker.helpers.launchSplitScreen
import com.android.server.wm.flicker.helpers.reopenAppFromOverview
import com.android.server.wm.flicker.helpers.setRotation
@@ -89,15 +88,11 @@
@Presubmit
@Test
- fun navBarLayerRotatesAndScales() =
- testSpec.navBarLayerRotatesAndScales(testSpec.config.startRotation,
- testSpec.config.endRotation)
+ fun navBarLayerRotatesAndScales() = testSpec.navBarLayerRotatesAndScales()
@Presubmit
@Test
- fun statusBarLayerRotatesScales() =
- testSpec.statusBarLayerRotatesScales(testSpec.config.startRotation,
- testSpec.config.endRotation)
+ fun statusBarLayerRotatesScales() = testSpec.statusBarLayerRotatesScales()
@FlakyTest
@Test
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/CommonAssertions.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/CommonAssertions.kt
index 443204c2..f9b0800 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/CommonAssertions.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/CommonAssertions.kt
@@ -14,6 +14,7 @@
* limitations under the License.
*/
+@file:JvmName("CommonAssertions")
package com.android.wm.shell.flicker.pip
internal const val PIP_WINDOW_COMPONENT = "PipMenuActivity"
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipTest.kt
index 046972d..52a744f 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipTest.kt
@@ -26,7 +26,6 @@
import com.android.server.wm.flicker.LAUNCHER_COMPONENT
import com.android.server.wm.flicker.annotation.Group3
import com.android.server.wm.flicker.dsl.FlickerBuilder
-import com.android.server.wm.traces.parser.toLayerName
import org.junit.FixMethodOrder
import org.junit.Test
import org.junit.runner.RunWith
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipToOtherOrientationTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipToOtherOrientationTest.kt
index 097ccb8..c8c3f4d 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipToOtherOrientationTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipToOtherOrientationTest.kt
@@ -29,7 +29,7 @@
import com.android.server.wm.flicker.helpers.WindowUtils
import com.android.server.wm.flicker.navBarLayerRotatesAndScales
import com.android.server.wm.flicker.statusBarLayerRotatesScales
-import com.android.server.wm.traces.parser.windowmanager.WindowManagerStateHelper
+import com.android.server.wm.traces.common.FlickerComponentName
import com.android.wm.shell.flicker.helpers.FixedAppHelper
import com.android.wm.shell.flicker.pip.PipTransition.BroadcastActionTrigger.Companion.ORIENTATION_LANDSCAPE
import com.android.wm.shell.flicker.pip.PipTransition.BroadcastActionTrigger.Companion.ORIENTATION_PORTRAIT
@@ -106,22 +106,20 @@
}
/**
- * Checks that the [WindowManagerStateHelper.NAV_BAR_COMPONENT] has the correct position at
+ * Checks that the [FlickerComponentName.NAV_BAR] has the correct position at
* the start and end of the transition
*/
@FlakyTest
@Test
- override fun navBarLayerRotatesAndScales() =
- testSpec.navBarLayerRotatesAndScales(Surface.ROTATION_90, Surface.ROTATION_0)
+ override fun navBarLayerRotatesAndScales() = testSpec.navBarLayerRotatesAndScales()
/**
- * Checks that the [WindowManagerStateHelper.STATUS_BAR_COMPONENT] has the correct position at
+ * Checks that the [FlickerComponentName.STATUS_BAR] has the correct position at
* the start and end of the transition
*/
@Presubmit
@Test
- override fun statusBarLayerRotatesScales() =
- testSpec.statusBarLayerRotatesScales(Surface.ROTATION_90, Surface.ROTATION_0)
+ override fun statusBarLayerRotatesScales() = testSpec.statusBarLayerRotatesScales()
/**
* Checks that all parts of the screen are covered at the start and end of the transition
@@ -150,7 +148,7 @@
@Test
fun testAppWindowInvisibleOnStart() {
testSpec.assertWmStart {
- isInvisible(testApp.component)
+ isAppWindowInvisible(testApp.component)
}
}
@@ -161,7 +159,7 @@
@Test
fun testAppWindowVisibleOnEnd() {
testSpec.assertWmEnd {
- isVisible(testApp.component)
+ isAppWindowVisible(testApp.component)
}
}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipToAppTransition.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipToAppTransition.kt
index faaaecb..64b7eb5 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipToAppTransition.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipToAppTransition.kt
@@ -18,7 +18,6 @@
import android.platform.test.annotations.Presubmit
import com.android.server.wm.flicker.FlickerTestParameter
-import com.android.server.wm.traces.parser.toLayerName
import com.android.wm.shell.flicker.helpers.FixedAppHelper
import org.junit.Test
@@ -63,7 +62,7 @@
// when the activity is STOPPING, sometimes it becomes invisible in an entry before
// the window, sometimes in the same entry. This occurs because we log 1x per frame
// thus we ignore activity here
- isAppWindowVisible(testApp.component, ignoreActivity = true)
+ isAppWindowVisible(testApp.component)
.isAppWindowOnTop(pipApp.component)
.then()
.isAppWindowInvisible(testApp.component)
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipTransition.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipTransition.kt
index 3414031..5207fed 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipTransition.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipTransition.kt
@@ -54,9 +54,9 @@
open fun pipWindowBecomesInvisible() {
testSpec.assertWm {
this.invoke("hasPipWindow") {
- it.isPinned(pipApp.component).isVisible(pipApp.component)
+ it.isPinned(pipApp.component).isAppWindowVisible(pipApp.component)
}.then().invoke("!hasPipWindow") {
- it.isNotPinned(pipApp.component).isInvisible(pipApp.component)
+ it.isNotPinned(pipApp.component).isAppWindowInvisible(pipApp.component)
}
}
}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipViaExpandButtonClickTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipViaExpandButtonClickTest.kt
index fa100b5..b53342d 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipViaExpandButtonClickTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipViaExpandButtonClickTest.kt
@@ -23,7 +23,6 @@
import com.android.server.wm.flicker.FlickerTestParameterFactory
import com.android.server.wm.flicker.annotation.Group3
import com.android.server.wm.flicker.dsl.FlickerBuilder
-import com.android.server.wm.traces.parser.toWindowName
import org.junit.FixMethodOrder
import org.junit.runner.RunWith
import org.junit.runners.MethodSorters
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipViaIntentTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipViaIntentTest.kt
index 617ef22..1fec3cf 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipViaIntentTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipViaIntentTest.kt
@@ -23,7 +23,6 @@
import com.android.server.wm.flicker.FlickerTestParameterFactory
import com.android.server.wm.flicker.annotation.Group3
import com.android.server.wm.flicker.dsl.FlickerBuilder
-import com.android.server.wm.traces.parser.toWindowName
import org.junit.FixMethodOrder
import org.junit.runner.RunWith
import org.junit.runners.MethodSorters
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipWithDismissButtonTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipWithDismissButtonTest.kt
index e3d099f..73626c2 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipWithDismissButtonTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipWithDismissButtonTest.kt
@@ -16,7 +16,6 @@
package com.android.wm.shell.flicker.pip
-import android.platform.test.annotations.Postsubmit
import android.view.Surface
import androidx.test.filters.RequiresDevice
import com.android.server.wm.flicker.FlickerParametersRunnerFactory
@@ -25,7 +24,6 @@
import com.android.server.wm.flicker.annotation.Group3
import com.android.server.wm.flicker.dsl.FlickerBuilder
import org.junit.FixMethodOrder
-import org.junit.Test
import org.junit.runner.RunWith
import org.junit.runners.MethodSorters
import org.junit.runners.Parameterized
@@ -62,14 +60,6 @@
}
}
- @Postsubmit
- @Test
- override fun pipLayerBecomesInvisible() = super.pipLayerBecomesInvisible()
-
- @Postsubmit
- @Test
- override fun pipWindowBecomesInvisible() = super.pipWindowBecomesInvisible()
-
companion object {
/**
* Creates the test configurations.
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipWithSwipeDownTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipWithSwipeDownTest.kt
index 2cdfc2b..9e43dee 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipWithSwipeDownTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipWithSwipeDownTest.kt
@@ -25,7 +25,6 @@
import com.android.server.wm.flicker.FlickerTestParameterFactory
import com.android.server.wm.flicker.annotation.Group3
import com.android.server.wm.flicker.dsl.FlickerBuilder
-import com.android.server.wm.flicker.startRotation
import com.android.server.wm.flicker.statusBarLayerRotatesScales
import org.junit.FixMethodOrder
import org.junit.Test
@@ -97,8 +96,7 @@
@Presubmit
@Test
- override fun statusBarLayerRotatesScales() =
- testSpec.statusBarLayerRotatesScales(testSpec.config.startRotation, Surface.ROTATION_0)
+ override fun statusBarLayerRotatesScales() = testSpec.statusBarLayerRotatesScales()
@Presubmit
@Test
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExpandPipOnDoubleClickTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExpandPipOnDoubleClickTest.kt
index 89b2c40..d0fee9a 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExpandPipOnDoubleClickTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExpandPipOnDoubleClickTest.kt
@@ -16,7 +16,6 @@
package com.android.wm.shell.flicker.pip
-import android.platform.test.annotations.Postsubmit
import android.platform.test.annotations.Presubmit
import android.view.Surface
import androidx.test.filters.FlakyTest
@@ -27,7 +26,6 @@
import com.android.server.wm.flicker.LAUNCHER_COMPONENT
import com.android.server.wm.flicker.annotation.Group3
import com.android.server.wm.flicker.dsl.FlickerBuilder
-import com.android.server.wm.traces.parser.toLayerName
import org.junit.FixMethodOrder
import org.junit.Test
import org.junit.runner.RunWith
@@ -91,7 +89,7 @@
/**
* Checks [pipApp] window remains visible throughout the animation
*/
- @Postsubmit
+ @Presubmit
@Test
fun pipWindowIsAlwaysVisible() {
testSpec.assertWm {
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/MovePipShelfHeightTransition.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/MovePipShelfHeightTransition.kt
index ed04fc9..6e0324c 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/MovePipShelfHeightTransition.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/MovePipShelfHeightTransition.kt
@@ -20,8 +20,6 @@
import com.android.launcher3.tapl.LauncherInstrumentation
import com.android.server.wm.flicker.FlickerTestParameter
import com.android.server.wm.flicker.traces.RegionSubject
-import com.android.server.wm.traces.parser.toLayerName
-import com.android.server.wm.traces.parser.toWindowName
import com.android.wm.shell.flicker.helpers.FixedAppHelper
import org.junit.Test
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipKeyboardTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipKeyboardTest.kt
index 5719413..aba8ace 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipKeyboardTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipKeyboardTest.kt
@@ -22,12 +22,12 @@
import com.android.server.wm.flicker.FlickerParametersRunnerFactory
import com.android.server.wm.flicker.FlickerTestParameter
import com.android.server.wm.flicker.FlickerTestParameterFactory
-import com.android.server.wm.flicker.annotation.Group3
+import com.android.server.wm.flicker.annotation.Group4
import com.android.server.wm.flicker.dsl.FlickerBuilder
import com.android.server.wm.flicker.helpers.WindowUtils
import com.android.server.wm.flicker.helpers.setRotation
import com.android.server.wm.flicker.startRotation
-import com.android.server.wm.traces.parser.windowmanager.WindowManagerStateHelper
+import com.android.server.wm.traces.common.FlickerComponentName
import com.android.wm.shell.flicker.helpers.ImeAppHelper
import org.junit.FixMethodOrder
import org.junit.Test
@@ -43,7 +43,7 @@
@RunWith(Parameterized::class)
@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-@Group3
+@Group4
class PipKeyboardTest(testSpec: FlickerTestParameter) : PipTransition(testSpec) {
private val imeApp = ImeAppHelper(instrumentation)
@@ -90,7 +90,7 @@
@Test
fun pipIsAboveAppWindow() {
testSpec.assertWmTag(TAG_IME_VISIBLE) {
- isAboveWindow(WindowManagerStateHelper.IME_COMPONENT, pipApp.component)
+ isAboveWindow(FlickerComponentName.IME, pipApp.component)
}
}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipLegacySplitScreenTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipLegacySplitScreenTest.kt
index 0861652..9bea5c0 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipLegacySplitScreenTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipLegacySplitScreenTest.kt
@@ -23,7 +23,7 @@
import com.android.server.wm.flicker.FlickerParametersRunnerFactory
import com.android.server.wm.flicker.FlickerTestParameter
import com.android.server.wm.flicker.FlickerTestParameterFactory
-import com.android.server.wm.flicker.annotation.Group3
+import com.android.server.wm.flicker.annotation.Group4
import com.android.server.wm.flicker.dsl.FlickerBuilder
import com.android.server.wm.flicker.helpers.launchSplitScreen
import com.android.server.wm.flicker.helpers.wakeUpAndGoToHomeScreen
@@ -51,7 +51,7 @@
@RunWith(Parameterized::class)
@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-@Group3
+@Group4
class PipLegacySplitScreenTest(testSpec: FlickerTestParameter) : PipTransition(testSpec) {
private val imeApp = ImeAppHelper(instrumentation)
private val testApp = FixedAppHelper(instrumentation)
@@ -104,9 +104,9 @@
@Test
fun bothAppWindowsVisible() {
testSpec.assertWmEnd {
- isVisible(testApp.component)
- isVisible(imeApp.component)
- noWindowsOverlap(testApp.component, imeApp.component)
+ isAppWindowVisible(testApp.component)
+ isAppWindowVisible(imeApp.component)
+ doNotOverlap(testApp.component, imeApp.component)
}
}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipRotationTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipRotationTest.kt
index b105460..669f37a 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipRotationTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipRotationTest.kt
@@ -23,7 +23,7 @@
import com.android.server.wm.flicker.FlickerParametersRunnerFactory
import com.android.server.wm.flicker.FlickerTestParameter
import com.android.server.wm.flicker.FlickerTestParameterFactory
-import com.android.server.wm.flicker.annotation.Group3
+import com.android.server.wm.flicker.annotation.Group4
import com.android.server.wm.flicker.dsl.FlickerBuilder
import com.android.server.wm.flicker.endRotation
import com.android.server.wm.flicker.entireScreenCovered
@@ -62,7 +62,7 @@
@RunWith(Parameterized::class)
@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-@Group3
+@Group4
class PipRotationTest(testSpec: FlickerTestParameter) : PipTransition(testSpec) {
private val fixedApp = FixedAppHelper(instrumentation)
private val screenBoundsStart = WindowUtils.getDisplayBounds(testSpec.config.startRotation)
@@ -95,18 +95,14 @@
*/
@FlakyTest
@Test
- override fun navBarLayerRotatesAndScales() =
- testSpec.navBarLayerRotatesAndScales(testSpec.config.startRotation,
- testSpec.config.endRotation)
+ override fun navBarLayerRotatesAndScales() = testSpec.navBarLayerRotatesAndScales()
/**
* Checks the position of the status bar at the start and end of the transition
*/
@Presubmit
@Test
- override fun statusBarLayerRotatesScales() =
- testSpec.statusBarLayerRotatesScales(testSpec.config.startRotation,
- testSpec.config.endRotation)
+ override fun statusBarLayerRotatesScales() = testSpec.statusBarLayerRotatesScales()
/**
* Checks that [fixedApp] layer is within [screenBoundsStart] at the start of the transition
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipTransition.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipTransition.kt
index ce89fb6..e8a61e8 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipTransition.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipTransition.kt
@@ -177,13 +177,11 @@
@Presubmit
@Test
- open fun navBarLayerRotatesAndScales() =
- testSpec.navBarLayerRotatesAndScales(testSpec.config.startRotation, Surface.ROTATION_0)
+ open fun navBarLayerRotatesAndScales() = testSpec.navBarLayerRotatesAndScales()
@Presubmit
@Test
- open fun statusBarLayerRotatesScales() =
- testSpec.statusBarLayerRotatesScales(testSpec.config.startRotation, Surface.ROTATION_0)
+ open fun statusBarLayerRotatesScales() = testSpec.statusBarLayerRotatesScales()
@Presubmit
@Test
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/SetRequestedOrientationWhilePinnedTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/SetRequestedOrientationWhilePinnedTest.kt
index 02a3eb1..d6dbc36 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/SetRequestedOrientationWhilePinnedTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/SetRequestedOrientationWhilePinnedTest.kt
@@ -22,7 +22,7 @@
import com.android.server.wm.flicker.FlickerParametersRunnerFactory
import com.android.server.wm.flicker.FlickerTestParameter
import com.android.server.wm.flicker.FlickerTestParameterFactory
-import com.android.server.wm.flicker.annotation.Group3
+import com.android.server.wm.flicker.annotation.Group4
import com.android.server.wm.flicker.dsl.FlickerBuilder
import com.android.server.wm.flicker.helpers.WindowUtils
import com.android.wm.shell.flicker.pip.PipTransition.BroadcastActionTrigger.Companion.ORIENTATION_LANDSCAPE
@@ -43,7 +43,7 @@
@RunWith(Parameterized::class)
@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-@Group3
+@Group4
class SetRequestedOrientationWhilePinnedTest(
testSpec: FlickerTestParameter
) : PipTransition(testSpec) {
diff --git a/libs/WindowManager/Shell/tests/flicker/test-apps/flickerapp/AndroidManifest.xml b/libs/WindowManager/Shell/tests/flicker/test-apps/flickerapp/AndroidManifest.xml
index 5549330..2cdbffa 100644
--- a/libs/WindowManager/Shell/tests/flicker/test-apps/flickerapp/AndroidManifest.xml
+++ b/libs/WindowManager/Shell/tests/flicker/test-apps/flickerapp/AndroidManifest.xml
@@ -107,5 +107,20 @@
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
</activity>
+ <activity
+ android:name=".LaunchBubbleActivity"
+ android:label="LaunchBubbleApp"
+ android:exported="true"
+ android:launchMode="singleTop">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ <action android:name="android.intent.action.VIEW" />
+ </intent-filter>
+ </activity>
+ <activity
+ android:name=".BubbleActivity"
+ android:label="BubbleApp"
+ android:exported="false"
+ android:resizeableActivity="true" />
</application>
</manifest>
diff --git a/libs/WindowManager/Shell/tests/flicker/test-apps/flickerapp/res/drawable/bg.png b/libs/WindowManager/Shell/tests/flicker/test-apps/flickerapp/res/drawable/bg.png
new file mode 100644
index 0000000..d424a17
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/flicker/test-apps/flickerapp/res/drawable/bg.png
Binary files differ
diff --git a/libs/WindowManager/Shell/tests/flicker/test-apps/flickerapp/res/drawable/ic_bubble.xml b/libs/WindowManager/Shell/tests/flicker/test-apps/flickerapp/res/drawable/ic_bubble.xml
new file mode 100644
index 0000000..b43f31d
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/flicker/test-apps/flickerapp/res/drawable/ic_bubble.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M7.2,14.4m-3.2,0a3.2,3.2 0,1 1,6.4 0a3.2,3.2 0,1 1,-6.4 0"/>
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M14.8,18m-2,0a2,2 0,1 1,4 0a2,2 0,1 1,-4 0"/>
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M15.2,8.8m-4.8,0a4.8,4.8 0,1 1,9.6 0a4.8,4.8 0,1 1,-9.6 0"/>
+</vector>
diff --git a/libs/WindowManager/Shell/tests/flicker/test-apps/flickerapp/res/drawable/ic_message.xml b/libs/WindowManager/Shell/tests/flicker/test-apps/flickerapp/res/drawable/ic_message.xml
new file mode 100644
index 0000000..0e8c7a0
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/flicker/test-apps/flickerapp/res/drawable/ic_message.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24"
+ android:viewportHeight="24">
+ <path
+ android:pathData="M12,4c-4.97,0 -9,3.58 -9,8c0,1.53 0.49,2.97 1.33,4.18c0.12,0.18 0.2,0.46 0.1,0.66c-0.33,0.68 -0.79,1.52 -1.38,2.39c-0.12,0.17 0.01,0.41 0.21,0.39c0.63,-0.05 1.86,-0.26 3.38,-0.91c0.17,-0.07 0.36,-0.06 0.52,0.03C8.55,19.54 10.21,20 12,20c4.97,0 9,-3.58 9,-8S16.97,4 12,4zM16.94,11.63l-3.29,3.29c-0.13,0.13 -0.34,0.04 -0.34,-0.14v-1.57c0,-0.11 -0.1,-0.21 -0.21,-0.2c-2.19,0.06 -3.65,0.65 -5.14,1.95c-0.15,0.13 -0.38,0 -0.33,-0.19c0.7,-2.57 2.9,-4.57 5.5,-4.75c0.1,-0.01 0.18,-0.09 0.18,-0.19V8.2c0,-0.18 0.22,-0.27 0.34,-0.14l3.29,3.29C17.02,11.43 17.02,11.55 16.94,11.63z"
+ android:fillColor="#000000"
+ android:fillType="evenOdd"/>
+</vector>
diff --git a/libs/WindowManager/Shell/tests/flicker/test-apps/flickerapp/res/layout/activity_bubble.xml b/libs/WindowManager/Shell/tests/flicker/test-apps/flickerapp/res/layout/activity_bubble.xml
new file mode 100644
index 0000000..f8b0ca3
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/flicker/test-apps/flickerapp/res/layout/activity_bubble.xml
@@ -0,0 +1,48 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright 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.
+-->
+<LinearLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent">
+ <Button
+ android:id="@+id/button_finish"
+ android:layout_width="wrap_content"
+ android:layout_height="48dp"
+ android:layout_marginStart="8dp"
+ android:text="Finish" />
+ <Button
+ android:id="@+id/button_new_task"
+ android:layout_width="wrap_content"
+ android:layout_height="46dp"
+ android:layout_marginStart="8dp"
+ android:text="New Task" />
+ <Button
+ android:id="@+id/button_new_bubble"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginStart="8dp"
+ android:layout_marginEnd="8dp"
+ android:text="New Bubble" />
+
+ <Button
+ android:id="@+id/button_activity_for_result"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginEnd="8dp"
+ android:layout_marginStart="8dp"
+ android:text="Activity For Result" />
+</LinearLayout>
diff --git a/libs/WindowManager/Shell/tests/flicker/test-apps/flickerapp/res/layout/activity_main.xml b/libs/WindowManager/Shell/tests/flicker/test-apps/flickerapp/res/layout/activity_main.xml
new file mode 100644
index 0000000..f23c464
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/flicker/test-apps/flickerapp/res/layout/activity_main.xml
@@ -0,0 +1,48 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright 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.
+-->
+<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:orientation="vertical"
+ android:background="@android:color/black">
+
+ <Button
+ android:id="@+id/button_create"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_centerHorizontal="true"
+ android:layout_centerVertical="true"
+ android:text="Add Bubble" />
+
+ <Button
+ android:id="@+id/button_cancel"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_below="@id/button_create"
+ android:layout_centerHorizontal="true"
+ android:layout_marginTop="20dp"
+ android:text="Cancel Bubble" />
+
+ <Button
+ android:id="@+id/button_cancel_all"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_below="@id/button_cancel"
+ android:layout_centerHorizontal="true"
+ android:layout_marginTop="20dp"
+ android:text="Cancel All Bubble" />
+</RelativeLayout>
diff --git a/libs/WindowManager/Shell/tests/flicker/test-apps/flickerapp/src/com/android/wm/shell/flicker/testapp/BubbleActivity.java b/libs/WindowManager/Shell/tests/flicker/test-apps/flickerapp/src/com/android/wm/shell/flicker/testapp/BubbleActivity.java
new file mode 100644
index 0000000..bc3bc75
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/flicker/test-apps/flickerapp/src/com/android/wm/shell/flicker/testapp/BubbleActivity.java
@@ -0,0 +1,77 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell.flicker.testapp;
+
+
+import android.app.Activity;
+import android.content.Intent;
+import android.os.Bundle;
+import android.widget.Toast;
+
+public class BubbleActivity extends Activity {
+ private int mNotifId = 0;
+
+ public BubbleActivity() {
+ super();
+ }
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ Intent intent = getIntent();
+ if (intent != null) {
+ mNotifId = intent.getIntExtra(BubbleHelper.EXTRA_BUBBLE_NOTIF_ID, -1);
+ } else {
+ mNotifId = -1;
+ }
+
+ setContentView(R.layout.activity_bubble);
+ }
+
+ @Override
+ protected void onStart() {
+ super.onStart();
+ }
+
+ @Override
+ protected void onResume() {
+ super.onResume();
+ }
+
+ @Override
+ protected void onPause() {
+ super.onPause();
+ }
+
+ @Override
+ protected void onStop() {
+ super.onStop();
+ }
+
+ @Override
+ protected void onDestroy() {
+ super.onDestroy();
+ }
+
+ @Override
+ protected void onActivityResult(int requestCode, int resultCode, Intent data) {
+ super.onActivityResult(requestCode, resultCode, data);
+ String result = resultCode == Activity.RESULT_OK ? "OK" : "CANCELLED";
+ Toast.makeText(this, "Activity result: " + result, Toast.LENGTH_SHORT).show();
+ }
+}
diff --git a/libs/WindowManager/Shell/tests/flicker/test-apps/flickerapp/src/com/android/wm/shell/flicker/testapp/BubbleHelper.java b/libs/WindowManager/Shell/tests/flicker/test-apps/flickerapp/src/com/android/wm/shell/flicker/testapp/BubbleHelper.java
new file mode 100644
index 0000000..d743dff
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/flicker/test-apps/flickerapp/src/com/android/wm/shell/flicker/testapp/BubbleHelper.java
@@ -0,0 +1,178 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell.flicker.testapp;
+
+
+import android.app.Notification;
+import android.app.NotificationChannel;
+import android.app.NotificationManager;
+import android.app.PendingIntent;
+import android.app.Person;
+import android.app.RemoteInput;
+import android.content.Context;
+import android.content.Intent;
+import android.graphics.Point;
+import android.graphics.drawable.Icon;
+import android.os.SystemClock;
+import android.service.notification.StatusBarNotification;
+import android.view.WindowManager;
+
+import java.util.HashMap;
+
+public class BubbleHelper {
+
+ static final String EXTRA_BUBBLE_NOTIF_ID = "EXTRA_BUBBLE_NOTIF_ID";
+ static final String CHANNEL_ID = "bubbles";
+ static final String CHANNEL_NAME = "Bubbles";
+ static final int DEFAULT_HEIGHT_DP = 300;
+
+ private static BubbleHelper sInstance;
+
+ private final Context mContext;
+ private NotificationManager mNotificationManager;
+ private float mDisplayHeight;
+
+ private HashMap<Integer, BubbleInfo> mBubbleMap = new HashMap<>();
+
+ private int mNextNotifyId = 0;
+ private int mColourIndex = 0;
+
+ public static class BubbleInfo {
+ public int id;
+ public int height;
+ public Icon icon;
+
+ public BubbleInfo(int id, int height, Icon icon) {
+ this.id = id;
+ this.height = height;
+ this.icon = icon;
+ }
+ }
+
+ public static BubbleHelper getInstance(Context context) {
+ if (sInstance == null) {
+ sInstance = new BubbleHelper(context);
+ }
+ return sInstance;
+ }
+
+ private BubbleHelper(Context context) {
+ mContext = context;
+ mNotificationManager = context.getSystemService(NotificationManager.class);
+
+ NotificationChannel channel = new NotificationChannel(CHANNEL_ID, CHANNEL_NAME,
+ NotificationManager.IMPORTANCE_DEFAULT);
+ channel.setDescription("Channel that posts bubbles");
+ channel.setAllowBubbles(true);
+ mNotificationManager.createNotificationChannel(channel);
+
+ Point p = new Point();
+ WindowManager wm = context.getSystemService(WindowManager.class);
+ wm.getDefaultDisplay().getRealSize(p);
+ mDisplayHeight = p.y;
+
+ }
+
+ private int getNextNotifyId() {
+ int id = mNextNotifyId;
+ mNextNotifyId++;
+ return id;
+ }
+
+ private Icon getIcon() {
+ return Icon.createWithResource(mContext, R.drawable.bg);
+ }
+
+ public int addNewBubble(boolean autoExpand, boolean suppressNotif) {
+ int id = getNextNotifyId();
+ BubbleInfo info = new BubbleInfo(id, DEFAULT_HEIGHT_DP, getIcon());
+ mBubbleMap.put(info.id, info);
+
+ Notification.BubbleMetadata data = getBubbleBuilder(info)
+ .setSuppressNotification(suppressNotif)
+ .setAutoExpandBubble(false)
+ .build();
+ Notification notification = getNotificationBuilder(info.id)
+ .setBubbleMetadata(data).build();
+
+ mNotificationManager.notify(info.id, notification);
+ return info.id;
+ }
+
+ private Notification.Builder getNotificationBuilder(int id) {
+ Person chatBot = new Person.Builder()
+ .setBot(true)
+ .setName("BubbleBot")
+ .setImportant(true)
+ .build();
+
+ RemoteInput remoteInput = new RemoteInput.Builder("key")
+ .setLabel("Reply")
+ .build();
+
+ String shortcutId = "BubbleChat";
+ return new Notification.Builder(mContext, CHANNEL_ID)
+ .setChannelId(CHANNEL_ID)
+ .setShortcutId(shortcutId)
+ .setContentIntent(PendingIntent.getActivity(mContext, 0,
+ new Intent(mContext, LaunchBubbleActivity.class),
+ PendingIntent.FLAG_UPDATE_CURRENT))
+ .setStyle(new Notification.MessagingStyle(chatBot)
+ .setConversationTitle("Bubble Chat")
+ .addMessage("Hello? This is bubble: " + id,
+ SystemClock.currentThreadTimeMillis() - 300000, chatBot)
+ .addMessage("Is it me, " + id + ", you're looking for?",
+ SystemClock.currentThreadTimeMillis(), chatBot)
+ )
+ .setSmallIcon(R.drawable.ic_bubble);
+ }
+
+ private Notification.BubbleMetadata.Builder getBubbleBuilder(BubbleInfo info) {
+ Intent target = new Intent(mContext, BubbleActivity.class);
+ target.putExtra(EXTRA_BUBBLE_NOTIF_ID, info.id);
+ PendingIntent bubbleIntent = PendingIntent.getActivity(mContext, info.id, target,
+ PendingIntent.FLAG_UPDATE_CURRENT);
+
+ return new Notification.BubbleMetadata.Builder()
+ .setIntent(bubbleIntent)
+ .setIcon(info.icon)
+ .setDesiredHeight(info.height);
+ }
+
+ public void cancel(int id) {
+ mNotificationManager.cancel(id);
+ }
+
+ public void cancelAll() {
+ mNotificationManager.cancelAll();
+ }
+
+ public void cancelLast() {
+ StatusBarNotification[] activeNotifications = mNotificationManager.getActiveNotifications();
+ if (activeNotifications.length > 0) {
+ mNotificationManager.cancel(
+ activeNotifications[activeNotifications.length - 1].getId());
+ }
+ }
+
+ public void cancelFirst() {
+ StatusBarNotification[] activeNotifications = mNotificationManager.getActiveNotifications();
+ if (activeNotifications.length > 0) {
+ mNotificationManager.cancel(activeNotifications[0].getId());
+ }
+ }
+}
diff --git a/libs/WindowManager/Shell/tests/flicker/test-apps/flickerapp/src/com/android/wm/shell/flicker/testapp/Components.java b/libs/WindowManager/Shell/tests/flicker/test-apps/flickerapp/src/com/android/wm/shell/flicker/testapp/Components.java
index 0ead91b..0ed59bd 100644
--- a/libs/WindowManager/Shell/tests/flicker/test-apps/flickerapp/src/com/android/wm/shell/flicker/testapp/Components.java
+++ b/libs/WindowManager/Shell/tests/flicker/test-apps/flickerapp/src/com/android/wm/shell/flicker/testapp/Components.java
@@ -87,4 +87,16 @@
public static final ComponentName COMPONENT = new ComponentName(PACKAGE_NAME,
PACKAGE_NAME + ".SplitScreenSecondaryActivity");
}
+
+ public static class LaunchBubbleActivity {
+ public static final String LABEL = "LaunchBubbleApp";
+ public static final ComponentName COMPONENT = new ComponentName(PACKAGE_NAME,
+ PACKAGE_NAME + ".LaunchBubbleActivity");
+ }
+
+ public static class BubbleActivity {
+ public static final String LABEL = "BubbleApp";
+ public static final ComponentName COMPONENT = new ComponentName(PACKAGE_NAME,
+ PACKAGE_NAME + ".BubbleActivity");
+ }
}
diff --git a/libs/WindowManager/Shell/tests/flicker/test-apps/flickerapp/src/com/android/wm/shell/flicker/testapp/LaunchBubbleActivity.java b/libs/WindowManager/Shell/tests/flicker/test-apps/flickerapp/src/com/android/wm/shell/flicker/testapp/LaunchBubbleActivity.java
new file mode 100644
index 0000000..71fa66d
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/flicker/test-apps/flickerapp/src/com/android/wm/shell/flicker/testapp/LaunchBubbleActivity.java
@@ -0,0 +1,82 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell.flicker.testapp;
+
+
+import android.app.Activity;
+import android.app.Person;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.ShortcutInfo;
+import android.content.pm.ShortcutManager;
+import android.graphics.drawable.Icon;
+import android.os.Bundle;
+import android.view.View;
+
+import java.util.Arrays;
+
+public class LaunchBubbleActivity extends Activity {
+
+ private BubbleHelper mBubbleHelper;
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ addInboxShortcut(getApplicationContext());
+ mBubbleHelper = BubbleHelper.getInstance(this);
+ setContentView(R.layout.activity_main);
+ findViewById(R.id.button_create).setOnClickListener(this::add);
+ findViewById(R.id.button_cancel).setOnClickListener(this::cancel);
+ findViewById(R.id.button_cancel_all).setOnClickListener(this::cancelAll);
+ }
+
+ private void add(View v) {
+ mBubbleHelper.addNewBubble(false /* autoExpand */, false /* suppressNotif */);
+ }
+
+ private void cancel(View v) {
+ mBubbleHelper.cancelLast();
+ }
+
+ private void cancelAll(View v) {
+ mBubbleHelper.cancelAll();
+ }
+
+ private void addInboxShortcut(Context context) {
+ Icon icon = Icon.createWithResource(this, R.drawable.bg);
+ Person[] persons = new Person[4];
+ for (int i = 0; i < persons.length; i++) {
+ persons[i] = new Person.Builder()
+ .setBot(false)
+ .setIcon(icon)
+ .setName("google" + i)
+ .setImportant(true)
+ .build();
+ }
+
+ ShortcutInfo shortcut = new ShortcutInfo.Builder(context, "BubbleChat")
+ .setShortLabel("BubbleChat")
+ .setLongLived(true)
+ .setIntent(new Intent(Intent.ACTION_VIEW))
+ .setIcon(Icon.createWithResource(context, R.drawable.ic_message))
+ .setPersons(persons)
+ .build();
+ ShortcutManager scmanager = context.getSystemService(ShortcutManager.class);
+ scmanager.addDynamicShortcuts(Arrays.asList(shortcut));
+ }
+
+}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/BubbleDataTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/BubbleDataTest.java
index b0312e6..091022a 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/BubbleDataTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/BubbleDataTest.java
@@ -793,7 +793,7 @@
}
@Test
- public void test_expanded_removeLastBubble_collapsesStack() {
+ public void test_expanded_removeLastBubble_showsOverflowIfNotEmpty() {
// Setup
sendUpdatedEntryAtTime(mEntryA1, 1000);
changeExpandedStateAtTime(true, 2000);
@@ -802,6 +802,21 @@
// Test
mBubbleData.dismissBubbleWithKey(mEntryA1.getKey(), Bubbles.DISMISS_USER_GESTURE);
verifyUpdateReceived();
+ assertThat(mBubbleData.getOverflowBubbles().size()).isGreaterThan(0);
+ assertSelectionChangedTo(mBubbleData.getOverflow());
+ }
+
+ @Test
+ public void test_expanded_removeLastBubble_collapsesIfOverflowEmpty() {
+ // Setup
+ sendUpdatedEntryAtTime(mEntryA1, 1000);
+ changeExpandedStateAtTime(true, 2000);
+ mBubbleData.setListener(mListener);
+
+ // Test
+ mBubbleData.dismissBubbleWithKey(mEntryA1.getKey(), Bubbles.DISMISS_NO_BUBBLE_UP);
+ verifyUpdateReceived();
+ assertThat(mBubbleData.getOverflowBubbles()).isEmpty();
assertExpandedChangedTo(false);
}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/MainStageTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/MainStageTests.java
index 1bb5fd1..12b547a 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/MainStageTests.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/MainStageTests.java
@@ -56,7 +56,7 @@
MockitoAnnotations.initMocks(this);
mRootTaskInfo = new TestRunningTaskInfoBuilder().build();
mMainStage = new MainStage(mTaskOrganizer, DEFAULT_DISPLAY, mCallbacks, mSyncQueue,
- mSurfaceSession);
+ mSurfaceSession, null);
mMainStage.onTaskAppeared(mRootTaskInfo, mRootLeash);
}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SideStageTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SideStageTests.java
index 3a2516e..838aa81 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SideStageTests.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SideStageTests.java
@@ -64,7 +64,7 @@
MockitoAnnotations.initMocks(this);
mRootTask = new TestRunningTaskInfoBuilder().build();
mSideStage = new SideStage(mContext, mTaskOrganizer, DEFAULT_DISPLAY, mCallbacks,
- mSyncQueue, mSurfaceSession);
+ mSyncQueue, mSurfaceSession, null);
mSideStage.onTaskAppeared(mRootTask, mRootLeash);
}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitTestUtils.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitTestUtils.java
index 736566e5..f90af23 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitTestUtils.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitTestUtils.java
@@ -18,7 +18,6 @@
import static android.view.Display.DEFAULT_DISPLAY;
import static android.window.DisplayAreaOrganizer.FEATURE_DEFAULT_TASK_CONTAINER;
-
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
@@ -39,6 +38,10 @@
import com.android.wm.shell.common.split.SplitLayout;
import com.android.wm.shell.transition.Transitions;
+import java.util.Optional;
+
+import javax.inject.Provider;
+
public class SplitTestUtils {
static SplitLayout createMockSplitLayout() {
@@ -68,10 +71,11 @@
MainStage mainStage, SideStage sideStage, DisplayImeController imeController,
DisplayInsetsController insetsController, SplitLayout splitLayout,
Transitions transitions, TransactionPool transactionPool,
- SplitscreenEventLogger logger) {
+ SplitscreenEventLogger logger,
+ Provider<Optional<StageTaskUnfoldController>> unfoldController) {
super(context, displayId, syncQueue, rootTDAOrganizer, taskOrganizer, mainStage,
sideStage, imeController, insetsController, splitLayout, transitions,
- transactionPool, logger);
+ transactionPool, logger, unfoldController);
// Prepare default TaskDisplayArea for testing.
mDisplayAreaInfo = new DisplayAreaInfo(
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitTransitionTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitTransitionTests.java
index 8dce454..be103863 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitTransitionTests.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitTransitionTests.java
@@ -74,6 +74,8 @@
import org.mockito.MockitoAnnotations;
import org.mockito.stubbing.Answer;
+import java.util.Optional;
+
/** Tests for {@link StageCoordinator} */
@SmallTest
@RunWith(AndroidJUnit4.class)
@@ -106,16 +108,16 @@
doReturn(mock(SurfaceControl.Transaction.class)).when(mTransactionPool).acquire();
mSplitLayout = SplitTestUtils.createMockSplitLayout();
mMainStage = new MainStage(mTaskOrganizer, DEFAULT_DISPLAY, mock(
- StageTaskListener.StageListenerCallbacks.class), mSyncQueue, mSurfaceSession);
+ StageTaskListener.StageListenerCallbacks.class), mSyncQueue, mSurfaceSession, null);
mMainStage.onTaskAppeared(new TestRunningTaskInfoBuilder().build(), createMockSurface());
mSideStage = new SideStage(mContext, mTaskOrganizer, DEFAULT_DISPLAY, mock(
- StageTaskListener.StageListenerCallbacks.class), mSyncQueue, mSurfaceSession);
+ StageTaskListener.StageListenerCallbacks.class), mSyncQueue, mSurfaceSession, null);
mSideStage.onTaskAppeared(new TestRunningTaskInfoBuilder().build(), createMockSurface());
mStageCoordinator = new SplitTestUtils.TestStageCoordinator(mContext, DEFAULT_DISPLAY,
mSyncQueue, mRootTDAOrganizer, mTaskOrganizer, mMainStage, mSideStage,
mDisplayImeController, mDisplayInsetsController, mSplitLayout, mTransitions,
mTransactionPool,
- mLogger);
+ mLogger, Optional::empty);
mSplitScreenTransitions = mStageCoordinator.getSplitTransitions();
doAnswer((Answer<IBinder>) invocation -> mock(IBinder.class))
.when(mTransitions).startTransition(anyInt(), any(), any());
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/StageCoordinatorTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/StageCoordinatorTests.java
index d930d4ca..a39d331 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/StageCoordinatorTests.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/StageCoordinatorTests.java
@@ -18,18 +18,20 @@
import static android.app.ActivityTaskManager.INVALID_TASK_ID;
import static android.view.Display.DEFAULT_DISPLAY;
-
import static com.android.internal.util.FrameworkStatsLog.SPLITSCREEN_UICHANGED__EXIT_REASON__RETURN_HOME;
import static com.android.wm.shell.common.split.SplitLayout.SPLIT_POSITION_BOTTOM_OR_RIGHT;
-
+import static com.android.wm.shell.common.split.SplitLayout.SPLIT_POSITION_TOP_OR_LEFT;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.clearInvocations;
import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import android.app.ActivityManager;
import android.graphics.Rect;
+import android.window.DisplayAreaInfo;
import android.window.WindowContainerTransaction;
import androidx.test.ext.junit.runners.AndroidJUnit4;
@@ -43,6 +45,7 @@
import com.android.wm.shell.common.DisplayInsetsController;
import com.android.wm.shell.common.SyncTransactionQueue;
import com.android.wm.shell.common.TransactionPool;
+import com.android.wm.shell.common.split.SplitLayout;
import com.android.wm.shell.transition.Transitions;
import org.junit.Before;
@@ -51,29 +54,55 @@
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
-/** Tests for {@link StageCoordinator} */
+import java.util.Optional;
+
+import javax.inject.Provider;
+
+/**
+ * Tests for {@link StageCoordinator}
+ */
@SmallTest
@RunWith(AndroidJUnit4.class)
public class StageCoordinatorTests extends ShellTestCase {
- @Mock private ShellTaskOrganizer mTaskOrganizer;
- @Mock private SyncTransactionQueue mSyncQueue;
- @Mock private RootTaskDisplayAreaOrganizer mRootTDAOrganizer;
- @Mock private MainStage mMainStage;
- @Mock private SideStage mSideStage;
- @Mock private DisplayImeController mDisplayImeController;
- @Mock private DisplayInsetsController mDisplayInsetsController;
- @Mock private Transitions mTransitions;
- @Mock private TransactionPool mTransactionPool;
- @Mock private SplitscreenEventLogger mLogger;
+ @Mock
+ private ShellTaskOrganizer mTaskOrganizer;
+ @Mock
+ private SyncTransactionQueue mSyncQueue;
+ @Mock
+ private RootTaskDisplayAreaOrganizer mRootTDAOrganizer;
+ @Mock
+ private MainStage mMainStage;
+ @Mock
+ private SideStage mSideStage;
+ @Mock
+ private StageTaskUnfoldController mMainUnfoldController;
+ @Mock
+ private StageTaskUnfoldController mSideUnfoldController;
+ @Mock
+ private SplitLayout mSplitLayout;
+ @Mock
+ private DisplayImeController mDisplayImeController;
+ @Mock
+ private DisplayInsetsController mDisplayInsetsController;
+ @Mock
+ private Transitions mTransitions;
+ @Mock
+ private TransactionPool mTransactionPool;
+ @Mock
+ private SplitscreenEventLogger mLogger;
+
+ private final Rect mBounds1 = new Rect(10, 20, 30, 40);
+ private final Rect mBounds2 = new Rect(5, 10, 15, 20);
+
private StageCoordinator mStageCoordinator;
@Before
public void setup() {
MockitoAnnotations.initMocks(this);
- mStageCoordinator = new SplitTestUtils.TestStageCoordinator(mContext, DEFAULT_DISPLAY,
- mSyncQueue, mRootTDAOrganizer, mTaskOrganizer, mMainStage, mSideStage,
- mDisplayImeController, mDisplayInsetsController, null /* splitLayout */,
- mTransitions, mTransactionPool, mLogger);
+ mStageCoordinator = createStageCoordinator(/* splitLayout */ null);
+
+ when(mSplitLayout.getBounds1()).thenReturn(mBounds1);
+ when(mSplitLayout.getBounds2()).thenReturn(mBounds2);
}
@Test
@@ -88,6 +117,38 @@
}
@Test
+ public void testDisplayAreaAppeared_initializesUnfoldControllers() {
+ mStageCoordinator.onDisplayAreaAppeared(mock(DisplayAreaInfo.class));
+
+ verify(mMainUnfoldController).init();
+ verify(mSideUnfoldController).init();
+ }
+
+ @Test
+ public void testLayoutChanged_topLeftSplitPosition_updatesUnfoldStageBounds() {
+ mStageCoordinator = createStageCoordinator(mSplitLayout);
+ mStageCoordinator.setSideStagePosition(SPLIT_POSITION_TOP_OR_LEFT, null);
+ clearInvocations(mMainUnfoldController, mSideUnfoldController);
+
+ mStageCoordinator.onLayoutChanged(mSplitLayout);
+
+ verify(mMainUnfoldController).onLayoutChanged(mBounds2);
+ verify(mSideUnfoldController).onLayoutChanged(mBounds1);
+ }
+
+ @Test
+ public void testLayoutChanged_bottomRightSplitPosition_updatesUnfoldStageBounds() {
+ mStageCoordinator = createStageCoordinator(mSplitLayout);
+ mStageCoordinator.setSideStagePosition(SPLIT_POSITION_BOTTOM_OR_RIGHT, null);
+ clearInvocations(mMainUnfoldController, mSideUnfoldController);
+
+ mStageCoordinator.onLayoutChanged(mSplitLayout);
+
+ verify(mMainUnfoldController).onLayoutChanged(mBounds1);
+ verify(mSideUnfoldController).onLayoutChanged(mBounds2);
+ }
+
+ @Test
public void testRemoveFromSideStage() {
final ActivityManager.RunningTaskInfo task = new TestRunningTaskInfoBuilder().build();
@@ -131,4 +192,27 @@
verify(mSideStage).removeAllTasks(any(WindowContainerTransaction.class), eq(true));
verify(mMainStage).deactivate(any(WindowContainerTransaction.class), eq(false));
}
+
+ private StageCoordinator createStageCoordinator(SplitLayout splitLayout) {
+ return new SplitTestUtils.TestStageCoordinator(mContext, DEFAULT_DISPLAY,
+ mSyncQueue, mRootTDAOrganizer, mTaskOrganizer, mMainStage, mSideStage,
+ mDisplayImeController, mDisplayInsetsController, splitLayout,
+ mTransitions, mTransactionPool, mLogger, new UnfoldControllerProvider());
+ }
+
+ private class UnfoldControllerProvider implements
+ Provider<Optional<StageTaskUnfoldController>> {
+
+ private boolean isMain = true;
+
+ @Override
+ public Optional<StageTaskUnfoldController> get() {
+ if (isMain) {
+ isMain = false;
+ return Optional.of(mMainUnfoldController);
+ } else {
+ return Optional.of(mSideUnfoldController);
+ }
+ }
+ }
}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/StageTaskListenerTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/StageTaskListenerTests.java
index 0916dd1..a5746a4 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/StageTaskListenerTests.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/StageTaskListenerTests.java
@@ -23,6 +23,7 @@
import static org.junit.Assume.assumeFalse;
import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.clearInvocations;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
@@ -60,6 +61,7 @@
@Mock private ShellTaskOrganizer mTaskOrganizer;
@Mock private StageTaskListener.StageListenerCallbacks mCallbacks;
@Mock private SyncTransactionQueue mSyncQueue;
+ @Mock private StageTaskUnfoldController mStageTaskUnfoldController;
@Captor private ArgumentCaptor<SyncTransactionQueue.TransactionRunnable> mRunnableCaptor;
private SurfaceSession mSurfaceSession = new SurfaceSession();
private SurfaceControl mSurfaceControl;
@@ -74,7 +76,8 @@
DEFAULT_DISPLAY,
mCallbacks,
mSyncQueue,
- mSurfaceSession);
+ mSurfaceSession,
+ mStageTaskUnfoldController);
mRootTask = new TestRunningTaskInfoBuilder().build();
mRootTask.parentTaskId = INVALID_TASK_ID;
mSurfaceControl = new SurfaceControl.Builder(mSurfaceSession).setName("test").build();
@@ -111,6 +114,28 @@
verify(mCallbacks).onStatusChanged(eq(mRootTask.isVisible), eq(true));
}
+ @Test
+ public void testTaskAppeared_notifiesUnfoldListener() {
+ final ActivityManager.RunningTaskInfo task =
+ new TestRunningTaskInfoBuilder().setParentTaskId(mRootTask.taskId).build();
+
+ mStageTaskListener.onTaskAppeared(task, mSurfaceControl);
+
+ verify(mStageTaskUnfoldController).onTaskAppeared(eq(task), eq(mSurfaceControl));
+ }
+
+ @Test
+ public void testTaskVanished_notifiesUnfoldListener() {
+ final ActivityManager.RunningTaskInfo task =
+ new TestRunningTaskInfoBuilder().setParentTaskId(mRootTask.taskId).build();
+ mStageTaskListener.onTaskAppeared(task, mSurfaceControl);
+ clearInvocations(mStageTaskUnfoldController);
+
+ mStageTaskListener.onTaskVanished(task);
+
+ verify(mStageTaskUnfoldController).onTaskVanished(eq(task));
+ }
+
@Test(expected = IllegalArgumentException.class)
public void testUnknownTaskVanished() {
final ActivityManager.RunningTaskInfo task = new TestRunningTaskInfoBuilder().build();
diff --git a/libs/hwui/Android.bp b/libs/hwui/Android.bp
index 2c299fa..2b31bcf7 100644
--- a/libs/hwui/Android.bp
+++ b/libs/hwui/Android.bp
@@ -570,6 +570,7 @@
"renderthread/DrawFrameTask.cpp",
"renderthread/EglManager.cpp",
"renderthread/ReliableSurface.cpp",
+ "renderthread/RenderEffectCapabilityQuery.cpp",
"renderthread/VulkanManager.cpp",
"renderthread/VulkanSurface.cpp",
"renderthread/RenderProxy.cpp",
@@ -696,6 +697,7 @@
"tests/unit/MatrixTests.cpp",
"tests/unit/OpBufferTests.cpp",
"tests/unit/PathInterpolatorTests.cpp",
+ "tests/unit/RenderEffectCapabilityQueryTests.cpp",
"tests/unit/RenderNodeDrawableTests.cpp",
"tests/unit/RenderNodeTests.cpp",
"tests/unit/RenderPropertiesTests.cpp",
diff --git a/libs/hwui/Properties.cpp b/libs/hwui/Properties.cpp
index 3544987..475fd70 100644
--- a/libs/hwui/Properties.cpp
+++ b/libs/hwui/Properties.cpp
@@ -50,7 +50,8 @@
bool Properties::skipEmptyFrames = true;
bool Properties::useBufferAge = true;
bool Properties::enablePartialUpdates = true;
-bool Properties::enableRenderEffectCache = false;
+// Default true unless otherwise specified in RenderThread Configuration
+bool Properties::enableRenderEffectCache = true;
DebugLevel Properties::debugLevel = kDebugDisabled;
OverdrawColorSet Properties::overdrawColorSet = OverdrawColorSet::Default;
diff --git a/libs/hwui/renderthread/EglManager.cpp b/libs/hwui/renderthread/EglManager.cpp
index 383c79b..c7d7a17 100644
--- a/libs/hwui/renderthread/EglManager.cpp
+++ b/libs/hwui/renderthread/EglManager.cpp
@@ -28,6 +28,7 @@
#include "Frame.h"
#include "Properties.h"
+#include "RenderEffectCapabilityQuery.h"
#include "utils/Color.h"
#include "utils/StringUtils.h"
@@ -148,7 +149,11 @@
mHasWideColorGamutSupport = EglExtensions.glColorSpace && hasWideColorSpaceExtension;
auto* vendor = reinterpret_cast<const char*>(glGetString(GL_VENDOR));
- Properties::enableRenderEffectCache = (strcmp(vendor, "Qualcomm") != 0);
+ auto* version = reinterpret_cast<const char*>(glGetString(GL_VERSION));
+ Properties::enableRenderEffectCache = supportsRenderEffectCache(
+ vendor, version);
+ ALOGV("RenderEffectCache supported %d on driver version %s",
+ Properties::enableRenderEffectCache, version);
}
EGLConfig EglManager::load8BitsConfig(EGLDisplay display, EglManager::SwapBehavior swapBehavior) {
diff --git a/libs/hwui/renderthread/RenderEffectCapabilityQuery.cpp b/libs/hwui/renderthread/RenderEffectCapabilityQuery.cpp
new file mode 100644
index 0000000..a003988
--- /dev/null
+++ b/libs/hwui/renderthread/RenderEffectCapabilityQuery.cpp
@@ -0,0 +1,39 @@
+/*
+ * 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.
+ */
+#include <stdio.h>
+#include <string.h>
+#include <utils/Log.h>
+
+bool supportsRenderEffectCache(const char* vendor, const char* version) {
+ if (strcmp(vendor, "Qualcomm") != 0) {
+ return true;
+ }
+
+ int major;
+ int minor;
+ int driverMajor;
+ int driverMinor;
+ int n = sscanf(version,"OpenGL ES %d.%d V@%d.%d",
+ &major,
+ &minor,
+ &driverMajor,
+ &driverMinor);
+ // Ensure we have parsed the vendor string properly and we have either
+ // a newer major driver version, or the minor version is rev'ed
+ // Based on b/198227600#comment5 it appears that the corresponding fix
+ // is in driver version 571.0
+ return n == 4 && driverMajor >= 571;
+}
\ No newline at end of file
diff --git a/libs/hwui/renderthread/RenderEffectCapabilityQuery.h b/libs/hwui/renderthread/RenderEffectCapabilityQuery.h
new file mode 100644
index 0000000..ea673dd
--- /dev/null
+++ b/libs/hwui/renderthread/RenderEffectCapabilityQuery.h
@@ -0,0 +1,35 @@
+/*
+ * 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.
+ */
+
+#pragma once
+
+/**
+ * Verify if the provided vendor and version supports RenderEffect caching
+ * behavior.
+ *
+ * Certain Open GL Driver implementations run into blocking scenarios
+ * with Fence::waitForever without a corresponding signal to unblock
+ * This happens during attempts to cache SkImage instances across frames
+ * especially in circumstances using RenderEffect/SkImageFilter internally.
+ * So detect the corresponding GL Vendor and driver version to determine if
+ * caching SkImage instances across frames is supported.
+ * See b/197263715 & b/193145089
+ * @param vendor Vendor of the GL driver
+ * @param version Version of the GL driver from the given vendor
+ * @return True if a RenderEffect result can be cached across frames,
+ * false otherwise
+ */
+bool supportsRenderEffectCache(const char* vendor, const char* version);
diff --git a/libs/hwui/tests/unit/EglManagerTests.cpp b/libs/hwui/tests/unit/EglManagerTests.cpp
index f7f2406..7f2e158 100644
--- a/libs/hwui/tests/unit/EglManagerTests.cpp
+++ b/libs/hwui/tests/unit/EglManagerTests.cpp
@@ -17,6 +17,7 @@
#include <gtest/gtest.h>
#include "renderthread/EglManager.h"
+#include "renderthread/RenderEffectCapabilityQuery.h"
#include "tests/common/TestContext.h"
using namespace android;
@@ -41,4 +42,17 @@
}
eglManager.destroy();
+}
+
+TEST(EglManager, verifyRenderEffectCacheSupported) {
+ EglManager eglManager;
+ eglManager.initialize();
+ auto* vendor = reinterpret_cast<const char*>(glGetString(GL_VENDOR));
+ auto* version = reinterpret_cast<const char*>(glGetString(GL_VERSION));
+ // Make sure that EglManager initializes Properties::enableRenderEffectCache
+ // based on the given gl vendor and version within EglManager->initialize()
+ bool renderEffectCacheSupported = supportsRenderEffectCache(vendor, version);
+ EXPECT_EQ(renderEffectCacheSupported,
+ Properties::enableRenderEffectCache);
+ eglManager.destroy();
}
\ No newline at end of file
diff --git a/libs/hwui/tests/unit/RenderEffectCapabilityQueryTests.cpp b/libs/hwui/tests/unit/RenderEffectCapabilityQueryTests.cpp
new file mode 100644
index 0000000..0ee6549
--- /dev/null
+++ b/libs/hwui/tests/unit/RenderEffectCapabilityQueryTests.cpp
@@ -0,0 +1,52 @@
+/*
+ * 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.
+ */
+
+#include <gtest/gtest.h>
+#include "renderthread/RenderEffectCapabilityQuery.h"
+#include "tests/common/TestContext.h"
+
+TEST(RenderEffectCapabilityQuery, testSupportedVendor) {
+ ASSERT_TRUE(supportsRenderEffectCache("Google", "OpenGL ES 1.4 V@0.0"));
+}
+
+TEST(RenderEffectCapabilityQuery, testSupportedVendorWithDifferentVersion) {
+ ASSERT_TRUE(supportsRenderEffectCache("Google", "OpenGL ES 1.3 V@571.0"));
+}
+
+TEST(RenderEffectCapabilityQuery, testVendorWithSupportedVersion) {
+ ASSERT_TRUE(supportsRenderEffectCache("Qualcomm", "OpenGL ES 1.5 V@571.0"));
+}
+
+TEST(RenderEffectCapabilityQuery, testVendorWithSupportedPatchVersion) {
+ ASSERT_TRUE(supportsRenderEffectCache("Qualcomm", "OpenGL ES 1.5 V@571.1"));
+}
+
+TEST(RenderEffectCapabilityQuery, testVendorWithNewerThanSupportedMajorVersion) {
+ ASSERT_TRUE(supportsRenderEffectCache("Qualcomm", "OpenGL ES 1.5 V@572.0"));
+}
+
+TEST(RenderEffectCapabilityQuery, testVendorWithNewerThanSupportedMinorVersion) {
+ ASSERT_TRUE(supportsRenderEffectCache("Qualcomm", "OpenGL ES 1.5 V@571.2"));
+}
+
+TEST(RenderEffectCapabilityQuery, testVendorWithUnsupportedMajorVersion) {
+ ASSERT_FALSE(supportsRenderEffectCache("Qualcomm", "OpenGL ES 1.0 V@570.1"));
+}
+
+TEST(RenderEffectCapabilityQuery, testVendorWithUnsupportedVersion) {
+ ASSERT_FALSE(supportsRenderEffectCache("Qualcomm", "OpenGL ES 1.1 V@570.0"));
+}
+
diff --git a/media/java/android/media/AudioTrack.java b/media/java/android/media/AudioTrack.java
index 23d9532..aa72d965 100644
--- a/media/java/android/media/AudioTrack.java
+++ b/media/java/android/media/AudioTrack.java
@@ -1613,7 +1613,9 @@
AudioFormat.CHANNEL_OUT_BOTTOM_FRONT_LEFT |
AudioFormat.CHANNEL_OUT_BOTTOM_FRONT_CENTER |
AudioFormat.CHANNEL_OUT_BOTTOM_FRONT_RIGHT |
- AudioFormat.CHANNEL_OUT_LOW_FREQUENCY_2;
+ AudioFormat.CHANNEL_OUT_LOW_FREQUENCY_2 |
+ AudioFormat.CHANNEL_OUT_FRONT_WIDE_LEFT |
+ AudioFormat.CHANNEL_OUT_FRONT_WIDE_RIGHT;
// Returns a boolean whether the attributes, format, bufferSizeInBytes, mode allow
// power saving to be automatically enabled for an AudioTrack. Returns false if
@@ -1787,6 +1789,8 @@
| AudioFormat.CHANNEL_OUT_TOP_SIDE_RIGHT);
put("bottom front", AudioFormat.CHANNEL_OUT_BOTTOM_FRONT_LEFT
| AudioFormat.CHANNEL_OUT_BOTTOM_FRONT_RIGHT);
+ put("front wide", AudioFormat.CHANNEL_OUT_FRONT_WIDE_LEFT
+ | AudioFormat.CHANNEL_OUT_FRONT_WIDE_RIGHT);
}};
/**
diff --git a/packages/SettingsLib/res/values-eu/strings.xml b/packages/SettingsLib/res/values-eu/strings.xml
index a7f5eac5..de97437 100644
--- a/packages/SettingsLib/res/values-eu/strings.xml
+++ b/packages/SettingsLib/res/values-eu/strings.xml
@@ -304,7 +304,7 @@
<string name="adb_warning_message" msgid="8145270656419669221">"USB bidezko arazketa garapen-xedeetarako soilik dago diseinatuta. Erabil ezazu ordenagailuaren eta gailuaren artean datuak kopiatzeko, aplikazioak gailuan jakinarazi gabe instalatzeko eta erregistro-datuak irakurtzeko."</string>
<string name="adbwifi_warning_title" msgid="727104571653031865">"Hari gabeko arazketa baimendu nahi duzu?"</string>
<string name="adbwifi_warning_message" msgid="8005936574322702388">"Hari gabeko arazketa garapen-xedeetarako soilik dago diseinatuta. Erabil ezazu ordenagailuaren eta gailuaren artean datuak kopiatzeko, gailuan aplikazioak jakinarazi gabe instalatzeko eta erregistroko datuak irakurtzeko."</string>
- <string name="adb_keys_warning_message" msgid="2968555274488101220">"Aurretik baimendutako ordenagailu guztiei USB bidezko arazketarako sarbidea baliogabetu nahi diezu?"</string>
+ <string name="adb_keys_warning_message" msgid="2968555274488101220">"Aurretik baimendutako ordenagailu guztiei USB bidezko arazketarako sarbidea kendu nahi diezu?"</string>
<string name="dev_settings_warning_title" msgid="8251234890169074553">"Baimendu garapenerako ezarpenak?"</string>
<string name="dev_settings_warning_message" msgid="37741686486073668">"Ezarpen hauek garapen-xedeetarako pentsatu dira soilik. Baliteke ezarpenen eraginez gailua matxuratzea edo funtzionamendu okerra izatea."</string>
<string name="verify_apps_over_usb_title" msgid="6031809675604442636">"Egiaztatu USB bidezko aplik."</string>
diff --git a/packages/SettingsLib/res/values-ky/strings.xml b/packages/SettingsLib/res/values-ky/strings.xml
index 7387985..29510a7 100644
--- a/packages/SettingsLib/res/values-ky/strings.xml
+++ b/packages/SettingsLib/res/values-ky/strings.xml
@@ -305,7 +305,7 @@
<string name="adbwifi_warning_title" msgid="727104571653031865">"Мүчүлүштүктөрдү Wi-Fi аркылуу оңдоого уруксат бересизби?"</string>
<string name="adbwifi_warning_message" msgid="8005936574322702388">"Мүчүлүштүктөрдү Wi-Fi аркылуу аныктоо – өндүрүү максатында гана түзүлгөн. Аны компьютериңиз менен түзмөгүңүздүн ортосунда маалыматты алмашуу, колдонмолорду түзмөгүңүзгө эскертүүсүз орнотуу жана маалыматтар таржымалын окуу үчүн колдонсоңуз болот."</string>
<string name="adb_keys_warning_message" msgid="2968555274488101220">"Сиз мурун USB жөндөөлөрүнө уруксат берген бардык компүтерлердин жеткиси жокко чыгарылсынбы?"</string>
- <string name="dev_settings_warning_title" msgid="8251234890169074553">"Жөндөөлөрдү өзгөртүү"</string>
+ <string name="dev_settings_warning_title" msgid="8251234890169074553">"Параметрлерди өзгөртүү"</string>
<string name="dev_settings_warning_message" msgid="37741686486073668">"Бул орнотуулар өндүрүүчүлөр үчүн гана берилген. Булар түзмөгүңүздүн колдонмолорун бузулушуна же туура эмес иштешине алып келиши мүмкүн."</string>
<string name="verify_apps_over_usb_title" msgid="6031809675604442636">"Орнотулуучу колдонмону текшерүү"</string>
<string name="verify_apps_over_usb_summary" msgid="1317933737581167839">"ADB/ADT аркылуу орнотулган колдонмолордун коопсуздугу текшерилет."</string>
diff --git a/packages/SettingsLib/res/values-or/strings.xml b/packages/SettingsLib/res/values-or/strings.xml
index c885c16..8938fcf 100644
--- a/packages/SettingsLib/res/values-or/strings.xml
+++ b/packages/SettingsLib/res/values-or/strings.xml
@@ -155,7 +155,7 @@
<string name="running_process_item_user_label" msgid="3988506293099805796">"ଉପଯୋଗକର୍ତ୍ତା: <xliff:g id="USER_NAME">%1$s</xliff:g>"</string>
<string name="launch_defaults_some" msgid="3631650616557252926">"କିଛି ପୂର୍ବ-ନିର୍ଦ୍ଧାରିତ ମାନ ସେଟ୍ ହୋଇଛି"</string>
<string name="launch_defaults_none" msgid="8049374306261262709">"କୌଣସି ଡିଫଲ୍ଟ ସେଟ୍ ହୋଇନାହିଁ"</string>
- <string name="tts_settings" msgid="8130616705989351312">"ଟେକ୍ସଟ-ରୁ-ସ୍ପିଚ୍ ସେଟିଂସ୍"</string>
+ <string name="tts_settings" msgid="8130616705989351312">"ଟେକ୍ସଟ୍-ଟୁ-ସ୍ପିଚ୍ ସେଟିଂସ"</string>
<string name="tts_settings_title" msgid="7602210956640483039">"ଟେକ୍ସଟ୍-ଟୁ-ସ୍ପିଚ୍ ଆଉଟ୍ପୁଟ୍"</string>
<string name="tts_default_rate_title" msgid="3964187817364304022">"ସ୍ପିଚ୍ ରେଟ୍"</string>
<string name="tts_default_rate_summary" msgid="3781937042151716987">"ଲେଖା ପଢ଼ିବାର ବେଗ"</string>
diff --git a/packages/SystemUI/animation/src/com/android/systemui/animation/Interpolators.java b/packages/SystemUI/animation/src/com/android/systemui/animation/Interpolators.java
index 2765882..a3d924f 100644
--- a/packages/SystemUI/animation/src/com/android/systemui/animation/Interpolators.java
+++ b/packages/SystemUI/animation/src/com/android/systemui/animation/Interpolators.java
@@ -200,10 +200,10 @@
/**
* Interpolate alpha for notifications background scrim during shade expansion.
* @param fraction Shade expansion fraction
- * @param forNotification If we want the alpha of the notification shade or the scrim.
+ * @param forUiContent If we want the alpha of the scrims, or ui that's on top of them.
*/
- public static float getNotificationScrimAlpha(float fraction, boolean forNotification) {
- if (forNotification) {
+ public static float getNotificationScrimAlpha(float fraction, boolean forUiContent) {
+ if (forUiContent) {
fraction = MathUtils.constrainedMap(0f, 1f, 0.3f, 1f, fraction);
} else {
fraction = MathUtils.constrainedMap(0f, 1f, 0f, 0.5f, fraction);
diff --git a/packages/SystemUI/docs/keyguard.md b/packages/SystemUI/docs/keyguard.md
new file mode 100644
index 0000000..5e7bc1c
--- /dev/null
+++ b/packages/SystemUI/docs/keyguard.md
@@ -0,0 +1,46 @@
+# Keyguard (aka Lockscreen)
+
+Keyguard is responsible for:
+
+1. Handling authentication to allow the user to unlock the device, via biometrics or [KeyguardBouncer][1]
+2. Displaying informational content such as the time, notifications, and smartspace
+3. Always-on Display (AOD)
+
+Keyguard is the first screen available when turning on the device, as long as the user has not specified a security method of NONE.
+
+## Critical User Journeys
+
+The journeys below generally refer to Keyguard's portion of the overall flow, especially regarding use of the power button. Power button key interpretation (short press, long press, very long press, multi press) is done in [PhoneWindowManager][4], with calls to [PowerManagerService][2] to sleep or wake up, if needed.
+
+### Power On - AOD enabled or disabled
+
+Begins with the device in low power mode, with the display active for [AOD][3] or inactive. [PowerManagerService][2] can be directed to wake up on various user-configurable signals, such as lift to wake, screen taps, among others. [AOD][2], whether visibly enabled or not, handles these signals to transition AOD to full Lockscreen content. See more in [AOD][3].
+
+### Power Off
+
+An indication to power off the device most likely comes from one of two signals: the user presses the power button or the screen timeout has passed. This may [lock the device](#How-the-device-locks)
+
+#### On Lockscreen
+
+#### On Lockscreen, occluded by an activity
+
+#### Device unlocked, Keyguard has gone away
+
+### Pulsing (Incoming notifications while dozing)
+
+### How the device locks
+
+More coming
+* Screen timeout
+* Smart lock
+* Device policy
+* Power button instantly locks setting
+* Lock timeout after screen timeout setting
+
+
+[1]: /frameworks/base/packages/SystemUI/docs/keyguard/bouncer.md
+[2]: /frameworks/base/services/core/java/com/android/server/power/PowerManagerService.java
+[3]: /frameworks/base/packages/SystemUI/docs/keyguard/aod.md
+[4]: /frameworks/base/services/core/java/com/android/server/policy/PhoneWindowManager.java
+
+
diff --git a/packages/SystemUI/docs/keyguard/aod.md b/packages/SystemUI/docs/keyguard/aod.md
new file mode 100644
index 0000000..6d76ed55
--- /dev/null
+++ b/packages/SystemUI/docs/keyguard/aod.md
@@ -0,0 +1 @@
+# Always-on Display (AOD)
diff --git a/packages/SystemUI/docs/keyguard/bouncer.md b/packages/SystemUI/docs/keyguard/bouncer.md
new file mode 100644
index 0000000..51f8516
--- /dev/null
+++ b/packages/SystemUI/docs/keyguard/bouncer.md
@@ -0,0 +1,18 @@
+# Bouncer
+
+[KeyguardBouncer][1] is the component responsible for displaying the security method set by the user (password, PIN, pattern) as well as SIM-related security methods, allowing the user to unlock the device or SIM.
+
+## Components
+
+The bouncer contains a hierarchy of controllers/views to render the user's security method and to manage the authentication attempts.
+
+1. [KeyguardBouncer][1] - Entrypoint for managing the bouncer visibility.
+ 1. [KeyguardHostViewController][2] - Intercepts media keys. Can most likely be merged with the next item.
+ 1. [KeyguardSecurityContainerController][3] - Manages unlock attempt responses, one-handed use
+ 1. [KeyguardSecurityViewFlipperController][4] - Based upon the [KeyguardSecurityModel#SecurityMode][5], will instantiate the required view and controller. PIN, Pattern, etc.
+
+[1]: /frameworks/base/packages/SystemUI/com/android/systemui/statusbar/phone/KeyguardBouncer
+[2]: /frameworks/base/packages/SystemUI/com/android/keyguard/KeyguardHostViewController
+[3]: /frameworks/base/packages/SystemUI/com/android/keyguard/KeyguardSecurityContainerController
+[4]: /frameworks/base/packages/SystemUI/com/android/keyguard/KeyguardSecurityViewFlipperController
+[5]: /frameworks/base/packages/SystemUI/com/android/keyguard/KeyguardSecurityModel
diff --git a/packages/SystemUI/plugin/src/com/android/systemui/plugins/qs/QS.java b/packages/SystemUI/plugin/src/com/android/systemui/plugins/qs/QS.java
index 0424382..757dc2e 100644
--- a/packages/SystemUI/plugin/src/com/android/systemui/plugins/qs/QS.java
+++ b/packages/SystemUI/plugin/src/com/android/systemui/plugins/qs/QS.java
@@ -16,7 +16,6 @@
import android.view.View;
import android.view.View.OnClickListener;
-import android.view.ViewGroup;
import com.android.systemui.plugins.FragmentBase;
import com.android.systemui.plugins.annotations.DependsOn;
@@ -53,10 +52,19 @@
boolean isShowingDetail();
void closeDetail();
void animateHeaderSlidingOut();
- void setQsExpansion(float qsExpansionFraction, float headerTranslation);
+
+ /**
+ * Asks QS to update its presentation, according to {@code NotificationPanelViewController}.
+ *
+ * @param qsExpansionFraction How much each UI element in QS should be expanded (QQS to QS.)
+ * @param panelExpansionFraction Whats the expansion of the whole shade.
+ * @param headerTranslation How much we should vertically translate QS.
+ */
+ void setQsExpansion(float qsExpansionFraction, float panelExpansionFraction,
+ float headerTranslation);
void setHeaderListening(boolean listening);
void notifyCustomizeChanged();
- void setContainer(ViewGroup container);
+ void setContainerController(QSContainerController controller);
void setExpandClickListener(OnClickListener onClickListener);
View getHeader();
@@ -76,13 +84,13 @@
/**
* If QS should translate as we pull it down, or if it should be static.
*/
- void setTranslateWhileExpanding(boolean shouldTranslate);
+ void setInSplitShade(boolean shouldTranslate);
/**
* Set the amount of pixels we have currently dragged down if we're transitioning to the full
* shade. 0.0f means we're not transitioning yet.
*/
- default void setTransitionToFullShadeAmount(float pxAmount, boolean animated) {}
+ default void setTransitionToFullShadeAmount(float pxAmount, float progress) {}
/**
* A rounded corner clipping that makes QS feel as if it were behind everything.
diff --git a/packages/SystemUI/plugin/src/com/android/systemui/plugins/qs/QSContainerController.kt b/packages/SystemUI/plugin/src/com/android/systemui/plugins/qs/QSContainerController.kt
new file mode 100644
index 0000000..8bf982d
--- /dev/null
+++ b/packages/SystemUI/plugin/src/com/android/systemui/plugins/qs/QSContainerController.kt
@@ -0,0 +1,9 @@
+package com.android.systemui.plugins.qs
+
+interface QSContainerController {
+ fun setCustomizerAnimating(animating: Boolean)
+
+ fun setCustomizerShowing(showing: Boolean)
+
+ fun setDetailShowing(showing: Boolean)
+}
\ No newline at end of file
diff --git a/packages/SystemUI/plugin/src/com/android/systemui/plugins/statusbar/StatusBarStateController.java b/packages/SystemUI/plugin/src/com/android/systemui/plugins/statusbar/StatusBarStateController.java
index 6c5c4ef..9829918 100644
--- a/packages/SystemUI/plugin/src/com/android/systemui/plugins/statusbar/StatusBarStateController.java
+++ b/packages/SystemUI/plugin/src/com/android/systemui/plugins/statusbar/StatusBarStateController.java
@@ -109,9 +109,8 @@
* Callback to be notified when the fullscreen or immersive state changes.
*
* @param isFullscreen if any of the system bar is hidden by the focused window.
- * @param isImmersive if the navigation bar can stay hidden when the display gets tapped.
*/
- default void onFullscreenStateChanged(boolean isFullscreen, boolean isImmersive) {}
+ default void onFullscreenStateChanged(boolean isFullscreen) {}
/**
* Callback to be notified when the pulsing state changes
diff --git a/packages/SystemUI/res/color/prv_color_surface.xml b/packages/SystemUI/res/color/prv_color_surface.xml
new file mode 100644
index 0000000..b9d016c
--- /dev/null
+++ b/packages/SystemUI/res/color/prv_color_surface.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2021 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+<selector xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
+ xmlns:android="http://schemas.android.com/apk/res/android">
+ <item android:color="?androidprv:attr/colorSurface" />
+</selector>
\ No newline at end of file
diff --git a/packages/SystemUI/res/color/prv_text_color_on_accent.xml b/packages/SystemUI/res/color/prv_text_color_on_accent.xml
new file mode 100644
index 0000000..9f44aca
--- /dev/null
+++ b/packages/SystemUI/res/color/prv_text_color_on_accent.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2021 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+<selector xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
+ xmlns:android="http://schemas.android.com/apk/res/android">
+ <item android:color="?androidprv:attr/textColorOnAccent" />
+</selector>
\ No newline at end of file
diff --git a/packages/SystemUI/res/drawable/media_output_dialog_button_background.xml b/packages/SystemUI/res/drawable/media_output_dialog_button_background.xml
new file mode 100644
index 0000000..363a022
--- /dev/null
+++ b/packages/SystemUI/res/drawable/media_output_dialog_button_background.xml
@@ -0,0 +1,29 @@
+<!--
+ ~ 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.
+ -->
+<shape xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
+ android:shape="rectangle">
+ <stroke
+ android:color="?androidprv:attr/colorAccentPrimaryVariant"
+ android:width="1dp"/>
+ <corners android:radius="20dp"/>
+ <padding
+ android:left="16dp"
+ android:right="16dp"
+ android:top="8dp"
+ android:bottom="8dp" />
+ <solid android:color="@android:color/transparent" />
+</shape>
diff --git a/packages/SystemUI/res/drawable/qs_dialog_btn_filled.xml b/packages/SystemUI/res/drawable/qs_dialog_btn_filled.xml
new file mode 100644
index 0000000..1a128df
--- /dev/null
+++ b/packages/SystemUI/res/drawable/qs_dialog_btn_filled.xml
@@ -0,0 +1,39 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2021 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+<inset xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
+ android:insetTop="@dimen/qs_dialog_button_vertical_inset"
+ android:insetBottom="@dimen/qs_dialog_button_vertical_inset">
+ <ripple android:color="?android:attr/colorControlHighlight">
+ <item android:id="@android:id/mask">
+ <shape android:shape="rectangle">
+ <solid android:color="@android:color/white"/>
+ <corners android:radius="?android:attr/buttonCornerRadius"/>
+ </shape>
+ </item>
+ <item>
+ <shape android:shape="rectangle">
+ <corners android:radius="?android:attr/buttonCornerRadius"/>
+ <solid android:color="?androidprv:attr/colorAccentPrimary"/>
+ <padding android:left="@dimen/qs_dialog_button_horizontal_padding"
+ android:top="@dimen/qs_dialog_button_vertical_padding"
+ android:right="@dimen/qs_dialog_button_horizontal_padding"
+ android:bottom="@dimen/qs_dialog_button_vertical_padding"/>
+ </shape>
+ </item>
+ </ripple>
+</inset>
diff --git a/packages/SystemUI/res/drawable/qs_dialog_btn_outline.xml b/packages/SystemUI/res/drawable/qs_dialog_btn_outline.xml
new file mode 100644
index 0000000..467c20f
--- /dev/null
+++ b/packages/SystemUI/res/drawable/qs_dialog_btn_outline.xml
@@ -0,0 +1,42 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2021 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+<inset xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
+ android:insetTop="@dimen/qs_dialog_button_vertical_inset"
+ android:insetBottom="@dimen/qs_dialog_button_vertical_inset">
+ <ripple android:color="?android:attr/colorControlHighlight">
+ <item android:id="@android:id/mask">
+ <shape android:shape="rectangle">
+ <solid android:color="@android:color/white"/>
+ <corners android:radius="?android:attr/buttonCornerRadius"/>
+ </shape>
+ </item>
+ <item>
+ <shape android:shape="rectangle">
+ <corners android:radius="?android:attr/buttonCornerRadius"/>
+ <solid android:color="@android:color/transparent"/>
+ <stroke android:color="?androidprv:attr/colorAccentPrimary"
+ android:width="1dp"
+ />
+ <padding android:left="@dimen/qs_dialog_button_horizontal_padding"
+ android:top="@dimen/qs_dialog_button_vertical_padding"
+ android:right="@dimen/qs_dialog_button_horizontal_padding"
+ android:bottom="@dimen/qs_dialog_button_vertical_padding"/>
+ </shape>
+ </item>
+ </ripple>
+</inset>
diff --git a/packages/SystemUI/res/layout/media_output_dialog.xml b/packages/SystemUI/res/layout/media_output_dialog.xml
index b338894..cd6bc11 100644
--- a/packages/SystemUI/res/layout/media_output_dialog.xml
+++ b/packages/SystemUI/res/layout/media_output_dialog.xml
@@ -24,41 +24,44 @@
<LinearLayout
android:layout_width="match_parent"
- android:layout_height="94dp"
+ android:layout_height="96dp"
android:gravity="start|center_vertical"
android:paddingStart="16dp"
android:orientation="horizontal">
<ImageView
android:id="@+id/header_icon"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:paddingEnd="@dimen/media_output_dialog_header_icon_padding"
+ android:layout_width="48dp"
+ android:layout_height="48dp"
android:importantForAccessibility="no"/>
<LinearLayout
android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_marginEnd="16dp"
+ android:layout_height="match_parent"
+ android:paddingStart="16dp"
+ android:paddingTop="20dp"
+ android:paddingBottom="24dp"
+ android:paddingEnd="24dp"
android:orientation="vertical">
<TextView
android:id="@+id/header_title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:ellipsize="end"
+ android:gravity="center_vertical"
android:maxLines="1"
android:textColor="?android:attr/textColorPrimary"
android:fontFamily="@*android:string/config_headlineFontFamilyMedium"
android:textSize="20sp"/>
-
<TextView
android:id="@+id/header_subtitle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
+ android:gravity="center_vertical"
android:ellipsize="end"
android:maxLines="1"
+ android:textColor="?android:attr/textColorTertiary"
android:fontFamily="roboto-regular"
- android:textSize="14sp"/>
-
+ android:textSize="16sp"/>
</LinearLayout>
</LinearLayout>
@@ -81,21 +84,21 @@
android:overScrollMode="never"/>
</LinearLayout>
- <View
- android:layout_width="match_parent"
- android:layout_height="1dp"
- android:background="?android:attr/listDivider"/>
-
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
+ android:layout_marginTop="16dp"
+ android:layout_marginStart="24dp"
+ android:layout_marginBottom="18dp"
+ android:layout_marginEnd="24dp"
android:orientation="horizontal">
<Button
android:id="@+id/stop"
- style="@*android:style/Widget.DeviceDefault.Button.Borderless.Colored"
+ style="@style/MediaOutputRoundedOutlinedButton"
android:layout_width="wrap_content"
- android:layout_height="64dp"
+ android:layout_height="36dp"
+ android:minWidth="0dp"
android:text="@string/keyboard_key_media_stop"
android:visibility="gone"/>
@@ -106,10 +109,10 @@
<Button
android:id="@+id/done"
- style="@*android:style/Widget.DeviceDefault.Button.Borderless.Colored"
+ style="@style/MediaOutputRoundedOutlinedButton"
android:layout_width="wrap_content"
- android:layout_height="64dp"
- android:layout_marginEnd="0dp"
+ android:layout_height="36dp"
+ android:minWidth="0dp"
android:text="@string/inline_done_button"/>
</LinearLayout>
</LinearLayout>
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout/media_output_list_item.xml b/packages/SystemUI/res/layout/media_output_list_item.xml
index 16c03e1..a5a7efa 100644
--- a/packages/SystemUI/res/layout/media_output_list_item.xml
+++ b/packages/SystemUI/res/layout/media_output_list_item.xml
@@ -23,17 +23,20 @@
android:orientation="vertical">
<FrameLayout
android:layout_width="match_parent"
- android:layout_height="64dp">
+ android:layout_height="88dp"
+ android:paddingTop="24dp"
+ android:paddingBottom="16dp"
+ android:paddingStart="24dp"
+ android:paddingEnd="8dp">
<FrameLayout
- android:layout_width="36dp"
- android:layout_height="36dp"
- android:layout_gravity="center_vertical|start"
- android:layout_marginStart="16dp">
+ android:layout_width="48dp"
+ android:layout_height="48dp"
+ android:layout_gravity="center_vertical|start">
<ImageView
android:id="@+id/title_icon"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
+ android:layout_width="48dp"
+ android:layout_height="48dp"
android:layout_gravity="center"/>
</FrameLayout>
@@ -42,49 +45,69 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical|start"
- android:layout_marginStart="68dp"
+ android:layout_marginStart="64dp"
android:ellipsize="end"
android:maxLines="1"
android:textColor="?android:attr/textColorPrimary"
- android:textSize="14sp"/>
+ android:textSize="16sp"/>
<RelativeLayout
android:id="@+id/two_line_layout"
android:layout_width="wrap_content"
android:layout_height="48dp"
- android:layout_marginStart="52dp"
- android:layout_marginEnd="69dp"
- android:layout_marginTop="10dp">
+ android:layout_marginStart="48dp">
<TextView
android:id="@+id/two_line_title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
- android:layout_marginEnd="15dp"
+ android:layout_marginEnd="48dp"
android:ellipsize="end"
android:maxLines="1"
android:textColor="?android:attr/textColorPrimary"
- android:textSize="14sp"/>
+ android:textSize="16sp"/>
<TextView
android:id="@+id/subtitle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
android:layout_marginEnd="15dp"
- android:layout_marginBottom="7dp"
+ android:layout_marginTop="4dp"
android:layout_alignParentBottom="true"
android:ellipsize="end"
android:maxLines="1"
android:textColor="?android:attr/textColorSecondary"
- android:textSize="12sp"
+ android:textSize="14sp"
android:fontFamily="roboto-regular"
android:visibility="gone"/>
<SeekBar
android:id="@+id/volume_seekbar"
+ android:layout_marginTop="16dp"
+ android:layout_marginEnd="8dp"
style="@*android:style/Widget.DeviceDefault.SeekBar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"/>
+ <ImageView
+ android:id="@+id/add_icon"
+ android:layout_width="24dp"
+ android:layout_height="24dp"
+ android:layout_gravity="right"
+ android:layout_marginEnd="24dp"
+ android:layout_alignParentRight="true"
+ android:src="@drawable/ic_add"
+ android:tint="?android:attr/colorAccent"
+ />
+ <CheckBox
+ android:id="@+id/check_box"
+ android:layout_width="24dp"
+ android:layout_height="24dp"
+ android:layout_gravity="right"
+ android:layout_marginEnd="24dp"
+ android:layout_alignParentRight="true"
+ android:button="@drawable/ic_check_box"
+ android:visibility="gone"
+ />
</RelativeLayout>
<ProgressBar
@@ -92,47 +115,17 @@
style="@*android:style/Widget.Material.ProgressBar.Horizontal"
android:layout_width="258dp"
android:layout_height="18dp"
- android:layout_marginStart="68dp"
- android:layout_marginTop="40dp"
+ android:layout_marginStart="64dp"
+ android:layout_marginTop="28dp"
android:indeterminate="true"
android:indeterminateOnly="true"
android:visibility="gone"/>
-
- <View
- android:id="@+id/end_divider"
- android:layout_width="1dp"
- android:layout_height="36dp"
- android:layout_marginEnd="68dp"
- android:layout_gravity="right|center_vertical"
- android:background="?android:attr/listDivider"
- android:visibility="gone"/>
-
- <ImageView
- android:id="@+id/add_icon"
- android:layout_width="24dp"
- android:layout_height="24dp"
- android:layout_gravity="right|center_vertical"
- android:layout_marginEnd="24dp"
- android:src="@drawable/ic_add"
- android:tint="?android:attr/colorAccent"
- android:visibility="gone"/>
-
- <CheckBox
- android:id="@+id/check_box"
- android:layout_width="24dp"
- android:layout_height="24dp"
- android:layout_gravity="right|center_vertical"
- android:layout_marginEnd="24dp"
- android:button="@drawable/ic_check_box"
- android:visibility="gone"/>
</FrameLayout>
<View
android:id="@+id/bottom_divider"
android:layout_width="match_parent"
android:layout_height="1dp"
- android:layout_marginTop="12dp"
- android:layout_marginBottom="12dp"
android:layout_gravity="bottom"
android:background="?android:attr/listDivider"
android:visibility="gone"/>
diff --git a/packages/SystemUI/res/layout/qs_user_dialog_content.xml b/packages/SystemUI/res/layout/qs_user_dialog_content.xml
new file mode 100644
index 0000000..321fe68
--- /dev/null
+++ b/packages/SystemUI/res/layout/qs_user_dialog_content.xml
@@ -0,0 +1,89 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<!--
+ ~ Copyright (C) 2021 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<androidx.constraintlayout.widget.ConstraintLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:sysui="http://schemas.android.com/apk/res-auto"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:padding="24dp"
+ android:layout_marginStart="16dp"
+ android:layout_marginEnd="16dp"
+ android:background="@drawable/qs_dialog_bg"
+>
+ <TextView
+ android:id="@+id/title"
+ android:layout_height="wrap_content"
+ android:layout_width="0dp"
+ android:textAlignment="center"
+ android:text="@string/qs_user_switch_dialog_title"
+ android:textAppearance="@style/TextAppearance.QSDialog.Title"
+ android:layout_marginBottom="32dp"
+ sysui:layout_constraintTop_toTopOf="parent"
+ sysui:layout_constraintStart_toStartOf="parent"
+ sysui:layout_constraintEnd_toEndOf="parent"
+ sysui:layout_constraintBottom_toTopOf="@id/grid"
+ />
+
+ <com.android.systemui.qs.PseudoGridView
+ android:id="@+id/grid"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginBottom="28dp"
+ sysui:verticalSpacing="4dp"
+ sysui:horizontalSpacing="4dp"
+ sysui:fixedChildWidth="80dp"
+ sysui:layout_constraintTop_toBottomOf="@id/title"
+ sysui:layout_constraintStart_toStartOf="parent"
+ sysui:layout_constraintEnd_toEndOf="parent"
+ sysui:layout_constraintBottom_toTopOf="@id/barrier"
+ />
+
+ <androidx.constraintlayout.widget.Barrier
+ android:id="@+id/barrier"
+ android:layout_height="wrap_content"
+ android:layout_width="wrap_content"
+ sysui:barrierDirection="top"
+ sysui:constraint_referenced_ids="settings,done"
+ />
+
+ <Button
+ android:id="@+id/settings"
+ android:layout_width="wrap_content"
+ android:layout_height="48dp"
+ android:text="@string/quick_settings_more_user_settings"
+ sysui:layout_constraintTop_toBottomOf="@id/barrier"
+ sysui:layout_constraintBottom_toBottomOf="parent"
+ sysui:layout_constraintStart_toStartOf="parent"
+ sysui:layout_constraintEnd_toStartOf="@id/done"
+ sysui:layout_constraintHorizontal_chainStyle="spread_inside"
+ style="@style/Widget.QSDialog.Button.BorderButton"
+ />
+
+ <Button
+ android:id="@+id/done"
+ android:layout_width="wrap_content"
+ android:layout_height="48dp"
+ android:text="@string/quick_settings_done"
+ sysui:layout_constraintTop_toBottomOf="@id/barrier"
+ sysui:layout_constraintBottom_toBottomOf="parent"
+ sysui:layout_constraintStart_toEndOf="@id/settings"
+ sysui:layout_constraintEnd_toEndOf="parent"
+ style="@style/Widget.QSDialog.Button"
+ />
+</androidx.constraintlayout.widget.ConstraintLayout>
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout/super_notification_shade.xml b/packages/SystemUI/res/layout/super_notification_shade.xml
index d9a5670..e02a1767 100644
--- a/packages/SystemUI/res/layout/super_notification_shade.xml
+++ b/packages/SystemUI/res/layout/super_notification_shade.xml
@@ -97,7 +97,7 @@
android:singleLine="true"
android:ellipsize="marquee"
android:focusable="true" />
- <FrameLayout android:id="@+id/keyboard_bouncer_container"
+ <FrameLayout android:id="@+id/keyguard_bouncer_container"
android:layout_height="0dp"
android:layout_width="match_parent"
android:layout_weight="1" />
diff --git a/packages/SystemUI/res/values-af/strings.xml b/packages/SystemUI/res/values-af/strings.xml
index f589498..522fcb2 100644
--- a/packages/SystemUI/res/values-af/strings.xml
+++ b/packages/SystemUI/res/values-af/strings.xml
@@ -1178,4 +1178,5 @@
<string name="wifi_wont_autoconnect_for_now" msgid="5782282612749867762">"Wi-fi sal vir nou nie outomaties koppel nie"</string>
<string name="see_all_networks" msgid="3773666844913168122">"Sien alles"</string>
<string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"Ontkoppel Ethernet om netwerke te wissel"</string>
+ <string name="qs_user_switch_dialog_title" msgid="3045189293587781366">"Kies gebruiker"</string>
</resources>
diff --git a/packages/SystemUI/res/values-am/strings.xml b/packages/SystemUI/res/values-am/strings.xml
index e94dc68..432c557 100644
--- a/packages/SystemUI/res/values-am/strings.xml
+++ b/packages/SystemUI/res/values-am/strings.xml
@@ -1178,4 +1178,5 @@
<string name="wifi_wont_autoconnect_for_now" msgid="5782282612749867762">"Wifi ለአሁን በራስ-ሰር አይገናኝም"</string>
<string name="see_all_networks" msgid="3773666844913168122">"ሁሉንም ይመልከቱ"</string>
<string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"አውታረ መረቦችን ለመቀየር፣ የኢተርኔት ግንኙነት ያቋርጡ"</string>
+ <string name="qs_user_switch_dialog_title" msgid="3045189293587781366">"ተጠቃሚን ይምረጡ"</string>
</resources>
diff --git a/packages/SystemUI/res/values-ar/strings.xml b/packages/SystemUI/res/values-ar/strings.xml
index 14753da..2728c98 100644
--- a/packages/SystemUI/res/values-ar/strings.xml
+++ b/packages/SystemUI/res/values-ar/strings.xml
@@ -1202,4 +1202,5 @@
<string name="wifi_wont_autoconnect_for_now" msgid="5782282612749867762">"لن يتم الاتصال بشبكة Wi-Fi تلقائيًا في الوقت الحالي."</string>
<string name="see_all_networks" msgid="3773666844913168122">"عرض الكل"</string>
<string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"للتبديل بين الشبكات، يجب فصل إيثرنت."</string>
+ <string name="qs_user_switch_dialog_title" msgid="3045189293587781366">"اختيار المستخدم"</string>
</resources>
diff --git a/packages/SystemUI/res/values-as/strings.xml b/packages/SystemUI/res/values-as/strings.xml
index a50d8c1..e8158f1 100644
--- a/packages/SystemUI/res/values-as/strings.xml
+++ b/packages/SystemUI/res/values-as/strings.xml
@@ -1178,4 +1178,5 @@
<string name="wifi_wont_autoconnect_for_now" msgid="5782282612749867762">"এতিয়া ৱাই-ফাই স্বয়ংক্ৰিয়ভাৱে সংযুক্ত নহ’ব"</string>
<string name="see_all_networks" msgid="3773666844913168122">"আটাইবোৰ চাওক"</string>
<string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"নেটৱৰ্ক সলনি কৰিবলৈ ইথাৰনেটৰ পৰা সংযোগ বিচ্ছিন্ন কৰক"</string>
+ <string name="qs_user_switch_dialog_title" msgid="3045189293587781366">"ব্যৱহাৰকাৰী বাছনি কৰক"</string>
</resources>
diff --git a/packages/SystemUI/res/values-az/strings.xml b/packages/SystemUI/res/values-az/strings.xml
index e0ef1ec..0203288 100644
--- a/packages/SystemUI/res/values-az/strings.xml
+++ b/packages/SystemUI/res/values-az/strings.xml
@@ -1178,4 +1178,5 @@
<string name="wifi_wont_autoconnect_for_now" msgid="5782282612749867762">"Wi-Fi hələlik avtomatik qoşulmayacaq"</string>
<string name="see_all_networks" msgid="3773666844913168122">"Hamısına baxın"</string>
<string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"Şəbəkəni dəyişmək üçün etherneti ayırın"</string>
+ <string name="qs_user_switch_dialog_title" msgid="3045189293587781366">"İstifadəçi seçin"</string>
</resources>
diff --git a/packages/SystemUI/res/values-b+sr+Latn/strings.xml b/packages/SystemUI/res/values-b+sr+Latn/strings.xml
index 31f8014..60dee9e 100644
--- a/packages/SystemUI/res/values-b+sr+Latn/strings.xml
+++ b/packages/SystemUI/res/values-b+sr+Latn/strings.xml
@@ -1184,4 +1184,5 @@
<string name="wifi_wont_autoconnect_for_now" msgid="5782282612749867762">"WiFi trenutno ne može da se automatski poveže"</string>
<string name="see_all_networks" msgid="3773666844913168122">"Pogledajte sve"</string>
<string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"Da biste promenili mrežu, prekinite eternet vezu"</string>
+ <string name="qs_user_switch_dialog_title" msgid="3045189293587781366">"Izaberite korisnika"</string>
</resources>
diff --git a/packages/SystemUI/res/values-be/strings.xml b/packages/SystemUI/res/values-be/strings.xml
index b296aa6..f234be4 100644
--- a/packages/SystemUI/res/values-be/strings.xml
+++ b/packages/SystemUI/res/values-be/strings.xml
@@ -1190,4 +1190,5 @@
<string name="wifi_wont_autoconnect_for_now" msgid="5782282612749867762">"Аўтаматычнае падключэнне да Wi-Fi адсутнічае"</string>
<string name="see_all_networks" msgid="3773666844913168122">"Паказаць усе"</string>
<string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"Каб падключыцца да сетак, выключыце Ethernet"</string>
+ <string name="qs_user_switch_dialog_title" msgid="3045189293587781366">"Выбар карыстальніка"</string>
</resources>
diff --git a/packages/SystemUI/res/values-bg/strings.xml b/packages/SystemUI/res/values-bg/strings.xml
index 84c2291..170a565 100644
--- a/packages/SystemUI/res/values-bg/strings.xml
+++ b/packages/SystemUI/res/values-bg/strings.xml
@@ -1178,4 +1178,5 @@
<string name="wifi_wont_autoconnect_for_now" msgid="5782282612749867762">"Засега Wi-Fi няма да се свързва автоматично"</string>
<string name="see_all_networks" msgid="3773666844913168122">"Вижте всички"</string>
<string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"За да превключите мрежите, прекъснете връзката с Ethernet"</string>
+ <string name="qs_user_switch_dialog_title" msgid="3045189293587781366">"Избор на потребител"</string>
</resources>
diff --git a/packages/SystemUI/res/values-bn/strings.xml b/packages/SystemUI/res/values-bn/strings.xml
index 57e70a6..f4b1935 100644
--- a/packages/SystemUI/res/values-bn/strings.xml
+++ b/packages/SystemUI/res/values-bn/strings.xml
@@ -1178,4 +1178,5 @@
<string name="wifi_wont_autoconnect_for_now" msgid="5782282612749867762">"এখন ওয়াই-ফাই নিজে থেকে কানেক্ট হবে না"</string>
<string name="see_all_networks" msgid="3773666844913168122">"সবকটি দেখুন"</string>
<string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"নেটওয়ার্ক বদলাতে ইথারনেট ডিসকানেক্ট করুন"</string>
+ <string name="qs_user_switch_dialog_title" msgid="3045189293587781366">"ব্যবহারকারী বেছে নিন"</string>
</resources>
diff --git a/packages/SystemUI/res/values-bs/strings.xml b/packages/SystemUI/res/values-bs/strings.xml
index c8588ec..4f5db01 100644
--- a/packages/SystemUI/res/values-bs/strings.xml
+++ b/packages/SystemUI/res/values-bs/strings.xml
@@ -1184,4 +1184,5 @@
<string name="wifi_wont_autoconnect_for_now" msgid="5782282612749867762">"WiFi se trenutno ne može automatski povezati"</string>
<string name="see_all_networks" msgid="3773666844913168122">"Prikaži sve"</string>
<string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"Da promijenite mrežu, isključite ethernet"</string>
+ <string name="qs_user_switch_dialog_title" msgid="3045189293587781366">"Odaberite korisnika"</string>
</resources>
diff --git a/packages/SystemUI/res/values-ca/strings.xml b/packages/SystemUI/res/values-ca/strings.xml
index cfe70c2..ace4791 100644
--- a/packages/SystemUI/res/values-ca/strings.xml
+++ b/packages/SystemUI/res/values-ca/strings.xml
@@ -1178,4 +1178,5 @@
<string name="wifi_wont_autoconnect_for_now" msgid="5782282612749867762">"Per ara la Wi‑Fi no es connectarà automàticament"</string>
<string name="see_all_networks" msgid="3773666844913168122">"Mostra-ho tot"</string>
<string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"Per canviar de xarxa, desconnecta la connexió Ethernet"</string>
+ <string name="qs_user_switch_dialog_title" msgid="3045189293587781366">"Selecciona un usuari"</string>
</resources>
diff --git a/packages/SystemUI/res/values-cs/strings.xml b/packages/SystemUI/res/values-cs/strings.xml
index 3d648cb..f4c055d 100644
--- a/packages/SystemUI/res/values-cs/strings.xml
+++ b/packages/SystemUI/res/values-cs/strings.xml
@@ -1190,4 +1190,5 @@
<string name="wifi_wont_autoconnect_for_now" msgid="5782282612749867762">"Wi-Fi se prozatím nebude připojovat automaticky"</string>
<string name="see_all_networks" msgid="3773666844913168122">"Zobrazit vše"</string>
<string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"Pokud chcete přepnout sítě, odpojte ethernet"</string>
+ <string name="qs_user_switch_dialog_title" msgid="3045189293587781366">"Zvolte uživatele"</string>
</resources>
diff --git a/packages/SystemUI/res/values-da/strings.xml b/packages/SystemUI/res/values-da/strings.xml
index eb715aa..3a6030e 100644
--- a/packages/SystemUI/res/values-da/strings.xml
+++ b/packages/SystemUI/res/values-da/strings.xml
@@ -1178,4 +1178,5 @@
<string name="wifi_wont_autoconnect_for_now" msgid="5782282612749867762">"Ingen automatisk forbindelse til Wi-Fi i øjeblikket"</string>
<string name="see_all_networks" msgid="3773666844913168122">"Se alle"</string>
<string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"Afbryd ethernetforbindelsen for at skifte netværk"</string>
+ <string name="qs_user_switch_dialog_title" msgid="3045189293587781366">"Vælg bruger"</string>
</resources>
diff --git a/packages/SystemUI/res/values-de/strings.xml b/packages/SystemUI/res/values-de/strings.xml
index b815492..be501ca 100644
--- a/packages/SystemUI/res/values-de/strings.xml
+++ b/packages/SystemUI/res/values-de/strings.xml
@@ -1178,4 +1178,5 @@
<string name="wifi_wont_autoconnect_for_now" msgid="5782282612749867762">"Zurzeit wird keine automatische WLAN-Verbindung hergestellt"</string>
<string name="see_all_networks" msgid="3773666844913168122">"Alle ansehen"</string>
<string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"Trenne das Ethernetkabel, um das Netzwerk zu wechseln"</string>
+ <string name="qs_user_switch_dialog_title" msgid="3045189293587781366">"Nutzer auswählen"</string>
</resources>
diff --git a/packages/SystemUI/res/values-el/strings.xml b/packages/SystemUI/res/values-el/strings.xml
index c6cc7f0..145c72d 100644
--- a/packages/SystemUI/res/values-el/strings.xml
+++ b/packages/SystemUI/res/values-el/strings.xml
@@ -1178,4 +1178,5 @@
<string name="wifi_wont_autoconnect_for_now" msgid="5782282612749867762">"Δεν θα γίνεται προς το παρόν αυτόματη σύνδεση Wi-Fi."</string>
<string name="see_all_networks" msgid="3773666844913168122">"Εμφάνιση όλων"</string>
<string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"Για εναλλαγή δικτύων, αποσυνδέστε το ethernet"</string>
+ <string name="qs_user_switch_dialog_title" msgid="3045189293587781366">"Επιλογή χρήστη"</string>
</resources>
diff --git a/packages/SystemUI/res/values-en-rAU/strings.xml b/packages/SystemUI/res/values-en-rAU/strings.xml
index e1006fc..590033a 100644
--- a/packages/SystemUI/res/values-en-rAU/strings.xml
+++ b/packages/SystemUI/res/values-en-rAU/strings.xml
@@ -1178,4 +1178,5 @@
<string name="wifi_wont_autoconnect_for_now" msgid="5782282612749867762">"Wi‑Fi won’t auto-connect for now"</string>
<string name="see_all_networks" msgid="3773666844913168122">"See all"</string>
<string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"To switch networks, disconnect Ethernet"</string>
+ <string name="qs_user_switch_dialog_title" msgid="3045189293587781366">"Select user"</string>
</resources>
diff --git a/packages/SystemUI/res/values-en-rCA/strings.xml b/packages/SystemUI/res/values-en-rCA/strings.xml
index 66b44d5..ca7099f 100644
--- a/packages/SystemUI/res/values-en-rCA/strings.xml
+++ b/packages/SystemUI/res/values-en-rCA/strings.xml
@@ -1178,4 +1178,5 @@
<string name="wifi_wont_autoconnect_for_now" msgid="5782282612749867762">"Wi‑Fi won’t auto-connect for now"</string>
<string name="see_all_networks" msgid="3773666844913168122">"See all"</string>
<string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"To switch networks, disconnect Ethernet"</string>
+ <string name="qs_user_switch_dialog_title" msgid="3045189293587781366">"Select user"</string>
</resources>
diff --git a/packages/SystemUI/res/values-en-rGB/strings.xml b/packages/SystemUI/res/values-en-rGB/strings.xml
index e1006fc..590033a 100644
--- a/packages/SystemUI/res/values-en-rGB/strings.xml
+++ b/packages/SystemUI/res/values-en-rGB/strings.xml
@@ -1178,4 +1178,5 @@
<string name="wifi_wont_autoconnect_for_now" msgid="5782282612749867762">"Wi‑Fi won’t auto-connect for now"</string>
<string name="see_all_networks" msgid="3773666844913168122">"See all"</string>
<string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"To switch networks, disconnect Ethernet"</string>
+ <string name="qs_user_switch_dialog_title" msgid="3045189293587781366">"Select user"</string>
</resources>
diff --git a/packages/SystemUI/res/values-en-rIN/strings.xml b/packages/SystemUI/res/values-en-rIN/strings.xml
index e1006fc..590033a 100644
--- a/packages/SystemUI/res/values-en-rIN/strings.xml
+++ b/packages/SystemUI/res/values-en-rIN/strings.xml
@@ -1178,4 +1178,5 @@
<string name="wifi_wont_autoconnect_for_now" msgid="5782282612749867762">"Wi‑Fi won’t auto-connect for now"</string>
<string name="see_all_networks" msgid="3773666844913168122">"See all"</string>
<string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"To switch networks, disconnect Ethernet"</string>
+ <string name="qs_user_switch_dialog_title" msgid="3045189293587781366">"Select user"</string>
</resources>
diff --git a/packages/SystemUI/res/values-en-rXC/strings.xml b/packages/SystemUI/res/values-en-rXC/strings.xml
index 9af7322..1a26024 100644
--- a/packages/SystemUI/res/values-en-rXC/strings.xml
+++ b/packages/SystemUI/res/values-en-rXC/strings.xml
@@ -1178,4 +1178,5 @@
<string name="wifi_wont_autoconnect_for_now" msgid="5782282612749867762">"Wi‑Fi won’t auto-connect for now"</string>
<string name="see_all_networks" msgid="3773666844913168122">"See all"</string>
<string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"To switch networks, disconnect ethernet"</string>
+ <string name="qs_user_switch_dialog_title" msgid="3045189293587781366">"Select user"</string>
</resources>
diff --git a/packages/SystemUI/res/values-es-rUS/strings.xml b/packages/SystemUI/res/values-es-rUS/strings.xml
index 8d53bb9..bfe22c2 100644
--- a/packages/SystemUI/res/values-es-rUS/strings.xml
+++ b/packages/SystemUI/res/values-es-rUS/strings.xml
@@ -1178,4 +1178,5 @@
<string name="wifi_wont_autoconnect_for_now" msgid="5782282612749867762">"Por ahora, el Wi-Fi no se conectará automáticamente"</string>
<string name="see_all_networks" msgid="3773666844913168122">"Ver todo"</string>
<string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"Para cambiar de red, desconéctate de Ethernet"</string>
+ <string name="qs_user_switch_dialog_title" msgid="3045189293587781366">"Seleccionar usuario"</string>
</resources>
diff --git a/packages/SystemUI/res/values-es/strings.xml b/packages/SystemUI/res/values-es/strings.xml
index c0c12fa..762c1be 100644
--- a/packages/SystemUI/res/values-es/strings.xml
+++ b/packages/SystemUI/res/values-es/strings.xml
@@ -124,8 +124,8 @@
<string name="screenrecord_permission_error" msgid="7856841237023137686">"No se han podido obtener los permisos"</string>
<string name="screenrecord_start_error" msgid="2200660692479682368">"No se ha podido empezar a grabar la pantalla"</string>
<string name="usb_preference_title" msgid="1439924437558480718">"Opciones de transferencia de archivos por USB"</string>
- <string name="use_mtp_button_title" msgid="5036082897886518086">"Activar como reproductor de medios (MTP)"</string>
- <string name="use_ptp_button_title" msgid="7676427598943446826">"Activar como cámara (PTP)"</string>
+ <string name="use_mtp_button_title" msgid="5036082897886518086">"Montar como reproductor de medios (MTP)"</string>
+ <string name="use_ptp_button_title" msgid="7676427598943446826">"Montar como cámara (PTP)"</string>
<string name="installer_cd_button_title" msgid="5499998592841984743">"Instalar Android File Transfer para Mac"</string>
<string name="accessibility_back" msgid="6530104400086152611">"Atrás"</string>
<string name="accessibility_home" msgid="5430449841237966217">"Inicio"</string>
@@ -1178,4 +1178,5 @@
<string name="wifi_wont_autoconnect_for_now" msgid="5782282612749867762">"Por ahora no se conectará automáticamente a redes Wi-Fi"</string>
<string name="see_all_networks" msgid="3773666844913168122">"Ver todo"</string>
<string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"Para cambiar de red, desconecta el cable Ethernet"</string>
+ <string name="qs_user_switch_dialog_title" msgid="3045189293587781366">"Seleccionar usuario"</string>
</resources>
diff --git a/packages/SystemUI/res/values-et/strings.xml b/packages/SystemUI/res/values-et/strings.xml
index c3cb552..04874d2 100644
--- a/packages/SystemUI/res/values-et/strings.xml
+++ b/packages/SystemUI/res/values-et/strings.xml
@@ -1178,4 +1178,5 @@
<string name="wifi_wont_autoconnect_for_now" msgid="5782282612749867762">"WiFi-ühendust ei looda praegu automaatselt"</string>
<string name="see_all_networks" msgid="3773666844913168122">"Kuva kõik"</string>
<string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"Võrkude vahetamiseks katkestage Etherneti-ühendus"</string>
+ <string name="qs_user_switch_dialog_title" msgid="3045189293587781366">"Kasutaja valimine"</string>
</resources>
diff --git a/packages/SystemUI/res/values-eu/strings.xml b/packages/SystemUI/res/values-eu/strings.xml
index 8df329a..5c6fc3b 100644
--- a/packages/SystemUI/res/values-eu/strings.xml
+++ b/packages/SystemUI/res/values-eu/strings.xml
@@ -1178,4 +1178,5 @@
<string name="wifi_wont_autoconnect_for_now" msgid="5782282612749867762">"Oraingoz ez da automatikoki konektatuko wifira"</string>
<string name="see_all_networks" msgid="3773666844913168122">"Ikusi guztiak"</string>
<string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"Sarea aldatzeko, deskonektatu Ethernet-a"</string>
+ <string name="qs_user_switch_dialog_title" msgid="3045189293587781366">"Hautatu erabiltzaile bat"</string>
</resources>
diff --git a/packages/SystemUI/res/values-fa/strings.xml b/packages/SystemUI/res/values-fa/strings.xml
index ae308b7..ae8fab6 100644
--- a/packages/SystemUI/res/values-fa/strings.xml
+++ b/packages/SystemUI/res/values-fa/strings.xml
@@ -1178,4 +1178,5 @@
<string name="wifi_wont_autoconnect_for_now" msgid="5782282612749867762">"فعلاً Wi-Fi بهطور خودکار متصل نمیشود"</string>
<string name="see_all_networks" msgid="3773666844913168122">"مشاهده همه"</string>
<string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"برای تغییر شبکه، اترنت را قطع کنید"</string>
+ <string name="qs_user_switch_dialog_title" msgid="3045189293587781366">"انتخاب کاربر"</string>
</resources>
diff --git a/packages/SystemUI/res/values-fi/strings.xml b/packages/SystemUI/res/values-fi/strings.xml
index ee0cdb6..9bd16bd 100644
--- a/packages/SystemUI/res/values-fi/strings.xml
+++ b/packages/SystemUI/res/values-fi/strings.xml
@@ -1178,4 +1178,5 @@
<string name="wifi_wont_autoconnect_for_now" msgid="5782282612749867762">"Wi-Fi ei toistaiseksi yhdistä automaattisesti"</string>
<string name="see_all_networks" msgid="3773666844913168122">"Näytä kaikki"</string>
<string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"Irrota Ethernet-johto, jos haluat vaihtaa verkkoa"</string>
+ <string name="qs_user_switch_dialog_title" msgid="3045189293587781366">"Valitse käyttäjä"</string>
</resources>
diff --git a/packages/SystemUI/res/values-fr-rCA/strings.xml b/packages/SystemUI/res/values-fr-rCA/strings.xml
index 7fcbcfb..d8b312f 100644
--- a/packages/SystemUI/res/values-fr-rCA/strings.xml
+++ b/packages/SystemUI/res/values-fr-rCA/strings.xml
@@ -1178,4 +1178,5 @@
<string name="wifi_wont_autoconnect_for_now" msgid="5782282612749867762">"Connexion automatique au Wi-Fi impossible pour le moment"</string>
<string name="see_all_networks" msgid="3773666844913168122">"Tout afficher"</string>
<string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"Pour changer de réseau, débranchez le câble Ethernet"</string>
+ <string name="qs_user_switch_dialog_title" msgid="3045189293587781366">"Sélect. utilisateur"</string>
</resources>
diff --git a/packages/SystemUI/res/values-fr/strings.xml b/packages/SystemUI/res/values-fr/strings.xml
index 74e55fc..e897a69 100644
--- a/packages/SystemUI/res/values-fr/strings.xml
+++ b/packages/SystemUI/res/values-fr/strings.xml
@@ -1178,4 +1178,5 @@
<string name="wifi_wont_autoconnect_for_now" msgid="5782282612749867762">"Connexion automatique au Wi-Fi désactivée pour le moment"</string>
<string name="see_all_networks" msgid="3773666844913168122">"Tout afficher"</string>
<string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"Pour changer de réseau, déconnectez l\'Ethernet"</string>
+ <string name="qs_user_switch_dialog_title" msgid="3045189293587781366">"Choisir utilisateur"</string>
</resources>
diff --git a/packages/SystemUI/res/values-gl/strings.xml b/packages/SystemUI/res/values-gl/strings.xml
index 3bcfcc8..328ac9c 100644
--- a/packages/SystemUI/res/values-gl/strings.xml
+++ b/packages/SystemUI/res/values-gl/strings.xml
@@ -1178,4 +1178,5 @@
<string name="wifi_wont_autoconnect_for_now" msgid="5782282612749867762">"De momento, a wifi non se conectará automaticamente"</string>
<string name="see_all_networks" msgid="3773666844913168122">"Ver todo"</string>
<string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"Para cambiar de rede, desconecta a Ethernet"</string>
+ <string name="qs_user_switch_dialog_title" msgid="3045189293587781366">"Seleccionar usuario"</string>
</resources>
diff --git a/packages/SystemUI/res/values-gu/strings.xml b/packages/SystemUI/res/values-gu/strings.xml
index e713cb7..3524d85 100644
--- a/packages/SystemUI/res/values-gu/strings.xml
+++ b/packages/SystemUI/res/values-gu/strings.xml
@@ -1178,4 +1178,5 @@
<string name="wifi_wont_autoconnect_for_now" msgid="5782282612749867762">"હમણાં પૂરતું વાઇ-ફાઇ ઑટોમૅટિક રીતે કનેક્ટ થશે નહીં"</string>
<string name="see_all_networks" msgid="3773666844913168122">"બધા જુઓ"</string>
<string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"બીજા નેટવર્ક પર જવા માટે, ઇથરનેટ ડિસ્કનેક્ટ કરો"</string>
+ <string name="qs_user_switch_dialog_title" msgid="3045189293587781366">"વપરાશકર્તા પસંદ કરો"</string>
</resources>
diff --git a/packages/SystemUI/res/values-hi/strings.xml b/packages/SystemUI/res/values-hi/strings.xml
index edd7fc6..3aec1f4 100644
--- a/packages/SystemUI/res/values-hi/strings.xml
+++ b/packages/SystemUI/res/values-hi/strings.xml
@@ -1178,4 +1178,5 @@
<string name="wifi_wont_autoconnect_for_now" msgid="5782282612749867762">"फ़िलहाल, वाई-फ़ाई अपने-आप कनेक्ट नहीं होगा"</string>
<string name="see_all_networks" msgid="3773666844913168122">"सभी देखें"</string>
<string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"नेटवर्क बदलने के लिए, पहले ईथरनेट को डिसकनेक्ट करें"</string>
+ <string name="qs_user_switch_dialog_title" msgid="3045189293587781366">"उपयोगकर्ता चुनें"</string>
</resources>
diff --git a/packages/SystemUI/res/values-hr/strings.xml b/packages/SystemUI/res/values-hr/strings.xml
index b066191..0b89903 100644
--- a/packages/SystemUI/res/values-hr/strings.xml
+++ b/packages/SystemUI/res/values-hr/strings.xml
@@ -1184,4 +1184,5 @@
<string name="wifi_wont_autoconnect_for_now" msgid="5782282612749867762">"Wi-Fi se zasad neće automatski povezivati"</string>
<string name="see_all_networks" msgid="3773666844913168122">"Prikaži sve"</string>
<string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"Da biste se prebacili na drugu mrežu, odspojite Ethernet"</string>
+ <string name="qs_user_switch_dialog_title" msgid="3045189293587781366">"Odabir korisnika"</string>
</resources>
diff --git a/packages/SystemUI/res/values-hu/strings.xml b/packages/SystemUI/res/values-hu/strings.xml
index 5880cf0..573fa6e 100644
--- a/packages/SystemUI/res/values-hu/strings.xml
+++ b/packages/SystemUI/res/values-hu/strings.xml
@@ -1178,4 +1178,5 @@
<string name="wifi_wont_autoconnect_for_now" msgid="5782282612749867762">"A Wi-Fi-re történő csatlakozás jelenleg nem automatikus"</string>
<string name="see_all_networks" msgid="3773666844913168122">"Megtekintés"</string>
<string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"Hálózatváltáshoz válassza le az ethernetet"</string>
+ <string name="qs_user_switch_dialog_title" msgid="3045189293587781366">"Felhasználóválasztás"</string>
</resources>
diff --git a/packages/SystemUI/res/values-hy/strings.xml b/packages/SystemUI/res/values-hy/strings.xml
index ad3cf25..c6be8c0 100644
--- a/packages/SystemUI/res/values-hy/strings.xml
+++ b/packages/SystemUI/res/values-hy/strings.xml
@@ -1178,4 +1178,5 @@
<string name="wifi_wont_autoconnect_for_now" msgid="5782282612749867762">"Wi-Fi-ն ավտոմատ չի միանա"</string>
<string name="see_all_networks" msgid="3773666844913168122">"Տեսնել բոլորը"</string>
<string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"Մի ցանցից մյուսին անցնելու համար անջատեք Ethernet-ը"</string>
+ <string name="qs_user_switch_dialog_title" msgid="3045189293587781366">"Ընտրեք օգտատեր"</string>
</resources>
diff --git a/packages/SystemUI/res/values-in/strings.xml b/packages/SystemUI/res/values-in/strings.xml
index c42b143..53c7e26 100644
--- a/packages/SystemUI/res/values-in/strings.xml
+++ b/packages/SystemUI/res/values-in/strings.xml
@@ -1178,4 +1178,5 @@
<string name="wifi_wont_autoconnect_for_now" msgid="5782282612749867762">"Wi-Fi tidak akan otomatis terhubung untuk saat ini"</string>
<string name="see_all_networks" msgid="3773666844913168122">"Lihat semua"</string>
<string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"Untuk beralih jaringan, lepaskan kabel ethernet"</string>
+ <string name="qs_user_switch_dialog_title" msgid="3045189293587781366">"Pilih pengguna"</string>
</resources>
diff --git a/packages/SystemUI/res/values-is/strings.xml b/packages/SystemUI/res/values-is/strings.xml
index 4692280..76953ef 100644
--- a/packages/SystemUI/res/values-is/strings.xml
+++ b/packages/SystemUI/res/values-is/strings.xml
@@ -1178,4 +1178,5 @@
<string name="wifi_wont_autoconnect_for_now" msgid="5782282612749867762">"Wi-Fi tengist ekki sjálfkrafa eins og er"</string>
<string name="see_all_networks" msgid="3773666844913168122">"Sjá allt"</string>
<string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"Aftengdu ethernet til að skipta um net"</string>
+ <string name="qs_user_switch_dialog_title" msgid="3045189293587781366">"Velja notanda"</string>
</resources>
diff --git a/packages/SystemUI/res/values-it/strings.xml b/packages/SystemUI/res/values-it/strings.xml
index 1d96598..d2467b1 100644
--- a/packages/SystemUI/res/values-it/strings.xml
+++ b/packages/SystemUI/res/values-it/strings.xml
@@ -1178,4 +1178,5 @@
<string name="wifi_wont_autoconnect_for_now" msgid="5782282612749867762">"Connessione automatica rete Wi-Fi non attiva al momento"</string>
<string name="see_all_networks" msgid="3773666844913168122">"Mostra tutte"</string>
<string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"Per cambiare rete, scollega il cavo Ethernet"</string>
+ <string name="qs_user_switch_dialog_title" msgid="3045189293587781366">"Seleziona utente"</string>
</resources>
diff --git a/packages/SystemUI/res/values-iw/strings.xml b/packages/SystemUI/res/values-iw/strings.xml
index 8fd2bb9..b4566e8 100644
--- a/packages/SystemUI/res/values-iw/strings.xml
+++ b/packages/SystemUI/res/values-iw/strings.xml
@@ -1190,4 +1190,5 @@
<string name="wifi_wont_autoconnect_for_now" msgid="5782282612749867762">"ה-Wi-Fi לא יתחבר באופן אוטומטי בינתיים"</string>
<string name="see_all_networks" msgid="3773666844913168122">"הצגת הכול"</string>
<string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"כדי לעבור בין רשתות, צריך לנתק את האתרנט"</string>
+ <string name="qs_user_switch_dialog_title" msgid="3045189293587781366">"בחירת משתמש"</string>
</resources>
diff --git a/packages/SystemUI/res/values-ja/strings.xml b/packages/SystemUI/res/values-ja/strings.xml
index 3134f73..34f9c0c 100644
--- a/packages/SystemUI/res/values-ja/strings.xml
+++ b/packages/SystemUI/res/values-ja/strings.xml
@@ -1178,4 +1178,5 @@
<string name="wifi_wont_autoconnect_for_now" msgid="5782282612749867762">"Wi-Fi に自動接続しません"</string>
<string name="see_all_networks" msgid="3773666844913168122">"すべて表示"</string>
<string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"ネットワークを変更するにはイーサネット接続を解除してください"</string>
+ <string name="qs_user_switch_dialog_title" msgid="3045189293587781366">"ユーザーの選択"</string>
</resources>
diff --git a/packages/SystemUI/res/values-ka/strings.xml b/packages/SystemUI/res/values-ka/strings.xml
index c4c73c5..bead5fe 100644
--- a/packages/SystemUI/res/values-ka/strings.xml
+++ b/packages/SystemUI/res/values-ka/strings.xml
@@ -1178,4 +1178,5 @@
<string name="wifi_wont_autoconnect_for_now" msgid="5782282612749867762">"Wi-Fi ინტერნეტს დროებით ავტომატურად არ დაუკავშირდება"</string>
<string name="see_all_networks" msgid="3773666844913168122">"ყველას ნახვა"</string>
<string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"ქსელების გადასართავად, გაწყვიტეთ Ethernet-თან კავშირი"</string>
+ <string name="qs_user_switch_dialog_title" msgid="3045189293587781366">"მომხმარებლის არჩევა"</string>
</resources>
diff --git a/packages/SystemUI/res/values-kk/strings.xml b/packages/SystemUI/res/values-kk/strings.xml
index bccc0ab..14e8456 100644
--- a/packages/SystemUI/res/values-kk/strings.xml
+++ b/packages/SystemUI/res/values-kk/strings.xml
@@ -1178,4 +1178,5 @@
<string name="wifi_wont_autoconnect_for_now" msgid="5782282612749867762">"Әзірше Wi-Fi автоматты түрде қосылмайды."</string>
<string name="see_all_networks" msgid="3773666844913168122">"Барлығын көру"</string>
<string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"Желілерді ауыстыру үшін ethernet кабелін ажыратыңыз."</string>
+ <string name="qs_user_switch_dialog_title" msgid="3045189293587781366">"Пайдаланушыны таңдау"</string>
</resources>
diff --git a/packages/SystemUI/res/values-km/strings.xml b/packages/SystemUI/res/values-km/strings.xml
index 7d79023..9bcc41a 100644
--- a/packages/SystemUI/res/values-km/strings.xml
+++ b/packages/SystemUI/res/values-km/strings.xml
@@ -1178,4 +1178,5 @@
<string name="wifi_wont_autoconnect_for_now" msgid="5782282612749867762">"Wi-Fi នឹងមិនភ្ជាប់ដោយស្វ័យប្រវត្តិក្នុងពេលនេះទេ"</string>
<string name="see_all_networks" msgid="3773666844913168122">"មើលទាំងអស់"</string>
<string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"ដើម្បីប្ដូរបណ្ដាញ សូមផ្ដាច់អ៊ីសឺរណិត"</string>
+ <string name="qs_user_switch_dialog_title" msgid="3045189293587781366">"ជ្រើសរើសអ្នកប្រើប្រាស់"</string>
</resources>
diff --git a/packages/SystemUI/res/values-kn/strings.xml b/packages/SystemUI/res/values-kn/strings.xml
index ca69a82..cad8abb 100644
--- a/packages/SystemUI/res/values-kn/strings.xml
+++ b/packages/SystemUI/res/values-kn/strings.xml
@@ -1178,4 +1178,5 @@
<string name="wifi_wont_autoconnect_for_now" msgid="5782282612749867762">"ಸದ್ಯದ ಮಟ್ಟಿಗೆ ವೈ-ಫೈ ಸ್ವಯಂಚಾಲಿತವಾಗಿ ಕನೆಕ್ಟ್ ಆಗುವುದಿಲ್ಲ"</string>
<string name="see_all_networks" msgid="3773666844913168122">"ಎಲ್ಲವನ್ನೂ ನೋಡಿ"</string>
<string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"ನೆಟ್ವರ್ಕ್ಗಳನ್ನು ಬದಲಿಸಲು, ಇಥರ್ನೆಟ್ ಅನ್ನು ಡಿಸ್ಕನೆಕ್ಟ್ ಮಾಡಿ"</string>
+ <string name="qs_user_switch_dialog_title" msgid="3045189293587781366">"ಬಳಕೆದಾರ ಆಯ್ಕೆಮಾಡಿ"</string>
</resources>
diff --git a/packages/SystemUI/res/values-ko/strings.xml b/packages/SystemUI/res/values-ko/strings.xml
index 3a8657b..6541cbf 100644
--- a/packages/SystemUI/res/values-ko/strings.xml
+++ b/packages/SystemUI/res/values-ko/strings.xml
@@ -1178,4 +1178,5 @@
<string name="wifi_wont_autoconnect_for_now" msgid="5782282612749867762">"지금은 Wi-Fi가 자동으로 연결되지 않습니다."</string>
<string name="see_all_networks" msgid="3773666844913168122">"모두 보기"</string>
<string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"네트워크를 전환하려면 이더넷을 연결 해제하세요."</string>
+ <string name="qs_user_switch_dialog_title" msgid="3045189293587781366">"사용자 선택"</string>
</resources>
diff --git a/packages/SystemUI/res/values-ky/strings.xml b/packages/SystemUI/res/values-ky/strings.xml
index 9f242f34..1c6f3be 100644
--- a/packages/SystemUI/res/values-ky/strings.xml
+++ b/packages/SystemUI/res/values-ky/strings.xml
@@ -1178,4 +1178,5 @@
<string name="wifi_wont_autoconnect_for_now" msgid="5782282612749867762">"Wi-Fi азырынча автоматтык түрдө туташпайт"</string>
<string name="see_all_networks" msgid="3773666844913168122">"Баарын көрүү"</string>
<string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"Башка тармактарга которулуу үчүн Ethernet кабелин ажыратыңыз"</string>
+ <string name="qs_user_switch_dialog_title" msgid="3045189293587781366">"Колдонуучуну тандоо"</string>
</resources>
diff --git a/packages/SystemUI/res/values-lo/strings.xml b/packages/SystemUI/res/values-lo/strings.xml
index afd914d..dfe5916 100644
--- a/packages/SystemUI/res/values-lo/strings.xml
+++ b/packages/SystemUI/res/values-lo/strings.xml
@@ -1178,4 +1178,5 @@
<string name="wifi_wont_autoconnect_for_now" msgid="5782282612749867762">"Wi-Fi ຈະບໍ່ເຊື່ອມຕໍ່ອັດຕະໂນມັດສຳລັບຕອນນີ້"</string>
<string name="see_all_networks" msgid="3773666844913168122">"ເບິ່ງທັງໝົດ"</string>
<string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"ເພື່ອສະຫຼັບເຄືອຂ່າຍ, ໃຫ້ຕັດການເຊື່ອມຕໍ່ອີເທີເນັດກ່ອນ"</string>
+ <string name="qs_user_switch_dialog_title" msgid="3045189293587781366">"ເລືອກຜູ້ໃຊ້"</string>
</resources>
diff --git a/packages/SystemUI/res/values-lt/strings.xml b/packages/SystemUI/res/values-lt/strings.xml
index 233610f..57af386 100644
--- a/packages/SystemUI/res/values-lt/strings.xml
+++ b/packages/SystemUI/res/values-lt/strings.xml
@@ -1190,4 +1190,5 @@
<string name="wifi_wont_autoconnect_for_now" msgid="5782282612749867762">"„Wi-Fi“ šiuo metu nebus prijungtas automatiškai"</string>
<string name="see_all_networks" msgid="3773666844913168122">"Žiūrėti viską"</string>
<string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"Norėdami perjungti tinklus, atjunkite eternetą"</string>
+ <string name="qs_user_switch_dialog_title" msgid="3045189293587781366">"Naudotojo pasirinkimas"</string>
</resources>
diff --git a/packages/SystemUI/res/values-lv/strings.xml b/packages/SystemUI/res/values-lv/strings.xml
index 83e6d6e..ced02b3 100644
--- a/packages/SystemUI/res/values-lv/strings.xml
+++ b/packages/SystemUI/res/values-lv/strings.xml
@@ -1184,4 +1184,5 @@
<string name="wifi_wont_autoconnect_for_now" msgid="5782282612749867762">"Wi-Fi savienojums īslaicīgi netiks izveidots automātiski."</string>
<string name="see_all_networks" msgid="3773666844913168122">"Visu tīklu skatīšana"</string>
<string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"Lai pārslēgtu tīklus, atvienojiet tīkla Ethernet vadu."</string>
+ <string name="qs_user_switch_dialog_title" msgid="3045189293587781366">"Lietotāja atlase"</string>
</resources>
diff --git a/packages/SystemUI/res/values-mk/strings.xml b/packages/SystemUI/res/values-mk/strings.xml
index e00108a..7b54b63 100644
--- a/packages/SystemUI/res/values-mk/strings.xml
+++ b/packages/SystemUI/res/values-mk/strings.xml
@@ -1178,4 +1178,5 @@
<string name="wifi_wont_autoconnect_for_now" msgid="5782282612749867762">"Wi-Fi нема да се поврзува автоматски засега"</string>
<string name="see_all_networks" msgid="3773666844913168122">"Прикажи ги сите"</string>
<string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"За промена на мрежата, прекинете ја врската со етернетот"</string>
+ <string name="qs_user_switch_dialog_title" msgid="3045189293587781366">"Изберете корисник"</string>
</resources>
diff --git a/packages/SystemUI/res/values-ml/strings.xml b/packages/SystemUI/res/values-ml/strings.xml
index 49beea2..6e924aa 100644
--- a/packages/SystemUI/res/values-ml/strings.xml
+++ b/packages/SystemUI/res/values-ml/strings.xml
@@ -1178,4 +1178,5 @@
<string name="wifi_wont_autoconnect_for_now" msgid="5782282612749867762">"വൈഫൈ ഇപ്പോൾ സ്വയമേവ കണക്റ്റ് ചെയ്യില്ല"</string>
<string name="see_all_networks" msgid="3773666844913168122">"എല്ലാം കാണുക"</string>
<string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"മറ്റ് നെറ്റ്വർക്കുകളിലേക്ക് മാറാൻ, ഇതർനെറ്റ് വിച്ഛേദിക്കുക"</string>
+ <string name="qs_user_switch_dialog_title" msgid="3045189293587781366">"ഉപയോക്താവിനെ തിരഞ്ഞെടുക്കൂ"</string>
</resources>
diff --git a/packages/SystemUI/res/values-mn/strings.xml b/packages/SystemUI/res/values-mn/strings.xml
index f6dea18..5512659 100644
--- a/packages/SystemUI/res/values-mn/strings.xml
+++ b/packages/SystemUI/res/values-mn/strings.xml
@@ -1178,4 +1178,5 @@
<string name="wifi_wont_autoconnect_for_now" msgid="5782282612749867762">"Wi-Fi-г одоогоор автоматаар холбохгүй"</string>
<string name="see_all_networks" msgid="3773666844913168122">"Бүгдийг харах"</string>
<string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"Сүлжээг сэлгэхийн тулд этернэтийг салгана уу"</string>
+ <string name="qs_user_switch_dialog_title" msgid="3045189293587781366">"Хэрэглэгч сонгох"</string>
</resources>
diff --git a/packages/SystemUI/res/values-mr/strings.xml b/packages/SystemUI/res/values-mr/strings.xml
index 6156cf5..2864f32 100644
--- a/packages/SystemUI/res/values-mr/strings.xml
+++ b/packages/SystemUI/res/values-mr/strings.xml
@@ -848,7 +848,7 @@
<string name="keyboard_shortcut_group_applications_sms" msgid="6912633831752843566">"SMS"</string>
<string name="keyboard_shortcut_group_applications_music" msgid="9032078456666204025">"संगीत"</string>
<string name="keyboard_shortcut_group_applications_youtube" msgid="5078136084632450333">"YouTube"</string>
- <string name="keyboard_shortcut_group_applications_calendar" msgid="4229602992120154157">"Calendar"</string>
+ <string name="keyboard_shortcut_group_applications_calendar" msgid="4229602992120154157">"कॅलेंडर"</string>
<string name="tuner_full_zen_title" msgid="5120366354224404511">"आवाज नियंत्रणांसह दर्शवा"</string>
<string name="volume_and_do_not_disturb" msgid="502044092739382832">"व्यत्यय आणू नका"</string>
<string name="volume_dnd_silent" msgid="4154597281458298093">"आवाजाच्या बटणांचा शार्टकट"</string>
@@ -1178,4 +1178,5 @@
<string name="wifi_wont_autoconnect_for_now" msgid="5782282612749867762">"सध्या वाय-फाय ऑटो-कनेक्ट होणार नाही"</string>
<string name="see_all_networks" msgid="3773666844913168122">"सर्व पहा"</string>
<string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"नेटवर्क स्विच करण्यासाठी, इथरनेट केबल डिस्कनेक्ट करा"</string>
+ <string name="qs_user_switch_dialog_title" msgid="3045189293587781366">"वापरकर्ता निवडा"</string>
</resources>
diff --git a/packages/SystemUI/res/values-ms/strings.xml b/packages/SystemUI/res/values-ms/strings.xml
index 9dde6b6..96c7f89 100644
--- a/packages/SystemUI/res/values-ms/strings.xml
+++ b/packages/SystemUI/res/values-ms/strings.xml
@@ -1178,4 +1178,5 @@
<string name="wifi_wont_autoconnect_for_now" msgid="5782282612749867762">"Wi-Fi tidak akan disambungkan secara automatik buat masa ini"</string>
<string name="see_all_networks" msgid="3773666844913168122">"Lihat semua"</string>
<string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"Untuk menukar rangkaian, putuskan sambungan ethernet"</string>
+ <string name="qs_user_switch_dialog_title" msgid="3045189293587781366">"Pilih pengguna"</string>
</resources>
diff --git a/packages/SystemUI/res/values-my/strings.xml b/packages/SystemUI/res/values-my/strings.xml
index 666f9ed..1d521dc 100644
--- a/packages/SystemUI/res/values-my/strings.xml
+++ b/packages/SystemUI/res/values-my/strings.xml
@@ -1178,4 +1178,5 @@
<string name="wifi_wont_autoconnect_for_now" msgid="5782282612749867762">"Wi-Fi က လောလောဆယ် အလိုအလျောက် ချိတ်ဆက်မည်မဟုတ်ပါ"</string>
<string name="see_all_networks" msgid="3773666844913168122">"အားလုံးကြည့်ရန်"</string>
<string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"ကွန်ရက်ပြောင်းရန် အီသာနက်ကို ချိတ်ဆက်မှုဖြုတ်ပါ"</string>
+ <string name="qs_user_switch_dialog_title" msgid="3045189293587781366">"အသုံးပြုသူ ရွေးခြင်း"</string>
</resources>
diff --git a/packages/SystemUI/res/values-nb/strings.xml b/packages/SystemUI/res/values-nb/strings.xml
index d4eef72..32a3740 100644
--- a/packages/SystemUI/res/values-nb/strings.xml
+++ b/packages/SystemUI/res/values-nb/strings.xml
@@ -602,8 +602,8 @@
<string name="volume_odi_captions_tip" msgid="8825655463280990941">"Automatisk medieteksting"</string>
<string name="accessibility_volume_close_odi_captions_tip" msgid="8924753283621160480">"Verktøytips for teksting"</string>
<string name="volume_odi_captions_content_description" msgid="4172765742046013630">"Overlegg med teksting"</string>
- <string name="volume_odi_captions_hint_enable" msgid="2073091194012843195">"slå på"</string>
- <string name="volume_odi_captions_hint_disable" msgid="2518846326748183407">"slå av"</string>
+ <string name="volume_odi_captions_hint_enable" msgid="2073091194012843195">"aktivér"</string>
+ <string name="volume_odi_captions_hint_disable" msgid="2518846326748183407">"deaktiver"</string>
<string name="accessibility_output_chooser" msgid="7807898688967194183">"Bytt enhet for lydutgang"</string>
<string name="screen_pinning_title" msgid="9058007390337841305">"Appen er festet"</string>
<string name="screen_pinning_description" msgid="8699395373875667743">"Gjør at den vises til du løsner den. Trykk og hold inne Tilbake og Oversikt for å løsne den."</string>
@@ -1178,4 +1178,5 @@
<string name="wifi_wont_autoconnect_for_now" msgid="5782282612749867762">"Wi-Fi kobles ikke til automatisk inntil videre"</string>
<string name="see_all_networks" msgid="3773666844913168122">"Se alle"</string>
<string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"For å bytte nettverk, koble fra Ethernet"</string>
+ <string name="qs_user_switch_dialog_title" msgid="3045189293587781366">"Velg bruker"</string>
</resources>
diff --git a/packages/SystemUI/res/values-ne/strings.xml b/packages/SystemUI/res/values-ne/strings.xml
index ce1787c..8b50929b 100644
--- a/packages/SystemUI/res/values-ne/strings.xml
+++ b/packages/SystemUI/res/values-ne/strings.xml
@@ -1178,4 +1178,5 @@
<string name="wifi_wont_autoconnect_for_now" msgid="5782282612749867762">"केही समयका लागि Wi-Fi स्वतः कनेक्ट हुँदैन"</string>
<string name="see_all_networks" msgid="3773666844913168122">"सबै नेटवर्क हेर्नुहोस्"</string>
<string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"नेटवर्क बदल्न इथरनेट डिस्कनेक्ट गर्नुहोस्"</string>
+ <string name="qs_user_switch_dialog_title" msgid="3045189293587781366">"प्रयोगकर्ता चयन गर्नु…"</string>
</resources>
diff --git a/packages/SystemUI/res/values-nl/strings.xml b/packages/SystemUI/res/values-nl/strings.xml
index ad1403e..2c9b459 100644
--- a/packages/SystemUI/res/values-nl/strings.xml
+++ b/packages/SystemUI/res/values-nl/strings.xml
@@ -1178,4 +1178,5 @@
<string name="wifi_wont_autoconnect_for_now" msgid="5782282612749867762">"Wifi maakt momenteel niet automatisch verbinding"</string>
<string name="see_all_networks" msgid="3773666844913168122">"Alles tonen"</string>
<string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"Verbreek de ethernetverbinding om van netwerk te wisselen"</string>
+ <string name="qs_user_switch_dialog_title" msgid="3045189293587781366">"Gebruiker selecteren"</string>
</resources>
diff --git a/packages/SystemUI/res/values-or/strings.xml b/packages/SystemUI/res/values-or/strings.xml
index e5c2a2b..a69c9a9 100644
--- a/packages/SystemUI/res/values-or/strings.xml
+++ b/packages/SystemUI/res/values-or/strings.xml
@@ -1178,4 +1178,5 @@
<string name="wifi_wont_autoconnect_for_now" msgid="5782282612749867762">"ବର୍ତ୍ତମାନ ପାଇଁ ୱାଇ-ଫାଇ ସ୍ୱତଃ-ସଂଯୋଗ ହେବ ନାହିଁ"</string>
<string name="see_all_networks" msgid="3773666844913168122">"ସବୁ ଦେଖନ୍ତୁ"</string>
<string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"ନେଟୱାର୍କ ସ୍ୱିଚ୍ କରିବାକୁ, ଇଥରନେଟ୍ ବିଚ୍ଛିନ୍ନ କରନ୍ତୁ"</string>
+ <string name="qs_user_switch_dialog_title" msgid="3045189293587781366">"ଉପଯୋଗକର୍ତ୍ତା ଚୟନ କର"</string>
</resources>
diff --git a/packages/SystemUI/res/values-pa/strings.xml b/packages/SystemUI/res/values-pa/strings.xml
index 48750cf..7288263 100644
--- a/packages/SystemUI/res/values-pa/strings.xml
+++ b/packages/SystemUI/res/values-pa/strings.xml
@@ -1178,4 +1178,5 @@
<string name="wifi_wont_autoconnect_for_now" msgid="5782282612749867762">"ਫ਼ਿਲਹਾਲ ਵਾਈ-ਫਾਈ ਸਵੈ-ਕਨੈਕਟ ਨਹੀਂ ਹੋਵੇਗਾ"</string>
<string name="see_all_networks" msgid="3773666844913168122">"ਸਭ ਦੇਖੋ"</string>
<string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"ਨੈੱਟਵਰਕਾਂ ਨੂੰ ਬਦਲਣ ਲਈ, ਈਥਰਨੈੱਟ ਨੂੰ ਡਿਸਕਨੈਕਟ ਕਰੋ"</string>
+ <string name="qs_user_switch_dialog_title" msgid="3045189293587781366">"ਵਰਤੋਂਕਾਰ ਚੁਣੋ"</string>
</resources>
diff --git a/packages/SystemUI/res/values-pl/strings.xml b/packages/SystemUI/res/values-pl/strings.xml
index 3635876..b7fc15d 100644
--- a/packages/SystemUI/res/values-pl/strings.xml
+++ b/packages/SystemUI/res/values-pl/strings.xml
@@ -1190,4 +1190,5 @@
<string name="wifi_wont_autoconnect_for_now" msgid="5782282612749867762">"Wi-Fi nie będzie na razie włączać się automatycznie"</string>
<string name="see_all_networks" msgid="3773666844913168122">"Pokaż wszystko"</string>
<string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"Aby przełączać sieci, odłącz Ethernet"</string>
+ <string name="qs_user_switch_dialog_title" msgid="3045189293587781366">"Wybierz użytkownika"</string>
</resources>
diff --git a/packages/SystemUI/res/values-pt-rBR/strings.xml b/packages/SystemUI/res/values-pt-rBR/strings.xml
index 8f499f7..ceb6ff5 100644
--- a/packages/SystemUI/res/values-pt-rBR/strings.xml
+++ b/packages/SystemUI/res/values-pt-rBR/strings.xml
@@ -1178,4 +1178,5 @@
<string name="wifi_wont_autoconnect_for_now" msgid="5782282612749867762">"A conexão automática ao Wi-Fi ficará indisponível"</string>
<string name="see_all_networks" msgid="3773666844913168122">"Ver tudo"</string>
<string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"Para mudar de rede, desconecte o cabo Ethernet"</string>
+ <string name="qs_user_switch_dialog_title" msgid="3045189293587781366">"Selecionar usuário"</string>
</resources>
diff --git a/packages/SystemUI/res/values-pt-rPT/strings.xml b/packages/SystemUI/res/values-pt-rPT/strings.xml
index b0c65d8..a103e41 100644
--- a/packages/SystemUI/res/values-pt-rPT/strings.xml
+++ b/packages/SystemUI/res/values-pt-rPT/strings.xml
@@ -1178,4 +1178,5 @@
<string name="wifi_wont_autoconnect_for_now" msgid="5782282612749867762">"Por agora, o Wi-Fi não irá estabelecer lig. automaticamente"</string>
<string name="see_all_networks" msgid="3773666844913168122">"Veja tudo"</string>
<string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"Para mudar de rede, desligue a Ethernet"</string>
+ <string name="qs_user_switch_dialog_title" msgid="3045189293587781366">"Selecione utilizador"</string>
</resources>
diff --git a/packages/SystemUI/res/values-pt/strings.xml b/packages/SystemUI/res/values-pt/strings.xml
index 8f499f7..ceb6ff5 100644
--- a/packages/SystemUI/res/values-pt/strings.xml
+++ b/packages/SystemUI/res/values-pt/strings.xml
@@ -1178,4 +1178,5 @@
<string name="wifi_wont_autoconnect_for_now" msgid="5782282612749867762">"A conexão automática ao Wi-Fi ficará indisponível"</string>
<string name="see_all_networks" msgid="3773666844913168122">"Ver tudo"</string>
<string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"Para mudar de rede, desconecte o cabo Ethernet"</string>
+ <string name="qs_user_switch_dialog_title" msgid="3045189293587781366">"Selecionar usuário"</string>
</resources>
diff --git a/packages/SystemUI/res/values-ro/strings.xml b/packages/SystemUI/res/values-ro/strings.xml
index c143d53..dba5b7d 100644
--- a/packages/SystemUI/res/values-ro/strings.xml
+++ b/packages/SystemUI/res/values-ro/strings.xml
@@ -1184,4 +1184,5 @@
<string name="wifi_wont_autoconnect_for_now" msgid="5782282612749867762">"Deocamdată, Wi-Fi nu se poate conecta automat"</string>
<string name="see_all_networks" msgid="3773666844913168122">"Afișează-le pe toate"</string>
<string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"Pentru a schimba rețeaua, deconectați ethernet"</string>
+ <string name="qs_user_switch_dialog_title" msgid="3045189293587781366">"Alegeți utilizatorul"</string>
</resources>
diff --git a/packages/SystemUI/res/values-ru/strings.xml b/packages/SystemUI/res/values-ru/strings.xml
index 752110e..808cb54 100644
--- a/packages/SystemUI/res/values-ru/strings.xml
+++ b/packages/SystemUI/res/values-ru/strings.xml
@@ -1190,4 +1190,5 @@
<string name="wifi_wont_autoconnect_for_now" msgid="5782282612749867762">"Подключение по Wi-Fi не установится автоматически."</string>
<string name="see_all_networks" msgid="3773666844913168122">"Показать все"</string>
<string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"Чтобы переключиться между сетями, отключите кабель Ethernet."</string>
+ <string name="qs_user_switch_dialog_title" msgid="3045189293587781366">"Выберите профиль"</string>
</resources>
diff --git a/packages/SystemUI/res/values-si/strings.xml b/packages/SystemUI/res/values-si/strings.xml
index b3a7a8e3..a81292c 100644
--- a/packages/SystemUI/res/values-si/strings.xml
+++ b/packages/SystemUI/res/values-si/strings.xml
@@ -1178,4 +1178,5 @@
<string name="wifi_wont_autoconnect_for_now" msgid="5782282612749867762">"Wi-Fi දැනට ස්වයං-සබැඳි නොවනු ඇත"</string>
<string name="see_all_networks" msgid="3773666844913168122">"සියල්ල බලන්න"</string>
<string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"ජාල මාරු කිරීමට, ඊතර්නෙට් විසන්ධි කරන්න"</string>
+ <string name="qs_user_switch_dialog_title" msgid="3045189293587781366">"පරිශීලක තෝරන්න"</string>
</resources>
diff --git a/packages/SystemUI/res/values-sk/strings.xml b/packages/SystemUI/res/values-sk/strings.xml
index f45226b..a2cb0d6 100644
--- a/packages/SystemUI/res/values-sk/strings.xml
+++ b/packages/SystemUI/res/values-sk/strings.xml
@@ -1190,4 +1190,5 @@
<string name="wifi_wont_autoconnect_for_now" msgid="5782282612749867762">"Wi‑Fi sa teraz automaticky nepripojí"</string>
<string name="see_all_networks" msgid="3773666844913168122">"Zobraziť všetko"</string>
<string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"Ak chcete prepnúť siete, odpojte ethernet"</string>
+ <string name="qs_user_switch_dialog_title" msgid="3045189293587781366">"Vyberte používateľa"</string>
</resources>
diff --git a/packages/SystemUI/res/values-sl/strings.xml b/packages/SystemUI/res/values-sl/strings.xml
index ed75390..5092c03 100644
--- a/packages/SystemUI/res/values-sl/strings.xml
+++ b/packages/SystemUI/res/values-sl/strings.xml
@@ -1190,4 +1190,5 @@
<string name="wifi_wont_autoconnect_for_now" msgid="5782282612749867762">"Vmesnik Wi-Fi trenutno ne bo samodejno vzpostavil povezave."</string>
<string name="see_all_networks" msgid="3773666844913168122">"Prikaz vseh omrežij"</string>
<string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"Če želite preklopiti omrežje, prekinite ethernetno povezavo."</string>
+ <string name="qs_user_switch_dialog_title" msgid="3045189293587781366">"Izberite uporabnika"</string>
</resources>
diff --git a/packages/SystemUI/res/values-sq/strings.xml b/packages/SystemUI/res/values-sq/strings.xml
index dc7e825..adb773d 100644
--- a/packages/SystemUI/res/values-sq/strings.xml
+++ b/packages/SystemUI/res/values-sq/strings.xml
@@ -1178,4 +1178,5 @@
<string name="wifi_wont_autoconnect_for_now" msgid="5782282612749867762">"Wi-Fi nuk do të lidhet automatikisht për momentin"</string>
<string name="see_all_networks" msgid="3773666844913168122">"Shiko të gjitha"</string>
<string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"Për të ndërruar rrjetet, shkëput Ethernet-in"</string>
+ <string name="qs_user_switch_dialog_title" msgid="3045189293587781366">"Zgjidh përdoruesin"</string>
</resources>
diff --git a/packages/SystemUI/res/values-sr/strings.xml b/packages/SystemUI/res/values-sr/strings.xml
index b486589..a3f85c9 100644
--- a/packages/SystemUI/res/values-sr/strings.xml
+++ b/packages/SystemUI/res/values-sr/strings.xml
@@ -1184,4 +1184,5 @@
<string name="wifi_wont_autoconnect_for_now" msgid="5782282612749867762">"WiFi тренутно не може да се аутоматски повеже"</string>
<string name="see_all_networks" msgid="3773666844913168122">"Погледајте све"</string>
<string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"Да бисте променили мрежу, прекините етернет везу"</string>
+ <string name="qs_user_switch_dialog_title" msgid="3045189293587781366">"Изаберите корисника"</string>
</resources>
diff --git a/packages/SystemUI/res/values-sv/strings.xml b/packages/SystemUI/res/values-sv/strings.xml
index 5db089b..6073c7d 100644
--- a/packages/SystemUI/res/values-sv/strings.xml
+++ b/packages/SystemUI/res/values-sv/strings.xml
@@ -1178,4 +1178,5 @@
<string name="wifi_wont_autoconnect_for_now" msgid="5782282612749867762">"Du ansluts inte till wifi automatiskt för närvarande"</string>
<string name="see_all_networks" msgid="3773666844913168122">"Visa alla"</string>
<string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"Koppla bort Ethernet för att växla nätverk"</string>
+ <string name="qs_user_switch_dialog_title" msgid="3045189293587781366">"Välj användare"</string>
</resources>
diff --git a/packages/SystemUI/res/values-sw/strings.xml b/packages/SystemUI/res/values-sw/strings.xml
index 60104f6..5856a61 100644
--- a/packages/SystemUI/res/values-sw/strings.xml
+++ b/packages/SystemUI/res/values-sw/strings.xml
@@ -1178,4 +1178,5 @@
<string name="wifi_wont_autoconnect_for_now" msgid="5782282612749867762">"Wi-Fi haitaunganishwa kiotomatiki kwa sasa"</string>
<string name="see_all_networks" msgid="3773666844913168122">"Angalia yote"</string>
<string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"Ili kubadili mitandao, tenganisha ethaneti"</string>
+ <string name="qs_user_switch_dialog_title" msgid="3045189293587781366">"Chagua mtumiaji"</string>
</resources>
diff --git a/packages/SystemUI/res/values-sw720dp-land/config.xml b/packages/SystemUI/res/values-sw720dp-land/config.xml
new file mode 100644
index 0000000..e4573c6
--- /dev/null
+++ b/packages/SystemUI/res/values-sw720dp-land/config.xml
@@ -0,0 +1,35 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2021 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License
+ -->
+<resources>
+ <!-- Max number of columns for quick controls area -->
+ <integer name="controls_max_columns">2</integer>
+
+ <!-- The maximum number of rows in the QuickQSPanel -->
+ <integer name="quick_qs_panel_max_rows">4</integer>
+
+ <!-- The maximum number of tiles in the QuickQSPanel -->
+ <integer name="quick_qs_panel_max_tiles">8</integer>
+
+ <!-- Whether to use the split 2-column notification shade -->
+ <bool name="config_use_split_notification_shade">true</bool>
+
+ <!-- The number of columns in the QuickSettings -->
+ <integer name="quick_settings_num_columns">2</integer>
+
+ <!-- Notifications are sized to match the width of two (of 4) qs tiles in landscape. -->
+ <bool name="config_skinnyNotifsInLandscape">false</bool>
+</resources>
diff --git a/packages/SystemUI/res/values-sw720dp-port/config.xml b/packages/SystemUI/res/values-sw720dp-port/config.xml
new file mode 100644
index 0000000..1225086
--- /dev/null
+++ b/packages/SystemUI/res/values-sw720dp-port/config.xml
@@ -0,0 +1,33 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+** Copyright 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.
+*/
+-->
+
+<!-- These resources are around just to allow their values to be customized
+ for different hardware and product builds. -->
+<resources>
+ <!-- The maximum number of tiles in the QuickQSPanel -->
+ <integer name="quick_qs_panel_max_tiles">6</integer>
+
+ <!-- The number of columns in the QuickSettings -->
+ <integer name="quick_settings_num_columns">3</integer>
+
+ <!-- The maximum number of rows in the QuickSettings -->
+ <integer name="quick_settings_max_rows">3</integer>
+
+</resources>
+
diff --git a/packages/SystemUI/res/values-sw720dp/config.xml b/packages/SystemUI/res/values-sw720dp/config.xml
index ac44251..436f8d0 100644
--- a/packages/SystemUI/res/values-sw720dp/config.xml
+++ b/packages/SystemUI/res/values-sw720dp/config.xml
@@ -22,14 +22,5 @@
<resources>
<integer name="status_bar_config_maxNotificationIcons">5</integer>
- <!-- The maximum number of tiles in the QuickQSPanel -->
- <integer name="quick_qs_panel_max_tiles">6</integer>
-
- <!-- The number of columns in the QuickSettings -->
- <integer name="quick_settings_num_columns">3</integer>
-
- <!-- The maximum number of rows in the QuickSettings -->
- <integer name="quick_settings_max_rows">3</integer>
-
</resources>
diff --git a/packages/SystemUI/res/values-ta/strings.xml b/packages/SystemUI/res/values-ta/strings.xml
index 8cc4c8d..8daa24f 100644
--- a/packages/SystemUI/res/values-ta/strings.xml
+++ b/packages/SystemUI/res/values-ta/strings.xml
@@ -1178,4 +1178,5 @@
<string name="wifi_wont_autoconnect_for_now" msgid="5782282612749867762">"தற்போதைக்கு வைஃபை தானாக இணைக்கப்படாது"</string>
<string name="see_all_networks" msgid="3773666844913168122">"அனைத்தையும் காட்டு"</string>
<string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"நெட்வொர்க்குகளை மாற்ற ஈதர்நெட் இணைப்பைத் துண்டிக்கவும்"</string>
+ <string name="qs_user_switch_dialog_title" msgid="3045189293587781366">"பயனரைத் தேர்வுசெய்க"</string>
</resources>
diff --git a/packages/SystemUI/res/values-te/strings.xml b/packages/SystemUI/res/values-te/strings.xml
index 95b8a63..cab7b1c 100644
--- a/packages/SystemUI/res/values-te/strings.xml
+++ b/packages/SystemUI/res/values-te/strings.xml
@@ -1178,4 +1178,5 @@
<string name="wifi_wont_autoconnect_for_now" msgid="5782282612749867762">"ప్రస్తుతానికి Wi-Fi ఆటోమేటిక్గా కనెక్ట్ అవ్వదు"</string>
<string name="see_all_networks" msgid="3773666844913168122">"అన్నీ చూడండి"</string>
<string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"నెట్వర్క్లను మార్చడానికి, ఈథర్నెట్ను డిస్కనెక్ట్ చేయండి"</string>
+ <string name="qs_user_switch_dialog_title" msgid="3045189293587781366">"యూజర్ను ఎంచుకోండి"</string>
</resources>
diff --git a/packages/SystemUI/res/values-th/strings.xml b/packages/SystemUI/res/values-th/strings.xml
index 91a2440..6da8888 100644
--- a/packages/SystemUI/res/values-th/strings.xml
+++ b/packages/SystemUI/res/values-th/strings.xml
@@ -1178,4 +1178,5 @@
<string name="wifi_wont_autoconnect_for_now" msgid="5782282612749867762">"Wi-Fi จะไม่เชื่อมต่ออัตโนมัติในตอนนี้"</string>
<string name="see_all_networks" msgid="3773666844913168122">"ดูทั้งหมด"</string>
<string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"ตัดการเชื่อมต่ออีเทอร์เน็ตเพื่อสลับเครือข่าย"</string>
+ <string name="qs_user_switch_dialog_title" msgid="3045189293587781366">"เลือกผู้ใช้"</string>
</resources>
diff --git a/packages/SystemUI/res/values-tl/strings.xml b/packages/SystemUI/res/values-tl/strings.xml
index dcde0fc..a9c5886 100644
--- a/packages/SystemUI/res/values-tl/strings.xml
+++ b/packages/SystemUI/res/values-tl/strings.xml
@@ -1178,4 +1178,5 @@
<string name="wifi_wont_autoconnect_for_now" msgid="5782282612749867762">"Hindi awtomatikong kokonekta ang Wi-Fi sa ngayon"</string>
<string name="see_all_networks" msgid="3773666844913168122">"Tingnan lahat"</string>
<string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"Para lumipat ng network, idiskonekta ang ethernet"</string>
+ <string name="qs_user_switch_dialog_title" msgid="3045189293587781366">"Pumili ng user"</string>
</resources>
diff --git a/packages/SystemUI/res/values-tr/strings.xml b/packages/SystemUI/res/values-tr/strings.xml
index 367d0bf..8fe7862 100644
--- a/packages/SystemUI/res/values-tr/strings.xml
+++ b/packages/SystemUI/res/values-tr/strings.xml
@@ -1178,4 +1178,5 @@
<string name="wifi_wont_autoconnect_for_now" msgid="5782282612749867762">"Şu anda kablosuz ağa otomatik olarak bağlanılamıyor"</string>
<string name="see_all_networks" msgid="3773666844913168122">"Tümünü göster"</string>
<string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"Ağ değiştirmek için ethernet bağlantısını kesin"</string>
+ <string name="qs_user_switch_dialog_title" msgid="3045189293587781366">"Kullanıcı seçin"</string>
</resources>
diff --git a/packages/SystemUI/res/values-uk/strings.xml b/packages/SystemUI/res/values-uk/strings.xml
index e692a36..596ba95 100644
--- a/packages/SystemUI/res/values-uk/strings.xml
+++ b/packages/SystemUI/res/values-uk/strings.xml
@@ -1190,4 +1190,5 @@
<string name="wifi_wont_autoconnect_for_now" msgid="5782282612749867762">"Пристрій не підключатиметься до Wi-Fi автоматично"</string>
<string name="see_all_networks" msgid="3773666844913168122">"Показати все"</string>
<string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"Щоб вибрати іншу мережу, від’єднайте кабель Ethernet"</string>
+ <string name="qs_user_switch_dialog_title" msgid="3045189293587781366">"Виберіть користувача"</string>
</resources>
diff --git a/packages/SystemUI/res/values-ur/strings.xml b/packages/SystemUI/res/values-ur/strings.xml
index 44b91ad..f05c709 100644
--- a/packages/SystemUI/res/values-ur/strings.xml
+++ b/packages/SystemUI/res/values-ur/strings.xml
@@ -1178,4 +1178,5 @@
<string name="wifi_wont_autoconnect_for_now" msgid="5782282612749867762">"ابھی Wi-Fi خود کار طور پر منسلک نہیں ہوگا"</string>
<string name="see_all_networks" msgid="3773666844913168122">"سبھی دیکھیں"</string>
<string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"نیٹ ورکس پر سوئچ کرنے کیلئے، ایتھرنیٹ غیر منسلک کریں"</string>
+ <string name="qs_user_switch_dialog_title" msgid="3045189293587781366">"صارف منتخب کریں"</string>
</resources>
diff --git a/packages/SystemUI/res/values-uz/strings.xml b/packages/SystemUI/res/values-uz/strings.xml
index f0afb6c..e9c20a9 100644
--- a/packages/SystemUI/res/values-uz/strings.xml
+++ b/packages/SystemUI/res/values-uz/strings.xml
@@ -1178,4 +1178,5 @@
<string name="wifi_wont_autoconnect_for_now" msgid="5782282612749867762">"Wi-Fi hozir avtomatik ulanmaydi"</string>
<string name="see_all_networks" msgid="3773666844913168122">"Hammasi"</string>
<string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"Boshqa tarmoqqa almashish uchun Ethernet tarmogʻini uzing"</string>
+ <string name="qs_user_switch_dialog_title" msgid="3045189293587781366">"Foydalanuvchini tanlang"</string>
</resources>
diff --git a/packages/SystemUI/res/values-vi/strings.xml b/packages/SystemUI/res/values-vi/strings.xml
index b0f1dcf..07a08ad 100644
--- a/packages/SystemUI/res/values-vi/strings.xml
+++ b/packages/SystemUI/res/values-vi/strings.xml
@@ -1178,4 +1178,5 @@
<string name="wifi_wont_autoconnect_for_now" msgid="5782282612749867762">"Tạm thời, Wi-Fi sẽ không tự động kết nối"</string>
<string name="see_all_networks" msgid="3773666844913168122">"Xem tất cả"</string>
<string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"Để chuyển mạng, hãy rút cáp Ethernet"</string>
+ <string name="qs_user_switch_dialog_title" msgid="3045189293587781366">"Chọn người dùng"</string>
</resources>
diff --git a/packages/SystemUI/res/values-zh-rCN/strings.xml b/packages/SystemUI/res/values-zh-rCN/strings.xml
index 7caae5d..377d15c 100644
--- a/packages/SystemUI/res/values-zh-rCN/strings.xml
+++ b/packages/SystemUI/res/values-zh-rCN/strings.xml
@@ -1178,4 +1178,5 @@
<string name="wifi_wont_autoconnect_for_now" msgid="5782282612749867762">"WLAN 暂时无法自动连接"</string>
<string name="see_all_networks" msgid="3773666844913168122">"查看全部"</string>
<string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"如要切换网络,请断开以太网连接"</string>
+ <string name="qs_user_switch_dialog_title" msgid="3045189293587781366">"选择用户"</string>
</resources>
diff --git a/packages/SystemUI/res/values-zh-rHK/strings.xml b/packages/SystemUI/res/values-zh-rHK/strings.xml
index 4a8567d..73df468 100644
--- a/packages/SystemUI/res/values-zh-rHK/strings.xml
+++ b/packages/SystemUI/res/values-zh-rHK/strings.xml
@@ -1178,4 +1178,5 @@
<string name="wifi_wont_autoconnect_for_now" msgid="5782282612749867762">"目前系統不會自動連線至 Wi-Fi"</string>
<string name="see_all_networks" msgid="3773666844913168122">"顯示全部"</string>
<string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"如要切換網絡,請中斷以太網連線"</string>
+ <string name="qs_user_switch_dialog_title" msgid="3045189293587781366">"選取使用者"</string>
</resources>
diff --git a/packages/SystemUI/res/values-zh-rTW/strings.xml b/packages/SystemUI/res/values-zh-rTW/strings.xml
index b444d42..f883b10 100644
--- a/packages/SystemUI/res/values-zh-rTW/strings.xml
+++ b/packages/SystemUI/res/values-zh-rTW/strings.xml
@@ -1178,4 +1178,5 @@
<string name="wifi_wont_autoconnect_for_now" msgid="5782282612749867762">"目前不會自動連上 Wi-Fi"</string>
<string name="see_all_networks" msgid="3773666844913168122">"查看全部"</string>
<string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"如要切換網路,請中斷乙太網路連線"</string>
+ <string name="qs_user_switch_dialog_title" msgid="3045189293587781366">"選取使用者"</string>
</resources>
diff --git a/packages/SystemUI/res/values-zu/strings.xml b/packages/SystemUI/res/values-zu/strings.xml
index 7988476..e1c1f6f 100644
--- a/packages/SystemUI/res/values-zu/strings.xml
+++ b/packages/SystemUI/res/values-zu/strings.xml
@@ -1178,4 +1178,5 @@
<string name="wifi_wont_autoconnect_for_now" msgid="5782282612749867762">"I-Wi-Fi ngeke ixhume ngokuzenzakalelayo okwamanje"</string>
<string name="see_all_networks" msgid="3773666844913168122">"Bona konke"</string>
<string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"Ukuze ushintshe amanethiwekhi, nqamula i-ethernet"</string>
+ <string name="qs_user_switch_dialog_title" msgid="3045189293587781366">"Khetha umsebenzisi"</string>
</resources>
diff --git a/packages/SystemUI/res/values/attrs.xml b/packages/SystemUI/res/values/attrs.xml
index 3121ce3..db69924 100644
--- a/packages/SystemUI/res/values/attrs.xml
+++ b/packages/SystemUI/res/values/attrs.xml
@@ -77,6 +77,7 @@
<attr name="numColumns" format="integer" />
<attr name="verticalSpacing" format="dimension" />
<attr name="horizontalSpacing" format="dimension" />
+ <attr name="fixedChildWidth" format="dimension" />
</declare-styleable>
<!-- Theme for icons in the status/nav bar (light/dark). background/fillColor is used for dual
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index 9f25746..e8ebedd 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -1439,10 +1439,10 @@
<!-- Output switcher panel related dimensions -->
<dimen name="media_output_dialog_list_margin">12dp</dimen>
<dimen name="media_output_dialog_list_max_height">364dp</dimen>
- <dimen name="media_output_dialog_header_album_icon_size">52dp</dimen>
- <dimen name="media_output_dialog_header_back_icon_size">36dp</dimen>
+ <dimen name="media_output_dialog_header_album_icon_size">48dp</dimen>
+ <dimen name="media_output_dialog_header_back_icon_size">32dp</dimen>
<dimen name="media_output_dialog_header_icon_padding">16dp</dimen>
- <dimen name="media_output_dialog_icon_corner_radius">16dp</dimen>
+ <dimen name="media_output_dialog_icon_corner_radius">8dp</dimen>
<dimen name="media_output_dialog_title_anim_y_delta">12.5dp</dimen>
<!-- Distance that the full shade transition takes in order for qs to fully transition to the
@@ -1639,4 +1639,9 @@
<item name="communal_source_height_percentage" format="float" type="dimen">0.80</item>
<dimen name="drag_and_drop_icon_size">70dp</dimen>
+
+ <dimen name="qs_dialog_button_horizontal_padding">16dp</dimen>
+ <dimen name="qs_dialog_button_vertical_padding">8dp</dimen>
+ <!-- The button will be 48dp tall, but the background needs to be 36dp tall -->
+ <dimen name="qs_dialog_button_vertical_inset">6dp</dimen>
</resources>
diff --git a/packages/SystemUI/res/values/flags.xml b/packages/SystemUI/res/values/flags.xml
index d442cc5..d6d98b9 100644
--- a/packages/SystemUI/res/values/flags.xml
+++ b/packages/SystemUI/res/values/flags.xml
@@ -46,9 +46,13 @@
<bool name="flag_ongoing_call_status_bar_chip">true</bool>
+ <bool name="flag_ongoing_call_in_immersive">false</bool>
+
<bool name="flag_smartspace">false</bool>
<bool name="flag_smartspace_deduping">true</bool>
<bool name="flag_combined_status_bar_signal_icons">false</bool>
+
+ <bool name="flag_new_user_switcher">true</bool>
</resources>
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index c26de37..523b9c8 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -2830,6 +2830,8 @@
<string name="controls_media_settings_button">Settings</string>
<!-- Description for media control's playing media item, including information for the media's title, the artist, and source app [CHAR LIMIT=NONE]-->
<string name="controls_media_playing_item_description"><xliff:g id="song_name" example="Daily mix">%1$s</xliff:g> by <xliff:g id="artist_name" example="Various artists">%2$s</xliff:g> is playing from <xliff:g id="app_label" example="Spotify">%3$s</xliff:g></string>
+ <!-- Content description for media cotnrols progress bar [CHAR_LIMIT=NONE] -->
+ <string name="controls_media_seekbar_description"><xliff:g id="elapsed_time" example="1:30">%1$s</xliff:g> of <xliff:g id="total_time" example="3:00">%2$s</xliff:g></string>
<!-- Title for Smartspace recommendation card within media controls. The "Play" means the action to play a media [CHAR_LIMIT=10] -->
<string name="controls_media_smartspace_rec_title">Play</string>
@@ -3026,4 +3028,7 @@
<string name="see_all_networks">See all</string>
<!-- Summary for warning to disconnect ethernet first then switch to other networks. [CHAR LIMIT=60] -->
<string name="to_switch_networks_disconnect_ethernet">To switch networks, disconnect ethernet</string>
+
+ <!-- Title for User Switch dialog. [CHAR LIMIT=20] -->
+ <string name="qs_user_switch_dialog_title">Select user</string>
</resources>
diff --git a/packages/SystemUI/res/values/styles.xml b/packages/SystemUI/res/values/styles.xml
index 6594216..3fff699 100644
--- a/packages/SystemUI/res/values/styles.xml
+++ b/packages/SystemUI/res/values/styles.xml
@@ -513,6 +513,10 @@
<item name="android:background">@drawable/btn_borderless_rect</item>
</style>
+ <style name="MediaOutputRoundedOutlinedButton" parent="@android:style/Widget.Material.Button">
+ <item name="android:background">@drawable/media_output_dialog_button_background</item>
+ </style>
+
<style name="TunerSettings" parent="@android:style/Theme.DeviceDefault.Settings">
<item name="android:windowActionBar">false</item>
<item name="preferenceTheme">@style/TunerPreferenceTheme</item>
@@ -929,6 +933,39 @@
<item name="actionDividerHeight">32dp</item>
</style>
+ <style name="Theme.SystemUI.Dialog.QSDialog">
+ <item name="android:windowIsTranslucent">true</item>
+ <item name="android:windowBackground">@android:color/transparent</item>
+ <item name="android:windowIsFloating">true</item>
+ <item name="android:backgroundDimEnabled">true</item>
+ <item name="android:windowCloseOnTouchOutside">true</item>
+ <item name="android:windowAnimationStyle">@android:style/Animation.Dialog</item>
+ <item name="android:dialogCornerRadius">28dp</item>
+ <item name="android:buttonCornerRadius">28dp</item>
+ <item name="android:colorBackground">@color/prv_color_surface</item>
+ </style>
+
+ <style name="TextAppearance.QSDialog.Title" parent="Theme.SystemUI.Dialog.QSDialog">
+ <item name="android:textColor">?android:attr/textColorPrimary</item>
+ <item name="android:textSize">24sp</item>
+ <item name="android:fontFamily">@*android:string/config_headlineFontFamily</item>
+ <item name="android:lineHeight">32sp</item>
+ </style>
+
+ <style name="Widget.QSDialog.Button" parent = "Theme.SystemUI.Dialog.QSDialog">
+ <item name="android:background">@drawable/qs_dialog_btn_filled</item>
+ <item name="android:textColor">@color/prv_text_color_on_accent</item>
+ <item name="android:textSize">14sp</item>
+ <item name="android:lineHeight">20sp</item>
+ <item name="android:fontFamily">@*android:string/config_bodyFontFamily</item>
+ <item name="android:stateListAnimator">@null</item>
+ </style>
+
+ <style name="Widget.QSDialog.Button.BorderButton">
+ <item name="android:background">@drawable/qs_dialog_btn_outline</item>
+ <item name="android:textColor">?android:attr/textColorPrimary</item>
+ </style>
+
<style name="Theme.SystemUI.Dialog.Internet">
<item name="android:windowBackground">@drawable/internet_dialog_background</item>
</style>
diff --git a/packages/SystemUI/shared/Android.bp b/packages/SystemUI/shared/Android.bp
index 3a23094..4880b12 100644
--- a/packages/SystemUI/shared/Android.bp
+++ b/packages/SystemUI/shared/Android.bp
@@ -43,6 +43,7 @@
"src/**/*.kt",
"src/**/*.aidl",
":wm_shell-aidls",
+ ":wm_shell_util-sources",
],
static_libs: [
@@ -50,5 +51,5 @@
"androidx.dynamicanimation_dynamicanimation",
],
java_version: "1.8",
- min_sdk_version: "26",
+ min_sdk_version: "current",
}
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteAnimationAdapterCompat.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteAnimationAdapterCompat.java
index 7aee721..dcc4ea1 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteAnimationAdapterCompat.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteAnimationAdapterCompat.java
@@ -40,7 +40,7 @@
import android.window.RemoteTransition;
import android.window.TransitionInfo;
-import java.util.ArrayList;
+import com.android.wm.shell.util.CounterRotator;
/**
* @see RemoteAnimationAdapter
@@ -109,52 +109,6 @@
};
}
- private static class CounterRotator {
- SurfaceControl mSurface = null;
- ArrayList<SurfaceControl> mRotateChildren = null;
-
- void setup(SurfaceControl.Transaction t, SurfaceControl parent, int rotateDelta,
- float displayW, float displayH) {
- if (rotateDelta == 0) return;
- mRotateChildren = new ArrayList<>();
- // We want to counter-rotate, so subtract from 4
- rotateDelta = 4 - (rotateDelta + 4) % 4;
- mSurface = new SurfaceControl.Builder()
- .setName("Transition Unrotate")
- .setContainerLayer()
- .setParent(parent)
- .build();
- // column-major
- if (rotateDelta == 1) {
- t.setMatrix(mSurface, 0, 1, -1, 0);
- t.setPosition(mSurface, displayW, 0);
- } else if (rotateDelta == 2) {
- t.setMatrix(mSurface, -1, 0, 0, -1);
- t.setPosition(mSurface, displayW, displayH);
- } else if (rotateDelta == 3) {
- t.setMatrix(mSurface, 0, -1, 1, 0);
- t.setPosition(mSurface, 0, displayH);
- }
- t.show(mSurface);
- }
-
- void addChild(SurfaceControl.Transaction t, SurfaceControl child) {
- if (mSurface == null) return;
- t.reparent(child, mSurface);
- mRotateChildren.add(child);
- }
-
- void cleanUp(SurfaceControl rootLeash) {
- if (mSurface == null) return;
- SurfaceControl.Transaction t = new SurfaceControl.Transaction();
- for (int i = mRotateChildren.size() - 1; i >= 0; --i) {
- t.reparent(mRotateChildren.get(i), rootLeash);
- }
- t.remove(mSurface);
- t.apply();
- }
- }
-
private static IRemoteTransition.Stub wrapRemoteTransition(
final RemoteAnimationRunnerCompat remoteAnimationAdapter) {
return new IRemoteTransition.Stub() {
@@ -204,14 +158,14 @@
if (launcherTask != null && rotateDelta != 0 && launcherTask.getParent() != null) {
counterLauncher.setup(t, info.getChange(launcherTask.getParent()).getLeash(),
rotateDelta, displayW, displayH);
- if (counterLauncher.mSurface != null) {
- t.setLayer(counterLauncher.mSurface, launcherLayer);
+ if (counterLauncher.getSurface() != null) {
+ t.setLayer(counterLauncher.getSurface(), launcherLayer);
}
}
if (isReturnToHome) {
- if (counterLauncher.mSurface != null) {
- t.setLayer(counterLauncher.mSurface, info.getChanges().size() * 3);
+ if (counterLauncher.getSurface() != null) {
+ t.setLayer(counterLauncher.getSurface(), info.getChanges().size() * 3);
}
// Need to "boost" the closing things since that's what launcher expects.
for (int i = info.getChanges().size() - 1; i >= 0; --i) {
@@ -237,8 +191,8 @@
if (wallpaper != null && rotateDelta != 0 && wallpaper.getParent() != null) {
counterWallpaper.setup(t, info.getChange(wallpaper.getParent()).getLeash(),
rotateDelta, displayW, displayH);
- if (counterWallpaper.mSurface != null) {
- t.setLayer(counterWallpaper.mSurface, -1);
+ if (counterWallpaper.getSurface() != null) {
+ t.setLayer(counterWallpaper.getSurface(), -1);
counterWallpaper.addChild(t, leashMap.get(wallpaper.getLeash()));
}
}
diff --git a/packages/SystemUI/src/com/android/keyguard/AnimatableClockController.java b/packages/SystemUI/src/com/android/keyguard/AnimatableClockController.java
index efcf40a..a383cab 100644
--- a/packages/SystemUI/src/com/android/keyguard/AnimatableClockController.java
+++ b/packages/SystemUI/src/com/android/keyguard/AnimatableClockController.java
@@ -20,12 +20,16 @@
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
+import android.content.res.Resources;
import android.graphics.Color;
import android.icu.text.NumberFormat;
+import androidx.annotation.VisibleForTesting;
+
import com.android.settingslib.Utils;
import com.android.systemui.R;
import com.android.systemui.broadcast.BroadcastDispatcher;
+import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.statusbar.phone.KeyguardBypassController;
import com.android.systemui.statusbar.policy.BatteryController;
@@ -67,20 +71,20 @@
BroadcastDispatcher broadcastDispatcher,
BatteryController batteryController,
KeyguardUpdateMonitor keyguardUpdateMonitor,
- KeyguardBypassController bypassController) {
+ KeyguardBypassController bypassController,
+ @Main Resources resources
+ ) {
super(view);
mStatusBarStateController = statusBarStateController;
- mIsDozing = mStatusBarStateController.isDozing();
- mDozeAmount = mStatusBarStateController.getDozeAmount();
mBroadcastDispatcher = broadcastDispatcher;
mKeyguardUpdateMonitor = keyguardUpdateMonitor;
mBypassController = bypassController;
mBatteryController = batteryController;
mBurmeseNumerals = mBurmeseNf.format(FORMAT_NUMBER);
- mBurmeseLineSpacing = getContext().getResources().getFloat(
+ mBurmeseLineSpacing = resources.getFloat(
R.dimen.keyguard_clock_line_spacing_scale_burmese);
- mDefaultLineSpacing = getContext().getResources().getFloat(
+ mDefaultLineSpacing = resources.getFloat(
R.dimen.keyguard_clock_line_spacing_scale);
}
@@ -106,7 +110,7 @@
}
};
- private final StatusBarStateController.StateListener mStatusBarStatePersistentListener =
+ private final StatusBarStateController.StateListener mStatusBarStateListener =
new StatusBarStateController.StateListener() {
@Override
public void onDozeAmountChanged(float linear, float eased) {
@@ -144,11 +148,11 @@
mBroadcastDispatcher.registerReceiver(mLocaleBroadcastReceiver,
new IntentFilter(Intent.ACTION_LOCALE_CHANGED));
mDozeAmount = mStatusBarStateController.getDozeAmount();
+ mIsDozing = mStatusBarStateController.isDozing() || mDozeAmount != 0;
mBatteryController.addCallback(mBatteryCallback);
mKeyguardUpdateMonitor.registerCallback(mKeyguardUpdateMonitorCallback);
- mStatusBarStateController.removeCallback(mStatusBarStatePersistentListener);
- mStatusBarStateController.addCallback(mStatusBarStatePersistentListener);
+ mStatusBarStateController.addCallback(mStatusBarStateListener);
refreshTime();
initColors();
@@ -160,7 +164,7 @@
mBroadcastDispatcher.unregisterReceiver(mLocaleBroadcastReceiver);
mKeyguardUpdateMonitor.removeCallback(mKeyguardUpdateMonitorCallback);
mBatteryController.removeCallback(mBatteryCallback);
- mStatusBarStateController.removeCallback(mStatusBarStatePersistentListener);
+ mStatusBarStateController.removeCallback(mStatusBarStateListener);
}
/** Animate the clock appearance */
@@ -189,6 +193,14 @@
mView.refreshFormat();
}
+ /**
+ * Return locallly stored dozing state.
+ */
+ @VisibleForTesting
+ public boolean isDozing() {
+ return mIsDozing;
+ }
+
private void updateLocale() {
Locale currLocale = Locale.getDefault();
if (!Objects.equals(currLocale, mLocale)) {
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java
index 260b393..4111020 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java
@@ -22,6 +22,7 @@
import static com.android.keyguard.KeyguardClockSwitch.LARGE;
import android.app.WallpaperManager;
+import android.content.res.Resources;
import android.text.TextUtils;
import android.view.View;
import android.widget.FrameLayout;
@@ -32,6 +33,7 @@
import com.android.systemui.R;
import com.android.systemui.broadcast.BroadcastDispatcher;
import com.android.systemui.colorextraction.SysuiColorExtractor;
+import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.keyguard.KeyguardUnlockAnimationController;
import com.android.systemui.plugins.ClockPlugin;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
@@ -67,6 +69,7 @@
private final BroadcastDispatcher mBroadcastDispatcher;
private final BatteryController mBatteryController;
private final LockscreenSmartspaceController mSmartspaceController;
+ private final Resources mResources;
/**
* Clock for both small and large sizes
@@ -118,7 +121,8 @@
KeyguardBypassController bypassController,
LockscreenSmartspaceController smartspaceController,
KeyguardUnlockAnimationController keyguardUnlockAnimationController,
- SmartspaceTransitionController smartspaceTransitionController) {
+ SmartspaceTransitionController smartspaceTransitionController,
+ @Main Resources resources) {
super(keyguardClockSwitch);
mStatusBarStateController = statusBarStateController;
mColorExtractor = colorExtractor;
@@ -130,6 +134,7 @@
mKeyguardUpdateMonitor = keyguardUpdateMonitor;
mBypassController = bypassController;
mSmartspaceController = smartspaceController;
+ mResources = resources;
mKeyguardUnlockAnimationController = keyguardUnlockAnimationController;
mSmartspaceTransitionController = smartspaceTransitionController;
@@ -159,7 +164,8 @@
mBroadcastDispatcher,
mBatteryController,
mKeyguardUpdateMonitor,
- mBypassController);
+ mBypassController,
+ mResources);
mClockViewController.init();
mLargeClockViewController =
@@ -169,7 +175,8 @@
mBroadcastDispatcher,
mBatteryController,
mKeyguardUpdateMonitor,
- mBypassController);
+ mBypassController,
+ mResources);
mLargeClockViewController.init();
}
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java
index d80a408..0328b5a 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java
@@ -21,6 +21,8 @@
import static java.lang.Integer.max;
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
import android.animation.ValueAnimator;
import android.app.Activity;
import android.app.AlertDialog;
@@ -35,7 +37,6 @@
import android.view.VelocityTracker;
import android.view.View;
import android.view.ViewConfiguration;
-import android.view.ViewPropertyAnimator;
import android.view.WindowInsets;
import android.view.WindowInsetsAnimation;
import android.view.WindowManager;
@@ -43,6 +44,7 @@
import android.view.animation.Interpolator;
import android.widget.FrameLayout;
+import androidx.annotation.Nullable;
import androidx.annotation.VisibleForTesting;
import androidx.dynamicanimation.animation.DynamicAnimation;
import androidx.dynamicanimation.animation.SpringAnimation;
@@ -111,7 +113,7 @@
private boolean mIsSecurityViewLeftAligned = true;
private boolean mOneHandedMode = false;
- private ViewPropertyAnimator mRunningOneHandedAnimator;
+ @Nullable private ValueAnimator mRunningOneHandedAnimator;
private final WindowInsetsAnimation.Callback mWindowInsetsAnimationCallback =
new WindowInsetsAnimation.Callback(DISPATCH_MODE_STOP) {
@@ -347,9 +349,9 @@
Interpolator fadeOutInterpolator = Interpolators.FAST_OUT_LINEAR_IN;
Interpolator fadeInInterpolator = Interpolators.LINEAR_OUT_SLOW_IN;
- ValueAnimator anim = ValueAnimator.ofFloat(0.0f, 1.0f);
- anim.setDuration(BOUNCER_HANDEDNESS_ANIMATION_DURATION_MS);
- anim.setInterpolator(Interpolators.LINEAR);
+ mRunningOneHandedAnimator = ValueAnimator.ofFloat(0.0f, 1.0f);
+ mRunningOneHandedAnimator.setDuration(BOUNCER_HANDEDNESS_ANIMATION_DURATION_MS);
+ mRunningOneHandedAnimator.setInterpolator(Interpolators.LINEAR);
int initialTranslation = (int) mSecurityViewFlipper.getTranslationX();
int totalTranslation = (int) getResources().getDimension(
@@ -361,7 +363,15 @@
mSecurityViewFlipper.setLayerType(View.LAYER_TYPE_HARDWARE, /* paint= */null);
}
- anim.addUpdateListener(animation -> {
+ float initialAlpha = mSecurityViewFlipper.getAlpha();
+
+ mRunningOneHandedAnimator.addListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ mRunningOneHandedAnimator = null;
+ }
+ });
+ mRunningOneHandedAnimator.addUpdateListener(animation -> {
float switchPoint = BOUNCER_HANDEDNESS_ANIMATION_FADE_OUT_PROPORTION;
boolean isFadingOut = animation.getAnimatedFraction() < switchPoint;
@@ -378,13 +388,16 @@
if (isFadingOut) {
// The bouncer fades out over the first X%.
float fadeOutFraction = MathUtils.constrainedMap(
- /* rangeMin= */0.0f,
- /* rangeMax= */1.0f,
+ /* rangeMin= */1.0f,
+ /* rangeMax= */0.0f,
/* valueMin= */0.0f,
/* valueMax= */switchPoint,
animation.getAnimatedFraction());
float opacity = fadeOutInterpolator.getInterpolation(fadeOutFraction);
- mSecurityViewFlipper.setAlpha(1f - opacity);
+
+ // When fading out, the alpha needs to start from the initial opacity of the
+ // view flipper, otherwise we get a weird bit of jank as it ramps back to 100%.
+ mSecurityViewFlipper.setAlpha(opacity * initialAlpha);
// Animate away from the source.
mSecurityViewFlipper.setTranslationX(initialTranslation + currentTranslation);
@@ -409,7 +422,7 @@
}
});
- anim.start();
+ mRunningOneHandedAnimator.start();
} else {
mSecurityViewFlipper.setTranslationX(targetTranslation);
}
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardViewController.java
index ecc8c00..db729da 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardViewController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardViewController.java
@@ -18,7 +18,6 @@
import android.os.Bundle;
import android.view.View;
-import android.view.ViewGroup;
import android.view.ViewRootImpl;
import com.android.systemui.keyguard.KeyguardViewMediator;
@@ -186,14 +185,12 @@
/**
* Registers the StatusBar to which this Keyguard View is mounted.
* @param statusBar
- * @param container
* @param notificationPanelViewController
* @param biometricUnlockController
* @param notificationContainer
* @param bypassController
*/
void registerStatusBar(StatusBar statusBar,
- ViewGroup container,
NotificationPanelViewController notificationPanelViewController,
BiometricUnlockController biometricUnlockController,
View notificationContainer,
diff --git a/packages/SystemUI/src/com/android/keyguard/LockIconViewController.java b/packages/SystemUI/src/com/android/keyguard/LockIconViewController.java
index 8cfd225..9aa03a9 100644
--- a/packages/SystemUI/src/com/android/keyguard/LockIconViewController.java
+++ b/packages/SystemUI/src/com/android/keyguard/LockIconViewController.java
@@ -265,7 +265,7 @@
boolean wasShowingUnlockIcon = mShowUnlockIcon;
mShowLockIcon = !mCanDismissLockScreen && !mUserUnlockedWithBiometric && isLockScreen()
&& (!mUdfpsEnrolled || !mRunningFPS);
- mShowUnlockIcon = mCanDismissLockScreen && isLockScreen();
+ mShowUnlockIcon = (mCanDismissLockScreen || mUserUnlockedWithBiometric) && isLockScreen();
mShowAODFpIcon = mIsDozing && mUdfpsEnrolled && !mRunningFPS;
final CharSequence prevContentDescription = mView.getContentDescription();
@@ -477,13 +477,15 @@
@Override
public void onBiometricRunningStateChanged(boolean running,
BiometricSourceType biometricSourceType) {
+ final boolean wasRunningFps = mRunningFPS;
+ final boolean wasUserUnlockedWithBiometric = mUserUnlockedWithBiometric;
mUserUnlockedWithBiometric =
mKeyguardUpdateMonitor.getUserUnlockedWithBiometric(
KeyguardUpdateMonitor.getCurrentUser());
if (biometricSourceType == FINGERPRINT) {
mRunningFPS = running;
- if (!mRunningFPS) {
+ if (wasRunningFps && !mRunningFPS) {
if (mCancelDelayedUpdateVisibilityRunnable != null) {
mCancelDelayedUpdateVisibilityRunnable.run();
}
@@ -493,10 +495,14 @@
// button in this case, so we delay updating the visibility by 50ms.
mCancelDelayedUpdateVisibilityRunnable =
mExecutor.executeDelayed(() -> updateVisibility(), 50);
- } else {
- updateVisibility();
+ return;
}
}
+
+ if (wasUserUnlockedWithBiometric != mUserUnlockedWithBiometric
+ || wasRunningFps != mRunningFPS) {
+ updateVisibility();
+ }
}
};
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricView.java b/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricView.java
index 60b0637..f11dc93 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricView.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricView.java
@@ -824,11 +824,25 @@
return new AuthDialog.LayoutParams(width, totalHeight);
}
+ private boolean isLargeDisplay() {
+ return com.android.systemui.util.Utils.shouldUseSplitNotificationShade(getResources());
+ }
+
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
final int width = MeasureSpec.getSize(widthMeasureSpec);
final int height = MeasureSpec.getSize(heightMeasureSpec);
- final int newWidth = Math.min(width, height);
+
+ final boolean isLargeDisplay = isLargeDisplay();
+
+ final int newWidth;
+ if (isLargeDisplay) {
+ // TODO(b/201811580): Unless we can come up with a one-size-fits-all equation, we may
+ // want to consider moving this to an overlay.
+ newWidth = 2 * Math.min(width, height) / 3;
+ } else {
+ newWidth = Math.min(width, height);
+ }
// Use "newWidth" instead, so the landscape dialog width is the same as the portrait
// width.
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthPanelController.java b/packages/SystemUI/src/com/android/systemui/biometrics/AuthPanelController.java
index fa50f89..f1e42e0 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthPanelController.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthPanelController.java
@@ -117,24 +117,6 @@
mUseFullScreen = fullScreen;
}
- public ValueAnimator getTranslationAnimator(float relativeTranslationY) {
- final ValueAnimator animator = ValueAnimator.ofFloat(
- mPanelView.getY(), mPanelView.getY() - relativeTranslationY);
- animator.addUpdateListener(animation -> {
- final float translation = (float) animation.getAnimatedValue();
- mPanelView.setTranslationY(translation);
- });
- return animator;
- }
-
- public ValueAnimator getAlphaAnimator(float alpha) {
- final ValueAnimator animator = ValueAnimator.ofFloat(mPanelView.getAlpha(), alpha);
- animator.addUpdateListener(animation -> {
- mPanelView.setAlpha((float) animation.getAnimatedValue());
- });
- return animator;
- }
-
public void updateForContentDimensions(int contentWidth, int contentHeight,
int animateDurationMs) {
if (DEBUG) {
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthRippleController.kt b/packages/SystemUI/src/com/android/systemui/biometrics/AuthRippleController.kt
index eb6b193..0932a8c 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthRippleController.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthRippleController.kt
@@ -18,7 +18,6 @@
import android.animation.ValueAnimator
import android.content.Context
-import android.content.res.Configuration
import android.graphics.PointF
import android.hardware.biometrics.BiometricSourceType
import android.util.DisplayMetrics
@@ -125,6 +124,7 @@
return
}
+ updateSensorLocation()
if (biometricSourceType == BiometricSourceType.FINGERPRINT &&
fingerprintSensorLocation != null) {
mView.setSensorLocation(fingerprintSensorLocation!!)
@@ -266,9 +266,6 @@
private val configurationChangedListener =
object : ConfigurationController.ConfigurationListener {
- override fun onConfigChanged(newConfig: Configuration?) {
- updateSensorLocation()
- }
override fun onUiModeChanged() {
updateRippleColor()
}
diff --git a/packages/SystemUI/src/com/android/systemui/flags/FeatureFlags.java b/packages/SystemUI/src/com/android/systemui/flags/FeatureFlags.java
index e51f90f..f81811a 100644
--- a/packages/SystemUI/src/com/android/systemui/flags/FeatureFlags.java
+++ b/packages/SystemUI/src/com/android/systemui/flags/FeatureFlags.java
@@ -161,6 +161,11 @@
return mFlagReader.isEnabled(R.bool.flag_ongoing_call_status_bar_chip);
}
+ public boolean isOngoingCallInImmersiveEnabled() {
+ return isOngoingCallStatusBarChipEnabled()
+ && mFlagReader.isEnabled(R.bool.flag_ongoing_call_in_immersive);
+ }
+
public boolean isSmartspaceEnabled() {
return mFlagReader.isEnabled(R.bool.flag_smartspace);
}
@@ -187,6 +192,13 @@
return FeatureFlagUtils.isEnabled(mContext, FeatureFlagUtils.SETTINGS_PROVIDER_MODEL);
}
+ /**
+ * Use the new version of the user switcher
+ */
+ public boolean useNewUserSwitcher() {
+ return mFlagReader.isEnabled(R.bool.flag_new_user_switcher);
+ }
+
/** static method for the system setting */
public static boolean isProviderModelSettingEnabled(Context context) {
return FeatureFlagUtils.isEnabled(context, FeatureFlagUtils.SETTINGS_PROVIDER_MODEL);
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
index 1561eb6..c569439 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
@@ -77,7 +77,6 @@
import android.view.RemoteAnimationTarget;
import android.view.SyncRtSurfaceTransactionApplier;
import android.view.View;
-import android.view.ViewGroup;
import android.view.WindowManager;
import android.view.WindowManagerPolicyConstants;
import android.view.animation.Animation;
@@ -2623,7 +2622,6 @@
* Registers the StatusBar to which the Keyguard View is mounted.
*
* @param statusBar
- * @param container
* @param panelView
* @param biometricUnlockController
* @param notificationContainer
@@ -2631,10 +2629,10 @@
* @return the View Controller for the Keyguard View this class is mediating.
*/
public KeyguardViewController registerStatusBar(StatusBar statusBar,
- ViewGroup container, NotificationPanelViewController panelView,
+ NotificationPanelViewController panelView,
BiometricUnlockController biometricUnlockController,
View notificationContainer, KeyguardBypassController bypassController) {
- mKeyguardViewControllerLazy.get().registerStatusBar(statusBar, container, panelView,
+ mKeyguardViewControllerLazy.get().registerStatusBar(statusBar, panelView,
biometricUnlockController, notificationContainer, bypassController);
return mKeyguardViewControllerLazy.get();
}
diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaCarouselController.kt b/packages/SystemUI/src/com/android/systemui/media/MediaCarouselController.kt
index c743fe1..0e70945 100644
--- a/packages/SystemUI/src/com/android/systemui/media/MediaCarouselController.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/MediaCarouselController.kt
@@ -833,11 +833,12 @@
)
private val comparator =
- compareByDescending<MediaSortKey> { it.data.isPlaying }
+ compareByDescending<MediaSortKey> { it.data.isPlaying == true && it.data.isLocalSession }
+ .thenByDescending { it.data.isPlaying }
.thenByDescending { if (shouldPrioritizeSs) it.isSsMediaRec else !it.isSsMediaRec }
- .thenByDescending { it.data.isLocalSession }
.thenByDescending { !it.data.resumption }
.thenByDescending { it.updateTime }
+ .thenByDescending { !it.data.isLocalSession }
private val mediaPlayers = TreeMap<MediaSortKey, MediaControlPanel>(comparator)
private val mediaData: MutableMap<String, MediaSortKey> = mutableMapOf()
diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaControlPanel.java b/packages/SystemUI/src/com/android/systemui/media/MediaControlPanel.java
index 424f801..e73eb66 100644
--- a/packages/SystemUI/src/com/android/systemui/media/MediaControlPanel.java
+++ b/packages/SystemUI/src/com/android/systemui/media/MediaControlPanel.java
@@ -55,6 +55,7 @@
import com.android.systemui.dagger.qualifiers.Background;
import com.android.systemui.media.dialog.MediaOutputDialogFactory;
import com.android.systemui.plugins.ActivityStarter;
+import com.android.systemui.plugins.FalsingManager;
import com.android.systemui.shared.system.SysUiStatsLog;
import com.android.systemui.statusbar.phone.KeyguardDismissUtil;
import com.android.systemui.util.animation.TransitionLayout;
@@ -119,6 +120,7 @@
private int mSmartspaceMediaItemsCount;
private MediaCarouselController mMediaCarouselController;
private final MediaOutputDialogFactory mMediaOutputDialogFactory;
+ private final FalsingManager mFalsingManager;
/**
* Initialize a new control panel
@@ -131,7 +133,8 @@
ActivityStarter activityStarter, MediaViewController mediaViewController,
SeekBarViewModel seekBarViewModel, Lazy<MediaDataManager> lazyMediaDataManager,
KeyguardDismissUtil keyguardDismissUtil, MediaOutputDialogFactory
- mediaOutputDialogFactory, MediaCarouselController mediaCarouselController) {
+ mediaOutputDialogFactory, MediaCarouselController mediaCarouselController,
+ FalsingManager falsingManager) {
mContext = context;
mBackgroundExecutor = backgroundExecutor;
mActivityStarter = activityStarter;
@@ -141,6 +144,7 @@
mKeyguardDismissUtil = keyguardDismissUtil;
mMediaOutputDialogFactory = mediaOutputDialogFactory;
mMediaCarouselController = mediaCarouselController;
+ mFalsingManager = falsingManager;
loadDimens();
mSeekBarViewModel.setLogSmartspaceClick(() -> {
@@ -235,10 +239,14 @@
}
});
mPlayerViewHolder.getCancel().setOnClickListener(v -> {
- closeGuts();
+ if (!mFalsingManager.isFalseTap(FalsingManager.LOW_PENALTY)) {
+ closeGuts();
+ }
});
mPlayerViewHolder.getSettings().setOnClickListener(v -> {
- mActivityStarter.startActivity(SETTINGS_INTENT, true /* dismissShade */);
+ if (!mFalsingManager.isFalseTap(FalsingManager.LOW_PENALTY)) {
+ mActivityStarter.startActivity(SETTINGS_INTENT, true /* dismissShade */);
+ }
});
}
@@ -259,10 +267,14 @@
}
});
mRecommendationViewHolder.getCancel().setOnClickListener(v -> {
- closeGuts();
+ if (!mFalsingManager.isFalseTap(FalsingManager.LOW_PENALTY)) {
+ closeGuts();
+ }
});
mRecommendationViewHolder.getSettings().setOnClickListener(v -> {
- mActivityStarter.startActivity(SETTINGS_INTENT, true /* dismissShade */);
+ if (!mFalsingManager.isFalseTap(FalsingManager.LOW_PENALTY)) {
+ mActivityStarter.startActivity(SETTINGS_INTENT, true /* dismissShade */);
+ }
});
}
@@ -299,6 +311,7 @@
PendingIntent clickIntent = data.getClickIntent();
if (clickIntent != null) {
mPlayerViewHolder.getPlayer().setOnClickListener(v -> {
+ if (mFalsingManager.isFalseTap(FalsingManager.LOW_PENALTY)) return;
if (mMediaViewController.isGutsVisible()) return;
logSmartspaceCardReported(760, // SMARTSPACE_CARD_CLICK
@@ -365,8 +378,12 @@
setVisibleAndAlpha(collapsedSet, R.id.media_seamless, true /*visible */);
setVisibleAndAlpha(expandedSet, R.id.media_seamless, true /*visible */);
seamlessView.setOnClickListener(
- v -> mMediaOutputDialogFactory.create(data.getPackageName(), true,
- mPlayerViewHolder.getSeamlessButton()));
+ v -> {
+ if (!mFalsingManager.isFalseTap(FalsingManager.LOW_PENALTY)) {
+ mMediaOutputDialogFactory.create(data.getPackageName(), true,
+ mPlayerViewHolder.getSeamlessButton());
+ }
+ });
ImageView iconView = mPlayerViewHolder.getSeamlessIcon();
TextView deviceName = mPlayerViewHolder.getSeamlessText();
@@ -417,9 +434,11 @@
} else {
button.setEnabled(true);
button.setOnClickListener(v -> {
- logSmartspaceCardReported(760, // SMARTSPACE_CARD_CLICK
- /* isRecommendationCard */ false);
- action.run();
+ if (!mFalsingManager.isFalseTap(FalsingManager.LOW_PENALTY)) {
+ logSmartspaceCardReported(760, // SMARTSPACE_CARD_CLICK
+ /* isRecommendationCard */ false);
+ action.run();
+ }
});
}
boolean visibleInCompat = actionsWhenCollapsed.contains(i);
@@ -451,6 +470,8 @@
mPlayerViewHolder.getDismissLabel().setAlpha(isDismissible ? 1 : DISABLED_ALPHA);
mPlayerViewHolder.getDismiss().setEnabled(isDismissible);
mPlayerViewHolder.getDismiss().setOnClickListener(v -> {
+ if (mFalsingManager.isFalseTap(FalsingManager.LOW_PENALTY)) return;
+
logSmartspaceCardReported(761, // SMARTSPACE_CARD_DISMISS
/* isRecommendationCard */ false);
@@ -633,6 +654,8 @@
mSmartspaceMediaItemsCount = uiComponentIndex;
// Set up long press to show guts setting panel.
mRecommendationViewHolder.getDismiss().setOnClickListener(v -> {
+ if (mFalsingManager.isFalseTap(FalsingManager.LOW_PENALTY)) return;
+
logSmartspaceCardReported(761, // SMARTSPACE_CARD_DISMISS
/* isRecommendationCard */ true);
closeGuts();
@@ -788,6 +811,8 @@
}
view.setOnClickListener(v -> {
+ if (mFalsingManager.isFalseTap(FalsingManager.LOW_PENALTY)) return;
+
logSmartspaceCardReported(760, // SMARTSPACE_CARD_CLICK
/* isRecommendationCard */ true,
interactedSubcardRank,
diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaDataManager.kt b/packages/SystemUI/src/com/android/systemui/media/MediaDataManager.kt
index 06a1eea..3631d2f 100644
--- a/packages/SystemUI/src/com/android/systemui/media/MediaDataManager.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/MediaDataManager.kt
@@ -768,7 +768,7 @@
val resumeAction = getResumeMediaAction(removed.resumeAction!!)
val updated = removed.copy(token = null, actions = listOf(resumeAction),
actionsToShowInCompact = listOf(0), active = false, resumption = true,
- isClearable = true)
+ isPlaying = false, isClearable = true)
val pkg = removed.packageName
val migrate = mediaEntries.put(pkg, updated) == null
// Notify listeners of "new" controls when migrating or removed and update when not
diff --git a/packages/SystemUI/src/com/android/systemui/media/SeekBarObserver.kt b/packages/SystemUI/src/com/android/systemui/media/SeekBarObserver.kt
index f17ad6f..33ef19a 100644
--- a/packages/SystemUI/src/com/android/systemui/media/SeekBarObserver.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/SeekBarObserver.kt
@@ -50,6 +50,7 @@
holder.seekBar.setProgress(0)
holder.elapsedTimeView.setText("")
holder.totalTimeView.setText("")
+ holder.seekBar.contentDescription = ""
return
}
@@ -61,16 +62,22 @@
setVerticalPadding(seekBarEnabledVerticalPadding)
}
- data.duration?.let {
- holder.seekBar.setMax(it)
- holder.totalTimeView.setText(DateUtils.formatElapsedTime(
- it / DateUtils.SECOND_IN_MILLIS))
- }
+ holder.seekBar.setMax(data.duration)
+ val totalTimeString = DateUtils.formatElapsedTime(
+ data.duration / DateUtils.SECOND_IN_MILLIS)
+ holder.totalTimeView.setText(totalTimeString)
data.elapsedTime?.let {
holder.seekBar.setProgress(it)
- holder.elapsedTimeView.setText(DateUtils.formatElapsedTime(
- it / DateUtils.SECOND_IN_MILLIS))
+ val elapsedTimeString = DateUtils.formatElapsedTime(
+ it / DateUtils.SECOND_IN_MILLIS)
+ holder.elapsedTimeView.setText(elapsedTimeString)
+
+ holder.seekBar.contentDescription = holder.seekBar.context.getString(
+ R.string.controls_media_seekbar_description,
+ elapsedTimeString,
+ totalTimeString
+ )
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputAdapter.java b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputAdapter.java
index d1b6548..125b87b 100644
--- a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputAdapter.java
+++ b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputAdapter.java
@@ -135,14 +135,11 @@
if (currentlyConnected && mController.isActiveRemoteDevice(device)
&& mController.getSelectableMediaDevice().size() > 0) {
// Init active device layout
- mDivider.setVisibility(View.VISIBLE);
- mDivider.setTransitionAlpha(1);
mAddIcon.setVisibility(View.VISIBLE);
mAddIcon.setTransitionAlpha(1);
mAddIcon.setOnClickListener(this::onEndItemClick);
} else {
// Init non-active device layout
- mDivider.setVisibility(View.GONE);
mAddIcon.setVisibility(View.GONE);
}
if (mCurrentActivePosition == position) {
@@ -181,7 +178,6 @@
super.onBind(customizedItem, topMargin, bottomMargin);
if (customizedItem == CUSTOMIZED_ITEM_PAIR_NEW) {
mCheckBox.setVisibility(View.GONE);
- mDivider.setVisibility(View.GONE);
mAddIcon.setVisibility(View.GONE);
mBottomDivider.setVisibility(View.GONE);
setSingleLineLayout(mContext.getText(R.string.media_output_dialog_pairing_new),
@@ -196,13 +192,10 @@
mBottomDivider.setVisibility(View.GONE);
mCheckBox.setVisibility(View.GONE);
if (mController.getSelectableMediaDevice().size() > 0) {
- mDivider.setVisibility(View.VISIBLE);
- mDivider.setTransitionAlpha(1);
mAddIcon.setVisibility(View.VISIBLE);
mAddIcon.setTransitionAlpha(1);
mAddIcon.setOnClickListener(this::onEndItemClick);
} else {
- mDivider.setVisibility(View.GONE);
mAddIcon.setVisibility(View.GONE);
}
mTitleIcon.setImageDrawable(getSpeakerDrawable());
diff --git a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBaseAdapter.java b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBaseAdapter.java
index 0890841..1ffc2c4 100644
--- a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBaseAdapter.java
+++ b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBaseAdapter.java
@@ -121,7 +121,6 @@
final ProgressBar mProgressBar;
final SeekBar mSeekBar;
final RelativeLayout mTwoLineLayout;
- final View mDivider;
final View mBottomDivider;
final CheckBox mCheckBox;
private String mDeviceId;
@@ -136,7 +135,6 @@
mTitleIcon = view.requireViewById(R.id.title_icon);
mProgressBar = view.requireViewById(R.id.volume_indeterminate_progress);
mSeekBar = view.requireViewById(R.id.volume_seekbar);
- mDivider = view.requireViewById(R.id.end_divider);
mBottomDivider = view.requireViewById(R.id.bottom_divider);
mAddIcon = view.requireViewById(R.id.add_icon);
mCheckBox = view.requireViewById(R.id.check_box);
@@ -151,21 +149,12 @@
return;
}
mTitleIcon.setImageIcon(icon);
- setMargin(topMargin, bottomMargin);
});
});
}
void onBind(int customizedItem, boolean topMargin, boolean bottomMargin) {
- setMargin(topMargin, bottomMargin);
- }
-
- private void setMargin(boolean topMargin, boolean bottomMargin) {
- ViewGroup.MarginLayoutParams params = (ViewGroup.MarginLayoutParams) mContainerLayout
- .getLayoutParams();
- params.topMargin = topMargin ? mMargin : 0;
- params.bottomMargin = bottomMargin ? mMargin : 0;
- mContainerLayout.setLayoutParams(params);
+ // TODO (b/201718621): clean up method after adjustment
}
void setSingleLineLayout(CharSequence title, boolean bFocused) {
diff --git a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBaseDialog.java b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBaseDialog.java
index 85d0802..6895ef1 100644
--- a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBaseDialog.java
+++ b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBaseDialog.java
@@ -175,7 +175,7 @@
}
if (!mAdapter.isDragging() && !mAdapter.isAnimating()) {
int currentActivePosition = mAdapter.getCurrentActivePosition();
- if (currentActivePosition >= 0) {
+ if (currentActivePosition >= 0 && currentActivePosition < mAdapter.getItemCount()) {
mAdapter.notifyItemChanged(currentActivePosition);
} else {
mAdapter.notifyDataSetChanged();
diff --git a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputController.java b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputController.java
index 437a0c8..42dd886 100644
--- a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputController.java
+++ b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputController.java
@@ -73,6 +73,7 @@
private final String mPackageName;
private final Context mContext;
private final MediaSessionManager mMediaSessionManager;
+ private final LocalBluetoothManager mLocalBluetoothManager;
private final ShadeController mShadeController;
private final ActivityStarter mActivityStarter;
private final DialogLaunchAnimator mDialogLaunchAnimator;
@@ -85,7 +86,6 @@
private MediaController mMediaController;
@VisibleForTesting
Callback mCallback;
- Callback mPreviousCallback;
@VisibleForTesting
LocalMediaManager mLocalMediaManager;
@@ -101,6 +101,7 @@
mContext = context;
mPackageName = packageName;
mMediaSessionManager = mediaSessionManager;
+ mLocalBluetoothManager = lbm;
mShadeController = shadeController;
mActivityStarter = starter;
mAboveStatusbar = aboveStatusbar;
@@ -135,19 +136,7 @@
}
return;
}
-
- if (mPreviousCallback != null) {
- Log.w(TAG,
- "Callback started when mPreviousCallback is not null, which is unexpected");
- mPreviousCallback.dismissDialog();
- }
-
- // If we start the output group dialog when the output dialog is shown, we need to keep a
- // reference to the output dialog to set it back as the callback once we dismiss the output
- // group dialog.
- mPreviousCallback = mCallback;
mCallback = cb;
-
mLocalMediaManager.unregisterCallback(this);
mLocalMediaManager.stopScan();
mLocalMediaManager.registerCallback(this);
@@ -163,15 +152,6 @@
mLocalMediaManager.stopScan();
}
mMediaDevices.clear();
-
- // If there was a previous callback, i.e. we just dismissed the output group dialog and are
- // now back on the output dialog, then we reset the callback to its previous value.
- mCallback = null;
- Callback previous = mPreviousCallback;
- mPreviousCallback = null;
- if (previous != null) {
- start(previous);
- }
}
@Override
@@ -480,7 +460,11 @@
void launchMediaOutputGroupDialog(View mediaOutputDialog) {
// We show the output group dialog from the output dialog.
- MediaOutputGroupDialog dialog = new MediaOutputGroupDialog(mContext, mAboveStatusbar, this);
+ MediaOutputController controller = new MediaOutputController(mContext, mPackageName,
+ mAboveStatusbar, mMediaSessionManager, mLocalBluetoothManager, mShadeController,
+ mActivityStarter, mNotificationEntryManager, mUiEventLogger, mDialogLaunchAnimator);
+ MediaOutputGroupDialog dialog = new MediaOutputGroupDialog(mContext, mAboveStatusbar,
+ controller);
mDialogLaunchAnimator.showFromView(dialog, mediaOutputDialog);
}
diff --git a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputGroupAdapter.java b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputGroupAdapter.java
index 968c350..11d76db 100644
--- a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputGroupAdapter.java
+++ b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputGroupAdapter.java
@@ -96,7 +96,6 @@
@Override
void onBind(MediaDevice device, boolean topMargin, boolean bottomMargin, int position) {
super.onBind(device, topMargin, bottomMargin, position);
- mDivider.setVisibility(View.GONE);
mAddIcon.setVisibility(View.GONE);
mBottomDivider.setVisibility(View.GONE);
mCheckBox.setVisibility(View.VISIBLE);
@@ -135,7 +134,6 @@
mTitleIcon.setImageDrawable(getSpeakerDrawable());
mBottomDivider.setVisibility(View.VISIBLE);
mCheckBox.setVisibility(View.GONE);
- mDivider.setVisibility(View.GONE);
mAddIcon.setVisibility(View.GONE);
initSessionSeekbar();
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/PseudoGridView.java b/packages/SystemUI/src/com/android/systemui/qs/PseudoGridView.java
index 87c64c7..2f189be 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/PseudoGridView.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/PseudoGridView.java
@@ -38,6 +38,7 @@
private int mNumColumns = 3;
private int mVerticalSpacing;
private int mHorizontalSpacing;
+ private int mFixedChildWidth = -1;
public PseudoGridView(Context context, AttributeSet attrs) {
super(context, attrs);
@@ -53,6 +54,8 @@
mVerticalSpacing = a.getDimensionPixelSize(attr, 0);
} else if (attr == R.styleable.PseudoGridView_horizontalSpacing) {
mHorizontalSpacing = a.getDimensionPixelSize(attr, 0);
+ } else if (attr == R.styleable.PseudoGridView_fixedChildWidth) {
+ mFixedChildWidth = a.getDimensionPixelSize(attr, -1);
}
}
@@ -65,8 +68,15 @@
throw new UnsupportedOperationException("Needs a maximum width");
}
int width = MeasureSpec.getSize(widthMeasureSpec);
-
- int childWidth = (width - (mNumColumns - 1) * mHorizontalSpacing) / mNumColumns;
+ int childWidth;
+ int necessarySpaceForChildWidth =
+ mFixedChildWidth * mNumColumns + mHorizontalSpacing * (mNumColumns - 1);
+ if (mFixedChildWidth != -1 && necessarySpaceForChildWidth <= width) {
+ childWidth = mFixedChildWidth;
+ width = mFixedChildWidth * mNumColumns + mHorizontalSpacing * (mNumColumns - 1);
+ } else {
+ childWidth = (width - (mNumColumns - 1) * mHorizontalSpacing) / mNumColumns;
+ }
int childWidthSpec = MeasureSpec.makeMeasureSpec(childWidth, MeasureSpec.EXACTLY);
int childHeightSpec = MeasureSpec.UNSPECIFIED;
int totalHeight = 0;
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSAnimator.java b/packages/SystemUI/src/com/android/systemui/qs/QSAnimator.java
index bfb63ea..90d3448 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSAnimator.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSAnimator.java
@@ -35,7 +35,6 @@
import com.android.systemui.qs.TouchAnimator.Listener;
import com.android.systemui.qs.dagger.QSScope;
import com.android.systemui.qs.tileimpl.HeightOverrideable;
-import com.android.systemui.statusbar.CrossFadeHelper;
import com.android.systemui.tuner.TunerService;
import com.android.systemui.tuner.TunerService.Tunable;
import com.android.wm.shell.animation.Interpolators;
@@ -170,19 +169,6 @@
}
}
- void startAlphaAnimation(boolean show) {
- if (show == mToShowing) {
- return;
- }
- mToShowing = show;
- if (show) {
- CrossFadeHelper.fadeIn(mQs.getView(), QQS_FADE_IN_DURATION, 0 /* delay */);
- } else {
- CrossFadeHelper.fadeOut(mQs.getView(), QQS_FADE_OUT_DURATION, 0 /* delay */,
- null /* endRunnable */);
- }
- }
-
/**
* Sets whether or not the keyguard is currently being shown with a collapsed header.
*/
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSContainerImpl.java b/packages/SystemUI/src/com/android/systemui/qs/QSContainerImpl.java
index 8e43661..3fc4f50 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSContainerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSContainerImpl.java
@@ -26,7 +26,6 @@
import android.graphics.PointF;
import android.util.AttributeSet;
import android.view.View;
-import android.view.WindowInsets;
import android.widget.FrameLayout;
import android.widget.ImageView;
@@ -58,7 +57,6 @@
private int mSideMargins;
private boolean mQsDisabled;
private int mContentPadding = -1;
- private int mNavBarInset = 0;
private boolean mClippingEnabled;
public QSContainerImpl(Context context, AttributeSet attrs) {
@@ -95,24 +93,13 @@
}
@Override
- public WindowInsets onApplyWindowInsets(WindowInsets insets) {
- mNavBarInset = insets.getInsets(WindowInsets.Type.navigationBars()).bottom;
- mQSPanelContainer.setPaddingRelative(
- mQSPanelContainer.getPaddingStart(),
- mQSPanelContainer.getPaddingTop(),
- mQSPanelContainer.getPaddingEnd(),
- mNavBarInset
- );
- return super.onApplyWindowInsets(insets);
- }
-
- @Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
// QSPanel will show as many rows as it can (up to TileLayout.MAX_ROWS) such that the
// bottom and footer are inside the screen.
MarginLayoutParams layoutParams = (MarginLayoutParams) mQSPanelContainer.getLayoutParams();
- int maxQs = getDisplayHeight() - layoutParams.topMargin - layoutParams.bottomMargin
+ int availableHeight = View.MeasureSpec.getSize(heightMeasureSpec);
+ int maxQs = availableHeight - layoutParams.topMargin - layoutParams.bottomMargin
- getPaddingBottom();
int padding = mPaddingLeft + mPaddingRight + layoutParams.leftMargin
+ layoutParams.rightMargin;
@@ -122,11 +109,11 @@
MeasureSpec.makeMeasureSpec(maxQs, MeasureSpec.AT_MOST));
int width = mQSPanelContainer.getMeasuredWidth() + padding;
super.onMeasure(MeasureSpec.makeMeasureSpec(width, MeasureSpec.EXACTLY),
- MeasureSpec.makeMeasureSpec(getDisplayHeight(), MeasureSpec.EXACTLY));
+ MeasureSpec.makeMeasureSpec(availableHeight, MeasureSpec.EXACTLY));
// QSCustomizer will always be the height of the screen, but do this after
// other measuring to avoid changing the height of the QS.
mQSCustomizer.measure(widthMeasureSpec,
- MeasureSpec.makeMeasureSpec(getDisplayHeight(), MeasureSpec.EXACTLY));
+ MeasureSpec.makeMeasureSpec(availableHeight, MeasureSpec.EXACTLY));
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSDetail.java b/packages/SystemUI/src/com/android/systemui/qs/QSDetail.java
index 929927e..58a942a 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSDetail.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSDetail.java
@@ -46,8 +46,8 @@
import com.android.systemui.plugins.ActivityStarter;
import com.android.systemui.plugins.FalsingManager;
import com.android.systemui.plugins.qs.DetailAdapter;
+import com.android.systemui.plugins.qs.QSContainerController;
import com.android.systemui.statusbar.CommandQueue;
-import com.android.systemui.statusbar.phone.NotificationsQuickSettingsContainer;
public class QSDetail extends LinearLayout {
@@ -86,7 +86,7 @@
private boolean mSwitchState;
private QSFooter mFooter;
- private NotificationsQuickSettingsContainer mContainer;
+ private QSContainerController mQsContainerController;
public QSDetail(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
@@ -120,8 +120,8 @@
mClipper = new QSDetailClipper(this);
}
- public void setContainer(NotificationsQuickSettingsContainer container) {
- mContainer = container;
+ public void setContainerController(QSContainerController controller) {
+ mQsContainerController = controller;
}
/** */
@@ -262,8 +262,8 @@
}
sendAccessibilityEvent(AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED);
animateDetailVisibleDiff(x, y, visibleDiff, listener);
- if (mContainer != null) {
- mContainer.setDetailShowing(showingDetail);
+ if (mQsContainerController != null) {
+ mQsContainerController.setDetailShowing(showingDetail);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java b/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java
index 6fd8141..f300efc 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java
@@ -43,6 +43,7 @@
import com.android.systemui.media.MediaHost;
import com.android.systemui.plugins.FalsingManager;
import com.android.systemui.plugins.qs.QS;
+import com.android.systemui.plugins.qs.QSContainerController;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.qs.customize.QSCustomizerController;
import com.android.systemui.qs.dagger.QSFragmentComponent;
@@ -50,7 +51,6 @@
import com.android.systemui.statusbar.StatusBarState;
import com.android.systemui.statusbar.notification.stack.StackStateAnimator;
import com.android.systemui.statusbar.phone.KeyguardBypassController;
-import com.android.systemui.statusbar.phone.NotificationsQuickSettingsContainer;
import com.android.systemui.statusbar.policy.BrightnessMirrorController;
import com.android.systemui.statusbar.policy.RemoteInputQuickSettingsDisabler;
import com.android.systemui.util.InjectionInflationController;
@@ -89,6 +89,7 @@
private int mLayoutDirection;
private QSFooter mFooter;
private float mLastQSExpansion = -1;
+ private float mLastPanelFraction;
private boolean mQsDisabled;
private ImageView mQsDragHandler;
@@ -120,7 +121,7 @@
* When true, QS will translate from outside the screen. It will be clipped with parallax
* otherwise.
*/
- private boolean mTranslateWhileExpanding;
+ private boolean mInSplitShade;
private boolean mPulseExpanding;
/**
@@ -135,6 +136,12 @@
private DumpManager mDumpManager;
+ /**
+ * Progress of pull down from the center of the lock screen.
+ * @see com.android.systemui.statusbar.LockscreenShadeTransitionController
+ */
+ private float mFullShadeProgress;
+
@Inject
public QSFragment(RemoteInputQuickSettingsDisabler remoteInputQsDisabler,
InjectionInflationController injectionInflater, QSTileHost qsTileHost,
@@ -226,7 +233,8 @@
(v, left, top, right, bottom, oldLeft, oldTop, oldRight, oldBottom) -> {
boolean sizeChanged = (oldTop - oldBottom) != (top - bottom);
if (sizeChanged) {
- setQsExpansion(mLastQSExpansion, mLastHeaderTranslation);
+ setQsExpansion(mLastQSExpansion, mLastPanelFraction,
+ mLastHeaderTranslation);
}
});
mQSPanelController.setUsingHorizontalLayoutChangeListener(
@@ -336,11 +344,9 @@
}
@Override
- public void setContainer(ViewGroup container) {
- if (container instanceof NotificationsQuickSettingsContainer) {
- mQSCustomizerController.setContainer((NotificationsQuickSettingsContainer) container);
- mQSDetail.setContainer((NotificationsQuickSettingsContainer) container);
- }
+ public void setContainerController(QSContainerController controller) {
+ mQSCustomizerController.setContainerController(controller);
+ mQSDetail.setContainerController(controller);
}
@Override
@@ -410,7 +416,7 @@
mQSAnimator.setShowCollapsedOnKeyguard(showCollapsed);
}
if (!showCollapsed && isKeyguardState()) {
- setQsExpansion(mLastQSExpansion, 0);
+ setQsExpansion(mLastQSExpansion, mLastPanelFraction, 0);
}
}
}
@@ -478,33 +484,30 @@
}
@Override
- public void setTranslateWhileExpanding(boolean shouldTranslate) {
- mTranslateWhileExpanding = shouldTranslate;
- mQSAnimator.setTranslateWhileExpanding(shouldTranslate);
+ public void setInSplitShade(boolean inSplitShade) {
+ mInSplitShade = inSplitShade;
+ mQSAnimator.setTranslateWhileExpanding(inSplitShade);
}
@Override
- public void setTransitionToFullShadeAmount(float pxAmount, boolean animated) {
+ public void setTransitionToFullShadeAmount(float pxAmount, float progress) {
boolean isTransitioningToFullShade = pxAmount > 0;
if (isTransitioningToFullShade != mTransitioningToFullShade) {
mTransitioningToFullShade = isTransitioningToFullShade;
updateShowCollapsedOnKeyguard();
- setQsExpansion(mLastQSExpansion, mLastHeaderTranslation);
}
+ mFullShadeProgress = progress;
+ setQsExpansion(mLastQSExpansion, mLastPanelFraction, mLastHeaderTranslation);
}
@Override
- public void setQsExpansion(float expansion, float proposedTranslation) {
- if (DEBUG) Log.d(TAG, "setQSExpansion " + expansion + " " + proposedTranslation);
+ public void setQsExpansion(float expansion, float panelExpansionFraction,
+ float proposedTranslation) {
float headerTranslation = mTransitioningToFullShade ? 0 : proposedTranslation;
- if (mQSAnimator != null) {
- final boolean showQSOnLockscreen = expansion > 0;
- final boolean showQSUnlocked = headerTranslation == 0 || !mTranslateWhileExpanding;
- mQSAnimator.startAlphaAnimation(showQSOnLockscreen || showQSUnlocked
- || mTransitioningToFullShade);
- }
+ float progress = mTransitioningToFullShade ? mFullShadeProgress : panelExpansionFraction;
+ setAlphaAnimationProgress(mInSplitShade ? progress : 1);
mContainer.setExpansion(expansion);
- final float translationScaleY = (mTranslateWhileExpanding
+ final float translationScaleY = (mInSplitShade
? 1 : QSAnimator.SHORT_PARALLAX_AMOUNT) * (expansion - 1);
boolean onKeyguardAndExpanded = isKeyguardState() && !mShowCollapsedOnKeyguard;
if (!mHeaderAnimating && !headerWillBeAnimating()) {
@@ -521,6 +524,7 @@
return;
}
mLastHeaderTranslation = headerTranslation;
+ mLastPanelFraction = panelExpansionFraction;
mLastQSExpansion = expansion;
mLastKeyguardAndExpanded = onKeyguardAndExpanded;
mLastViewHeight = currentHeight;
@@ -562,6 +566,17 @@
updateMediaPositions();
}
+ private void setAlphaAnimationProgress(float progress) {
+ final View view = getView();
+ if (progress == 0 && view.getVisibility() != View.INVISIBLE) {
+ view.setVisibility(View.INVISIBLE);
+ } else if (progress > 0 && view.getVisibility() != View.VISIBLE) {
+ view.setVisibility((View.VISIBLE));
+ }
+ float alpha = Interpolators.getNotificationScrimAlpha(progress, true /* uiContent */);
+ view.setAlpha(alpha);
+ }
+
private void updateQsBounds() {
if (mLastQSExpansion == 1.0f) {
// Fully expanded, let's set the layout bounds as clip bounds. This is necessary because
diff --git a/packages/SystemUI/src/com/android/systemui/qs/customize/QSCustomizer.java b/packages/SystemUI/src/com/android/systemui/qs/customize/QSCustomizer.java
index d33982c..1a6d490 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/customize/QSCustomizer.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/customize/QSCustomizer.java
@@ -33,9 +33,9 @@
import com.android.systemui.R;
import com.android.systemui.plugins.qs.QS;
+import com.android.systemui.plugins.qs.QSContainerController;
import com.android.systemui.qs.QSDetailClipper;
import com.android.systemui.statusbar.phone.LightBarController;
-import com.android.systemui.statusbar.phone.NotificationsQuickSettingsContainer;
/**
* Allows full-screen customization of QS, through show() and hide().
@@ -54,7 +54,7 @@
private boolean isShown;
private final RecyclerView mRecyclerView;
private boolean mCustomizing;
- private NotificationsQuickSettingsContainer mNotifQsContainer;
+ private QSContainerController mQsContainerController;
private QS mQs;
private int mX;
private int mY;
@@ -103,8 +103,8 @@
lightBarController.setQsCustomizing(mIsShowingNavBackdrop && isShown);
}
- public void setContainer(NotificationsQuickSettingsContainer notificationsQsContainer) {
- mNotifQsContainer = notificationsQsContainer;
+ public void setContainerController(QSContainerController controller) {
+ mQsContainerController = controller;
}
public void setQs(QS qs) {
@@ -123,8 +123,8 @@
mOpening = true;
setVisibility(View.VISIBLE);
mClipper.animateCircularClip(mX, mY, true, new ExpandAnimatorListener(tileAdapter));
- mNotifQsContainer.setCustomizerAnimating(true);
- mNotifQsContainer.setCustomizerShowing(true);
+ mQsContainerController.setCustomizerAnimating(true);
+ mQsContainerController.setCustomizerShowing(true);
}
}
@@ -136,8 +136,8 @@
mClipper.showBackground();
isShown = true;
setCustomizing(true);
- mNotifQsContainer.setCustomizerAnimating(false);
- mNotifQsContainer.setCustomizerShowing(true);
+ mQsContainerController.setCustomizerAnimating(false);
+ mQsContainerController.setCustomizerShowing(true);
}
}
@@ -154,8 +154,8 @@
} else {
setVisibility(View.GONE);
}
- mNotifQsContainer.setCustomizerAnimating(animate);
- mNotifQsContainer.setCustomizerShowing(false);
+ mQsContainerController.setCustomizerAnimating(animate);
+ mQsContainerController.setCustomizerShowing(false);
}
}
@@ -193,7 +193,7 @@
setCustomizing(true);
}
mOpening = false;
- mNotifQsContainer.setCustomizerAnimating(false);
+ mQsContainerController.setCustomizerAnimating(false);
mRecyclerView.setAdapter(mTileAdapter);
}
@@ -201,7 +201,7 @@
public void onAnimationCancel(Animator animation) {
mOpening = false;
mQs.notifyCustomizeChanged();
- mNotifQsContainer.setCustomizerAnimating(false);
+ mQsContainerController.setCustomizerAnimating(false);
}
}
@@ -211,7 +211,7 @@
if (!isShown) {
setVisibility(View.GONE);
}
- mNotifQsContainer.setCustomizerAnimating(false);
+ mQsContainerController.setCustomizerAnimating(false);
}
@Override
@@ -219,7 +219,7 @@
if (!isShown) {
setVisibility(View.GONE);
}
- mNotifQsContainer.setCustomizerAnimating(false);
+ mQsContainerController.setCustomizerAnimating(false);
}
};
diff --git a/packages/SystemUI/src/com/android/systemui/qs/customize/QSCustomizerController.java b/packages/SystemUI/src/com/android/systemui/qs/customize/QSCustomizerController.java
index 49d18e6..618a429 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/customize/QSCustomizerController.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/customize/QSCustomizerController.java
@@ -35,13 +35,13 @@
import com.android.internal.logging.UiEventLogger;
import com.android.systemui.R;
import com.android.systemui.keyguard.ScreenLifecycle;
+import com.android.systemui.plugins.qs.QSContainerController;
import com.android.systemui.plugins.qs.QSTile;
import com.android.systemui.qs.QSEditEvent;
import com.android.systemui.qs.QSFragment;
import com.android.systemui.qs.QSTileHost;
import com.android.systemui.qs.dagger.QSScope;
import com.android.systemui.statusbar.phone.LightBarController;
-import com.android.systemui.statusbar.phone.NotificationsQuickSettingsContainer;
import com.android.systemui.statusbar.policy.ConfigurationController;
import com.android.systemui.statusbar.policy.ConfigurationController.ConfigurationListener;
import com.android.systemui.statusbar.policy.KeyguardStateController;
@@ -233,8 +233,8 @@
}
/** */
- public void setContainer(NotificationsQuickSettingsContainer container) {
- mView.setContainer(container);
+ public void setContainerController(QSContainerController controller) {
+ mView.setContainerController(controller);
}
public boolean isShown() {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/UserDetailView.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/UserDetailView.java
index 04437ea..821bd51 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/UserDetailView.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/UserDetailView.java
@@ -39,6 +39,8 @@
import com.android.systemui.qs.QSUserSwitcherEvent;
import com.android.systemui.statusbar.policy.UserSwitcherController;
+import java.util.function.Consumer;
+
import javax.inject.Inject;
/**
@@ -75,6 +77,7 @@
private View mCurrentUserView;
private final UiEventLogger mUiEventLogger;
private final FalsingManager mFalsingManager;
+ private Consumer<UserSwitcherController.UserRecord> mClickCallback;
@Inject
public Adapter(Context context, UserSwitcherController controller,
@@ -92,6 +95,10 @@
return createUserDetailItemView(convertView, parent, item);
}
+ public void injectCallback(Consumer<UserSwitcherController.UserRecord> clickCallback) {
+ mClickCallback = clickCallback;
+ }
+
public UserDetailItemView createUserDetailItemView(View convertView, ViewGroup parent,
UserSwitcherController.UserRecord item) {
UserDetailItemView v = UserDetailItemView.convertOrInflate(
@@ -167,6 +174,13 @@
}
onUserListItemClicked(tag);
}
+ if (mClickCallback != null) {
+ mClickCallback.accept(tag);
+ }
+ }
+
+ public void linkToViewGroup(ViewGroup viewGroup) {
+ PseudoGridView.ViewGroupAdapterBridge.link(viewGroup, this);
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/user/UserDialog.kt b/packages/SystemUI/src/com/android/systemui/qs/user/UserDialog.kt
new file mode 100644
index 0000000..2ad06c1
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/qs/user/UserDialog.kt
@@ -0,0 +1,86 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.qs.user
+
+import android.content.Context
+import android.os.Bundle
+import android.view.Gravity
+import android.view.View
+import android.view.ViewGroup
+import android.view.WindowInsets
+import android.view.WindowManager
+import com.android.systemui.qs.PseudoGridView
+import com.android.systemui.statusbar.phone.SystemUIDialog
+import com.android.systemui.R
+
+/**
+ * Dialog for switching users or creating new ones.
+ */
+class UserDialog(
+ context: Context
+) : SystemUIDialog(context, R.style.Theme_SystemUI_Dialog_QSDialog) {
+
+ // create() is no-op after creation
+ private lateinit var _doneButton: View
+ /**
+ * Button with text "Done" in dialog.
+ */
+ val doneButton: View
+ get() {
+ create()
+ return _doneButton
+ }
+
+ private lateinit var _settingsButton: View
+ /**
+ * Button with text "User Settings" in dialog.
+ */
+ val settingsButton: View
+ get() {
+ create()
+ return _settingsButton
+ }
+
+ private lateinit var _grid: PseudoGridView
+ /**
+ * Grid to populate with user avatar from adapter
+ */
+ val grid: ViewGroup
+ get() {
+ create()
+ return _grid
+ }
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+ window?.apply {
+ setType(WindowManager.LayoutParams.TYPE_STATUS_BAR_SUB_PANEL)
+ attributes.fitInsetsTypes = attributes.fitInsetsTypes or WindowInsets.Type.statusBars()
+ attributes.receiveInsetsIgnoringZOrder = true
+ setLayout(
+ context.resources.getDimensionPixelSize(R.dimen.qs_panel_width),
+ ViewGroup.LayoutParams.WRAP_CONTENT
+ )
+ setGravity(Gravity.CENTER)
+ }
+ setContentView(R.layout.qs_user_dialog_content)
+
+ _doneButton = requireViewById(R.id.done)
+ _settingsButton = requireViewById(R.id.settings)
+ _grid = requireViewById(R.id.grid)
+ }
+}
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/qs/user/UserSwitchDialogController.kt b/packages/SystemUI/src/com/android/systemui/qs/user/UserSwitchDialogController.kt
new file mode 100644
index 0000000..a5e4ba1
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/qs/user/UserSwitchDialogController.kt
@@ -0,0 +1,87 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.qs.user
+
+import android.content.Context
+import android.content.Intent
+import android.provider.Settings
+import android.view.View
+import androidx.annotation.VisibleForTesting
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.plugins.ActivityStarter
+import com.android.systemui.plugins.FalsingManager
+import com.android.systemui.qs.tiles.UserDetailView
+import javax.inject.Inject
+import javax.inject.Provider
+
+/**
+ * Controller for [UserDialog].
+ */
+@SysUISingleton
+class UserSwitchDialogController @VisibleForTesting constructor(
+ private val userDetailViewAdapterProvider: Provider<UserDetailView.Adapter>,
+ private val activityStarter: ActivityStarter,
+ private val falsingManager: FalsingManager,
+ private val dialogFactory: (Context) -> UserDialog
+) {
+
+ @Inject
+ constructor(
+ userDetailViewAdapterProvider: Provider<UserDetailView.Adapter>,
+ activityStarter: ActivityStarter,
+ falsingManager: FalsingManager
+ ) : this(
+ userDetailViewAdapterProvider,
+ activityStarter,
+ falsingManager,
+ { UserDialog(it) }
+ )
+
+ companion object {
+ private val USER_SETTINGS_INTENT = Intent(Settings.ACTION_USER_SETTINGS)
+ }
+
+ /**
+ * Show a [UserDialog].
+ *
+ * Populate the dialog with information from and adapter obtained from
+ * [userDetailViewAdapterProvider] and show it as launched from [view].
+ */
+ fun showDialog(view: View) {
+ with(dialogFactory(view.context)) {
+ setShowForAllUsers(true)
+ setCanceledOnTouchOutside(true)
+ create() // Needs to be called before we can retrieve views
+
+ settingsButton.setOnClickListener {
+ if (!falsingManager.isFalseTap(FalsingManager.LOW_PENALTY)) {
+ activityStarter.postStartActivityDismissingKeyguard(USER_SETTINGS_INTENT, 0)
+ }
+ dismiss()
+ }
+ doneButton.setOnClickListener { dismiss() }
+
+ val adapter = userDetailViewAdapterProvider.get()
+ adapter.injectCallback {
+ dismiss()
+ }
+ adapter.linkToViewGroup(grid)
+
+ show()
+ }
+ }
+}
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java b/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java
index 8a39719..4f932a3 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java
@@ -819,12 +819,15 @@
}
}
- private void showTryFingerprintMsg(String a11yString) {
+ private void showTryFingerprintMsg(int msgId, String a11yString) {
if (mKeyguardUpdateMonitor.isUdfpsAvailable()) {
// if udfps available, there will always be a tappable affordance to unlock
// For example, the lock icon
if (mKeyguardBypassController.getUserHasDeviceEntryIntent()) {
showTransientIndication(R.string.keyguard_unlock_press);
+ } else if (msgId == FaceManager.FACE_ERROR_LOCKOUT_PERMANENT) {
+ // since face is locked out, simply show "try fingerprint"
+ showTransientIndication(R.string.keyguard_try_fingerprint);
} else {
showTransientIndication(R.string.keyguard_face_failed_use_fp);
}
@@ -916,7 +919,7 @@
} else if (mKeyguardUpdateMonitor.isScreenOn()) {
if (biometricSourceType == BiometricSourceType.FACE
&& shouldSuppressFaceMsgAndShowTryFingerprintMsg()) {
- showTryFingerprintMsg(helpString);
+ showTryFingerprintMsg(msgId, helpString);
return;
}
showTransientIndication(helpString, false /* isError */, showActionToUnlock);
@@ -936,7 +939,7 @@
&& shouldSuppressFaceMsgAndShowTryFingerprintMsg()
&& !mStatusBarKeyguardViewManager.isBouncerShowing()
&& mKeyguardUpdateMonitor.isScreenOn()) {
- showTryFingerprintMsg(errString);
+ showTryFingerprintMsg(msgId, errString);
return;
}
if (msgId == FaceManager.FACE_ERROR_TIMEOUT) {
@@ -945,7 +948,7 @@
if (!mStatusBarKeyguardViewManager.isBouncerShowing()
&& mKeyguardUpdateMonitor.isUdfpsEnrolled()
&& mKeyguardUpdateMonitor.isFingerprintDetectionRunning()) {
- showTryFingerprintMsg(errString);
+ showTryFingerprintMsg(msgId, errString);
} else if (mStatusBarKeyguardViewManager.isShowingAlternateAuth()) {
mStatusBarKeyguardViewManager.showBouncerMessage(
mContext.getResources().getString(R.string.keyguard_unlock_press),
@@ -989,8 +992,8 @@
private boolean shouldSuppressFaceMsgAndShowTryFingerprintMsg() {
// For dual biometric, don't show face auth messages
return mKeyguardUpdateMonitor.isFingerprintDetectionRunning()
- && mKeyguardUpdateMonitor.isUnlockingWithBiometricAllowed(
- true /* isStrongBiometric */);
+ && mKeyguardUpdateMonitor.isUnlockingWithBiometricAllowed(
+ true /* isStrongBiometric */);
}
private boolean shouldSuppressFaceError(int msgId, KeyguardUpdateMonitor updateMonitor) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/LightRevealScrim.kt b/packages/SystemUI/src/com/android/systemui/statusbar/LightRevealScrim.kt
index 03d8e7e..77e329f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/LightRevealScrim.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/LightRevealScrim.kt
@@ -208,6 +208,11 @@
lateinit var isScrimOpaqueChangedListener: Consumer<Boolean>
/**
+ * A runnable to call when the scrim has been fully revealed. This is only invoked once
+ */
+ var fullyRevealedRunnable: Runnable? = null
+
+ /**
* How much of the underlying views are revealed, in percent. 0 means they will be completely
* obscured and 1 means they'll be fully visible.
*/
@@ -218,10 +223,20 @@
revealEffect.setRevealAmountOnScrim(value, this)
updateScrimOpaque()
+ maybeTriggerFullyRevealedRunnable()
invalidate()
}
}
+ private fun maybeTriggerFullyRevealedRunnable() {
+ if (revealAmount == 1.0f) {
+ fullyRevealedRunnable?.let {
+ it.run()
+ fullyRevealedRunnable = null
+ }
+ }
+ }
+
/**
* The [LightRevealEffect] used to manipulate the radial gradient whenever [revealAmount]
* changes.
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/LockscreenShadeTransitionController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/LockscreenShadeTransitionController.kt
index ca18b07..dca7f70 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/LockscreenShadeTransitionController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/LockscreenShadeTransitionController.kt
@@ -298,9 +298,8 @@
nsslController.setTransitionToFullShadeAmount(field)
notificationPanelController.setTransitionToFullShadeAmount(field,
false /* animate */, 0 /* delay */)
- // TODO: appear qs also in split shade
- val qsAmount = if (useSplitShade) 0f else field
- qS.setTransitionToFullShadeAmount(qsAmount, false /* animate */)
+ val progress = MathUtils.saturate(dragDownAmount / scrimTransitionDistance)
+ qS.setTransitionToFullShadeAmount(field, progress)
// TODO: appear media also in split shade
val mediaAmount = if (useSplitShade) 0f else field
mediaHierarchyManager.setTransitionToFullShadeAmount(mediaAmount)
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShadeDepthController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShadeDepthController.kt
index 7aa2dc7..d4f54e1 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShadeDepthController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShadeDepthController.kt
@@ -36,7 +36,6 @@
import com.android.systemui.dump.DumpManager
import com.android.systemui.plugins.statusbar.StatusBarStateController
import com.android.systemui.statusbar.phone.BiometricUnlockController
-import com.android.systemui.statusbar.phone.BiometricUnlockController.MODE_WAKE_AND_UNLOCK
import com.android.systemui.statusbar.phone.DozeParameters
import com.android.systemui.statusbar.phone.PanelExpansionListener
import com.android.systemui.statusbar.phone.ScrimController
@@ -73,6 +72,10 @@
private const val TAG = "DepthController"
}
+ /**
+ * Did we already unblur while dozing?
+ */
+ private var alreadyUnblurredWhileDozing = false
lateinit var root: View
private var blurRoot: View? = null
private var keyguardAnimator: Animator? = null
@@ -229,9 +232,11 @@
private val keyguardStateCallback = object : KeyguardStateController.Callback {
override fun onKeyguardFadingAwayChanged() {
if (!keyguardStateController.isKeyguardFadingAway ||
- biometricUnlockController.mode != MODE_WAKE_AND_UNLOCK) {
+ !biometricUnlockController.isWakeAndUnlock) {
return
}
+ // When wakeAndUnlocking the screen remains dozing, so we have to manually trigger
+ // the unblur earlier
keyguardAnimator?.cancel()
keyguardAnimator = ValueAnimator.ofFloat(1f, 0f).apply {
@@ -253,6 +258,7 @@
})
start()
}
+ alreadyUnblurredWhileDozing = statusBarStateController.dozeAmount != 0.0f
}
override fun onKeyguardShowingChanged() {
@@ -274,10 +280,24 @@
if (isDozing) {
shadeAnimation.finishIfRunning()
brightnessMirrorSpring.finishIfRunning()
+
+ // unset this for safety, to be ready for the next wakeup
+ alreadyUnblurredWhileDozing = false
}
}
override fun onDozeAmountChanged(linear: Float, eased: Float) {
+ if (alreadyUnblurredWhileDozing) {
+ if (linear == 0.0f) {
+ // We finished waking up, let's reset
+ alreadyUnblurredWhileDozing = false
+ } else {
+ // We've already handled the unbluring from the keyguardAnimator above.
+ // if we would continue, we'd play another unzoom / blur animation from the
+ // dozing changing.
+ return
+ }
+ }
wakeAndUnlockBlurRadius = blurUtils.blurRadiusOfRatio(eased)
scheduleUpdate()
}
@@ -435,6 +455,7 @@
it.println("blursDisabledForAppLaunch: $blursDisabledForAppLaunch")
it.println("qsPanelExpansion: $qsPanelExpansion")
it.println("transitionToFullShadeProgress: $transitionToFullShadeProgress")
+ it.println("alreadyUnblurredWhileDozing: $alreadyUnblurredWhileDozing")
it.println("lastAppliedBlur: $lastAppliedBlur")
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarStateControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarStateControllerImpl.java
index 19876ba..cbb3aba 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarStateControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarStateControllerImpl.java
@@ -446,7 +446,7 @@
mIsFullscreen = isFullscreen;
synchronized (mListeners) {
for (RankedListener rl : new ArrayList<>(mListeners)) {
- rl.mListener.onFullscreenStateChanged(isFullscreen, true /* isImmersive */);
+ rl.mListener.onFullscreenStateChanged(isFullscreen);
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/dagger/StatusBarDependenciesModule.java b/packages/SystemUI/src/com/android/systemui/statusbar/dagger/StatusBarDependenciesModule.java
index 6730afa..452e737 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/dagger/StatusBarDependenciesModule.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/dagger/StatusBarDependenciesModule.java
@@ -65,9 +65,11 @@
import com.android.systemui.statusbar.phone.StatusBarIconController;
import com.android.systemui.statusbar.phone.StatusBarIconControllerImpl;
import com.android.systemui.statusbar.phone.StatusBarRemoteInputCallback;
+import com.android.systemui.statusbar.phone.StatusBarWindowController;
import com.android.systemui.statusbar.phone.SystemUIHostDialogProvider;
import com.android.systemui.statusbar.phone.ongoingcall.OngoingCallController;
import com.android.systemui.statusbar.phone.ongoingcall.OngoingCallLogger;
+import com.android.systemui.statusbar.phone.ongoingcall.SwipeStatusBarAwayGestureHandler;
import com.android.systemui.statusbar.policy.RemoteInputUriController;
import com.android.systemui.tracing.ProtoTracer;
import com.android.systemui.util.concurrency.DelayableExecutor;
@@ -253,11 +255,30 @@
@Main Executor mainExecutor,
IActivityManager iActivityManager,
OngoingCallLogger logger,
- DumpManager dumpManager) {
+ DumpManager dumpManager,
+ StatusBarWindowController statusBarWindowController,
+ SwipeStatusBarAwayGestureHandler swipeStatusBarAwayGestureHandler) {
+ Optional<StatusBarWindowController> windowController =
+ featureFlags.isOngoingCallInImmersiveEnabled()
+ ? Optional.of(statusBarWindowController)
+ : Optional.empty();
+ Optional<SwipeStatusBarAwayGestureHandler> gestureHandler =
+ featureFlags.isOngoingCallInImmersiveEnabled()
+ ? Optional.of(swipeStatusBarAwayGestureHandler)
+ : Optional.empty();
OngoingCallController ongoingCallController =
new OngoingCallController(
- notifCollection, featureFlags, systemClock, activityStarter, mainExecutor,
- iActivityManager, logger, dumpManager);
+ notifCollection,
+ featureFlags,
+ systemClock,
+ activityStarter,
+ mainExecutor,
+ iActivityManager,
+ logger,
+ dumpManager,
+ windowController,
+ gestureHandler
+ );
ongoingCallController.init();
return ongoingCallController;
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java
index f1d5e02..a090ac3 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java
@@ -126,6 +126,19 @@
mExpansionCallbacks.add(expansionCallback);
}
+ /**
+ * Enable/disable only the back button
+ */
+ public void setBackButtonEnabled(boolean enabled) {
+ int vis = mContainer.getSystemUiVisibility();
+ if (enabled) {
+ vis &= ~View.STATUS_BAR_DISABLE_BACK;
+ } else {
+ vis |= View.STATUS_BAR_DISABLE_BACK;
+ }
+ mContainer.setSystemUiVisibility(vis);
+ }
+
public void show(boolean resetSecuritySelection) {
show(resetSecuritySelection, true /* scrimmed */);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/MultiUserSwitchController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/MultiUserSwitchController.java
index 3fdf1ce..dd21c8a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/MultiUserSwitchController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/MultiUserSwitchController.java
@@ -23,11 +23,13 @@
import android.view.ViewGroup;
import com.android.systemui.R;
+import com.android.systemui.flags.FeatureFlags;
import com.android.systemui.plugins.FalsingManager;
import com.android.systemui.plugins.qs.DetailAdapter;
import com.android.systemui.qs.FooterActionsView;
import com.android.systemui.qs.QSDetailDisplayer;
import com.android.systemui.qs.dagger.QSScope;
+import com.android.systemui.qs.user.UserSwitchDialogController;
import com.android.systemui.statusbar.policy.UserSwitcherController;
import com.android.systemui.util.ViewController;
@@ -39,6 +41,8 @@
private final UserSwitcherController mUserSwitcherController;
private final QSDetailDisplayer mQsDetailDisplayer;
private final FalsingManager mFalsingManager;
+ private final UserSwitchDialogController mUserSwitchDialogController;
+ private final FeatureFlags mFeatureFlags;
private UserSwitcherController.BaseUserAdapter mUserListener;
@@ -49,14 +53,18 @@
return;
}
- View center = mView.getChildCount() > 0 ? mView.getChildAt(0) : mView;
+ if (mFeatureFlags.useNewUserSwitcher()) {
+ mUserSwitchDialogController.showDialog(v);
+ } else {
+ View center = mView.getChildCount() > 0 ? mView.getChildAt(0) : mView;
- int[] tmpInt = new int[2];
- center.getLocationInWindow(tmpInt);
- tmpInt[0] += center.getWidth() / 2;
- tmpInt[1] += center.getHeight() / 2;
+ int[] tmpInt = new int[2];
+ center.getLocationInWindow(tmpInt);
+ tmpInt[0] += center.getWidth() / 2;
+ tmpInt[1] += center.getHeight() / 2;
- mQsDetailDisplayer.showDetailAdapter(getUserDetailAdapter(), tmpInt[0], tmpInt[1]);
+ mQsDetailDisplayer.showDetailAdapter(getUserDetailAdapter(), tmpInt[0], tmpInt[1]);
+ }
}
};
@@ -66,31 +74,40 @@
private final UserSwitcherController mUserSwitcherController;
private final QSDetailDisplayer mQsDetailDisplayer;
private final FalsingManager mFalsingManager;
+ private final UserSwitchDialogController mUserSwitchDialogController;
+ private final FeatureFlags mFeatureFlags;
@Inject
public Factory(UserManager userManager, UserSwitcherController userSwitcherController,
- QSDetailDisplayer qsDetailDisplayer, FalsingManager falsingManager) {
+ QSDetailDisplayer qsDetailDisplayer, FalsingManager falsingManager,
+ UserSwitchDialogController userSwitchDialogController,
+ FeatureFlags featureFlags) {
mUserManager = userManager;
mUserSwitcherController = userSwitcherController;
mQsDetailDisplayer = qsDetailDisplayer;
mFalsingManager = falsingManager;
+ mUserSwitchDialogController = userSwitchDialogController;
+ mFeatureFlags = featureFlags;
}
public MultiUserSwitchController create(FooterActionsView view) {
return new MultiUserSwitchController(view.findViewById(R.id.multi_user_switch),
mUserManager, mUserSwitcherController, mQsDetailDisplayer,
- mFalsingManager);
+ mFalsingManager, mUserSwitchDialogController, mFeatureFlags);
}
}
private MultiUserSwitchController(MultiUserSwitch view, UserManager userManager,
UserSwitcherController userSwitcherController, QSDetailDisplayer qsDetailDisplayer,
- FalsingManager falsingManager) {
+ FalsingManager falsingManager, UserSwitchDialogController userSwitchDialogController,
+ FeatureFlags featureFlags) {
super(view);
mUserManager = userManager;
mUserSwitcherController = userSwitcherController;
mQsDetailDisplayer = qsDetailDisplayer;
mFalsingManager = falsingManager;
+ mUserSwitchDialogController = userSwitchDialogController;
+ mFeatureFlags = featureFlags;
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java
index 4862d16..1dface6 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java
@@ -34,6 +34,9 @@
import static com.android.systemui.statusbar.StatusBarState.SHADE;
import static com.android.systemui.statusbar.StatusBarState.SHADE_LOCKED;
import static com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout.ROWS_ALL;
+import static com.android.systemui.statusbar.phone.PanelBar.STATE_CLOSED;
+import static com.android.systemui.statusbar.phone.PanelBar.STATE_OPEN;
+import static com.android.systemui.statusbar.phone.PanelBar.STATE_OPENING;
import static java.lang.Float.isNaN;
@@ -57,7 +60,6 @@
import android.graphics.Rect;
import android.graphics.Region;
import android.graphics.drawable.Drawable;
-import android.hardware.biometrics.BiometricSourceType;
import android.hardware.biometrics.SensorLocationInternal;
import android.hardware.fingerprint.FingerprintSensorPropertiesInternal;
import android.os.Bundle;
@@ -80,6 +82,7 @@
import android.view.ViewStub;
import android.view.ViewTreeObserver;
import android.view.WindowInsets;
+import android.view.accessibility.AccessibilityEvent;
import android.view.accessibility.AccessibilityManager;
import android.view.accessibility.AccessibilityNodeInfo;
import android.widget.FrameLayout;
@@ -310,6 +313,7 @@
private KeyguardStatusViewController mKeyguardStatusViewController;
private LockIconViewController mLockIconViewController;
private NotificationsQuickSettingsContainer mNotificationContainerParent;
+ private NotificationsQSContainerController mNotificationsQSContainerController;
private boolean mAnimateNextPositionUpdate;
private float mQuickQsOffsetHeight;
private UnlockedScreenOffAnimationController mUnlockedScreenOffAnimationController;
@@ -653,6 +657,7 @@
ConversationNotificationManager conversationNotificationManager,
MediaHierarchyManager mediaHierarchyManager,
StatusBarKeyguardViewManager statusBarKeyguardViewManager,
+ NotificationsQSContainerController notificationsQSContainerController,
NotificationStackScrollLayoutController notificationStackScrollLayoutController,
KeyguardStatusViewComponent.Factory keyguardStatusViewComponentFactory,
KeyguardQsUserSwitchComponent.Factory keyguardQsUserSwitchComponentFactory,
@@ -708,6 +713,8 @@
mFlingAnimationUtilsBuilder = flingAnimationUtilsBuilder;
mMediaHierarchyManager = mediaHierarchyManager;
mStatusBarKeyguardViewManager = statusBarKeyguardViewManager;
+ mNotificationsQSContainerController = notificationsQSContainerController;
+ mNotificationsQSContainerController.init();
mNotificationStackScrollLayoutController = notificationStackScrollLayoutController;
mGroupManager = groupManager;
mNotificationIconAreaController = notificationIconAreaController;
@@ -977,7 +984,7 @@
Utils.shouldUseSplitNotificationShade(mResources);
mScrimController.setClipsQsScrim(!mShouldUseSplitNotificationShade);
if (mQs != null) {
- mQs.setTranslateWhileExpanding(mShouldUseSplitNotificationShade);
+ mQs.setInSplitShade(mShouldUseSplitNotificationShade);
}
int topMargin = mShouldUseSplitNotificationShade ? mSplitShadeStatusBarHeight :
@@ -1005,7 +1012,7 @@
constraintSet.setMargin(R.id.notification_stack_scroller, TOP, topMargin);
constraintSet.setMargin(R.id.qs_frame, TOP, topMargin);
constraintSet.applyTo(mNotificationContainerParent);
- mNotificationContainerParent.setSplitShadeEnabled(mShouldUseSplitNotificationShade);
+ mNotificationsQSContainerController.setSplitShadeEnabled(mShouldUseSplitNotificationShade);
updateKeyguardStatusViewAlignment(/* animate= */false);
@@ -1798,15 +1805,6 @@
return !mQsTouchAboveFalsingThreshold;
}
- /**
- * Percentage of panel expansion offset, caused by pulling down on a heads-up.
- */
- @Override
- public void setMinFraction(float minFraction) {
- mMinFraction = minFraction;
- mDepthController.setPanelPullDownMinFraction(mMinFraction);
- }
-
private float computeQsExpansionFraction() {
if (mQSAnimatingHiddenFromCollapsed) {
// When hiding QS from collapsed state, the expansion can sometimes temporarily
@@ -2106,7 +2104,7 @@
requestPanelHeightUpdate();
mFalsingCollector.setQsExpanded(expanded);
mStatusBar.setQsExpanded(expanded);
- mNotificationContainerParent.setQsExpanded(expanded);
+ mNotificationsQSContainerController.setQsExpanded(expanded);
mPulseExpansionHandler.setQsExpanded(expanded);
mKeyguardBypassController.setQSExpanded(expanded);
mStatusBarKeyguardViewManager.setQsExpanded(expanded);
@@ -2204,7 +2202,7 @@
private void updateQsExpansion() {
if (mQs == null) return;
float qsExpansionFraction = computeQsExpansionFraction();
- mQs.setQsExpansion(qsExpansionFraction, getHeaderTranslation());
+ mQs.setQsExpansion(qsExpansionFraction, getExpandedFraction(), getHeaderTranslation());
mMediaHierarchyManager.setQsExpansion(qsExpansionFraction);
int qsPanelBottomY = calculateQsBottomPosition(qsExpansionFraction);
mScrimController.setQsPosition(qsExpansionFraction, qsPanelBottomY);
@@ -2228,12 +2226,6 @@
updateQSExpansionEnabledAmbient();
}
- @Override
- public void setIsShadeOpening(boolean opening) {
- mAmbientState.setIsShadeOpening(opening);
- updateQSExpansionEnabledAmbient();
- }
-
private void updateQSExpansionEnabledAmbient() {
final float scrollRangeToTop = mAmbientState.getTopPadding() - mQuickQsOffsetHeight;
mQsExpansionEnabledAmbient = mShouldUseSplitNotificationShade
@@ -2704,8 +2696,9 @@
* @return Whether we should intercept a gesture to open Quick Settings.
*/
private boolean shouldQuickSettingsIntercept(float x, float y, float yDiff) {
- if (!isQsExpansionEnabled() || mCollapsedOnDown || (mKeyguardShowing
- && mKeyguardBypassController.getBypassEnabled())) {
+ if (!isQsExpansionEnabled() || mCollapsedOnDown
+ || (mKeyguardShowing && mKeyguardBypassController.getBypassEnabled())
+ || (mKeyguardShowing && mShouldUseSplitNotificationShade)) {
return false;
}
View header = mKeyguardShowing || mQs == null ? mKeyguardStatusBar : mQs.getHeader();
@@ -3340,8 +3333,14 @@
return mBarState == KEYGUARD;
}
+ /**
+ * Sets the minimum fraction for the panel expansion offset. This may be non-zero in certain
+ * cases, such as if there's a heads-up notification.
+ */
public void setPanelScrimMinFraction(float minFraction) {
- mBar.onPanelMinFractionChanged(minFraction);
+ mMinFraction = minFraction;
+ mDepthController.setPanelPullDownMinFraction(mMinFraction);
+ mScrimController.setPanelScrimMinFraction(mMinFraction);
}
public void clearNotificationEffects() {
@@ -3457,7 +3456,7 @@
mQs.setExpandClickListener(mOnClickListener);
mQs.setHeaderClickable(isQsExpansionEnabled());
mQs.setOverscrolling(mStackScrollerOverscrolling);
- mQs.setTranslateWhileExpanding(mShouldUseSplitNotificationShade);
+ mQs.setInSplitShade(mShouldUseSplitNotificationShade);
// recompute internal state when qspanel height changes
mQs.getView().addOnLayoutChangeListener(
@@ -4629,4 +4628,26 @@
return insets;
}
}
+
+ private final PanelBar.PanelStateChangeListener mPanelStateChangeListener =
+ new PanelBar.PanelStateChangeListener() {
+
+ @PanelBar.PanelState
+ private int mCurrentState = STATE_CLOSED;
+
+ @Override
+ public void onStateChanged(@PanelBar.PanelState int state) {
+ mAmbientState.setIsShadeOpening(state == STATE_OPENING);
+ updateQSExpansionEnabledAmbient();
+
+ if (state == STATE_OPEN && mCurrentState != state) {
+ mView.sendAccessibilityEvent(AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED);
+ }
+ mCurrentState = state;
+ }
+ };
+
+ public PanelBar.PanelStateChangeListener getPanelStateChangeListener() {
+ return mPanelStateChangeListener;
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationShadeWindowViewController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationShadeWindowViewController.java
index 03d0bb0..9d06d2b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationShadeWindowViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationShadeWindowViewController.java
@@ -171,6 +171,13 @@
mBrightnessMirror = mView.findViewById(R.id.brightness_mirror_container);
}
+ /**
+ * @return Location where to place the KeyguardBouncer
+ */
+ public ViewGroup getBouncerContainer() {
+ return mView.findViewById(R.id.keyguard_bouncer_container);
+ }
+
/** Inflates the {@link R.layout#status_bar_expanded} layout and sets it up. */
public void setupExpandedStatusBar() {
mStackScrollLayout = mView.findViewById(R.id.notification_stack_scroller);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationsQSContainerController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationsQSContainerController.kt
new file mode 100644
index 0000000..34bb6d3
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationsQSContainerController.kt
@@ -0,0 +1,141 @@
+package com.android.systemui.statusbar.phone
+
+import android.view.WindowInsets
+import com.android.systemui.navigationbar.NavigationModeController
+import com.android.systemui.plugins.qs.QS
+import com.android.systemui.plugins.qs.QSContainerController
+import com.android.systemui.recents.OverviewProxyService
+import com.android.systemui.recents.OverviewProxyService.OverviewProxyListener
+import com.android.systemui.shared.system.QuickStepContract
+import com.android.systemui.util.ViewController
+import java.util.function.Consumer
+import javax.inject.Inject
+
+class NotificationsQSContainerController @Inject constructor(
+ view: NotificationsQuickSettingsContainer,
+ private val navigationModeController: NavigationModeController,
+ private val overviewProxyService: OverviewProxyService
+) : ViewController<NotificationsQuickSettingsContainer>(view), QSContainerController {
+
+ var qsExpanded = false
+ set(value) {
+ if (field != value) {
+ field = value
+ mView.invalidate()
+ }
+ }
+ var splitShadeEnabled = false
+ set(value) {
+ if (field != value) {
+ field = value
+ // in case device configuration changed while showing QS details/customizer
+ updateBottomSpacing()
+ }
+ }
+
+ private var isQSDetailShowing = false
+ private var isQSCustomizing = false
+ private var isQSCustomizerAnimating = false
+
+ private var notificationsBottomMargin = 0
+ private var bottomStableInsets = 0
+ private var bottomCutoutInsets = 0
+
+ private var isGestureNavigation = true
+ private var taskbarVisible = false
+ private val taskbarVisibilityListener: OverviewProxyListener = object : OverviewProxyListener {
+ override fun onTaskbarStatusUpdated(visible: Boolean, stashed: Boolean) {
+ taskbarVisible = visible
+ }
+ }
+ private val windowInsetsListener: Consumer<WindowInsets> = Consumer { insets ->
+ // when taskbar is visible, stableInsetBottom will include its height
+ bottomStableInsets = insets.stableInsetBottom
+ bottomCutoutInsets = insets.displayCutout?.safeInsetBottom ?: 0
+ updateBottomSpacing()
+ }
+
+ override fun onInit() {
+ val currentMode: Int = navigationModeController.addListener { mode: Int ->
+ isGestureNavigation = QuickStepContract.isGesturalMode(mode)
+ }
+ isGestureNavigation = QuickStepContract.isGesturalMode(currentMode)
+ }
+
+ public override fun onViewAttached() {
+ notificationsBottomMargin = mView.defaultNotificationsMarginBottom
+ overviewProxyService.addCallback(taskbarVisibilityListener)
+ mView.setInsetsChangedListener(windowInsetsListener)
+ mView.setQSFragmentAttachedListener { qs: QS -> qs.setContainerController(this) }
+ }
+
+ override fun onViewDetached() {
+ overviewProxyService.removeCallback(taskbarVisibilityListener)
+ mView.removeOnInsetsChangedListener()
+ mView.removeQSFragmentAttachedListener()
+ }
+
+ override fun setCustomizerAnimating(animating: Boolean) {
+ if (isQSCustomizerAnimating != animating) {
+ isQSCustomizerAnimating = animating
+ mView.invalidate()
+ }
+ }
+
+ override fun setCustomizerShowing(showing: Boolean) {
+ isQSCustomizing = showing
+ updateBottomSpacing()
+ }
+
+ override fun setDetailShowing(showing: Boolean) {
+ isQSDetailShowing = showing
+ updateBottomSpacing()
+ }
+
+ private fun updateBottomSpacing() {
+ val (containerPadding, notificationsMargin) = calculateBottomSpacing()
+ var qsScrollPaddingBottom = 0
+ if (!(splitShadeEnabled || isQSCustomizing || isQSDetailShowing || isGestureNavigation ||
+ taskbarVisible)) {
+ // no taskbar, portrait, navigation buttons enabled:
+ // padding is needed so QS can scroll up over bottom insets - to reach the point when
+ // the whole QS is above bottom insets
+ qsScrollPaddingBottom = bottomStableInsets
+ }
+ mView.setPadding(0, 0, 0, containerPadding)
+ mView.setNotificationsMarginBottom(notificationsMargin)
+ mView.setQSScrollPaddingBottom(qsScrollPaddingBottom)
+ }
+
+ private fun calculateBottomSpacing(): Pair<Int, Int> {
+ val containerPadding: Int
+ var stackScrollMargin = notificationsBottomMargin
+ if (splitShadeEnabled) {
+ if (isGestureNavigation) {
+ // only default cutout padding, taskbar always hides
+ containerPadding = bottomCutoutInsets
+ } else if (taskbarVisible) {
+ // navigation buttons + visible taskbar means we're NOT on homescreen
+ containerPadding = bottomStableInsets
+ } else {
+ // navigation buttons + hidden taskbar means we're on homescreen
+ containerPadding = 0
+ // we need extra margin for notifications as navigation buttons are below them
+ stackScrollMargin = bottomStableInsets + notificationsBottomMargin
+ }
+ } else {
+ if (isQSCustomizing || isQSDetailShowing) {
+ // Clear out bottom paddings/margins so the qs customization can be full height.
+ containerPadding = 0
+ stackScrollMargin = 0
+ } else if (isGestureNavigation) {
+ containerPadding = bottomCutoutInsets
+ } else if (taskbarVisible) {
+ containerPadding = bottomStableInsets
+ } else {
+ containerPadding = 0
+ }
+ }
+ return containerPadding to stackScrollMargin
+ }
+}
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationsQuickSettingsContainer.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationsQuickSettingsContainer.java
index 68e28cd..9210a8b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationsQuickSettingsContainer.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationsQuickSettingsContainer.java
@@ -33,6 +33,7 @@
import java.util.ArrayList;
import java.util.Comparator;
+import java.util.function.Consumer;
/**
* The container with notification stack scroller and quick settings inside.
@@ -43,17 +44,15 @@
private View mQsFrame;
private View mStackScroller;
private View mKeyguardStatusBar;
- private boolean mQsExpanded;
- private boolean mCustomizerAnimating;
- private boolean mCustomizing;
- private boolean mDetailShowing;
- private int mBottomPadding;
private int mStackScrollerMargin;
private ArrayList<View> mDrawingOrderedChildren = new ArrayList<>();
private ArrayList<View> mLayoutDrawingOrder = new ArrayList<>();
private final Comparator<View> mIndexComparator = Comparator.comparingInt(this::indexOfChild);
- private boolean mSplitShadeEnabled;
+ private Consumer<WindowInsets> mInsetsChangedListener = insets -> {};
+ private Consumer<QS> mQSFragmentAttachedListener = qs -> {};
+ private QS mQs;
+ private View mQSScrollView;
public NotificationsQuickSettingsContainer(Context context, AttributeSet attrs) {
super(context, attrs);
@@ -69,6 +68,59 @@
}
@Override
+ public void onFragmentViewCreated(String tag, Fragment fragment) {
+ mQs = (QS) fragment;
+ mQSFragmentAttachedListener.accept(mQs);
+ mQSScrollView = mQs.getView().findViewById(R.id.expanded_qs_scroll_view);
+ }
+
+ @Override
+ public void onHasViewsAboveShelfChanged(boolean hasViewsAboveShelf) {
+ invalidate();
+ }
+
+ public void setNotificationsMarginBottom(int margin) {
+ LayoutParams params = (LayoutParams) mStackScroller.getLayoutParams();
+ params.bottomMargin = margin;
+ mStackScroller.setLayoutParams(params);
+ }
+
+ public void setQSScrollPaddingBottom(int paddingBottom) {
+ if (mQSScrollView != null) {
+ mQSScrollView.setPaddingRelative(
+ mQSScrollView.getPaddingLeft(),
+ mQSScrollView.getPaddingTop(),
+ mQSScrollView.getPaddingRight(),
+ paddingBottom
+ );
+ }
+ }
+
+ public int getDefaultNotificationsMarginBottom() {
+ return mStackScrollerMargin;
+ }
+
+ public void setInsetsChangedListener(Consumer<WindowInsets> onInsetsChangedListener) {
+ mInsetsChangedListener = onInsetsChangedListener;
+ }
+
+ public void removeOnInsetsChangedListener() {
+ mInsetsChangedListener = insets -> {};
+ }
+
+ public void setQSFragmentAttachedListener(Consumer<QS> qsFragmentAttachedListener) {
+ mQSFragmentAttachedListener = qsFragmentAttachedListener;
+ // listener might be attached after fragment is attached
+ if (mQs != null) {
+ mQSFragmentAttachedListener.accept(mQs);
+ }
+ }
+
+ public void removeQSFragmentAttachedListener() {
+ mQSFragmentAttachedListener = qs -> {};
+ }
+
+ @Override
protected void onAttachedToWindow() {
super.onAttachedToWindow();
FragmentHostManager.get(this).addTagListener(QS.TAG, this);
@@ -82,8 +134,7 @@
@Override
public WindowInsets onApplyWindowInsets(WindowInsets insets) {
- mBottomPadding = insets.getStableInsetBottom();
- setPadding(0, 0, 0, mBottomPadding);
+ mInsetsChangedListener.accept(insets);
return insets;
}
@@ -119,66 +170,4 @@
}
}
- @Override
- public void onFragmentViewCreated(String tag, Fragment fragment) {
- QS container = (QS) fragment;
- container.setContainer(this);
- }
-
- public void setQsExpanded(boolean expanded) {
- if (mQsExpanded != expanded) {
- mQsExpanded = expanded;
- invalidate();
- }
- }
-
- public void setCustomizerAnimating(boolean isAnimating) {
- if (mCustomizerAnimating != isAnimating) {
- mCustomizerAnimating = isAnimating;
- invalidate();
- }
- }
-
- public void setCustomizerShowing(boolean isShowing) {
- mCustomizing = isShowing;
- updateBottomMargin();
- }
-
- public void setDetailShowing(boolean isShowing) {
- mDetailShowing = isShowing;
- updateBottomMargin();
- }
-
- /**
- * Sets if split shade is enabled and adjusts margins/paddings depending on QS details and
- * customizer state
- */
- public void setSplitShadeEnabled(boolean splitShadeEnabled) {
- mSplitShadeEnabled = splitShadeEnabled;
- // in case device was rotated while showing QS details/customizer
- updateBottomMargin();
- }
-
- private void updateBottomMargin() {
- // in split shade, QS state changes should not influence notifications panel
- if (!mSplitShadeEnabled && (mCustomizing || mDetailShowing)) {
- // Clear out bottom paddings/margins so the qs customization can be full height.
- setPadding(0, 0, 0, 0);
- setBottomMargin(mStackScroller, 0);
- } else {
- setPadding(0, 0, 0, mBottomPadding);
- setBottomMargin(mStackScroller, mStackScrollerMargin);
- }
- }
-
- private void setBottomMargin(View v, int bottomMargin) {
- LayoutParams params = (LayoutParams) v.getLayoutParams();
- params.bottomMargin = bottomMargin;
- v.setLayoutParams(params);
- }
-
- @Override
- public void onHasViewsAboveShelfChanged(boolean hasViewsAboveShelf) {
- invalidate();
- }
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelBar.java
index 247ede9..310fe73 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelBar.java
@@ -17,8 +17,9 @@
package com.android.systemui.statusbar.phone;
import static java.lang.Float.isNaN;
+import static java.lang.annotation.RetentionPolicy.SOURCE;
-import android.annotation.CallSuper;
+import android.annotation.IntDef;
import android.content.Context;
import android.os.Bundle;
import android.os.Parcelable;
@@ -27,6 +28,10 @@
import android.view.MotionEvent;
import android.widget.FrameLayout;
+import androidx.annotation.Nullable;
+
+import java.lang.annotation.Retention;
+
public abstract class PanelBar extends FrameLayout {
public static final boolean DEBUG = false;
public static final String TAG = PanelBar.class.getSimpleName();
@@ -40,26 +45,27 @@
Log.v(TAG, String.format(fmt, args));
}
+ /** Enum for the current state of the panel. */
+ @Retention(SOURCE)
+ @IntDef({STATE_CLOSED, STATE_OPENING, STATE_OPEN})
+ @interface PanelState {}
public static final int STATE_CLOSED = 0;
public static final int STATE_OPENING = 1;
public static final int STATE_OPEN = 2;
- PanelViewController mPanel;
+ private PanelViewController mPanel;
+ @Nullable private PanelStateChangeListener mPanelStateChangeListener;
private int mState = STATE_CLOSED;
private boolean mTracking;
- public void go(int state) {
+ private void go(@PanelState int state) {
if (DEBUG) LOG("go state: %d -> %d", mState, state);
mState = state;
- if (mPanel != null) {
- mPanel.setIsShadeOpening(state == STATE_OPENING);
+ if (mPanelStateChangeListener != null) {
+ mPanelStateChangeListener.onStateChanged(state);
}
}
- protected boolean isShadeOpening() {
- return mState == STATE_OPENING;
- }
-
@Override
protected Parcelable onSaveInstanceState() {
Bundle bundle = new Bundle();
@@ -97,6 +103,11 @@
pv.setBar(this);
}
+ /** Sets the listener that will be notified of panel state changes. */
+ public void setPanelStateChangeListener(PanelStateChangeListener listener) {
+ mPanelStateChangeListener = listener;
+ }
+
public boolean panelEnabled() {
return true;
}
@@ -135,14 +146,6 @@
}
/**
- * Percentage of panel expansion offset, caused by pulling down on a heads-up.
- */
- @CallSuper
- public void onPanelMinFractionChanged(float minFraction) {
- mPanel.setMinFraction(minFraction);
- }
-
- /**
* @param frac the fraction from the expansion in [0, 1]
* @param expanded whether the panel is currently expanded; this is independent from the
* fraction as the panel also might be expanded if the fraction is 0
@@ -166,7 +169,6 @@
}
if (fullyOpened && !mTracking) {
go(STATE_OPEN);
- onPanelFullyOpened();
} else if (fullyClosed && !mTracking && mState != STATE_CLOSED) {
go(STATE_CLOSED);
onPanelCollapsed();
@@ -207,10 +209,6 @@
if (DEBUG) LOG("onPanelCollapsed");
}
- public void onPanelFullyOpened() {
- if (DEBUG) LOG("onPanelFullyOpened");
- }
-
public void onTrackingStarted() {
mTracking = true;
}
@@ -226,4 +224,10 @@
public void onClosingFinished() {
}
+
+ /** An interface that will be notified of panel state changes. */
+ public interface PanelStateChangeListener {
+ /** Called when the state changes. */
+ void onStateChanged(@PanelState int state);
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelViewController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelViewController.java
index b215515..c23577c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelViewController.java
@@ -343,13 +343,6 @@
protected abstract float getOpeningHeight();
/**
- * Minimum fraction from where expansion should start. This is set when pulling down on a
- * heads-up notification.
- * @param minFraction Fraction from 0 to 1.
- */
- public abstract void setMinFraction(float minFraction);
-
- /**
* @return whether the swiping direction is upwards and above a 45 degree angle compared to the
* horizontal direction
*/
@@ -392,11 +385,16 @@
final boolean expand;
if (event.getActionMasked() == MotionEvent.ACTION_CANCEL || forceCancel) {
- // If we get a cancel, put the shade back to the state it was in when the gesture
- // started
- if (onKeyguard) {
+ // If the keyguard is fading away, don't expand it again. This can happen if you're
+ // swiping to unlock, the app below the keyguard is in landscape, and the screen
+ // rotates while your finger is still down after the swipe to unlock.
+ if (mKeyguardStateController.isKeyguardFadingAway()) {
+ expand = false;
+ } else if (onKeyguard) {
expand = true;
} else {
+ // If we get a cancel, put the shade back to the state it was in when the
+ // gesture started
expand = !mPanelClosedOnDown;
}
} else {
@@ -1171,11 +1169,6 @@
return new OnConfigurationChangedListener();
}
- /**
- * Set that the panel is currently opening and not fully opened or closed.
- */
- public abstract void setIsShadeOpening(boolean opening);
-
public class TouchHandler implements View.OnTouchListener {
public boolean onInterceptTouchEvent(MotionEvent event) {
if (mInstantExpanding || !mNotificationsDragEnabled || mTouchDisabled || (mMotionAborted
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarView.java
index 9ab6cdd..150d9c8 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarView.java
@@ -18,8 +18,6 @@
import static com.android.systemui.ScreenDecorations.DisplayCutoutView.boundsFromDirection;
-import static java.lang.Float.isNaN;
-
import android.annotation.Nullable;
import android.content.Context;
import android.content.res.Configuration;
@@ -56,9 +54,7 @@
StatusBar mBar;
- boolean mIsFullyOpenedPanel = false;
private ScrimController mScrimController;
- private float mMinFraction;
private Runnable mHideExpandedRunnable = new Runnable() {
@Override
public void run() {
@@ -216,7 +212,6 @@
super.onPanelCollapsed();
// Close the status bar in the next frame so we can show the end of the animation.
post(mHideExpandedRunnable);
- mIsFullyOpenedPanel = false;
}
public void removePendingHideExpandedRunnables() {
@@ -224,15 +219,6 @@
}
@Override
- public void onPanelFullyOpened() {
- super.onPanelFullyOpened();
- if (!mIsFullyOpenedPanel) {
- mPanel.getView().sendAccessibilityEvent(AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED);
- }
- mIsFullyOpenedPanel = true;
- }
-
- @Override
public boolean onTouchEvent(MotionEvent event) {
boolean barConsumedEvent = mBar.interceptTouchEvent(event);
@@ -279,21 +265,8 @@
}
@Override
- public void onPanelMinFractionChanged(float minFraction) {
- if (isNaN(minFraction)) {
- throw new IllegalArgumentException("minFraction cannot be NaN");
- }
- super.onPanelMinFractionChanged(minFraction);
- if (mMinFraction != minFraction) {
- mMinFraction = minFraction;
- updateScrimFraction();
- }
- }
-
- @Override
public void panelExpansionChanged(float frac, boolean expanded) {
super.panelExpansionChanged(frac, expanded);
- updateScrimFraction();
if ((frac == 0 || frac == 1)) {
if (mPanelExpansionStateChangedListener != null) {
mPanelExpansionStateChangedListener.onPanelExpansionStateChanged();
@@ -314,15 +287,6 @@
mPanelEnabledProvider = panelEnabledProvider;
}
- private void updateScrimFraction() {
- float scrimFraction = mPanelFraction;
- if (mMinFraction < 1.0f) {
- scrimFraction = Math.max((mPanelFraction - mMinFraction) / (1.0f - mMinFraction),
- 0);
- }
- mScrimController.setPanelExpansion(scrimFraction);
- }
-
public void updateResources() {
mCutoutSideNudge = getResources().getDimensionPixelSize(
R.dimen.display_cutout_margin_consumption);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
index a564637..371ec7a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
@@ -34,6 +34,7 @@
import android.view.animation.DecelerateInterpolator;
import android.view.animation.Interpolator;
+import androidx.annotation.FloatRange;
import androidx.annotation.Nullable;
import com.android.internal.annotations.VisibleForTesting;
@@ -183,8 +184,10 @@
private float mScrimBehindAlphaKeyguard = KEYGUARD_SCRIM_ALPHA;
private final float mDefaultScrimAlpha;
- // Assuming the shade is expanded during initialization
- private float mPanelExpansion = 1f;
+ private float mRawPanelExpansionFraction;
+ private float mPanelScrimMinFraction;
+ // Calculated based on mRawPanelExpansionFraction and mPanelScrimMinFraction
+ private float mPanelExpansionFraction = 1f; // Assume shade is expanded during initialization
private float mQsExpansion;
private boolean mQsBottomVisible;
@@ -483,14 +486,39 @@
*
* The expansion fraction is tied to the scrim opacity.
*
- * @param fraction From 0 to 1 where 0 means collapsed and 1 expanded.
+ * See {@link PanelBar#panelExpansionChanged}.
+ *
+ * @param rawPanelExpansionFraction From 0 to 1 where 0 means collapsed and 1 expanded.
*/
- public void setPanelExpansion(float fraction) {
- if (isNaN(fraction)) {
- throw new IllegalArgumentException("Fraction should not be NaN");
+ public void setRawPanelExpansionFraction(
+ @FloatRange(from = 0.0, to = 1.0) float rawPanelExpansionFraction) {
+ if (isNaN(rawPanelExpansionFraction)) {
+ throw new IllegalArgumentException("rawPanelExpansionFraction should not be NaN");
}
- if (mPanelExpansion != fraction) {
- mPanelExpansion = fraction;
+ mRawPanelExpansionFraction = rawPanelExpansionFraction;
+ calculateAndUpdatePanelExpansion();
+ }
+
+ /** See {@link NotificationPanelViewController#setPanelScrimMinFraction(float)}. */
+ public void setPanelScrimMinFraction(float minFraction) {
+ if (isNaN(minFraction)) {
+ throw new IllegalArgumentException("minFraction should not be NaN");
+ }
+ mPanelScrimMinFraction = minFraction;
+ calculateAndUpdatePanelExpansion();
+ }
+
+ private void calculateAndUpdatePanelExpansion() {
+ float panelExpansionFraction = mRawPanelExpansionFraction;
+ if (mPanelScrimMinFraction < 1.0f) {
+ panelExpansionFraction = Math.max(
+ (mRawPanelExpansionFraction - mPanelScrimMinFraction)
+ / (1.0f - mPanelScrimMinFraction),
+ 0);
+ }
+
+ if (mPanelExpansionFraction != panelExpansionFraction) {
+ mPanelExpansionFraction = panelExpansionFraction;
boolean relevantState = (mState == ScrimState.UNLOCKED
|| mState == ScrimState.KEYGUARD
@@ -892,7 +920,8 @@
}
private float getInterpolatedFraction() {
- return Interpolators.getNotificationScrimAlpha(mPanelExpansion, false /* notification */);
+ return Interpolators.getNotificationScrimAlpha(
+ mPanelExpansionFraction, false /* notification */);
}
private void setScrimAlpha(ScrimView scrim, float alpha) {
@@ -1228,8 +1257,8 @@
pw.println(mTracking);
pw.print(" mDefaultScrimAlpha=");
pw.println(mDefaultScrimAlpha);
- pw.print(" mExpansionFraction=");
- pw.println(mPanelExpansion);
+ pw.print(" mPanelExpansionFraction=");
+ pw.println(mPanelExpansionFraction);
pw.print(" mExpansionAffectsAlpha=");
pw.println(mExpansionAffectsAlpha);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
index 2130e02..16c11e3 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
@@ -336,6 +336,7 @@
}
private final LockscreenShadeTransitionController mLockscreenShadeTransitionController;
+ private boolean mCallingFadingAwayAfterReveal;
private StatusBarCommandQueueCallbacks mCommandQueueCallbacks;
void setWindowState(int state) {
@@ -628,6 +629,7 @@
private final Executor mUiBgExecutor;
protected boolean mDozing;
+ private boolean mIsFullscreen;
private final NotificationMediaManager mMediaManager;
private final NotificationLockscreenUserManager mLockscreenUserManager;
@@ -898,6 +900,8 @@
lockscreenShadeTransitionController.setStatusbar(this);
mExpansionChangedListeners = new ArrayList<>();
+ addExpansionChangedListener(
+ (expansion, expanded) -> mScrimController.setRawPanelExpansionFraction(expansion));
mBubbleExpandListener =
(isExpanding, key) -> mContext.getMainExecutor().execute(() -> {
@@ -909,6 +913,9 @@
mActivityLaunchAnimator = activityLaunchAnimator;
mDialogLaunchAnimator = dialogLaunchAnimator;
+ // The status bar background may need updating when the ongoing call status changes.
+ mOngoingCallController.addCallback((animate) -> maybeUpdateBarMode());
+
// TODO(b/190746471): Find a better home for this.
DateTimeView.setReceiverHandler(timeTickHandler);
@@ -1161,6 +1168,8 @@
mStatusBarView = (PhoneStatusBarView) statusBarFragment.getView();
mStatusBarView.setBar(this);
mStatusBarView.setPanel(mNotificationPanelViewController);
+ mStatusBarView.setPanelStateChangeListener(
+ mNotificationPanelViewController.getPanelStateChangeListener());
mStatusBarView.setScrimController(mScrimController);
mStatusBarView.setExpansionChangedListeners(mExpansionChangedListeners);
for (ExpansionChangedListener listener : mExpansionChangedListeners) {
@@ -1398,6 +1407,12 @@
mDeviceProvisionedController.addCallback(mUserSetupObserver);
mUserSetupObserver.onUserSetupChanged();
+ for (ExpansionChangedListener listener : mExpansionChangedListeners) {
+ // The initial expansion amount comes from mNotificationPanelViewController, so we
+ // should send the amount once we've fully set up that controller.
+ sendInitialExpansionAmount(listener);
+ }
+
// disable profiling bars, since they overlap and clutter the output on app windows
ThreadedRenderer.overrideProperty("disableProfileBars", "true");
@@ -1550,6 +1565,9 @@
time, PowerManager.WAKE_REASON_GESTURE, "com.android.systemui:" + why);
mWakeUpComingFromTouch = true;
where.getLocationInWindow(mTmpInt2);
+
+ // NOTE, the incoming view can sometimes be the entire container... unsure if
+ // this location is valuable enough
mWakeUpTouchLocation = new PointF(mTmpInt2[0] + where.getWidth() / 2,
mTmpInt2[1] + where.getHeight() / 2);
mFalsingCollector.onScreenOnFromTouch();
@@ -1643,7 +1661,7 @@
}
});
mStatusBarKeyguardViewManager.registerStatusBar(
- /* statusBar= */ this, getBouncerContainer(),
+ /* statusBar= */ this,
mNotificationPanelViewController, mBiometricUnlockController,
mStackScroller, mKeyguardBypassController);
mKeyguardIndicationController
@@ -1678,8 +1696,8 @@
return mNotificationPanelViewController;
}
- protected ViewGroup getBouncerContainer() {
- return mNotificationShadeWindowView.findViewById(R.id.keyboard_bouncer_container);
+ public ViewGroup getBouncerContainer() {
+ return mNotificationShadeWindowViewController.getBouncerContainer();
}
public int getStatusBarHeight() {
@@ -2237,7 +2255,7 @@
if (!mTransientShown) {
mTransientShown = true;
mNoAnimationOnNextBarModeChange = true;
- handleTransientChanged();
+ maybeUpdateBarMode();
}
}
@@ -2245,11 +2263,11 @@
void clearTransient() {
if (mTransientShown) {
mTransientShown = false;
- handleTransientChanged();
+ maybeUpdateBarMode();
}
}
- private void handleTransientChanged() {
+ private void maybeUpdateBarMode() {
final int barMode = barMode(mTransientShown, mAppearance);
if (updateBarMode(barMode)) {
mLightBarController.onStatusBarModeChanged(barMode);
@@ -2267,9 +2285,11 @@
return false;
}
- private static @TransitionMode int barMode(boolean isTransient, int appearance) {
+ private @TransitionMode int barMode(boolean isTransient, int appearance) {
final int lightsOutOpaque = APPEARANCE_LOW_PROFILE_BARS | APPEARANCE_OPAQUE_STATUS_BARS;
- if (isTransient) {
+ if (mOngoingCallController.hasOngoingCall() && mIsFullscreen) {
+ return MODE_SEMI_TRANSPARENT;
+ } else if (isTransient) {
return MODE_SEMI_TRANSPARENT;
} else if ((appearance & lightsOutOpaque) == lightsOutOpaque) {
return MODE_LIGHTS_OUT;
@@ -3120,8 +3140,20 @@
public void fadeKeyguardWhilePulsing() {
mNotificationPanelViewController.fadeOut(0, FADE_KEYGUARD_DURATION_PULSING,
()-> {
- hideKeyguard();
- mStatusBarKeyguardViewManager.onKeyguardFadedAway();
+ Runnable finishFading = () -> {
+ mCallingFadingAwayAfterReveal = false;
+ hideKeyguard();
+ mStatusBarKeyguardViewManager.onKeyguardFadedAway();
+ };
+ if (mLightRevealScrim.getRevealAmount() != 1.0f) {
+ mCallingFadingAwayAfterReveal = true;
+ // We're still revealing the Light reveal, let's only go to keyguard once
+ // that has finished and nothing moves anymore.
+ // Going there introduces lots of jank
+ mLightRevealScrim.setFullyRevealedRunnable(finishFading);
+ } else {
+ finishFading.run();
+ }
}).start();
}
@@ -4222,9 +4254,11 @@
}
private void sendInitialExpansionAmount(ExpansionChangedListener expansionChangedListener) {
- expansionChangedListener.onExpansionChanged(
- mNotificationPanelViewController.getExpandedFraction(),
- mNotificationPanelViewController.isExpanded());
+ if (mNotificationPanelViewController != null) {
+ expansionChangedListener.onExpansionChanged(
+ mNotificationPanelViewController.getExpandedFraction(),
+ mNotificationPanelViewController.isExpanded());
+ }
}
public void removeExpansionChangedListener(@NonNull ExpansionChangedListener listener) {
@@ -4280,7 +4314,7 @@
+ "mStatusBarKeyguardViewManager was null");
return;
}
- if (mKeyguardStateController.isKeyguardFadingAway()) {
+ if (mKeyguardStateController.isKeyguardFadingAway() && !mCallingFadingAwayAfterReveal) {
mStatusBarKeyguardViewManager.onKeyguardFadedAway();
}
}
@@ -4471,6 +4505,12 @@
updateReportRejectedTouchVisibility();
Trace.endSection();
}
+
+ @Override
+ public void onFullscreenStateChanged(boolean isFullscreen) {
+ mIsFullscreen = isFullscreen;
+ maybeUpdateBarMode();
+ }
};
private final BatteryController.BatteryStateChangeCallback mBatteryStateChangeCallback =
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
index 18fd9d3..07489a9 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
@@ -120,7 +120,8 @@
@Override
public void onFullyShown() {
updateStates();
- mStatusBar.wakeUpIfDozing(SystemClock.uptimeMillis(), mContainer, "BOUNCER_VISIBLE");
+ mStatusBar.wakeUpIfDozing(SystemClock.uptimeMillis(),
+ mStatusBar.getBouncerContainer(), "BOUNCER_VISIBLE");
}
@Override
@@ -174,7 +175,6 @@
private NotificationPanelViewController mNotificationPanelViewController;
private BiometricUnlockController mBiometricUnlockController;
- private ViewGroup mContainer;
private View mNotificationContainer;
protected KeyguardBouncer mBouncer;
@@ -270,14 +270,14 @@
@Override
public void registerStatusBar(StatusBar statusBar,
- ViewGroup container,
NotificationPanelViewController notificationPanelViewController,
BiometricUnlockController biometricUnlockController,
View notificationContainer,
KeyguardBypassController bypassController) {
mStatusBar = statusBar;
- mContainer = container;
mBiometricUnlockController = biometricUnlockController;
+
+ ViewGroup container = mStatusBar.getBouncerContainer();
mBouncer = mKeyguardBouncerFactory.create(container, mExpansionCallback);
mNotificationPanelViewController = notificationPanelViewController;
notificationPanelViewController.addExpansionListener(this);
@@ -356,7 +356,8 @@
} else if (mPulsing && expansion == KeyguardBouncer.EXPANSION_VISIBLE) {
// Panel expanded while pulsing but didn't translate the bouncer (because we are
// unlocked.) Let's simply wake-up to dismiss the lock screen.
- mStatusBar.wakeUpIfDozing(SystemClock.uptimeMillis(), mContainer, "BOUNCER_VISIBLE");
+ mStatusBar.wakeUpIfDozing(SystemClock.uptimeMillis(), mStatusBar.getBouncerContainer(),
+ "BOUNCER_VISIBLE");
}
}
@@ -833,7 +834,7 @@
}
public void onKeyguardFadedAway() {
- mContainer.postDelayed(() -> mNotificationShadeWindowController
+ mNotificationContainer.postDelayed(() -> mNotificationShadeWindowController
.setKeyguardFadingAway(false), 100);
ViewGroupFadeHelper.reset(mNotificationPanelViewController.getView());
mStatusBar.finishKeyguardFadingAway();
@@ -958,10 +959,6 @@
};
protected void updateStates() {
- if (mContainer == null ) {
- return;
- }
- int vis = mContainer.getSystemUiVisibility();
boolean showing = mShowing;
boolean occluded = mOccluded;
boolean bouncerShowing = mBouncer.isShowing();
@@ -973,9 +970,9 @@
(mLastBouncerDismissible || !mLastShowing || mLastRemoteInputActive)
|| mFirstUpdate) {
if (bouncerDismissible || !showing || remoteInputActive) {
- mContainer.setSystemUiVisibility(vis & ~View.STATUS_BAR_DISABLE_BACK);
+ mBouncer.setBackButtonEnabled(true);
} else {
- mContainer.setSystemUiVisibility(vis | View.STATUS_BAR_DISABLE_BACK);
+ mBouncer.setBackButtonEnabled(false);
}
}
@@ -1042,11 +1039,11 @@
if (delay == 0) {
mMakeNavigationBarVisibleRunnable.run();
} else {
- mContainer.postOnAnimationDelayed(mMakeNavigationBarVisibleRunnable,
+ mNotificationContainer.postOnAnimationDelayed(mMakeNavigationBarVisibleRunnable,
delay);
}
} else {
- mContainer.removeCallbacks(mMakeNavigationBarVisibleRunnable);
+ mNotificationContainer.removeCallbacks(mMakeNavigationBarVisibleRunnable);
mStatusBar.getNotificationShadeWindowView().getWindowInsetsController()
.hide(navigationBars());
}
@@ -1371,4 +1368,4 @@
void requestUdfps(boolean requestUdfps, int color);
}
-}
\ No newline at end of file
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowController.java
index aec27d0..235a8e8 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowController.java
@@ -209,6 +209,21 @@
}
/**
+ * Sets whether an ongoing process requires the status bar to be forced visible.
+ *
+ * This method is separate from {@link this#setForceStatusBarVisible} because the ongoing
+ * process **takes priority**. For example, if {@link this#setForceStatusBarVisible} is set to
+ * false but this method is set to true, then the status bar **will** be visible.
+ *
+ * TODO(b/195839150): We should likely merge this method and
+ * {@link this#setForceStatusBarVisible} together and use some sort of ranking system instead.
+ */
+ public void setOngoingProcessRequiresStatusBarVisible(boolean visible) {
+ mCurrentState.mOngoingProcessRequiresStatusBarVisible = visible;
+ apply(mCurrentState);
+ }
+
+ /**
* Return the container in which we should run launch animations started from the status bar and
* expanding into the opening window.
*
@@ -248,10 +263,14 @@
private static class State {
boolean mForceStatusBarVisible;
boolean mIsLaunchAnimationRunning;
+ boolean mOngoingProcessRequiresStatusBarVisible;
}
private void applyForceStatusBarVisibleFlag(State state) {
- if (state.mForceStatusBarVisible || state.mIsLaunchAnimationRunning) {
+ if (state.mForceStatusBarVisible
+ || state.mIsLaunchAnimationRunning
+ // Don't force-show the status bar if the user has already dismissed it.
+ || state.mOngoingProcessRequiresStatusBarVisible) {
mLpChanged.privateFlags |= PRIVATE_FLAG_FORCE_SHOW_STATUS_BAR;
} else {
mLpChanged.privateFlags &= ~PRIVATE_FLAG_FORCE_SHOW_STATUS_BAR;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/UnlockedScreenOffAnimationController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/UnlockedScreenOffAnimationController.kt
index 143aaba..f3f3325 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/UnlockedScreenOffAnimationController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/UnlockedScreenOffAnimationController.kt
@@ -4,10 +4,10 @@
import android.animation.AnimatorListenerAdapter
import android.animation.ValueAnimator
import android.content.Context
-import android.content.res.Configuration
import android.database.ContentObserver
import android.os.Handler
import android.provider.Settings
+import android.view.Surface
import android.view.View
import com.android.systemui.animation.Interpolators
import com.android.systemui.dagger.SysUISingleton
@@ -239,10 +239,11 @@
return false
}
- // If we're not allowed to rotate the keyguard, then only do the screen off animation if
- // we're in portrait. Otherwise, AOD will animate in sideways, which looks weird.
+ // If we're not allowed to rotate the keyguard, it can only be displayed in zero-degree
+ // portrait. If we're in another orientation, disable the screen off animation so we don't
+ // animate in the keyguard AOD UI sideways or upside down.
if (!keyguardStateController.isKeyguardScreenRotationAllowed &&
- context.resources.configuration.orientation != Configuration.ORIENTATION_PORTRAIT) {
+ context.display.rotation != Surface.ROTATION_0) {
return false
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarViewModule.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarViewModule.java
index ecf3b86..2791678 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarViewModule.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarViewModule.java
@@ -29,9 +29,9 @@
import com.android.systemui.statusbar.NotificationShelfController;
import com.android.systemui.statusbar.notification.row.dagger.NotificationShelfComponent;
import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout;
-import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController;
import com.android.systemui.statusbar.phone.NotificationPanelView;
import com.android.systemui.statusbar.phone.NotificationShadeWindowView;
+import com.android.systemui.statusbar.phone.NotificationsQuickSettingsContainer;
import com.android.systemui.statusbar.phone.TapAgainView;
import com.android.systemui.util.InjectionInflationController;
@@ -67,8 +67,8 @@
@Provides
@StatusBarComponent.StatusBarScope
public static NotificationStackScrollLayout providesNotificationStackScrollLayout(
- NotificationStackScrollLayoutController notificationStackScrollLayoutController) {
- return notificationStackScrollLayoutController.getView();
+ NotificationShadeWindowView notificationShadeWindowView) {
+ return notificationShadeWindowView.findViewById(R.id.notification_stack_scroller);
}
/** */
@@ -149,4 +149,12 @@
public static TapAgainView getTapAgainView(NotificationPanelView npv) {
return npv.getTapAgainView();
}
+
+ /** */
+ @Provides
+ @StatusBarComponent.StatusBarScope
+ public static NotificationsQuickSettingsContainer getNotificationsQuickSettingsContainer(
+ NotificationShadeWindowView notificationShadeWindowView) {
+ return notificationShadeWindowView.findViewById(R.id.notification_container_parent);
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallController.kt
index ab6ee89..c3c935e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallController.kt
@@ -37,10 +37,12 @@
import com.android.systemui.statusbar.notification.collection.NotificationEntry
import com.android.systemui.statusbar.notification.collection.notifcollection.CommonNotifCollection
import com.android.systemui.statusbar.notification.collection.notifcollection.NotifCollectionListener
+import com.android.systemui.statusbar.phone.StatusBarWindowController
import com.android.systemui.statusbar.policy.CallbackController
import com.android.systemui.util.time.SystemClock
import java.io.FileDescriptor
import java.io.PrintWriter
+import java.util.Optional
import java.util.concurrent.Executor
import javax.inject.Inject
@@ -57,6 +59,8 @@
private val iActivityManager: IActivityManager,
private val logger: OngoingCallLogger,
private val dumpManager: DumpManager,
+ private val statusBarWindowController: Optional<StatusBarWindowController>,
+ private val swipeStatusBarAwayGestureHandler: Optional<SwipeStatusBarAwayGestureHandler>,
) : CallbackController<OngoingCallListener>, Dumpable {
/** Non-null if there's an active call notification. */
@@ -93,7 +97,8 @@
entry.sbn.notification.contentIntent?.intent,
entry.sbn.uid,
entry.sbn.notification.extras.getInt(
- Notification.EXTRA_CALL_TYPE, -1) == CALL_TYPE_ONGOING
+ Notification.EXTRA_CALL_TYPE, -1) == CALL_TYPE_ONGOING,
+ statusBarSwipedAway = callNotificationInfo?.statusBarSwipedAway ?: false
)
if (newOngoingCallInfo == callNotificationInfo) {
return
@@ -199,9 +204,16 @@
)
}
}
-
setUpUidObserver(currentCallNotificationInfo)
-
+ if (!currentCallNotificationInfo.statusBarSwipedAway) {
+ statusBarWindowController.ifPresent {
+ it.setOngoingProcessRequiresStatusBarVisible(true)
+ }
+ // TODO(b/195839150): Only listen for the gesture when in immersive mode.
+ swipeStatusBarAwayGestureHandler.ifPresent {
+ it.addOnGestureDetectedCallback(TAG, this::onSwipeAwayGestureDetected)
+ }
+ }
mListeners.forEach { l -> l.onOngoingCallStateChanged(animate = true) }
} else {
// If we failed to update the chip, don't store the call info. Then [hasOngoingCall]
@@ -268,6 +280,8 @@
private fun removeChip() {
callNotificationInfo = null
tearDownChipView()
+ statusBarWindowController.ifPresent { it.setOngoingProcessRequiresStatusBarVisible(false) }
+ swipeStatusBarAwayGestureHandler.ifPresent { it.removeOnGestureDetectedCallback(TAG) }
mListeners.forEach { l -> l.onOngoingCallStateChanged(animate = true) }
if (uidObserver != null) {
iActivityManager.unregisterUidObserver(uidObserver)
@@ -282,13 +296,34 @@
return this.findViewById(R.id.ongoing_call_chip_time)
}
+ /**
+ * If there's an active ongoing call, then we will force the status bar to always show, even if
+ * the user is in immersive mode. However, we also want to give users the ability to swipe away
+ * the status bar if they need to access the area under the status bar.
+ *
+ * This method updates the status bar window appropriately when the swipe away gesture is
+ * detected.
+ */
+ private fun onSwipeAwayGestureDetected() {
+ if (DEBUG) { Log.d(TAG, "Swipe away gesture detected") }
+ callNotificationInfo = callNotificationInfo?.copy(statusBarSwipedAway = true)
+ statusBarWindowController.ifPresent {
+ it.setOngoingProcessRequiresStatusBarVisible(false)
+ }
+ swipeStatusBarAwayGestureHandler.ifPresent {
+ it.removeOnGestureDetectedCallback(TAG)
+ }
+ }
+
private data class CallNotificationInfo(
val key: String,
val callStartTime: Long,
val intent: Intent?,
val uid: Int,
/** True if the call is currently ongoing (as opposed to incoming, screening, etc.). */
- val isOngoing: Boolean
+ val isOngoing: Boolean,
+ /** True if the user has swiped away the status bar while in this phone call. */
+ val statusBarSwipedAway: Boolean
) {
/**
* Returns true if the notification information has a valid call start time.
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/SwipeStatusBarAwayGestureHandler.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/SwipeStatusBarAwayGestureHandler.kt
new file mode 100644
index 0000000..c97cf14
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/SwipeStatusBarAwayGestureHandler.kt
@@ -0,0 +1,153 @@
+/*
+ * 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.ongoingcall
+
+import android.content.Context
+import android.os.Looper
+import android.util.Log
+import android.view.Choreographer
+import android.view.Display
+import android.view.InputEvent
+import android.view.MotionEvent
+import android.view.MotionEvent.*
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.shared.system.InputChannelCompat
+import com.android.systemui.shared.system.InputMonitorCompat
+import com.android.systemui.statusbar.phone.StatusBarWindowController
+import javax.inject.Inject
+
+/**
+ * A class to detect when a user swipes away the status bar. To be notified when the swipe away
+ * gesture is detected, add a callback via [addOnGestureDetectedCallback].
+ */
+@SysUISingleton
+open class SwipeStatusBarAwayGestureHandler @Inject constructor(
+ context: Context,
+ private val statusBarWindowController: StatusBarWindowController,
+) {
+
+ /**
+ * Active callbacks, each associated with a tag. Gestures will only be monitored if
+ * [callbacks.size] > 0.
+ */
+ private val callbacks: MutableMap<String, () -> Unit> = mutableMapOf()
+
+ private var startY: Float = 0f
+ private var startTime: Long = 0L
+ private var monitoringCurrentTouch: Boolean = false
+
+ private var inputMonitor: InputMonitorCompat? = null
+ private var inputReceiver: InputChannelCompat.InputEventReceiver? = null
+
+ // TODO(b/195839150): Update this threshold when the config changes?
+ private var swipeDistanceThreshold: Int = context.resources.getDimensionPixelSize(
+ com.android.internal.R.dimen.system_gestures_start_threshold
+ )
+
+ /** Adds a callback that will be triggered when the swipe away gesture is detected. */
+ fun addOnGestureDetectedCallback(tag: String, callback: () -> Unit) {
+ val callbacksWasEmpty = callbacks.isEmpty()
+ callbacks[tag] = callback
+ if (callbacksWasEmpty) {
+ startGestureListening()
+ }
+ }
+
+ /** Removes the callback. */
+ fun removeOnGestureDetectedCallback(tag: String) {
+ callbacks.remove(tag)
+ if (callbacks.isEmpty()) {
+ stopGestureListening()
+ }
+ }
+
+ private fun onInputEvent(ev: InputEvent) {
+ if (ev !is MotionEvent) {
+ return
+ }
+
+ when (ev.actionMasked) {
+ ACTION_DOWN -> {
+ if (
+ // Gesture starts just below the status bar
+ // TODO(b/195839150): Is [statusBarHeight] the correct dimension to use for
+ // determining which down touches are valid?
+ ev.y >= statusBarWindowController.statusBarHeight
+ && ev.y <= 3 * statusBarWindowController.statusBarHeight
+ ) {
+ Log.d(TAG, "Beginning gesture detection, y=${ev.y}")
+ startY = ev.y
+ startTime = ev.eventTime
+ monitoringCurrentTouch = true
+ } else {
+ monitoringCurrentTouch = false
+ }
+ }
+ ACTION_MOVE -> {
+ if (!monitoringCurrentTouch) {
+ return
+ }
+ if (
+ // Gesture is up
+ ev.y < startY
+ // Gesture went far enough
+ && (startY - ev.y) >= swipeDistanceThreshold
+ // Gesture completed quickly enough
+ && (ev.eventTime - startTime) < SWIPE_TIMEOUT_MS
+ ) {
+ Log.i(TAG, "Gesture detected; notifying callbacks")
+ callbacks.values.forEach { it.invoke() }
+ monitoringCurrentTouch = false
+ }
+ }
+ ACTION_CANCEL, ACTION_UP -> {
+ monitoringCurrentTouch = false
+ }
+ }
+ }
+
+ /** Start listening for the swipe gesture. */
+ private fun startGestureListening() {
+ stopGestureListening()
+
+ if (DEBUG) { Log.d(TAG, "Input listening started") }
+ inputMonitor = InputMonitorCompat(TAG, Display.DEFAULT_DISPLAY).also {
+ inputReceiver = it.getInputReceiver(
+ Looper.getMainLooper(),
+ Choreographer.getInstance(),
+ this::onInputEvent
+ )
+ }
+ }
+
+ /** Stop listening for the swipe gesture. */
+ private fun stopGestureListening() {
+ inputMonitor?.let {
+ if (DEBUG) { Log.d(TAG, "Input listening stopped") }
+ inputMonitor = null
+ it.dispose()
+ }
+ inputReceiver?.let {
+ inputReceiver = null
+ it.dispose()
+ }
+ }
+}
+
+private const val SWIPE_TIMEOUT_MS: Long = 500
+private val TAG = SwipeStatusBarAwayGestureHandler::class.simpleName
+private val DEBUG = Log.isLoggable(TAG, Log.DEBUG)
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardQsUserSwitchController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardQsUserSwitchController.java
index d838a05..b4aba38 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardQsUserSwitchController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardQsUserSwitchController.java
@@ -35,10 +35,12 @@
import com.android.settingslib.drawable.CircleFramedDrawable;
import com.android.systemui.R;
import com.android.systemui.dagger.qualifiers.Main;
+import com.android.systemui.flags.FeatureFlags;
import com.android.systemui.keyguard.ScreenLifecycle;
import com.android.systemui.plugins.FalsingManager;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.qs.tiles.UserDetailView;
+import com.android.systemui.qs.user.UserSwitchDialogController;
import com.android.systemui.statusbar.SysuiStatusBarStateController;
import com.android.systemui.statusbar.notification.AnimatableProperty;
import com.android.systemui.statusbar.notification.PropertyAnimator;
@@ -76,6 +78,8 @@
private final ConfigurationController mConfigurationController;
private final KeyguardVisibilityHelper mKeyguardVisibilityHelper;
private final KeyguardUserDetailAdapter mUserDetailAdapter;
+ private final FeatureFlags mFeatureFlags;
+ private final UserSwitchDialogController mUserSwitchDialogController;
private NotificationPanelViewController mNotificationPanelViewController;
private UserAvatarView mUserAvatarView;
UserSwitcherController.UserRecord mCurrentUser;
@@ -124,7 +128,9 @@
SysuiStatusBarStateController statusBarStateController,
DozeParameters dozeParameters,
Provider<UserDetailView.Adapter> userDetailViewAdapterProvider,
- UnlockedScreenOffAnimationController unlockedScreenOffAnimationController) {
+ UnlockedScreenOffAnimationController unlockedScreenOffAnimationController,
+ FeatureFlags featureFlags,
+ UserSwitchDialogController userSwitchDialogController) {
super(view);
if (DEBUG) Log.d(TAG, "New KeyguardQsUserSwitchController");
mContext = context;
@@ -139,6 +145,8 @@
keyguardStateController, dozeParameters,
unlockedScreenOffAnimationController, /* animateYPos= */ false);
mUserDetailAdapter = new KeyguardUserDetailAdapter(context, userDetailViewAdapterProvider);
+ mFeatureFlags = featureFlags;
+ mUserSwitchDialogController = userSwitchDialogController;
}
@Override
@@ -162,7 +170,11 @@
}
// Tapping anywhere in the view will open QS user panel
- openQsUserPanel();
+ if (mFeatureFlags.useNewUserSwitcher()) {
+ mUserSwitchDialogController.showDialog(mView);
+ } else {
+ openQsUserPanel();
+ }
});
mUserAvatarView.setAccessibilityDelegate(new View.AccessibilityDelegate() {
diff --git a/packages/SystemUI/src/com/android/systemui/util/NotificationChannels.java b/packages/SystemUI/src/com/android/systemui/util/NotificationChannels.java
index 0ba072e..f97f4d0 100644
--- a/packages/SystemUI/src/com/android/systemui/util/NotificationChannels.java
+++ b/packages/SystemUI/src/com/android/systemui/util/NotificationChannels.java
@@ -31,7 +31,6 @@
public class NotificationChannels extends SystemUI {
public static String ALERTS = "ALR";
- public static String SCREENSHOTS_LEGACY = "SCN";
public static String SCREENSHOTS_HEADSUP = "SCN_HEADSUP";
public static String GENERAL = "GEN";
public static String STORAGE = "DSK";
@@ -84,18 +83,11 @@
general,
storage,
createScreenshotChannel(
- context.getString(R.string.notification_channel_screenshot),
- nm.getNotificationChannel(SCREENSHOTS_LEGACY)),
+ context.getString(R.string.notification_channel_screenshot)),
batteryChannel,
hint
));
- // Delete older SS channel if present.
- // Screenshots promoted to heads-up in P, this cleans up the lower priority channel from O.
- // This line can be deleted in Q.
- nm.deleteNotificationChannel(SCREENSHOTS_LEGACY);
-
-
if (isTv(context)) {
// TV specific notification channel for TV PIP controls.
// Importance should be {@link NotificationManager#IMPORTANCE_MAX} to have the highest
@@ -113,7 +105,7 @@
* @return
*/
@VisibleForTesting static NotificationChannel createScreenshotChannel(
- String name, NotificationChannel legacySS) {
+ String name) {
NotificationChannel screenshotChannel = new NotificationChannel(SCREENSHOTS_HEADSUP,
name, NotificationManager.IMPORTANCE_HIGH); // pop on screen
@@ -121,24 +113,6 @@
new AudioAttributes.Builder().setUsage(AudioAttributes.USAGE_NOTIFICATION).build());
screenshotChannel.setBlockable(true);
- if (legacySS != null) {
- // Respect any user modified fields from the old channel.
- int userlock = legacySS.getUserLockedFields();
- if ((userlock & NotificationChannel.USER_LOCKED_IMPORTANCE) != 0) {
- screenshotChannel.setImportance(legacySS.getImportance());
- }
- if ((userlock & NotificationChannel.USER_LOCKED_SOUND) != 0) {
- screenshotChannel.setSound(legacySS.getSound(), legacySS.getAudioAttributes());
- }
- if ((userlock & NotificationChannel.USER_LOCKED_VIBRATION) != 0) {
- screenshotChannel.setVibrationPattern(legacySS.getVibrationPattern());
- }
- if ((userlock & NotificationChannel.USER_LOCKED_LIGHTS) != 0) {
- screenshotChannel.setLightColor(legacySS.getLightColor());
- }
- // skip show_badge, irrelevant for system channel
- }
-
return screenshotChannel;
}
diff --git a/packages/SystemUI/src/com/android/systemui/util/WallpaperController.kt b/packages/SystemUI/src/com/android/systemui/util/WallpaperController.kt
index 2c01a70..db2aca8 100644
--- a/packages/SystemUI/src/com/android/systemui/util/WallpaperController.kt
+++ b/packages/SystemUI/src/com/android/systemui/util/WallpaperController.kt
@@ -40,8 +40,8 @@
this.wallpaperInfo = wallpaperInfo
}
- private val shouldUseDefaultDisplayStateChangeTransition: Boolean
- get() = wallpaperInfo?.shouldUseDefaultDisplayStateChangeTransition()
+ private val shouldUseDefaultUnfoldTransition: Boolean
+ get() = wallpaperInfo?.shouldUseDefaultUnfoldTransition()
?: true
fun setNotificationShadeZoom(zoomOut: Float) {
@@ -50,7 +50,7 @@
}
fun setUnfoldTransitionZoom(zoomOut: Float) {
- if (shouldUseDefaultDisplayStateChangeTransition) {
+ if (shouldUseDefaultUnfoldTransition) {
unfoldTransitionZoomOut = zoomOut
updateZoom()
}
diff --git a/packages/SystemUI/src/com/android/systemui/util/sensors/PostureDependentProximitySensor.java b/packages/SystemUI/src/com/android/systemui/util/sensors/PostureDependentProximitySensor.java
index 28cba8e..40982bb 100644
--- a/packages/SystemUI/src/com/android/systemui/util/sensors/PostureDependentProximitySensor.java
+++ b/packages/SystemUI/src/com/android/systemui/util/sensors/PostureDependentProximitySensor.java
@@ -18,8 +18,7 @@
import android.util.Log;
-import androidx.annotation.NonNull;
-
+import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.statusbar.policy.DevicePostureController;
import com.android.systemui.util.concurrency.DelayableExecutor;
import com.android.systemui.util.concurrency.Execution;
@@ -42,9 +41,9 @@
PostureDependentProximitySensor(
@PrimaryProxSensor ThresholdSensor[] postureToPrimaryProxSensorMap,
@SecondaryProxSensor ThresholdSensor[] postureToSecondaryProxSensorMap,
- @NonNull DelayableExecutor delayableExecutor,
- @NonNull Execution execution,
- @NonNull DevicePostureController devicePostureController
+ @Main DelayableExecutor delayableExecutor,
+ Execution execution,
+ DevicePostureController devicePostureController
) {
super(
postureToPrimaryProxSensorMap[0],
diff --git a/packages/SystemUI/src/com/android/systemui/wmshell/WMShellBaseModule.java b/packages/SystemUI/src/com/android/systemui/wmshell/WMShellBaseModule.java
index ff26310..514a903 100644
--- a/packages/SystemUI/src/com/android/systemui/wmshell/WMShellBaseModule.java
+++ b/packages/SystemUI/src/com/android/systemui/wmshell/WMShellBaseModule.java
@@ -73,6 +73,7 @@
import com.android.wm.shell.sizecompatui.SizeCompatUIController;
import com.android.wm.shell.splitscreen.SplitScreen;
import com.android.wm.shell.splitscreen.SplitScreenController;
+import com.android.wm.shell.splitscreen.StageTaskUnfoldController;
import com.android.wm.shell.startingsurface.StartingSurface;
import com.android.wm.shell.startingsurface.StartingWindowController;
import com.android.wm.shell.startingsurface.StartingWindowTypeAlgorithm;
@@ -81,10 +82,14 @@
import com.android.wm.shell.transition.ShellTransitions;
import com.android.wm.shell.transition.Transitions;
import com.android.wm.shell.unfold.ShellUnfoldProgressProvider;
+import com.android.wm.shell.unfold.UnfoldBackgroundController;
import java.util.Optional;
+import javax.inject.Provider;
+
import dagger.BindsOptionalOf;
+import dagger.Lazy;
import dagger.Module;
import dagger.Provides;
@@ -234,16 +239,48 @@
static Optional<FullscreenUnfoldController> provideFullscreenUnfoldController(
Context context,
Optional<ShellUnfoldProgressProvider> progressProvider,
- RootTaskDisplayAreaOrganizer rootTaskDisplayAreaOrganizer,
+ Lazy<UnfoldBackgroundController> unfoldBackgroundController,
DisplayInsetsController displayInsetsController,
@ShellMainThread ShellExecutor mainExecutor
) {
return progressProvider.map(shellUnfoldTransitionProgressProvider ->
new FullscreenUnfoldController(context, mainExecutor,
- shellUnfoldTransitionProgressProvider, rootTaskDisplayAreaOrganizer,
+ unfoldBackgroundController.get(), shellUnfoldTransitionProgressProvider,
displayInsetsController));
}
+ @Provides
+ static Optional<StageTaskUnfoldController> provideStageTaskUnfoldController(
+ Optional<ShellUnfoldProgressProvider> progressProvider,
+ Context context,
+ TransactionPool transactionPool,
+ Lazy<UnfoldBackgroundController> unfoldBackgroundController,
+ DisplayInsetsController displayInsetsController,
+ @ShellMainThread ShellExecutor mainExecutor
+ ) {
+ return progressProvider.map(shellUnfoldTransitionProgressProvider ->
+ new StageTaskUnfoldController(
+ context,
+ transactionPool,
+ shellUnfoldTransitionProgressProvider,
+ displayInsetsController,
+ unfoldBackgroundController.get(),
+ mainExecutor
+ ));
+ }
+
+ @WMSingleton
+ @Provides
+ static UnfoldBackgroundController provideUnfoldBackgroundController(
+ RootTaskDisplayAreaOrganizer rootTaskDisplayAreaOrganizer,
+ Context context
+ ) {
+ return new UnfoldBackgroundController(
+ context,
+ rootTaskDisplayAreaOrganizer
+ );
+ }
+
//
// Freeform (optional feature)
//
@@ -401,12 +438,13 @@
@ShellMainThread ShellExecutor mainExecutor,
DisplayImeController displayImeController,
DisplayInsetsController displayInsetsController, Transitions transitions,
- TransactionPool transactionPool) {
+ TransactionPool transactionPool,
+ Provider<Optional<StageTaskUnfoldController>> stageTaskUnfoldControllerProvider) {
if (ActivityTaskManager.supportsSplitScreenMultiWindow(context)) {
return Optional.of(new SplitScreenController(shellTaskOrganizer, syncQueue, context,
rootTaskDisplayAreaOrganizer, mainExecutor, displayImeController,
displayInsetsController, transitions,
- transactionPool));
+ transactionPool, stageTaskUnfoldControllerProvider));
} else {
return Optional.empty();
}
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchControllerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchControllerTest.java
index 88b596c..5c7885f 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchControllerTest.java
@@ -142,7 +142,8 @@
mBypassController,
mSmartspaceController,
mKeyguardUnlockAnimationController,
- mSmartSpaceTransitionController
+ mSmartSpaceTransitionController,
+ mResources
);
when(mStatusBarStateController.getState()).thenReturn(StatusBarState.SHADE);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/AnimatableClockControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/keyguard/AnimatableClockControllerTest.java
new file mode 100644
index 0000000..df11284
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/AnimatableClockControllerTest.java
@@ -0,0 +1,144 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.keyguard;
+
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession;
+
+import static junit.framework.Assert.assertFalse;
+import static junit.framework.Assert.assertTrue;
+
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.anyObject;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.content.res.Resources;
+import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper;
+import android.view.View;
+
+import androidx.test.filters.SmallTest;
+
+import com.android.keyguard.AnimatableClockController;
+import com.android.keyguard.AnimatableClockView;
+import com.android.keyguard.KeyguardUpdateMonitor;
+import com.android.settingslib.Utils;
+import com.android.systemui.SysuiTestCase;
+import com.android.systemui.broadcast.BroadcastDispatcher;
+import com.android.systemui.plugins.statusbar.StatusBarStateController;
+import com.android.systemui.statusbar.phone.KeyguardBypassController;
+import com.android.systemui.statusbar.policy.BatteryController;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Captor;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.mockito.MockitoSession;
+import org.mockito.quality.Strictness;
+
+@SmallTest
+@RunWith(AndroidTestingRunner.class)
+@TestableLooper.RunWithLooper
+public class AnimatableClockControllerTest extends SysuiTestCase {
+ @Mock
+ private AnimatableClockView mClockView;
+ @Mock
+ private StatusBarStateController mStatusBarStateController;
+ @Mock
+ private BroadcastDispatcher mBroadcastDispatcher;
+ @Mock
+ private BatteryController mBatteryController;
+ @Mock
+ private KeyguardUpdateMonitor mKeyguardUpdateMonitor;
+ @Mock
+ private KeyguardBypassController mBypassController;
+ @Mock
+ private Resources mResources;
+
+ private MockitoSession mStaticMockSession;
+ private AnimatableClockController mAnimatableClockController;
+
+ // Capture listeners so that they can be used to send events
+ @Captor private ArgumentCaptor<View.OnAttachStateChangeListener> mAttachCaptor =
+ ArgumentCaptor.forClass(View.OnAttachStateChangeListener.class);
+ private View.OnAttachStateChangeListener mAttachListener;
+
+ @Captor private ArgumentCaptor<StatusBarStateController.StateListener> mStatusBarStateCaptor;
+ private StatusBarStateController.StateListener mStatusBarStateCallback;
+
+ @Before
+ public void setUp() throws Exception {
+ MockitoAnnotations.initMocks(this);
+ mStaticMockSession = mockitoSession()
+ .mockStatic(Utils.class)
+ .strictness(Strictness.LENIENT) // it's ok if mocked classes aren't used
+ .startMocking();
+ when(Utils.getColorAttrDefaultColor(anyObject(), anyInt())).thenReturn(0);
+
+ mAnimatableClockController = new AnimatableClockController(
+ mClockView,
+ mStatusBarStateController,
+ mBroadcastDispatcher,
+ mBatteryController,
+ mKeyguardUpdateMonitor,
+ mBypassController,
+ mResources
+ );
+ mAnimatableClockController.init();
+ captureAttachListener();
+ }
+
+ @After
+ public void tearDown() {
+ mStaticMockSession.finishMocking();
+ }
+
+ @Test
+ public void testOnAttachedUpdatesDozeStateToTrue() {
+ // GIVEN dozing
+ when(mStatusBarStateController.isDozing()).thenReturn(true);
+ when(mStatusBarStateController.getDozeAmount()).thenReturn(1f);
+
+ // WHEN the clock view gets attached
+ mAttachListener.onViewAttachedToWindow(mClockView);
+
+ // THEN the clock controller updated its dozing state to true
+ assertTrue(mAnimatableClockController.isDozing());
+ }
+
+ @Test
+ public void testOnAttachedUpdatesDozeStateToFalse() {
+ // GIVEN not dozing
+ when(mStatusBarStateController.isDozing()).thenReturn(false);
+ when(mStatusBarStateController.getDozeAmount()).thenReturn(0f);
+
+ // WHEN the clock view gets attached
+ mAttachListener.onViewAttachedToWindow(mClockView);
+
+ // THEN the clock controller updated its dozing state to false
+ assertFalse(mAnimatableClockController.isDozing());
+ }
+
+ private void captureAttachListener() {
+ verify(mClockView).addOnAttachStateChangeListener(mAttachCaptor.capture());
+ mAttachListener = mAttachCaptor.getValue();
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/LockIconViewControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/keyguard/LockIconViewControllerTest.java
index c464cad..ddf1d70 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/LockIconViewControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/LockIconViewControllerTest.java
@@ -18,14 +18,18 @@
import static junit.framework.Assert.assertEquals;
+import static org.mockito.ArgumentMatchers.anyObject;
import static org.mockito.Mockito.anyInt;
import static org.mockito.Mockito.eq;
+import static org.mockito.Mockito.reset;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import android.content.Context;
import android.content.res.Resources;
import android.graphics.PointF;
+import android.graphics.drawable.AnimatedVectorDrawable;
+import android.hardware.biometrics.BiometricSourceType;
import android.hardware.biometrics.SensorLocationInternal;
import android.hardware.fingerprint.FingerprintSensorPropertiesInternal;
import android.os.Vibrator;
@@ -39,15 +43,18 @@
import androidx.test.filters.SmallTest;
import com.android.keyguard.KeyguardUpdateMonitor;
+import com.android.keyguard.KeyguardUpdateMonitorCallback;
import com.android.keyguard.KeyguardViewController;
import com.android.keyguard.LockIconView;
import com.android.keyguard.LockIconViewController;
+import com.android.systemui.R;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.biometrics.AuthController;
import com.android.systemui.biometrics.AuthRippleController;
import com.android.systemui.dump.DumpManager;
import com.android.systemui.plugins.FalsingManager;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
+import com.android.systemui.statusbar.StatusBarState;
import com.android.systemui.statusbar.policy.ConfigurationController;
import com.android.systemui.statusbar.policy.KeyguardStateController;
import com.android.systemui.util.concurrency.DelayableExecutor;
@@ -69,7 +76,10 @@
@RunWith(AndroidTestingRunner.class)
@TestableLooper.RunWithLooper
public class LockIconViewControllerTest extends SysuiTestCase {
+ private static final String UNLOCKED_LABEL = "unlocked";
+
private @Mock LockIconView mLockIconView;
+ private @Mock AnimatedVectorDrawable mIconDrawable;
private @Mock Context mContext;
private @Mock Resources mResources;
private @Mock DisplayMetrics mDisplayMetrics;
@@ -97,6 +107,11 @@
@Captor private ArgumentCaptor<AuthController.Callback> mAuthControllerCallbackCaptor;
private AuthController.Callback mAuthControllerCallback;
+ @Captor private ArgumentCaptor<KeyguardUpdateMonitorCallback>
+ mKeyguardUpdateMonitorCallbackCaptor =
+ ArgumentCaptor.forClass(KeyguardUpdateMonitorCallback.class);
+ private KeyguardUpdateMonitorCallback mKeyguardUpdateMonitorCallback;
+
@Captor private ArgumentCaptor<PointF> mPointCaptor;
@Before
@@ -108,6 +123,13 @@
when(mContext.getResources()).thenReturn(mResources);
when(mResources.getDisplayMetrics()).thenReturn(mDisplayMetrics);
when(mLockIconView.findViewById(anyInt())).thenReturn(mAodFp);
+ when(mResources.getString(R.string.accessibility_unlock_button)).thenReturn(UNLOCKED_LABEL);
+ when(mResources.getDrawable(anyInt(), anyObject())).thenReturn(mIconDrawable);
+
+ when(mKeyguardStateController.isShowing()).thenReturn(true);
+ when(mKeyguardStateController.isKeyguardGoingAway()).thenReturn(false);
+ when(mStatusBarStateController.isDozing()).thenReturn(false);
+ when(mStatusBarStateController.getState()).thenReturn(StatusBarState.KEYGUARD);
mLockIconViewController = new LockIconViewController(
mLockIconView,
@@ -192,6 +214,29 @@
verify(mLockIconView).setUseBackground(false);
}
+ @Test
+ public void testUnlockIconShows_biometricUnlockedTrue() {
+ // GIVEN UDFPS sensor location is available
+ setupUdfps();
+
+ // GIVEN lock icon controller is initialized and view is attached
+ mLockIconViewController.init();
+ captureAttachListener();
+ mAttachListener.onViewAttachedToWindow(mLockIconView);
+ captureKeyguardUpdateMonitorCallback();
+
+ // GIVEN user has unlocked with a biometric auth (ie: face auth)
+ when(mKeyguardUpdateMonitor.getUserUnlockedWithBiometric(anyInt())).thenReturn(true);
+ reset(mLockIconView);
+
+ // WHEN face auth's biometric running state changes
+ mKeyguardUpdateMonitorCallback.onBiometricRunningStateChanged(false,
+ BiometricSourceType.FACE);
+
+ // THEN the unlock icon is shown
+ verify(mLockIconView).setContentDescription(UNLOCKED_LABEL);
+ }
+
private Pair<Integer, PointF> setupUdfps() {
final PointF udfpsLocation = new PointF(50, 75);
final int radius = 33;
@@ -220,4 +265,10 @@
verify(mLockIconView).addOnAttachStateChangeListener(mAttachCaptor.capture());
mAttachListener = mAttachCaptor.getValue();
}
+
+ private void captureKeyguardUpdateMonitorCallback() {
+ verify(mKeyguardUpdateMonitor).registerCallback(
+ mKeyguardUpdateMonitorCallbackCaptor.capture());
+ mKeyguardUpdateMonitorCallback = mKeyguardUpdateMonitorCallbackCaptor.getValue();
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/MediaCarouselControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/MediaCarouselControllerTest.kt
new file mode 100644
index 0000000..175ec87f
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/MediaCarouselControllerTest.kt
@@ -0,0 +1,174 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.media
+
+import android.testing.AndroidTestingRunner
+import android.testing.TestableLooper
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.classifier.FalsingCollector
+import com.android.systemui.dagger.qualifiers.Main
+import com.android.systemui.dump.DumpManager
+import com.android.systemui.plugins.ActivityStarter
+import com.android.systemui.plugins.FalsingManager
+import com.android.systemui.statusbar.notification.collection.legacy.VisualStabilityManager
+import com.android.systemui.statusbar.policy.ConfigurationController
+import com.android.systemui.util.concurrency.DelayableExecutor
+import com.android.systemui.util.time.FakeSystemClock
+import junit.framework.Assert.assertEquals
+import junit.framework.Assert.assertTrue
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.Mock
+import org.mockito.MockitoAnnotations
+import javax.inject.Provider
+
+private val DATA = MediaData(
+ userId = -1,
+ initialized = false,
+ backgroundColor = 0,
+ app = null,
+ appIcon = null,
+ artist = null,
+ song = null,
+ artwork = null,
+ actions = emptyList(),
+ actionsToShowInCompact = emptyList(),
+ packageName = "INVALID",
+ token = null,
+ clickIntent = null,
+ device = null,
+ active = true,
+ resumeAction = null)
+
+private val SMARTSPACE_KEY = "smartspace"
+
+@SmallTest
+@TestableLooper.RunWithLooper(setAsMainLooper = true)
+@RunWith(AndroidTestingRunner::class)
+class MediaCarouselControllerTest : SysuiTestCase() {
+
+ @Mock lateinit var mediaControlPanelFactory: Provider<MediaControlPanel>
+ @Mock lateinit var panel: MediaControlPanel
+ @Mock lateinit var visualStabilityManager: VisualStabilityManager
+ @Mock lateinit var mediaHostStatesManager: MediaHostStatesManager
+ @Mock lateinit var activityStarter: ActivityStarter
+ @Mock @Main private lateinit var executor: DelayableExecutor
+ @Mock lateinit var mediaDataManager: MediaDataManager
+ @Mock lateinit var configurationController: ConfigurationController
+ @Mock lateinit var falsingCollector: FalsingCollector
+ @Mock lateinit var falsingManager: FalsingManager
+ @Mock lateinit var dumpManager: DumpManager
+
+ private val clock = FakeSystemClock()
+ private lateinit var mediaCarouselController: MediaCarouselController
+
+ @Before
+ fun setup() {
+ MockitoAnnotations.initMocks(this)
+
+ mediaCarouselController = MediaCarouselController(
+ context,
+ mediaControlPanelFactory,
+ visualStabilityManager,
+ mediaHostStatesManager,
+ activityStarter,
+ clock,
+ executor,
+ mediaDataManager,
+ configurationController,
+ falsingCollector,
+ falsingManager,
+ dumpManager
+ )
+
+ MediaPlayerData.clear()
+ }
+
+ @Test
+ fun testPlayerOrdering() {
+ // Test values: key, data, last active time
+ val playingLocal = Triple("playing local",
+ DATA.copy(active = true, isPlaying = true, isLocalSession = true, resumption = false),
+ 4500L)
+
+ val playingRemote = Triple("playing remote",
+ DATA.copy(active = true, isPlaying = true, isLocalSession = false, resumption = false),
+ 5000L)
+
+ val pausedLocal = Triple("paused local",
+ DATA.copy(active = true, isPlaying = false, isLocalSession = true, resumption = false),
+ 1000L)
+
+ val pausedRemote = Triple("paused remote",
+ DATA.copy(active = true, isPlaying = false, isLocalSession = false, resumption = false),
+ 2000L)
+
+ val resume1 = Triple("resume 1",
+ DATA.copy(active = false, isPlaying = false, isLocalSession = true, resumption = true),
+ 500L)
+
+ val resume2 = Triple("resume 2",
+ DATA.copy(active = false, isPlaying = false, isLocalSession = true, resumption = true),
+ 1000L)
+
+ // Expected ordering for media players:
+ // Actively playing local sessions
+ // Actively playing remote sessions
+ // Paused sessions, by last active
+ // Resume controls, by last active
+
+ val expected = listOf(playingLocal, playingRemote, pausedRemote, pausedLocal, resume2,
+ resume1)
+
+ expected.forEach {
+ clock.setCurrentTimeMillis(it.third)
+ MediaPlayerData.addMediaPlayer(it.first, it.second.copy(notificationKey = it.first),
+ panel, clock)
+ }
+
+ for ((index, key) in MediaPlayerData.playerKeys().withIndex()) {
+ assertEquals(expected.get(index).first, key.data.notificationKey)
+ }
+ }
+
+ @Test
+ fun testOrderWithSmartspace_prioritized() {
+ testPlayerOrdering()
+
+ // If smartspace is prioritized
+ MediaPlayerData.addMediaRecommendation(SMARTSPACE_KEY, EMPTY_SMARTSPACE_MEDIA_DATA, panel,
+ true, clock)
+
+ // Then it should be shown immediately after any actively playing controls
+ assertTrue(MediaPlayerData.playerKeys().elementAt(2).isSsMediaRec)
+ }
+
+ @Test
+ fun testOrderWithSmartspace_notPrioritized() {
+ testPlayerOrdering()
+
+ // If smartspace is not prioritized
+ MediaPlayerData.addMediaRecommendation(SMARTSPACE_KEY, EMPTY_SMARTSPACE_MEDIA_DATA, panel,
+ false, clock)
+
+ // Then it should be shown at the end of the carousel
+ val size = MediaPlayerData.playerKeys().size
+ assertTrue(MediaPlayerData.playerKeys().elementAt(size - 1).isSsMediaRec)
+ }
+}
\ No newline at end of file
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/MediaControlPanelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/MediaControlPanelTest.kt
index 42629f5..bf5a6e4 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/MediaControlPanelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/MediaControlPanelTest.kt
@@ -41,6 +41,7 @@
import com.android.systemui.SysuiTestCase
import com.android.systemui.media.dialog.MediaOutputDialogFactory
import com.android.systemui.plugins.ActivityStarter
+import com.android.systemui.plugins.FalsingManager
import com.android.systemui.statusbar.phone.KeyguardDismissUtil
import com.android.systemui.util.animation.TransitionLayout
import com.android.systemui.util.concurrency.FakeExecutor
@@ -95,6 +96,7 @@
@Mock private lateinit var collapsedSet: ConstraintSet
@Mock private lateinit var mediaOutputDialogFactory: MediaOutputDialogFactory
@Mock private lateinit var mediaCarouselController: MediaCarouselController
+ @Mock private lateinit var falsingManager: FalsingManager
private lateinit var appIcon: ImageView
private lateinit var albumView: ImageView
private lateinit var titleText: TextView
@@ -131,8 +133,8 @@
whenever(mediaViewController.collapsedLayout).thenReturn(collapsedSet)
player = MediaControlPanel(context, bgExecutor, activityStarter, mediaViewController,
- seekBarViewModel, Lazy { mediaDataManager }, keyguardDismissUtil,
- mediaOutputDialogFactory, mediaCarouselController)
+ seekBarViewModel, Lazy { mediaDataManager }, keyguardDismissUtil,
+ mediaOutputDialogFactory, mediaCarouselController, falsingManager)
whenever(seekBarViewModel.progress).thenReturn(seekBarData)
// Mock out a view holder for the player to attach to.
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/MediaDataFilterTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/MediaDataFilterTest.kt
index 28aed20..a435e79 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/MediaDataFilterTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/MediaDataFilterTest.kt
@@ -86,6 +86,7 @@
@Before
fun setup() {
MockitoAnnotations.initMocks(this)
+ MediaPlayerData.clear()
mediaDataFilter = MediaDataFilter(context, broadcastDispatcher, mediaResumeListener,
lockscreenUserManager, executor, clock)
mediaDataFilter.mediaDataManager = mediaDataManager
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/MediaDataManagerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/MediaDataManagerTest.kt
index d864aae..2b2fc51 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/MediaDataManagerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/MediaDataManagerTest.kt
@@ -255,6 +255,7 @@
.onMediaDataLoaded(eq(PACKAGE_NAME), eq(KEY), capture(mediaDataCaptor), eq(true),
eq(false))
assertThat(mediaDataCaptor.value.resumption).isTrue()
+ assertThat(mediaDataCaptor.value.isPlaying).isFalse()
}
@Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/MediaPlayerDataTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/MediaPlayerDataTest.kt
index e9e965e..8dc9eff 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/MediaPlayerDataTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/MediaPlayerDataTest.kt
@@ -127,7 +127,7 @@
val players = MediaPlayerData.players()
assertThat(players).hasSize(6)
assertThat(players).containsExactly(playerIsPlaying, playerIsPlayingAndRemote,
- playerIsStoppedAndLocal, playerCanResume, playerIsStoppedAndRemote,
+ playerIsStoppedAndRemote, playerIsStoppedAndLocal, playerCanResume,
playerUndetermined).inOrder()
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/SeekBarObserverTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/SeekBarObserverTest.kt
index 7d8728e..e77802f8 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/SeekBarObserverTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/SeekBarObserverTest.kt
@@ -76,6 +76,7 @@
assertThat(seekBarView.getThumb().getAlpha()).isEqualTo(0)
assertThat(elapsedTimeView.getText()).isEqualTo("")
assertThat(totalTimeView.getText()).isEqualTo("")
+ assertThat(seekBarView.contentDescription).isEqualTo("")
assertThat(seekBarView.maxHeight).isEqualTo(disabledHeight)
}
@@ -102,6 +103,9 @@
assertThat(seekBarView.max).isEqualTo(120000)
assertThat(elapsedTimeView.getText()).isEqualTo("00:03")
assertThat(totalTimeView.getText()).isEqualTo("02:00")
+
+ val desc = context.getString(R.string.controls_media_seekbar_description, "00:03", "02:00")
+ assertThat(seekBarView.contentDescription).isEqualTo(desc)
}
@Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputAdapterTest.java b/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputAdapterTest.java
index 25ca8c9..750600ad 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputAdapterTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputAdapterTest.java
@@ -119,7 +119,6 @@
assertThat(mViewHolder.mTitleText.getVisibility()).isEqualTo(View.VISIBLE);
assertThat(mViewHolder.mTwoLineLayout.getVisibility()).isEqualTo(View.GONE);
assertThat(mViewHolder.mProgressBar.getVisibility()).isEqualTo(View.GONE);
- assertThat(mViewHolder.mDivider.getVisibility()).isEqualTo(View.GONE);
assertThat(mViewHolder.mAddIcon.getVisibility()).isEqualTo(View.GONE);
assertThat(mViewHolder.mCheckBox.getVisibility()).isEqualTo(View.GONE);
assertThat(mViewHolder.mBottomDivider.getVisibility()).isEqualTo(View.GONE);
@@ -181,7 +180,6 @@
when(mMediaOutputController.getSelectableMediaDevice()).thenReturn(mMediaDevices);
mMediaOutputAdapter.onBindViewHolder(mViewHolder, 0);
- assertThat(mViewHolder.mDivider.getVisibility()).isEqualTo(View.VISIBLE);
assertThat(mViewHolder.mAddIcon.getVisibility()).isEqualTo(View.VISIBLE);
}
@@ -190,7 +188,6 @@
when(mMediaOutputController.getSelectableMediaDevice()).thenReturn(new ArrayList<>());
mMediaOutputAdapter.onBindViewHolder(mViewHolder, 0);
- assertThat(mViewHolder.mDivider.getVisibility()).isEqualTo(View.GONE);
assertThat(mViewHolder.mAddIcon.getVisibility()).isEqualTo(View.GONE);
}
@@ -198,7 +195,6 @@
public void onBindViewHolder_bindNonActiveConnectedDevice_verifyView() {
mMediaOutputAdapter.onBindViewHolder(mViewHolder, 1);
- assertThat(mViewHolder.mDivider.getVisibility()).isEqualTo(View.GONE);
assertThat(mViewHolder.mAddIcon.getVisibility()).isEqualTo(View.GONE);
assertThat(mViewHolder.mTwoLineLayout.getVisibility()).isEqualTo(View.GONE);
assertThat(mViewHolder.mProgressBar.getVisibility()).isEqualTo(View.GONE);
@@ -215,7 +211,6 @@
when(mMediaDevice2.isConnected()).thenReturn(false);
mMediaOutputAdapter.onBindViewHolder(mViewHolder, 1);
- assertThat(mViewHolder.mDivider.getVisibility()).isEqualTo(View.GONE);
assertThat(mViewHolder.mAddIcon.getVisibility()).isEqualTo(View.GONE);
assertThat(mViewHolder.mTwoLineLayout.getVisibility()).isEqualTo(View.GONE);
assertThat(mViewHolder.mProgressBar.getVisibility()).isEqualTo(View.GONE);
@@ -231,7 +226,6 @@
LocalMediaManager.MediaDeviceState.STATE_CONNECTING_FAILED);
mMediaOutputAdapter.onBindViewHolder(mViewHolder, 1);
- assertThat(mViewHolder.mDivider.getVisibility()).isEqualTo(View.GONE);
assertThat(mViewHolder.mAddIcon.getVisibility()).isEqualTo(View.GONE);
assertThat(mViewHolder.mTitleText.getVisibility()).isEqualTo(View.GONE);
assertThat(mViewHolder.mSeekBar.getVisibility()).isEqualTo(View.GONE);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputGroupAdapterTest.java b/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputGroupAdapterTest.java
index 1f85112..ca5d570 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputGroupAdapterTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputGroupAdapterTest.java
@@ -99,7 +99,6 @@
assertThat(mGroupViewHolder.mCheckBox.getVisibility()).isEqualTo(View.GONE);
assertThat(mGroupViewHolder.mSubTitleText.getVisibility()).isEqualTo(View.GONE);
assertThat(mGroupViewHolder.mProgressBar.getVisibility()).isEqualTo(View.GONE);
- assertThat(mGroupViewHolder.mDivider.getVisibility()).isEqualTo(View.GONE);
assertThat(mGroupViewHolder.mAddIcon.getVisibility()).isEqualTo(View.GONE);
assertThat(mGroupViewHolder.mBottomDivider.getVisibility()).isEqualTo(View.VISIBLE);
assertThat(mGroupViewHolder.mSeekBar.getVisibility()).isEqualTo(View.VISIBLE);
@@ -114,7 +113,6 @@
assertThat(mGroupViewHolder.mTitleText.getVisibility()).isEqualTo(View.GONE);
assertThat(mGroupViewHolder.mProgressBar.getVisibility()).isEqualTo(View.GONE);
- assertThat(mGroupViewHolder.mDivider.getVisibility()).isEqualTo(View.GONE);
assertThat(mGroupViewHolder.mAddIcon.getVisibility()).isEqualTo(View.GONE);
assertThat(mGroupViewHolder.mBottomDivider.getVisibility()).isEqualTo(View.GONE);
assertThat(mGroupViewHolder.mSubTitleText.getVisibility()).isEqualTo(View.GONE);
@@ -141,7 +139,6 @@
assertThat(mGroupViewHolder.mTitleText.getVisibility()).isEqualTo(View.GONE);
assertThat(mGroupViewHolder.mProgressBar.getVisibility()).isEqualTo(View.GONE);
- assertThat(mGroupViewHolder.mDivider.getVisibility()).isEqualTo(View.GONE);
assertThat(mGroupViewHolder.mAddIcon.getVisibility()).isEqualTo(View.GONE);
assertThat(mGroupViewHolder.mBottomDivider.getVisibility()).isEqualTo(View.GONE);
assertThat(mGroupViewHolder.mSubTitleText.getVisibility()).isEqualTo(View.GONE);
@@ -167,7 +164,6 @@
assertThat(mGroupViewHolder.mTitleText.getVisibility()).isEqualTo(View.GONE);
assertThat(mGroupViewHolder.mProgressBar.getVisibility()).isEqualTo(View.GONE);
- assertThat(mGroupViewHolder.mDivider.getVisibility()).isEqualTo(View.GONE);
assertThat(mGroupViewHolder.mAddIcon.getVisibility()).isEqualTo(View.GONE);
assertThat(mGroupViewHolder.mBottomDivider.getVisibility()).isEqualTo(View.GONE);
assertThat(mGroupViewHolder.mSubTitleText.getVisibility()).isEqualTo(View.GONE);
@@ -186,7 +182,6 @@
assertThat(mGroupViewHolder.mTitleText.getVisibility()).isEqualTo(View.GONE);
assertThat(mGroupViewHolder.mProgressBar.getVisibility()).isEqualTo(View.GONE);
- assertThat(mGroupViewHolder.mDivider.getVisibility()).isEqualTo(View.GONE);
assertThat(mGroupViewHolder.mAddIcon.getVisibility()).isEqualTo(View.GONE);
assertThat(mGroupViewHolder.mBottomDivider.getVisibility()).isEqualTo(View.GONE);
assertThat(mGroupViewHolder.mSubTitleText.getVisibility()).isEqualTo(View.GONE);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/user/UserDialogTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/user/UserDialogTest.kt
new file mode 100644
index 0000000..d5fe588
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/user/UserDialogTest.kt
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.qs.user
+
+import android.testing.AndroidTestingRunner
+import android.testing.TestableLooper
+import android.view.View
+import android.view.ViewGroup
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.google.common.truth.Truth.assertThat
+import org.junit.After
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@SmallTest
+@RunWith(AndroidTestingRunner::class)
+@TestableLooper.RunWithLooper
+class UserDialogTest : SysuiTestCase() {
+
+ private lateinit var dialog: UserDialog
+
+ @Before
+ fun setUp() {
+ dialog = UserDialog(mContext)
+ }
+
+ @After
+ fun tearDown() {
+ dialog.dismiss()
+ }
+
+ @Test
+ fun doneButtonExists() {
+ assertThat(dialog.doneButton).isInstanceOf(View::class.java)
+ }
+
+ @Test
+ fun settingsButtonExists() {
+ assertThat(dialog.settingsButton).isInstanceOf(View::class.java)
+ }
+
+ @Test
+ fun gridExistsAndIsViewGroup() {
+ assertThat(dialog.grid).isInstanceOf(ViewGroup::class.java)
+ }
+}
\ No newline at end of file
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/user/UserSwitchDialogControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/user/UserSwitchDialogControllerTest.kt
new file mode 100644
index 0000000..a1760a7
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/user/UserSwitchDialogControllerTest.kt
@@ -0,0 +1,203 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.qs.user
+
+import android.content.Intent
+import android.provider.Settings
+import android.testing.AndroidTestingRunner
+import android.view.View
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.plugins.ActivityStarter
+import com.android.systemui.plugins.FalsingManager
+import com.android.systemui.qs.PseudoGridView
+import com.android.systemui.qs.tiles.UserDetailView
+import com.android.systemui.statusbar.policy.UserSwitcherController
+import com.android.systemui.util.mockito.argumentCaptor
+import com.android.systemui.util.mockito.capture
+import com.android.systemui.util.mockito.eq
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.ArgumentCaptor
+import org.mockito.ArgumentMatcher
+import org.mockito.Captor
+import org.mockito.Mock
+import org.mockito.Mockito.`when`
+import org.mockito.Mockito.any
+import org.mockito.Mockito.anyInt
+import org.mockito.Mockito.argThat
+import org.mockito.Mockito.inOrder
+import org.mockito.Mockito.mock
+import org.mockito.Mockito.never
+import org.mockito.Mockito.verify
+import org.mockito.MockitoAnnotations
+import java.util.function.Consumer
+
+@SmallTest
+@RunWith(AndroidTestingRunner::class)
+class UserSwitchDialogControllerTest : SysuiTestCase() {
+
+ @Mock
+ private lateinit var dialog: UserDialog
+ @Mock
+ private lateinit var falsingManager: FalsingManager
+ @Mock
+ private lateinit var settingsView: View
+ @Mock
+ private lateinit var doneView: View
+ @Mock
+ private lateinit var activityStarter: ActivityStarter
+ @Mock
+ private lateinit var userDetailViewAdapter: UserDetailView.Adapter
+ @Mock
+ private lateinit var launchView: View
+ @Mock
+ private lateinit var gridView: PseudoGridView
+ @Captor
+ private lateinit var clickCaptor: ArgumentCaptor<View.OnClickListener>
+
+ private lateinit var controller: UserSwitchDialogController
+
+ @Before
+ fun setUp() {
+ MockitoAnnotations.initMocks(this)
+
+ `when`(dialog.settingsButton).thenReturn(settingsView)
+ `when`(dialog.doneButton).thenReturn(doneView)
+ `when`(dialog.grid).thenReturn(gridView)
+
+ `when`(launchView.context).thenReturn(mContext)
+
+ controller = UserSwitchDialogController(
+ { userDetailViewAdapter },
+ activityStarter,
+ falsingManager,
+ { dialog }
+ )
+ }
+
+ @Test
+ fun showDialog_callsDialogShow() {
+ controller.showDialog(launchView)
+ verify(dialog).show()
+ }
+
+ @Test
+ fun createCalledBeforeDoneButton() {
+ controller.showDialog(launchView)
+ val inOrder = inOrder(dialog)
+ inOrder.verify(dialog).create()
+ inOrder.verify(dialog).doneButton
+ }
+
+ @Test
+ fun createCalledBeforeSettingsButton() {
+ controller.showDialog(launchView)
+ val inOrder = inOrder(dialog)
+ inOrder.verify(dialog).create()
+ inOrder.verify(dialog).settingsButton
+ }
+
+ @Test
+ fun createCalledBeforeGrid() {
+ controller.showDialog(launchView)
+ val inOrder = inOrder(dialog)
+ inOrder.verify(dialog).create()
+ inOrder.verify(dialog).grid
+ }
+
+ @Test
+ fun dialog_showForAllUsers() {
+ controller.showDialog(launchView)
+ verify(dialog).setShowForAllUsers(true)
+ }
+
+ @Test
+ fun dialog_cancelOnTouchOutside() {
+ controller.showDialog(launchView)
+ verify(dialog).setCanceledOnTouchOutside(true)
+ }
+
+ @Test
+ fun adapterAndGridLinked() {
+ controller.showDialog(launchView)
+ verify(userDetailViewAdapter).linkToViewGroup(gridView)
+ }
+
+ @Test
+ fun clickDoneButton_dismiss() {
+ controller.showDialog(launchView)
+
+ verify(doneView).setOnClickListener(capture(clickCaptor))
+
+ clickCaptor.value.onClick(doneView)
+
+ verify(activityStarter, never()).postStartActivityDismissingKeyguard(any(), anyInt())
+ verify(dialog).dismiss()
+ }
+
+ @Test
+ fun clickSettingsButton_noFalsing_opensSettingsAndDismisses() {
+ `when`(falsingManager.isFalseTap(anyInt())).thenReturn(false)
+
+ controller.showDialog(launchView)
+
+ verify(settingsView).setOnClickListener(capture(clickCaptor))
+
+ clickCaptor.value.onClick(settingsView)
+
+ verify(activityStarter)
+ .postStartActivityDismissingKeyguard(
+ argThat(IntentMatcher(Settings.ACTION_USER_SETTINGS)),
+ eq(0)
+ )
+ verify(dialog).dismiss()
+ }
+
+ @Test
+ fun clickSettingsButton_Falsing_notOpensSettingsAndDismisses() {
+ `when`(falsingManager.isFalseTap(anyInt())).thenReturn(true)
+
+ controller.showDialog(launchView)
+
+ verify(settingsView).setOnClickListener(capture(clickCaptor))
+
+ clickCaptor.value.onClick(settingsView)
+
+ verify(activityStarter, never()).postStartActivityDismissingKeyguard(any(), anyInt())
+ verify(dialog).dismiss()
+ }
+
+ @Test
+ fun callbackFromDetailView_dismissesDialog() {
+ val captor = argumentCaptor<Consumer<UserSwitcherController.UserRecord>>()
+
+ controller.showDialog(launchView)
+ verify(userDetailViewAdapter).injectCallback(capture(captor))
+
+ captor.value.accept(mock(UserSwitcherController.UserRecord::class.java))
+
+ verify(dialog).dismiss()
+ }
+
+ private class IntentMatcher(private val action: String) : ArgumentMatcher<Intent> {
+ override fun matches(argument: Intent?): Boolean {
+ return argument?.action == action
+ }
+ }
+}
\ No newline at end of file
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/LockscreenShadeTransitionControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/LockscreenShadeTransitionControllerTest.kt
index c50296b..7938511 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/LockscreenShadeTransitionControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/LockscreenShadeTransitionControllerTest.kt
@@ -209,7 +209,7 @@
verify(scrimController, never()).setTransitionToFullShadeProgress(anyFloat())
verify(notificationPanelController, never()).setTransitionToFullShadeAmount(anyFloat(),
anyBoolean(), anyLong())
- verify(qS, never()).setTransitionToFullShadeAmount(anyFloat(), anyBoolean())
+ verify(qS, never()).setTransitionToFullShadeAmount(anyFloat(), anyFloat())
}
@Test
@@ -220,7 +220,7 @@
verify(scrimController).setTransitionToFullShadeProgress(anyFloat())
verify(notificationPanelController).setTransitionToFullShadeAmount(anyFloat(),
anyBoolean(), anyLong())
- verify(qS).setTransitionToFullShadeAmount(anyFloat(), anyBoolean())
+ verify(qS).setTransitionToFullShadeAmount(anyFloat(), anyFloat())
verify(depthController).transitionToFullShadeProgress = anyFloat()
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationPanelViewControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationPanelViewControllerTest.java
index f7423bb..b18ea4b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationPanelViewControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationPanelViewControllerTest.java
@@ -300,6 +300,8 @@
private LockscreenGestureLogger mLockscreenGestureLogger;
@Mock
private DumpManager mDumpManager;
+ @Mock
+ private NotificationsQSContainerController mNotificationsQSContainerController;
private SysuiStatusBarStateController mStatusBarStateController;
private NotificationPanelViewController mNotificationPanelViewController;
@@ -414,6 +416,7 @@
() -> flingAnimationUtilsBuilder, mStatusBarTouchableRegionManager,
mConversationNotificationManager, mMediaHiearchyManager,
mStatusBarKeyguardViewManager,
+ mNotificationsQSContainerController,
mNotificationStackScrollLayoutController,
mKeyguardStatusViewComponentFactory,
mKeyguardQsUserSwitchComponentFactory,
@@ -469,8 +472,8 @@
}
@Test
- public void testSetMinFraction() {
- mNotificationPanelViewController.setMinFraction(0.5f);
+ public void testSetPanelScrimMinFraction() {
+ mNotificationPanelViewController.setPanelScrimMinFraction(0.5f);
verify(mNotificationShadeDepthController).setPanelPullDownMinFraction(eq(0.5f));
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationQSContainerControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationQSContainerControllerTest.kt
new file mode 100644
index 0000000..337e64592
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationQSContainerControllerTest.kt
@@ -0,0 +1,254 @@
+package com.android.systemui.statusbar.phone
+
+import android.testing.AndroidTestingRunner
+import android.testing.TestableLooper
+import android.view.WindowInsets
+import android.view.WindowManagerPolicyConstants
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.navigationbar.NavigationModeController
+import com.android.systemui.navigationbar.NavigationModeController.ModeChangedListener
+import com.android.systemui.recents.OverviewProxyService
+import com.android.systemui.recents.OverviewProxyService.OverviewProxyListener
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.ArgumentCaptor
+import org.mockito.Captor
+import org.mockito.Mock
+import org.mockito.Mockito
+import org.mockito.Mockito.RETURNS_DEEP_STUBS
+import org.mockito.Mockito.anyInt
+import org.mockito.Mockito.doNothing
+import org.mockito.Mockito.eq
+import org.mockito.Mockito.mock
+import org.mockito.Mockito.verify
+import org.mockito.MockitoAnnotations
+import java.util.function.Consumer
+import org.mockito.Mockito.`when` as whenever
+
+@SmallTest
+@RunWith(AndroidTestingRunner::class)
+@TestableLooper.RunWithLooper
+class NotificationQSContainerControllerTest : SysuiTestCase() {
+
+ companion object {
+ const val STABLE_INSET_BOTTOM = 100
+ const val CUTOUT_HEIGHT = 50
+ const val GESTURES_NAVIGATION = WindowManagerPolicyConstants.NAV_BAR_MODE_GESTURAL
+ const val BUTTONS_NAVIGATION = WindowManagerPolicyConstants.NAV_BAR_MODE_3BUTTON
+ const val NOTIFICATIONS_MARGIN = 50
+ }
+
+ @Mock
+ private lateinit var navigationModeController: NavigationModeController
+ @Mock
+ private lateinit var overviewProxyService: OverviewProxyService
+ @Mock
+ private lateinit var notificationsQSContainer: NotificationsQuickSettingsContainer
+ @Captor
+ lateinit var navigationModeCaptor: ArgumentCaptor<ModeChangedListener>
+ @Captor
+ lateinit var taskbarVisibilityCaptor: ArgumentCaptor<OverviewProxyListener>
+ @Captor
+ lateinit var windowInsetsCallbackCaptor: ArgumentCaptor<Consumer<WindowInsets>>
+
+ private lateinit var notificationsQSContainerController: NotificationsQSContainerController
+ private lateinit var navigationModeCallback: ModeChangedListener
+ private lateinit var taskbarVisibilityCallback: OverviewProxyListener
+ private lateinit var windowInsetsCallback: Consumer<WindowInsets>
+
+ @Before
+ fun setup() {
+ MockitoAnnotations.initMocks(this)
+ notificationsQSContainerController = NotificationsQSContainerController(
+ notificationsQSContainer,
+ navigationModeController,
+ overviewProxyService
+ )
+ whenever(notificationsQSContainer.defaultNotificationsMarginBottom)
+ .thenReturn(NOTIFICATIONS_MARGIN)
+ whenever(navigationModeController.addListener(navigationModeCaptor.capture()))
+ .thenReturn(GESTURES_NAVIGATION)
+ doNothing().`when`(overviewProxyService).addCallback(taskbarVisibilityCaptor.capture())
+ doNothing().`when`(notificationsQSContainer)
+ .setInsetsChangedListener(windowInsetsCallbackCaptor.capture())
+
+ notificationsQSContainerController.init()
+ notificationsQSContainerController.onViewAttached()
+
+ navigationModeCallback = navigationModeCaptor.value
+ taskbarVisibilityCallback = taskbarVisibilityCaptor.value
+ windowInsetsCallback = windowInsetsCallbackCaptor.value
+ }
+
+ @Test
+ fun testTaskbarVisibleInSplitShade() {
+ notificationsQSContainerController.splitShadeEnabled = true
+ given(taskbarVisible = true,
+ navigationMode = GESTURES_NAVIGATION,
+ insets = windowInsets().withStableBottom())
+ then(expectedContainerPadding = 0, // taskbar should disappear when shade is expanded
+ expectedNotificationsMargin = NOTIFICATIONS_MARGIN)
+
+ given(taskbarVisible = true,
+ navigationMode = BUTTONS_NAVIGATION,
+ insets = windowInsets().withStableBottom())
+ then(expectedContainerPadding = STABLE_INSET_BOTTOM,
+ expectedNotificationsMargin = NOTIFICATIONS_MARGIN)
+ }
+
+ @Test
+ fun testTaskbarNotVisibleInSplitShade() {
+ // when taskbar is not visible, it means we're on the home screen
+ notificationsQSContainerController.splitShadeEnabled = true
+ given(taskbarVisible = false,
+ navigationMode = GESTURES_NAVIGATION,
+ insets = windowInsets().withStableBottom())
+ then(expectedContainerPadding = 0)
+
+ given(taskbarVisible = false,
+ navigationMode = BUTTONS_NAVIGATION,
+ insets = windowInsets().withStableBottom())
+ then(expectedContainerPadding = 0, // qs goes full height as it's not obscuring nav buttons
+ expectedNotificationsMargin = STABLE_INSET_BOTTOM + NOTIFICATIONS_MARGIN)
+ }
+
+ @Test
+ fun testTaskbarNotVisibleInSplitShadeWithCutout() {
+ notificationsQSContainerController.splitShadeEnabled = true
+ given(taskbarVisible = false,
+ navigationMode = GESTURES_NAVIGATION,
+ insets = windowInsets().withCutout())
+ then(expectedContainerPadding = CUTOUT_HEIGHT)
+
+ given(taskbarVisible = false,
+ navigationMode = BUTTONS_NAVIGATION,
+ insets = windowInsets().withCutout().withStableBottom())
+ then(expectedContainerPadding = 0,
+ expectedNotificationsMargin = STABLE_INSET_BOTTOM + NOTIFICATIONS_MARGIN)
+ }
+
+ @Test
+ fun testTaskbarVisibleInSinglePaneShade() {
+ notificationsQSContainerController.splitShadeEnabled = false
+ given(taskbarVisible = true,
+ navigationMode = GESTURES_NAVIGATION,
+ insets = windowInsets().withStableBottom())
+ then(expectedContainerPadding = 0)
+
+ given(taskbarVisible = true,
+ navigationMode = BUTTONS_NAVIGATION,
+ insets = windowInsets().withStableBottom())
+ then(expectedContainerPadding = STABLE_INSET_BOTTOM)
+ }
+
+ @Test
+ fun testTaskbarNotVisibleInSinglePaneShade() {
+ notificationsQSContainerController.splitShadeEnabled = false
+ given(taskbarVisible = false,
+ navigationMode = GESTURES_NAVIGATION,
+ insets = emptyInsets())
+ then(expectedContainerPadding = 0)
+
+ given(taskbarVisible = false,
+ navigationMode = GESTURES_NAVIGATION,
+ insets = windowInsets().withCutout().withStableBottom())
+ then(expectedContainerPadding = CUTOUT_HEIGHT)
+
+ given(taskbarVisible = false,
+ navigationMode = BUTTONS_NAVIGATION,
+ insets = windowInsets().withStableBottom())
+ then(expectedContainerPadding = 0,
+ expectedQsPadding = STABLE_INSET_BOTTOM)
+ }
+
+ @Test
+ fun testCustomizingInSinglePaneShade() {
+ notificationsQSContainerController.splitShadeEnabled = false
+ notificationsQSContainerController.setCustomizerShowing(true)
+ // always sets spacings to 0
+ given(taskbarVisible = false,
+ navigationMode = GESTURES_NAVIGATION,
+ insets = windowInsets().withStableBottom())
+ then(expectedContainerPadding = 0,
+ expectedNotificationsMargin = 0)
+
+ given(taskbarVisible = false,
+ navigationMode = BUTTONS_NAVIGATION,
+ insets = emptyInsets())
+ then(expectedContainerPadding = 0,
+ expectedNotificationsMargin = 0)
+ }
+
+ @Test
+ fun testDetailShowingInSinglePaneShade() {
+ notificationsQSContainerController.splitShadeEnabled = false
+ notificationsQSContainerController.setDetailShowing(true)
+ // always sets spacings to 0
+ given(taskbarVisible = false,
+ navigationMode = GESTURES_NAVIGATION,
+ insets = windowInsets().withStableBottom())
+ then(expectedContainerPadding = 0,
+ expectedNotificationsMargin = 0)
+
+ given(taskbarVisible = false,
+ navigationMode = BUTTONS_NAVIGATION,
+ insets = emptyInsets())
+ then(expectedContainerPadding = 0,
+ expectedNotificationsMargin = 0)
+ }
+
+ @Test
+ fun testDetailShowingInSplitShade() {
+ notificationsQSContainerController.splitShadeEnabled = true
+ given(taskbarVisible = false,
+ navigationMode = GESTURES_NAVIGATION,
+ insets = windowInsets().withStableBottom())
+ then(expectedContainerPadding = 0)
+
+ notificationsQSContainerController.setDetailShowing(true)
+ // should not influence spacing
+ given(taskbarVisible = false,
+ navigationMode = BUTTONS_NAVIGATION,
+ insets = emptyInsets())
+ then(expectedContainerPadding = 0)
+ }
+
+ private fun given(
+ taskbarVisible: Boolean,
+ navigationMode: Int,
+ insets: WindowInsets
+ ) {
+ Mockito.clearInvocations(notificationsQSContainer)
+ taskbarVisibilityCallback.onTaskbarStatusUpdated(taskbarVisible, false)
+ navigationModeCallback.onNavigationModeChanged(navigationMode)
+ windowInsetsCallback.accept(insets)
+ }
+
+ fun then(
+ expectedContainerPadding: Int,
+ expectedNotificationsMargin: Int = NOTIFICATIONS_MARGIN,
+ expectedQsPadding: Int = 0
+ ) {
+ verify(notificationsQSContainer)
+ .setPadding(anyInt(), anyInt(), anyInt(), eq(expectedContainerPadding))
+ verify(notificationsQSContainer).setNotificationsMarginBottom(expectedNotificationsMargin)
+ verify(notificationsQSContainer).setQSScrollPaddingBottom(expectedQsPadding)
+ Mockito.clearInvocations(notificationsQSContainer)
+ }
+
+ private fun windowInsets() = mock(WindowInsets::class.java, RETURNS_DEEP_STUBS)
+
+ private fun emptyInsets() = mock(WindowInsets::class.java)
+
+ private fun WindowInsets.withCutout(): WindowInsets {
+ whenever(displayCutout.safeInsetBottom).thenReturn(CUTOUT_HEIGHT)
+ return this
+ }
+
+ private fun WindowInsets.withStableBottom(): WindowInsets {
+ whenever(stableInsetBottom).thenReturn(STABLE_INSET_BOTTOM)
+ return this
+ }
+}
\ No newline at end of file
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewTest.kt
index aee9f12..ec7e07f90 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewTest.kt
@@ -35,6 +35,8 @@
private lateinit var panelView: ViewGroup
@Mock
private lateinit var scrimController: ScrimController
+ @Mock
+ private lateinit var statusBar: StatusBar
private lateinit var view: PhoneStatusBarView
@@ -48,6 +50,7 @@
view = PhoneStatusBarView(mContext, null)
view.setPanel(panelViewController)
view.setScrimController(scrimController)
+ view.setBar(statusBar)
}
@Test
@@ -72,7 +75,7 @@
@Test
fun panelExpansionChanged_fracZero_stateChangeListenerNotified() {
- val listener = TestStateChangedListener()
+ val listener = TestExpansionStateChangedListener()
view.setPanelExpansionStateChangedListener(listener)
view.panelExpansionChanged(0f, false)
@@ -82,7 +85,7 @@
@Test
fun panelExpansionChanged_fracOne_stateChangeListenerNotified() {
- val listener = TestStateChangedListener()
+ val listener = TestExpansionStateChangedListener()
view.setPanelExpansionStateChangedListener(listener)
view.panelExpansionChanged(1f, false)
@@ -92,7 +95,7 @@
@Test
fun panelExpansionChanged_fracHalf_stateChangeListenerNotNotified() {
- val listener = TestStateChangedListener()
+ val listener = TestExpansionStateChangedListener()
view.setPanelExpansionStateChangedListener(listener)
view.panelExpansionChanged(0.5f, false)
@@ -106,11 +109,59 @@
// No assert needed, just testing no crash
}
- private class TestStateChangedListener : PhoneStatusBarView.PanelExpansionStateChangedListener {
+ @Test
+ fun panelStateChanged_toStateOpening_listenerNotified() {
+ val listener = TestStateChangedListener()
+ view.setPanelStateChangeListener(listener)
+
+ view.panelExpansionChanged(0.5f, true)
+
+ assertThat(listener.state).isEqualTo(PanelBar.STATE_OPENING)
+ }
+
+ @Test
+ fun panelStateChanged_toStateOpen_listenerNotified() {
+ val listener = TestStateChangedListener()
+ view.setPanelStateChangeListener(listener)
+
+ view.panelExpansionChanged(1f, true)
+
+ assertThat(listener.state).isEqualTo(PanelBar.STATE_OPEN)
+ }
+
+ @Test
+ fun panelStateChanged_toStateClosed_listenerNotified() {
+ val listener = TestStateChangedListener()
+ view.setPanelStateChangeListener(listener)
+
+ // First, open the panel
+ view.panelExpansionChanged(1f, true)
+
+ // Then, close it again
+ view.panelExpansionChanged(0f, false)
+
+ assertThat(listener.state).isEqualTo(PanelBar.STATE_CLOSED)
+ }
+
+ @Test
+ fun panelStateChanged_noListener_noCrash() {
+ view.panelExpansionChanged(1f, true)
+ // No assert needed, just testing no crash
+ }
+
+ private class TestExpansionStateChangedListener
+ : PhoneStatusBarView.PanelExpansionStateChangedListener {
var stateChangeCalled: Boolean = false
override fun onPanelExpansionStateChanged() {
stateChangeCalled = true
}
}
+
+ private class TestStateChangedListener : PanelBar.PanelStateChangeListener {
+ var state: Int = 0
+ override fun onStateChanged(state: Int) {
+ this.state = state
+ }
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java
index 5ebe900..705112a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java
@@ -623,7 +623,7 @@
@Test
public void transitionToUnlocked() {
- mScrimController.setPanelExpansion(0f);
+ mScrimController.setRawPanelExpansionFraction(0f);
mScrimController.transitionTo(ScrimState.UNLOCKED);
finishAnimationsImmediately();
assertScrimAlpha(Map.of(
@@ -638,7 +638,7 @@
));
// Back scrim should be visible after start dragging
- mScrimController.setPanelExpansion(0.3f);
+ mScrimController.setRawPanelExpansionFraction(0.3f);
assertScrimAlpha(Map.of(
mScrimInFront, TRANSPARENT,
mNotificationsScrim, SEMI_TRANSPARENT,
@@ -663,20 +663,20 @@
@Test
public void panelExpansion() {
- mScrimController.setPanelExpansion(0f);
- mScrimController.setPanelExpansion(0.5f);
+ mScrimController.setRawPanelExpansionFraction(0f);
+ mScrimController.setRawPanelExpansionFraction(0.5f);
mScrimController.transitionTo(ScrimState.UNLOCKED);
finishAnimationsImmediately();
reset(mScrimBehind);
- mScrimController.setPanelExpansion(0f);
- mScrimController.setPanelExpansion(1.0f);
+ mScrimController.setRawPanelExpansionFraction(0f);
+ mScrimController.setRawPanelExpansionFraction(1.0f);
finishAnimationsImmediately();
assertEquals("Scrim alpha should change after setPanelExpansion",
mScrimBehindAlpha, mScrimBehind.getViewAlpha(), 0.01f);
- mScrimController.setPanelExpansion(0f);
+ mScrimController.setRawPanelExpansionFraction(0f);
finishAnimationsImmediately();
assertEquals("Scrim alpha should change after setPanelExpansion",
@@ -723,21 +723,21 @@
@Test
public void panelExpansionAffectsAlpha() {
- mScrimController.setPanelExpansion(0f);
- mScrimController.setPanelExpansion(0.5f);
+ mScrimController.setRawPanelExpansionFraction(0f);
+ mScrimController.setRawPanelExpansionFraction(0.5f);
mScrimController.transitionTo(ScrimState.UNLOCKED);
finishAnimationsImmediately();
final float scrimAlpha = mScrimBehind.getViewAlpha();
reset(mScrimBehind);
mScrimController.setExpansionAffectsAlpha(false);
- mScrimController.setPanelExpansion(0.8f);
+ mScrimController.setRawPanelExpansionFraction(0.8f);
verifyZeroInteractions(mScrimBehind);
assertEquals("Scrim opacity shouldn't change when setExpansionAffectsAlpha "
+ "is false", scrimAlpha, mScrimBehind.getViewAlpha(), 0.01f);
mScrimController.setExpansionAffectsAlpha(true);
- mScrimController.setPanelExpansion(0.1f);
+ mScrimController.setRawPanelExpansionFraction(0.1f);
finishAnimationsImmediately();
Assert.assertNotEquals("Scrim opacity should change when setExpansionAffectsAlpha "
+ "is true", scrimAlpha, mScrimBehind.getViewAlpha(), 0.01f);
@@ -747,7 +747,7 @@
public void transitionToUnlockedFromOff() {
// Simulate unlock with fingerprint without AOD
mScrimController.transitionTo(ScrimState.OFF);
- mScrimController.setPanelExpansion(0f);
+ mScrimController.setRawPanelExpansionFraction(0f);
finishAnimationsImmediately();
mScrimController.transitionTo(ScrimState.UNLOCKED);
@@ -769,7 +769,7 @@
public void transitionToUnlockedFromAod() {
// Simulate unlock with fingerprint
mScrimController.transitionTo(ScrimState.AOD);
- mScrimController.setPanelExpansion(0f);
+ mScrimController.setRawPanelExpansionFraction(0f);
finishAnimationsImmediately();
mScrimController.transitionTo(ScrimState.UNLOCKED);
@@ -948,7 +948,7 @@
@Test
public void testConservesExpansionOpacityAfterTransition() {
mScrimController.transitionTo(ScrimState.UNLOCKED);
- mScrimController.setPanelExpansion(0.5f);
+ mScrimController.setRawPanelExpansionFraction(0.5f);
finishAnimationsImmediately();
final float expandedAlpha = mScrimBehind.getViewAlpha();
@@ -1075,7 +1075,7 @@
@Test
public void testScrimsOpaque_whenShadeFullyExpanded() {
mScrimController.transitionTo(ScrimState.UNLOCKED);
- mScrimController.setPanelExpansion(1);
+ mScrimController.setRawPanelExpansionFraction(1);
// notifications scrim alpha change require calling setQsPosition
mScrimController.setQsPosition(0, 300);
finishAnimationsImmediately();
@@ -1089,7 +1089,7 @@
@Test
public void testScrimsVisible_whenShadeVisible() {
mScrimController.transitionTo(ScrimState.UNLOCKED);
- mScrimController.setPanelExpansion(0.3f);
+ mScrimController.setRawPanelExpansionFraction(0.3f);
// notifications scrim alpha change require calling setQsPosition
mScrimController.setQsPosition(0, 300);
finishAnimationsImmediately();
@@ -1124,7 +1124,7 @@
public void testScrimsVisible_whenShadeVisible_clippingQs() {
mScrimController.setClipsQsScrim(true);
mScrimController.transitionTo(ScrimState.UNLOCKED);
- mScrimController.setPanelExpansion(0.3f);
+ mScrimController.setRawPanelExpansionFraction(0.3f);
// notifications scrim alpha change require calling setQsPosition
mScrimController.setQsPosition(0.5f, 300);
finishAnimationsImmediately();
@@ -1150,7 +1150,7 @@
public void testNotificationScrimTransparent_whenOnLockscreen() {
mScrimController.transitionTo(ScrimState.KEYGUARD);
// even if shade is not pulled down, panel has expansion of 1 on the lockscreen
- mScrimController.setPanelExpansion(1);
+ mScrimController.setRawPanelExpansionFraction(1);
mScrimController.setQsPosition(0f, /*qs panel bottom*/ 0);
assertScrimAlpha(Map.of(
@@ -1160,7 +1160,7 @@
@Test
public void testNotificationScrimVisible_afterOpeningShadeFromLockscreen() {
- mScrimController.setPanelExpansion(1);
+ mScrimController.setRawPanelExpansionFraction(1);
mScrimController.transitionTo(ScrimState.SHADE_LOCKED);
finishAnimationsImmediately();
@@ -1203,11 +1203,11 @@
@Test
public void testNotificationTransparency_followsTransitionToFullShade() {
mScrimController.transitionTo(ScrimState.SHADE_LOCKED);
- mScrimController.setPanelExpansion(1.0f);
+ mScrimController.setRawPanelExpansionFraction(1.0f);
finishAnimationsImmediately();
float shadeLockedAlpha = mNotificationsScrim.getViewAlpha();
mScrimController.transitionTo(ScrimState.KEYGUARD);
- mScrimController.setPanelExpansion(1.0f);
+ mScrimController.setRawPanelExpansionFraction(1.0f);
finishAnimationsImmediately();
float keyguardAlpha = mNotificationsScrim.getViewAlpha();
@@ -1227,7 +1227,7 @@
}
private void assertAlphaAfterExpansion(ScrimView scrim, float expectedAlpha, float expansion) {
- mScrimController.setPanelExpansion(expansion);
+ mScrimController.setRawPanelExpansionFraction(expansion);
finishAnimationsImmediately();
// alpha is not changing linearly thus 0.2 of leeway when asserting
assertEquals(expectedAlpha, mNotificationsScrim.getViewAlpha(), 0.2);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java
index 6e3d5ce..3fcd071 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java
@@ -16,15 +16,12 @@
package com.android.systemui.statusbar.phone;
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.anyFloat;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.clearInvocations;
-import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.reset;
@@ -119,7 +116,7 @@
any(ViewGroup.class),
any(KeyguardBouncer.BouncerExpansionCallback.class)))
.thenReturn(mBouncer);
-
+ when(mStatusBar.getBouncerContainer()).thenReturn(mContainer);
when(mContainer.findViewById(anyInt())).thenReturn(mKeyguardMessageArea);
mWakefulnessLifecycle = new WakefulnessLifecycle(
getContext(),
@@ -143,7 +140,7 @@
mUnlockedScreenOffAnimationController,
mKeyguardMessageAreaFactory,
mShadeController);
- mStatusBarKeyguardViewManager.registerStatusBar(mStatusBar, mContainer,
+ mStatusBarKeyguardViewManager.registerStatusBar(mStatusBar,
mNotificationPanelView, mBiometrucUnlockController,
mNotificationContainer, mBypassController);
mStatusBarKeyguardViewManager.show(null);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java
index 6868e18..741d95f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java
@@ -459,7 +459,7 @@
mock(DumpManager.class),
mActivityLaunchAnimator,
mDialogLaunchAnimator);
- when(mKeyguardViewMediator.registerStatusBar(any(StatusBar.class), any(ViewGroup.class),
+ when(mKeyguardViewMediator.registerStatusBar(any(StatusBar.class),
any(NotificationPanelViewController.class), any(BiometricUnlockController.class),
any(ViewGroup.class), any(KeyguardBypassController.class)))
.thenReturn(mStatusBarKeyguardViewManager);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallControllerTest.kt
index fe7ec68..be27876 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallControllerTest.kt
@@ -40,6 +40,7 @@
import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder
import com.android.systemui.statusbar.notification.collection.notifcollection.CommonNotifCollection
import com.android.systemui.statusbar.notification.collection.notifcollection.NotifCollectionListener
+import com.android.systemui.statusbar.phone.StatusBarWindowController
import com.android.systemui.util.concurrency.FakeExecutor
import com.android.systemui.util.mockito.any
import com.android.systemui.util.time.FakeSystemClock
@@ -49,8 +50,7 @@
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.ArgumentCaptor
-import org.mockito.ArgumentMatchers.anyBoolean
-import org.mockito.ArgumentMatchers.nullable
+import org.mockito.ArgumentMatchers.*
import org.mockito.Mock
import org.mockito.Mockito.`when`
import org.mockito.Mockito.eq
@@ -60,6 +60,7 @@
import org.mockito.Mockito.times
import org.mockito.Mockito.verify
import org.mockito.MockitoAnnotations
+import java.util.Optional
private const val CALL_UID = 900
@@ -81,9 +82,11 @@
private lateinit var controller: OngoingCallController
private lateinit var notifCollectionListener: NotifCollectionListener
+ @Mock private lateinit var mockSwipeStatusBarAwayGestureHandler: SwipeStatusBarAwayGestureHandler
@Mock private lateinit var mockOngoingCallListener: OngoingCallListener
@Mock private lateinit var mockActivityStarter: ActivityStarter
@Mock private lateinit var mockIActivityManager: IActivityManager
+ @Mock private lateinit var mockStatusBarWindowController: StatusBarWindowController
private lateinit var chipView: View
@@ -108,6 +111,8 @@
mockIActivityManager,
OngoingCallLogger(uiEventLoggerFake),
DumpManager(),
+ Optional.of(mockStatusBarWindowController),
+ Optional.of(mockSwipeStatusBarAwayGestureHandler),
)
controller.init()
controller.addCallback(mockOngoingCallListener)
@@ -134,6 +139,21 @@
}
@Test
+ fun onEntryUpdated_isOngoingCallNotif_windowControllerUpdated() {
+ notifCollectionListener.onEntryUpdated(createOngoingCallNotifEntry())
+
+ verify(mockStatusBarWindowController).setOngoingProcessRequiresStatusBarVisible(true)
+ }
+
+ @Test
+ fun onEntryUpdated_isOngoingCallNotif_swipeGestureCallbackAdded() {
+ notifCollectionListener.onEntryUpdated(createOngoingCallNotifEntry())
+
+ verify(mockSwipeStatusBarAwayGestureHandler)
+ .addOnGestureDetectedCallback(anyString(), any())
+ }
+
+ @Test
fun onEntryUpdated_notOngoingCallNotif_listenerNotNotified() {
notifCollectionListener.onEntryUpdated(createNotCallNotifEntry())
@@ -224,6 +244,27 @@
verify(mockOngoingCallListener).onOngoingCallStateChanged(anyBoolean())
}
+ @Test
+ fun onEntryUpdated_callNotifAddedThenRemoved_windowControllerUpdated() {
+ val ongoingCallNotifEntry = createOngoingCallNotifEntry()
+ notifCollectionListener.onEntryAdded(ongoingCallNotifEntry)
+
+ notifCollectionListener.onEntryRemoved(ongoingCallNotifEntry, REASON_USER_STOPPED)
+
+ verify(mockStatusBarWindowController).setOngoingProcessRequiresStatusBarVisible(false)
+ }
+
+ @Test
+ fun onEntryUpdated_callNotifAddedThenRemoved_swipeGestureCallbackRemoved() {
+ val ongoingCallNotifEntry = createOngoingCallNotifEntry()
+ notifCollectionListener.onEntryAdded(ongoingCallNotifEntry)
+
+ notifCollectionListener.onEntryRemoved(ongoingCallNotifEntry, REASON_USER_STOPPED)
+
+ verify(mockSwipeStatusBarAwayGestureHandler)
+ .removeOnGestureDetectedCallback(anyString())
+ }
+
/** Regression test for b/188491504. */
@Test
fun onEntryRemoved_removedNotifHasSameKeyAsAddedNotif_listenerNotified() {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/usb/UsbPermissionActivityTest.kt b/packages/SystemUI/tests/src/com/android/systemui/usb/UsbPermissionActivityTest.kt
index eebcbe6..3d55488 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/usb/UsbPermissionActivityTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/usb/UsbPermissionActivityTest.kt
@@ -31,7 +31,6 @@
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
-import java.lang.Exception
/**
* UsbPermissionActivityTest
@@ -54,7 +53,9 @@
putExtra(Intent.EXTRA_INTENT, PendingIntent.getBroadcast(
mContext,
334,
- Intent("NO_ACTION"),
+ Intent("NO_ACTION").apply {
+ setPackage("com.android.systemui.tests")
+ },
PendingIntent.FLAG_MUTABLE))
putExtra(UsbManager.EXTRA_ACCESSORY, UsbAccessory(
"manufacturer",
diff --git a/packages/SystemUI/tests/src/com/android/systemui/util/ChannelsTest.java b/packages/SystemUI/tests/src/com/android/systemui/util/ChannelsTest.java
index f1c9980..c7bcdef 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/util/ChannelsTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/util/ChannelsTest.java
@@ -22,7 +22,6 @@
import android.app.NotificationChannel;
import android.app.NotificationManager;
import android.content.Context;
-import android.provider.Settings;
import android.test.suitebuilder.annotation.SmallTest;
import android.util.ArraySet;
@@ -68,51 +67,4 @@
list.forEach((chan) -> assertTrue(ALL_CHANNELS.contains(chan.getId())));
}
- @Test
- public void testChannelSetup_noLegacyScreenshot() {
- // Assert old channel cleaned up.
- // TODO: remove that code + this test after P.
- NotificationChannels.createAll(mContext);
- ArgumentCaptor<List> captor = ArgumentCaptor.forClass(List.class);
- verify(mMockNotificationManager).deleteNotificationChannel(
- NotificationChannels.SCREENSHOTS_LEGACY);
- }
-
- @Test
- public void testInheritFromLegacy_keepsUserLockedLegacySettings() {
- NotificationChannel legacyChannel = new NotificationChannel("id", "oldName",
- NotificationManager.IMPORTANCE_MIN);
- legacyChannel.setImportance(NotificationManager.IMPORTANCE_NONE);;
- legacyChannel.setSound(Settings.System.DEFAULT_NOTIFICATION_URI,
- legacyChannel.getAudioAttributes());
- legacyChannel.lockFields(NotificationChannel.USER_LOCKED_IMPORTANCE |
- NotificationChannel.USER_LOCKED_SOUND);
- NotificationChannel newChannel =
- NotificationChannels.createScreenshotChannel("newName", legacyChannel);
- // NONE importance user locked, so don't use HIGH for new channel.
- assertEquals(NotificationManager.IMPORTANCE_NONE, newChannel.getImportance());
- assertEquals(Settings.System.DEFAULT_NOTIFICATION_URI, newChannel.getSound());
- }
-
- @Test
- public void testInheritFromLegacy_dropsUnlockedLegacySettings() {
- NotificationChannel legacyChannel = new NotificationChannel("id", "oldName",
- NotificationManager.IMPORTANCE_MIN);
- NotificationChannel newChannel =
- NotificationChannels.createScreenshotChannel("newName", legacyChannel);
- assertEquals(null, newChannel.getSound());
- assertEquals("newName", newChannel.getName());
- // MIN importance not user locked, so HIGH wins out.
- assertEquals(NotificationManager.IMPORTANCE_HIGH, newChannel.getImportance());
- }
-
- @Test
- public void testInheritFromLegacy_noLegacyExists() {
- NotificationChannel newChannel =
- NotificationChannels.createScreenshotChannel("newName", null);
- assertEquals(null, newChannel.getSound());
- assertEquals("newName", newChannel.getName());
- assertEquals(NotificationManager.IMPORTANCE_HIGH, newChannel.getImportance());
- }
-
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/util/WallpaperControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/util/WallpaperControllerTest.kt
index b54aadb..d8e418a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/util/WallpaperControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/util/WallpaperControllerTest.kt
@@ -138,7 +138,7 @@
private fun createWallpaperInfo(useDefaultTransition: Boolean = true): WallpaperInfo {
val info = mock(WallpaperInfo::class.java)
- whenever(info.shouldUseDefaultDisplayStateChangeTransition())
+ whenever(info.shouldUseDefaultUnfoldTransition())
.thenReturn(useDefaultTransition)
return info
}
diff --git a/packages/services/CameraExtensionsProxy/src/com/android/cameraextensions/CameraExtensionsProxyService.java b/packages/services/CameraExtensionsProxy/src/com/android/cameraextensions/CameraExtensionsProxyService.java
index b0893cc..ed37d7e 100644
--- a/packages/services/CameraExtensionsProxy/src/com/android/cameraextensions/CameraExtensionsProxyService.java
+++ b/packages/services/CameraExtensionsProxy/src/com/android/cameraextensions/CameraExtensionsProxyService.java
@@ -19,6 +19,7 @@
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
+import android.graphics.Camera;
import android.graphics.GraphicBuffer;
import android.graphics.Rect;
import android.hardware.camera2.CameraAccessException;
@@ -135,6 +136,7 @@
private HashMap<String, CameraCharacteristics> mCharacteristicsHashMap = new HashMap<>();
private HashMap<String, Long> mMetadataVendorIdMap = new HashMap<>();
+ private CameraManager mCameraManager;
private static boolean checkForAdvancedAPI() {
if (EXTENSIONS_PRESENT && EXTENSIONS_VERSION.startsWith(ADVANCED_VERSION_PREFIX)) {
@@ -460,12 +462,12 @@
// This will setup the camera vendor tag descriptor in the service process
// along with all camera characteristics.
try {
- CameraManager manager = getSystemService(CameraManager.class);
+ mCameraManager = getSystemService(CameraManager.class);
- String [] cameraIds = manager.getCameraIdListNoLazy();
+ String [] cameraIds = mCameraManager.getCameraIdListNoLazy();
if (cameraIds != null) {
for (String cameraId : cameraIds) {
- CameraCharacteristics chars = manager.getCameraCharacteristics(cameraId);
+ CameraCharacteristics chars = mCameraManager.getCameraCharacteristics(cameraId);
mCharacteristicsHashMap.put(cameraId, chars);
Object thisClass = CameraCharacteristics.Key.class;
Class<CameraCharacteristics.Key<?>> keyClass =
@@ -1174,8 +1176,9 @@
@Override
public void onInit(String cameraId, CameraMetadataNative cameraCharacteristics) {
mCameraId = cameraId;
- mPreviewExtender.onInit(cameraId, new CameraCharacteristics(cameraCharacteristics),
- CameraExtensionsProxyService.this);
+ CameraCharacteristics chars = new CameraCharacteristics(cameraCharacteristics);
+ mCameraManager.registerDeviceStateListener(chars);
+ mPreviewExtender.onInit(cameraId, chars, CameraExtensionsProxyService.this);
}
@Override
@@ -1200,13 +1203,16 @@
@Override
public void init(String cameraId, CameraMetadataNative chars) {
- mPreviewExtender.init(cameraId, new CameraCharacteristics(chars));
+ CameraCharacteristics c = new CameraCharacteristics(chars);
+ mCameraManager.registerDeviceStateListener(c);
+ mPreviewExtender.init(cameraId, c);
}
@Override
public boolean isExtensionAvailable(String cameraId, CameraMetadataNative chars) {
- return mPreviewExtender.isExtensionAvailable(cameraId,
- new CameraCharacteristics(chars));
+ CameraCharacteristics c = new CameraCharacteristics(chars);
+ mCameraManager.registerDeviceStateListener(c);
+ return mPreviewExtender.isExtensionAvailable(cameraId, c);
}
@Override
@@ -1283,8 +1289,9 @@
@Override
public void onInit(String cameraId, CameraMetadataNative cameraCharacteristics) {
- mImageExtender.onInit(cameraId, new CameraCharacteristics(cameraCharacteristics),
- CameraExtensionsProxyService.this);
+ CameraCharacteristics chars = new CameraCharacteristics(cameraCharacteristics);
+ mCameraManager.registerDeviceStateListener(chars);
+ mImageExtender.onInit(cameraId, chars, CameraExtensionsProxyService.this);
mCameraId = cameraId;
}
@@ -1310,13 +1317,16 @@
@Override
public void init(String cameraId, CameraMetadataNative chars) {
- mImageExtender.init(cameraId, new CameraCharacteristics(chars));
+ CameraCharacteristics c = new CameraCharacteristics(chars);
+ mCameraManager.registerDeviceStateListener(c);
+ mImageExtender.init(cameraId, c);
}
@Override
public boolean isExtensionAvailable(String cameraId, CameraMetadataNative chars) {
- return mImageExtender.isExtensionAvailable(cameraId,
- new CameraCharacteristics(chars));
+ CameraCharacteristics c = new CameraCharacteristics(chars);
+ mCameraManager.registerDeviceStateListener(c);
+ return mImageExtender.isExtensionAvailable(cameraId, c);
}
@Override
diff --git a/services/core/java/com/android/server/SensorPrivacyService.java b/services/core/java/com/android/server/SensorPrivacyService.java
index 93a820c..06a78c8 100644
--- a/services/core/java/com/android/server/SensorPrivacyService.java
+++ b/services/core/java/com/android/server/SensorPrivacyService.java
@@ -39,6 +39,7 @@
import static android.hardware.SensorPrivacyManager.Sources.QS_TILE;
import static android.hardware.SensorPrivacyManager.Sources.SETTINGS;
import static android.hardware.SensorPrivacyManager.Sources.SHELL;
+import static android.os.UserHandle.USER_NULL;
import static android.os.UserHandle.USER_SYSTEM;
import static android.service.SensorPrivacyIndividualEnabledSensorProto.UNKNOWN;
@@ -195,7 +196,7 @@
private EmergencyCallHelper mEmergencyCallHelper;
private KeyguardManager mKeyguardManager;
- private int mCurrentUser = -1;
+ private int mCurrentUser = USER_NULL;
public SensorPrivacyService(Context context) {
super(context);
@@ -228,9 +229,9 @@
@Override
public void onUserStarting(TargetUser user) {
- if (mCurrentUser == -1) {
+ if (mCurrentUser == USER_NULL) {
mCurrentUser = user.getUserIdentifier();
- mSensorPrivacyServiceImpl.userSwitching(-1, user.getUserIdentifier());
+ mSensorPrivacyServiceImpl.userSwitching(USER_NULL, user.getUserIdentifier());
}
}
@@ -1294,13 +1295,13 @@
micState = isIndividualSensorPrivacyEnabledLocked(to, MICROPHONE);
camState = isIndividualSensorPrivacyEnabledLocked(to, CAMERA);
}
- if (prevMicState != micState) {
+ if (from == USER_NULL || prevMicState != micState) {
mHandler.onUserGlobalSensorPrivacyChanged(MICROPHONE, micState);
setGlobalRestriction(MICROPHONE, micState);
}
- if (prevCamState != camState) {
+ if (from == USER_NULL || prevCamState != camState) {
mHandler.onUserGlobalSensorPrivacyChanged(CAMERA, camState);
- setGlobalRestriction(CAMERA, micState);
+ setGlobalRestriction(CAMERA, camState);
}
}
diff --git a/services/core/java/com/android/server/am/BatteryExternalStatsWorker.java b/services/core/java/com/android/server/am/BatteryExternalStatsWorker.java
index 7ba032f..e9aa3be 100644
--- a/services/core/java/com/android/server/am/BatteryExternalStatsWorker.java
+++ b/services/core/java/com/android/server/am/BatteryExternalStatsWorker.java
@@ -659,11 +659,13 @@
// Inform mStats about each applicable measured energy (unless addressed elsewhere).
if (measuredEnergyDeltas != null) {
- final long displayChargeUC = measuredEnergyDeltas.displayChargeUC;
- if (displayChargeUC != MeasuredEnergySnapshot.UNAVAILABLE) {
+ final long[] displayChargeUC = measuredEnergyDeltas.displayChargeUC;
+ if (displayChargeUC != null && displayChargeUC.length > 0) {
+ // TODO (b/194107383): pass all display ordinals to mStats.
+ final long primaryDisplayChargeUC = displayChargeUC[0];
// If updating, pass in what BatteryExternalStatsWorker thinks screenState is.
- mStats.updateDisplayMeasuredEnergyStatsLocked(displayChargeUC, screenState,
- elapsedRealtime);
+ mStats.updateDisplayMeasuredEnergyStatsLocked(primaryDisplayChargeUC,
+ screenState, elapsedRealtime);
}
final long gnssChargeUC = measuredEnergyDeltas.gnssChargeUC;
@@ -948,6 +950,7 @@
switch (consumer.type) {
case EnergyConsumerType.OTHER:
case EnergyConsumerType.CPU_CLUSTER:
+ case EnergyConsumerType.DISPLAY:
break;
default:
Slog.w(TAG, "EnergyConsumer '" + consumer.name + "' has unexpected ordinal "
diff --git a/services/core/java/com/android/server/am/MeasuredEnergySnapshot.java b/services/core/java/com/android/server/am/MeasuredEnergySnapshot.java
index a9fca4f..0359aa5 100644
--- a/services/core/java/com/android/server/am/MeasuredEnergySnapshot.java
+++ b/services/core/java/com/android/server/am/MeasuredEnergySnapshot.java
@@ -49,6 +49,9 @@
/** Number of ordinals for {@link EnergyConsumerType#CPU_CLUSTER}. */
private final int mNumCpuClusterOrdinals;
+ /** Number of ordinals for {@link EnergyConsumerType#DISPLAY}. */
+ private final int mNumDisplayOrdinals;
+
/** Number of ordinals for {@link EnergyConsumerType#OTHER}. */
private final int mNumOtherOrdinals;
@@ -95,6 +98,7 @@
mNumCpuClusterOrdinals = calculateNumOrdinals(EnergyConsumerType.CPU_CLUSTER,
idToConsumerMap);
+ mNumDisplayOrdinals = calculateNumOrdinals(EnergyConsumerType.DISPLAY, idToConsumerMap);
mNumOtherOrdinals = calculateNumOrdinals(EnergyConsumerType.OTHER, idToConsumerMap);
mAttributionSnapshots = new SparseArray<>(mNumOtherOrdinals);
}
@@ -108,7 +112,7 @@
public long[] cpuClusterChargeUC = null;
/** The chargeUC for {@link EnergyConsumerType#DISPLAY}. */
- public long displayChargeUC = UNAVAILABLE;
+ public long[] displayChargeUC = null;
/** The chargeUC for {@link EnergyConsumerType#GNSS}. */
public long gnssChargeUC = UNAVAILABLE;
@@ -212,7 +216,10 @@
break;
case EnergyConsumerType.DISPLAY:
- output.displayChargeUC = deltaChargeUC;
+ if (output.displayChargeUC == null) {
+ output.displayChargeUC = new long[mNumDisplayOrdinals];
+ }
+ output.displayChargeUC[ordinal] = deltaChargeUC;
break;
case EnergyConsumerType.GNSS:
diff --git a/services/core/java/com/android/server/audio/SpatializerHelper.java b/services/core/java/com/android/server/audio/SpatializerHelper.java
index b2fa86b..16c8588 100644
--- a/services/core/java/com/android/server/audio/SpatializerHelper.java
+++ b/services/core/java/com/android/server/audio/SpatializerHelper.java
@@ -213,6 +213,10 @@
postInitSensors(true);
}
}
+
+ public void onOutputChanged(int output) {
+ logd("SpatializerCallback.onOutputChanged output:" + output);
+ }
};
// spatializer head tracking callback from native
@@ -825,9 +829,18 @@
}
synchronized void onInitSensors(boolean init) {
- final int[] modes = getSupportedHeadTrackingModes();
- if (modes.length == 0) {
- Log.i(TAG, "not initializing sensors, no headtracking supported");
+ final String action = init ? "initializing" : "releasing";
+ if (mSpat == null) {
+ Log.e(TAG, "not " + action + " sensors, null spatializer");
+ return;
+ }
+ try {
+ if (!mSpat.isHeadTrackingSupported()) {
+ Log.e(TAG, "not " + action + " sensors, spatializer doesn't support headtracking");
+ return;
+ }
+ } catch (RemoteException e) {
+ Log.e(TAG, "not " + action + " sensors, error querying headtracking", e);
return;
}
initSensors(init);
diff --git a/services/core/java/com/android/server/display/DisplayPowerController.java b/services/core/java/com/android/server/display/DisplayPowerController.java
index bf5208a..63d32c8 100644
--- a/services/core/java/com/android/server/display/DisplayPowerController.java
+++ b/services/core/java/com/android/server/display/DisplayPowerController.java
@@ -125,6 +125,7 @@
private static final int MSG_IGNORE_PROXIMITY = 8;
private static final int MSG_STOP = 9;
private static final int MSG_UPDATE_BRIGHTNESS = 10;
+ private static final int MSG_UPDATE_RBC = 11;
private static final int PROXIMITY_UNKNOWN = -1;
private static final int PROXIMITY_NEGATIVE = 0;
@@ -422,13 +423,13 @@
// PowerManager.BRIGHTNESS_INVALID_FLOAT when there's no temporary adjustment set.
private float mTemporaryAutoBrightnessAdjustment;
- // Whether a reduce bright colors (rbc) change has been initiated by the user. We want to
- // retain the current backlight level when rbc is toggled, since rbc additionally makes the
- // screen appear dimmer using screen colors rather than backlight levels, and therefore we
- // don't actually want to compensate for this by then in/decreasing the backlight when
- // toggling this feature.
+ // Whether reduce bright colors (rbc) has been turned on, or a change in strength has been
+ // requested. We want to retain the current backlight level when rbc is toggled, since rbc
+ // additionally makes the screen appear dimmer using screen colors rather than backlight levels,
+ // and therefore we don't actually want to compensate for this by then in/decreasing the
+ // backlight when toggling this feature.
// This should be false during system start up.
- private boolean mPendingUserRbcChange;
+ private boolean mPendingRbcOnOrChanged = false;
// Animators.
private ObjectAnimator mColorFadeOnAnimator;
@@ -564,23 +565,35 @@
@Override
public void onReduceBrightColorsActivationChanged(boolean activated,
boolean userInitiated) {
- applyReduceBrightColorsSplineAdjustment(userInitiated);
+ applyReduceBrightColorsSplineAdjustment(
+ /* rbcStrengthChanged= */ false, activated);
+
}
@Override
public void onReduceBrightColorsStrengthChanged(int strength) {
- applyReduceBrightColorsSplineAdjustment(/*userInitiated*/ false);
+ applyReduceBrightColorsSplineAdjustment(
+ /* rbcStrengthChanged= */ true, /* justActivated= */ false);
}
});
if (active) {
- applyReduceBrightColorsSplineAdjustment(/*userInitiated*/ false);
+ applyReduceBrightColorsSplineAdjustment(
+ /* rbcStrengthChanged= */ false, /* justActivated= */ false);
}
} else {
mCdsi = null;
}
}
- private void applyReduceBrightColorsSplineAdjustment(boolean userInitiated) {
+ private void applyReduceBrightColorsSplineAdjustment(
+ boolean rbcStrengthChanged, boolean justActivated) {
+ final int strengthChanged = rbcStrengthChanged ? 1 : 0;
+ final int activated = justActivated ? 1 : 0;
+ mHandler.obtainMessage(MSG_UPDATE_RBC, strengthChanged, activated).sendToTarget();
+ sendUpdatePowerState();
+ }
+
+ private void handleRbcChanged(boolean strengthChanged, boolean justActivated) {
if (mBrightnessMapper == null) {
Log.w(TAG, "No brightness mapping available to recalculate splines");
return;
@@ -591,8 +604,13 @@
adjustedNits[i] = mCdsi.getReduceBrightColorsAdjustedBrightnessNits(mNitsRange[i]);
}
mBrightnessMapper.recalculateSplines(mCdsi.isReduceBrightColorsActivated(), adjustedNits);
- mPendingUserRbcChange = userInitiated;
- sendUpdatePowerState();
+
+ mPendingRbcOnOrChanged = strengthChanged || justActivated;
+
+ // Reset model if strength changed OR rbc is turned off
+ if (strengthChanged || !justActivated && mAutomaticBrightnessController != null) {
+ mAutomaticBrightnessController.resetShortTermModel();
+ }
}
/**
@@ -926,7 +944,8 @@
private void reloadReduceBrightColours() {
if (mCdsi != null && mCdsi.isReduceBrightColorsActivated()) {
- applyReduceBrightColorsSplineAdjustment(/*userInitiated*/ false);
+ applyReduceBrightColorsSplineAdjustment(
+ /* rbcStrengthChanged= */ false, /* justActivated= */ false);
}
}
@@ -2072,21 +2091,24 @@
return true;
}
+ // We want to return true if the user has set the screen brightness.
+ // If they have just turned RBC on (and therefore added that interaction to the curve),
+ // or changed the brightness another way, then we should return true.
private boolean updateUserSetScreenBrightness() {
- final boolean brightnessSplineChanged = mPendingUserRbcChange;
- if (mPendingUserRbcChange && !Float.isNaN(mCurrentScreenBrightnessSetting)) {
+ final boolean treatAsIfUserChanged = mPendingRbcOnOrChanged;
+ if (treatAsIfUserChanged && !Float.isNaN(mCurrentScreenBrightnessSetting)) {
mLastUserSetScreenBrightness = mCurrentScreenBrightnessSetting;
}
- mPendingUserRbcChange = false;
+ mPendingRbcOnOrChanged = false;
if ((Float.isNaN(mPendingScreenBrightnessSetting)
|| mPendingScreenBrightnessSetting < 0.0f)) {
- return brightnessSplineChanged;
+ return treatAsIfUserChanged;
}
if (mCurrentScreenBrightnessSetting == mPendingScreenBrightnessSetting) {
mPendingScreenBrightnessSetting = PowerManager.BRIGHTNESS_INVALID_FLOAT;
mTemporaryScreenBrightness = PowerManager.BRIGHTNESS_INVALID_FLOAT;
- return brightnessSplineChanged;
+ return treatAsIfUserChanged;
}
setCurrentScreenBrightness(mPendingScreenBrightnessSetting);
mLastUserSetScreenBrightness = mPendingScreenBrightnessSetting;
@@ -2428,6 +2450,12 @@
}
handleSettingsChange(false /*userSwitch*/);
break;
+
+ case MSG_UPDATE_RBC:
+ final int strengthChanged = msg.arg1;
+ final int justActivated = msg.arg2;
+ handleRbcChanged(strengthChanged == 1, justActivated == 1);
+ break;
}
}
}
diff --git a/services/core/java/com/android/server/input/InputManagerService.java b/services/core/java/com/android/server/input/InputManagerService.java
index b0b8be2..fae7e45 100644
--- a/services/core/java/com/android/server/input/InputManagerService.java
+++ b/services/core/java/com/android/server/input/InputManagerService.java
@@ -900,6 +900,7 @@
@Override // Binder call
public VerifiedInputEvent verifyInputEvent(InputEvent event) {
+ Objects.requireNonNull(event, "event must not be null");
return nativeVerifyInputEvent(mPtr, event);
}
diff --git a/services/core/java/com/android/server/location/contexthub/ContextHubClientBroker.java b/services/core/java/com/android/server/location/contexthub/ContextHubClientBroker.java
index fa33338..03e421b 100644
--- a/services/core/java/com/android/server/location/contexthub/ContextHubClientBroker.java
+++ b/services/core/java/com/android/server/location/contexthub/ContextHubClientBroker.java
@@ -209,6 +209,12 @@
*/
private AtomicBoolean mIsPendingIntentCancelled = new AtomicBoolean(false);
+ /**
+ * True if a permissions query has been issued and is being processed. Used to prevent too many
+ * queries from being issued by a single client at once.
+ */
+ private AtomicBoolean mIsPermQueryIssued = new AtomicBoolean(false);
+
/*
* True if the application creating the client has the ACCESS_CONTEXT_HUB permission.
*/
@@ -240,11 +246,11 @@
private final IContextHubTransactionCallback mQueryPermsCallback =
new IContextHubTransactionCallback.Stub() {
@Override
- public void onTransactionComplete(int result) {
- }
+ public void onTransactionComplete(int result) {}
@Override
public void onQueryResponse(int result, List<NanoAppState> nanoAppStateList) {
+ mIsPermQueryIssued.set(false);
if (result != ContextHubTransaction.RESULT_SUCCESS && nanoAppStateList != null) {
Log.e(TAG, "Permissions query failed, but still received nanoapp state");
} else if (nanoAppStateList != null) {
@@ -656,9 +662,11 @@
* communicated with in the past.
*/
private void checkNanoappPermsAsync() {
- ContextHubServiceTransaction transaction = mTransactionManager.createQueryTransaction(
- mAttachedContextHubInfo.getId(), mQueryPermsCallback, mPackage);
- mTransactionManager.addTransaction(transaction);
+ if (!mIsPermQueryIssued.getAndSet(true)) {
+ ContextHubServiceTransaction transaction = mTransactionManager.createQueryTransaction(
+ mAttachedContextHubInfo.getId(), mQueryPermsCallback, mPackage);
+ mTransactionManager.addTransaction(transaction);
+ }
}
private int updateNanoAppAuthState(
diff --git a/services/core/java/com/android/server/location/injector/LocationPermissionsHelper.java b/services/core/java/com/android/server/location/injector/LocationPermissionsHelper.java
index a47c48f..2df2101 100644
--- a/services/core/java/com/android/server/location/injector/LocationPermissionsHelper.java
+++ b/services/core/java/com/android/server/location/injector/LocationPermissionsHelper.java
@@ -100,7 +100,7 @@
return false;
}
- return mAppOps.checkOpNoThrow(permissionLevel, identity);
+ return mAppOps.checkOpNoThrow(LocationPermissions.asAppOp(permissionLevel), identity);
}
protected abstract boolean hasPermission(String permission, CallerIdentity callerIdentity);
diff --git a/services/core/java/com/android/server/location/provider/StationaryThrottlingLocationProvider.java b/services/core/java/com/android/server/location/provider/StationaryThrottlingLocationProvider.java
index cc51cea..22a675a 100644
--- a/services/core/java/com/android/server/location/provider/StationaryThrottlingLocationProvider.java
+++ b/services/core/java/com/android/server/location/provider/StationaryThrottlingLocationProvider.java
@@ -105,15 +105,20 @@
synchronized (mLock) {
mDeviceIdleHelper.addListener(this);
- onDeviceIdleChanged(mDeviceIdleHelper.isDeviceIdle());
+ mDeviceIdle = mDeviceIdleHelper.isDeviceIdle();
+ mDeviceStationaryHelper.addListener(this);
+ mDeviceStationary = false;
+ mDeviceStationaryRealtimeMs = Long.MIN_VALUE;
+
+ onThrottlingChangedLocked(false);
}
}
@Override
protected void onStop() {
synchronized (mLock) {
+ mDeviceStationaryHelper.removeListener(this);
mDeviceIdleHelper.removeListener(this);
- onDeviceIdleChanged(false);
mIncomingRequest = ProviderRequest.EMPTY_REQUEST;
mOutgoingRequest = ProviderRequest.EMPTY_REQUEST;
@@ -146,27 +151,13 @@
}
mDeviceIdle = deviceIdle;
-
- if (deviceIdle) {
- // device stationary helper will deliver an immediate listener update
- mDeviceStationaryHelper.addListener(this);
- } else {
- mDeviceStationaryHelper.removeListener(this);
- mDeviceStationary = false;
- mDeviceStationaryRealtimeMs = Long.MIN_VALUE;
- onThrottlingChangedLocked(false);
- }
+ onThrottlingChangedLocked(false);
}
}
@Override
public void onDeviceStationaryChanged(boolean deviceStationary) {
synchronized (mLock) {
- if (!mDeviceIdle) {
- // stationary detection is only registered while idle - ignore late notifications
- return;
- }
-
if (mDeviceStationary == deviceStationary) {
return;
}
diff --git a/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java b/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java
index dab980a..3019146 100644
--- a/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java
+++ b/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java
@@ -557,11 +557,14 @@
grantPermissionsToSystemPackage(pm, verifier, userId, PHONE_PERMISSIONS, SMS_PERMISSIONS);
// SetupWizard
- grantPermissionsToSystemPackage(pm,
- ArrayUtils.firstOrNull(getKnownPackages(
- PackageManagerInternal.PACKAGE_SETUP_WIZARD, userId)), userId,
- PHONE_PERMISSIONS, CONTACTS_PERMISSIONS, ALWAYS_LOCATION_PERMISSIONS,
- CAMERA_PERMISSIONS);
+ final String setupWizardPackage = ArrayUtils.firstOrNull(getKnownPackages(
+ PackageManagerInternal.PACKAGE_SETUP_WIZARD, userId));
+ grantPermissionsToSystemPackage(pm, setupWizardPackage, userId, PHONE_PERMISSIONS,
+ CONTACTS_PERMISSIONS, ALWAYS_LOCATION_PERMISSIONS, CAMERA_PERMISSIONS);
+ if (mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_AUTOMOTIVE, 0)) {
+ grantPermissionsToSystemPackage(
+ pm, setupWizardPackage, userId, NEARBY_DEVICES_PERMISSIONS);
+ }
// Camera
grantPermissionsToSystemPackage(pm,
diff --git a/services/core/java/com/android/server/pm/permission/LegacyPermissionManagerService.java b/services/core/java/com/android/server/pm/permission/LegacyPermissionManagerService.java
index b1676d0..ea554d3 100644
--- a/services/core/java/com/android/server/pm/permission/LegacyPermissionManagerService.java
+++ b/services/core/java/com/android/server/pm/permission/LegacyPermissionManagerService.java
@@ -30,6 +30,7 @@
import android.os.ServiceManager;
import android.os.UserHandle;
import android.permission.ILegacyPermissionManager;
+import android.util.EventLog;
import android.util.Log;
import com.android.internal.annotations.VisibleForTesting;
@@ -187,10 +188,25 @@
private void verifyCallerCanCheckAccess(String packageName, String message, int pid, int uid) {
// If the check is being requested by an app then only allow the app to query its own
// access status.
+ boolean reportError = false;
int callingUid = mInjector.getCallingUid();
int callingPid = mInjector.getCallingPid();
if (UserHandle.getAppId(callingUid) >= Process.FIRST_APPLICATION_UID && (callingUid != uid
|| callingPid != pid)) {
+ reportError = true;
+ }
+ // If the query is against an app on the device, then the check should only be allowed if
+ // the provided uid matches that of the specified package.
+ if (packageName != null && UserHandle.getAppId(uid) >= Process.FIRST_APPLICATION_UID) {
+ int packageUid = mInjector.getPackageUidForUser(packageName, UserHandle.getUserId(uid));
+ if (uid != packageUid) {
+ EventLog.writeEvent(0x534e4554, "193441322",
+ UserHandle.getAppId(callingUid) >= Process.FIRST_APPLICATION_UID
+ ? callingUid : uid, "Package uid mismatch");
+ reportError = true;
+ }
+ }
+ if (reportError) {
String response = String.format(
"Calling uid %d, pid %d cannot access for package %s (uid=%d, pid=%d): %s",
callingUid, callingPid, packageName, uid, pid, message);
@@ -385,12 +401,14 @@
@VisibleForTesting
public static class Injector {
private final Context mContext;
+ private final PackageManagerInternal mPackageManagerInternal;
/**
* Public constructor that accepts a {@code context} within which to operate.
*/
public Injector(@NonNull Context context) {
mContext = context;
+ mPackageManagerInternal = LocalServices.getService(PackageManagerInternal.class);
}
/**
@@ -453,5 +471,12 @@
return mContext.getPackageManager().getApplicationInfoAsUser(packageName, 0,
UserHandle.getUserHandleForUid(uid));
}
+
+ /**
+ * Returns the uid for the specified {@code packageName} under the provided {@code userId}.
+ */
+ public int getPackageUidForUser(String packageName, int userId) {
+ return mPackageManagerInternal.getPackageUid(packageName, 0, userId);
+ }
}
}
diff --git a/services/core/java/com/android/server/wm/AccessibilityController.java b/services/core/java/com/android/server/wm/AccessibilityController.java
index e59c82cf..cf9783f 100644
--- a/services/core/java/com/android/server/wm/AccessibilityController.java
+++ b/services/core/java/com/android/server/wm/AccessibilityController.java
@@ -50,6 +50,7 @@
import android.animation.ObjectAnimator;
import android.animation.ValueAnimator;
import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.app.Application;
import android.content.Context;
import android.content.pm.PackageManagerInternal;
@@ -134,6 +135,8 @@
private SparseArray<DisplayMagnifier> mDisplayMagnifiers = new SparseArray<>();
private SparseArray<WindowsForAccessibilityObserver> mWindowsForAccessibilityObserver =
new SparseArray<>();
+ private SparseArray<IBinder> mFocusedWindow = new SparseArray<>();
+ private int mFocusedDisplay = -1;
// Set to true if initializing window population complete.
private boolean mAllObserversInitialized = true;
@@ -603,6 +606,29 @@
return display.getType() == Display.TYPE_VIRTUAL && dc.getParentWindow() != null;
}
+ void onFocusChanged(InputTarget lastTarget, InputTarget newTarget) {
+ if (lastTarget != null) {
+ mFocusedWindow.remove(lastTarget.getDisplayId());
+ }
+ if (newTarget != null) {
+ int displayId = newTarget.getDisplayId();
+ IBinder clientBinder = newTarget.getIWindow().asBinder();
+ mFocusedWindow.put(displayId, clientBinder);
+ }
+ }
+
+ public void onDisplayRemoved(int displayId) {
+ mFocusedWindow.remove(displayId);
+ }
+
+ public void setFocusedDisplay(int focusedDisplayId) {
+ mFocusedDisplay = focusedDisplayId;
+ }
+
+ @Nullable IBinder getFocusedWindowToken() {
+ return mFocusedWindow.get(mFocusedDisplay);
+ }
+
/**
* This class encapsulates the functionality related to display magnification.
*/
diff --git a/services/core/java/com/android/server/wm/ActivityInterceptorCallback.java b/services/core/java/com/android/server/wm/ActivityInterceptorCallback.java
new file mode 100644
index 0000000..1c2333a
--- /dev/null
+++ b/services/core/java/com/android/server/wm/ActivityInterceptorCallback.java
@@ -0,0 +1,98 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.wm;
+
+import android.annotation.IntDef;
+import android.annotation.Nullable;
+import android.app.ActivityOptions;
+import android.content.Intent;
+import android.content.pm.ActivityInfo;
+import android.content.pm.ResolveInfo;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * Callback to intercept activity starts and possibly block/redirect them.
+ */
+public abstract class ActivityInterceptorCallback {
+ /**
+ * Intercept the launch intent based on various signals. If an interception happened, returns
+ * a new/existing non-null {@link Intent} which may redirect to another activity.
+ *
+ * @return null if no interception occurred, or a non-null intent which replaces the
+ * existing intent.
+ */
+ public abstract @Nullable Intent intercept(ActivityInterceptorInfo info);
+
+ /**
+ * The unique id of each interceptor which determines the order it will execute in.
+ */
+ @IntDef(suffix = { "_ORDERED_ID" }, value = {
+ FIRST_ORDERED_ID,
+ LAST_ORDERED_ID // Update this when adding new ids
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface OrderedId {}
+
+ /**
+ * The first id, used by the framework to determine the valid range of ids.
+ */
+ static final int FIRST_ORDERED_ID = 0;
+
+ /**
+ * The final id, used by the framework to determine the valid range of ids. Update this when
+ * adding new ids.
+ */
+ static final int LAST_ORDERED_ID = FIRST_ORDERED_ID;
+
+ /**
+ * Data class for storing the various arguments needed for activity interception.
+ */
+ public static final class ActivityInterceptorInfo {
+ public final int realCallingUid;
+ public final int realCallingPid;
+ public final int userId;
+ public final String callingPackage;
+ public final String callingFeatureId;
+ public final Intent intent;
+ public final ResolveInfo rInfo;
+ public final ActivityInfo aInfo;
+ public final String resolvedType;
+ public final int callingPid;
+ public final int callingUid;
+ public final ActivityOptions checkedOptions;
+
+ public ActivityInterceptorInfo(int realCallingUid, int realCallingPid, int userId,
+ String callingPackage, String callingFeatureId, Intent intent,
+ ResolveInfo rInfo, ActivityInfo aInfo, String resolvedType, int callingPid,
+ int callingUid, ActivityOptions checkedOptions) {
+ this.realCallingUid = realCallingUid;
+ this.realCallingPid = realCallingPid;
+ this.userId = userId;
+ this.callingPackage = callingPackage;
+ this.callingFeatureId = callingFeatureId;
+ this.intent = intent;
+ this.rInfo = rInfo;
+ this.aInfo = aInfo;
+ this.resolvedType = resolvedType;
+ this.callingPid = callingPid;
+ this.callingUid = callingUid;
+ this.checkedOptions = checkedOptions;
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index 6d144e1..394ff75 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -401,10 +401,6 @@
private static final int STARTING_WINDOW_TYPE_SNAPSHOT = 1;
private static final int STARTING_WINDOW_TYPE_SPLASH_SCREEN = 2;
- /**
- * Value to increment the z-layer when boosting a layer during animations. BOOST in l33tsp34k.
- */
- @VisibleForTesting static final int Z_BOOST_BASE = 800570000;
static final int INVALID_PID = -1;
// How long we wait until giving up on the last activity to pause. This
@@ -4244,20 +4240,6 @@
return callback.test(this) ? this : null;
}
- @Override
- protected void setLayer(Transaction t, int layer) {
- if (!mSurfaceAnimator.hasLeash()) {
- t.setLayer(mSurfaceControl, layer);
- }
- }
-
- @Override
- protected void setRelativeLayer(Transaction t, SurfaceControl relativeTo, int layer) {
- if (!mSurfaceAnimator.hasLeash()) {
- t.setRelativeLayer(mSurfaceControl, relativeTo, layer);
- }
- }
-
void logStartActivity(int tag, Task task) {
final Uri data = intent.getData();
final String strData = data != null ? data.toSafeString() : null;
@@ -4908,8 +4890,9 @@
* @param visible {@code true} if this {@link ActivityRecord} should become visible, otherwise
* this should become invisible.
* @param performLayout if {@code true}, perform surface placement after committing visibility.
+ * @param fromTransition {@code true} if this is part of finishing a transition.
*/
- void commitVisibility(boolean visible, boolean performLayout) {
+ void commitVisibility(boolean visible, boolean performLayout, boolean fromTransition) {
// Reset the state of mVisibleSetFromTransferredStartingWindow since visibility is actually
// been set by the app now.
mVisibleSetFromTransferredStartingWindow = false;
@@ -4959,7 +4942,11 @@
displayContent.getInputMonitor().updateInputWindowsLw(false /*force*/);
mUseTransferredAnimation = false;
- postApplyAnimation(visible);
+ postApplyAnimation(visible, fromTransition);
+ }
+
+ void commitVisibility(boolean visible, boolean performLayout) {
+ commitVisibility(visible, performLayout, false /* fromTransition */);
}
/**
@@ -4970,8 +4957,11 @@
*
* @param visible {@code true} if this {@link ActivityRecord} has become visible, otherwise
* this has become invisible.
+ * @param fromTransition {@code true} if this call is part of finishing a transition. This is
+ * needed because the shell transition is no-longer active by the time
+ * commitVisibility is called.
*/
- private void postApplyAnimation(boolean visible) {
+ private void postApplyAnimation(boolean visible, boolean fromTransition) {
final boolean usingShellTransitions = mTransitionController.isShellTransitionsEnabled();
final boolean delayed = isAnimating(TRANSITION | PARENTS | CHILDREN,
ANIMATION_TYPE_APP_TRANSITION | ANIMATION_TYPE_WINDOW_ANIMATION
@@ -5012,7 +5002,8 @@
final DisplayContent displayContent = getDisplayContent();
if (!displayContent.mClosingApps.contains(this)
- && !displayContent.mOpeningApps.contains(this)) {
+ && !displayContent.mOpeningApps.contains(this)
+ && !fromTransition) {
// Take the screenshot before possibly hiding the WSA, otherwise the screenshot
// will not be taken.
mWmService.mTaskSnapshotController.notifyAppVisibilityChanged(this, visible);
@@ -6787,12 +6778,6 @@
return candidate;
}
- SurfaceControl getAppAnimationLayer() {
- return getAppAnimationLayer(isActivityTypeHome() ? ANIMATION_LAYER_HOME
- : needsZBoost() ? ANIMATION_LAYER_BOOSTED
- : ANIMATION_LAYER_STANDARD);
- }
-
@Override
boolean needsZBoost() {
return mNeedsZBoost || super.needsZBoost();
@@ -6853,29 +6838,9 @@
|| mDisplayContent.isNextTransitionForward();
}
- private int getAnimationLayer() {
- // The leash is parented to the animation layer. We need to preserve the z-order by using
- // the prefix order index, but we boost if necessary.
- int layer;
- if (!inPinnedWindowingMode()) {
- layer = getPrefixOrderIndex();
- } else {
- // Root pinned tasks have animations take place within themselves rather than an
- // animation layer so we need to preserve the order relative to the root task (e.g.
- // the order of our task/parent).
- layer = getParent().getPrefixOrderIndex();
- }
-
- if (mNeedsZBoost) {
- layer += Z_BOOST_BASE;
- }
- return layer;
- }
-
@Override
- public void onAnimationLeashCreated(Transaction t, SurfaceControl leash) {
- t.setLayer(leash, getAnimationLayer());
- getDisplayContent().assignRootTaskOrdering();
+ void resetSurfacePositionForAnimationLeash(SurfaceControl.Transaction t) {
+ // Noop as Activity may be offset for letterbox
}
@Override
@@ -6904,7 +6869,7 @@
// Crop to root task bounds.
t.setLayer(leash, 0);
- t.setLayer(mAnimationBoundsLayer, getAnimationLayer());
+ t.setLayer(mAnimationBoundsLayer, getLastLayer());
// Reparent leash to animation bounds layer.
t.reparent(leash, mAnimationBoundsLayer);
diff --git a/services/core/java/com/android/server/wm/ActivityStartController.java b/services/core/java/com/android/server/wm/ActivityStartController.java
index b71ad2e..6ad2f7c 100644
--- a/services/core/java/com/android/server/wm/ActivityStartController.java
+++ b/services/core/java/com/android/server/wm/ActivityStartController.java
@@ -501,6 +501,8 @@
int startActivityInTaskFragment(@NonNull TaskFragment taskFragment,
@NonNull Intent activityIntent, @Nullable Bundle activityOptions,
@Nullable IBinder resultTo) {
+ final ActivityRecord caller =
+ resultTo != null ? ActivityRecord.forTokenLocked(resultTo) : null;
return obtainStarter(activityIntent, "startActivityInTaskFragment")
.setActivityOptions(activityOptions)
.setInTaskFragment(taskFragment)
@@ -508,6 +510,7 @@
.setRequestCode(-1)
.setCallingUid(Binder.getCallingUid())
.setCallingPid(Binder.getCallingPid())
+ .setUserId(caller != null ? caller.mUserId : mService.getCurrentUserId())
.execute();
}
diff --git a/services/core/java/com/android/server/wm/ActivityStartInterceptor.java b/services/core/java/com/android/server/wm/ActivityStartInterceptor.java
index 979cea9..223f0be 100644
--- a/services/core/java/com/android/server/wm/ActivityStartInterceptor.java
+++ b/services/core/java/com/android/server/wm/ActivityStartInterceptor.java
@@ -51,6 +51,7 @@
import android.os.RemoteException;
import android.os.UserHandle;
import android.os.UserManager;
+import android.util.SparseArray;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.app.BlockedAppActivity;
@@ -178,7 +179,33 @@
// before issuing the work challenge.
return true;
}
- return interceptLockedManagedProfileIfNeeded();
+ if (interceptLockedManagedProfileIfNeeded()) {
+ return true;
+ }
+
+ final SparseArray<ActivityInterceptorCallback> callbacks =
+ mService.getActivityInterceptorCallbacks();
+ final ActivityInterceptorCallback.ActivityInterceptorInfo interceptorInfo =
+ new ActivityInterceptorCallback.ActivityInterceptorInfo(mRealCallingUid,
+ mRealCallingPid, mUserId, mCallingPackage, mCallingFeatureId, mIntent,
+ mRInfo, mAInfo, mResolvedType, mCallingPid, mCallingUid,
+ mActivityOptions);
+
+ for (int i = 0; i < callbacks.size(); i++) {
+ final ActivityInterceptorCallback callback = callbacks.valueAt(i);
+ final Intent newIntent = callback.intercept(interceptorInfo);
+ if (newIntent == null) {
+ continue;
+ }
+ mIntent = newIntent;
+ mCallingPid = mRealCallingPid;
+ mCallingUid = mRealCallingUid;
+ mRInfo = mSupervisor.resolveIntent(mIntent, null, mUserId, 0, mRealCallingUid);
+ mAInfo = mSupervisor.resolveActivity(mIntent, mRInfo, mStartFlags,
+ null /*profilerInfo*/);
+ return true;
+ }
+ return false;
}
private boolean hasCrossProfileAnimation() {
diff --git a/services/core/java/com/android/server/wm/ActivityStarter.java b/services/core/java/com/android/server/wm/ActivityStarter.java
index 1707895..47467abf 100644
--- a/services/core/java/com/android/server/wm/ActivityStarter.java
+++ b/services/core/java/com/android/server/wm/ActivityStarter.java
@@ -1979,7 +1979,7 @@
// Allowing the embedding if the task is owned by system.
final int hostUid = hostTask.effectiveUid;
- if (hostUid == Process.SYSTEM_UID) {
+ if (UserHandle.getAppId(hostUid) == Process.SYSTEM_UID) {
return true;
}
diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java b/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java
index 9db13ba..3150ccd 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java
@@ -678,4 +678,12 @@
/** Called when the device is waking up */
public abstract void notifyWakingUp();
+
+ /**
+ * Registers a callback which can intercept activity starts.
+ * @throws IllegalArgumentException if duplicate ids are provided
+ */
+ public abstract void registerActivityStartInterceptor(
+ @ActivityInterceptorCallback.OrderedId int id,
+ ActivityInterceptorCallback callback);
}
diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
index 89f7d92..60a514e 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
@@ -92,6 +92,8 @@
import static com.android.server.am.ActivityManagerServiceDumpProcessesProto.ScreenCompatPackage.PACKAGE;
import static com.android.server.am.EventLogTags.writeBootProgressEnableScreen;
import static com.android.server.am.EventLogTags.writeConfigurationChanged;
+import static com.android.server.wm.ActivityInterceptorCallback.FIRST_ORDERED_ID;
+import static com.android.server.wm.ActivityInterceptorCallback.LAST_ORDERED_ID;
import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_ALL;
import static com.android.server.wm.ActivityTaskManagerDebugConfig.POSTFIX_ROOT_TASK;
import static com.android.server.wm.ActivityTaskManagerDebugConfig.POSTFIX_SWITCH;
@@ -458,6 +460,8 @@
/** The controller for all operations related to locktask. */
private LockTaskController mLockTaskController;
private ActivityStartController mActivityStartController;
+ private SparseArray<ActivityInterceptorCallback> mActivityInterceptorCallbacks =
+ new SparseArray<>();
PackageConfigPersister mPackageConfigPersister;
boolean mSuppressResizeConfigChanges;
@@ -987,6 +991,7 @@
synchronized (mGlobalLock) {
mWindowManager = wm;
mRootWindowContainer = wm.mRoot;
+ mWindowOrganizerController.setWindowManager(wm);
mTempConfig.setToDefaults();
mTempConfig.setLocales(LocaleList.getDefault());
mConfigurationSeq = mTempConfig.seq = 1;
@@ -1115,6 +1120,10 @@
return mBackgroundActivityStartCallback;
}
+ SparseArray<ActivityInterceptorCallback> getActivityInterceptorCallbacks() {
+ return mActivityInterceptorCallbacks;
+ }
+
private void start() {
LocalServices.addService(ActivityTaskManagerInternal.class, mInternal);
}
@@ -3415,9 +3424,15 @@
final List<RemoteAction> actions = r.pictureInPictureArgs.getActions();
mRootWindowContainer.moveActivityToPinnedRootTask(
r, "enterPictureInPictureMode");
- final Task rootTask = r.getRootTask();
- rootTask.setPictureInPictureAspectRatio(aspectRatio);
- rootTask.setPictureInPictureActions(actions);
+ final Task task = r.getTask();
+ task.setPictureInPictureAspectRatio(aspectRatio);
+ task.setPictureInPictureActions(actions);
+
+ // Continue the pausing process after entering pip.
+ if (task.getPausingActivity() == r) {
+ task.schedulePauseActivity(r, false /* userLeaving */,
+ false /* pauseImmediately */, "auto-pip");
+ }
}
};
@@ -4738,7 +4753,7 @@
mContext.getText(R.string.heavy_weight_notification_detail))
.setContentIntent(PendingIntent.getActivityAsUser(mContext, 0,
intent, PendingIntent.FLAG_CANCEL_CURRENT
- | PendingIntent.FLAG_IMMUTABLE, null,
+ | PendingIntent.FLAG_IMMUTABLE, null,
new UserHandle(userId)))
.build();
try {
@@ -6583,5 +6598,22 @@
getTransitionController().requestTransitionIfNeeded(TRANSIT_WAKE, 0 /* flags */,
null /* trigger */, mRootWindowContainer.getDefaultDisplay());
}
+
+ @Override
+ public void registerActivityStartInterceptor(
+ @ActivityInterceptorCallback.OrderedId int id,
+ ActivityInterceptorCallback callback) {
+ synchronized (mGlobalLock) {
+ if (mActivityInterceptorCallbacks.contains(id)) {
+ throw new IllegalArgumentException("Duplicate id provided: " + id);
+ }
+ if (id > LAST_ORDERED_ID || id < FIRST_ORDERED_ID) {
+ throw new IllegalArgumentException(
+ "Provided id " + id + " is not in range of valid ids ["
+ + FIRST_ORDERED_ID + "," + LAST_ORDERED_ID + "]");
+ }
+ mActivityInterceptorCallbacks.put(id, callback);
+ }
+ }
}
}
diff --git a/services/core/java/com/android/server/wm/AnrController.java b/services/core/java/com/android/server/wm/AnrController.java
index 892db9c..c881864 100644
--- a/services/core/java/com/android/server/wm/AnrController.java
+++ b/services/core/java/com/android/server/wm/AnrController.java
@@ -31,7 +31,6 @@
import android.view.InputApplicationHandle;
import com.android.server.am.ActivityManagerService;
-import com.android.server.wm.EmbeddedWindowController.EmbeddedWindow;
import java.io.File;
import java.util.ArrayList;
@@ -81,21 +80,19 @@
final boolean aboveSystem;
final ActivityRecord activity;
synchronized (mService.mGlobalLock) {
- WindowState windowState = mService.mInputToWindowMap.get(inputToken);
- if (windowState != null) {
- pid = windowState.mSession.mPid;
- activity = windowState.mActivityRecord;
- Slog.i(TAG_WM, "ANR in " + windowState.mAttrs.getTitle() + ". Reason:" + reason);
- } else {
- EmbeddedWindow embeddedWindow = mService.mEmbeddedWindowController.get(inputToken);
- if (embeddedWindow == null) {
- Slog.e(TAG_WM, "Unknown token, dropping notifyConnectionUnresponsive request");
- return;
- }
- pid = embeddedWindow.mOwnerPid;
- windowState = embeddedWindow.mHostWindowState;
- activity = null; // Don't blame the host process, instead blame the embedded pid.
+ InputTarget target = mService.getInputTargetFromToken(inputToken);
+ if (target == null) {
+ Slog.e(TAG_WM, "Unknown token, dropping notifyConnectionUnresponsive request");
+ return;
}
+
+ WindowState windowState = target.getWindowState();
+ pid = target.getPid();
+ // Blame the activity if the input token belongs to the window. If the target is
+ // embedded, then we will blame the pid instead.
+ activity = (windowState.mInputChannelToken == inputToken)
+ ? windowState.mActivityRecord : null;
+ Slog.i(TAG_WM, "ANR in " + target + ". Reason:" + reason);
aboveSystem = isWindowAboveSystem(windowState);
dumpAnrStateLocked(activity, windowState, reason);
}
@@ -109,19 +106,12 @@
void notifyWindowResponsive(IBinder inputToken) {
final int pid;
synchronized (mService.mGlobalLock) {
- WindowState windowState = mService.mInputToWindowMap.get(inputToken);
- if (windowState != null) {
- pid = windowState.mSession.mPid;
- } else {
- // Check if the token belongs to an embedded window.
- EmbeddedWindow embeddedWindow = mService.mEmbeddedWindowController.get(inputToken);
- if (embeddedWindow == null) {
- Slog.e(TAG_WM,
- "Unknown token, dropping notifyWindowConnectionResponsive request");
- return;
- }
- pid = embeddedWindow.mOwnerPid;
+ InputTarget target = mService.getInputTargetFromToken(inputToken);
+ if (target == null) {
+ Slog.e(TAG_WM, "Unknown token, dropping notifyWindowConnectionResponsive request");
+ return;
}
+ pid = target.getPid();
}
mService.mAmInternal.inputDispatchingResumed(pid);
}
diff --git a/services/core/java/com/android/server/wm/AppTransitionController.java b/services/core/java/com/android/server/wm/AppTransitionController.java
index ffaf710..535a061 100644
--- a/services/core/java/com/android/server/wm/AppTransitionController.java
+++ b/services/core/java/com/android/server/wm/AppTransitionController.java
@@ -124,6 +124,7 @@
@interface TransitContainerType {}
private final ArrayMap<WindowContainer, Integer> mTempTransitionReasons = new ArrayMap<>();
+ private final ArrayList<WindowContainer> mTempTransitionWindows = new ArrayList<>();
AppTransitionController(WindowManagerService service, DisplayContent displayContent) {
mService = service;
@@ -523,26 +524,44 @@
}
}
+ private boolean transitionMayContainNonAppWindows(@TransitionOldType int transit) {
+ // We don't want to have the client to animate any non-app windows.
+ // Having {@code transit} of those types doesn't mean it will contain non-app windows, but
+ // non-app windows will only be included with those transition types. And we don't currently
+ // have any use case of those for TaskFragment transition.
+ // @see NonAppWindowAnimationAdapter#startNonAppWindowAnimations
+ if (transit == TRANSIT_OLD_KEYGUARD_GOING_AWAY
+ || transit == TRANSIT_OLD_KEYGUARD_GOING_AWAY_ON_WALLPAPER
+ || transit == TRANSIT_OLD_TASK_OPEN || transit == TRANSIT_OLD_TASK_TO_FRONT
+ || transit == TRANSIT_OLD_WALLPAPER_CLOSE) {
+ return true;
+ }
+
+ // Check if the wallpaper is going to participate in the transition. We don't want to have
+ // the client to animate the wallpaper windows.
+ // @see WallpaperAnimationAdapter#startWallpaperAnimations
+ return mDisplayContent.mWallpaperController.isWallpaperVisible();
+ }
+
/**
- * Overrides the pending transition with the remote animation defined by the
- * {@link ITaskFragmentOrganizer} if all windows in the transition are children of
- * {@link TaskFragment} that are organized by the same organizer.
- *
- * @return {@code true} if the transition is overridden.
+ * Finds the common {@link android.window.TaskFragmentOrganizer} that organizes all app windows
+ * in the current transition.
+ * @return {@code null} if there is no such organizer, or if there are more than one.
*/
- private boolean overrideWithTaskFragmentRemoteAnimation(@TransitionOldType int transit,
- ArraySet<Integer> activityTypes) {
- final ArrayList<WindowContainer> allWindows = new ArrayList<>();
- allWindows.addAll(mDisplayContent.mClosingApps);
- allWindows.addAll(mDisplayContent.mOpeningApps);
- allWindows.addAll(mDisplayContent.mChangingContainers);
+ @Nullable
+ private ITaskFragmentOrganizer findTaskFragmentOrganizerForAllWindows() {
+ mTempTransitionWindows.clear();
+ mTempTransitionWindows.addAll(mDisplayContent.mClosingApps);
+ mTempTransitionWindows.addAll(mDisplayContent.mOpeningApps);
+ mTempTransitionWindows.addAll(mDisplayContent.mChangingContainers);
// It should only animated by the organizer if all windows are below the same leaf Task.
Task leafTask = null;
- for (int i = allWindows.size() - 1; i >= 0; i--) {
- final ActivityRecord r = getAppFromContainer(allWindows.get(i));
+ for (int i = mTempTransitionWindows.size() - 1; i >= 0; i--) {
+ final ActivityRecord r = getAppFromContainer(mTempTransitionWindows.get(i));
if (r == null) {
- return false;
+ leafTask = null;
+ break;
}
// The activity may be a child of embedded Task, but we want to find the owner Task.
// As a result, find the organized TaskFragment first.
@@ -561,26 +580,31 @@
? organizedTaskFragment.getTask()
: r.getTask();
if (task == null) {
- return false;
+ leafTask = null;
+ break;
}
// We don't want the organizer to handle transition of other non-embedded Task.
if (leafTask != null && leafTask != task) {
- return false;
+ leafTask = null;
+ break;
}
final ActivityRecord rootActivity = task.getRootActivity();
// We don't want the organizer to handle transition when the whole app is closing.
if (rootActivity == null) {
- return false;
+ leafTask = null;
+ break;
}
// We don't want the organizer to handle transition of non-embedded activity of other
// app.
if (r.getUid() != rootActivity.getUid() && !r.isEmbedded()) {
- return false;
+ leafTask = null;
+ break;
}
leafTask = task;
}
+ mTempTransitionWindows.clear();
if (leafTask == null) {
- return false;
+ return null;
}
// We don't support remote animation for Task with multiple TaskFragmentOrganizers.
@@ -599,12 +623,28 @@
if (hasMultipleOrganizers) {
ProtoLog.e(WM_DEBUG_APP_TRANSITIONS, "We don't support remote animation for"
+ " Task with multiple TaskFragmentOrganizers.");
+ return null;
+ }
+ return organizer[0];
+ }
+
+ /**
+ * Overrides the pending transition with the remote animation defined by the
+ * {@link ITaskFragmentOrganizer} if all windows in the transition are children of
+ * {@link TaskFragment} that are organized by the same organizer.
+ *
+ * @return {@code true} if the transition is overridden.
+ */
+ private boolean overrideWithTaskFragmentRemoteAnimation(@TransitionOldType int transit,
+ ArraySet<Integer> activityTypes) {
+ if (transitionMayContainNonAppWindows(transit)) {
return false;
}
- final RemoteAnimationDefinition definition = organizer[0] != null
+ final ITaskFragmentOrganizer organizer = findTaskFragmentOrganizerForAllWindows();
+ final RemoteAnimationDefinition definition = organizer != null
? mDisplayContent.mAtmService.mTaskFragmentOrganizerController
- .getRemoteAnimationDefinition(organizer[0])
+ .getRemoteAnimationDefinition(organizer)
: null;
final RemoteAnimationAdapter adapter = definition != null
? definition.getAdapter(transit, activityTypes)
diff --git a/services/core/java/com/android/server/wm/ConfigurationContainer.java b/services/core/java/com/android/server/wm/ConfigurationContainer.java
index eeb85c5..5a2cf17 100644
--- a/services/core/java/com/android/server/wm/ConfigurationContainer.java
+++ b/services/core/java/com/android/server/wm/ConfigurationContainer.java
@@ -28,6 +28,8 @@
import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_SECONDARY;
import static android.app.WindowConfiguration.activityTypeToString;
import static android.app.WindowConfiguration.windowingModeToString;
+import static android.app.WindowConfigurationProto.WINDOWING_MODE;
+import static android.content.ConfigurationProto.WINDOW_CONFIGURATION;
import static com.android.server.wm.ConfigurationContainerProto.FULL_CONFIGURATION;
import static com.android.server.wm.ConfigurationContainerProto.MERGED_OVERRIDE_CONFIGURATION;
@@ -695,22 +697,40 @@
@CallSuper
protected void dumpDebug(ProtoOutputStream proto, long fieldId,
@WindowTraceLogLevel int logLevel) {
- // Critical log level logs only visible elements to mitigate performance overheard
- if (logLevel != WindowTraceLogLevel.ALL && !mHasOverrideConfiguration) {
- return;
+ final long token = proto.start(fieldId);
+
+ if (logLevel == WindowTraceLogLevel.ALL || mHasOverrideConfiguration) {
+ mRequestedOverrideConfiguration.dumpDebug(proto, OVERRIDE_CONFIGURATION,
+ logLevel == WindowTraceLogLevel.CRITICAL);
}
- final long token = proto.start(fieldId);
- mRequestedOverrideConfiguration.dumpDebug(proto, OVERRIDE_CONFIGURATION,
- logLevel == WindowTraceLogLevel.CRITICAL);
+ // Unless trace level is set to `WindowTraceLogLevel.ALL` don't dump anything that isn't
+ // required to mitigate performance overhead
if (logLevel == WindowTraceLogLevel.ALL) {
mFullConfiguration.dumpDebug(proto, FULL_CONFIGURATION, false /* critical */);
mMergedOverrideConfiguration.dumpDebug(proto, MERGED_OVERRIDE_CONFIGURATION,
false /* critical */);
}
+
+ if (logLevel == WindowTraceLogLevel.TRIM) {
+ // Required for Fass to automatically detect pip transitions in Winscope traces
+ dumpDebugWindowingMode(proto);
+ }
+
proto.end(token);
}
+ private void dumpDebugWindowingMode(ProtoOutputStream proto) {
+ final long fullConfigToken = proto.start(FULL_CONFIGURATION);
+ final long windowConfigToken = proto.start(WINDOW_CONFIGURATION);
+
+ int windowingMode = mFullConfiguration.windowConfiguration.getWindowingMode();
+ proto.write(WINDOWING_MODE, windowingMode);
+
+ proto.end(windowConfigToken);
+ proto.end(fullConfigToken);
+ }
+
/**
* Dumps the names of this container children in the input print writer indenting each
* level with the input prefix.
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index 5f03f89..e14d30b 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -91,6 +91,7 @@
import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_LAYER_MIRRORING;
import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_ORIENTATION;
import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_SCREEN_ON;
+import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_WALLPAPER;
import static com.android.internal.protolog.ProtoLogGroup.WM_SHOW_TRANSACTIONS;
import static com.android.server.policy.WindowManagerPolicy.FINISH_LAYOUT_REDO_ANIM;
import static com.android.server.policy.WindowManagerPolicy.FINISH_LAYOUT_REDO_CONFIG;
@@ -127,7 +128,6 @@
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_LAYOUT;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_LAYOUT_REPEATS;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_SCREENSHOT;
-import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_WALLPAPER_LIGHT;
import static com.android.server.wm.WindowManagerDebugConfig.SHOW_STACK_CRAWLS;
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
@@ -1003,8 +1003,8 @@
final boolean committed = winAnimator.commitFinishDrawingLocked();
if (isDefaultDisplay && committed) {
if (w.hasWallpaper()) {
- if (DEBUG_WALLPAPER_LIGHT) Slog.v(TAG,
- "First draw done in potential wallpaper target " + w);
+ ProtoLog.v(WM_DEBUG_WALLPAPER,
+ "First draw done in potential wallpaper target %s", w);
mWallpaperMayChange = true;
pendingLayoutChanges |= FINISH_LAYOUT_REDO_WALLPAPER;
if (DEBUG_LAYOUT_REPEATS) {
@@ -3099,6 +3099,7 @@
mOverlayLayer.release();
mInputMonitor.onDisplayRemoved();
mWmService.mDisplayNotificationController.dispatchDisplayRemoved(this);
+ mWmService.mAccessibilityController.onDisplayRemoved(mDisplayId);
} finally {
mDisplayReady = false;
}
@@ -3804,7 +3805,11 @@
}
boolean shouldImeAttachedToApp() {
- return isImeControlledByApp()
+ // Force attaching IME to the display when magnifying, or it would be magnified with
+ // target app together.
+ final boolean allowAttachToApp = (mMagnificationSpec == null);
+
+ return allowAttachToApp && isImeControlledByApp()
&& mImeLayeringTarget != null
&& mImeLayeringTarget.mActivityRecord != null
&& mImeLayeringTarget.getWindowingMode() == WINDOWING_MODE_FULLSCREEN
@@ -4143,14 +4148,10 @@
*/
@VisibleForTesting
SurfaceControl computeImeParent() {
- // Force attaching IME to the display when magnifying, or it would be magnified with
- // target app together.
- final boolean allowAttachToApp = (mMagnificationSpec == null);
-
// Attach it to app if the target is part of an app and such app is covering the entire
// screen. If it's not covering the entire screen the IME might extend beyond the apps
// bounds.
- if (allowAttachToApp && shouldImeAttachedToApp()) {
+ if (shouldImeAttachedToApp()) {
if (mImeLayeringTarget.mActivityRecord != mImeInputTarget.mActivityRecord) {
// Do not change parent if the window hasn't requested IME.
return null;
@@ -5155,9 +5156,7 @@
onAppTransitionDone();
changes |= FINISH_LAYOUT_REDO_LAYOUT;
- if (DEBUG_WALLPAPER_LIGHT) {
- Slog.v(TAG_WM, "Wallpaper layer changed: assigning layers + relayout");
- }
+ ProtoLog.v(WM_DEBUG_WALLPAPER, "Wallpaper layer changed: assigning layers + relayout");
computeImeTarget(true /* updateImeTarget */);
mWallpaperMayChange = true;
// Since the window list has been rebuilt, focus might have to be recomputed since the
diff --git a/services/core/java/com/android/server/wm/EmbeddedWindowController.java b/services/core/java/com/android/server/wm/EmbeddedWindowController.java
index b08d6e1d..fc317a1 100644
--- a/services/core/java/com/android/server/wm/EmbeddedWindowController.java
+++ b/services/core/java/com/android/server/wm/EmbeddedWindowController.java
@@ -127,7 +127,7 @@
}
}
- static class EmbeddedWindow {
+ static class EmbeddedWindow implements InputTarget {
final IWindow mClient;
@Nullable final WindowState mHostWindowState;
@Nullable final ActivityRecord mHostActivityRecord;
@@ -166,7 +166,8 @@
mDisplayId = displayId;
}
- String getName() {
+ @Override
+ public String toString() {
final String hostWindowName = (mHostWindowState != null)
? mHostWindowState.getWindowTag().toString() : "Internal";
return "EmbeddedWindow{ u" + UserHandle.getUserId(mOwnerUid) + " " + hostWindowName
@@ -183,7 +184,7 @@
}
InputChannel openInputChannel() {
- final String name = getName();
+ final String name = toString();
mInputChannel = mWmService.mInputManager.createInputChannel(name);
return mInputChannel;
}
@@ -195,5 +196,25 @@
mInputChannel = null;
}
}
+
+ @Override
+ public WindowState getWindowState() {
+ return mHostWindowState;
+ }
+
+ @Override
+ public int getDisplayId() {
+ return mDisplayId;
+ }
+
+ @Override
+ public IWindow getIWindow() {
+ return mClient;
+ }
+
+ @Override
+ public int getPid() {
+ return mOwnerPid;
+ }
}
}
diff --git a/services/core/java/com/android/server/wm/InputTarget.java b/services/core/java/com/android/server/wm/InputTarget.java
new file mode 100644
index 0000000..c7d328a
--- /dev/null
+++ b/services/core/java/com/android/server/wm/InputTarget.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.wm;
+
+import android.view.IWindow;
+
+/**
+ * Common interface between focusable objects.
+ *
+ * Both WindowState and EmbeddedWindows can receive input. This consolidates some common properties
+ * of both targets.
+ */
+interface InputTarget {
+ /* Get the WindowState associated with the target. */
+ WindowState getWindowState();
+
+ /* Display id of the target. */
+ int getDisplayId();
+
+ /* Client IWindow for the target. */
+ IWindow getIWindow();
+
+ /* Owning pid of the target. */
+ int getPid();
+}
+
diff --git a/services/core/java/com/android/server/wm/KeyguardController.java b/services/core/java/com/android/server/wm/KeyguardController.java
index c630e91..bd41de3 100644
--- a/services/core/java/com/android/server/wm/KeyguardController.java
+++ b/services/core/java/com/android/server/wm/KeyguardController.java
@@ -194,8 +194,7 @@
if (keyguardChanged) {
// Irrelevant to AOD.
- dismissMultiWindowModeForTaskIfNeeded(null /* currentTaskControllsingOcclusion */,
- false /* turningScreenOn */);
+ dismissMultiWindowModeForTaskIfNeeded(null /* currentTaskControllsingOcclusion */);
mKeyguardGoingAway = false;
if (keyguardShowing) {
mDismissalRequested = false;
@@ -396,6 +395,8 @@
mService.continueWindowLayout();
}
}
+ dismissMultiWindowModeForTaskIfNeeded(topActivity != null
+ ? topActivity.getRootTask() : null);
}
/**
@@ -421,21 +422,6 @@
}
}
- /**
- * Called when somebody wants to turn screen on.
- */
- private void handleTurnScreenOn(int displayId) {
- if (displayId != DEFAULT_DISPLAY) {
- return;
- }
-
- mTaskSupervisor.wakeUp("handleTurnScreenOn");
- if (mKeyguardShowing && canDismissKeyguard()) {
- mWindowManager.dismissKeyguard(null /* callback */, null /* message */);
- mDismissalRequested = true;
- }
- }
-
boolean isDisplayOccluded(int displayId) {
return getDisplayState(displayId).mOccluded;
}
@@ -449,11 +435,9 @@
}
private void dismissMultiWindowModeForTaskIfNeeded(
- @Nullable Task currentTaskControllingOcclusion, boolean turningScreenOn) {
- // If turningScreenOn is true, it means that the visibility state has changed from
- // currentTaskControllingOcclusion and we should update windowing mode.
+ @Nullable Task currentTaskControllingOcclusion) {
// TODO(b/113840485): Handle docked stack for individual display.
- if (!turningScreenOn && (!mKeyguardShowing || !isDisplayOccluded(DEFAULT_DISPLAY))) {
+ if (!mKeyguardShowing || !isDisplayOccluded(DEFAULT_DISPLAY)) {
return;
}
@@ -592,26 +576,17 @@
&& controller.mWindowManager.isKeyguardSecure(
controller.mService.getCurrentUserId());
- boolean occludingChange = false;
- boolean turningScreenOn = false;
if (mTopTurnScreenOnActivity != lastTurnScreenOnActivity
&& mTopTurnScreenOnActivity != null
&& !mService.mWindowManager.mPowerManager.isInteractive()
- && (mRequestDismissKeyguard || occludedByActivity
- || controller.canDismissKeyguard())) {
- turningScreenOn = true;
- controller.handleTurnScreenOn(mDisplayId);
+ && (mRequestDismissKeyguard || occludedByActivity)) {
+ controller.mTaskSupervisor.wakeUp("handleTurnScreenOn");
mTopTurnScreenOnActivity.setCurrentLaunchCanTurnScreenOn(false);
}
if (lastOccluded != mOccluded) {
- occludingChange = true;
controller.handleOccludedChanged(mDisplayId, mTopOccludesActivity);
}
-
- if (occludingChange || turningScreenOn) {
- controller.dismissMultiWindowModeForTaskIfNeeded(task, turningScreenOn);
- }
}
/**
diff --git a/services/core/java/com/android/server/wm/RecentsAnimationController.java b/services/core/java/com/android/server/wm/RecentsAnimationController.java
index a663c62..22c8459 100644
--- a/services/core/java/com/android/server/wm/RecentsAnimationController.java
+++ b/services/core/java/com/android/server/wm/RecentsAnimationController.java
@@ -793,7 +793,7 @@
private RemoteAnimationTarget[] createWallpaperAnimations() {
ProtoLog.d(WM_DEBUG_RECENTS_ANIMATIONS, "createWallpaperAnimations()");
- return WallpaperAnimationAdapter.startWallpaperAnimations(mService, 0L, 0L,
+ return WallpaperAnimationAdapter.startWallpaperAnimations(mDisplayContent, 0L, 0L,
adapter -> {
synchronized (mService.mGlobalLock) {
// If the wallpaper animation is canceled, continue with the recents
diff --git a/services/core/java/com/android/server/wm/RemoteAnimationController.java b/services/core/java/com/android/server/wm/RemoteAnimationController.java
index 16a45fe..ca1aed5 100644
--- a/services/core/java/com/android/server/wm/RemoteAnimationController.java
+++ b/services/core/java/com/android/server/wm/RemoteAnimationController.java
@@ -207,7 +207,7 @@
if (wrappers.mThumbnailAdapter != null
&& wrappers.mThumbnailAdapter.mCapturedFinishCallback != null) {
wrappers.mThumbnailAdapter.mCapturedFinishCallback
- .onAnimationFinished(wrappers.mAdapter.mAnimationType,
+ .onAnimationFinished(wrappers.mThumbnailAdapter.mAnimationType,
wrappers.mThumbnailAdapter);
}
mPendingAnimations.remove(i);
@@ -218,7 +218,7 @@
private RemoteAnimationTarget[] createWallpaperAnimations() {
ProtoLog.d(WM_DEBUG_REMOTE_ANIMATIONS, "createWallpaperAnimations()");
- return WallpaperAnimationAdapter.startWallpaperAnimations(mService,
+ return WallpaperAnimationAdapter.startWallpaperAnimations(mDisplayContent,
mRemoteAnimationAdapter.getDuration(),
mRemoteAnimationAdapter.getStatusBarTransitionDelay(),
adapter -> {
@@ -260,7 +260,7 @@
}
if (adapters.mThumbnailAdapter != null) {
adapters.mThumbnailAdapter.mCapturedFinishCallback
- .onAnimationFinished(adapters.mAdapter.mAnimationType,
+ .onAnimationFinished(adapters.mThumbnailAdapter.mAnimationType,
adapters.mThumbnailAdapter);
}
mPendingAnimations.remove(i);
diff --git a/services/core/java/com/android/server/wm/RootWindowContainer.java b/services/core/java/com/android/server/wm/RootWindowContainer.java
index 97ea41c..0f7c649 100644
--- a/services/core/java/com/android/server/wm/RootWindowContainer.java
+++ b/services/core/java/com/android/server/wm/RootWindowContainer.java
@@ -48,6 +48,7 @@
import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_ORIENTATION;
import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_STATES;
import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_TASKS;
+import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_WALLPAPER;
import static com.android.internal.protolog.ProtoLogGroup.WM_SHOW_SURFACE_ALLOC;
import static com.android.internal.protolog.ProtoLogGroup.WM_SHOW_TRANSACTIONS;
import static com.android.server.policy.PhoneWindowManager.SYSTEM_DIALOG_REASON_ASSIST;
@@ -79,7 +80,6 @@
import static com.android.server.wm.Task.REPARENT_MOVE_ROOT_TASK_TO_FRONT;
import static com.android.server.wm.TaskFragment.TASK_FRAGMENT_VISIBILITY_INVISIBLE;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_LAYOUT_REPEATS;
-import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_WALLPAPER_LIGHT;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_WINDOW_TRACE;
import static com.android.server.wm.WindowManagerDebugConfig.SHOW_LIGHT_TRANSACTIONS;
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
@@ -504,6 +504,7 @@
mTopFocusedDisplayId = topFocusedDisplayId;
mWmService.mInputManager.setFocusedDisplay(topFocusedDisplayId);
mWmService.mPolicy.setTopFocusedDisplay(topFocusedDisplayId);
+ mWmService.mAccessibilityController.setFocusedDisplay(topFocusedDisplayId);
ProtoLog.d(WM_DEBUG_FOCUS_LIGHT, "New topFocusedDisplayId=%d", topFocusedDisplayId);
}
return changed;
@@ -893,7 +894,7 @@
for (int displayNdx = 0; displayNdx < mChildren.size(); ++displayNdx) {
final DisplayContent displayContent = mChildren.get(displayNdx);
if (displayContent.mWallpaperMayChange) {
- if (DEBUG_WALLPAPER_LIGHT) Slog.v(TAG, "Wallpaper may change! Adjusting");
+ ProtoLog.v(WM_DEBUG_WALLPAPER, "Wallpaper may change! Adjusting");
displayContent.pendingLayoutChanges |= FINISH_LAYOUT_REDO_WALLPAPER;
if (DEBUG_LAYOUT_REPEATS) {
surfacePlacer.debugLayoutRepeats("WallpaperMayChange",
@@ -2150,6 +2151,9 @@
final Task rootTask;
if (singleActivity) {
rootTask = task;
+
+ // Apply the last recents animation leash transform to the task entering PIP
+ rootTask.maybeApplyLastRecentsAnimationTransaction();
} else {
// In the case of multiple activities, we will create a new task for it and then
// move the PIP activity into the task. Note that we explicitly defer the task
diff --git a/services/core/java/com/android/server/wm/SurfaceAnimator.java b/services/core/java/com/android/server/wm/SurfaceAnimator.java
index c7bf8ec..d712bbf 100644
--- a/services/core/java/com/android/server/wm/SurfaceAnimator.java
+++ b/services/core/java/com/android/server/wm/SurfaceAnimator.java
@@ -57,8 +57,11 @@
@VisibleForTesting
SurfaceControl mLeash;
@VisibleForTesting
+ SurfaceFreezer.Snapshot mSnapshot;
+ @VisibleForTesting
final Animatable mAnimatable;
- private final OnAnimationFinishedCallback mInnerAnimationFinishedCallback;
+ @VisibleForTesting
+ final OnAnimationFinishedCallback mInnerAnimationFinishedCallback;
/**
* Static callback to run on all animations started through this SurfaceAnimator
@@ -151,12 +154,14 @@
* @param animationFinishedCallback The callback being triggered when the animation finishes.
* @param animationCancelledCallback The callback is triggered after the SurfaceAnimator sends a
* cancel call to the underlying AnimationAdapter.
+ * @param snapshotAnim The animation to run for the snapshot. {@code null} if there is no
+ * snapshot.
*/
void startAnimation(Transaction t, AnimationAdapter anim, boolean hidden,
@AnimationType int type,
@Nullable OnAnimationFinishedCallback animationFinishedCallback,
@Nullable Runnable animationCancelledCallback,
- @Nullable SurfaceFreezer freezer) {
+ @Nullable AnimationAdapter snapshotAnim, @Nullable SurfaceFreezer freezer) {
cancelAnimation(t, true /* restarting */, true /* forwardCancel */);
mAnimation = anim;
mAnimationType = type;
@@ -181,12 +186,16 @@
return;
}
mAnimation.startAnimation(mLeash, t, type, mInnerAnimationFinishedCallback);
+ if (snapshotAnim != null) {
+ mSnapshot = freezer.takeSnapshotForAnimation();
+ mSnapshot.startAnimation(t, snapshotAnim, type);
+ }
}
void startAnimation(Transaction t, AnimationAdapter anim, boolean hidden,
@AnimationType int type) {
startAnimation(t, anim, hidden, type, null /* animationFinishedCallback */,
- null /* animationCancelledCallback */, null /* freezer */);
+ null /* animationCancelledCallback */, null /* snapshotAnim */, null /* freezer */);
}
/**
@@ -328,6 +337,7 @@
final OnAnimationFinishedCallback animationFinishedCallback =
mSurfaceAnimationFinishedCallback;
final Runnable animationCancelledCallback = mAnimationCancelledCallback;
+ final SurfaceFreezer.Snapshot snapshot = mSnapshot;
reset(t, false);
if (animation != null) {
if (!mAnimationStartDelayed && forwardCancel) {
@@ -346,9 +356,14 @@
}
}
- if (forwardCancel && leash != null) {
- t.remove(leash);
- mService.scheduleAnimationLocked();
+ if (forwardCancel) {
+ if (snapshot != null) {
+ snapshot.cancelAnimation(t, false /* restarting */);
+ }
+ if (leash != null) {
+ t.remove(leash);
+ mService.scheduleAnimationLocked();
+ }
}
if (!restarting) {
@@ -361,6 +376,12 @@
mAnimation = null;
mSurfaceAnimationFinishedCallback = null;
mAnimationType = ANIMATION_TYPE_NONE;
+ final SurfaceFreezer.Snapshot snapshot = mSnapshot;
+ mSnapshot = null;
+ if (snapshot != null) {
+ // Reset the mSnapshot reference before calling the callback to prevent circular reset.
+ snapshot.cancelAnimation(t, !destroyLeash);
+ }
if (mLeash == null) {
return;
}
@@ -377,11 +398,15 @@
boolean scheduleAnim = false;
final SurfaceControl surface = animatable.getSurfaceControl();
final SurfaceControl parent = animatable.getParentSurfaceControl();
+ final SurfaceControl curAnimationLeash = animatable.getAnimationLeash();
// If the surface was destroyed or the leash is invalid, we don't care to reparent it back.
// Note that we also set this variable to true even if the parent isn't valid anymore, in
// order to ensure onAnimationLeashLost still gets called in this case.
- final boolean reparent = surface != null;
+ // If the animation leash is set, and it is different from the removing leash, it means the
+ // surface now has a new animation surface. We don't want to reparent for that.
+ final boolean reparent = surface != null && (curAnimationLeash == null
+ || curAnimationLeash.equals(leash));
if (reparent) {
if (DEBUG_ANIM) Slog.i(TAG, "Reparenting to original parent: " + parent);
// We shouldn't really need these isValid checks but we do
@@ -608,6 +633,14 @@
void onAnimationLeashLost(Transaction t);
/**
+ * Gets the last created animation leash that has not lost yet.
+ */
+ @Nullable
+ default SurfaceControl getAnimationLeash() {
+ return null;
+ }
+
+ /**
* @return A new surface to be used for the animation leash, inserted at the correct
* position in the hierarchy.
*/
diff --git a/services/core/java/com/android/server/wm/SurfaceFreezer.java b/services/core/java/com/android/server/wm/SurfaceFreezer.java
index 89986ce..9a0cabe 100644
--- a/services/core/java/com/android/server/wm/SurfaceFreezer.java
+++ b/services/core/java/com/android/server/wm/SurfaceFreezer.java
@@ -17,7 +17,6 @@
package com.android.server.wm;
import static com.android.internal.protolog.ProtoLogGroup.WM_SHOW_TRANSACTIONS;
-import static com.android.server.wm.SurfaceAnimator.ANIMATION_TYPE_APP_TRANSITION;
import static com.android.server.wm.SurfaceAnimator.ANIMATION_TYPE_SCREEN_ROTATION;
import android.annotation.NonNull;
@@ -27,13 +26,11 @@
import android.graphics.Point;
import android.graphics.Rect;
import android.hardware.HardwareBuffer;
-import android.view.Surface;
import android.view.SurfaceControl;
+import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.protolog.common.ProtoLog;
-import java.util.function.Supplier;
-
/**
* This class handles "freezing" of an Animatable. The Animatable in question should implement
* Freezable.
@@ -54,7 +51,8 @@
private final Freezable mAnimatable;
private final WindowManagerService mWmService;
- private SurfaceControl mLeash;
+ @VisibleForTesting
+ SurfaceControl mLeash;
Snapshot mSnapshot = null;
final Rect mFreezeBounds = new Rect();
@@ -94,7 +92,7 @@
if (buffer == null || buffer.getWidth() <= 1 || buffer.getHeight() <= 1) {
return;
}
- mSnapshot = new Snapshot(mWmService.mSurfaceFactory, t, screenshotBuffer, mLeash);
+ mSnapshot = new Snapshot(t, screenshotBuffer, mLeash);
}
}
@@ -109,12 +107,25 @@
}
/**
+ * Used by {@link SurfaceAnimator}. This "transfers" the snapshot leash to be used for
+ * animation. By transferring the leash, this will no longer try to clean-up the leash when
+ * finished.
+ */
+ @Nullable
+ Snapshot takeSnapshotForAnimation() {
+ final Snapshot out = mSnapshot;
+ mSnapshot = null;
+ return out;
+ }
+
+ /**
* Clean-up the snapshot and remove leash. If the leash was taken, this just cleans-up the
* snapshot.
*/
void unfreeze(SurfaceControl.Transaction t) {
if (mSnapshot != null) {
mSnapshot.cancelAnimation(t, false /* restarting */);
+ mSnapshot = null;
}
if (mLeash == null) {
return;
@@ -163,13 +174,12 @@
class Snapshot {
private SurfaceControl mSurfaceControl;
private AnimationAdapter mAnimation;
- private SurfaceAnimator.OnAnimationFinishedCallback mFinishedCallback;
/**
* @param t Transaction to create the thumbnail in.
* @param screenshotBuffer A thumbnail or placeholder for thumbnail to initialize with.
*/
- Snapshot(Supplier<Surface> surfaceFactory, SurfaceControl.Transaction t,
+ Snapshot(SurfaceControl.Transaction t,
SurfaceControl.ScreenshotHardwareBuffer screenshotBuffer, SurfaceControl parent) {
// We can't use a delegating constructor since we need to
// reference this::onAnimationFinished
@@ -211,19 +221,15 @@
* component responsible for running the animation. It runs the animation with
* {@link AnimationAdapter#startAnimation} once the hierarchy with
* the Leash has been set up.
- * @param animationFinishedCallback The callback being triggered when the animation
- * finishes.
*/
- void startAnimation(SurfaceControl.Transaction t, AnimationAdapter anim, int type,
- @Nullable SurfaceAnimator.OnAnimationFinishedCallback animationFinishedCallback) {
+ void startAnimation(SurfaceControl.Transaction t, AnimationAdapter anim, int type) {
cancelAnimation(t, true /* restarting */);
mAnimation = anim;
- mFinishedCallback = animationFinishedCallback;
if (mSurfaceControl == null) {
cancelAnimation(t, false /* restarting */);
return;
}
- mAnimation.startAnimation(mSurfaceControl, t, type, animationFinishedCallback);
+ mAnimation.startAnimation(mSurfaceControl, t, type, null /* finishCallback */);
}
/**
@@ -235,18 +241,9 @@
void cancelAnimation(SurfaceControl.Transaction t, boolean restarting) {
final SurfaceControl leash = mSurfaceControl;
final AnimationAdapter animation = mAnimation;
- final SurfaceAnimator.OnAnimationFinishedCallback animationFinishedCallback =
- mFinishedCallback;
mAnimation = null;
- mFinishedCallback = null;
if (animation != null) {
animation.onAnimationCancelled(leash);
- if (!restarting) {
- if (animationFinishedCallback != null) {
- animationFinishedCallback.onAnimationFinished(
- ANIMATION_TYPE_APP_TRANSITION, animation);
- }
- }
}
if (!restarting) {
destroy(t);
diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java
index 594b6be..3cb80d6 100644
--- a/services/core/java/com/android/server/wm/Task.java
+++ b/services/core/java/com/android/server/wm/Task.java
@@ -2972,11 +2972,6 @@
return super.makeAnimationLeash().setMetadata(METADATA_TASK_ID, mTaskId);
}
- @Override
- void resetSurfacePositionForAnimationLeash(SurfaceControl.Transaction t) {
- super.resetSurfacePositionForAnimationLeash(t);
- }
-
boolean shouldAnimate() {
/**
* Animations are handled by the TaskOrganizer implementation.
diff --git a/services/core/java/com/android/server/wm/TaskDisplayArea.java b/services/core/java/com/android/server/wm/TaskDisplayArea.java
index 66f2dbc..71844ce 100644
--- a/services/core/java/com/android/server/wm/TaskDisplayArea.java
+++ b/services/core/java/com/android/server/wm/TaskDisplayArea.java
@@ -99,13 +99,6 @@
private int mColorLayerCounter = 0;
/**
- * A control placed at the appropriate level for transitions to occur.
- */
- private SurfaceControl mAppAnimationLayer;
- private SurfaceControl mBoostedAppAnimationLayer;
- private SurfaceControl mHomeAppAnimationLayer;
-
- /**
* Given that the split-screen divider does not have an AppWindowToken, it
* will have to live inside of a "NonAppWindowContainer". However, in visual Z order
* it will need to be interleaved with some of our children, appearing on top of
@@ -132,7 +125,6 @@
private final ArrayList<WindowContainer> mTmpNormalChildren = new ArrayList<>();
private final ArrayList<WindowContainer> mTmpHomeChildren = new ArrayList<>();
private final IntArray mTmpNeedsZBoostIndexes = new IntArray();
- private int mTmpLayerForAnimationLayer;
private ArrayList<Task> mTmpTasks = new ArrayList<>();
@@ -871,33 +863,13 @@
int layer = 0;
// Place root home tasks to the bottom.
- layer = adjustRootTaskLayer(t, mTmpHomeChildren, layer, false /* normalRootTasks */);
- // The home animation layer is between the root home tasks and the normal root tasks.
- final int layerForHomeAnimationLayer = layer++;
- mTmpLayerForAnimationLayer = layer++;
- layer = adjustRootTaskLayer(t, mTmpNormalChildren, layer, true /* normalRootTasks */);
+ layer = adjustRootTaskLayer(t, mTmpHomeChildren, layer);
+ adjustRootTaskLayer(t, mTmpNormalChildren, layer);
- // The boosted animation layer is between the normal root tasks and the always on top
- // root tasks.
- final int layerForBoostedAnimationLayer = layer++;
// Always on top tasks layer should higher than split divider layer so set it as start.
- layer = SPLIT_DIVIDER_LAYER + 1;
- adjustRootTaskLayer(t, mTmpAlwaysOnTopChildren, layer, false /* normalRootTasks */);
-
- t.setLayer(mHomeAppAnimationLayer, layerForHomeAnimationLayer);
- t.setLayer(mAppAnimationLayer, mTmpLayerForAnimationLayer);
t.setLayer(mSplitScreenDividerAnchor, SPLIT_DIVIDER_LAYER);
- t.setLayer(mBoostedAppAnimationLayer, layerForBoostedAnimationLayer);
- }
-
- private int adjustNormalRootTaskLayer(WindowContainer child, int layer) {
- if ((child.asTask() != null && child.asTask().isAnimatingByRecents())
- || child.isAppTransitioning()) {
- // The animation layer is located above the highest animating root task and no
- // higher.
- mTmpLayerForAnimationLayer = layer++;
- }
- return layer;
+ layer = SPLIT_DIVIDER_LAYER + 1;
+ adjustRootTaskLayer(t, mTmpAlwaysOnTopChildren, layer);
}
/**
@@ -906,11 +878,10 @@
* normal rootTasks.
*
* @param startLayer The beginning layer of this group of rootTasks.
- * @param normalRootTasks Set {@code true} if this group is neither home nor always on top.
* @return The adjusted layer value.
*/
private int adjustRootTaskLayer(SurfaceControl.Transaction t,
- ArrayList<WindowContainer> children, int startLayer, boolean normalRootTasks) {
+ ArrayList<WindowContainer> children, int startLayer) {
mTmpNeedsZBoostIndexes.clear();
final int childCount = children.size();
for (int i = 0; i < childCount; i++) {
@@ -923,9 +894,6 @@
if (!childNeedsZBoost) {
child.assignLayer(t, startLayer++);
- if (normalRootTasks) {
- startLayer = adjustNormalRootTaskLayer(child, startLayer);
- }
} else {
mTmpNeedsZBoostIndexes.add(i);
}
@@ -935,9 +903,6 @@
for (int i = 0; i < zBoostSize; i++) {
final WindowContainer child = children.get(mTmpNeedsZBoostIndexes.get(i));
child.assignLayer(t, startLayer++);
- if (normalRootTasks) {
- startLayer = adjustNormalRootTaskLayer(child, startLayer);
- }
}
return startLayer;
}
@@ -951,19 +916,6 @@
}
@Override
- SurfaceControl getAppAnimationLayer(@AnimationLayer int animationLayer) {
- switch (animationLayer) {
- case ANIMATION_LAYER_BOOSTED:
- return mBoostedAppAnimationLayer;
- case ANIMATION_LAYER_HOME:
- return mHomeAppAnimationLayer;
- case ANIMATION_LAYER_STANDARD:
- default:
- return mAppAnimationLayer;
- }
- }
-
- @Override
RemoteAnimationTarget createRemoteAnimationTarget(
RemoteAnimationController.RemoteAnimationRecord record) {
final ActivityRecord activity = getTopMostActivity();
@@ -983,42 +935,21 @@
.setName("colorBackgroundLayer")
.setCallsite("TaskDisplayArea.onParentChanged")
.build();
- mAppAnimationLayer = makeChildSurface(null)
- .setName("animationLayer")
- .setCallsite("TaskDisplayArea.onParentChanged")
- .build();
- mBoostedAppAnimationLayer = makeChildSurface(null)
- .setName("boostedAnimationLayer")
- .setCallsite("TaskDisplayArea.onParentChanged")
- .build();
- mHomeAppAnimationLayer = makeChildSurface(null)
- .setName("homeAnimationLayer")
- .setCallsite("TaskDisplayArea.onParentChanged")
- .build();
mSplitScreenDividerAnchor = makeChildSurface(null)
.setName("splitScreenDividerAnchor")
.setCallsite("TaskDisplayArea.onParentChanged")
.build();
getSyncTransaction()
- .show(mAppAnimationLayer)
- .show(mBoostedAppAnimationLayer)
- .show(mHomeAppAnimationLayer)
.show(mSplitScreenDividerAnchor);
});
} else {
super.onParentChanged(newParent, oldParent);
mWmService.mTransactionFactory.get()
.remove(mColorBackgroundLayer)
- .remove(mAppAnimationLayer)
- .remove(mBoostedAppAnimationLayer)
- .remove(mHomeAppAnimationLayer)
.remove(mSplitScreenDividerAnchor)
.apply();
mColorBackgroundLayer = null;
- mAppAnimationLayer = null;
- mBoostedAppAnimationLayer = null;
- mHomeAppAnimationLayer = null;
mSplitScreenDividerAnchor = null;
}
}
@@ -1059,15 +990,12 @@
@Override
void migrateToNewSurfaceControl(SurfaceControl.Transaction t) {
super.migrateToNewSurfaceControl(t);
- if (mAppAnimationLayer == null) {
+ if (mColorBackgroundLayer == null) {
return;
}
// As TaskDisplayArea is getting a new surface, reparent and reorder the child surfaces.
t.reparent(mColorBackgroundLayer, mSurfaceControl);
- t.reparent(mAppAnimationLayer, mSurfaceControl);
- t.reparent(mBoostedAppAnimationLayer, mSurfaceControl);
- t.reparent(mHomeAppAnimationLayer, mSurfaceControl);
t.reparent(mSplitScreenDividerAnchor, mSurfaceControl);
reassignLayer(t);
scheduleAnimation();
diff --git a/services/core/java/com/android/server/wm/TaskFragment.java b/services/core/java/com/android/server/wm/TaskFragment.java
index 44b22c6..99f6f34 100644
--- a/services/core/java/com/android/server/wm/TaskFragment.java
+++ b/services/core/java/com/android/server/wm/TaskFragment.java
@@ -1428,23 +1428,8 @@
+ "directly: %s", prev);
didAutoPip = mAtmService.enterPictureInPictureMode(prev, prev.pictureInPictureArgs);
- mPausingActivity = null;
} else {
- ProtoLog.v(WM_DEBUG_STATES, "Enqueueing pending pause: %s", prev);
- try {
- EventLogTags.writeWmPauseActivity(prev.mUserId, System.identityHashCode(prev),
- prev.shortComponentName, "userLeaving=" + userLeaving, reason);
-
- mAtmService.getLifecycleManager().scheduleTransaction(prev.app.getThread(),
- prev.appToken, PauseActivityItem.obtain(prev.finishing, userLeaving,
- prev.configChangeFlags, pauseImmediately));
- } catch (Exception e) {
- // Ignore exception, if process died other code will cleanup.
- Slog.w(TAG, "Exception thrown during pause", e);
- mPausingActivity = null;
- mLastPausedActivity = null;
- mTaskSupervisor.mNoHistoryActivities.remove(prev);
- }
+ schedulePauseActivity(prev, userLeaving, pauseImmediately, reason);
}
} else {
mPausingActivity = null;
@@ -1459,7 +1444,7 @@
}
// If already entered PIP mode, no need to keep pausing.
- if (mPausingActivity != null && !didAutoPip) {
+ if (mPausingActivity != null) {
// Have the window manager pause its key dispatching until the new
// activity has started. If we're pausing the activity just because
// the screen is being turned off and the UI is sleeping, don't interrupt
@@ -1494,6 +1479,25 @@
}
}
+ void schedulePauseActivity(ActivityRecord prev, boolean userLeaving,
+ boolean pauseImmediately, String reason) {
+ ProtoLog.v(WM_DEBUG_STATES, "Enqueueing pending pause: %s", prev);
+ try {
+ EventLogTags.writeWmPauseActivity(prev.mUserId, System.identityHashCode(prev),
+ prev.shortComponentName, "userLeaving=" + userLeaving, reason);
+
+ mAtmService.getLifecycleManager().scheduleTransaction(prev.app.getThread(),
+ prev.appToken, PauseActivityItem.obtain(prev.finishing, userLeaving,
+ prev.configChangeFlags, pauseImmediately));
+ } catch (Exception e) {
+ // Ignore exception, if process died other code will cleanup.
+ Slog.w(TAG, "Exception thrown during pause", e);
+ mPausingActivity = null;
+ mLastPausedActivity = null;
+ mTaskSupervisor.mNoHistoryActivities.remove(prev);
+ }
+ }
+
@VisibleForTesting
void completePause(boolean resumeNext, ActivityRecord resuming) {
// Complete the pausing process of a pausing activity, so it doesn't make sense to
diff --git a/services/core/java/com/android/server/wm/TaskLaunchParamsModifier.java b/services/core/java/com/android/server/wm/TaskLaunchParamsModifier.java
index 9cc24e2..e31a662 100644
--- a/services/core/java/com/android/server/wm/TaskLaunchParamsModifier.java
+++ b/services/core/java/com/android/server/wm/TaskLaunchParamsModifier.java
@@ -261,8 +261,7 @@
if (launchMode == WINDOWING_MODE_PINNED) {
if (DEBUG) appendLog("picture-in-picture");
} else if (!root.isResizeable()) {
- if (shouldLaunchUnresizableAppInFreeform(root, suggestedDisplayArea,
- options.getLaunchWindowingMode())) {
+ if (shouldLaunchUnresizableAppInFreeform(root, suggestedDisplayArea, options)) {
launchMode = WINDOWING_MODE_FREEFORM;
if (outParams.mBounds.isEmpty()) {
getTaskBounds(root, suggestedDisplayArea, layout, launchMode,
@@ -618,8 +617,8 @@
}
private boolean shouldLaunchUnresizableAppInFreeform(ActivityRecord activity,
- TaskDisplayArea displayArea, int launchWindowingMode) {
- if (launchWindowingMode == WINDOWING_MODE_FULLSCREEN) {
+ TaskDisplayArea displayArea, @Nullable ActivityOptions options) {
+ if (options != null && options.getLaunchWindowingMode() == WINDOWING_MODE_FULLSCREEN) {
// Do not launch the activity in freeform if it explicitly requested fullscreen mode.
return false;
}
diff --git a/services/core/java/com/android/server/wm/TaskSnapshotController.java b/services/core/java/com/android/server/wm/TaskSnapshotController.java
index 5ce9938..ce93f24 100644
--- a/services/core/java/com/android/server/wm/TaskSnapshotController.java
+++ b/services/core/java/com/android/server/wm/TaskSnapshotController.java
@@ -178,43 +178,46 @@
snapshotTasks(tasks, false /* allowSnapshotHome */);
}
+ void recordTaskSnapshot(Task task, boolean allowSnapshotHome) {
+ final TaskSnapshot snapshot;
+ final boolean snapshotHome = allowSnapshotHome && task.isActivityTypeHome();
+ if (snapshotHome) {
+ snapshot = snapshotTask(task);
+ } else {
+ switch (getSnapshotMode(task)) {
+ case SNAPSHOT_MODE_NONE:
+ return;
+ case SNAPSHOT_MODE_APP_THEME:
+ snapshot = drawAppThemeSnapshot(task);
+ break;
+ case SNAPSHOT_MODE_REAL:
+ snapshot = snapshotTask(task);
+ break;
+ default:
+ snapshot = null;
+ break;
+ }
+ }
+ if (snapshot != null) {
+ final HardwareBuffer buffer = snapshot.getHardwareBuffer();
+ if (buffer.getWidth() == 0 || buffer.getHeight() == 0) {
+ buffer.close();
+ Slog.e(TAG, "Invalid task snapshot dimensions " + buffer.getWidth() + "x"
+ + buffer.getHeight());
+ } else {
+ mCache.putSnapshot(task, snapshot);
+ // Don't persist or notify the change for the temporal snapshot.
+ if (!snapshotHome) {
+ mPersister.persistSnapshot(task.mTaskId, task.mUserId, snapshot);
+ task.onSnapshotChanged(snapshot);
+ }
+ }
+ }
+ }
+
private void snapshotTasks(ArraySet<Task> tasks, boolean allowSnapshotHome) {
for (int i = tasks.size() - 1; i >= 0; i--) {
- final Task task = tasks.valueAt(i);
- final TaskSnapshot snapshot;
- final boolean snapshotHome = allowSnapshotHome && task.isActivityTypeHome();
- if (snapshotHome) {
- snapshot = snapshotTask(task);
- } else {
- switch (getSnapshotMode(task)) {
- case SNAPSHOT_MODE_NONE:
- continue;
- case SNAPSHOT_MODE_APP_THEME:
- snapshot = drawAppThemeSnapshot(task);
- break;
- case SNAPSHOT_MODE_REAL:
- snapshot = snapshotTask(task);
- break;
- default:
- snapshot = null;
- break;
- }
- }
- if (snapshot != null) {
- final HardwareBuffer buffer = snapshot.getHardwareBuffer();
- if (buffer.getWidth() == 0 || buffer.getHeight() == 0) {
- buffer.close();
- Slog.e(TAG, "Invalid task snapshot dimensions " + buffer.getWidth() + "x"
- + buffer.getHeight());
- } else {
- mCache.putSnapshot(task, snapshot);
- // Don't persist or notify the change for the temporal snapshot.
- if (!snapshotHome) {
- mPersister.persistSnapshot(task.mTaskId, task.mUserId, snapshot);
- task.onSnapshotChanged(snapshot);
- }
- }
- }
+ recordTaskSnapshot(tasks.valueAt(i), allowSnapshotHome);
}
}
diff --git a/services/core/java/com/android/server/wm/Transition.java b/services/core/java/com/android/server/wm/Transition.java
index e50e8ef..e537c0a 100644
--- a/services/core/java/com/android/server/wm/Transition.java
+++ b/services/core/java/com/android/server/wm/Transition.java
@@ -415,7 +415,16 @@
if (commitVisibility) {
ProtoLog.v(ProtoLogGroup.WM_DEBUG_WINDOW_TRANSITIONS,
" Commit activity becoming invisible: %s", ar);
- ar.commitVisibility(false /* visible */, false /* performLayout */);
+ final Task task = ar.getTask();
+ if (task != null && !task.isVisibleRequested()
+ && mTransientLaunches != null) {
+ // If transition is transient, then snapshots are taken at end of
+ // transition.
+ mController.mTaskSnapshotController.recordTaskSnapshot(
+ task, false /* allowSnapshotHome */);
+ }
+ ar.commitVisibility(false /* visible */, false /* performLayout */,
+ true /* fromTransition */);
activitiesWentInvisible = true;
}
}
@@ -558,6 +567,19 @@
mVisibleAtTransitionEndTokens.add(wc.asWindowToken());
}
+ // Take task snapshots before the animation so that we can capture IME before it gets
+ // transferred. If transition is transient, IME won't be moved during the transition and
+ // the tasks are still live, so we take the snapshot at the end of the transition instead.
+ if (mTransientLaunches == null) {
+ for (int i = mParticipants.size() - 1; i >= 0; --i) {
+ final ActivityRecord ar = mParticipants.valueAt(i).asActivityRecord();
+ if (ar == null || ar.isVisibleRequested() || ar.getTask() == null
+ || ar.getTask().isVisibleRequested()) continue;
+ mController.mTaskSnapshotController.recordTaskSnapshot(
+ ar.getTask(), false /* allowSnapshotHome */);
+ }
+ }
+
mStartTransaction = transaction;
mFinishTransaction = mController.mAtm.mWindowManager.mTransactionFactory.get();
buildFinishTransaction(mFinishTransaction, info.getRootLeash());
diff --git a/services/core/java/com/android/server/wm/TransitionController.java b/services/core/java/com/android/server/wm/TransitionController.java
index fc54239..ff4cc3d 100644
--- a/services/core/java/com/android/server/wm/TransitionController.java
+++ b/services/core/java/com/android/server/wm/TransitionController.java
@@ -59,6 +59,7 @@
private ITransitionPlayer mTransitionPlayer;
final ActivityTaskManagerService mAtm;
+ final TaskSnapshotController mTaskSnapshotController;
private final ArrayList<WindowManagerInternal.AppTransitionListener> mLegacyListeners =
new ArrayList<>();
@@ -79,9 +80,11 @@
// TODO(b/188595497): remove when not needed.
final StatusBarManagerInternal mStatusBar;
- TransitionController(ActivityTaskManagerService atm) {
+ TransitionController(ActivityTaskManagerService atm,
+ TaskSnapshotController taskSnapshotController) {
mAtm = atm;
mStatusBar = LocalServices.getService(StatusBarManagerInternal.class);
+ mTaskSnapshotController = taskSnapshotController;
mTransitionPlayerDeath = () -> {
synchronized (mAtm.mGlobalLock) {
// Clean-up/finish any playing transitions.
diff --git a/services/core/java/com/android/server/wm/WallpaperAnimationAdapter.java b/services/core/java/com/android/server/wm/WallpaperAnimationAdapter.java
index 25f7269..0b20f37 100644
--- a/services/core/java/com/android/server/wm/WallpaperAnimationAdapter.java
+++ b/services/core/java/com/android/server/wm/WallpaperAnimationAdapter.java
@@ -64,18 +64,17 @@
*
* @return RemoteAnimationTarget[] targets for all the visible wallpaper windows
*/
- public static RemoteAnimationTarget[] startWallpaperAnimations(WindowManagerService service,
+ public static RemoteAnimationTarget[] startWallpaperAnimations(DisplayContent displayContent,
long durationHint, long statusBarTransitionDelay,
Consumer<WallpaperAnimationAdapter> animationCanceledRunnable,
ArrayList<WallpaperAnimationAdapter> adaptersOut) {
+ if (!displayContent.mWallpaperController.isWallpaperVisible()) {
+ ProtoLog.d(WM_DEBUG_REMOTE_ANIMATIONS,
+ "\tWallpaper of display=%s is not visible", displayContent);
+ return new RemoteAnimationTarget[0];
+ }
final ArrayList<RemoteAnimationTarget> targets = new ArrayList<>();
- service.mRoot.forAllWallpaperWindows(wallpaperWindow -> {
- if (!wallpaperWindow.getDisplayContent().mWallpaperController.isWallpaperVisible()) {
- ProtoLog.d(WM_DEBUG_REMOTE_ANIMATIONS, "\tNot visible=%s", wallpaperWindow);
- return;
- }
-
- ProtoLog.d(WM_DEBUG_REMOTE_ANIMATIONS, "\tvisible=%s", wallpaperWindow);
+ displayContent.forAllWallpaperWindows(wallpaperWindow -> {
final WallpaperAnimationAdapter wallpaperAdapter = new WallpaperAnimationAdapter(
wallpaperWindow, durationHint, statusBarTransitionDelay,
animationCanceledRunnable);
diff --git a/services/core/java/com/android/server/wm/WallpaperController.java b/services/core/java/com/android/server/wm/WallpaperController.java
index 0909462..a92e088 100644
--- a/services/core/java/com/android/server/wm/WallpaperController.java
+++ b/services/core/java/com/android/server/wm/WallpaperController.java
@@ -24,12 +24,12 @@
import static android.view.WindowManager.LayoutParams.TYPE_WALLPAPER;
import static android.view.WindowManager.TRANSIT_FLAG_KEYGUARD_GOING_AWAY_WITH_WALLPAPER;
+import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_WALLPAPER;
import static com.android.server.policy.WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER;
import static com.android.server.wm.WindowContainer.AnimationFlags.PARENTS;
import static com.android.server.wm.WindowContainer.AnimationFlags.TRANSITION;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_SCREENSHOT;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_WALLPAPER;
-import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_WALLPAPER_LIGHT;
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
import static com.android.server.wm.WindowManagerService.H.WALLPAPER_DRAW_PENDING_TIMEOUT;
@@ -49,6 +49,8 @@
import android.view.animation.Animation;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.protolog.ProtoLogImpl;
+import com.android.internal.protolog.common.ProtoLog;
import com.android.internal.util.ToBooleanFunction;
import java.io.PrintWriter;
@@ -291,10 +293,11 @@
for (int i = mWallpaperTokens.size() - 1; i >= 0; i--) {
final WallpaperWindowToken token = mWallpaperTokens.get(i);
token.setVisibility(false);
- if (DEBUG_WALLPAPER_LIGHT && token.isVisible()) {
- Slog.d(TAG, "Hiding wallpaper " + token
- + " from " + winGoingAway + " target=" + mWallpaperTarget + " prev="
- + mPrevWallpaperTarget + "\n" + Debug.getCallers(5, " "));
+ if (ProtoLogImpl.isEnabled(WM_DEBUG_WALLPAPER) && token.isVisible()) {
+ ProtoLog.d(WM_DEBUG_WALLPAPER,
+ "Hiding wallpaper %s from %s target=%s prev=%s callers=%s",
+ token, winGoingAway, mWallpaperTarget, mPrevWallpaperTarget,
+ Debug.getCallers(5));
}
}
}
@@ -544,15 +547,15 @@
// Is it time to stop animating?
if (!mPrevWallpaperTarget.isAnimatingLw()) {
- if (DEBUG_WALLPAPER_LIGHT) Slog.v(TAG, "No longer animating wallpaper targets!");
+ ProtoLog.v(WM_DEBUG_WALLPAPER, "No longer animating wallpaper targets!");
mPrevWallpaperTarget = null;
mWallpaperTarget = wallpaperTarget;
}
return;
}
- if (DEBUG_WALLPAPER_LIGHT) Slog.v(TAG,
- "New wallpaper target: " + wallpaperTarget + " prevTarget: " + mWallpaperTarget);
+ ProtoLog.v(WM_DEBUG_WALLPAPER, "New wallpaper target: %s prevTarget: %s caller=%s",
+ wallpaperTarget, mWallpaperTarget, Debug.getCallers(5));
mPrevWallpaperTarget = null;
@@ -570,8 +573,8 @@
// then we are in our super special mode!
boolean oldAnim = prevWallpaperTarget.isAnimatingLw();
boolean foundAnim = wallpaperTarget.isAnimatingLw();
- if (DEBUG_WALLPAPER_LIGHT) Slog.v(TAG,
- "New animation: " + foundAnim + " old animation: " + oldAnim);
+ ProtoLog.v(WM_DEBUG_WALLPAPER, "New animation: %s old animation: %s",
+ foundAnim, oldAnim);
if (!foundAnim || !oldAnim) {
return;
@@ -586,14 +589,14 @@
final boolean oldTargetHidden = prevWallpaperTarget.mActivityRecord != null
&& !prevWallpaperTarget.mActivityRecord.mVisibleRequested;
- if (DEBUG_WALLPAPER_LIGHT) Slog.v(TAG, "Animating wallpapers:" + " old: "
- + prevWallpaperTarget + " hidden=" + oldTargetHidden + " new: " + wallpaperTarget
- + " hidden=" + newTargetHidden);
+ ProtoLog.v(WM_DEBUG_WALLPAPER, "Animating wallpapers: "
+ + "old: %s hidden=%b new: %s hidden=%b",
+ prevWallpaperTarget, oldTargetHidden, wallpaperTarget, newTargetHidden);
mPrevWallpaperTarget = prevWallpaperTarget;
if (newTargetHidden && !oldTargetHidden) {
- if (DEBUG_WALLPAPER_LIGHT) Slog.v(TAG, "Old wallpaper still the target.");
+ ProtoLog.v(WM_DEBUG_WALLPAPER, "Old wallpaper still the target.");
// Use the old target if new target is hidden but old target
// is not. If they're both hidden, still use the new target.
mWallpaperTarget = prevWallpaperTarget;
@@ -661,8 +664,8 @@
/* x= */ 0, /* y= */ 0, /* z= */ 0, /* extras= */ null, /* sync= */ false);
}
- if (DEBUG_WALLPAPER_LIGHT) Slog.d(TAG, "New wallpaper: target=" + mWallpaperTarget
- + " prev=" + mPrevWallpaperTarget);
+ ProtoLog.d(WM_DEBUG_WALLPAPER, "New wallpaper: target=%s prev=%s",
+ mWallpaperTarget, mPrevWallpaperTarget);
}
boolean processWallpaperDrawPendingTimeout() {
diff --git a/services/core/java/com/android/server/wm/WallpaperWindowToken.java b/services/core/java/com/android/server/wm/WallpaperWindowToken.java
index 75c84c4..3a639f5 100644
--- a/services/core/java/com/android/server/wm/WallpaperWindowToken.java
+++ b/services/core/java/com/android/server/wm/WallpaperWindowToken.java
@@ -20,7 +20,7 @@
import static android.view.WindowManager.LayoutParams.TYPE_WALLPAPER;
import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_APP_TRANSITIONS;
-import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_WALLPAPER_LIGHT;
+import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_WALLPAPER;
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
@@ -28,7 +28,6 @@
import android.os.Bundle;
import android.os.IBinder;
import android.os.RemoteException;
-import android.util.Slog;
import android.view.DisplayInfo;
import android.view.ViewGroup;
import android.view.WindowManager;
@@ -107,8 +106,8 @@
void updateWallpaperWindows(boolean visible) {
if (isVisible() != visible) {
- if (DEBUG_WALLPAPER_LIGHT) Slog.d(TAG,
- "Wallpaper token " + token + " visible=" + visible);
+ ProtoLog.d(WM_DEBUG_WALLPAPER, "Wallpaper token %s visible=%b",
+ token, visible);
setVisibility(visible);
}
final WallpaperController wallpaperController = mDisplayContent.mWallpaperController;
diff --git a/services/core/java/com/android/server/wm/WindowContainer.java b/services/core/java/com/android/server/wm/WindowContainer.java
index 841783d..51ecce0 100644
--- a/services/core/java/com/android/server/wm/WindowContainer.java
+++ b/services/core/java/com/android/server/wm/WindowContainer.java
@@ -57,6 +57,7 @@
import static com.android.server.wm.WindowContainerProto.IDENTIFIER;
import static com.android.server.wm.WindowContainerProto.ORIENTATION;
import static com.android.server.wm.WindowContainerProto.SURFACE_ANIMATOR;
+import static com.android.server.wm.WindowContainerProto.SURFACE_CONTROL;
import static com.android.server.wm.WindowContainerProto.VISIBLE;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_ANIM;
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
@@ -70,7 +71,6 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.ActivityThread;
-import android.app.WindowConfiguration;
import android.content.Context;
import android.content.pm.ActivityInfo;
import android.content.res.Configuration;
@@ -111,6 +111,7 @@
import java.util.Comparator;
import java.util.LinkedList;
import java.util.List;
+import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.BiFunction;
import java.util.function.Consumer;
import java.util.function.Function;
@@ -127,26 +128,6 @@
private static final String TAG = TAG_WITH_CLASS_NAME ? "WindowContainer" : TAG_WM;
- /** Animation layer that happens above all animating {@link Task}s. */
- static final int ANIMATION_LAYER_STANDARD = 0;
-
- /** Animation layer that happens above all {@link Task}s. */
- static final int ANIMATION_LAYER_BOOSTED = 1;
-
- /**
- * Animation layer that is reserved for {@link WindowConfiguration#ACTIVITY_TYPE_HOME}
- * activities and all activities that are being controlled by the recents animation. This
- * layer is generally below all {@link Task}s.
- */
- static final int ANIMATION_LAYER_HOME = 2;
-
- @IntDef(prefix = { "ANIMATION_LAYER_" }, value = {
- ANIMATION_LAYER_STANDARD,
- ANIMATION_LAYER_BOOSTED,
- ANIMATION_LAYER_HOME,
- })
- @interface AnimationLayer {}
-
static final int POSITION_TOP = Integer.MAX_VALUE;
static final int POSITION_BOTTOM = Integer.MIN_VALUE;
@@ -198,6 +179,10 @@
*/
protected final SurfaceAnimator mSurfaceAnimator;
+ /** The parent leash added for animation. */
+ @Nullable
+ private SurfaceControl mAnimationLeash;
+
final SurfaceFreezer mSurfaceFreezer;
protected final WindowManagerService mWmService;
final TransitionController mTransitionController;
@@ -2429,6 +2414,9 @@
if (mSurfaceAnimator.isAnimating()) {
mSurfaceAnimator.dumpDebug(proto, SURFACE_ANIMATOR);
}
+ if (mSurfaceControl != null) {
+ mSurfaceControl.dumpDebug(proto, SURFACE_CONTROL);
+ }
// add children to proto
for (int i = 0; i < getChildCount(); i++) {
@@ -2578,11 +2566,14 @@
* @param animationFinishedCallback The callback being triggered when the animation finishes.
* @param animationCancelledCallback The callback is triggered after the SurfaceAnimator sends a
* cancel call to the underlying AnimationAdapter.
+ * @param snapshotAnim The animation to run for the snapshot. {@code null} if there is no
+ * snapshot.
*/
void startAnimation(Transaction t, AnimationAdapter anim, boolean hidden,
@AnimationType int type,
@Nullable OnAnimationFinishedCallback animationFinishedCallback,
- @Nullable Runnable animationCancelledCallback) {
+ @Nullable Runnable animationCancelledCallback,
+ @Nullable AnimationAdapter snapshotAnim) {
if (DEBUG_ANIM) {
Slog.v(TAG, "Starting animation on " + this + ": type=" + type + ", anim=" + anim);
}
@@ -2590,14 +2581,14 @@
// TODO: This should use isVisible() but because isVisible has a really weird meaning at
// the moment this doesn't work for all animatable window containers.
mSurfaceAnimator.startAnimation(t, anim, hidden, type, animationFinishedCallback,
- animationCancelledCallback, mSurfaceFreezer);
+ animationCancelledCallback, snapshotAnim, mSurfaceFreezer);
}
void startAnimation(Transaction t, AnimationAdapter anim, boolean hidden,
@AnimationType int type,
@Nullable OnAnimationFinishedCallback animationFinishedCallback) {
startAnimation(t, anim, hidden, type, animationFinishedCallback,
- null /* adapterAnimationCancelledCallback */);
+ null /* adapterAnimationCancelledCallback */, null /* snapshotAnim */);
}
void startAnimation(Transaction t, AnimationAdapter anim, boolean hidden,
@@ -2668,17 +2659,6 @@
return getParentSurfaceControl();
}
- /**
- * @return The layer on which all app animations are happening.
- */
- SurfaceControl getAppAnimationLayer(@AnimationLayer int animationLayer) {
- final WindowContainer parent = getParent();
- if (parent != null) {
- return parent.getAppAnimationLayer(animationLayer);
- }
- return null;
- }
-
// TODO: Remove this and use #getBounds() instead once we set an app transition animation
// on TaskStack.
Rect getAnimationBounds(int appRootTaskClipMode) {
@@ -2856,21 +2836,26 @@
taskDisplayArea.setBackgroundColor(backgroundColor);
}
+ // Atomic counter to make sure the clearColor callback is only called one.
+ // It will be called twice in the case we cancel the animation without restart
+ // (in that case it will run as the cancel and finished callbacks).
+ final AtomicInteger callbackCounter = new AtomicInteger(0);
+ final Runnable clearBackgroundColorHandler = () -> {
+ if (callbackCounter.getAndIncrement() == 0) {
+ taskDisplayArea.clearBackgroundColor();
+ }
+ };
+
final Runnable cleanUpCallback = isSettingBackgroundColor
- ? taskDisplayArea::clearBackgroundColor : () -> {};
+ ? clearBackgroundColorHandler : () -> {};
startAnimation(getPendingTransaction(), adapter, !isVisible(),
- ANIMATION_TYPE_APP_TRANSITION,
- (type, anim) -> cleanUpCallback.run(),
- cleanUpCallback);
+ ANIMATION_TYPE_APP_TRANSITION, (type, anim) -> cleanUpCallback.run(),
+ cleanUpCallback, thumbnailAdapter);
if (adapter.getShowWallpaper()) {
getDisplayContent().pendingLayoutChanges |= FINISH_LAYOUT_REDO_WALLPAPER;
}
- if (thumbnailAdapter != null) {
- mSurfaceFreezer.mSnapshot.startAnimation(getPendingTransaction(),
- thumbnailAdapter, ANIMATION_TYPE_APP_TRANSITION, (type, anim) -> { });
- }
}
}
@@ -3000,6 +2985,7 @@
@Override
public void onAnimationLeashCreated(Transaction t, SurfaceControl leash) {
mLastLayer = -1;
+ mAnimationLeash = leash;
reassignLayer(t);
// Leash is now responsible for position, so set our position to 0.
@@ -3009,11 +2995,16 @@
@Override
public void onAnimationLeashLost(Transaction t) {
mLastLayer = -1;
- mSurfaceFreezer.unfreeze(t);
+ mAnimationLeash = null;
reassignLayer(t);
updateSurfacePosition(t);
}
+ @Override
+ public SurfaceControl getAnimationLeash() {
+ return mAnimationLeash;
+ }
+
private void doAnimationFinished(@AnimationType int type, AnimationAdapter anim) {
for (int i = 0; i < mSurfaceAnimationSources.size(); ++i) {
mSurfaceAnimationSources.valueAt(i).onAnimationFinished(type, anim);
diff --git a/services/core/java/com/android/server/wm/WindowManagerDebugConfig.java b/services/core/java/com/android/server/wm/WindowManagerDebugConfig.java
index 0840441..c954700 100644
--- a/services/core/java/com/android/server/wm/WindowManagerDebugConfig.java
+++ b/services/core/java/com/android/server/wm/WindowManagerDebugConfig.java
@@ -43,7 +43,6 @@
static final boolean DEBUG_CONFIGURATION = false;
static final boolean DEBUG_STARTING_WINDOW_VERBOSE = false;
static final boolean DEBUG_WALLPAPER = false;
- static final boolean DEBUG_WALLPAPER_LIGHT = false || DEBUG_WALLPAPER;
static final boolean DEBUG_DRAG = true;
static final boolean DEBUG_SCREENSHOT = false;
static final boolean DEBUG_LAYOUT_REPEATS = false;
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 4cf8c97..776a65d 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -4981,23 +4981,49 @@
return Surface.ROTATION_0;
}
- void reportFocusChanged(IBinder oldToken, IBinder newToken) {
- WindowState lastFocus;
- WindowState newFocus;
- synchronized (mGlobalLock) {
- lastFocus = mInputToWindowMap.get(oldToken);
- newFocus = mInputToWindowMap.get(newToken);
- ProtoLog.i(WM_DEBUG_FOCUS_LIGHT, "Focus changing: %s -> %s", lastFocus, newFocus);
+ // Returns an input target which is mapped to the given input token. This can be a WindowState
+ // or an embedded window.
+ @Nullable InputTarget getInputTargetFromToken(IBinder inputToken) {
+ WindowState windowState = mInputToWindowMap.get(inputToken);
+ if (windowState != null) {
+ return windowState;
}
- if (newFocus != null) {
- mAnrController.onFocusChanged(newFocus);
- newFocus.reportFocusChangedSerialized(true);
+ EmbeddedWindowController.EmbeddedWindow embeddedWindow =
+ mEmbeddedWindowController.get(inputToken);
+ if (embeddedWindow != null) {
+ return embeddedWindow;
+ }
+
+ return null;
+ }
+
+ void reportFocusChanged(IBinder oldToken, IBinder newToken) {
+ InputTarget lastTarget;
+ InputTarget newTarget;
+ synchronized (mGlobalLock) {
+ lastTarget = getInputTargetFromToken(oldToken);
+ newTarget = getInputTargetFromToken(newToken);
+ if (newTarget == null && lastTarget == null) {
+ Slog.v(TAG_WM, "Unknown focus tokens, dropping reportFocusChanged");
+ return;
+ }
+
+ mAccessibilityController.onFocusChanged(lastTarget, newTarget);
+ ProtoLog.i(WM_DEBUG_FOCUS_LIGHT, "Focus changing: %s -> %s", lastTarget, newTarget);
+ }
+
+ // Call WindowState focus change observers
+ WindowState newFocusedWindow = newTarget != null ? newTarget.getWindowState() : null;
+ if (newFocusedWindow != null && newFocusedWindow.mInputChannelToken == newToken) {
+ mAnrController.onFocusChanged(newFocusedWindow);
+ newFocusedWindow.reportFocusChangedSerialized(true);
notifyFocusChanged();
}
- if (lastFocus != null) {
- lastFocus.reportFocusChangedSerialized(false);
+ WindowState lastFocusedWindow = lastTarget != null ? lastTarget.getWindowState() : null;
+ if (lastFocusedWindow != null && lastFocusedWindow.mInputChannelToken == oldToken) {
+ lastFocusedWindow.reportFocusChangedSerialized(false);
}
}
@@ -7522,11 +7548,7 @@
@Override
public IBinder getFocusedWindowToken() {
synchronized (mGlobalLock) {
- WindowState windowState = getFocusedWindowLocked();
- if (windowState != null) {
- return windowState.mClient.asBinder();
- }
- return null;
+ return mAccessibilityController.getFocusedWindowToken();
}
}
@@ -8267,7 +8289,7 @@
clientChannel = win.openInputChannel();
mEmbeddedWindowController.add(clientChannel.getToken(), win);
applicationHandle = win.getApplicationHandle();
- name = win.getName();
+ name = win.toString();
}
updateInputChannel(clientChannel.getToken(), callingUid, callingPid, displayId, surface,
@@ -8333,7 +8355,7 @@
Slog.e(TAG, "Couldn't find window for provided channelToken.");
return;
}
- name = win.getName();
+ name = win.toString();
applicationHandle = win.getApplicationHandle();
}
@@ -8542,10 +8564,9 @@
SurfaceControl.Transaction t = mTransactionFactory.get();
final int displayId = embeddedWindow.mDisplayId;
if (grantFocus) {
- t.setFocusedWindow(inputToken, embeddedWindow.getName(), displayId).apply();
+ t.setFocusedWindow(inputToken, embeddedWindow.toString(), displayId).apply();
EventLog.writeEvent(LOGTAG_INPUT_FOCUS,
- "Focus request " + embeddedWindow.getName(),
- "reason=grantEmbeddedWindowFocus(true)");
+ "Focus request " + embeddedWindow, "reason=grantEmbeddedWindowFocus(true)");
} else {
// Search for a new focus target
DisplayContent displayContent = mRoot.getDisplayContent(displayId);
@@ -8554,18 +8575,18 @@
if (newFocusTarget == null) {
ProtoLog.v(WM_DEBUG_FOCUS, "grantEmbeddedWindowFocus remove request for "
+ "win=%s dropped since no candidate was found",
- embeddedWindow.getName());
+ embeddedWindow);
return;
}
t.requestFocusTransfer(newFocusTarget.mInputChannelToken, newFocusTarget.getName(),
- inputToken, embeddedWindow.getName(),
+ inputToken, embeddedWindow.toString(),
displayId).apply();
EventLog.writeEvent(LOGTAG_INPUT_FOCUS,
"Transfer focus request " + newFocusTarget,
"reason=grantEmbeddedWindowFocus(false)");
}
ProtoLog.v(WM_DEBUG_FOCUS, "grantEmbeddedWindowFocus win=%s grantFocus=%s",
- embeddedWindow.getName(), grantFocus);
+ embeddedWindow, grantFocus);
}
}
@@ -8594,24 +8615,24 @@
}
SurfaceControl.Transaction t = mTransactionFactory.get();
if (grantFocus) {
- t.requestFocusTransfer(targetInputToken, embeddedWindow.getName(),
+ t.requestFocusTransfer(targetInputToken, embeddedWindow.toString(),
hostWindow.mInputChannel.getToken(),
hostWindow.getName(),
hostWindow.getDisplayId()).apply();
EventLog.writeEvent(LOGTAG_INPUT_FOCUS,
- "Transfer focus request " + embeddedWindow.getName(),
+ "Transfer focus request " + embeddedWindow,
"reason=grantEmbeddedWindowFocus(true)");
} else {
t.requestFocusTransfer(hostWindow.mInputChannel.getToken(), hostWindow.getName(),
targetInputToken,
- embeddedWindow.getName(),
+ embeddedWindow.toString(),
hostWindow.getDisplayId()).apply();
EventLog.writeEvent(LOGTAG_INPUT_FOCUS,
"Transfer focus request " + hostWindow,
"reason=grantEmbeddedWindowFocus(false)");
}
ProtoLog.v(WM_DEBUG_FOCUS, "grantEmbeddedWindowFocus win=%s grantFocus=%s",
- embeddedWindow.getName(), grantFocus);
+ embeddedWindow, grantFocus);
}
}
diff --git a/services/core/java/com/android/server/wm/WindowOrganizerController.java b/services/core/java/com/android/server/wm/WindowOrganizerController.java
index 781b53d..54ce5fc 100644
--- a/services/core/java/com/android/server/wm/WindowOrganizerController.java
+++ b/services/core/java/com/android/server/wm/WindowOrganizerController.java
@@ -117,7 +117,7 @@
final DisplayAreaOrganizerController mDisplayAreaOrganizerController;
final TaskFragmentOrganizerController mTaskFragmentOrganizerController;
- final TransitionController mTransitionController;
+ TransitionController mTransitionController;
/**
* A Map which manages the relationship between
* {@link TaskFragmentCreationParams#getFragmentToken()} and {@link TaskFragment}
@@ -131,7 +131,10 @@
mTaskOrganizerController = new TaskOrganizerController(mService);
mDisplayAreaOrganizerController = new DisplayAreaOrganizerController(mService);
mTaskFragmentOrganizerController = new TaskFragmentOrganizerController(atm);
- mTransitionController = new TransitionController(atm);
+ }
+
+ void setWindowManager(WindowManagerService wms) {
+ mTransitionController = new TransitionController(mService, wms.mTaskSnapshotController);
}
TransitionController getTransitionController() {
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index 6afa2e0..d29b2ae 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -273,7 +273,7 @@
/** A window in the window manager. */
class WindowState extends WindowContainer<WindowState> implements WindowManagerPolicy.WindowState,
- InsetsControlTarget {
+ InsetsControlTarget, InputTarget {
static final String TAG = TAG_WITH_CLASS_NAME ? "WindowState" : TAG_WM;
// The minimal size of a window within the usable area of the freeform root task.
@@ -1727,7 +1727,8 @@
return state;
}
- int getDisplayId() {
+ @Override
+ public int getDisplayId() {
final DisplayContent displayContent = getDisplayContent();
if (displayContent == null) {
return Display.INVALID_DISPLAY;
@@ -1735,6 +1736,21 @@
return displayContent.getDisplayId();
}
+ @Override
+ public WindowState getWindowState() {
+ return this;
+ }
+
+ @Override
+ public IWindow getIWindow() {
+ return mClient;
+ }
+
+ @Override
+ public int getPid() {
+ return mSession.mPid;
+ }
+
Task getTask() {
return mActivityRecord != null ? mActivityRecord.getTask() : null;
}
@@ -5681,7 +5697,7 @@
// elevating the IME and windows above it's target above the docked divider in
// split-screen, or make the popupMenu to be above the IME when the parent window is the
// IME layering target in bubble/freeform mode.
- if (mDisplayContent.isImeAttachedToApp()) {
+ if (mDisplayContent.shouldImeAttachedToApp()) {
return false;
}
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index 14970ce..a3ff6da 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -10685,7 +10685,10 @@
}
}
- if (!mOwners.hasDeviceOwner() || !user.isFull() || user.isManagedProfile()) return;
+ if (!mOwners.hasDeviceOwner() || !user.isFull() || user.isManagedProfile()
+ || user.isGuest()) {
+ return;
+ }
if (mInjector.userManagerIsHeadlessSystemUserMode()) {
ComponentName admin = mOwners.getDeviceOwnerComponent();
diff --git a/services/tests/mockingservicestests/assets/SensorPrivacyServiceMockingTest/persisted_file_micMute_camMute.xml b/services/tests/mockingservicestests/assets/SensorPrivacyServiceMockingTest/persisted_file_micMute_camMute.xml
new file mode 100644
index 0000000..a4de08a
--- /dev/null
+++ b/services/tests/mockingservicestests/assets/SensorPrivacyServiceMockingTest/persisted_file_micMute_camMute.xml
@@ -0,0 +1,7 @@
+<?xml version='1.0' encoding='UTF-8' standalone='yes' ?>
+<sensor-privacy persistence-version="1" version="1">
+ <user id="0" enabled="false">
+ <individual-sensor-privacy sensor="1" enabled="true" />
+ <individual-sensor-privacy sensor="2" enabled="true" />
+ </user>
+</sensor-privacy>
diff --git a/services/tests/mockingservicestests/assets/SensorPrivacyServiceMockingTest/persisted_file_micMute_camUnmute.xml b/services/tests/mockingservicestests/assets/SensorPrivacyServiceMockingTest/persisted_file_micMute_camUnmute.xml
new file mode 100644
index 0000000..47649d7
--- /dev/null
+++ b/services/tests/mockingservicestests/assets/SensorPrivacyServiceMockingTest/persisted_file_micMute_camUnmute.xml
@@ -0,0 +1,7 @@
+<?xml version='1.0' encoding='UTF-8' standalone='yes' ?>
+<sensor-privacy persistence-version="1" version="1">
+ <user id="0" enabled="false">
+ <individual-sensor-privacy sensor="1" enabled="true" />
+ <individual-sensor-privacy sensor="2" enabled="false" />
+ </user>
+</sensor-privacy>
diff --git a/services/tests/mockingservicestests/assets/SensorPrivacyServiceMockingTest/persisted_file_micUnmute_camMute.xml b/services/tests/mockingservicestests/assets/SensorPrivacyServiceMockingTest/persisted_file_micUnmute_camMute.xml
new file mode 100644
index 0000000..4fd9ebf
--- /dev/null
+++ b/services/tests/mockingservicestests/assets/SensorPrivacyServiceMockingTest/persisted_file_micUnmute_camMute.xml
@@ -0,0 +1,7 @@
+<?xml version='1.0' encoding='UTF-8' standalone='yes' ?>
+<sensor-privacy persistence-version="1" version="1">
+ <user id="0" enabled="false">
+ <individual-sensor-privacy sensor="1" enabled="false" />
+ <individual-sensor-privacy sensor="2" enabled="true" />
+ </user>
+</sensor-privacy>
diff --git a/services/tests/mockingservicestests/assets/SensorPrivacyServiceMockingTest/persisted_file_micUnmute_camUnmute.xml b/services/tests/mockingservicestests/assets/SensorPrivacyServiceMockingTest/persisted_file_micUnmute_camUnmute.xml
new file mode 100644
index 0000000..e8f9edf
--- /dev/null
+++ b/services/tests/mockingservicestests/assets/SensorPrivacyServiceMockingTest/persisted_file_micUnmute_camUnmute.xml
@@ -0,0 +1,7 @@
+<?xml version='1.0' encoding='UTF-8' standalone='yes' ?>
+<sensor-privacy persistence-version="1" version="1">
+ <user id="0" enabled="false">
+ <individual-sensor-privacy sensor="1" enabled="false" />
+ <individual-sensor-privacy sensor="2" enabled="false" />
+ </user>
+</sensor-privacy>
diff --git a/services/tests/mockingservicestests/src/com/android/server/sensorprivacy/SensorPrivacyServiceMockingTest.java b/services/tests/mockingservicestests/src/com/android/server/sensorprivacy/SensorPrivacyServiceMockingTest.java
index ba79a76..38f01b5 100644
--- a/services/tests/mockingservicestests/src/com/android/server/sensorprivacy/SensorPrivacyServiceMockingTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/sensorprivacy/SensorPrivacyServiceMockingTest.java
@@ -16,12 +16,18 @@
package com.android.server.sensorprivacy;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.doAnswer;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyBoolean;
+import static org.mockito.ArgumentMatchers.anyInt;
+
import android.app.ActivityManager;
import android.app.ActivityTaskManager;
import android.app.AppOpsManager;
+import android.app.AppOpsManagerInternal;
import android.content.Context;
import android.content.pm.UserInfo;
import android.os.Environment;
@@ -33,8 +39,10 @@
import com.android.dx.mockito.inline.extended.ExtendedMockito;
import com.android.server.LocalServices;
import com.android.server.SensorPrivacyService;
+import com.android.server.SystemService;
import com.android.server.pm.UserManagerInternal;
+import org.junit.Assert;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
@@ -44,6 +52,7 @@
import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
+import java.util.concurrent.CompletableFuture;
@RunWith(AndroidTestingRunner.class)
public class SensorPrivacyServiceMockingTest {
@@ -63,10 +72,21 @@
public static final String PERSISTENCE_FILE6 =
String.format(PERSISTENCE_FILE_PATHS_TEMPLATE, 6);
+ public static final String PERSISTENCE_FILE_MIC_MUTE_CAM_MUTE =
+ "SensorPrivacyServiceMockingTest/persisted_file_micMute_camMute.xml";
+ public static final String PERSISTENCE_FILE_MIC_MUTE_CAM_UNMUTE =
+ "SensorPrivacyServiceMockingTest/persisted_file_micMute_camUnmute.xml";
+ public static final String PERSISTENCE_FILE_MIC_UNMUTE_CAM_MUTE =
+ "SensorPrivacyServiceMockingTest/persisted_file_micUnmute_camMute.xml";
+ public static final String PERSISTENCE_FILE_MIC_UNMUTE_CAM_UNMUTE =
+ "SensorPrivacyServiceMockingTest/persisted_file_micUnmute_camUnmute.xml";
+
private Context mContext;
@Mock
private AppOpsManager mMockedAppOpsManager;
@Mock
+ private AppOpsManagerInternal mMockedAppOpsManagerInternal;
+ @Mock
private UserManagerInternal mMockedUserManagerInternal;
@Mock
private ActivityManager mMockedActivityManager;
@@ -134,13 +154,103 @@
}
}
+ @Test
+ public void testServiceInit_AppOpsRestricted_micMute_camMute() throws IOException {
+ testServiceInit_AppOpsRestricted(PERSISTENCE_FILE_MIC_MUTE_CAM_MUTE, true, true);
+ }
+
+ @Test
+ public void testServiceInit_AppOpsRestricted_micMute_camUnmute() throws IOException {
+ testServiceInit_AppOpsRestricted(PERSISTENCE_FILE_MIC_MUTE_CAM_UNMUTE, true, false);
+ }
+
+ @Test
+ public void testServiceInit_AppOpsRestricted_micUnmute_camMute() throws IOException {
+ testServiceInit_AppOpsRestricted(PERSISTENCE_FILE_MIC_UNMUTE_CAM_MUTE, false, true);
+ }
+
+ @Test
+ public void testServiceInit_AppOpsRestricted_micUnmute_camUnmute() throws IOException {
+ testServiceInit_AppOpsRestricted(PERSISTENCE_FILE_MIC_UNMUTE_CAM_UNMUTE, false, false);
+ }
+
+ private void testServiceInit_AppOpsRestricted(String persistenceFileMicMuteCamMute,
+ boolean expectedMicState, boolean expectedCamState)
+ throws IOException {
+ MockitoSession mockitoSession = ExtendedMockito.mockitoSession()
+ .initMocks(this)
+ .strictness(Strictness.WARN)
+ .spyStatic(LocalServices.class)
+ .spyStatic(Environment.class)
+ .startMocking();
+
+ try {
+ mContext = InstrumentationRegistry.getInstrumentation().getContext();
+ spyOn(mContext);
+
+ doReturn(mMockedAppOpsManager).when(mContext).getSystemService(AppOpsManager.class);
+ doReturn(mMockedAppOpsManagerInternal)
+ .when(() -> LocalServices.getService(AppOpsManagerInternal.class));
+ doReturn(mMockedUserManagerInternal)
+ .when(() -> LocalServices.getService(UserManagerInternal.class));
+ doReturn(mMockedActivityManager).when(mContext).getSystemService(ActivityManager.class);
+ doReturn(mMockedActivityTaskManager)
+ .when(mContext).getSystemService(ActivityTaskManager.class);
+ doReturn(mMockedTelephonyManager).when(mContext).getSystemService(
+ TelephonyManager.class);
+
+ String dataDir = mContext.getApplicationInfo().dataDir;
+ doReturn(new File(dataDir)).when(() -> Environment.getDataSystemDirectory());
+
+ File onDeviceFile = new File(dataDir, "sensor_privacy.xml");
+ onDeviceFile.delete();
+
+ doReturn(new int[]{0}).when(mMockedUserManagerInternal).getUserIds();
+ doReturn(ExtendedMockito.mock(UserInfo.class)).when(mMockedUserManagerInternal)
+ .getUserInfo(0);
+
+ CompletableFuture<Boolean> micState = new CompletableFuture<>();
+ CompletableFuture<Boolean> camState = new CompletableFuture<>();
+ doAnswer(invocation -> {
+ int code = invocation.getArgument(0);
+ boolean restricted = invocation.getArgument(1);
+ if (code == AppOpsManager.OP_RECORD_AUDIO) {
+ micState.complete(restricted);
+ } else if (code == AppOpsManager.OP_CAMERA) {
+ camState.complete(restricted);
+ }
+ return null;
+ }).when(mMockedAppOpsManagerInternal).setGlobalRestriction(anyInt(), anyBoolean(),
+ any());
+
+ initServiceWithPersistenceFile(onDeviceFile, persistenceFileMicMuteCamMute, 0);
+
+ Assert.assertTrue(micState.join() == expectedMicState);
+ Assert.assertTrue(camState.join() == expectedCamState);
+
+ } finally {
+ mockitoSession.finishMocking();
+ }
+ }
+
private void initServiceWithPersistenceFile(File onDeviceFile,
String persistenceFilePath) throws IOException {
+ initServiceWithPersistenceFile(onDeviceFile, persistenceFilePath, -1);
+ }
+
+ private void initServiceWithPersistenceFile(File onDeviceFile,
+ String persistenceFilePath, int startingUserId) throws IOException {
if (persistenceFilePath != null) {
Files.copy(mContext.getAssets().open(persistenceFilePath),
onDeviceFile.toPath());
}
- new SensorPrivacyService(mContext);
+ SensorPrivacyService service = new SensorPrivacyService(mContext);
+ if (startingUserId != -1) {
+ SystemService.TargetUser mockedTargetUser =
+ ExtendedMockito.mock(SystemService.TargetUser.class);
+ doReturn(startingUserId).when(mockedTargetUser).getUserIdentifier();
+ service.onUserStarting(mockedTargetUser);
+ }
onDeviceFile.delete();
}
}
diff --git a/services/tests/servicestests/src/com/android/server/am/BatteryExternalStatsWorkerTest.java b/services/tests/servicestests/src/com/android/server/am/BatteryExternalStatsWorkerTest.java
index 4a67ec7..8a8a624 100644
--- a/services/tests/servicestests/src/com/android/server/am/BatteryExternalStatsWorkerTest.java
+++ b/services/tests/servicestests/src/com/android/server/am/BatteryExternalStatsWorkerTest.java
@@ -74,15 +74,20 @@
@Test
public void testTargetedEnergyConsumerQuerying() {
final int numCpuClusters = 4;
+ final int numDisplays = 5;
final int numOther = 3;
// Add some energy consumers used by BatteryExternalStatsWorker.
final IntArray tempAllIds = new IntArray();
- final int displayId = mPowerStatsInternal.addEnergyConsumer(EnergyConsumerType.DISPLAY, 0,
- "display");
- tempAllIds.add(displayId);
- mPowerStatsInternal.incrementEnergyConsumption(displayId, 12345);
+ final int[] displayIds = new int[numDisplays];
+ for (int i = 0; i < numDisplays; i++) {
+ displayIds[i] = mPowerStatsInternal.addEnergyConsumer(
+ EnergyConsumerType.DISPLAY, i, "display" + i);
+ tempAllIds.add(displayIds[i]);
+ mPowerStatsInternal.incrementEnergyConsumption(displayIds[i], 12345 + i);
+ }
+ Arrays.sort(displayIds);
final int wifiId = mPowerStatsInternal.addEnergyConsumer(EnergyConsumerType.WIFI, 0,
"wifi");
@@ -130,9 +135,13 @@
final EnergyConsumerResult[] displayResults =
mBatteryExternalStatsWorker.getMeasuredEnergyLocked(UPDATE_DISPLAY).getNow(null);
- // Results should only have the display energy consumer
- assertEquals(1, displayResults.length);
- assertEquals(displayId, displayResults[0].id);
+ // Results should only have the cpu cluster energy consumers
+ final int[] receivedDisplayIds = new int[displayResults.length];
+ for (int i = 0; i < displayResults.length; i++) {
+ receivedDisplayIds[i] = displayResults[i].id;
+ }
+ Arrays.sort(receivedDisplayIds);
+ assertArrayEquals(displayIds, receivedDisplayIds);
final EnergyConsumerResult[] wifiResults =
mBatteryExternalStatsWorker.getMeasuredEnergyLocked(UPDATE_WIFI).getNow(null);
diff --git a/services/tests/servicestests/src/com/android/server/am/MeasuredEnergySnapshotTest.java b/services/tests/servicestests/src/com/android/server/am/MeasuredEnergySnapshotTest.java
index 8c87506..a0cbcad 100644
--- a/services/tests/servicestests/src/com/android/server/am/MeasuredEnergySnapshotTest.java
+++ b/services/tests/servicestests/src/com/android/server/am/MeasuredEnergySnapshotTest.java
@@ -16,8 +16,6 @@
package com.android.server.am;
-import static com.android.server.am.MeasuredEnergySnapshot.UNAVAILABLE;
-
import static com.google.common.truth.Truth.assertThat;
import static org.junit.Assert.assertEquals;
@@ -120,7 +118,7 @@
// results0
MeasuredEnergyDeltaData delta = snapshot.updateAndGetDelta(RESULTS_0, VOLTAGE_0);
if (delta != null) { // null is fine here. If non-null, it better be uninteresting though.
- assertEquals(UNAVAILABLE, delta.displayChargeUC);
+ assertNull(delta.displayChargeUC);
assertNull(delta.otherTotalChargeUC);
assertNull(delta.otherUidChargesUC);
}
@@ -130,7 +128,7 @@
assertNotNull(delta);
long expectedChargeUC;
expectedChargeUC = calculateChargeConsumedUC(14_000, VOLTAGE_0, 24_000, VOLTAGE_1);
- assertEquals(expectedChargeUC, delta.displayChargeUC);
+ assertEquals(expectedChargeUC, delta.displayChargeUC[0]);
assertNotNull(delta.otherTotalChargeUC);
@@ -149,14 +147,14 @@
delta = snapshot.updateAndGetDelta(RESULTS_2, VOLTAGE_2);
assertNotNull(delta);
expectedChargeUC = calculateChargeConsumedUC(24_000, VOLTAGE_1, 36_000, VOLTAGE_2);
- assertEquals(expectedChargeUC, delta.displayChargeUC);
+ assertEquals(expectedChargeUC, delta.displayChargeUC[0]);
assertNull(delta.otherUidChargesUC);
assertNull(delta.otherTotalChargeUC);
// results3
delta = snapshot.updateAndGetDelta(RESULTS_3, VOLTAGE_3);
assertNotNull(delta);
- assertEquals(UNAVAILABLE, delta.displayChargeUC);
+ assertNull(delta.displayChargeUC);
assertNotNull(delta.otherTotalChargeUC);
@@ -183,7 +181,7 @@
delta = snapshot.updateAndGetDelta(RESULTS_4, VOLTAGE_4);
assertNotNull(delta);
expectedChargeUC = calculateChargeConsumedUC(36_000, VOLTAGE_2, 43_000, VOLTAGE_4);
- assertEquals(expectedChargeUC, delta.displayChargeUC);
+ assertEquals(expectedChargeUC, delta.displayChargeUC[0]);
assertNotNull(delta.otherTotalChargeUC);
expectedChargeUC = calculateChargeConsumedUC(190_000, VOLTAGE_3, 290_000, VOLTAGE_4);
@@ -210,7 +208,7 @@
// results0
MeasuredEnergyDeltaData delta = snapshot.updateAndGetDelta(RESULTS_0, VOLTAGE_0);
if (delta != null) { // null is fine here. If non-null, it better be uninteresting though.
- assertEquals(UNAVAILABLE, delta.displayChargeUC);
+ assertNull(delta.displayChargeUC);
assertNull(delta.otherTotalChargeUC);
assertNull(delta.otherUidChargesUC);
}
@@ -220,7 +218,7 @@
assertNotNull(delta);
final long expectedChargeUC =
calculateChargeConsumedUC(14_000, VOLTAGE_0, 24_000, VOLTAGE_1);
- assertEquals(expectedChargeUC, delta.displayChargeUC);
+ assertEquals(expectedChargeUC, delta.displayChargeUC[0]);
assertNull(delta.otherTotalChargeUC); // Although in the results, they're not in the idMap
assertNull(delta.otherUidChargesUC);
}
diff --git a/services/tests/servicestests/src/com/android/server/pm/permission/LegacyPermissionManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/pm/permission/LegacyPermissionManagerServiceTest.java
index acd3fca..3261dfa 100644
--- a/services/tests/servicestests/src/com/android/server/pm/permission/LegacyPermissionManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/permission/LegacyPermissionManagerServiceTest.java
@@ -125,7 +125,7 @@
public void checkDeviceIdentifierAccess_hasPrivilegedPermission_returnsGranted() {
// Apps with the READ_PRIVILEGED_PHONE_STATE permission should have access to device
// identifiers.
- setupCheckDeviceIdentifierAccessTest(SYSTEM_PID, SYSTEM_UID);
+ setupCheckDeviceIdentifierAccessTest(SYSTEM_PID, SYSTEM_UID, APP_UID);
when(mInjector.checkPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE,
APP_PID, APP_UID)).thenReturn(PackageManager.PERMISSION_GRANTED);
@@ -140,7 +140,7 @@
public void checkDeviceIdentifierAccess_hasAppOp_returnsGranted() {
// Apps that have been granted the READ_DEVICE_IDENTIFIERS appop should have access to
// device identifiers.
- setupCheckDeviceIdentifierAccessTest(SYSTEM_PID, SYSTEM_UID);
+ setupCheckDeviceIdentifierAccessTest(SYSTEM_PID, SYSTEM_UID, APP_UID);
when(mAppOpsManager.noteOpNoThrow(eq(AppOpsManager.OPSTR_READ_DEVICE_IDENTIFIERS),
eq(APP_UID), eq(mPackageName), any(), any())).thenReturn(
AppOpsManager.MODE_ALLOWED);
@@ -156,7 +156,7 @@
public void checkDeviceIdentifierAccess_hasDpmAccess_returnsGranted() {
// Apps that pass a DevicePolicyManager device / profile owner check should have access to
// device identifiers.
- setupCheckDeviceIdentifierAccessTest(SYSTEM_PID, SYSTEM_UID);
+ setupCheckDeviceIdentifierAccessTest(SYSTEM_PID, SYSTEM_UID, APP_UID);
when(mDevicePolicyManager.hasDeviceIdentifierAccess(mPackageName, APP_PID,
APP_UID)).thenReturn(true);
@@ -236,7 +236,7 @@
// both the permission and the appop must be granted. If the permission is granted but the
// appop is not then AppOpsManager#MODE_IGNORED should be returned to indicate that this
// should be a silent failure.
- setupCheckPhoneNumberAccessTest(SYSTEM_PID, SYSTEM_UID);
+ setupCheckPhoneNumberAccessTest(SYSTEM_PID, SYSTEM_UID, APP_UID);
setPackageTargetSdk(Build.VERSION_CODES.Q);
grantPermissionAndAppop(android.Manifest.permission.READ_PHONE_STATE, null);
@@ -256,7 +256,7 @@
// Apps targeting R+ with just the READ_PHONE_STATE permission granted should not have
// access to the phone number; PERMISSION_DENIED should be returned both with and without
// the appop granted since this check should be skipped for target SDK R+.
- setupCheckPhoneNumberAccessTest(SYSTEM_PID, SYSTEM_UID);
+ setupCheckPhoneNumberAccessTest(SYSTEM_PID, SYSTEM_UID, APP_UID);
grantPermissionAndAppop(android.Manifest.permission.READ_PHONE_STATE, null);
int resultWithoutAppop = mLegacyPermissionManagerService.checkPhoneNumberAccess(
@@ -319,12 +319,79 @@
assertEquals(PackageManager.PERMISSION_GRANTED, resultWithAppop);
}
+ @Test
+ public void checkPhoneNumberAccess_providedUidDoesNotMatchPackageUid_throwsException()
+ throws Exception {
+ // An app can directly interact with one of the services that accepts a package name and
+ // returns a protected resource via a direct binder transact. This app could then provide
+ // the name of another app that targets pre-R, then determine if the app is installed based
+ // on whether the service throws an exception or not. While the app can provide the package
+ // name of another app, it cannot specify the package uid which is passed to the
+ // LegacyPermissionManager using Binder#getCallingUid. Ultimately this uid should then be
+ // compared against the actual uid of the package to ensure information about packages
+ // installed on the device is not leaked.
+ setupCheckPhoneNumberAccessTest(SYSTEM_PID, SYSTEM_UID, APP_UID + 1);
+
+ assertThrows(SecurityException.class,
+ () -> mLegacyPermissionManagerService.checkPhoneNumberAccess(mPackageName,
+ CHECK_PHONE_NUMBER_MESSAGE, null, APP_PID, APP_UID));
+ }
+
+ @Test
+ public void checkPhoneNumberAccess_nullPackageNameSystemUid_returnsGranted() throws Exception {
+ // The platform can pass a null package name when checking if the platform itself has
+ // access to the device phone number(s) / identifier(s). This test ensures if a null package
+ // is provided, then the package uid check is skipped and the test is based on whether the
+ // the provided uid / pid has been granted the privileged permission.
+ setupCheckPhoneNumberAccessTest(SYSTEM_PID, SYSTEM_UID, -1);
+ when(mInjector.checkPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE,
+ SYSTEM_PID, SYSTEM_UID)).thenReturn(PackageManager.PERMISSION_GRANTED);
+
+ int result = mLegacyPermissionManagerService.checkPhoneNumberAccess(null,
+ CHECK_PHONE_NUMBER_MESSAGE, null, SYSTEM_PID, SYSTEM_UID);
+
+ assertEquals(PackageManager.PERMISSION_GRANTED, result);
+ }
+
+ @Test
+ public void checkPhoneNumberAccess_systemUidMismatchPackageUid_returnsGranted()
+ throws Exception {
+ // When the platform is checking device phone number / identifier access checks for other
+ // components on the platform, a uid less than the first application UID is provided; this
+ // test verifies the package uid check is skipped and access is still granted with the
+ // privileged permission.
+ int telephonyUid = SYSTEM_UID + 1;
+ int telephonyPid = SYSTEM_PID + 1;
+ setupCheckPhoneNumberAccessTest(SYSTEM_PID, SYSTEM_UID, -1);
+ when(mInjector.checkPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE,
+ telephonyPid, telephonyUid)).thenReturn(PackageManager.PERMISSION_GRANTED);
+
+ int result = mLegacyPermissionManagerService.checkPhoneNumberAccess(mPackageName,
+ CHECK_PHONE_NUMBER_MESSAGE, null, telephonyPid, telephonyUid);
+
+ assertEquals(PackageManager.PERMISSION_GRANTED, result);
+ }
+
/**
* Configures device identifier access tests to fail; tests verifying access should individually
* set an access check to succeed to verify access when that condition is met.
*/
private void setupCheckDeviceIdentifierAccessTest(int callingPid, int callingUid) {
- setupAccessTest(callingPid, callingUid);
+ setupCheckDeviceIdentifierAccessTest(callingPid, callingUid, callingUid);
+ }
+
+ /**
+ * Configures device identifier access tests to fail; tests verifying access should individually
+ * set an access check to succeed to verify access when that condition is met.
+ *
+ * <p>To prevent leaking package information, access checks for package UIDs >= {@link
+ * android.os.Process#FIRST_APPLICATION_UID} must ensure the provided uid matches the uid of
+ * the package being checked; to ensure this check is successful, this method accepts the
+ * {@code packageUid} to be used for the package being checked.
+ */
+ public void setupCheckDeviceIdentifierAccessTest(int callingPid, int callingUid,
+ int packageUid) {
+ setupAccessTest(callingPid, callingUid, packageUid);
when(mDevicePolicyManager.hasDeviceIdentifierAccess(anyString(), anyInt(),
anyInt())).thenReturn(false);
@@ -333,11 +400,26 @@
}
/**
- * Configures phone number access tests to fail; tests verifying access should individually set
- * an access check to succeed to verify access when that condition is met.
+ * Configures phone number access tests to fail; tests verifying access should individually
+ * set an access check to succeed to verify access when that condition is set.
+ *
*/
private void setupCheckPhoneNumberAccessTest(int callingPid, int callingUid) throws Exception {
- setupAccessTest(callingPid, callingUid);
+ setupCheckPhoneNumberAccessTest(callingPid, callingUid, callingUid);
+ }
+
+ /**
+ * Configures phone number access tests to fail; tests verifying access should individually set
+ * an access check to succeed to verify access when that condition is met.
+ *
+ * <p>To prevent leaking package information, access checks for package UIDs >= {@link
+ * android.os.Process#FIRST_APPLICATION_UID} must ensure the provided uid matches the uid of
+ * the package being checked; to ensure this check is successful, this method accepts the
+ * {@code packageUid} to be used for the package being checked.
+ */
+ private void setupCheckPhoneNumberAccessTest(int callingPid, int callingUid, int packageUid)
+ throws Exception {
+ setupAccessTest(callingPid, callingUid, packageUid);
setPackageTargetSdk(Build.VERSION_CODES.R);
}
@@ -345,9 +427,10 @@
* Configures the common mocks for any access tests using the provided {@code callingPid}
* and {@code callingUid}.
*/
- private void setupAccessTest(int callingPid, int callingUid) {
+ private void setupAccessTest(int callingPid, int callingUid, int packageUid) {
when(mInjector.getCallingPid()).thenReturn(callingPid);
when(mInjector.getCallingUid()).thenReturn(callingUid);
+ when(mInjector.getPackageUidForUser(anyString(), anyInt())).thenReturn(packageUid);
when(mInjector.checkPermission(anyString(), anyInt(), anyInt())).thenReturn(
PackageManager.PERMISSION_DENIED);
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityStartInterceptorTest.java b/services/tests/wmtests/src/com/android/server/wm/ActivityStartInterceptorTest.java
index a3ad09a..c103bc6 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityStartInterceptorTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityStartInterceptorTest.java
@@ -28,6 +28,7 @@
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.ArgumentMatchers.nullable;
+import android.annotation.Nullable;
import android.app.ActivityManagerInternal;
import android.app.KeyguardManager;
import android.app.admin.DevicePolicyManagerInternal;
@@ -42,6 +43,7 @@
import android.os.UserManager;
import android.platform.test.annotations.Presubmit;
import android.testing.DexmakerShareClassLoaderRule;
+import android.util.SparseArray;
import androidx.test.filters.SmallTest;
@@ -64,7 +66,7 @@
* Unit tests for {@link ActivityStartInterceptorTest}.
*
* Build/Install/Run:
- * atest WmTests:ActivityStartInterceptorTest
+ * atest WmTests:ActivityStartInterceptorTest
*/
@SmallTest
@Presubmit
@@ -114,6 +116,9 @@
private ActivityStartInterceptor mInterceptor;
private ActivityInfo mAInfo = new ActivityInfo();
+ private SparseArray<ActivityInterceptorCallback> mActivityInterceptorCallbacks =
+ new SparseArray<>();
+
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
@@ -157,6 +162,9 @@
TEST_USER_ID, TEST_PACKAGE_NAME, LOCK_TASK_LAUNCH_MODE_DEFAULT))
.thenReturn(true);
+ // Mock the activity start callbacks
+ when(mService.getActivityInterceptorCallbacks()).thenReturn(mActivityInterceptorCallbacks);
+
// Initialise activity info
mAInfo.applicationInfo = new ApplicationInfo();
mAInfo.packageName = mAInfo.applicationInfo.packageName = TEST_PACKAGE_NAME;
@@ -285,4 +293,38 @@
// THEN calling intercept returns false
assertFalse(mInterceptor.intercept(null, null, mAInfo, null, null, 0, 0, null));
}
+
+ public void addMockInterceptorCallback(@Nullable Intent intent) {
+ int size = mActivityInterceptorCallbacks.size();
+ mActivityInterceptorCallbacks.put(size, new ActivityInterceptorCallback() {
+ @Override
+ public Intent intercept(ActivityInterceptorInfo info) {
+ return intent;
+ }
+ });
+ }
+
+ @Test
+ public void testInterceptionCallback_singleCallback() {
+ addMockInterceptorCallback(new Intent("android.test.foo"));
+
+ assertTrue(mInterceptor.intercept(null, null, mAInfo, null, null, 0, 0, null));
+ assertEquals("android.test.foo", mInterceptor.mIntent.getAction());
+ }
+
+ @Test
+ public void testInterceptionCallback_singleCallbackReturnsNull() {
+ addMockInterceptorCallback(null);
+
+ assertFalse(mInterceptor.intercept(null, null, mAInfo, null, null, 0, 0, null));
+ }
+
+ @Test
+ public void testInterceptionCallback_fallbackToSecondCallback() {
+ addMockInterceptorCallback(null);
+ addMockInterceptorCallback(new Intent("android.test.second"));
+
+ assertTrue(mInterceptor.intercept(null, null, mAInfo, null, null, 0, 0, null));
+ assertEquals("android.test.second", mInterceptor.mIntent.getAction());
+ }
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityTaskManagerServiceTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityTaskManagerServiceTests.java
index 5de4fcb..5d1a068 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityTaskManagerServiceTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityTaskManagerServiceTests.java
@@ -26,6 +26,8 @@
import static com.android.dx.mockito.inline.extended.ExtendedMockito.mock;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
+import static com.android.server.wm.ActivityInterceptorCallback.FIRST_ORDERED_ID;
+import static com.android.server.wm.ActivityInterceptorCallback.LAST_ORDERED_ID;
import static com.android.server.wm.ActivityRecord.State.PAUSED;
import static com.android.server.wm.ActivityRecord.State.PAUSING;
import static com.android.server.wm.ActivityRecord.State.RESUMED;
@@ -42,12 +44,14 @@
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.when;
+import android.annotation.Nullable;
import android.app.Activity;
import android.app.ActivityManager;
import android.app.IApplicationThread;
import android.app.PictureInPictureParams;
import android.app.servertransaction.ClientTransaction;
import android.app.servertransaction.EnterPipRequestedItem;
+import android.content.Intent;
import android.content.pm.ActivityInfo;
import android.content.pm.ApplicationInfo;
import android.content.res.Configuration;
@@ -846,7 +850,64 @@
return wpc;
}
+ @Test(expected = IllegalArgumentException.class)
+ public void testRegisterActivityStartInterceptor_IndexTooSmall() {
+ mAtm.mInternal.registerActivityStartInterceptor(FIRST_ORDERED_ID - 1,
+ new ActivityInterceptorCallback() {
+ @Nullable
+ @Override
+ public Intent intercept(ActivityInterceptorInfo info) {
+ return null;
+ }
+ });
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void testRegisterActivityStartInterceptor_IndexTooLarge() {
+ mAtm.mInternal.registerActivityStartInterceptor(LAST_ORDERED_ID + 1,
+ new ActivityInterceptorCallback() {
+ @Nullable
+ @Override
+ public Intent intercept(ActivityInterceptorInfo info) {
+ return null;
+ }
+ });
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void testRegisterActivityStartInterceptor_DuplicateId() {
+ mAtm.mInternal.registerActivityStartInterceptor(FIRST_ORDERED_ID,
+ new ActivityInterceptorCallback() {
+ @Nullable
+ @Override
+ public Intent intercept(ActivityInterceptorInfo info) {
+ return null;
+ }
+ });
+ mAtm.mInternal.registerActivityStartInterceptor(FIRST_ORDERED_ID,
+ new ActivityInterceptorCallback() {
+ @Nullable
+ @Override
+ public Intent intercept(ActivityInterceptorInfo info) {
+ return null;
+ }
+ });
+ }
+
+ @Test
+ public void testRegisterActivityStartInterceptor() {
+ assertEquals(0, mAtm.getActivityInterceptorCallbacks().size());
+
+ mAtm.mInternal.registerActivityStartInterceptor(FIRST_ORDERED_ID,
+ new ActivityInterceptorCallback() {
+ @Nullable
+ @Override
+ public Intent intercept(ActivityInterceptorInfo info) {
+ return null;
+ }
+ });
+
+ assertEquals(1, mAtm.getActivityInterceptorCallbacks().size());
+ assertTrue(mAtm.getActivityInterceptorCallbacks().contains(FIRST_ORDERED_ID));
+ }
}
-
-
-
diff --git a/services/tests/wmtests/src/com/android/server/wm/AppTransitionControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/AppTransitionControllerTest.java
index 82140f4..5d0e34a 100644
--- a/services/tests/wmtests/src/com/android/server/wm/AppTransitionControllerTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/AppTransitionControllerTest.java
@@ -43,6 +43,7 @@
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.Mockito.doCallRealMethod;
+import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
@@ -893,6 +894,33 @@
}
@Test
+ public void testOverrideTaskFragmentAdapter_noOverrideWithWallpaper() {
+ final TaskFragmentOrganizer organizer = new TaskFragmentOrganizer(Runnable::run);
+ final RemoteAnimationAdapter adapter = new RemoteAnimationAdapter(
+ new TestRemoteAnimationRunner(), 10, 1);
+ setupTaskFragmentRemoteAnimation(organizer, adapter);
+
+ // Create a TaskFragment with embedded activity.
+ final TaskFragment taskFragment = createTaskFragmentWithEmbeddedActivity(
+ createTask(mDisplayContent), organizer);
+ final ActivityRecord activity = taskFragment.getTopMostActivity();
+ activity.allDrawn = true;
+ // Set wallpaper as visible.
+ final WallpaperWindowToken wallpaperWindowToken = new WallpaperWindowToken(mWm,
+ mock(IBinder.class), true, mDisplayContent, true /* ownerCanManageAppTokens */);
+ spyOn(mDisplayContent.mWallpaperController);
+ doReturn(true).when(mDisplayContent.mWallpaperController).isWallpaperVisible();
+ spyOn(mDisplayContent.mAppTransition);
+
+ // Prepare a transition.
+ prepareAndTriggerAppTransition(activity, null /* closingActivity */, taskFragment);
+
+ // Should not be overridden when there is wallpaper in the transition.
+ verify(mDisplayContent.mAppTransition, never())
+ .overridePendingAppTransitionRemote(adapter, false /* sync */);
+ }
+
+ @Test
public void testTransitionGoodToGoForTaskFragments() {
final TaskFragmentOrganizer organizer = new TaskFragmentOrganizer(Runnable::run);
final Task task = createTask(mDisplayContent);
diff --git a/services/tests/wmtests/src/com/android/server/wm/AppWindowTokenAnimationTests.java b/services/tests/wmtests/src/com/android/server/wm/AppWindowTokenAnimationTests.java
index fd562c3..ebefeaf 100644
--- a/services/tests/wmtests/src/com/android/server/wm/AppWindowTokenAnimationTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/AppWindowTokenAnimationTests.java
@@ -75,13 +75,19 @@
@Test
public void clipAfterAnim_boundsLayerZBoosted() {
+ final Task task = mActivity.getTask();
+ final ActivityRecord topActivity = createActivityRecord(task);
+ task.assignChildLayers(mTransaction);
+
+ assertThat(topActivity.getLastLayer()).isGreaterThan(mActivity.getLastLayer());
+
mActivity.mNeedsAnimationBoundsLayer = true;
mActivity.mNeedsZBoost = true;
-
mActivity.mSurfaceAnimator.startAnimation(mTransaction, mSpec, true /* hidden */,
ANIMATION_TYPE_APP_TRANSITION);
+
verify(mTransaction).setLayer(eq(mActivity.mAnimationBoundsLayer),
- intThat(layer -> layer >= ActivityRecord.Z_BOOST_BASE));
+ intThat(layer -> layer > topActivity.getLastLayer()));
}
@Test
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 6d60bcf..a1c24c2 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TransitionTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TransitionTests.java
@@ -34,8 +34,11 @@
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
import android.os.IBinder;
import android.platform.test.annotations.Presubmit;
@@ -448,7 +451,8 @@
@Test
public void testIntermediateVisibility() {
- final TransitionController controller = new TransitionController(mAtm);
+ final TaskSnapshotController snapshotController = mock(TaskSnapshotController.class);
+ final TransitionController controller = new TransitionController(mAtm, snapshotController);
final ITransitionPlayer player = new ITransitionPlayer.Default();
controller.registerTransitionPlayer(player);
ITaskOrganizer mockOrg = mock(ITaskOrganizer.class);
@@ -511,6 +515,71 @@
assertTrue(activity2.isVisible());
}
+ @Test
+ public void testTransientLaunch() {
+ final TaskSnapshotController snapshotController = mock(TaskSnapshotController.class);
+ final TransitionController controller = new TransitionController(mAtm, snapshotController);
+ final ITransitionPlayer player = new ITransitionPlayer.Default();
+ controller.registerTransitionPlayer(player);
+ ITaskOrganizer mockOrg = mock(ITaskOrganizer.class);
+ final Transition openTransition = controller.createTransition(TRANSIT_OPEN);
+
+ // Start out with task2 visible and set up a transition that closes task2 and opens task1
+ final Task task1 = createTask(mDisplayContent);
+ task1.mTaskOrganizer = mockOrg;
+ final ActivityRecord activity1 = createActivityRecord(task1);
+ activity1.mVisibleRequested = false;
+ activity1.setVisible(false);
+ final Task task2 = createTask(mDisplayContent);
+ task2.mTaskOrganizer = mockOrg;
+ final ActivityRecord activity2 = createActivityRecord(task2);
+ activity2.mVisibleRequested = true;
+ activity2.setVisible(true);
+
+ openTransition.collectExistenceChange(task1);
+ openTransition.collectExistenceChange(activity1);
+ openTransition.collectExistenceChange(task2);
+ openTransition.collectExistenceChange(activity2);
+
+ activity1.mVisibleRequested = true;
+ activity1.setVisible(true);
+ activity2.mVisibleRequested = 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
+ // normally.
+ mWm.mSyncEngine.abort(openTransition.getSyncId());
+
+ verify(snapshotController, times(1)).recordTaskSnapshot(eq(task2), eq(false));
+
+ openTransition.finishTransition();
+
+ // We are now going to simulate closing task1 to return back to (open) task2.
+ final Transition closeTransition = controller.createTransition(TRANSIT_CLOSE);
+
+ closeTransition.collectExistenceChange(task1);
+ closeTransition.collectExistenceChange(activity1);
+ closeTransition.collectExistenceChange(task2);
+ closeTransition.collectExistenceChange(activity2);
+ closeTransition.setTransientLaunch(activity2);
+
+ activity1.mVisibleRequested = false;
+ activity2.mVisibleRequested = true;
+
+ // Using abort to force-finish the sync (since we obviously can't wait for drawing).
+ // We didn't call abort on the actual transition, so it will still run onTransactionReady
+ // normally.
+ mWm.mSyncEngine.abort(closeTransition.getSyncId());
+
+ // Make sure we haven't called recordSnapshot (since we are transient, it shouldn't be
+ // called until finish).
+ verify(snapshotController, times(0)).recordTaskSnapshot(eq(task1), eq(false));
+
+ closeTransition.finishTransition();
+
+ verify(snapshotController, times(1)).recordTaskSnapshot(eq(task1), eq(false));
+ }
+
/** Fill the change map with all the parents of top. Change maps are usually fully populated */
private static void fillChangeMap(ArrayMap<WindowContainer, Transition.ChangeInfo> changes,
WindowContainer top) {
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowContainerTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowContainerTests.java
index 68053eb..bbeb980 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowContainerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowContainerTests.java
@@ -25,6 +25,7 @@
import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
import static android.view.WindowManager.TRANSIT_CLOSE;
import static android.view.WindowManager.TRANSIT_OLD_TASK_CLOSE;
+import static android.view.WindowManager.TRANSIT_OLD_TASK_FRAGMENT_CHANGE;
import static android.view.WindowManager.TRANSIT_OLD_TASK_OPEN;
import static android.view.WindowManager.TRANSIT_OPEN;
import static android.window.DisplayAreaOrganizer.FEATURE_DEFAULT_TASK_CONTAINER;
@@ -52,6 +53,7 @@
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
@@ -1106,6 +1108,71 @@
verify(surfaceAnimator, never()).setRelativeLayer(any(), any(), anyInt());
}
+ @Test
+ public void testStartChangeTransitionWhenPreviousIsNotFinished() {
+ final WindowContainer container = createTaskFragmentWithParentTask(
+ createTask(mDisplayContent), false);
+ container.mSurfaceControl = mock(SurfaceControl.class);
+ final SurfaceAnimator surfaceAnimator = container.mSurfaceAnimator;
+ final SurfaceFreezer surfaceFreezer = container.mSurfaceFreezer;
+ final SurfaceControl.Transaction t = mock(SurfaceControl.Transaction.class);
+ spyOn(container);
+ spyOn(surfaceAnimator);
+ spyOn(surfaceFreezer);
+ doReturn(t).when(container).getPendingTransaction();
+ doReturn(t).when(container).getSyncTransaction();
+
+ // Leash and snapshot created for change transition.
+ container.initializeChangeTransition(new Rect(0, 0, 1000, 2000));
+ // Can't really take a snapshot, manually set one.
+ surfaceFreezer.mSnapshot = mock(SurfaceFreezer.Snapshot.class);
+
+ assertNotNull(surfaceFreezer.mLeash);
+ assertEquals(surfaceFreezer.mLeash, container.getAnimationLeash());
+
+ // Start animation: surfaceAnimator take over the leash and snapshot from surfaceFreezer.
+ container.applyAnimationUnchecked(null /* lp */, true /* enter */,
+ TRANSIT_OLD_TASK_FRAGMENT_CHANGE, false /* isVoiceInteraction */,
+ null /* sources */);
+
+ assertNull(surfaceFreezer.mLeash);
+ assertNull(surfaceFreezer.mSnapshot);
+ assertNotNull(surfaceAnimator.mLeash);
+ assertNotNull(surfaceAnimator.mSnapshot);
+ final SurfaceControl prevLeash = surfaceAnimator.mLeash;
+ final SurfaceFreezer.Snapshot prevSnapshot = surfaceAnimator.mSnapshot;
+
+ // Prepare another change transition.
+ container.initializeChangeTransition(new Rect(0, 0, 1000, 2000));
+ surfaceFreezer.mSnapshot = mock(SurfaceFreezer.Snapshot.class);
+
+ assertNotNull(surfaceFreezer.mLeash);
+ assertEquals(surfaceFreezer.mLeash, container.getAnimationLeash());
+ assertNotEquals(prevLeash, container.getAnimationLeash());
+
+ // Start another animation before the previous one is finished, it should reset the previous
+ // one, but not change the current one.
+ container.applyAnimationUnchecked(null /* lp */, true /* enter */,
+ TRANSIT_OLD_TASK_FRAGMENT_CHANGE, false /* isVoiceInteraction */,
+ null /* sources */);
+
+ verify(container, never()).onAnimationLeashLost(any());
+ verify(surfaceFreezer, never()).unfreeze(any());
+ assertNotNull(surfaceAnimator.mLeash);
+ assertNotNull(surfaceAnimator.mSnapshot);
+ assertEquals(surfaceAnimator.mLeash, container.getAnimationLeash());
+ assertNotEquals(prevLeash, surfaceAnimator.mLeash);
+ assertNotEquals(prevSnapshot, surfaceAnimator.mSnapshot);
+
+ // Clean up after animation finished.
+ surfaceAnimator.mInnerAnimationFinishedCallback.onAnimationFinished(
+ ANIMATION_TYPE_APP_TRANSITION, surfaceAnimator.getAnimation());
+
+ verify(container).onAnimationLeashLost(any());
+ assertNull(surfaceAnimator.mLeash);
+ assertNull(surfaceAnimator.mSnapshot);
+ }
+
/* Used so we can gain access to some protected members of the {@link WindowContainer} class */
private static class TestWindowContainer extends WindowContainer<TestWindowContainer> {
private final int mLayer;
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/CommonAssertions.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/CommonAssertions.kt
index 2cc1943..64cb790 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/CommonAssertions.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/CommonAssertions.kt
@@ -17,22 +17,29 @@
@file:JvmName("CommonAssertions")
package com.android.server.wm.flicker
-import android.content.ComponentName
import com.android.server.wm.flicker.helpers.WindowUtils
-import com.android.server.wm.traces.parser.windowmanager.WindowManagerStateHelper
+import com.android.server.wm.traces.common.FlickerComponentName
-val LAUNCHER_COMPONENT = ComponentName("com.google.android.apps.nexuslauncher",
+val LAUNCHER_COMPONENT = FlickerComponentName("com.google.android.apps.nexuslauncher",
"com.google.android.apps.nexuslauncher.NexusLauncherActivity")
+/**
+ * Checks that [FlickerComponentName.STATUS_BAR] window is visible and above the app windows in
+ * all WM trace entries
+ */
fun FlickerTestParameter.statusBarWindowIsVisible() {
assertWm {
- this.isAboveAppWindowVisible(WindowManagerStateHelper.STATUS_BAR_COMPONENT)
+ this.isAboveAppWindowVisible(FlickerComponentName.STATUS_BAR)
}
}
+/**
+ * Checks that [FlickerComponentName.NAV_BAR] window is visible and above the app windows in
+ * all WM trace entries
+ */
fun FlickerTestParameter.navBarWindowIsVisible() {
assertWm {
- this.isAboveAppWindowVisible(WindowManagerStateHelper.NAV_BAR_COMPONENT)
+ this.isAboveAppWindowVisible(FlickerComponentName.NAV_BAR)
}
}
@@ -69,53 +76,59 @@
}
}
+/**
+ * Checks that [FlickerComponentName.NAV_BAR] layer is visible at the start and end of the SF
+ * trace
+ */
fun FlickerTestParameter.navBarLayerIsVisible() {
assertLayersStart {
- this.isVisible(WindowManagerStateHelper.NAV_BAR_COMPONENT)
+ this.isVisible(FlickerComponentName.NAV_BAR)
}
assertLayersEnd {
- this.isVisible(WindowManagerStateHelper.NAV_BAR_COMPONENT)
+ this.isVisible(FlickerComponentName.NAV_BAR)
}
}
+/**
+ * Checks that [FlickerComponentName.STATUS_BAR] layer is visible at the start and end of the SF
+ * trace
+ */
fun FlickerTestParameter.statusBarLayerIsVisible() {
assertLayersStart {
- this.isVisible(WindowManagerStateHelper.STATUS_BAR_COMPONENT)
+ this.isVisible(FlickerComponentName.STATUS_BAR)
}
assertLayersEnd {
- this.isVisible(WindowManagerStateHelper.STATUS_BAR_COMPONENT)
+ this.isVisible(FlickerComponentName.STATUS_BAR)
}
}
-@JvmOverloads
-fun FlickerTestParameter.navBarLayerRotatesAndScales(
- beginRotation: Int,
- endRotation: Int = beginRotation
-) {
- val startingPos = WindowUtils.getNavigationBarPosition(beginRotation)
- val endingPos = WindowUtils.getNavigationBarPosition(endRotation)
-
+fun FlickerTestParameter.navBarLayerRotatesAndScales() {
assertLayersStart {
- this.visibleRegion(WindowManagerStateHelper.NAV_BAR_COMPONENT).coversExactly(startingPos)
+ val display = this.entry.displays.minByOrNull { it.id }
+ ?: throw RuntimeException("There is no display!")
+ this.visibleRegion(FlickerComponentName.NAV_BAR)
+ .coversExactly(WindowUtils.getNavigationBarPosition(display))
}
assertLayersEnd {
- this.visibleRegion(WindowManagerStateHelper.NAV_BAR_COMPONENT).coversExactly(endingPos)
+ val display = this.entry.displays.minByOrNull { it.id }
+ ?: throw RuntimeException("There is no display!")
+ this.visibleRegion(FlickerComponentName.NAV_BAR)
+ .coversExactly(WindowUtils.getNavigationBarPosition(display))
}
}
-@JvmOverloads
-fun FlickerTestParameter.statusBarLayerRotatesScales(
- beginRotation: Int,
- endRotation: Int = beginRotation
-) {
- val startingPos = WindowUtils.getStatusBarPosition(beginRotation)
- val endingPos = WindowUtils.getStatusBarPosition(endRotation)
-
+fun FlickerTestParameter.statusBarLayerRotatesScales() {
assertLayersStart {
- this.visibleRegion(WindowManagerStateHelper.STATUS_BAR_COMPONENT).coversExactly(startingPos)
+ val display = this.entry.displays.minByOrNull { it.id }
+ ?: throw RuntimeException("There is no display!")
+ this.visibleRegion(FlickerComponentName.STATUS_BAR)
+ .coversExactly(WindowUtils.getStatusBarPosition(display))
}
assertLayersEnd {
- this.visibleRegion(WindowManagerStateHelper.STATUS_BAR_COMPONENT).coversExactly(endingPos)
+ val display = this.entry.displays.minByOrNull { it.id }
+ ?: throw RuntimeException("There is no display!")
+ this.visibleRegion(FlickerComponentName.STATUS_BAR)
+ .coversExactly(WindowUtils.getStatusBarPosition(display))
}
}
@@ -132,15 +145,15 @@
* (useful mostly for app launch)
*/
fun FlickerTestParameter.replacesLayer(
- originalLayer: ComponentName,
- newLayer: ComponentName,
+ originalLayer: FlickerComponentName,
+ newLayer: FlickerComponentName,
ignoreSnapshot: Boolean = false
) {
assertLayers {
val assertion = this.isVisible(originalLayer)
if (ignoreSnapshot) {
assertion.then()
- .isVisible(WindowManagerStateHelper.SNAPSHOT_COMPONENT, isOptional = true)
+ .isVisible(FlickerComponentName.SNAPSHOT, isOptional = true)
}
assertion.then().isVisible(newLayer)
}
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppBackButtonTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppBackButtonTest.kt
index 90c851d..9f26c31 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppBackButtonTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppBackButtonTest.kt
@@ -17,13 +17,12 @@
package com.android.server.wm.flicker.close
-import android.platform.test.annotations.Postsubmit
import androidx.test.filters.FlakyTest
import androidx.test.filters.RequiresDevice
import com.android.server.wm.flicker.FlickerParametersRunnerFactory
import com.android.server.wm.flicker.FlickerTestParameter
import com.android.server.wm.flicker.FlickerTestParameterFactory
-import com.android.server.wm.flicker.annotation.Group1
+import com.android.server.wm.flicker.annotation.Group4
import com.android.server.wm.flicker.dsl.FlickerBuilder
import org.junit.FixMethodOrder
import org.junit.Test
@@ -33,13 +32,38 @@
/**
* Test app closes by pressing back button
+ *
* To run this test: `atest FlickerTests:CloseAppBackButtonTest`
+ *
+ * Actions:
+ * Make sure no apps are running on the device
+ * Launch an app [testApp] and wait animation to complete
+ * Press back button
+ *
+ * To run only the presubmit assertions add: `--
+ * --module-arg FlickerTests:exclude-annotation:androidx.test.filters.FlakyTest
+ * --module-arg FlickerTests:include-annotation:android.platform.test.annotations.Presubmit`
+ *
+ * To run only the postsubmit assertions add: `--
+ * --module-arg FlickerTests:exclude-annotation:androidx.test.filters.FlakyTest
+ * --module-arg FlickerTests:include-annotation:android.platform.test.annotations.Postsubmit`
+ *
+ * To run only the flaky assertions add: `--
+ * --module-arg FlickerTests:include-annotation:androidx.test.filters.FlakyTest`
+ *
+ * Notes:
+ * 1. Some default assertions (e.g., nav bar, status bar and screen covered)
+ * are inherited [CloseAppTransition]
+ * 2. Part of the test setup occurs automatically via
+ * [com.android.server.wm.flicker.TransitionRunnerWithRules],
+ * including configuring navigation mode, initial orientation and ensuring no
+ * apps are running before setup
*/
@RequiresDevice
@RunWith(Parameterized::class)
@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-@Group1
+@Group4
class CloseAppBackButtonTest(testSpec: FlickerTestParameter) : CloseAppTransition(testSpec) {
override val transition: FlickerBuilder.(Map<String, Any?>) -> Unit
get() = {
@@ -50,14 +74,18 @@
}
}
+ /** {@inheritDoc} */
@FlakyTest
@Test
override fun navBarLayerRotatesAndScales() = super.navBarLayerRotatesAndScales()
- @Postsubmit
- override fun navBarLayerIsVisible() = super.navBarLayerIsVisible()
-
companion object {
+ /**
+ * Creates the test configurations.
+ *
+ * See [FlickerTestParameterFactory.getConfigNonRotationTests] for configuring
+ * repetitions, screen orientation and navigation modes.
+ */
@Parameterized.Parameters(name = "{0}")
@JvmStatic
fun getParams(): List<FlickerTestParameter> {
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppHomeButtonTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppHomeButtonTest.kt
index e8391ed..795766f 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppHomeButtonTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppHomeButtonTest.kt
@@ -16,13 +16,12 @@
package com.android.server.wm.flicker.close
-import android.platform.test.annotations.Postsubmit
import androidx.test.filters.FlakyTest
import androidx.test.filters.RequiresDevice
import com.android.server.wm.flicker.FlickerParametersRunnerFactory
import com.android.server.wm.flicker.FlickerTestParameter
import com.android.server.wm.flicker.FlickerTestParameterFactory
-import com.android.server.wm.flicker.annotation.Group1
+import com.android.server.wm.flicker.annotation.Group4
import com.android.server.wm.flicker.dsl.FlickerBuilder
import org.junit.FixMethodOrder
import org.junit.Test
@@ -31,14 +30,39 @@
import org.junit.runners.Parameterized
/**
- * Test app closes by pressing home button.
+ * Test app closes by pressing home button
+ *
* To run this test: `atest FlickerTests:CloseAppHomeButtonTest`
+ *
+ * Actions:
+ * Make sure no apps are running on the device
+ * Launch an app [testApp] and wait animation to complete
+ * Press home button
+ *
+ * To run only the presubmit assertions add: `--
+ * --module-arg FlickerTests:exclude-annotation:androidx.test.filters.FlakyTest
+ * --module-arg FlickerTests:include-annotation:android.platform.test.annotations.Presubmit`
+ *
+ * To run only the postsubmit assertions add: `--
+ * --module-arg FlickerTests:exclude-annotation:androidx.test.filters.FlakyTest
+ * --module-arg FlickerTests:include-annotation:android.platform.test.annotations.Postsubmit`
+ *
+ * To run only the flaky assertions add: `--
+ * --module-arg FlickerTests:include-annotation:androidx.test.filters.FlakyTest`
+ *
+ * Notes:
+ * 1. Some default assertions (e.g., nav bar, status bar and screen covered)
+ * are inherited [CloseAppTransition]
+ * 2. Part of the test setup occurs automatically via
+ * [com.android.server.wm.flicker.TransitionRunnerWithRules],
+ * including configuring navigation mode, initial orientation and ensuring no
+ * apps are running before setup
*/
@RequiresDevice
@RunWith(Parameterized::class)
@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-@Group1
+@Group4
class CloseAppHomeButtonTest(testSpec: FlickerTestParameter) : CloseAppTransition(testSpec) {
override val transition: FlickerBuilder.(Map<String, Any?>) -> Unit
get() = {
@@ -49,14 +73,18 @@
}
}
+ /** {@inheritDoc} */
@FlakyTest
@Test
override fun navBarLayerRotatesAndScales() = super.navBarLayerRotatesAndScales()
- @Postsubmit
- override fun navBarLayerIsVisible() = super.navBarLayerIsVisible()
-
companion object {
+ /**
+ * Creates the test configurations.
+ *
+ * See [FlickerTestParameterFactory.getConfigNonRotationTests] for configuring
+ * repetitions, screen orientation and navigation modes.
+ */
@Parameterized.Parameters(name = "{0}")
@JvmStatic
fun getParams(): Collection<FlickerTestParameter> {
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppTransition.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppTransition.kt
index 1efb6da..511fc26 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppTransition.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppTransition.kt
@@ -18,7 +18,6 @@
import android.app.Instrumentation
import android.platform.test.annotations.Presubmit
-import android.view.Surface
import androidx.test.platform.app.InstrumentationRegistry
import com.android.server.wm.flicker.FlickerBuilderProvider
import com.android.server.wm.flicker.FlickerTestParameter
@@ -62,6 +61,10 @@
}
}
+ /**
+ * Entry point for the test runner. It will use this method to initialize and cache
+ * flicker executions
+ */
@FlickerBuilderProvider
fun buildFlicker(): FlickerBuilder {
return FlickerBuilder(instrumentation).apply {
@@ -69,42 +72,60 @@
}
}
+ /**
+ * Checks that the navigation bar window is visible during the whole transition
+ */
@Presubmit
@Test
open fun navBarWindowIsVisible() {
testSpec.navBarWindowIsVisible()
}
+ /**
+ * Checks that the status bar window is visible during the whole transition
+ */
@Presubmit
@Test
open fun statusBarWindowIsVisible() {
testSpec.statusBarWindowIsVisible()
}
+ /**
+ * Checks that the navigation bar layer is visible during the whole transition
+ */
@Presubmit
@Test
open fun navBarLayerIsVisible() {
testSpec.navBarLayerIsVisible()
}
+ /**
+ * Checks that the status bar layer is visible during the whole transition
+ */
@Presubmit
@Test
open fun statusBarLayerIsVisible() {
testSpec.statusBarLayerIsVisible()
}
+ /**
+ * Checks the position of the navigation bar at the start and end of the transition
+ */
@Presubmit
@Test
- open fun navBarLayerRotatesAndScales() {
- testSpec.navBarLayerRotatesAndScales(testSpec.config.startRotation, Surface.ROTATION_0)
- }
+ open fun navBarLayerRotatesAndScales() = testSpec.navBarLayerRotatesAndScales()
+ /**
+ * Checks the position of the status bar at the start and end of the transition
+ */
@Presubmit
@Test
- open fun statusBarLayerRotatesScales() {
- testSpec.statusBarLayerRotatesScales(testSpec.config.startRotation, Surface.ROTATION_0)
- }
+ open fun statusBarLayerRotatesScales() = testSpec.statusBarLayerRotatesScales()
+ /**
+ * Checks that all windows that are visible on the trace, are visible for at least 2
+ * consecutive entries.
+ */
@Presubmit
@Test
open fun visibleWindowsShownMoreThanOneConsecutiveEntry() {
@@ -113,6 +134,10 @@
}
}
+ /**
+ * Checks that all layers that are visible on the trace, are visible for at least 2
+ * consecutive entries.
+ */
@Presubmit
@Test
open fun visibleLayersShownMoreThanOneConsecutiveEntry() {
@@ -121,10 +146,17 @@
}
}
+ /**
+ * Checks that all parts of the screen are covered during the transition
+ */
@Presubmit
@Test
open fun entireScreenCovered() = testSpec.entireScreenCovered()
+ /**
+ * Checks that [testApp] is the top visible app window at the start of the transition and
+ * that it is replaced by [LAUNCHER_COMPONENT] during the transition
+ */
@Presubmit
@Test
open fun launcherReplacesAppWindowAsTopWindow() {
@@ -135,19 +167,26 @@
}
}
+ /**
+ * Checks that [LAUNCHER_COMPONENT] is invisible at the start of the transition and that
+ * it becomes visible during the transition
+ */
@Presubmit
@Test
open fun launcherWindowBecomesVisible() {
testSpec.assertWm {
- this.isAppWindowInvisible(LAUNCHER_COMPONENT)
+ this.isAppWindowNotOnTop(LAUNCHER_COMPONENT)
.then()
.isAppWindowOnTop(LAUNCHER_COMPONENT)
}
}
+ /**
+ * Checks that [LAUNCHER_COMPONENT] layer becomes visible when [testApp] becomes invisible
+ */
@Presubmit
@Test
open fun launcherLayerReplacesApp() {
testSpec.replacesLayer(testApp.component, LAUNCHER_COMPONENT)
}
-}
+}
\ No newline at end of file
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/FlickerExtensions.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/FlickerExtensions.kt
index fad25b4..75900df 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/FlickerExtensions.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/FlickerExtensions.kt
@@ -14,6 +14,7 @@
* limitations under the License.
*/
+@file:JvmName("FlickerExtensions")
package com.android.server.wm.flicker.helpers
import com.android.server.wm.flicker.Flicker
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/ImeAppAutoFocusHelper.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/ImeAppAutoFocusHelper.kt
index bd7c185..0b1748a 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/ImeAppAutoFocusHelper.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/ImeAppAutoFocusHelper.kt
@@ -17,9 +17,10 @@
package com.android.server.wm.flicker.helpers
import android.app.Instrumentation
-import android.content.ComponentName
import androidx.test.uiautomator.UiDevice
import com.android.server.wm.flicker.testapp.ActivityOptions
+import com.android.server.wm.traces.common.FlickerComponentName
+import com.android.server.wm.traces.parser.toFlickerComponent
import com.android.server.wm.traces.parser.windowmanager.WindowManagerStateHelper
class ImeAppAutoFocusHelper @JvmOverloads constructor(
@@ -27,7 +28,8 @@
private val rotation: Int,
private val imePackageName: String = IME_PACKAGE,
launcherName: String = ActivityOptions.IME_ACTIVITY_AUTO_FOCUS_LAUNCHER_NAME,
- component: ComponentName = ActivityOptions.IME_ACTIVITY_AUTO_FOCUS_COMPONENT_NAME
+ component: FlickerComponentName =
+ ActivityOptions.IME_ACTIVITY_AUTO_FOCUS_COMPONENT_NAME.toFlickerComponent()
) : ImeAppHelper(instr, launcherName, component) {
override fun openIME(
device: UiDevice,
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/ImeAppHelper.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/ImeAppHelper.kt
index d224af9..1c2164a 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/ImeAppHelper.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/ImeAppHelper.kt
@@ -17,19 +17,21 @@
package com.android.server.wm.flicker.helpers
import android.app.Instrumentation
-import android.content.ComponentName
import android.support.test.launcherhelper.ILauncherStrategy
import android.support.test.launcherhelper.LauncherStrategyFactory
import androidx.test.uiautomator.By
import androidx.test.uiautomator.UiDevice
import androidx.test.uiautomator.Until
import com.android.server.wm.flicker.testapp.ActivityOptions
+import com.android.server.wm.traces.common.FlickerComponentName
+import com.android.server.wm.traces.parser.toFlickerComponent
import com.android.server.wm.traces.parser.windowmanager.WindowManagerStateHelper
open class ImeAppHelper @JvmOverloads constructor(
instr: Instrumentation,
launcherName: String = ActivityOptions.IME_ACTIVITY_LAUNCHER_NAME,
- component: ComponentName = ActivityOptions.IME_ACTIVITY_COMPONENT_NAME,
+ component: FlickerComponentName =
+ ActivityOptions.IME_ACTIVITY_COMPONENT_NAME.toFlickerComponent(),
launcherStrategy: ILauncherStrategy = LauncherStrategyFactory
.getInstance(instr)
.launcherStrategy
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/NewTasksAppHelper.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/NewTasksAppHelper.kt
new file mode 100644
index 0000000..be68704
--- /dev/null
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/NewTasksAppHelper.kt
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.wm.flicker.helpers
+
+import android.app.Instrumentation
+import android.support.test.launcherhelper.ILauncherStrategy
+import android.support.test.launcherhelper.LauncherStrategyFactory
+import androidx.test.uiautomator.By
+import androidx.test.uiautomator.UiDevice
+import androidx.test.uiautomator.Until
+import com.android.server.wm.flicker.testapp.ActivityOptions
+import com.android.server.wm.traces.common.FlickerComponentName
+import com.android.server.wm.traces.parser.toFlickerComponent
+import com.android.server.wm.traces.parser.windowmanager.WindowManagerStateHelper
+
+class NewTasksAppHelper @JvmOverloads constructor(
+ instr: Instrumentation,
+ launcherName: String = ActivityOptions.LAUNCH_NEW_TASK_ACTIVITY_LAUNCHER_NAME,
+ component: FlickerComponentName =
+ ActivityOptions.LAUNCH_NEW_TASK_ACTIVITY_COMPONENT_NAME.toFlickerComponent(),
+ launcherStrategy: ILauncherStrategy = LauncherStrategyFactory
+ .getInstance(instr)
+ .launcherStrategy
+) : StandardAppHelper(instr, launcherName, component, launcherStrategy) {
+ fun openNewTask(device: UiDevice, wmHelper: WindowManagerStateHelper) {
+ val button = device.wait(
+ Until.findObject(By.res(getPackage(), "launch_new_task")),
+ FIND_TIMEOUT)
+
+ require(button != null) {
+ "Button not found, this usually happens when the device " +
+ "was left in an unknown state (e.g. in split screen)"
+ }
+ button.click()
+ wmHelper.waitForAppTransitionIdle()
+ wmHelper.waitForFullScreenApp(component)
+ }
+}
\ No newline at end of file
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/NonResizeableAppHelper.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/NonResizeableAppHelper.kt
index 3074e28..f7ca5ce 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/NonResizeableAppHelper.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/NonResizeableAppHelper.kt
@@ -17,15 +17,17 @@
package com.android.server.wm.flicker.helpers
import android.app.Instrumentation
-import android.content.ComponentName
import android.support.test.launcherhelper.ILauncherStrategy
import android.support.test.launcherhelper.LauncherStrategyFactory
import com.android.server.wm.flicker.testapp.ActivityOptions
+import com.android.server.wm.traces.common.FlickerComponentName
+import com.android.server.wm.traces.parser.toFlickerComponent
class NonResizeableAppHelper @JvmOverloads constructor(
instr: Instrumentation,
launcherName: String = ActivityOptions.NON_RESIZEABLE_ACTIVITY_LAUNCHER_NAME,
- component: ComponentName = ActivityOptions.NON_RESIZEABLE_ACTIVITY_COMPONENT_NAME,
+ component: FlickerComponentName =
+ ActivityOptions.NON_RESIZEABLE_ACTIVITY_COMPONENT_NAME.toFlickerComponent(),
launcherStrategy: ILauncherStrategy = LauncherStrategyFactory
.getInstance(instr)
.launcherStrategy
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/SeamlessRotationAppHelper.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/SeamlessRotationAppHelper.kt
index 02be3cf..7bab981 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/SeamlessRotationAppHelper.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/SeamlessRotationAppHelper.kt
@@ -17,15 +17,17 @@
package com.android.server.wm.flicker.helpers
import android.app.Instrumentation
-import android.content.ComponentName
import android.support.test.launcherhelper.ILauncherStrategy
import android.support.test.launcherhelper.LauncherStrategyFactory
import com.android.server.wm.flicker.testapp.ActivityOptions
+import com.android.server.wm.traces.common.FlickerComponentName
+import com.android.server.wm.traces.parser.toFlickerComponent
class SeamlessRotationAppHelper @JvmOverloads constructor(
instr: Instrumentation,
launcherName: String = ActivityOptions.SEAMLESS_ACTIVITY_LAUNCHER_NAME,
- component: ComponentName = ActivityOptions.SEAMLESS_ACTIVITY_COMPONENT_NAME,
+ component: FlickerComponentName =
+ ActivityOptions.SEAMLESS_ACTIVITY_COMPONENT_NAME.toFlickerComponent(),
launcherStrategy: ILauncherStrategy = LauncherStrategyFactory
.getInstance(instr)
.launcherStrategy
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/SimpleAppHelper.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/SimpleAppHelper.kt
index d7cbaae..f6a8817 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/SimpleAppHelper.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/SimpleAppHelper.kt
@@ -17,15 +17,17 @@
package com.android.server.wm.flicker.helpers
import android.app.Instrumentation
-import android.content.ComponentName
import android.support.test.launcherhelper.ILauncherStrategy
import android.support.test.launcherhelper.LauncherStrategyFactory
import com.android.server.wm.flicker.testapp.ActivityOptions
+import com.android.server.wm.traces.common.FlickerComponentName
+import com.android.server.wm.traces.parser.toFlickerComponent
class SimpleAppHelper @JvmOverloads constructor(
instr: Instrumentation,
launcherName: String = ActivityOptions.SIMPLE_ACTIVITY_LAUNCHER_NAME,
- component: ComponentName = ActivityOptions.SIMPLE_ACTIVITY_AUTO_FOCUS_COMPONENT_NAME,
+ component: FlickerComponentName =
+ ActivityOptions.SIMPLE_ACTIVITY_AUTO_FOCUS_COMPONENT_NAME.toFlickerComponent(),
launcherStrategy: ILauncherStrategy = LauncherStrategyFactory
.getInstance(instr)
.launcherStrategy
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/TwoActivitiesAppHelper.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/TwoActivitiesAppHelper.kt
index 19fefb9..59e8dc8 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/TwoActivitiesAppHelper.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/TwoActivitiesAppHelper.kt
@@ -17,19 +17,21 @@
package com.android.server.wm.flicker.helpers
import android.app.Instrumentation
-import android.content.ComponentName
import android.support.test.launcherhelper.ILauncherStrategy
import android.support.test.launcherhelper.LauncherStrategyFactory
import androidx.test.uiautomator.By
import androidx.test.uiautomator.UiDevice
import androidx.test.uiautomator.Until
import com.android.server.wm.flicker.testapp.ActivityOptions
+import com.android.server.wm.traces.common.FlickerComponentName
+import com.android.server.wm.traces.parser.toFlickerComponent
import com.android.server.wm.traces.parser.windowmanager.WindowManagerStateHelper
class TwoActivitiesAppHelper @JvmOverloads constructor(
instr: Instrumentation,
launcherName: String = ActivityOptions.BUTTON_ACTIVITY_LAUNCHER_NAME,
- component: ComponentName = ActivityOptions.BUTTON_ACTIVITY_COMPONENT_NAME,
+ component: FlickerComponentName =
+ ActivityOptions.BUTTON_ACTIVITY_COMPONENT_NAME.toFlickerComponent(),
launcherStrategy: ILauncherStrategy = LauncherStrategyFactory
.getInstance(instr)
.launcherStrategy
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeAutoOpenWindowToAppTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeAutoOpenWindowToAppTest.kt
index d17e77d..5e21aff 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeAutoOpenWindowToAppTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeAutoOpenWindowToAppTest.kt
@@ -37,7 +37,7 @@
import com.android.server.wm.flicker.statusBarLayerIsVisible
import com.android.server.wm.flicker.statusBarLayerRotatesScales
import com.android.server.wm.flicker.statusBarWindowIsVisible
-import com.android.server.wm.traces.parser.windowmanager.WindowManagerStateHelper
+import com.android.server.wm.traces.common.FlickerComponentName
import org.junit.FixMethodOrder
import org.junit.Test
import org.junit.runner.RunWith
@@ -125,7 +125,7 @@
@Test
fun imeLayerVisibleStart() {
testSpec.assertLayersStart {
- this.isVisible(WindowManagerStateHelper.IME_COMPONENT)
+ this.isVisible(FlickerComponentName.IME)
}
}
@@ -133,7 +133,7 @@
@Test
fun imeLayerInvisibleEnd() {
testSpec.assertLayersEnd {
- this.isInvisible(WindowManagerStateHelper.IME_COMPONENT)
+ this.isInvisible(FlickerComponentName.IME)
}
}
@@ -151,15 +151,11 @@
@Presubmit
@Test
- fun navBarLayerRotatesAndScales() {
- testSpec.navBarLayerRotatesAndScales(testSpec.config.startRotation)
- }
+ fun navBarLayerRotatesAndScales() = testSpec.navBarLayerRotatesAndScales()
@Presubmit
@Test
- fun statusBarLayerRotatesScales() {
- testSpec.statusBarLayerRotatesScales(testSpec.config.startRotation)
- }
+ fun statusBarLayerRotatesScales() = testSpec.statusBarLayerRotatesScales()
@Presubmit
@Test
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeAutoOpenWindowToHomeTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeAutoOpenWindowToHomeTest.kt
index 6f0f55a..0582685 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeAutoOpenWindowToHomeTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeAutoOpenWindowToHomeTest.kt
@@ -38,7 +38,7 @@
import com.android.server.wm.flicker.statusBarLayerIsVisible
import com.android.server.wm.flicker.statusBarLayerRotatesScales
import com.android.server.wm.flicker.statusBarWindowIsVisible
-import com.android.server.wm.traces.parser.windowmanager.WindowManagerStateHelper
+import com.android.server.wm.traces.common.FlickerComponentName
import org.junit.FixMethodOrder
import org.junit.Test
import org.junit.runner.RunWith
@@ -110,7 +110,7 @@
testSpec.assertWm {
this.isAppWindowOnTop(testApp.component)
.then()
- .appWindowNotOnTop(testApp.component)
+ .isAppWindowNotOnTop(testApp.component)
}
}
@@ -122,7 +122,7 @@
@Test
fun imeLayerVisibleStart() {
testSpec.assertLayersStart {
- this.isVisible(WindowManagerStateHelper.IME_COMPONENT)
+ this.isVisible(FlickerComponentName.IME)
}
}
@@ -130,7 +130,7 @@
@Test
fun imeLayerInvisibleEnd() {
testSpec.assertLayersEnd {
- this.isInvisible(WindowManagerStateHelper.IME_COMPONENT)
+ this.isInvisible(FlickerComponentName.IME)
}
}
@@ -150,15 +150,11 @@
@Presubmit
@Test
- fun navBarLayerRotatesAndScales() {
- testSpec.navBarLayerRotatesAndScales(testSpec.config.startRotation, Surface.ROTATION_0)
- }
+ fun navBarLayerRotatesAndScales() = testSpec.navBarLayerRotatesAndScales()
@Presubmit
@Test
- fun statusBarLayerRotatesScales() {
- testSpec.statusBarLayerRotatesScales(testSpec.config.startRotation, Surface.ROTATION_0)
- }
+ fun statusBarLayerRotatesScales() = testSpec.statusBarLayerRotatesScales()
@Presubmit
@Test
@@ -173,8 +169,8 @@
fun visibleLayersShownMoreThanOneConsecutiveEntry() {
testSpec.assertLayers {
this.visibleLayersShownMoreThanOneConsecutiveEntry(listOf(
- WindowManagerStateHelper.IME_COMPONENT,
- WindowManagerStateHelper.SPLASH_SCREEN_COMPONENT))
+ FlickerComponentName.IME,
+ FlickerComponentName.SPLASH_SCREEN))
}
}
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeWindowToAppTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeWindowToAppTest.kt
index 6751439..91b3d3d 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeWindowToAppTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeWindowToAppTest.kt
@@ -32,11 +32,10 @@
import com.android.server.wm.flicker.navBarLayerRotatesAndScales
import com.android.server.wm.flicker.navBarWindowIsVisible
import com.android.server.wm.flicker.entireScreenCovered
-import com.android.server.wm.flicker.startRotation
import com.android.server.wm.flicker.statusBarLayerRotatesScales
import com.android.server.wm.flicker.statusBarWindowIsVisible
+import com.android.server.wm.traces.common.FlickerComponentName
import org.junit.Assume
-import com.android.server.wm.traces.parser.windowmanager.WindowManagerStateHelper
import org.junit.FixMethodOrder
import org.junit.Test
import org.junit.runner.RunWith
@@ -91,9 +90,9 @@
fun visibleWindowsShownMoreThanOneConsecutiveEntry() {
testSpec.assertWm {
this.visibleWindowsShownMoreThanOneConsecutiveEntry(listOf(
- WindowManagerStateHelper.IME_COMPONENT,
- WindowManagerStateHelper.SPLASH_SCREEN_COMPONENT,
- WindowManagerStateHelper.SNAPSHOT_COMPONENT))
+ FlickerComponentName.IME,
+ FlickerComponentName.SPLASH_SCREEN,
+ FlickerComponentName.SNAPSHOT))
}
}
@@ -121,21 +120,19 @@
@Test
fun navBarLayerRotatesAndScales() {
Assume.assumeFalse(testSpec.isRotated)
- testSpec.navBarLayerRotatesAndScales(testSpec.config.startRotation)
+ testSpec.navBarLayerRotatesAndScales()
}
@FlakyTest
@Test
fun navBarLayerRotatesAndScales_Flaky() {
Assume.assumeTrue(testSpec.isRotated)
- testSpec.navBarLayerRotatesAndScales(testSpec.config.startRotation)
+ testSpec.navBarLayerRotatesAndScales()
}
@Presubmit
@Test
- fun statusBarLayerRotatesScales() {
- testSpec.statusBarLayerRotatesScales(testSpec.config.startRotation)
- }
+ fun statusBarLayerRotatesScales() = testSpec.statusBarLayerRotatesScales()
@Presubmit
@Test
@@ -165,4 +162,4 @@
.getConfigNonRotationTests(repetitions = 5)
}
}
-}
+}
\ No newline at end of file
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeWindowToHomeTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeWindowToHomeTest.kt
index 8aaf925..b589969 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeWindowToHomeTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeWindowToHomeTest.kt
@@ -34,10 +34,9 @@
import com.android.server.wm.flicker.navBarLayerRotatesAndScales
import com.android.server.wm.flicker.navBarWindowIsVisible
import com.android.server.wm.flicker.entireScreenCovered
-import com.android.server.wm.flicker.startRotation
import com.android.server.wm.flicker.statusBarLayerRotatesScales
import com.android.server.wm.flicker.statusBarWindowIsVisible
-import com.android.server.wm.traces.parser.windowmanager.WindowManagerStateHelper
+import com.android.server.wm.traces.common.FlickerComponentName
import org.junit.FixMethodOrder
import org.junit.Test
import org.junit.runner.RunWith
@@ -95,9 +94,9 @@
fun visibleWindowsShownMoreThanOneConsecutiveEntry() {
testSpec.assertWm {
this.visibleWindowsShownMoreThanOneConsecutiveEntry(listOf(
- WindowManagerStateHelper.IME_COMPONENT,
- WindowManagerStateHelper.SPLASH_SCREEN_COMPONENT,
- WindowManagerStateHelper.SNAPSHOT_COMPONENT))
+ FlickerComponentName.IME,
+ FlickerComponentName.SPLASH_SCREEN,
+ FlickerComponentName.SNAPSHOT))
}
}
@@ -143,22 +142,19 @@
@Presubmit
@Test
- fun navBarLayerRotatesAndScales() =
- testSpec.navBarLayerRotatesAndScales(testSpec.config.startRotation, Surface.ROTATION_0)
+ fun navBarLayerRotatesAndScales() = testSpec.navBarLayerRotatesAndScales()
@Presubmit
@Test
- fun statusBarLayerRotatesScales() {
- testSpec.statusBarLayerRotatesScales(testSpec.config.startRotation, Surface.ROTATION_0)
- }
+ fun statusBarLayerRotatesScales() = testSpec.statusBarLayerRotatesScales()
@Presubmit
@Test
fun visibleLayersShownMoreThanOneConsecutiveEntry() {
testSpec.assertLayers {
this.visibleLayersShownMoreThanOneConsecutiveEntry(listOf(
- WindowManagerStateHelper.IME_COMPONENT,
- WindowManagerStateHelper.SPLASH_SCREEN_COMPONENT))
+ FlickerComponentName.IME,
+ FlickerComponentName.SPLASH_SCREEN))
}
}
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CommonAssertions.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CommonAssertions.kt
index 7659d94..ba78e25 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CommonAssertions.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CommonAssertions.kt
@@ -18,52 +18,52 @@
package com.android.server.wm.flicker.ime
import com.android.server.wm.flicker.FlickerTestParameter
-import com.android.server.wm.traces.parser.windowmanager.WindowManagerStateHelper
+import com.android.server.wm.traces.common.FlickerComponentName
fun FlickerTestParameter.imeLayerBecomesVisible() {
assertLayers {
- this.isInvisible(WindowManagerStateHelper.IME_COMPONENT)
+ this.isInvisible(FlickerComponentName.IME)
.then()
- .isVisible(WindowManagerStateHelper.IME_COMPONENT)
+ .isVisible(FlickerComponentName.IME)
}
}
fun FlickerTestParameter.imeLayerBecomesInvisible() {
assertLayers {
- this.isVisible(WindowManagerStateHelper.IME_COMPONENT)
+ this.isVisible(FlickerComponentName.IME)
.then()
- .isInvisible(WindowManagerStateHelper.IME_COMPONENT)
+ .isInvisible(FlickerComponentName.IME)
}
}
fun FlickerTestParameter.imeWindowIsAlwaysVisible(rotatesScreen: Boolean = false) {
if (rotatesScreen) {
assertWm {
- this.isNonAppWindowVisible(WindowManagerStateHelper.IME_COMPONENT)
+ this.isNonAppWindowVisible(FlickerComponentName.IME)
.then()
- .isNonAppWindowInvisible(WindowManagerStateHelper.IME_COMPONENT)
+ .isNonAppWindowInvisible(FlickerComponentName.IME)
.then()
- .isNonAppWindowVisible(WindowManagerStateHelper.IME_COMPONENT)
+ .isNonAppWindowVisible(FlickerComponentName.IME)
}
} else {
assertWm {
- this.isNonAppWindowVisible(WindowManagerStateHelper.IME_COMPONENT)
+ this.isNonAppWindowVisible(FlickerComponentName.IME)
}
}
}
fun FlickerTestParameter.imeWindowBecomesVisible() {
assertWm {
- this.isNonAppWindowInvisible(WindowManagerStateHelper.IME_COMPONENT)
+ this.isNonAppWindowInvisible(FlickerComponentName.IME)
.then()
- .isNonAppWindowVisible(WindowManagerStateHelper.IME_COMPONENT)
+ .isNonAppWindowVisible(FlickerComponentName.IME)
}
}
fun FlickerTestParameter.imeWindowBecomesInvisible() {
assertWm {
- this.isNonAppWindowVisible(WindowManagerStateHelper.IME_COMPONENT)
+ this.isNonAppWindowVisible(FlickerComponentName.IME)
.then()
- .isNonAppWindowInvisible(WindowManagerStateHelper.IME_COMPONENT)
+ .isNonAppWindowInvisible(FlickerComponentName.IME)
}
}
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/LaunchAppShowImeOnStartTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/LaunchAppShowImeOnStartTest.kt
new file mode 100644
index 0000000..a9568b3
--- /dev/null
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/LaunchAppShowImeOnStartTest.kt
@@ -0,0 +1,153 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.wm.flicker.ime
+
+import android.app.Instrumentation
+import android.platform.test.annotations.Presubmit
+import android.view.Surface
+import android.view.WindowManagerPolicyConstants
+import androidx.test.filters.RequiresDevice
+import androidx.test.platform.app.InstrumentationRegistry
+import com.android.server.wm.flicker.FlickerBuilderProvider
+import com.android.server.wm.flicker.FlickerParametersRunnerFactory
+import com.android.server.wm.flicker.FlickerTestParameter
+import com.android.server.wm.flicker.FlickerTestParameterFactory
+import com.android.server.wm.flicker.dsl.FlickerBuilder
+import com.android.server.wm.flicker.helpers.ImeAppAutoFocusHelper
+import com.android.server.wm.flicker.helpers.setRotation
+import com.android.server.wm.flicker.startRotation
+import com.android.server.wm.traces.common.FlickerComponentName
+import org.junit.FixMethodOrder
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.MethodSorters
+import org.junit.runners.Parameterized
+
+/**
+ * Launch an app that automatically displays the IME
+ *
+ * To run this test: `atest FlickerTests:LaunchAppShowImeOnStartTest`
+ *
+ * Actions:
+ * Make sure no apps are running on the device
+ * Launch an app [testApp] that automatically displays IME and wait animation to complete
+ *
+ * To run only the presubmit assertions add: `--
+ * --module-arg FlickerTests:exclude-annotation:androidx.test.filters.FlakyTest
+ * --module-arg FlickerTests:include-annotation:android.platform.test.annotations.Presubmit`
+ *
+ * To run only the postsubmit assertions add: `--
+ * --module-arg FlickerTests:exclude-annotation:androidx.test.filters.FlakyTest
+ * --module-arg FlickerTests:include-annotation:android.platform.test.annotations.Postsubmit`
+ *
+ * To run only the flaky assertions add: `--
+ * --module-arg FlickerTests:include-annotation:androidx.test.filters.FlakyTest`
+ *
+ * Notes:
+ * 1. Some default assertions (e.g., nav bar, status bar and screen covered)
+ * are inherited [CloseAppTransition]
+ * 2. Part of the test setup occurs automatically via
+ * [com.android.server.wm.flicker.TransitionRunnerWithRules],
+ * including configuring navigation mode, initial orientation and ensuring no
+ * apps are running before setup
+ */
+@RequiresDevice
+@RunWith(Parameterized::class)
+@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
+@FixMethodOrder(MethodSorters.NAME_ASCENDING)
+class LaunchAppShowImeOnStartTest(private val testSpec: FlickerTestParameter) {
+ private val instrumentation: Instrumentation = InstrumentationRegistry.getInstrumentation()
+ private val testApp = ImeAppAutoFocusHelper(instrumentation, testSpec.config.startRotation)
+
+ @FlickerBuilderProvider
+ fun buildFlicker(): FlickerBuilder {
+ return FlickerBuilder(instrumentation).apply {
+ setup {
+ eachRun {
+ this.setRotation(testSpec.config.startRotation)
+ }
+ }
+ teardown {
+ eachRun {
+ testApp.exit()
+ }
+ }
+ transitions {
+ testApp.launchViaIntent(wmHelper)
+ wmHelper.waitImeShown()
+ }
+ }
+ }
+
+ /**
+ * Checks that [FlickerComponentName.IME] window becomes visible during the transition
+ */
+ @Presubmit
+ @Test
+ fun imeWindowBecomesVisible() = testSpec.imeWindowBecomesVisible()
+
+ /**
+ * Checks that [FlickerComponentName.IME] layer becomes visible during the transition
+ */
+ @Presubmit
+ @Test
+ fun imeLayerBecomesVisible() = testSpec.imeLayerBecomesVisible()
+
+ /**
+ * Checks that [FlickerComponentName.IME] layer is invisible at the start of the transition
+ */
+ @Presubmit
+ @Test
+ fun imeLayerNotExistsStart() {
+ testSpec.assertLayersStart {
+ this.isInvisible(FlickerComponentName.IME)
+ }
+ }
+
+ /**
+ * Checks that [FlickerComponentName.IME] layer is visible at the end of the transition
+ */
+ @Presubmit
+ @Test
+ fun imeLayerExistsEnd() {
+ testSpec.assertLayersEnd {
+ this.isVisible(FlickerComponentName.IME)
+ }
+ }
+
+ companion object {
+ /**
+ * Creates the test configurations.
+ *
+ * See [FlickerTestParameterFactory.getConfigNonRotationTests] for configuring
+ * repetitions, screen orientation and navigation modes.
+ */
+ @Parameterized.Parameters(name = "{0}")
+ @JvmStatic
+ fun getParams(): Collection<FlickerTestParameter> {
+ return FlickerTestParameterFactory.getInstance()
+ .getConfigNonRotationTests(
+ repetitions = 5,
+ supportedRotations = listOf(Surface.ROTATION_0),
+ supportedNavigationModes = listOf(
+ WindowManagerPolicyConstants.NAV_BAR_MODE_3BUTTON_OVERLAY,
+ WindowManagerPolicyConstants.NAV_BAR_MODE_GESTURAL_OVERLAY
+ )
+ )
+ }
+ }
+}
\ No newline at end of file
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/OpenImeWindowTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/OpenImeWindowTest.kt
index 665204b..7bf0186 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/OpenImeWindowTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/OpenImeWindowTest.kt
@@ -20,6 +20,7 @@
import android.platform.test.annotations.Presubmit
import android.view.Surface
import android.view.WindowManagerPolicyConstants
+import androidx.test.filters.FlakyTest
import androidx.test.filters.RequiresDevice
import androidx.test.platform.app.InstrumentationRegistry
import com.android.server.wm.flicker.FlickerBuilderProvider
@@ -33,7 +34,6 @@
import com.android.server.wm.flicker.navBarWindowIsVisible
import com.android.server.wm.flicker.entireScreenCovered
import com.android.server.wm.flicker.dsl.FlickerBuilder
-import com.android.server.wm.flicker.startRotation
import com.android.server.wm.flicker.statusBarLayerIsVisible
import com.android.server.wm.flicker.statusBarLayerRotatesScales
import com.android.server.wm.flicker.statusBarWindowIsVisible
@@ -124,15 +124,11 @@
@Presubmit
@Test
- fun navBarLayerRotatesAndScales() {
- testSpec.navBarLayerRotatesAndScales(testSpec.config.startRotation)
- }
+ fun navBarLayerRotatesAndScales() = testSpec.navBarLayerRotatesAndScales()
@Presubmit
@Test
- fun statusBarLayerRotatesScales() {
- testSpec.statusBarLayerRotatesScales(testSpec.config.startRotation)
- }
+ fun statusBarLayerRotatesScales() = testSpec.statusBarLayerRotatesScales()
@Presubmit
@Test
@@ -142,7 +138,7 @@
}
}
- @Presubmit
+ @FlakyTest
@Test
fun visibleWindowsShownMoreThanOneConsecutiveEntry() {
testSpec.assertWm {
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ReOpenImeWindowTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ReOpenImeWindowTest.kt
index b37c404..f6febe9e 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ReOpenImeWindowTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ReOpenImeWindowTest.kt
@@ -17,7 +17,6 @@
package com.android.server.wm.flicker.ime
import android.app.Instrumentation
-import android.content.ComponentName
import android.os.SystemProperties
import android.platform.test.annotations.Presubmit
import android.view.Surface
@@ -39,11 +38,10 @@
import com.android.server.wm.flicker.dsl.FlickerBuilder
import com.android.server.wm.flicker.entireScreenCovered
import com.android.server.wm.flicker.startRotation
-import com.android.server.wm.flicker.endRotation
import com.android.server.wm.flicker.statusBarLayerIsVisible
import com.android.server.wm.flicker.statusBarLayerRotatesScales
import com.android.server.wm.flicker.statusBarWindowIsVisible
-import com.android.server.wm.traces.parser.windowmanager.WindowManagerStateHelper
+import com.android.server.wm.traces.common.FlickerComponentName
import org.junit.Assume
import org.junit.FixMethodOrder
import org.junit.Test
@@ -104,12 +102,12 @@
@Presubmit
@Test
fun visibleWindowsShownMoreThanOneConsecutiveEntry() {
- val component = ComponentName("", "RecentTaskScreenshotSurface")
+ val component = FlickerComponentName("", "RecentTaskScreenshotSurface")
testSpec.assertWm {
this.visibleWindowsShownMoreThanOneConsecutiveEntry(
- ignoreWindows = listOf(WindowManagerStateHelper.SPLASH_SCREEN_COMPONENT,
- WindowManagerStateHelper.SNAPSHOT_COMPONENT,
- component)
+ ignoreWindows = listOf(FlickerComponentName.SPLASH_SCREEN,
+ FlickerComponentName.SNAPSHOT,
+ component)
)
}
}
@@ -138,9 +136,9 @@
// Since we log 1x per frame, sometimes the activity visibility and the app visibility
// are updated together, sometimes not, thus ignore activity check at the start
testSpec.assertWm {
- this.isAppWindowVisible(testApp.component, ignoreActivity = true)
+ this.isAppWindowVisible(testApp.component)
.then()
- .isAppWindowInvisible(testApp.component, ignoreActivity = true)
+ .isAppWindowInvisible(testApp.component)
.then()
.isAppWindowVisible(testApp.component)
}
@@ -155,7 +153,7 @@
// and the app visibility are updated together, sometimes not, thus ignore activity
// check at the start
testSpec.assertWm {
- this.isAppWindowVisible(testApp.component, ignoreActivity = true)
+ this.isAppWindowVisible(testApp.component)
}
}
@@ -177,11 +175,11 @@
fun imeLayerIsBecomesVisibleLegacy() {
Assume.assumeFalse(isShellTransitionsEnabled)
testSpec.assertLayers {
- this.isVisible(WindowManagerStateHelper.IME_COMPONENT)
+ this.isVisible(FlickerComponentName.IME)
.then()
- .isInvisible(WindowManagerStateHelper.IME_COMPONENT)
+ .isInvisible(FlickerComponentName.IME)
.then()
- .isVisible(WindowManagerStateHelper.IME_COMPONENT)
+ .isVisible(FlickerComponentName.IME)
}
}
@@ -190,7 +188,7 @@
fun imeLayerIsBecomesVisible() {
Assume.assumeTrue(isShellTransitionsEnabled)
testSpec.assertLayers {
- this.isVisible(WindowManagerStateHelper.IME_COMPONENT)
+ this.isVisible(FlickerComponentName.IME)
}
}
@@ -200,7 +198,7 @@
testSpec.assertLayers {
this.isVisible(LAUNCHER_COMPONENT)
.then()
- .isVisible(WindowManagerStateHelper.SNAPSHOT_COMPONENT, isOptional = true)
+ .isVisible(FlickerComponentName.SNAPSHOT, isOptional = true)
.then()
.isVisible(testApp.component)
}
@@ -208,26 +206,22 @@
@Presubmit
@Test
- fun navBarLayerRotatesAndScales() {
- testSpec.navBarLayerRotatesAndScales(Surface.ROTATION_0, testSpec.config.endRotation)
- }
+ fun navBarLayerRotatesAndScales() = testSpec.navBarLayerRotatesAndScales()
@Presubmit
@Test
- fun statusBarLayerRotatesScales() {
- testSpec.statusBarLayerRotatesScales(Surface.ROTATION_0, testSpec.config.endRotation)
- }
+ fun statusBarLayerRotatesScales() = testSpec.statusBarLayerRotatesScales()
@Presubmit
@Test
fun visibleLayersShownMoreThanOneConsecutiveEntry() {
// depends on how much of the animation transactions are sent to SF at once
// sometimes this layer appears for 2-3 frames, sometimes for only 1
- val recentTaskComponent = ComponentName("", "RecentTaskScreenshotSurface")
+ val recentTaskComponent = FlickerComponentName("", "RecentTaskScreenshotSurface")
testSpec.assertLayers {
this.visibleLayersShownMoreThanOneConsecutiveEntry(
- listOf(WindowManagerStateHelper.SPLASH_SCREEN_COMPONENT,
- WindowManagerStateHelper.SNAPSHOT_COMPONENT, recentTaskComponent)
+ listOf(FlickerComponentName.SPLASH_SCREEN,
+ FlickerComponentName.SNAPSHOT, recentTaskComponent)
)
}
}
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/SwitchImeWindowsFromGestureNavTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/SwitchImeWindowsFromGestureNavTest.kt
index f9dd88e..4c506b0 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/SwitchImeWindowsFromGestureNavTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/SwitchImeWindowsFromGestureNavTest.kt
@@ -17,7 +17,6 @@
package com.android.server.wm.flicker.ime
import android.app.Instrumentation
-import android.content.ComponentName
import android.platform.test.annotations.Presubmit
import android.view.Surface
import android.view.WindowManagerPolicyConstants
@@ -28,7 +27,7 @@
import com.android.server.wm.flicker.FlickerParametersRunnerFactory
import com.android.server.wm.flicker.FlickerTestParameter
import com.android.server.wm.flicker.FlickerTestParameterFactory
-import com.android.server.wm.flicker.annotation.Group2
+import com.android.server.wm.flicker.annotation.Group4
import com.android.server.wm.flicker.helpers.ImeAppAutoFocusHelper
import com.android.server.wm.flicker.helpers.SimpleAppHelper
import com.android.server.wm.flicker.helpers.WindowUtils
@@ -36,7 +35,7 @@
import com.android.server.wm.flicker.navBarWindowIsVisible
import com.android.server.wm.flicker.startRotation
import com.android.server.wm.flicker.statusBarWindowIsVisible
-import com.android.server.wm.traces.parser.windowmanager.WindowManagerStateHelper
+import com.android.server.wm.traces.common.FlickerComponentName
import org.junit.FixMethodOrder
import org.junit.Test
@@ -52,7 +51,7 @@
@RunWith(Parameterized::class)
@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-@Group2
+@Group4
@Presubmit
class SwitchImeWindowsFromGestureNavTest(private val testSpec: FlickerTestParameter) {
private val instrumentation: Instrumentation = InstrumentationRegistry.getInstrumentation()
@@ -107,51 +106,52 @@
@Test
fun imeAppWindowVisibility() {
- val component = ComponentName(imeTestApp.`package`, "")
testSpec.assertWm {
- this.isAppWindowOnTop(component)
- .then()
- .isAppWindowVisible(component, ignoreActivity = true)
+ isAppWindowVisible(imeTestApp.component)
+ .then()
+ .isAppWindowVisible(testApp.component)
+ .then()
+ .isAppWindowVisible(imeTestApp.component)
}
}
@Test
fun navBarLayerIsVisibleAroundSwitching() {
testSpec.assertLayersStart {
- isVisible(WindowManagerStateHelper.NAV_BAR_COMPONENT)
+ isVisible(FlickerComponentName.NAV_BAR)
}
testSpec.assertLayersEnd {
- isVisible(WindowManagerStateHelper.NAV_BAR_COMPONENT)
+ isVisible(FlickerComponentName.NAV_BAR)
}
}
@Test
fun statusBarLayerIsVisibleAroundSwitching() {
testSpec.assertLayersStart {
- isVisible(WindowManagerStateHelper.STATUS_BAR_COMPONENT)
+ isVisible(FlickerComponentName.STATUS_BAR)
}
testSpec.assertLayersEnd {
- isVisible(WindowManagerStateHelper.STATUS_BAR_COMPONENT)
+ isVisible(FlickerComponentName.STATUS_BAR)
}
}
@Test
fun imeLayerIsVisibleWhenSwitchingToImeApp() {
testSpec.assertLayersStart {
- isVisible(WindowManagerStateHelper.IME_COMPONENT)
+ isVisible(FlickerComponentName.IME)
}
testSpec.assertLayersTag(TAG_IME_VISIBLE) {
- isVisible(WindowManagerStateHelper.IME_COMPONENT)
+ isVisible(FlickerComponentName.IME)
}
testSpec.assertLayersEnd {
- isVisible(WindowManagerStateHelper.IME_COMPONENT)
+ isVisible(FlickerComponentName.IME)
}
}
@Test
fun imeLayerIsInvisibleWhenSwitchingToTestApp() {
testSpec.assertLayersTag(TAG_IME_INVISIBLE) {
- isInvisible(WindowManagerStateHelper.IME_COMPONENT)
+ isInvisible(FlickerComponentName.IME)
}
}
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/ActivitiesTransitionTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/ActivitiesTransitionTest.kt
index 42c252e..f74a771 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/ActivitiesTransitionTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/ActivitiesTransitionTest.kt
@@ -27,10 +27,11 @@
import com.android.server.wm.flicker.FlickerTestParameterFactory
import com.android.server.wm.flicker.LAUNCHER_COMPONENT
import com.android.server.wm.flicker.repetitions
-import com.android.server.wm.flicker.annotation.Group1
+import com.android.server.wm.flicker.annotation.Group4
import com.android.server.wm.flicker.dsl.FlickerBuilder
import com.android.server.wm.flicker.helpers.TwoActivitiesAppHelper
import com.android.server.wm.flicker.testapp.ActivityOptions
+import com.android.server.wm.traces.parser.toFlickerComponent
import org.junit.FixMethodOrder
import org.junit.Test
import org.junit.runner.RunWith
@@ -39,17 +40,33 @@
/**
* Test the back and forward transition between 2 activities.
+ *
* To run this test: `atest FlickerTests:ActivitiesTransitionTest`
+ *
+ * Actions:
+ * Launch an app
+ * Launch a secondary activity within the app
+ * Close the secondary activity back to the initial one
+ *
+ * Notes:
+ * 1. Part of the test setup occurs automatically via
+ * [com.android.server.wm.flicker.TransitionRunnerWithRules],
+ * including configuring navigation mode, initial orientation and ensuring no
+ * apps are running before setup
*/
@RequiresDevice
@RunWith(Parameterized::class)
@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-@Group1
+@Group4
class ActivitiesTransitionTest(val testSpec: FlickerTestParameter) {
val instrumentation: Instrumentation = InstrumentationRegistry.getInstrumentation()
private val testApp: TwoActivitiesAppHelper = TwoActivitiesAppHelper(instrumentation)
+ /**
+ * Entry point for the test runner. It will use this method to initialize and cache
+ * flicker executions
+ */
@FlickerBuilderProvider
fun buildFlicker(): FlickerBuilder {
return FlickerBuilder(instrumentation).apply {
@@ -75,30 +92,52 @@
}
}
+ /**
+ * Checks that the [ActivityOptions.BUTTON_ACTIVITY_COMPONENT_NAME] activity is visible at
+ * the start of the transition, that
+ * [ActivityOptions.SIMPLE_ACTIVITY_AUTO_FOCUS_COMPONENT_NAME] becomes visible during the
+ * transition, and that [ActivityOptions.BUTTON_ACTIVITY_COMPONENT_NAME] is again visible
+ * at the end
+ */
@Presubmit
@Test
fun finishSubActivity() {
+ val buttonActivityComponent = ActivityOptions
+ .BUTTON_ACTIVITY_COMPONENT_NAME.toFlickerComponent()
+ val imeAutoFocusActivityComponent = ActivityOptions
+ .SIMPLE_ACTIVITY_AUTO_FOCUS_COMPONENT_NAME.toFlickerComponent()
testSpec.assertWm {
- this.isAppWindowOnTop(ActivityOptions.BUTTON_ACTIVITY_COMPONENT_NAME)
- .then()
- .isAppWindowOnTop(ActivityOptions.SIMPLE_ACTIVITY_AUTO_FOCUS_COMPONENT_NAME)
- .then()
- .isAppWindowOnTop(ActivityOptions.BUTTON_ACTIVITY_COMPONENT_NAME)
+ this.isAppWindowOnTop(buttonActivityComponent)
+ .then()
+ .isAppWindowOnTop(imeAutoFocusActivityComponent)
+ .then()
+ .isAppWindowOnTop(buttonActivityComponent)
}
}
+ /**
+ * Checks that all parts of the screen are covered during the transition
+ */
@Presubmit
@Test
fun entireScreenCovered() = testSpec.entireScreenCovered()
+ /**
+ * Checks that the [LAUNCHER_COMPONENT] window is not on top. The launcher cannot be
+ * asserted with `isAppWindowVisible` because it contains 2 windows with the exact same name,
+ * and both are never simultaneously visible
+ */
@Presubmit
@Test
- fun launcherWindowNotVisible() {
+ fun launcherWindowNotOnTop() {
testSpec.assertWm {
- this.isAppWindowInvisible(LAUNCHER_COMPONENT, ignoreActivity = true)
+ this.isAppWindowNotOnTop(LAUNCHER_COMPONENT)
}
}
+ /**
+ * Checks that the [LAUNCHER_COMPONENT] layer is never visible during the transition
+ */
@Presubmit
@Test
fun launcherLayerNotVisible() {
@@ -106,6 +145,12 @@
}
companion object {
+ /**
+ * Creates the test configurations.
+ *
+ * See [FlickerTestParameterFactory.getConfigNonRotationTests] for configuring
+ * repetitions, screen orientation and navigation modes.
+ */
@Parameterized.Parameters(name = "{0}")
@JvmStatic
fun getParams(): Collection<FlickerTestParameter> {
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromOverviewTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromOverviewTest.kt
index 3678f33..663af70 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromOverviewTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromOverviewTest.kt
@@ -27,6 +27,7 @@
import com.android.server.wm.flicker.helpers.setRotation
import com.android.server.wm.flicker.startRotation
import com.android.server.wm.flicker.dsl.FlickerBuilder
+import com.android.server.wm.traces.common.WindowManagerConditionsFactory
import org.junit.FixMethodOrder
import org.junit.Test
import org.junit.runner.RunWith
@@ -78,6 +79,11 @@
}
transitions {
device.reopenAppFromOverview(wmHelper)
+ wmHelper.waitFor(
+ WindowManagerConditionsFactory.hasLayersAnimating().negate(),
+ WindowManagerConditionsFactory.isWMStateComplete(),
+ WindowManagerConditionsFactory.isHomeActivityVisible().negate()
+ )
wmHelper.waitForFullScreenApp(testApp.component)
}
}
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppNonResizeableTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppNonResizeableTest.kt
index b717612..cf10c53 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppNonResizeableTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppNonResizeableTest.kt
@@ -16,7 +16,6 @@
package com.android.server.wm.flicker.launch
-import android.content.ComponentName
import android.platform.test.annotations.Postsubmit
import android.platform.test.annotations.Presubmit
import android.view.Surface
@@ -29,7 +28,8 @@
import com.android.server.wm.flicker.annotation.Group1
import com.android.server.wm.flicker.helpers.NonResizeableAppHelper
import com.android.server.wm.flicker.dsl.FlickerBuilder
-import com.android.server.wm.traces.parser.windowmanager.WindowManagerStateHelper
+import com.android.server.wm.flicker.statusBarWindowIsVisible
+import com.android.server.wm.traces.common.FlickerComponentName
import com.google.common.truth.Truth
import org.junit.FixMethodOrder
import org.junit.Test
@@ -61,7 +61,7 @@
@Group1
class OpenAppNonResizeableTest(testSpec: FlickerTestParameter) : OpenAppTransition(testSpec) {
override val testApp = NonResizeableAppHelper(instrumentation)
- private val colorFadComponent = ComponentName("", "ColorFade BLAST#")
+ private val colorFadComponent = FlickerComponentName("", "ColorFade BLAST#")
/**
* Defines the transition used to run the test
@@ -92,15 +92,15 @@
* Checks that the nav bar layer starts visible, becomes invisible during unlocking animation
* and becomes visible at the end
*/
- @Presubmit
+ @Postsubmit
@Test
fun navBarLayerVisibilityChanges() {
testSpec.assertLayers {
- this.isVisible(WindowManagerStateHelper.NAV_BAR_COMPONENT)
+ this.isVisible(FlickerComponentName.NAV_BAR)
.then()
- .isInvisible(WindowManagerStateHelper.NAV_BAR_COMPONENT)
+ .isInvisible(FlickerComponentName.NAV_BAR)
.then()
- .isVisible(WindowManagerStateHelper.NAV_BAR_COMPONENT)
+ .isVisible(FlickerComponentName.NAV_BAR)
}
}
@@ -108,7 +108,7 @@
* Checks that the app layer doesn't exist at the start of the transition, that it is
* created (invisible) and becomes visible during the transition
*/
- @Presubmit
+ @FlakyTest
@Test
fun appLayerBecomesVisible() {
testSpec.assertLayers {
@@ -133,10 +133,9 @@
testSpec.assertWm {
this.notContains(testApp.component)
.then()
- .isAppWindowInvisible(testApp.component,
- ignoreActivity = true, isOptional = true)
+ .isAppWindowInvisible(testApp.component, isOptional = true)
.then()
- .isAppWindowVisible(testApp.component, ignoreActivity = true)
+ .isAppWindowVisible(testApp.component)
}
}
@@ -147,7 +146,7 @@
@Test
fun appWindowBecomesVisibleAtEnd() {
testSpec.assertWmEnd {
- this.isVisible(testApp.component)
+ this.isAppWindowVisible(testApp.component)
}
}
@@ -159,20 +158,29 @@
@Test
fun navBarWindowsVisibilityChanges() {
testSpec.assertWm {
- this.isAboveAppWindowVisible(WindowManagerStateHelper.NAV_BAR_COMPONENT)
+ this.isAboveAppWindowVisible(FlickerComponentName.NAV_BAR)
.then()
- .isNonAppWindowInvisible(WindowManagerStateHelper.NAV_BAR_COMPONENT)
+ .isNonAppWindowInvisible(FlickerComponentName.NAV_BAR)
.then()
- .isAboveAppWindowVisible(WindowManagerStateHelper.NAV_BAR_COMPONENT)
+ .isAboveAppWindowVisible(FlickerComponentName.NAV_BAR)
}
}
/** {@inheritDoc} */
@FlakyTest
@Test
+ override fun statusBarWindowIsVisible() = super.statusBarWindowIsVisible()
+
+ /** {@inheritDoc} */
+ @FlakyTest
+ @Test
override fun visibleWindowsShownMoreThanOneConsecutiveEntry() =
super.visibleWindowsShownMoreThanOneConsecutiveEntry()
+ @FlakyTest
+ @Test
+ override fun navBarLayerRotatesAndScales() = super.navBarLayerRotatesAndScales()
+
/** {@inheritDoc} */
@FlakyTest
@Test
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppTransition.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppTransition.kt
index 14d17f8..7af7b3a 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppTransition.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppTransition.kt
@@ -18,13 +18,11 @@
import android.app.Instrumentation
import android.platform.test.annotations.Presubmit
-import android.view.Surface
import androidx.test.platform.app.InstrumentationRegistry
import com.android.server.wm.flicker.FlickerBuilderProvider
import com.android.server.wm.flicker.FlickerTestParameter
import com.android.server.wm.flicker.LAUNCHER_COMPONENT
import com.android.server.wm.flicker.dsl.FlickerBuilder
-import com.android.server.wm.flicker.endRotation
import com.android.server.wm.flicker.entireScreenCovered
import com.android.server.wm.flicker.helpers.SimpleAppHelper
import com.android.server.wm.flicker.helpers.StandardAppHelper
@@ -39,10 +37,12 @@
import com.android.server.wm.flicker.statusBarLayerIsVisible
import com.android.server.wm.flicker.statusBarLayerRotatesScales
import com.android.server.wm.flicker.statusBarWindowIsVisible
-import com.android.server.wm.traces.parser.windowmanager.WindowManagerStateHelper.Companion.SNAPSHOT_COMPONENT
-import com.android.server.wm.traces.parser.windowmanager.WindowManagerStateHelper.Companion.SPLASH_SCREEN_COMPONENT
+import com.android.server.wm.traces.common.FlickerComponentName
import org.junit.Test
+/**
+ * Base class for app launch tests
+ */
abstract class OpenAppTransition(protected val testSpec: FlickerTestParameter) {
protected val instrumentation: Instrumentation = InstrumentationRegistry.getInstrumentation()
protected open val testApp: StandardAppHelper = SimpleAppHelper(instrumentation)
@@ -85,7 +85,7 @@
}
/**
- * Checks that the navigation bar layer is visible during the whole transition
+ * Checks that the navigation bar layer is visible at the start and end of the trace
*/
open fun navBarLayerIsVisible() {
testSpec.navBarLayerIsVisible()
@@ -96,9 +96,7 @@
*/
@Presubmit
@Test
- open fun navBarLayerRotatesAndScales() {
- testSpec.navBarLayerRotatesAndScales(Surface.ROTATION_0, testSpec.config.endRotation)
- }
+ open fun navBarLayerRotatesAndScales() = testSpec.navBarLayerRotatesAndScales()
/**
* Checks that the status bar window is visible during the whole transition
@@ -110,7 +108,7 @@
}
/**
- * Checks that the status bar layer is visible during the whole transition
+ * Checks that the status bar layer is visible at the start and end of the trace
*/
@Presubmit
@Test
@@ -123,9 +121,7 @@
*/
@Presubmit
@Test
- open fun statusBarLayerRotatesScales() {
- testSpec.statusBarLayerRotatesScales(Surface.ROTATION_0, testSpec.config.endRotation)
- }
+ open fun statusBarLayerRotatesScales() = testSpec.statusBarLayerRotatesScales()
/**
* Checks that all windows that are visible on the trace, are visible for at least 2
@@ -188,9 +184,9 @@
testSpec.assertWm {
this.isAppWindowOnTop(LAUNCHER_COMPONENT)
.then()
- .isAppWindowOnTop(SNAPSHOT_COMPONENT, isOptional = true)
+ .isAppWindowOnTop(FlickerComponentName.SNAPSHOT, isOptional = true)
.then()
- .isAppWindowOnTop(SPLASH_SCREEN_COMPONENT, isOptional = true)
+ .isAppWindowOnTop(FlickerComponentName.SPLASH_SCREEN, isOptional = true)
.then()
.isAppWindowOnTop(testApp.component)
}
@@ -202,9 +198,9 @@
*/
open fun launcherWindowBecomesInvisible() {
testSpec.assertWm {
- this.isAppWindowVisible(LAUNCHER_COMPONENT)
+ this.isAppWindowOnTop(LAUNCHER_COMPONENT)
.then()
- .isAppWindowInvisible(LAUNCHER_COMPONENT)
+ .isAppWindowNotOnTop(LAUNCHER_COMPONENT)
}
}
}
\ No newline at end of file
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/TaskTransitionTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/TaskTransitionTest.kt
new file mode 100644
index 0000000..495e2d6
--- /dev/null
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/TaskTransitionTest.kt
@@ -0,0 +1,248 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.wm.flicker.launch
+
+import android.app.Instrumentation
+import android.app.WallpaperManager
+import android.platform.test.annotations.Postsubmit
+import androidx.test.filters.RequiresDevice
+import androidx.test.platform.app.InstrumentationRegistry
+import com.android.server.wm.flicker.FlickerBuilderProvider
+import com.android.server.wm.flicker.FlickerParametersRunnerFactory
+import com.android.server.wm.flicker.FlickerTestParameter
+import com.android.server.wm.flicker.FlickerTestParameterFactory
+import com.android.server.wm.flicker.LAUNCHER_COMPONENT
+import com.android.server.wm.flicker.annotation.Group4
+import com.android.server.wm.flicker.dsl.FlickerBuilder
+import com.android.server.wm.flicker.entireScreenCovered
+import com.android.server.wm.flicker.helpers.NewTasksAppHelper
+import com.android.server.wm.flicker.helpers.WindowUtils
+import com.android.server.wm.flicker.navBarLayerIsVisible
+import com.android.server.wm.flicker.navBarWindowIsVisible
+import com.android.server.wm.flicker.repetitions
+import com.android.server.wm.flicker.startRotation
+import com.android.server.wm.flicker.statusBarLayerIsVisible
+import com.android.server.wm.flicker.statusBarWindowIsVisible
+import com.android.server.wm.flicker.testapp.ActivityOptions.LAUNCH_NEW_TASK_ACTIVITY_COMPONENT_NAME
+import com.android.server.wm.flicker.testapp.ActivityOptions.SIMPLE_ACTIVITY_AUTO_FOCUS_COMPONENT_NAME
+import com.android.server.wm.traces.common.FlickerComponentName
+import com.android.server.wm.traces.common.FlickerComponentName.Companion.SPLASH_SCREEN
+import com.android.server.wm.traces.common.FlickerComponentName.Companion.WALLPAPER_BBQ_WRAPPER
+import com.android.server.wm.traces.parser.toFlickerComponent
+import org.junit.FixMethodOrder
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.MethodSorters
+import org.junit.runners.Parameterized
+
+/**
+ * Test the back and forward transition between 2 activities.
+ *
+ * To run this test: `atest FlickerTests:ActivitiesTransitionTest`
+ *
+ * Actions:
+ * Launch the NewTaskLauncherApp [mTestApp]
+ * Open a new task (SimpleActivity) from the NewTaskLauncherApp [mTestApp]
+ * Go back to the NewTaskLauncherApp [mTestApp]
+ */
+@RequiresDevice
+@RunWith(Parameterized::class)
+@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
+@FixMethodOrder(MethodSorters.NAME_ASCENDING)
+@Group4
+class TaskTransitionTest(val testSpec: FlickerTestParameter) {
+ val instrumentation: Instrumentation = InstrumentationRegistry.getInstrumentation()
+ private val mTestApp: NewTasksAppHelper = NewTasksAppHelper(instrumentation)
+
+ @FlickerBuilderProvider
+ fun buildFlicker(): FlickerBuilder {
+ return FlickerBuilder(instrumentation).apply {
+ withTestName { testSpec.name }
+ repeat { testSpec.config.repetitions }
+ setup {
+ eachRun {
+ mTestApp.launchViaIntent(wmHelper)
+ wmHelper.waitForFullScreenApp(mTestApp.component)
+ }
+ }
+ teardown {
+ test {
+ mTestApp.exit()
+ }
+ }
+ transitions {
+ mTestApp.openNewTask(device, wmHelper)
+ device.pressBack()
+ wmHelper.waitForAppTransitionIdle()
+ wmHelper.waitForFullScreenApp(mTestApp.component)
+ }
+ }
+ }
+
+ /**
+ * Checks that the wallpaper window is never visible when performing task transitions.
+ * A solid color background should be shown instead.
+ */
+ @Postsubmit
+ @Test
+ fun wallpaperWindowIsNeverVisible() {
+ testSpec.assertWm {
+ this.isNonAppWindowInvisible(WALLPAPER)
+ }
+ }
+
+ /**
+ * Checks that the wallpaper layer is never visible when performing task transitions.
+ * A solid color background should be shown instead.
+ */
+ @Postsubmit
+ @Test
+ fun wallpaperLayerIsNeverVisible() {
+ testSpec.assertLayers {
+ this.isInvisible(WALLPAPER)
+ this.isInvisible(WALLPAPER_BBQ_WRAPPER)
+ }
+ }
+
+ /**
+ * Check that the launcher window is never visible when performing task transitions.
+ * A solid color background should be shown above it.
+ */
+ @Postsubmit
+ @Test
+ fun launcherWindowIsNeverVisible() {
+ testSpec.assertWm {
+ this.isAppWindowInvisible(LAUNCHER_COMPONENT)
+ }
+ }
+
+ /**
+ * Checks that the launcher layer is never visible when performing task transitions.
+ * A solid color background should be shown above it.
+ */
+ @Postsubmit
+ @Test
+ fun launcherLayerIsNeverVisible() {
+ testSpec.assertLayers {
+ this.isInvisible(LAUNCHER_COMPONENT)
+ }
+ }
+
+ /**
+ * Checks that a color background is visible while the task transition is occurring.
+ */
+ @Postsubmit
+ @Test
+ fun colorLayerIsVisibleDuringTransition() {
+ val bgColorLayer = FlickerComponentName("", "colorBackgroundLayer")
+ val displayBounds = WindowUtils.getDisplayBounds(testSpec.config.startRotation)
+
+ testSpec.assertLayers {
+ this.coversExactly(displayBounds, LAUNCH_NEW_TASK_ACTIVITY)
+ .isInvisible(bgColorLayer)
+ .then()
+ // Transitioning
+ .isVisible(bgColorLayer)
+ .then()
+ // Fully transitioned to simple SIMPLE_ACTIVITY
+ .coversExactly(displayBounds, SIMPLE_ACTIVITY)
+ .isInvisible(bgColorLayer)
+ .then()
+ // Transitioning back
+ .isVisible(bgColorLayer)
+ .then()
+ // Fully transitioned back to LAUNCH_NEW_TASK_ACTIVITY
+ .isInvisible(bgColorLayer)
+ .coversExactly(displayBounds, LAUNCH_NEW_TASK_ACTIVITY)
+ }
+ }
+
+ /**
+ * Checks that we start with the LaunchNewTask activity on top and then open up
+ * the SimpleActivity and then go back to the LaunchNewTask activity.
+ */
+ @Postsubmit
+ @Test
+ fun newTaskOpensOnTopAndThenCloses() {
+ testSpec.assertWm {
+ this.isAppWindowOnTop(LAUNCH_NEW_TASK_ACTIVITY)
+ .then()
+ .isAppWindowOnTop(SPLASH_SCREEN, isOptional = true)
+ .then()
+ .isAppWindowOnTop(SIMPLE_ACTIVITY)
+ .then()
+ .isAppWindowOnTop(SPLASH_SCREEN, isOptional = true)
+ .then()
+ .isAppWindowOnTop(LAUNCH_NEW_TASK_ACTIVITY)
+ }
+ }
+
+ /**
+ * Checks that all parts of the screen are covered at the start and end of the transition
+ */
+ @Postsubmit
+ @Test
+ fun entireScreenCovered() = testSpec.entireScreenCovered()
+
+ /**
+ * Checks that the navbar window is visible throughout the transition
+ */
+ @Postsubmit
+ @Test
+ fun navBarWindowIsVisible() = testSpec.navBarWindowIsVisible()
+
+ /**
+ * Checks that the navbar layer is visible throughout the transition
+ */
+ @Postsubmit
+ @Test
+ fun navBarLayerIsVisible() = testSpec.navBarLayerIsVisible()
+
+ /**
+ * Checks that the status bar window is visible throughout the transition
+ */
+ @Postsubmit
+ @Test
+ fun statusBarWindowIsVisible() = testSpec.statusBarWindowIsVisible()
+
+ /**
+ * Checks that the status bar layer is visible throughout the transition
+ */
+ @Postsubmit
+ @Test
+ fun statusBarLayerIsVisible() = testSpec.statusBarLayerIsVisible()
+
+ companion object {
+ private val WALLPAPER = getWallpaperPackage(InstrumentationRegistry.getInstrumentation())
+ private val LAUNCH_NEW_TASK_ACTIVITY =
+ LAUNCH_NEW_TASK_ACTIVITY_COMPONENT_NAME.toFlickerComponent()
+ private val SIMPLE_ACTIVITY = SIMPLE_ACTIVITY_AUTO_FOCUS_COMPONENT_NAME.toFlickerComponent()
+
+ private fun getWallpaperPackage(instrumentation: Instrumentation): FlickerComponentName {
+ val wallpaperManager = WallpaperManager.getInstance(instrumentation.targetContext)
+
+ return wallpaperManager.wallpaperInfo.component.toFlickerComponent()
+ }
+
+ @Parameterized.Parameters(name = "{0}")
+ @JvmStatic
+ fun getParams(): Collection<FlickerTestParameter> {
+ return FlickerTestParameterFactory.getInstance()
+ .getConfigNonRotationTests(repetitions = 5)
+ }
+ }
+}
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/quickswitch/QuickSwitchBetweenTwoAppsBackTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/quickswitch/QuickSwitchBetweenTwoAppsBackTest.kt
index 035aac1..52904cc 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/quickswitch/QuickSwitchBetweenTwoAppsBackTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/quickswitch/QuickSwitchBetweenTwoAppsBackTest.kt
@@ -39,7 +39,7 @@
import com.android.server.wm.flicker.startRotation
import com.android.server.wm.flicker.statusBarLayerIsVisible
import com.android.server.wm.flicker.statusBarWindowIsVisible
-import com.android.server.wm.traces.parser.windowmanager.WindowManagerStateHelper.Companion.SNAPSHOT_COMPONENT
+import com.android.server.wm.traces.common.FlickerComponentName
import org.junit.FixMethodOrder
import org.junit.Test
import org.junit.runner.RunWith
@@ -189,9 +189,9 @@
testSpec.assertWm {
this.isAppWindowInvisible(testApp1.component)
.then()
- .isAppWindowVisible(SNAPSHOT_COMPONENT, isOptional = true)
+ .isAppWindowVisible(FlickerComponentName.SNAPSHOT, isOptional = true)
.then()
- .isAppWindowVisible(testApp1.component, ignoreActivity = true)
+ .isAppWindowVisible(testApp1.component)
}
}
@@ -217,7 +217,7 @@
@Test
fun app2WindowBecomesAndStaysInvisible() {
testSpec.assertWm {
- this.isAppWindowVisible(testApp2.component, ignoreActivity = true)
+ this.isAppWindowVisible(testApp2.component)
.then()
.isAppWindowInvisible(testApp2.component)
}
@@ -251,7 +251,7 @@
// TODO: Do we actually want to test this? Seems too implementation specific...
.isAppWindowVisible(LAUNCHER_COMPONENT, isOptional = true)
.then()
- .isAppWindowVisible(SNAPSHOT_COMPONENT, isOptional = true)
+ .isAppWindowVisible(FlickerComponentName.SNAPSHOT, isOptional = true)
.then()
.isAppWindowVisible(testApp1.component)
}
@@ -270,7 +270,7 @@
.then()
.isVisible(LAUNCHER_COMPONENT, isOptional = true)
.then()
- .isVisible(SNAPSHOT_COMPONENT, isOptional = true)
+ .isVisible(FlickerComponentName.SNAPSHOT, isOptional = true)
.then()
.isVisible(testApp1.component)
}
@@ -297,8 +297,7 @@
*/
@Postsubmit
@Test
- fun navbarIsAlwaysInRightPosition() =
- testSpec.navBarLayerRotatesAndScales(testSpec.config.startRotation)
+ fun navbarIsAlwaysInRightPosition() = testSpec.navBarLayerRotatesAndScales()
/**
* Checks that the status bar window is visible throughout the entire transition.
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/quickswitch/QuickSwitchBetweenTwoAppsForwardTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/quickswitch/QuickSwitchBetweenTwoAppsForwardTest.kt
new file mode 100644
index 0000000..842aa2b
--- /dev/null
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/quickswitch/QuickSwitchBetweenTwoAppsForwardTest.kt
@@ -0,0 +1,347 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.wm.flicker.quickswitch
+
+import android.app.Instrumentation
+import android.platform.test.annotations.Postsubmit
+import android.platform.test.annotations.RequiresDevice
+import android.view.Surface
+import android.view.WindowManagerPolicyConstants
+import androidx.test.platform.app.InstrumentationRegistry
+import com.android.server.wm.flicker.FlickerBuilderProvider
+import com.android.server.wm.flicker.FlickerParametersRunnerFactory
+import com.android.server.wm.flicker.FlickerTestParameter
+import com.android.server.wm.flicker.FlickerTestParameterFactory
+import com.android.server.wm.flicker.LAUNCHER_COMPONENT
+import com.android.server.wm.flicker.annotation.Group1
+import com.android.server.wm.flicker.dsl.FlickerBuilder
+import com.android.server.wm.flicker.helpers.NonResizeableAppHelper
+import com.android.server.wm.flicker.helpers.SimpleAppHelper
+import com.android.server.wm.flicker.helpers.WindowUtils
+import com.android.server.wm.flicker.helpers.isRotated
+import com.android.server.wm.flicker.navBarLayerIsVisible
+import com.android.server.wm.flicker.navBarLayerRotatesAndScales
+import com.android.server.wm.flicker.navBarWindowIsVisible
+import com.android.server.wm.flicker.repetitions
+import com.android.server.wm.flicker.startRotation
+import com.android.server.wm.flicker.statusBarLayerIsVisible
+import com.android.server.wm.flicker.statusBarWindowIsVisible
+import com.android.server.wm.traces.common.FlickerComponentName
+import org.junit.FixMethodOrder
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.MethodSorters
+import org.junit.runners.Parameterized
+
+/**
+ * Test quick switching back to previous app from last opened app
+ *
+ * To run this test: `atest FlickerTests:QuickSwitchBetweenTwoAppsBackTest`
+ *
+ * Actions:
+ * Launch an app [testApp1]
+ * Launch another app [testApp2]
+ * Swipe right from the bottom of the screen to quick switch back to the first app [testApp1]
+ * Swipe left from the bottom of the screen to quick switch forward to the second app [testApp2]
+ */
+@RequiresDevice
+@RunWith(Parameterized::class)
+@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
+@FixMethodOrder(MethodSorters.NAME_ASCENDING)
+@Group1
+class QuickSwitchBetweenTwoAppsForwardTest(private val testSpec: FlickerTestParameter) {
+ private val instrumentation: Instrumentation = InstrumentationRegistry.getInstrumentation()
+
+ private val testApp1 = SimpleAppHelper(instrumentation)
+ private val testApp2 = NonResizeableAppHelper(instrumentation)
+
+ private val startDisplayBounds = WindowUtils.getDisplayBounds(testSpec.config.startRotation)
+
+ @FlickerBuilderProvider
+ fun buildFlicker(): FlickerBuilder {
+ return FlickerBuilder(instrumentation).apply {
+ withTestName { testSpec.name }
+ repeat { testSpec.config.repetitions }
+ setup {
+ eachRun {
+ testApp1.launchViaIntent(wmHelper)
+ wmHelper.waitForFullScreenApp(testApp1.component)
+
+ testApp2.launchViaIntent(wmHelper)
+ wmHelper.waitForFullScreenApp(testApp2.component)
+
+ // Swipe right from bottom to quick switch back
+ // NOTE: We don't perform an edge-to-edge swipe but instead only swipe in the middle
+ // as to not accidentally trigger a swipe back or forward action which would result
+ // in the same behavior but not testing quick swap.
+ device.swipe(
+ startDisplayBounds.bounds.right / 3,
+ startDisplayBounds.bounds.bottom,
+ 2 * startDisplayBounds.bounds.right / 3,
+ startDisplayBounds.bounds.bottom,
+ if (testSpec.config.startRotation.isRotated()) 75 else 30
+ )
+
+ wmHelper.waitForFullScreenApp(testApp1.component)
+ wmHelper.waitForAppTransitionIdle()
+ }
+ }
+ transitions {
+ // Swipe left from bottom to quick switch forward
+ // NOTE: We don't perform an edge-to-edge swipe but instead only swipe in the middle
+ // as to not accidentally trigger a swipe back or forward action which would result
+ // in the same behavior but not testing quick swap.
+ device.swipe(
+ 2 * startDisplayBounds.bounds.right / 3,
+ startDisplayBounds.bounds.bottom,
+ startDisplayBounds.bounds.right / 3,
+ startDisplayBounds.bounds.bottom,
+ if (testSpec.config.startRotation.isRotated()) 75 else 30
+ )
+
+ wmHelper.waitForFullScreenApp(testApp2.component)
+ wmHelper.waitForAppTransitionIdle()
+ }
+
+ teardown {
+ test {
+ testApp1.exit()
+ testApp2.exit()
+ }
+ }
+ }
+ }
+
+ /**
+ * Checks that the transition starts with [testApp1]'s windows filling/covering exactly the
+ * entirety of the display.
+ */
+ @Postsubmit
+ @Test
+ fun startsWithApp1WindowsCoverFullScreen() {
+ testSpec.assertWmStart {
+ this.frameRegion(testApp1.component).coversExactly(startDisplayBounds)
+ }
+ }
+
+ /**
+ * Checks that the transition starts with [testApp1]'s layers filling/covering exactly the
+ * entirety of the display.
+ */
+ @Postsubmit
+ @Test
+ fun startsWithApp1LayersCoverFullScreen() {
+ testSpec.assertLayersStart {
+ this.visibleRegion(testApp1.component).coversExactly(startDisplayBounds)
+ }
+ }
+
+ /**
+ * Checks that the transition starts with [testApp1] being the top window.
+ */
+ @Postsubmit
+ @Test
+ fun startsWithApp1WindowBeingOnTop() {
+ testSpec.assertWmStart {
+ this.isAppWindowOnTop(testApp1.component)
+ }
+ }
+
+ /**
+ * Checks that [testApp2] windows fill the entire screen (i.e. is "fullscreen") at the end of the
+ * transition once we have fully quick switched from [testApp1] back to the [testApp2].
+ */
+ @Postsubmit
+ @Test
+ fun endsWithApp2WindowsCoveringFullScreen() {
+ testSpec.assertWmEnd {
+ this.frameRegion(testApp2.component).coversExactly(startDisplayBounds)
+ }
+ }
+
+ /**
+ * Checks that [testApp2] layers fill the entire screen (i.e. is "fullscreen") at the end of the
+ * transition once we have fully quick switched from [testApp1] back to the [testApp2].
+ */
+ @Postsubmit
+ @Test
+ fun endsWithApp2LayersCoveringFullScreen() {
+ testSpec.assertLayersEnd {
+ this.visibleRegion(testApp2.component).coversExactly(startDisplayBounds)
+ }
+ }
+
+ /**
+ * Checks that [testApp2] is the top window at the end of the transition once we have fully quick
+ * switched from [testApp1] back to the [testApp2].
+ */
+ @Postsubmit
+ @Test
+ fun endsWithApp2BeingOnTop() {
+ testSpec.assertWmEnd {
+ this.isAppWindowOnTop(testApp2.component)
+ }
+ }
+
+ /**
+ * Checks that [testApp2]'s window starts off invisible and becomes visible at some point before
+ * the end of the transition and then stays visible until the end of the transition.
+ */
+ @Postsubmit
+ @Test
+ fun app2WindowBecomesAndStaysVisible() {
+ testSpec.assertWm {
+ this.isAppWindowInvisible(testApp2.component)
+ .then()
+ .isAppWindowVisible(FlickerComponentName.SNAPSHOT, isOptional = true)
+ .then()
+ .isAppWindowVisible(testApp2.component)
+ }
+ }
+
+ /**
+ * Checks that [testApp2]'s layer starts off invisible and becomes visible at some point before
+ * the end of the transition and then stays visible until the end of the transition.
+ */
+ @Postsubmit
+ @Test
+ fun app2LayerBecomesAndStaysVisible() {
+ testSpec.assertLayers {
+ this.isInvisible(testApp2.component)
+ .then()
+ .isVisible(testApp2.component)
+ }
+ }
+
+ /**
+ * Checks that [testApp1]'s window starts off visible and becomes invisible at some point before
+ * the end of the transition and then stays invisible until the end of the transition.
+ */
+ @Postsubmit
+ @Test
+ fun app1WindowBecomesAndStaysInvisible() {
+ testSpec.assertWm {
+ this.isAppWindowVisible(testApp1.component)
+ .then()
+ .isAppWindowInvisible(testApp1.component)
+ }
+ }
+
+ /**
+ * Checks that [testApp1]'s layer starts off visible and becomes invisible at some point before
+ * the end of the transition and then stays invisible until the end of the transition.
+ */
+ @Postsubmit
+ @Test
+ fun app1LayerBecomesAndStaysInvisible() {
+ testSpec.assertLayers {
+ this.isVisible(testApp1.component)
+ .then()
+ .isInvisible(testApp1.component)
+ }
+ }
+
+ /**
+ * Checks that [testApp1]'s window is visible at least until [testApp2]'s window is visible.
+ * Ensures that at any point, either [testApp2] or [testApp1]'s windows are at least partially
+ * visible.
+ */
+ @Postsubmit
+ @Test
+ fun app2WindowIsVisibleOnceApp1WindowIsInvisible() {
+ testSpec.assertWm {
+ this.isAppWindowVisible(testApp1.component)
+ .then()
+ .isAppWindowVisible(LAUNCHER_COMPONENT, isOptional = true)
+ .then()
+ .isAppWindowVisible(FlickerComponentName.SNAPSHOT, isOptional = true)
+ .then()
+ .isAppWindowVisible(testApp2.component)
+ }
+ }
+
+ /**
+ * Checks that [testApp1]'s layer is visible at least until [testApp2]'s window is visible.
+ * Ensures that at any point, either [testApp2] or [testApp1]'s windows are at least partially
+ * visible.
+ */
+ @Postsubmit
+ @Test
+ fun app2LayerIsVisibleOnceApp1LayerIsInvisible() {
+ testSpec.assertLayers {
+ this.isVisible(testApp1.component)
+ .then()
+ .isVisible(LAUNCHER_COMPONENT, isOptional = true)
+ .then()
+ .isVisible(FlickerComponentName.SNAPSHOT, isOptional = true)
+ .then()
+ .isVisible(testApp2.component)
+ }
+ }
+
+ /**
+ * Checks that the navbar window is visible throughout the entire transition.
+ */
+ @Postsubmit
+ @Test
+ fun navBarWindowIsAlwaysVisible() = testSpec.navBarWindowIsVisible()
+
+ /**
+ * Checks that the navbar layer is visible throughout the entire transition.
+ */
+ @Postsubmit
+ @Test
+ fun navBarLayerAlwaysIsVisible() = testSpec.navBarLayerIsVisible()
+
+ /**
+ * Checks that the navbar is always in the right position and covers the expected region.
+ *
+ * NOTE: This doesn't check that the navbar is visible or not.
+ */
+ @Postsubmit
+ @Test
+ fun navbarIsAlwaysInRightPosition() = testSpec.navBarLayerRotatesAndScales()
+
+ /**
+ * Checks that the status bar window is visible throughout the entire transition.
+ */
+ @Postsubmit
+ @Test
+ fun statusBarWindowIsAlwaysVisible() = testSpec.statusBarWindowIsVisible()
+
+ /**
+ * Checks that the status bar layer is visible throughout the entire transition.
+ */
+ @Postsubmit
+ @Test
+ fun statusBarLayerIsAlwaysVisible() = testSpec.statusBarLayerIsVisible()
+
+ companion object {
+ @Parameterized.Parameters(name = "{0}")
+ @JvmStatic
+ fun getParams(): Collection<FlickerTestParameter> {
+ return FlickerTestParameterFactory.getInstance()
+ .getConfigNonRotationTests(
+ repetitions = 5,
+ supportedNavigationModes = listOf(
+ WindowManagerPolicyConstants.NAV_BAR_MODE_GESTURAL_OVERLAY
+ ),
+ supportedRotations = listOf(Surface.ROTATION_0, Surface.ROTATION_90)
+ )
+ }
+ }
+}
\ No newline at end of file
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/quickswitch/QuickSwitchFromLauncherTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/quickswitch/QuickSwitchFromLauncherTest.kt
index ca8f8af..10ca0d9 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/quickswitch/QuickSwitchFromLauncherTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/quickswitch/QuickSwitchFromLauncherTest.kt
@@ -27,7 +27,7 @@
import com.android.server.wm.flicker.FlickerTestParameter
import com.android.server.wm.flicker.FlickerTestParameterFactory
import com.android.server.wm.flicker.LAUNCHER_COMPONENT
-import com.android.server.wm.flicker.annotation.Group1
+import com.android.server.wm.flicker.annotation.Group4
import com.android.server.wm.flicker.dsl.FlickerBuilder
import com.android.server.wm.flicker.entireScreenCovered
import com.android.server.wm.flicker.helpers.SimpleAppHelper
@@ -38,7 +38,7 @@
import com.android.server.wm.flicker.startRotation
import com.android.server.wm.flicker.statusBarLayerIsVisible
import com.android.server.wm.flicker.statusBarWindowIsVisible
-import com.android.server.wm.traces.parser.windowmanager.WindowManagerStateHelper.Companion.SNAPSHOT_COMPONENT
+import com.android.server.wm.traces.common.FlickerComponentName
import org.junit.FixMethodOrder
import org.junit.Test
import org.junit.runner.RunWith
@@ -60,7 +60,7 @@
@RunWith(Parameterized::class)
@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-@Group1
+@Group4
class QuickSwitchFromLauncherTest(private val testSpec: FlickerTestParameter) {
private val instrumentation: Instrumentation = InstrumentationRegistry.getInstrumentation()
private val testApp = SimpleAppHelper(instrumentation)
@@ -145,7 +145,7 @@
@Test
fun startsWithHomeActivityFlaggedVisible() {
testSpec.assertWmStart {
- this.isHomeActivityVisible(true)
+ this.isHomeActivityVisible()
}
}
@@ -192,7 +192,7 @@
@Test
fun endsWithHomeActivityFlaggedInvisible() {
testSpec.assertWmEnd {
- this.isHomeActivityVisible(false)
+ this.isHomeActivityInvisible()
}
}
@@ -204,9 +204,9 @@
@Test
fun appWindowBecomesAndStaysVisible() {
testSpec.assertWm {
- this.isAppWindowInvisible(testApp.component, ignoreActivity = true)
+ this.isAppWindowInvisible(testApp.component)
.then()
- .isAppWindowVisible(testApp.component, ignoreActivity = true)
+ .isAppWindowVisible(testApp.component)
}
}
@@ -232,9 +232,9 @@
@Test
fun launcherWindowBecomesAndStaysInvisible() {
testSpec.assertWm {
- this.isAppWindowVisible(LAUNCHER_COMPONENT)
+ this.isAppWindowOnTop(LAUNCHER_COMPONENT)
.then()
- .isAppWindowInvisible(LAUNCHER_COMPONENT)
+ .isAppWindowNotOnTop(LAUNCHER_COMPONENT)
}
}
@@ -260,9 +260,9 @@
@Test
fun appWindowIsVisibleOnceLauncherWindowIsInvisible() {
testSpec.assertWm {
- this.isAppWindowVisible(LAUNCHER_COMPONENT)
+ this.isAppWindowOnTop(LAUNCHER_COMPONENT)
.then()
- .isAppWindowVisible(SNAPSHOT_COMPONENT)
+ .isAppWindowVisible(FlickerComponentName.SNAPSHOT)
.then()
.isAppWindowVisible(testApp.component)
}
@@ -278,7 +278,7 @@
testSpec.assertLayers {
this.isVisible(LAUNCHER_COMPONENT)
.then()
- .isVisible(SNAPSHOT_COMPONENT)
+ .isVisible(FlickerComponentName.SNAPSHOT)
.then()
.isVisible(testApp.component)
}
@@ -305,8 +305,7 @@
*/
@Presubmit
@Test
- fun navbarIsAlwaysInRightPosition() =
- testSpec.navBarLayerRotatesAndScales(testSpec.config.startRotation)
+ fun navbarIsAlwaysInRightPosition() = testSpec.navBarLayerRotatesAndScales()
/**
* Checks that the status bar window is visible throughout the entire transition.
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/ChangeAppRotationTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/ChangeAppRotationTest.kt
index d57c6698..fd8abc6 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/ChangeAppRotationTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/ChangeAppRotationTest.kt
@@ -16,6 +16,7 @@
package com.android.server.wm.flicker.rotation
+import android.platform.test.annotations.Postsubmit
import android.platform.test.annotations.Presubmit
import androidx.test.filters.FlakyTest
import androidx.test.filters.RequiresDevice
@@ -24,22 +25,53 @@
import com.android.server.wm.flicker.FlickerTestParameterFactory
import com.android.server.wm.flicker.annotation.Group3
import com.android.server.wm.flicker.dsl.FlickerBuilder
-import com.android.server.wm.flicker.endRotation
import com.android.server.wm.flicker.helpers.SimpleAppHelper
-import com.android.server.wm.flicker.startRotation
+import com.android.server.wm.flicker.rules.WMFlickerServiceRuleForTestSpec
import com.android.server.wm.flicker.statusBarLayerIsVisible
import com.android.server.wm.flicker.statusBarLayerRotatesScales
import com.android.server.wm.flicker.statusBarWindowIsVisible
-import com.android.server.wm.traces.parser.windowmanager.WindowManagerStateHelper.Companion.ROTATION_COMPONENT
+import com.android.server.wm.traces.common.FlickerComponentName
import org.junit.FixMethodOrder
+import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
import org.junit.runners.MethodSorters
import org.junit.runners.Parameterized
/**
- * Cycle through supported app rotations.
+ * Test opening an app and cycling through app rotations
+ *
+ * Currently runs:
+ * 0 -> 90 degrees
+ * 90 -> 0 degrees
+ *
+ * Actions:
+ * Launch an app (via intent)
+ * Set initial device orientation
+ * Start tracing
+ * Change device orientation
+ * Stop tracing
+ *
* To run this test: `atest FlickerTests:ChangeAppRotationTest`
+ *
+ * To run only the presubmit assertions add: `--
+ * --module-arg FlickerTests:exclude-annotation:androidx.test.filters.FlakyTest
+ * --module-arg FlickerTests:include-annotation:android.platform.test.annotations.Presubmit`
+ *
+ * To run only the postsubmit assertions add: `--
+ * --module-arg FlickerTests:exclude-annotation:androidx.test.filters.FlakyTest
+ * --module-arg FlickerTests:include-annotation:android.platform.test.annotations.Postsubmit`
+ *
+ * To run only the flaky assertions add: `--
+ * --module-arg FlickerTests:include-annotation:androidx.test.filters.FlakyTest`
+ *
+ * Notes:
+ * 1. Some default assertions (e.g., nav bar, status bar and screen covered)
+ * are inherited [RotationTransition]
+ * 2. Part of the test setup occurs automatically via
+ * [com.android.server.wm.flicker.TransitionRunnerWithRules],
+ * including configuring navigation mode, initial orientation and ensuring no
+ * apps are running before setup
*/
@RequiresDevice
@RunWith(Parameterized::class)
@@ -49,6 +81,9 @@
class ChangeAppRotationTest(
testSpec: FlickerTestParameter
) : RotationTransition(testSpec) {
+ @get:Rule
+ val flickerRule = WMFlickerServiceRuleForTestSpec(testSpec)
+
override val testApp = SimpleAppHelper(instrumentation)
override val transition: FlickerBuilder.(Map<String, Any?>) -> Unit
get() = {
@@ -60,50 +95,94 @@
}
}
+ @Postsubmit
+ @Test
+ fun runPresubmitAssertion() {
+ flickerRule.checkPresubmitAssertions()
+ }
+
+ @Postsubmit
+ @Test
+ fun runPostsubmitAssertion() {
+ flickerRule.checkPostsubmitAssertions()
+ }
+
+ @FlakyTest
+ @Test
+ fun runFlakyAssertion() {
+ flickerRule.checkFlakyAssertions()
+ }
+
+ /** {@inheritDoc} */
@FlakyTest(bugId = 190185577)
@Test
override fun focusDoesNotChange() {
super.focusDoesNotChange()
}
+ /**
+ * Checks that the [FlickerComponentName.ROTATION] layer appears during the transition,
+ * doesn't flicker, and disappears before the transition is complete
+ */
@Presubmit
@Test
- fun screenshotLayerBecomesInvisible() {
+ fun rotationLayerAppearsAndVanishes() {
testSpec.assertLayers {
this.isVisible(testApp.component)
.then()
- .isVisible(ROTATION_COMPONENT)
+ .isVisible(FlickerComponentName.ROTATION)
.then()
.isVisible(testApp.component)
+ .isInvisible(FlickerComponentName.ROTATION)
}
}
+ /**
+ * Checks that the status bar window is visible and above the app windows in all WM
+ * trace entries
+ */
@Presubmit
@Test
fun statusBarWindowIsVisible() {
testSpec.statusBarWindowIsVisible()
}
+ /**
+ * Checks that the status bar layer is visible at the start and end of the transition
+ */
@Presubmit
@Test
fun statusBarLayerIsVisible() {
testSpec.statusBarLayerIsVisible()
}
+ /**
+ * Checks the position of the status bar at the start and end of the transition
+ */
@Presubmit
@Test
- fun statusBarLayerRotatesScales() {
- testSpec.statusBarLayerRotatesScales(
- testSpec.config.startRotation, testSpec.config.endRotation)
- }
+ fun statusBarLayerRotatesScales() = testSpec.statusBarLayerRotatesScales()
+ /** {@inheritDoc} */
@FlakyTest
@Test
override fun navBarLayerRotatesAndScales() {
super.navBarLayerRotatesAndScales()
}
+ /** {@inheritDoc} */
+ @FlakyTest
+ @Test
+ override fun visibleLayersShownMoreThanOneConsecutiveEntry() =
+ super.visibleLayersShownMoreThanOneConsecutiveEntry()
+
companion object {
+ /**
+ * Creates the test configurations.
+ *
+ * See [FlickerTestParameterFactory.getConfigRotationTests] for configuring
+ * repetitions, screen orientation and navigation modes.
+ */
@Parameterized.Parameters(name = "{0}")
@JvmStatic
fun getParams(): Collection<FlickerTestParameter> {
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/RotationTransition.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/RotationTransition.kt
index 612ff9d..e850632 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/RotationTransition.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/RotationTransition.kt
@@ -17,7 +17,6 @@
package com.android.server.wm.flicker.rotation
import android.app.Instrumentation
-import android.content.ComponentName
import android.platform.test.annotations.Presubmit
import androidx.test.platform.app.InstrumentationRegistry
import com.android.server.wm.flicker.FlickerBuilderProvider
@@ -31,9 +30,12 @@
import com.android.server.wm.flicker.navBarWindowIsVisible
import com.android.server.wm.flicker.entireScreenCovered
import com.android.server.wm.flicker.startRotation
-import com.android.server.wm.traces.parser.windowmanager.WindowManagerStateHelper
+import com.android.server.wm.traces.common.FlickerComponentName
import org.junit.Test
+/**
+ * Base class for app rotation tests
+ */
abstract class RotationTransition(protected val testSpec: FlickerTestParameter) {
protected abstract val testApp: StandardAppHelper
@@ -55,6 +57,10 @@
}
}
+ /**
+ * Entry point for the test runner. It will use this method to initialize and cache
+ * flicker executions
+ */
@FlickerBuilderProvider
fun buildFlicker(): FlickerBuilder {
return FlickerBuilder(instrumentation).apply {
@@ -62,38 +68,53 @@
}
}
+ /**
+ * Checks that the navigation bar window is visible and above the app windows in all WM
+ * trace entries
+ */
@Presubmit
@Test
open fun navBarWindowIsVisible() {
testSpec.navBarWindowIsVisible()
}
+ /**
+ * Checks that the navigation bar layer is visible at the start and end of the transition
+ */
@Presubmit
@Test
open fun navBarLayerIsVisible() {
testSpec.navBarLayerIsVisible()
}
+ /**
+ * Checks the position of the navigation bar at the start and end of the transition
+ */
@Presubmit
@Test
- open fun navBarLayerRotatesAndScales() {
- testSpec.navBarLayerRotatesAndScales(
- testSpec.config.startRotation, testSpec.config.endRotation)
- }
+ open fun navBarLayerRotatesAndScales() = testSpec.navBarLayerRotatesAndScales()
+ /**
+ * Checks that all layers that are visible on the trace, are visible for at least 2
+ * consecutive entries.
+ */
@Presubmit
@Test
open fun visibleLayersShownMoreThanOneConsecutiveEntry() {
testSpec.assertLayers {
this.visibleLayersShownMoreThanOneConsecutiveEntry(
- ignoreLayers = listOf(WindowManagerStateHelper.SPLASH_SCREEN_COMPONENT,
- WindowManagerStateHelper.SNAPSHOT_COMPONENT,
- ComponentName("", "SecondaryHomeHandle")
+ ignoreLayers = listOf(FlickerComponentName.SPLASH_SCREEN,
+ FlickerComponentName.SNAPSHOT,
+ FlickerComponentName("", "SecondaryHomeHandle")
)
)
}
}
+ /**
+ * Checks that all windows that are visible on the trace, are visible for at least 2
+ * consecutive entries.
+ */
@Presubmit
@Test
open fun visibleWindowsShownMoreThanOneConsecutiveEntry() {
@@ -102,10 +123,16 @@
}
}
+ /**
+ * Checks that all parts of the screen are covered during the transition
+ */
@Presubmit
@Test
open fun entireScreenCovered() = testSpec.entireScreenCovered()
+ /**
+ * Checks that the focus doesn't change during animation
+ */
@Presubmit
@Test
open fun focusDoesNotChange() {
@@ -114,6 +141,9 @@
}
}
+ /**
+ * Checks that [testApp] layer covers the entire screen at the start of the transition
+ */
@Presubmit
@Test
open fun appLayerRotates_StartingPos() {
@@ -124,6 +154,9 @@
}
}
+ /**
+ * Checks that [testApp] layer covers the entire screen at the end of the transition
+ */
@Presubmit
@Test
open fun appLayerRotates_EndingPos() {
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/SeamlessAppRotationTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/SeamlessAppRotationTest.kt
index 48efe73..310f04b 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/SeamlessAppRotationTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/SeamlessAppRotationTest.kt
@@ -27,7 +27,7 @@
import com.android.server.wm.flicker.dsl.FlickerBuilder
import com.android.server.wm.flicker.helpers.SeamlessRotationAppHelper
import com.android.server.wm.flicker.testapp.ActivityOptions
-import com.android.server.wm.traces.parser.windowmanager.WindowManagerStateHelper
+import com.android.server.wm.traces.common.FlickerComponentName
import org.junit.FixMethodOrder
import org.junit.Test
import org.junit.runner.RunWith
@@ -35,8 +35,41 @@
import org.junit.runners.Parameterized
/**
- * Cycle through supported app rotations using seamless rotations.
+ * Test opening an app and cycling through app rotations using seamless rotations
+ *
+ * Currently runs:
+ * 0 -> 90 degrees
+ * 0 -> 90 degrees (with starved UI thread)
+ * 90 -> 0 degrees
+ * 90 -> 0 degrees (with starved UI thread)
+ *
+ * Actions:
+ * Launch an app in fullscreen and supporting seamless rotation (via intent)
+ * Set initial device orientation
+ * Start tracing
+ * Change device orientation
+ * Stop tracing
+ *
* To run this test: `atest FlickerTests:SeamlessAppRotationTest`
+ *
+ * To run only the presubmit assertions add: `--
+ * --module-arg FlickerTests:exclude-annotation:androidx.test.filters.FlakyTest
+ * --module-arg FlickerTests:include-annotation:android.platform.test.annotations.Presubmit`
+ *
+ * To run only the postsubmit assertions add: `--
+ * --module-arg FlickerTests:exclude-annotation:androidx.test.filters.FlakyTest
+ * --module-arg FlickerTests:include-annotation:android.platform.test.annotations.Postsubmit`
+ *
+ * To run only the flaky assertions add: `--
+ * --module-arg FlickerTests:include-annotation:androidx.test.filters.FlakyTest`
+ *
+ * Notes:
+ * 1. Some default assertions (e.g., nav bar, status bar and screen covered)
+ * are inherited [RotationTransition]
+ * 2. Part of the test setup occurs automatically via
+ * [com.android.server.wm.flicker.TransitionRunnerWithRules],
+ * including configuring navigation mode, initial orientation and ensuring no
+ * apps are running before setup
*/
@RequiresDevice
@RunWith(Parameterized::class)
@@ -61,6 +94,9 @@
}
}
+ /**
+ * Checks that [testApp] window is always in full screen
+ */
@Presubmit
@Test
fun appWindowFullScreen() {
@@ -75,6 +111,9 @@
}
}
+ /**
+ * Checks that [testApp] window is always with seamless rotation
+ */
@Presubmit
@Test
fun appWindowSeamlessRotation() {
@@ -90,6 +129,9 @@
}
}
+ /**
+ * Checks that [testApp] window is always visible
+ */
@Presubmit
@Test
fun appLayerAlwaysVisible() {
@@ -98,6 +140,9 @@
}
}
+ /**
+ * Checks that [testApp] layer covers the entire screen during the whole transition
+ */
@Presubmit
@Test
fun appLayerRotates() {
@@ -110,29 +155,36 @@
}
}
+ /**
+ * Checks that the [FlickerComponentName.STATUS_BAR] window is invisible during the whole
+ * transition
+ */
@Presubmit
@Test
fun statusBarWindowIsAlwaysInvisible() {
testSpec.assertWm {
- this.isAboveAppWindowInvisible(WindowManagerStateHelper.STATUS_BAR_COMPONENT)
+ this.isAboveAppWindowInvisible(FlickerComponentName.STATUS_BAR)
}
}
+ /**
+ * Checks that the [FlickerComponentName.STATUS_BAR] layer is invisible during the whole
+ * transition
+ */
@Presubmit
@Test
fun statusBarLayerIsAlwaysInvisible() {
testSpec.assertLayers {
- this.isInvisible(WindowManagerStateHelper.STATUS_BAR_COMPONENT)
+ this.isInvisible(FlickerComponentName.STATUS_BAR)
}
}
+ /** {@inheritDoc} */
@FlakyTest
@Test
override fun navBarLayerRotatesAndScales() = super.navBarLayerRotatesAndScales()
companion object {
- private val testFactory = FlickerTestParameterFactory.getInstance()
-
private val Map<String, Any?>.starveUiThread
get() = this.getOrDefault(ActivityOptions.EXTRA_STARVE_UI_THREAD, false) as Boolean
@@ -144,20 +196,34 @@
return config
}
+ /**
+ * Creates the test configurations for seamless rotation based on the default rotation
+ * tests from [FlickerTestParameterFactory.getConfigRotationTests], but adding an
+ * additional flag ([ActivityOptions.EXTRA_STARVE_UI_THREAD]) to indicate if the app
+ * should starve the UI thread of not
+ */
@JvmStatic
private fun getConfigurations(): List<FlickerTestParameter> {
- return testFactory.getConfigRotationTests(repetitions = 2).flatMap {
- val defaultRun = it.createConfig(starveUiThread = false)
- val busyUiRun = it.createConfig(starveUiThread = true)
- listOf(
- FlickerTestParameter(defaultRun),
- FlickerTestParameter(busyUiRun,
- name = "${FlickerTestParameter.defaultName(busyUiRun)}_BUSY_UI_THREAD"
+ return FlickerTestParameterFactory.getInstance()
+ .getConfigRotationTests(repetitions = 2)
+ .flatMap {
+ val defaultRun = it.createConfig(starveUiThread = false)
+ val busyUiRun = it.createConfig(starveUiThread = true)
+ listOf(
+ FlickerTestParameter(defaultRun),
+ FlickerTestParameter(busyUiRun,
+ name = "${FlickerTestParameter.defaultName(busyUiRun)}_BUSY_UI_THREAD"
+ )
)
- )
- }
+ }
}
+ /**
+ * Creates the test configurations.
+ *
+ * See [FlickerTestParameterFactory.getConfigRotationTests] for configuring
+ * repetitions, screen orientation and navigation modes.
+ */
@Parameterized.Parameters(name = "{0}")
@JvmStatic
fun getParams(): Collection<FlickerTestParameter> {
diff --git a/tests/FlickerTests/test-apps/flickerapp/AndroidManifest.xml b/tests/FlickerTests/test-apps/flickerapp/AndroidManifest.xml
index 3b9f33a..cb37fc7 100644
--- a/tests/FlickerTests/test-apps/flickerapp/AndroidManifest.xml
+++ b/tests/FlickerTests/test-apps/flickerapp/AndroidManifest.xml
@@ -80,5 +80,15 @@
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
</activity>
+ <activity android:name=".LaunchNewTaskActivity"
+ android:taskAffinity="com.android.server.wm.flicker.testapp.LaunchNewTaskActivity"
+ android:configChanges="orientation|screenSize"
+ android:label="LaunchNewTaskActivity"
+ android:exported="true">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN"/>
+ <category android:name="android.intent.category.LAUNCHER"/>
+ </intent-filter>
+ </activity>
</application>
</manifest>
diff --git a/tests/FlickerTests/test-apps/flickerapp/res/layout/task_button.xml b/tests/FlickerTests/test-apps/flickerapp/res/layout/task_button.xml
new file mode 100644
index 0000000..8f75d17
--- /dev/null
+++ b/tests/FlickerTests/test-apps/flickerapp/res/layout/task_button.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright 2018 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.
+-->
+<LinearLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:background="@android:color/holo_orange_light">
+ <Button
+ android:id="@+id/launch_new_task"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="New task" />
+</LinearLayout>
diff --git a/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/ActivityOptions.java b/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/ActivityOptions.java
index 224d2ac..baf36ab 100644
--- a/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/ActivityOptions.java
+++ b/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/ActivityOptions.java
@@ -51,4 +51,9 @@
public static final ComponentName BUTTON_ACTIVITY_COMPONENT_NAME =
new ComponentName(FLICKER_APP_PACKAGE,
FLICKER_APP_PACKAGE + ".ButtonActivity");
+
+ public static final String LAUNCH_NEW_TASK_ACTIVITY_LAUNCHER_NAME = "LaunchNewTaskApp";
+ public static final ComponentName LAUNCH_NEW_TASK_ACTIVITY_COMPONENT_NAME =
+ new ComponentName(FLICKER_APP_PACKAGE,
+ FLICKER_APP_PACKAGE + ".LaunchNewTaskActivity");
}
diff --git a/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/LaunchNewTaskActivity.java b/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/LaunchNewTaskActivity.java
new file mode 100644
index 0000000..1809781
--- /dev/null
+++ b/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/LaunchNewTaskActivity.java
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.wm.flicker.testapp;
+
+import static android.content.Intent.FLAG_ACTIVITY_MULTIPLE_TASK;
+
+import android.app.Activity;
+import android.content.Intent;
+import android.os.Bundle;
+import android.view.WindowManager;
+import android.widget.Button;
+
+public class LaunchNewTaskActivity extends Activity {
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ WindowManager.LayoutParams p = getWindow().getAttributes();
+ p.layoutInDisplayCutoutMode = WindowManager.LayoutParams
+ .LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES;
+ getWindow().setAttributes(p);
+ setContentView(R.layout.task_button);
+
+ Button button = findViewById(R.id.launch_new_task);
+ button.setOnClickListener(v -> {
+ Intent intent = new Intent(LaunchNewTaskActivity.this, SimpleActivity.class);
+ intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_MULTIPLE_TASK);
+ startActivity(intent);
+ });
+ }
+}