Merge "Use expression functions when appropriate" into main
diff --git a/libs/WindowManager/Shell/AndroidManifest.xml b/libs/WindowManager/Shell/AndroidManifest.xml
index b2ac640..4f1cd97 100644
--- a/libs/WindowManager/Shell/AndroidManifest.xml
+++ b/libs/WindowManager/Shell/AndroidManifest.xml
@@ -26,6 +26,7 @@
<uses-permission android:name="android.permission.SUBSCRIBE_TO_KEYGUARD_LOCKED_STATE" />
<uses-permission android:name="android.permission.UPDATE_DOMAIN_VERIFICATION_USER_SELECTION" />
<uses-permission android:name="android.permission.MANAGE_KEY_GESTURES" />
+ <uses-permission android:name="android.permission.MANAGE_DISPLAYS" />
<application>
<activity
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/appzoomout/AppZoomOutController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/appzoomout/AppZoomOutController.java
index 8cd7b0f..82ef00e 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/appzoomout/AppZoomOutController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/appzoomout/AppZoomOutController.java
@@ -16,6 +16,7 @@
package com.android.wm.shell.appzoomout;
+import static android.app.WindowConfiguration.ROTATION_UNDEFINED;
import static android.view.Display.DEFAULT_DISPLAY;
import android.app.ActivityManager;
@@ -100,6 +101,7 @@
mDisplayController.addDisplayWindowListener(mDisplaysChangedListener);
mDisplayController.addDisplayChangingController(this);
+ updateDisplayLayout(mContext.getDisplayId());
mDisplayAreaOrganizer.registerOrganizer();
}
@@ -135,7 +137,9 @@
public void onDisplayChange(int displayId, int fromRotation, int toRotation,
@Nullable DisplayAreaInfo newDisplayAreaInfo, WindowContainerTransaction wct) {
// TODO: verify if there is synchronization issues.
- mDisplayAreaOrganizer.onRotateDisplay(mContext, toRotation);
+ if (toRotation != ROTATION_UNDEFINED) {
+ mDisplayAreaOrganizer.onRotateDisplay(mContext, toRotation);
+ }
}
@Override
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayController.java
index f532be6..72be066 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayController.java
@@ -21,6 +21,7 @@
import android.content.res.Configuration;
import android.graphics.Rect;
import android.hardware.display.DisplayManager;
+import android.hardware.display.DisplayTopology;
import android.os.RemoteException;
import android.util.ArraySet;
import android.util.Size;
@@ -34,6 +35,7 @@
import androidx.annotation.BinderThread;
+import com.android.window.flags.Flags;
import com.android.wm.shell.common.DisplayChangeController.OnDisplayChangingListener;
import com.android.wm.shell.shared.annotations.ShellMainThread;
import com.android.wm.shell.sysui.ShellInit;
@@ -54,6 +56,7 @@
private final ShellExecutor mMainExecutor;
private final Context mContext;
private final IWindowManager mWmService;
+ private final DisplayManager mDisplayManager;
private final DisplayChangeController mChangeController;
private final IDisplayWindowListener mDisplayContainerListener;
@@ -61,10 +64,11 @@
private final ArrayList<OnDisplaysChangedListener> mDisplayChangedListeners = new ArrayList<>();
public DisplayController(Context context, IWindowManager wmService, ShellInit shellInit,
- ShellExecutor mainExecutor) {
+ ShellExecutor mainExecutor, DisplayManager displayManager) {
mMainExecutor = mainExecutor;
mContext = context;
mWmService = wmService;
+ mDisplayManager = displayManager;
// TODO: Inject this instead
mChangeController = new DisplayChangeController(mWmService, shellInit, mainExecutor);
mDisplayContainerListener = new DisplayWindowListenerImpl();
@@ -74,7 +78,7 @@
}
/**
- * Initializes the window listener.
+ * Initializes the window listener and the topology listener.
*/
public void onInit() {
try {
@@ -82,6 +86,12 @@
for (int i = 0; i < displayIds.length; i++) {
onDisplayAdded(displayIds[i]);
}
+
+ if (Flags.enableConnectedDisplaysWindowDrag()) {
+ mDisplayManager.registerTopologyListener(mMainExecutor,
+ this::onDisplayTopologyChanged);
+ onDisplayTopologyChanged(mDisplayManager.getDisplayTopology());
+ }
} catch (RemoteException e) {
throw new RuntimeException("Unable to register display controller");
}
@@ -91,8 +101,7 @@
* Gets a display by id from DisplayManager.
*/
public Display getDisplay(int displayId) {
- final DisplayManager displayManager = mContext.getSystemService(DisplayManager.class);
- return displayManager.getDisplay(displayId);
+ return mDisplayManager.getDisplay(displayId);
}
/**
@@ -221,6 +230,14 @@
}
}
+ private void onDisplayTopologyChanged(DisplayTopology topology) {
+ // TODO(b/381472611): Call DisplayTopology#getCoordinates and update values in
+ // DisplayLayout when DM code is ready.
+ for (int i = 0; i < mDisplayChangedListeners.size(); ++i) {
+ mDisplayChangedListeners.get(i).onTopologyChanged();
+ }
+ }
+
private void onDisplayConfigurationChanged(int displayId, Configuration newConfig) {
synchronized (mDisplays) {
final DisplayRecord dr = mDisplays.get(displayId);
@@ -408,5 +425,10 @@
*/
default void onKeepClearAreasChanged(int displayId, Set<Rect> restricted,
Set<Rect> unrestricted) {}
+
+ /**
+ * Called when the display topology has changed.
+ */
+ default void onTopologyChanged() {}
}
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayLayout.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayLayout.java
index b6a1686..4973a6f 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayLayout.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayLayout.java
@@ -31,7 +31,9 @@
import android.content.Context;
import android.content.res.Resources;
import android.graphics.Insets;
+import android.graphics.PointF;
import android.graphics.Rect;
+import android.graphics.RectF;
import android.os.SystemProperties;
import android.provider.Settings;
import android.util.DisplayMetrics;
@@ -71,9 +73,12 @@
public static final int NAV_BAR_RIGHT = 1 << 1;
public static final int NAV_BAR_BOTTOM = 1 << 2;
+ private static final String TAG = "DisplayLayout";
+
private int mUiMode;
private int mWidth;
private int mHeight;
+ private RectF mGlobalBoundsDp;
private DisplayCutout mCutout;
private int mRotation;
private int mDensityDpi;
@@ -109,6 +114,7 @@
return mUiMode == other.mUiMode
&& mWidth == other.mWidth
&& mHeight == other.mHeight
+ && Objects.equals(mGlobalBoundsDp, other.mGlobalBoundsDp)
&& Objects.equals(mCutout, other.mCutout)
&& mRotation == other.mRotation
&& mDensityDpi == other.mDensityDpi
@@ -127,8 +133,8 @@
@Override
public int hashCode() {
- return Objects.hash(mUiMode, mWidth, mHeight, mCutout, mRotation, mDensityDpi,
- mNonDecorInsets, mStableInsets, mHasNavigationBar, mHasStatusBar,
+ return Objects.hash(mUiMode, mWidth, mHeight, mGlobalBoundsDp, mCutout, mRotation,
+ mDensityDpi, mNonDecorInsets, mStableInsets, mHasNavigationBar, mHasStatusBar,
mNavBarFrameHeight, mTaskbarFrameHeight, mAllowSeamlessRotationDespiteNavBarMoving,
mNavigationBarCanMove, mReverseDefaultRotation, mInsetsState);
}
@@ -170,6 +176,7 @@
mUiMode = dl.mUiMode;
mWidth = dl.mWidth;
mHeight = dl.mHeight;
+ mGlobalBoundsDp = dl.mGlobalBoundsDp;
mCutout = dl.mCutout;
mRotation = dl.mRotation;
mDensityDpi = dl.mDensityDpi;
@@ -193,6 +200,7 @@
mRotation = info.rotation;
mCutout = info.displayCutout;
mDensityDpi = info.logicalDensityDpi;
+ mGlobalBoundsDp = new RectF(0, 0, pxToDp(mWidth), pxToDp(mHeight));
mHasNavigationBar = hasNavigationBar;
mHasStatusBar = hasStatusBar;
mAllowSeamlessRotationDespiteNavBarMoving = res.getBoolean(
@@ -255,6 +263,11 @@
recalcInsets(res);
}
+ /** Update the global bounds of this layout, in DP. */
+ public void setGlobalBoundsDp(RectF bounds) {
+ mGlobalBoundsDp = bounds;
+ }
+
/** Get this layout's non-decor insets. */
public Rect nonDecorInsets() {
return mNonDecorInsets;
@@ -265,16 +278,21 @@
return mStableInsets;
}
- /** Get this layout's width. */
+ /** Get this layout's width in pixels. */
public int width() {
return mWidth;
}
- /** Get this layout's height. */
+ /** Get this layout's height in pixels. */
public int height() {
return mHeight;
}
+ /** Get this layout's global bounds in the multi-display coordinate system in DP. */
+ public RectF globalBoundsDp() {
+ return mGlobalBoundsDp;
+ }
+
/** Get this layout's display rotation. */
public int rotation() {
return mRotation;
@@ -486,4 +504,48 @@
? R.dimen.navigation_bar_frame_height_landscape
: R.dimen.navigation_bar_frame_height);
}
+
+ /**
+ * Converts a pixel value to a density-independent pixel (dp) value.
+ *
+ * @param px The pixel value to convert.
+ * @return The equivalent value in DP units.
+ */
+ public float pxToDp(Number px) {
+ return px.floatValue() * DisplayMetrics.DENSITY_DEFAULT / mDensityDpi;
+ }
+
+ /**
+ * Converts a density-independent pixel (dp) value to a pixel value.
+ *
+ * @param dp The DP value to convert.
+ * @return The equivalent value in pixel units.
+ */
+ public float dpToPx(Number dp) {
+ return dp.floatValue() * mDensityDpi / DisplayMetrics.DENSITY_DEFAULT;
+ }
+
+ /**
+ * Converts local pixel coordinates on this layout to global DP coordinates.
+ *
+ * @param xPx The x-coordinate in pixels, relative to the layout's origin.
+ * @param yPx The y-coordinate in pixels, relative to the layout's origin.
+ * @return A PointF object representing the coordinates in global DP units.
+ */
+ public PointF localPxToGlobalDp(Number xPx, Number yPx) {
+ return new PointF(mGlobalBoundsDp.left + pxToDp(xPx),
+ mGlobalBoundsDp.top + pxToDp(yPx));
+ }
+
+ /**
+ * Converts global DP coordinates to local pixel coordinates on this layout.
+ *
+ * @param xDp The x-coordinate in global DP units.
+ * @param yDp The y-coordinate in global DP units.
+ * @return A PointF object representing the coordinates in local pixel units on this layout.
+ */
+ public PointF globalDpToLocalPx(Number xDp, Number yDp) {
+ return new PointF(dpToPx(xDp.floatValue() - mGlobalBoundsDp.left),
+ dpToPx(yDp.floatValue() - mGlobalBoundsDp.top));
+ }
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java
index ab3c33e..cbbe8a2 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java
@@ -25,6 +25,7 @@
import android.app.ActivityTaskManager;
import android.content.Context;
import android.content.pm.PackageManager;
+import android.hardware.display.DisplayManager;
import android.os.Handler;
import android.os.SystemProperties;
import android.provider.Settings;
@@ -175,8 +176,9 @@
static DisplayController provideDisplayController(Context context,
IWindowManager wmService,
ShellInit shellInit,
- @ShellMainThread ShellExecutor mainExecutor) {
- return new DisplayController(context, wmService, shellInit, mainExecutor);
+ @ShellMainThread ShellExecutor mainExecutor,
+ DisplayManager displayManager) {
+ return new DisplayController(context, wmService, shellInit, mainExecutor, displayManager);
}
@WMSingleton
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/DisplayControllerTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/DisplayControllerTests.java
index 1e5e153..d3de0f7 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/DisplayControllerTests.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/DisplayControllerTests.java
@@ -22,6 +22,7 @@
import static org.mockito.Mockito.verify;
import android.content.Context;
+import android.hardware.display.DisplayManager;
import android.view.IWindowManager;
import androidx.test.ext.junit.runners.AndroidJUnit4;
@@ -50,12 +51,14 @@
private @Mock IWindowManager mWM;
private @Mock ShellInit mShellInit;
private @Mock ShellExecutor mMainExecutor;
+ private @Mock DisplayManager mDisplayManager;
private DisplayController mController;
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
- mController = new DisplayController(mContext, mWM, mShellInit, mMainExecutor);
+ mController = new DisplayController(
+ mContext, mWM, mShellInit, mMainExecutor, mDisplayManager);
}
@Test
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/DisplayLayoutTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/DisplayLayoutTest.java
index d467b39..b0a455d 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/DisplayLayoutTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/DisplayLayoutTest.java
@@ -33,7 +33,9 @@
import android.content.res.Configuration;
import android.content.res.Resources;
import android.graphics.Insets;
+import android.graphics.PointF;
import android.graphics.Rect;
+import android.graphics.RectF;
import android.view.DisplayCutout;
import android.view.DisplayInfo;
@@ -58,6 +60,7 @@
@SmallTest
public class DisplayLayoutTest extends ShellTestCase {
private MockitoSession mMockitoSession;
+ private static final float DELTA = 0.1f; // Constant for assertion delta
@Before
public void setup() {
@@ -130,6 +133,39 @@
assertEquals(new Rect(40, 0, 60, 0), dl.nonDecorInsets());
}
+ @Test
+ public void testDpPxConversion() {
+ int px = 100;
+ float dp = 53.33f;
+ int xPx = 100;
+ int yPx = 200;
+ float xDp = 164.33f;
+ float yDp = 328.66f;
+
+ Resources res = createResources(40, 50, false);
+ DisplayInfo info = createDisplayInfo(1000, 1500, 0, ROTATION_0);
+ DisplayLayout dl = new DisplayLayout(info, res, false, false);
+ dl.setGlobalBoundsDp(new RectF(111f, 222f, 300f, 400f));
+
+ // Test pxToDp
+ float resultDp = dl.pxToDp(px);
+ assertEquals(dp, resultDp, DELTA);
+
+ // Test dpToPx
+ float resultPx = dl.dpToPx(dp);
+ assertEquals(px, resultPx, DELTA);
+
+ // Test localPxToGlobalDp
+ PointF resultGlobalDp = dl.localPxToGlobalDp(xPx, yPx);
+ assertEquals(xDp, resultGlobalDp.x, DELTA);
+ assertEquals(yDp, resultGlobalDp.y, DELTA);
+
+ // Test globalDpToLocalPx
+ PointF resultLocalPx = dl.globalDpToLocalPx(xDp, yDp);
+ assertEquals(xPx, resultLocalPx.x, DELTA);
+ assertEquals(yPx, resultLocalPx.y, DELTA);
+ }
+
private Resources createResources(int navLand, int navPort, boolean navMoves) {
Configuration cfg = new Configuration();
cfg.uiMode = UI_MODE_TYPE_NORMAL;
diff --git a/packages/SettingsLib/DataStore/src/com/android/settingslib/datastore/DataChangeReason.kt b/packages/SettingsLib/DataStore/src/com/android/settingslib/datastore/DataChangeReason.kt
index 145fabe..ac36b08 100644
--- a/packages/SettingsLib/DataStore/src/com/android/settingslib/datastore/DataChangeReason.kt
+++ b/packages/SettingsLib/DataStore/src/com/android/settingslib/datastore/DataChangeReason.kt
@@ -39,5 +39,7 @@
const val RESTORE = 3
/** Data is synced from another profile (e.g. personal profile to work profile). */
const val SYNC_ACROSS_PROFILES = 4
+
+ fun isDataChange(reason: Int): Boolean = reason in UNKNOWN..SYNC_ACROSS_PROFILES
}
}
diff --git a/packages/SettingsLib/Metadata/src/com/android/settingslib/metadata/PersistentPreference.kt b/packages/SettingsLib/Metadata/src/com/android/settingslib/metadata/PersistentPreference.kt
index e5bf41f..83725aa 100644
--- a/packages/SettingsLib/Metadata/src/com/android/settingslib/metadata/PersistentPreference.kt
+++ b/packages/SettingsLib/Metadata/src/com/android/settingslib/metadata/PersistentPreference.kt
@@ -44,6 +44,24 @@
}
}
+/** The reason of preference change. */
+@IntDef(
+ PreferenceChangeReason.VALUE,
+ PreferenceChangeReason.STATE,
+ PreferenceChangeReason.DEPENDENT,
+)
+@Retention(AnnotationRetention.SOURCE)
+annotation class PreferenceChangeReason {
+ companion object {
+ /** Preference value is changed. */
+ const val VALUE = 1000
+ /** Preference state (title/summary, enable state, etc.) is changed. */
+ const val STATE = 1001
+ /** Dependent preference state is changed. */
+ const val DEPENDENT = 1002
+ }
+}
+
/** Indicates how sensitive of the data. */
@Retention(AnnotationRetention.SOURCE)
@Target(AnnotationTarget.TYPE)
diff --git a/packages/SettingsLib/Preference/src/com/android/settingslib/preference/PreferenceScreenBindingHelper.kt b/packages/SettingsLib/Preference/src/com/android/settingslib/preference/PreferenceScreenBindingHelper.kt
index 91abd8b..8358ab9 100644
--- a/packages/SettingsLib/Preference/src/com/android/settingslib/preference/PreferenceScreenBindingHelper.kt
+++ b/packages/SettingsLib/Preference/src/com/android/settingslib/preference/PreferenceScreenBindingHelper.kt
@@ -25,12 +25,14 @@
import androidx.preference.PreferenceDataStore
import androidx.preference.PreferenceGroup
import androidx.preference.PreferenceScreen
+import com.android.settingslib.datastore.DataChangeReason
import com.android.settingslib.datastore.HandlerExecutor
import com.android.settingslib.datastore.KeyValueStore
import com.android.settingslib.datastore.KeyedDataObservable
import com.android.settingslib.datastore.KeyedObservable
import com.android.settingslib.datastore.KeyedObserver
import com.android.settingslib.metadata.PersistentPreference
+import com.android.settingslib.metadata.PreferenceChangeReason
import com.android.settingslib.metadata.PreferenceHierarchy
import com.android.settingslib.metadata.PreferenceHierarchyNode
import com.android.settingslib.metadata.PreferenceLifecycleContext
@@ -73,7 +75,7 @@
?.keyValueStore
override fun notifyPreferenceChange(key: String) =
- notifyChange(key, CHANGE_REASON_STATE)
+ notifyChange(key, PreferenceChangeReason.STATE)
@Suppress("DEPRECATION")
override fun startActivityForResult(
@@ -91,7 +93,13 @@
private val preferenceObserver: KeyedObserver<String?>
private val storageObserver =
- KeyedObserver<String> { key, _ -> notifyChange(key, CHANGE_REASON_VALUE) }
+ KeyedObserver<String> { key, reason ->
+ if (DataChangeReason.isDataChange(reason)) {
+ notifyChange(key, PreferenceChangeReason.VALUE)
+ } else {
+ notifyChange(key, PreferenceChangeReason.STATE)
+ }
+ }
init {
val preferencesBuilder = ImmutableMap.builder<String, PreferenceHierarchyNode>()
@@ -148,7 +156,7 @@
}
// check reason to avoid potential infinite loop
- if (reason != CHANGE_REASON_DEPENDENT) {
+ if (reason != PreferenceChangeReason.DEPENDENT) {
notifyDependents(key, mutableSetOf())
}
}
@@ -157,7 +165,7 @@
private fun notifyDependents(key: String, notifiedKeys: MutableSet<String>) {
if (!notifiedKeys.add(key)) return
for (dependency in dependencies[key]) {
- notifyChange(dependency, CHANGE_REASON_DEPENDENT)
+ notifyChange(dependency, PreferenceChangeReason.DEPENDENT)
notifyDependents(dependency, notifiedKeys)
}
}
@@ -210,13 +218,6 @@
}
companion object {
- /** Preference value is changed. */
- const val CHANGE_REASON_VALUE = 0
- /** Preference state (title/summary, enable state, etc.) is changed. */
- const val CHANGE_REASON_STATE = 1
- /** Dependent preference state is changed. */
- const val CHANGE_REASON_DEPENDENT = 2
-
/** Updates preference screen that has incomplete hierarchy. */
@JvmStatic
fun bind(preferenceScreen: PreferenceScreen) {
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/LeAudioProfile.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/LeAudioProfile.java
index 6be4336..155c7e6 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/LeAudioProfile.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/LeAudioProfile.java
@@ -21,6 +21,7 @@
import static android.bluetooth.BluetoothProfile.CONNECTION_POLICY_ALLOWED;
import static android.bluetooth.BluetoothProfile.CONNECTION_POLICY_FORBIDDEN;
+import android.annotation.CallbackExecutor;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothClass;
import android.bluetooth.BluetoothCsipSetCoordinator;
@@ -39,6 +40,7 @@
import java.util.ArrayList;
import java.util.List;
+import java.util.concurrent.Executor;
public class LeAudioProfile implements LocalBluetoothProfile {
private static final String TAG = "LeAudioProfile";
@@ -317,6 +319,78 @@
return mService.getAudioLocation(device);
}
+ /**
+ * Sets the fallback group id when broadcast switches to unicast.
+ *
+ * @param groupId the target fallback group id
+ */
+ public void setBroadcastToUnicastFallbackGroup(int groupId) {
+ if (mService == null) {
+ Log.w(TAG, "Proxy not attached to service. Cannot set fallback group: " + groupId);
+ return;
+ }
+
+ mService.setBroadcastToUnicastFallbackGroup(groupId);
+ }
+
+ /**
+ * Gets the fallback group id when broadcast switches to unicast.
+ *
+ * @return current fallback group id
+ */
+ public int getBroadcastToUnicastFallbackGroup() {
+ if (mService == null) {
+ Log.w(TAG, "Proxy not attached to service. Cannot get fallback group.");
+ return BluetoothCsipSetCoordinator.GROUP_ID_INVALID;
+ }
+ return mService.getBroadcastToUnicastFallbackGroup();
+ }
+
+ /**
+ * Registers a {@link BluetoothLeAudio.Callback} that will be invoked during the
+ * operation of this profile.
+ *
+ * Repeated registration of the same <var>callback</var> object after the first call to this
+ * method will result with IllegalArgumentException being thrown, even when the
+ * <var>executor</var> is different. API caller would have to call
+ * {@link #unregisterCallback(BluetoothLeAudio.Callback)} with the same callback object
+ * before registering it again.
+ *
+ * @param executor an {@link Executor} to execute given callback
+ * @param callback user implementation of the {@link BluetoothLeAudio.Callback}
+ * @throws NullPointerException if a null executor, or callback is given, or
+ * IllegalArgumentException if the same <var>callback</var> is
+ * already registered.
+ */
+ public void registerCallback(
+ @NonNull @CallbackExecutor Executor executor,
+ @NonNull BluetoothLeAudio.Callback callback) {
+ if (mService == null) {
+ Log.w(TAG, "Proxy not attached to service. Cannot register callback.");
+ return;
+ }
+ mService.registerCallback(executor, callback);
+ }
+
+ /**
+ * Unregisters the specified {@link BluetoothLeAudio.Callback}.
+ * <p>The same {@link BluetoothLeAudio.Callback} object used when calling
+ * {@link #registerCallback(Executor, BluetoothLeAudio.Callback)} must be used.
+ *
+ * <p>Callbacks are automatically unregistered when application process goes away
+ *
+ * @param callback user implementation of the {@link BluetoothLeAudio.Callback}
+ * @throws NullPointerException when callback is null or IllegalArgumentException when no
+ * callback is registered
+ */
+ public void unregisterCallback(@NonNull BluetoothLeAudio.Callback callback) {
+ if (mService == null) {
+ Log.w(TAG, "Proxy not attached to service. Cannot unregister callback.");
+ return;
+ }
+ mService.unregisterCallback(callback);
+ }
+
@RequiresApi(Build.VERSION_CODES.S)
protected void finalize() {
if (DEBUG) {
diff --git a/packages/SystemUI/compose/core/src/com/android/compose/gesture/NestedDraggable.kt b/packages/SystemUI/compose/core/src/com/android/compose/gesture/NestedDraggable.kt
index 96401ce..a27bf8a 100644
--- a/packages/SystemUI/compose/core/src/com/android/compose/gesture/NestedDraggable.kt
+++ b/packages/SystemUI/compose/core/src/com/android/compose/gesture/NestedDraggable.kt
@@ -444,7 +444,7 @@
val left = available - consumed
val postConsumed =
nestedScrollDispatcher.dispatchPostScroll(
- consumed = preConsumed + consumed,
+ consumed = consumed,
available = left,
source = NestedScrollSource.UserInput,
)
@@ -482,10 +482,9 @@
val available = velocity - preConsumed
val consumed = performFling(available)
val left = available - consumed
- return nestedScrollDispatcher.dispatchPostFling(
- consumed = consumed + preConsumed,
- available = left,
- )
+ val postConsumed =
+ nestedScrollDispatcher.dispatchPostFling(consumed = consumed, available = left)
+ return preConsumed + consumed + postConsumed
}
/*
@@ -549,9 +548,10 @@
nestedScrollController == null &&
// TODO(b/388231324): Remove this.
!lastEventWasScrollWheel &&
- draggable.shouldConsumeNestedScroll(sign)
+ draggable.shouldConsumeNestedScroll(sign) &&
+ lastFirstDown != null
) {
- val startedPosition = checkNotNull(lastFirstDown) { "lastFirstDown is not set" }
+ val startedPosition = checkNotNull(lastFirstDown)
// TODO(b/382665591): Ensure that there is at least one pointer down.
val pointersDownCount = pointersDown.size.coerceAtLeast(1)
diff --git a/packages/SystemUI/compose/core/tests/src/com/android/compose/gesture/NestedDraggableTest.kt b/packages/SystemUI/compose/core/tests/src/com/android/compose/gesture/NestedDraggableTest.kt
index 5de0f12..19d28cc 100644
--- a/packages/SystemUI/compose/core/tests/src/com/android/compose/gesture/NestedDraggableTest.kt
+++ b/packages/SystemUI/compose/core/tests/src/com/android/compose/gesture/NestedDraggableTest.kt
@@ -36,6 +36,7 @@
import androidx.compose.ui.Modifier
import androidx.compose.ui.geometry.Offset
import androidx.compose.ui.input.nestedscroll.NestedScrollConnection
+import androidx.compose.ui.input.nestedscroll.NestedScrollSource
import androidx.compose.ui.input.nestedscroll.nestedScroll
import androidx.compose.ui.input.pointer.PointerInputChange
import androidx.compose.ui.input.pointer.PointerType
@@ -52,6 +53,7 @@
import androidx.compose.ui.test.performTouchInput
import androidx.compose.ui.test.swipeDown
import androidx.compose.ui.test.swipeLeft
+import androidx.compose.ui.test.swipeWithVelocity
import androidx.compose.ui.unit.Velocity
import com.google.common.truth.Truth.assertThat
import kotlin.math.ceil
@@ -773,6 +775,181 @@
rule.onNodeWithTag(buttonTag).assertTextEquals("Count: 3")
}
+ @Test
+ fun nestedDragNotStartedWhenEnabledAfterDragStarted() {
+ val draggable = TestDraggable()
+ var enabled by mutableStateOf(false)
+ val touchSlop =
+ rule.setContentWithTouchSlop {
+ Box(
+ Modifier.fillMaxSize()
+ .nestedDraggable(draggable, orientation, enabled = enabled)
+ .scrollable(rememberScrollableState { 0f }, orientation)
+ )
+ }
+
+ rule.onRoot().performTouchInput { down(center) }
+
+ enabled = true
+ rule.waitForIdle()
+
+ rule.onRoot().performTouchInput { moveBy((touchSlop + 1f).toOffset()) }
+
+ assertThat(draggable.onDragStartedCalled).isFalse()
+ }
+
+ @Test
+ fun availableAndConsumedScrollDeltas() {
+ val totalScroll = 200f
+ val consumedByEffectPreScroll = 10f // 200f => 190f
+ val consumedByConnectionPreScroll = 20f // 190f => 170f
+ val consumedByScroll = 30f // 170f => 140f
+ val consumedByConnectionPostScroll = 40f // 140f => 100f
+
+ // Available scroll values that we will check later.
+ var availableToEffectPreScroll = 0f
+ var availableToConnectionPreScroll = 0f
+ var availableToScroll = 0f
+ var availableToConnectionPostScroll = 0f
+ var availableToEffectPostScroll = 0f
+
+ val effect =
+ TestOverscrollEffect(
+ orientation,
+ onPreScroll = {
+ availableToEffectPreScroll = it
+ consumedByEffectPreScroll
+ },
+ onPostScroll = {
+ availableToEffectPostScroll = it
+ it
+ },
+ )
+
+ val connection =
+ object : NestedScrollConnection {
+ override fun onPreScroll(available: Offset, source: NestedScrollSource): Offset {
+ availableToConnectionPreScroll = available.toFloat()
+ return consumedByConnectionPreScroll.toOffset()
+ }
+
+ override fun onPostScroll(
+ consumed: Offset,
+ available: Offset,
+ source: NestedScrollSource,
+ ): Offset {
+ assertThat(consumed.toFloat()).isEqualTo(consumedByScroll)
+ availableToConnectionPostScroll = available.toFloat()
+ return consumedByConnectionPostScroll.toOffset()
+ }
+ }
+
+ val draggable =
+ TestDraggable(
+ onDrag = {
+ availableToScroll = it
+ consumedByScroll
+ }
+ )
+
+ val touchSlop =
+ rule.setContentWithTouchSlop {
+ Box(
+ Modifier.fillMaxSize()
+ .nestedScroll(connection)
+ .nestedDraggable(draggable, orientation, effect)
+ )
+ }
+
+ rule.onRoot().performTouchInput {
+ down(center)
+ moveBy((touchSlop + totalScroll).toOffset())
+ }
+
+ assertThat(availableToEffectPreScroll).isEqualTo(200f)
+ assertThat(availableToConnectionPreScroll).isEqualTo(190f)
+ assertThat(availableToScroll).isEqualTo(170f)
+ assertThat(availableToConnectionPostScroll).isEqualTo(140f)
+ assertThat(availableToEffectPostScroll).isEqualTo(100f)
+ }
+
+ @Test
+ fun availableAndConsumedVelocities() {
+ val totalVelocity = 200f
+ val consumedByEffectPreFling = 10f // 200f => 190f
+ val consumedByConnectionPreFling = 20f // 190f => 170f
+ val consumedByFling = 30f // 170f => 140f
+ val consumedByConnectionPostFling = 40f // 140f => 100f
+
+ // Available velocities that we will check later.
+ var availableToEffectPreFling = 0f
+ var availableToConnectionPreFling = 0f
+ var availableToFling = 0f
+ var availableToConnectionPostFling = 0f
+ var availableToEffectPostFling = 0f
+
+ val effect =
+ TestOverscrollEffect(
+ orientation,
+ onPreFling = {
+ availableToEffectPreFling = it
+ consumedByEffectPreFling
+ },
+ onPostFling = {
+ availableToEffectPostFling = it
+ it
+ },
+ onPostScroll = { 0f },
+ )
+
+ val connection =
+ object : NestedScrollConnection {
+ override suspend fun onPreFling(available: Velocity): Velocity {
+ availableToConnectionPreFling = available.toFloat()
+ return consumedByConnectionPreFling.toVelocity()
+ }
+
+ override suspend fun onPostFling(
+ consumed: Velocity,
+ available: Velocity,
+ ): Velocity {
+ assertThat(consumed.toFloat()).isEqualTo(consumedByFling)
+ availableToConnectionPostFling = available.toFloat()
+ return consumedByConnectionPostFling.toVelocity()
+ }
+ }
+
+ val draggable =
+ TestDraggable(
+ onDragStopped = { velocity, _ ->
+ availableToFling = velocity
+ consumedByFling
+ },
+ onDrag = { 0f },
+ )
+
+ rule.setContent {
+ Box(
+ Modifier.fillMaxSize()
+ .nestedScroll(connection)
+ .nestedDraggable(draggable, orientation, effect)
+ )
+ }
+
+ rule.onRoot().performTouchInput {
+ when (orientation) {
+ Orientation.Horizontal -> swipeWithVelocity(topLeft, topRight, totalVelocity)
+ Orientation.Vertical -> swipeWithVelocity(topLeft, bottomLeft, totalVelocity)
+ }
+ }
+
+ assertThat(availableToEffectPreFling).isWithin(1f).of(200f)
+ assertThat(availableToConnectionPreFling).isWithin(1f).of(190f)
+ assertThat(availableToFling).isWithin(1f).of(170f)
+ assertThat(availableToConnectionPostFling).isWithin(1f).of(140f)
+ assertThat(availableToEffectPostFling).isWithin(1f).of(100f)
+ }
+
private fun ComposeContentTestRule.setContentWithTouchSlop(
content: @Composable () -> Unit
): Float {
diff --git a/packages/SystemUI/compose/core/tests/src/com/android/compose/gesture/TestOverscrollEffect.kt b/packages/SystemUI/compose/core/tests/src/com/android/compose/gesture/TestOverscrollEffect.kt
index 8bf9c21..0659f919 100644
--- a/packages/SystemUI/compose/core/tests/src/com/android/compose/gesture/TestOverscrollEffect.kt
+++ b/packages/SystemUI/compose/core/tests/src/com/android/compose/gesture/TestOverscrollEffect.kt
@@ -24,6 +24,8 @@
class TestOverscrollEffect(
override val orientation: Orientation,
+ private val onPreScroll: (Float) -> Float = { 0f },
+ private val onPreFling: suspend (Float) -> Float = { 0f },
private val onPostFling: suspend (Float) -> Float = { it },
private val onPostScroll: (Float) -> Float,
) : OverscrollEffect, OrientationAware {
@@ -36,19 +38,23 @@
source: NestedScrollSource,
performScroll: (Offset) -> Offset,
): Offset {
- val consumedByScroll = performScroll(delta)
- val available = delta - consumedByScroll
- val consumedByEffect = onPostScroll(available.toFloat()).toOffset()
- return consumedByScroll + consumedByEffect
+ val consumedByPreScroll = onPreScroll(delta.toFloat()).toOffset()
+ val availableToScroll = delta - consumedByPreScroll
+ val consumedByScroll = performScroll(availableToScroll)
+ val availableToPostScroll = availableToScroll - consumedByScroll
+ val consumedByPostScroll = onPostScroll(availableToPostScroll.toFloat()).toOffset()
+ return consumedByPreScroll + consumedByScroll + consumedByPostScroll
}
override suspend fun applyToFling(
velocity: Velocity,
performFling: suspend (Velocity) -> Velocity,
) {
- val consumedByFling = performFling(velocity)
- val available = velocity - consumedByFling
- onPostFling(available.toFloat())
+ val consumedByPreFling = onPreFling(velocity.toFloat()).toVelocity()
+ val availableToFling = velocity - consumedByPreFling
+ val consumedByFling = performFling(availableToFling)
+ val availableToPostFling = availableToFling - consumedByFling
+ onPostFling(availableToPostFling.toFloat())
applyToFlingDone = true
}
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/floatingmenu/DragToInteractAnimationControllerTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/floatingmenu/DragToInteractAnimationControllerTest.java
index 2665910..6edf949 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/floatingmenu/DragToInteractAnimationControllerTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/floatingmenu/DragToInteractAnimationControllerTest.java
@@ -65,7 +65,7 @@
@Before
public void setUp() throws Exception {
final WindowManager stubWindowManager = mContext.getSystemService(WindowManager.class);
- final SecureSettings mockSecureSettings = TestUtils.mockSecureSettings();
+ final SecureSettings mockSecureSettings = TestUtils.mockSecureSettings(mContext);
final MenuViewModel stubMenuViewModel = new MenuViewModel(mContext, mAccessibilityManager,
mockSecureSettings, mHearingAidDeviceManager);
final MenuViewAppearance stubMenuViewAppearance = new MenuViewAppearance(mContext,
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/floatingmenu/MenuItemAccessibilityDelegateTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/floatingmenu/MenuItemAccessibilityDelegateTest.java
index 241da5f..15afd25 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/floatingmenu/MenuItemAccessibilityDelegateTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/floatingmenu/MenuItemAccessibilityDelegateTest.java
@@ -71,7 +71,7 @@
private AccessibilityManager mAccessibilityManager;
@Mock
private HearingAidDeviceManager mHearingAidDeviceManager;
- private final SecureSettings mSecureSettings = TestUtils.mockSecureSettings();
+ private final SecureSettings mSecureSettings = TestUtils.mockSecureSettings(mContext);
private RecyclerView mStubListView;
private MenuView mMenuView;
private MenuViewLayer mMenuViewLayer;
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/floatingmenu/MenuListViewTouchHandlerTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/floatingmenu/MenuListViewTouchHandlerTest.java
index 715c40a..56a97bb 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/floatingmenu/MenuListViewTouchHandlerTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/floatingmenu/MenuListViewTouchHandlerTest.java
@@ -89,7 +89,7 @@
@Before
public void setUp() throws Exception {
final WindowManager windowManager = mContext.getSystemService(WindowManager.class);
- final SecureSettings secureSettings = TestUtils.mockSecureSettings();
+ final SecureSettings secureSettings = TestUtils.mockSecureSettings(mContext);
final MenuViewModel stubMenuViewModel = new MenuViewModel(mContext, mAccessibilityManager,
secureSettings, mHearingAidDeviceManager);
final MenuViewAppearance stubMenuViewAppearance = new MenuViewAppearance(mContext,
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/floatingmenu/MenuViewTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/floatingmenu/MenuViewTest.java
index cb7c205..5ff7bd0 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/floatingmenu/MenuViewTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/floatingmenu/MenuViewTest.java
@@ -91,7 +91,7 @@
mSpyContext = spy(mContext);
doNothing().when(mSpyContext).startActivity(any());
- final SecureSettings secureSettings = TestUtils.mockSecureSettings();
+ final SecureSettings secureSettings = TestUtils.mockSecureSettings(mContext);
final MenuViewModel stubMenuViewModel = new MenuViewModel(mContext, mAccessibilityManager,
secureSettings, mHearingAidDeviceManager);
final WindowManager stubWindowManager = mContext.getSystemService(WindowManager.class);
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/utils/TestUtils.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/utils/TestUtils.java
index 8399fa8..aafb212 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/utils/TestUtils.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/utils/TestUtils.java
@@ -22,6 +22,7 @@
import static org.mockito.Mockito.when;
import android.content.ComponentName;
+import android.content.Context;
import android.os.SystemClock;
import android.os.UserHandle;
import android.provider.Settings;
@@ -76,8 +77,10 @@
* Returns a mock secure settings configured to return information needed for tests.
* Currently, this only includes button targets.
*/
- public static SecureSettings mockSecureSettings() {
+ public static SecureSettings mockSecureSettings(Context context) {
SecureSettings secureSettings = mock(SecureSettings.class);
+ when(secureSettings.getRealUserHandle(UserHandle.USER_CURRENT))
+ .thenReturn(context.getUserId());
final String targets = getShortcutTargets(
Set.of(TEST_COMPONENT_A, TEST_COMPONENT_B));
diff --git a/packages/SystemUI/res/layout/volume_dialog.xml b/packages/SystemUI/res/layout/volume_dialog.xml
index 58f2d3c..67f620f 100644
--- a/packages/SystemUI/res/layout/volume_dialog.xml
+++ b/packages/SystemUI/res/layout/volume_dialog.xml
@@ -19,6 +19,7 @@
android:id="@+id/volume_dialog_root"
android:layout_width="match_parent"
android:layout_height="match_parent"
+ android:alpha="0"
android:clipChildren="false"
app:layoutDescription="@xml/volume_dialog_scene">
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/FullscreenMagnificationController.java b/packages/SystemUI/src/com/android/systemui/accessibility/FullscreenMagnificationController.java
index 04afd86..caf043a 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/FullscreenMagnificationController.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/FullscreenMagnificationController.java
@@ -22,6 +22,8 @@
import android.animation.AnimatorListenerAdapter;
import android.animation.ObjectAnimator;
import android.animation.ValueAnimator;
+import android.annotation.IntDef;
+import android.annotation.Nullable;
import android.annotation.UiContext;
import android.content.ComponentCallbacks;
import android.content.Context;
@@ -44,7 +46,8 @@
import android.view.View;
import android.view.WindowManager;
import android.view.accessibility.AccessibilityManager;
-import android.view.animation.AccelerateDecelerateInterpolator;
+import android.view.animation.AccelerateInterpolator;
+import android.view.animation.DecelerateInterpolator;
import android.view.animation.Interpolator;
import androidx.annotation.NonNull;
@@ -57,12 +60,16 @@
import com.android.systemui.res.R;
import com.android.systemui.util.leak.RotationUtils;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
import java.util.concurrent.Executor;
import java.util.function.Supplier;
public class FullscreenMagnificationController implements ComponentCallbacks {
- private static final String TAG = "FullscreenMagnificationController";
+ private static final String TAG = "FullscreenMagController";
+ private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
+
private final Context mContext;
private final AccessibilityManager mAccessibilityManager;
private final WindowManager mWindowManager;
@@ -77,12 +84,14 @@
private int mBorderStoke;
private final int mDisplayId;
private static final Region sEmptyRegion = new Region();
- private ValueAnimator mShowHideBorderAnimator;
+ @VisibleForTesting
+ @Nullable
+ ValueAnimator mShowHideBorderAnimator;
private Handler mHandler;
private Executor mExecutor;
- private boolean mFullscreenMagnificationActivated = false;
private final Configuration mConfiguration;
- private final Runnable mShowBorderRunnable = this::showBorderWithNullCheck;
+ private final Runnable mHideBorderImmediatelyRunnable = this::hideBorderImmediately;
+ private final Runnable mShowBorderRunnable = this::showBorder;
private int mRotation;
private final IRotationWatcher mRotationWatcher = new IRotationWatcher.Stub() {
@Override
@@ -95,6 +104,21 @@
private final DisplayManager.DisplayListener mDisplayListener;
private String mCurrentDisplayUniqueId;
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef({
+ DISABLED,
+ DISABLING,
+ ENABLING,
+ ENABLED
+ })
+ @interface FullscreenMagnificationActivationState {}
+ private static final int DISABLED = 0;
+ private static final int DISABLING = 1;
+ private static final int ENABLING = 2;
+ private static final int ENABLED = 3;
+ @FullscreenMagnificationActivationState
+ private int mActivationState = DISABLED;
+
public FullscreenMagnificationController(
@UiContext Context context,
@Main Handler handler,
@@ -106,7 +130,7 @@
Supplier<SurfaceControlViewHost> scvhSupplier) {
this(context, handler, executor, displayManager, accessibilityManager,
windowManager, iWindowManager, scvhSupplier,
- new SurfaceControl.Transaction(), null);
+ new SurfaceControl.Transaction());
}
@VisibleForTesting
@@ -119,8 +143,7 @@
WindowManager windowManager,
IWindowManager iWindowManager,
Supplier<SurfaceControlViewHost> scvhSupplier,
- SurfaceControl.Transaction transaction,
- ValueAnimator valueAnimator) {
+ SurfaceControl.Transaction transaction) {
mContext = context;
mHandler = handler;
mExecutor = executor;
@@ -135,18 +158,6 @@
mConfiguration = new Configuration(context.getResources().getConfiguration());
mLongAnimationTimeMs = mContext.getResources().getInteger(
com.android.internal.R.integer.config_longAnimTime);
- mShowHideBorderAnimator = (valueAnimator == null)
- ? createNullTargetObjectAnimator() : valueAnimator;
- mShowHideBorderAnimator.addListener(new AnimatorListenerAdapter() {
- @Override
- public void onAnimationEnd(@NonNull Animator animation, boolean isReverse) {
- if (isReverse) {
- // The animation was played in reverse, which means we are hiding the border.
- // We would like to perform clean up after the border is fully hidden.
- cleanUpBorder();
- }
- }
- });
mCurrentDisplayUniqueId = mContext.getDisplayNoVerify().getUniqueId();
mDisplayManager = displayManager;
mDisplayListener = new DisplayManager.DisplayListener() {
@@ -167,20 +178,51 @@
// Same unique ID means the physical display doesn't change. Early return.
return;
}
-
mCurrentDisplayUniqueId = uniqueId;
- applyCornerRadiusToBorder();
+ mHandler.post(FullscreenMagnificationController.this::applyCornerRadiusToBorder);
}
};
}
- private ValueAnimator createNullTargetObjectAnimator() {
+ @VisibleForTesting
+ @UiThread
+ ValueAnimator createShowTargetAnimator(@NonNull View target) {
+ if (mShowHideBorderAnimator != null) {
+ mShowHideBorderAnimator.cancel();
+ }
+
final ValueAnimator valueAnimator =
- ObjectAnimator.ofFloat(/* target= */ null, View.ALPHA, 0f, 1f);
- Interpolator interpolator = new AccelerateDecelerateInterpolator();
+ ObjectAnimator.ofFloat(target, View.ALPHA, 0f, 1f);
+ Interpolator interpolator = new AccelerateInterpolator();
valueAnimator.setInterpolator(interpolator);
valueAnimator.setDuration(mLongAnimationTimeMs);
+ valueAnimator.addListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(@NonNull Animator animation) {
+ mHandler.post(() -> setState(ENABLED));
+ }});
+ return valueAnimator;
+ }
+
+ @VisibleForTesting
+ @UiThread
+ ValueAnimator createHideTargetAnimator(@NonNull View target) {
+ if (mShowHideBorderAnimator != null) {
+ mShowHideBorderAnimator.cancel();
+ }
+
+ final ValueAnimator valueAnimator =
+ ObjectAnimator.ofFloat(target, View.ALPHA, 1f, 0f);
+ Interpolator interpolator = new DecelerateInterpolator();
+
+ valueAnimator.setInterpolator(interpolator);
+ valueAnimator.setDuration(mLongAnimationTimeMs);
+ valueAnimator.addListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(@NonNull Animator animation) {
+ mHandler.post(() -> cleanUpBorder());
+ }});
return valueAnimator;
}
@@ -190,14 +232,10 @@
*/
@UiThread
public void onFullscreenMagnificationActivationChanged(boolean activated) {
- final boolean changed = (mFullscreenMagnificationActivated != activated);
- if (changed) {
- mFullscreenMagnificationActivated = activated;
- if (activated) {
- createFullscreenMagnificationBorder();
- } else {
- removeFullscreenMagnificationBorder();
- }
+ if (activated) {
+ createFullscreenMagnificationBorder();
+ } else {
+ removeFullscreenMagnificationBorder();
}
}
@@ -207,16 +245,21 @@
*/
@UiThread
private void removeFullscreenMagnificationBorder() {
- if (mHandler.hasCallbacks(mShowBorderRunnable)) {
- mHandler.removeCallbacks(mShowBorderRunnable);
+ int state = getState();
+ if (state == DISABLING || state == DISABLED) {
+ // If there is an ongoing disable process or it is already disabled, return
+ return;
}
- mContext.unregisterComponentCallbacks(this);
-
-
- mShowHideBorderAnimator.reverse();
+ setState(DISABLING);
+ mShowHideBorderAnimator = createHideTargetAnimator(mFullscreenBorder);
+ mShowHideBorderAnimator.start();
}
- private void cleanUpBorder() {
+ @VisibleForTesting
+ @UiThread
+ void cleanUpBorder() {
+ mContext.unregisterComponentCallbacks(this);
+
if (Flags.updateCornerRadiusOnDisplayChanged()) {
mDisplayManager.unregisterDisplayListener(mDisplayListener);
}
@@ -227,6 +270,12 @@
}
if (mFullscreenBorder != null) {
+ if (mHandler.hasCallbacks(mHideBorderImmediatelyRunnable)) {
+ mHandler.removeCallbacks(mHideBorderImmediatelyRunnable);
+ }
+ if (mHandler.hasCallbacks(mShowBorderRunnable)) {
+ mHandler.removeCallbacks(mShowBorderRunnable);
+ }
mFullscreenBorder = null;
try {
mIWindowManager.removeRotationWatcher(mRotationWatcher);
@@ -234,6 +283,7 @@
Log.w(TAG, "Failed to remove rotation watcher", e);
}
}
+ setState(DISABLED);
}
/**
@@ -242,44 +292,47 @@
*/
@UiThread
private void createFullscreenMagnificationBorder() {
+ int state = getState();
+ if (state == ENABLING || state == ENABLED) {
+ // If there is an ongoing enable process or it is already enabled, return
+ return;
+ }
+ if (mShowHideBorderAnimator != null) {
+ mShowHideBorderAnimator.cancel();
+ }
+ setState(ENABLING);
+
onConfigurationChanged(mContext.getResources().getConfiguration());
mContext.registerComponentCallbacks(this);
if (mSurfaceControlViewHost == null) {
- // Create the view only if it does not exist yet. If we are trying to enable fullscreen
- // magnification before it was fully disabled, we use the previous view instead of
- // creating a new one.
+ // Create the view only if it does not exist yet. If we are trying to enable
+ // fullscreen magnification before it was fully disabled, we use the previous view
+ // instead of creating a new one.
mFullscreenBorder = LayoutInflater.from(mContext)
.inflate(R.layout.fullscreen_magnification_border, null);
- // Set the initial border view alpha manually so we won't show the border accidentally
- // after we apply show() to the SurfaceControl and before the animation starts to run.
+ // Set the initial border view alpha manually so we won't show the border
+ // accidentally after we apply show() to the SurfaceControl and before the
+ // animation starts to run.
mFullscreenBorder.setAlpha(0f);
- mShowHideBorderAnimator.setTarget(mFullscreenBorder);
mSurfaceControlViewHost = mScvhSupplier.get();
mSurfaceControlViewHost.setView(mFullscreenBorder, getBorderLayoutParams());
- mBorderSurfaceControl = mSurfaceControlViewHost.getSurfacePackage().getSurfaceControl();
+ mBorderSurfaceControl =
+ mSurfaceControlViewHost.getSurfacePackage().getSurfaceControl();
try {
mIWindowManager.watchRotation(mRotationWatcher, Display.DEFAULT_DISPLAY);
} catch (Exception e) {
Log.w(TAG, "Failed to register rotation watcher", e);
}
if (Flags.updateCornerRadiusOnDisplayChanged()) {
- mHandler.post(this::applyCornerRadiusToBorder);
+ applyCornerRadiusToBorder();
}
}
mTransaction
.addTransactionCommittedListener(
mExecutor,
- () -> {
- if (mShowHideBorderAnimator.isRunning()) {
- // Since the method is only called when there is an activation
- // status change, the running animator is hiding the border.
- mShowHideBorderAnimator.reverse();
- } else {
- mShowHideBorderAnimator.start();
- }
- })
+ this::showBorder)
.setPosition(mBorderSurfaceControl, -mBorderOffset, -mBorderOffset)
.setLayer(mBorderSurfaceControl, Integer.MAX_VALUE)
.show(mBorderSurfaceControl)
@@ -380,19 +433,25 @@
mHandler.removeCallbacks(mShowBorderRunnable);
}
- // We hide the border immediately as early as possible to beat the redrawing of window
- // in response to the orientation change so users won't see a weird shape border.
- mHandler.postAtFrontOfQueue(() -> {
- mFullscreenBorder.setAlpha(0f);
- });
-
+ // We hide the border immediately as early as possible to beat the redrawing of
+ // window in response to the orientation change so users won't see a weird shape
+ // border.
+ mHandler.postAtFrontOfQueue(mHideBorderImmediatelyRunnable);
mHandler.postDelayed(mShowBorderRunnable, mLongAnimationTimeMs);
}
- private void showBorderWithNullCheck() {
+ @UiThread
+ private void hideBorderImmediately() {
if (mShowHideBorderAnimator != null) {
- mShowHideBorderAnimator.start();
+ mShowHideBorderAnimator.cancel();
}
+ mFullscreenBorder.setAlpha(0f);
+ }
+
+ @UiThread
+ private void showBorder() {
+ mShowHideBorderAnimator = createShowTargetAnimator(mFullscreenBorder);
+ mShowHideBorderAnimator.start();
}
private void updateDimensions() {
@@ -404,7 +463,9 @@
R.dimen.magnifier_border_width_fullscreen_with_offset);
}
- private void applyCornerRadiusToBorder() {
+ @UiThread
+ @VisibleForTesting
+ void applyCornerRadiusToBorder() {
if (!isActivated()) {
return;
}
@@ -422,6 +483,20 @@
backgroundDrawable.setCornerRadius(cornerRadius);
}
+ @UiThread
+ private void setState(@FullscreenMagnificationActivationState int state) {
+ if (DEBUG) {
+ Log.d(TAG, "setState from " + mActivationState + " to " + state);
+ }
+ mActivationState = state;
+ }
+
+ @VisibleForTesting
+ @UiThread
+ int getState() {
+ return mActivationState;
+ }
+
@Override
public void onLowMemory() {
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/AccessibilityFloatingMenuController.java b/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/AccessibilityFloatingMenuController.java
index 5f0acfa..67aa4ff 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/AccessibilityFloatingMenuController.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/AccessibilityFloatingMenuController.java
@@ -23,7 +23,6 @@
import android.content.Context;
import android.hardware.display.DisplayManager;
import android.os.Handler;
-import android.os.UserHandle;
import android.text.TextUtils;
import android.view.Display;
import android.view.WindowManager;
@@ -58,7 +57,7 @@
private final AccessibilityButtonTargetsObserver mAccessibilityButtonTargetsObserver;
private final KeyguardUpdateMonitor mKeyguardUpdateMonitor;
- private Context mContext;
+ private final Context mContext;
private final WindowManager mWindowManager;
private final ViewCaptureAwareWindowManager mViewCaptureAwareWindowManager;
private final DisplayManager mDisplayManager;
@@ -226,7 +225,6 @@
@Override
public void onUserInitializationComplete(int userId) {
mIsUserInInitialization = false;
- mContext = mContext.createContextAsUser(UserHandle.of(userId), /* flags= */ 0);
mBtnMode = mAccessibilityButtonModeObserver.getCurrentAccessibilityButtonMode();
mBtnTargets =
mAccessibilityButtonTargetsObserver.getCurrentAccessibilityButtonTargets();
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuInfoRepository.java b/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuInfoRepository.java
index 121b51f..a1cb036 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuInfoRepository.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuInfoRepository.java
@@ -79,6 +79,8 @@
private static final int DEFAULT_MIGRATION_TOOLTIP_VALUE_PROMPT = MigrationPrompt.DISABLED;
private final Context mContext;
+ // Pref always get the userId from the context to store SharedPreferences for the correct user
+ private final Context mCurrentUserContext;
private final Configuration mConfiguration;
private final AccessibilityManager mAccessibilityManager;
private final AccessibilityManager.AccessibilityServicesStateChangeListener
@@ -157,6 +159,9 @@
OnContentsChanged settingsContentsChanged, SecureSettings secureSettings,
@Nullable HearingAidDeviceManager hearingAidDeviceManager) {
mContext = context;
+ final int currentUserId = secureSettings.getRealUserHandle(UserHandle.USER_CURRENT);
+ mCurrentUserContext = context.createContextAsUser(
+ UserHandle.of(currentUserId), /* flags= */ 0);
mAccessibilityManager = accessibilityManager;
mConfiguration = new Configuration(context.getResources().getConfiguration());
mSettingsContentsCallback = settingsContentsChanged;
@@ -168,12 +173,13 @@
void loadMenuMoveToTucked(OnInfoReady<Boolean> callback) {
callback.onReady(
- Prefs.getBoolean(mContext, Prefs.Key.HAS_ACCESSIBILITY_FLOATING_MENU_TUCKED,
+ Prefs.getBoolean(
+ mCurrentUserContext, Prefs.Key.HAS_ACCESSIBILITY_FLOATING_MENU_TUCKED,
DEFAULT_MOVE_TO_TUCKED_VALUE));
}
void loadDockTooltipVisibility(OnInfoReady<Boolean> callback) {
- callback.onReady(Prefs.getBoolean(mContext,
+ callback.onReady(Prefs.getBoolean(mCurrentUserContext,
Prefs.Key.HAS_SEEN_ACCESSIBILITY_FLOATING_MENU_DOCK_TOOLTIP,
DEFAULT_HAS_SEEN_DOCK_TOOLTIP_VALUE));
}
@@ -215,19 +221,19 @@
}
void updateMoveToTucked(boolean isMoveToTucked) {
- Prefs.putBoolean(mContext, Prefs.Key.HAS_ACCESSIBILITY_FLOATING_MENU_TUCKED,
+ Prefs.putBoolean(mCurrentUserContext, Prefs.Key.HAS_ACCESSIBILITY_FLOATING_MENU_TUCKED,
isMoveToTucked);
}
void updateMenuSavingPosition(Position percentagePosition) {
mPercentagePosition = percentagePosition;
- Prefs.putString(mContext, Prefs.Key.ACCESSIBILITY_FLOATING_MENU_POSITION,
+ Prefs.putString(mCurrentUserContext, Prefs.Key.ACCESSIBILITY_FLOATING_MENU_POSITION,
percentagePosition.toString());
}
void updateDockTooltipVisibility(boolean hasSeen) {
- Prefs.putBoolean(mContext, Prefs.Key.HAS_SEEN_ACCESSIBILITY_FLOATING_MENU_DOCK_TOOLTIP,
- hasSeen);
+ Prefs.putBoolean(mCurrentUserContext,
+ Prefs.Key.HAS_SEEN_ACCESSIBILITY_FLOATING_MENU_DOCK_TOOLTIP, hasSeen);
}
void updateMigrationTooltipVisibility(boolean visible) {
@@ -243,7 +249,7 @@
}
private Position getStartPosition() {
- final String absolutePositionString = Prefs.getString(mContext,
+ final String absolutePositionString = Prefs.getString(mCurrentUserContext,
Prefs.Key.ACCESSIBILITY_FLOATING_MENU_POSITION, /* defaultValue= */ null);
final float defaultPositionXPercent =
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuViewLayerController.java b/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuViewLayerController.java
index 184518a..e7470a3 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuViewLayerController.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuViewLayerController.java
@@ -17,6 +17,7 @@
package com.android.systemui.accessibility.floatingmenu;
import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_EXCLUDE_FROM_SCREEN_MAGNIFICATION;
+import static android.view.WindowManager.LayoutParams.SYSTEM_FLAG_SHOW_FOR_ALL_USERS;
import android.content.Context;
import android.graphics.PixelFormat;
@@ -90,7 +91,8 @@
WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE,
PixelFormat.TRANSLUCENT);
params.receiveInsetsIgnoringZOrder = true;
- params.privateFlags |= PRIVATE_FLAG_EXCLUDE_FROM_SCREEN_MAGNIFICATION;
+ params.privateFlags |=
+ PRIVATE_FLAG_EXCLUDE_FROM_SCREEN_MAGNIFICATION | SYSTEM_FLAG_SHOW_FOR_ALL_USERS;
params.windowAnimations = android.R.style.Animation_Translucent;
// Insets are configured to allow the menu to display over navigation and system bars.
params.setFitInsetsTypes(0);
diff --git a/packages/SystemUI/src/com/android/systemui/volume/dialog/ui/binder/VolumeDialogViewBinder.kt b/packages/SystemUI/src/com/android/systemui/volume/dialog/ui/binder/VolumeDialogViewBinder.kt
index 46d7d5f..428dc6e 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/dialog/ui/binder/VolumeDialogViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/dialog/ui/binder/VolumeDialogViewBinder.kt
@@ -80,7 +80,6 @@
MutableStateFlow(WindowInsets.Builder().build())
// Root view of the Volume Dialog.
val root: MotionLayout = dialog.requireViewById(R.id.volume_dialog_root)
- root.alpha = 0f
animateVisibility(root, dialog, viewModel.dialogVisibilityModel)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/FullscreenMagnificationControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/FullscreenMagnificationControllerTest.java
index 9d9fb9c..6ad2128 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/FullscreenMagnificationControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/FullscreenMagnificationControllerTest.java
@@ -30,9 +30,6 @@
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
-import android.animation.Animator;
-import android.animation.AnimatorListenerAdapter;
-import android.animation.ObjectAnimator;
import android.animation.ValueAnimator;
import android.content.Context;
import android.content.pm.ActivityInfo;
@@ -51,11 +48,8 @@
import android.view.View;
import android.view.WindowManager;
import android.view.accessibility.AccessibilityManager;
-import android.view.animation.DecelerateInterpolator;
-import android.view.animation.Interpolator;
import android.window.InputTransferToken;
-import androidx.annotation.NonNull;
import androidx.test.filters.FlakyTest;
import androidx.test.filters.SmallTest;
@@ -80,19 +74,17 @@
@RunWith(AndroidTestingRunner.class)
@FlakyTest(bugId = 385115361)
public class FullscreenMagnificationControllerTest extends SysuiTestCase {
- private static final long ANIMATION_DURATION_MS = 100L;
private static final long WAIT_TIMEOUT_S = 5L * HW_TIMEOUT_MULTIPLIER;
- private static final long ANIMATION_TIMEOUT_MS =
- 5L * ANIMATION_DURATION_MS * HW_TIMEOUT_MULTIPLIER;
private static final String UNIQUE_DISPLAY_ID_PRIMARY = "000";
private static final String UNIQUE_DISPLAY_ID_SECONDARY = "111";
private static final int CORNER_RADIUS_PRIMARY = 10;
private static final int CORNER_RADIUS_SECONDARY = 20;
+ private static final int DISABLED = 0;
+ private static final int ENABLED = 3;
private FullscreenMagnificationController mFullscreenMagnificationController;
private SurfaceControlViewHost mSurfaceControlViewHost;
- private ValueAnimator mShowHideBorderAnimator;
private SurfaceControl.Transaction mTransaction;
private TestableWindowManager mWindowManager;
@Mock
@@ -136,7 +128,6 @@
mContext.addMockSystemService(Context.WINDOW_SERVICE, mWindowManager);
mTransaction = new SurfaceControl.Transaction();
- mShowHideBorderAnimator = spy(newNullTargetObjectAnimator());
mFullscreenMagnificationController = new FullscreenMagnificationController(
mContext,
mContext.getMainThreadHandler(),
@@ -146,141 +137,68 @@
mContext.getSystemService(WindowManager.class),
mIWindowManager,
scvhSupplier,
- mTransaction,
- mShowHideBorderAnimator);
+ mTransaction);
}
@After
public void tearDown() {
- getInstrumentation().runOnMainSync(
- () -> mFullscreenMagnificationController
- .onFullscreenMagnificationActivationChanged(false));
+ getInstrumentation().runOnMainSync(() ->
+ mFullscreenMagnificationController.cleanUpBorder());
}
@Test
- public void enableFullscreenMagnification_visibleBorder()
+ public void createShowTargetAnimator_runAnimator_alphaIsEqualToOne() {
+ View view = new View(mContext);
+ view.setAlpha(0f);
+ ValueAnimator animator = mFullscreenMagnificationController.createShowTargetAnimator(view);
+ animator.end();
+ assertThat(view.getAlpha()).isEqualTo(1f);
+ }
+
+ @Test
+ public void createHideTargetAnimator_runAnimator_alphaIsEqualToZero() {
+ View view = new View(mContext);
+ view.setAlpha(1f);
+ ValueAnimator animator = mFullscreenMagnificationController.createHideTargetAnimator(view);
+ animator.end();
+ assertThat(view.getAlpha()).isEqualTo(0f);
+ }
+
+ @Test
+ public void enableFullscreenMagnification_stateEnabled()
throws InterruptedException, RemoteException {
- CountDownLatch transactionCommittedLatch = new CountDownLatch(1);
- CountDownLatch animationEndLatch = new CountDownLatch(1);
- mTransaction.addTransactionCommittedListener(
- Runnable::run, transactionCommittedLatch::countDown);
- mShowHideBorderAnimator.addListener(new AnimatorListenerAdapter() {
- @Override
- public void onAnimationEnd(Animator animation) {
- animationEndLatch.countDown();
- }
- });
- getInstrumentation().runOnMainSync(() ->
- //Enable fullscreen magnification
- mFullscreenMagnificationController
- .onFullscreenMagnificationActivationChanged(true));
- assertWithMessage("Failed to wait for transaction committed")
- .that(transactionCommittedLatch.await(WAIT_TIMEOUT_S, TimeUnit.SECONDS))
- .isTrue();
- assertWithMessage("Failed to wait for animation to be finished")
- .that(animationEndLatch.await(ANIMATION_TIMEOUT_MS, TimeUnit.MILLISECONDS))
- .isTrue();
- verify(mShowHideBorderAnimator).start();
+ enableFullscreenMagnificationAndWaitForTransactionAndAnimation();
+
+ assertThat(mFullscreenMagnificationController.getState()).isEqualTo(ENABLED);
verify(mIWindowManager)
.watchRotation(any(IRotationWatcher.class), eq(Display.DEFAULT_DISPLAY));
- assertThat(mSurfaceControlViewHost.getView().isVisibleToUser()).isTrue();
}
@Test
- public void disableFullscreenMagnification_reverseAnimationAndReleaseScvh()
+ public void disableFullscreenMagnification_stateDisabled()
throws InterruptedException, RemoteException {
- CountDownLatch transactionCommittedLatch = new CountDownLatch(1);
- CountDownLatch enableAnimationEndLatch = new CountDownLatch(1);
- CountDownLatch disableAnimationEndLatch = new CountDownLatch(1);
- mTransaction.addTransactionCommittedListener(
- Runnable::run, transactionCommittedLatch::countDown);
- mShowHideBorderAnimator.addListener(new AnimatorListenerAdapter() {
- @Override
- public void onAnimationEnd(@NonNull Animator animation, boolean isReverse) {
- if (isReverse) {
- disableAnimationEndLatch.countDown();
- } else {
- enableAnimationEndLatch.countDown();
- }
- }
+ enableFullscreenMagnificationAndWaitForTransactionAndAnimation();
+
+ getInstrumentation().runOnMainSync(() -> {
+ // Disable fullscreen magnification
+ mFullscreenMagnificationController
+ .onFullscreenMagnificationActivationChanged(false);
});
- getInstrumentation().runOnMainSync(() ->
- //Enable fullscreen magnification
- mFullscreenMagnificationController
- .onFullscreenMagnificationActivationChanged(true));
- assertWithMessage("Failed to wait for transaction committed")
- .that(transactionCommittedLatch.await(WAIT_TIMEOUT_S, TimeUnit.SECONDS))
- .isTrue();
- assertWithMessage("Failed to wait for enabling animation to be finished")
- .that(enableAnimationEndLatch.await(ANIMATION_TIMEOUT_MS, TimeUnit.MILLISECONDS))
- .isTrue();
- verify(mShowHideBorderAnimator).start();
+ waitForIdleSync();
+ assertThat(mFullscreenMagnificationController.mShowHideBorderAnimator).isNotNull();
+ mFullscreenMagnificationController.mShowHideBorderAnimator.end();
+ waitForIdleSync();
- getInstrumentation().runOnMainSync(() ->
- // Disable fullscreen magnification
- mFullscreenMagnificationController
- .onFullscreenMagnificationActivationChanged(false));
-
- assertWithMessage("Failed to wait for disabling animation to be finished")
- .that(disableAnimationEndLatch.await(ANIMATION_TIMEOUT_MS, TimeUnit.MILLISECONDS))
- .isTrue();
- verify(mShowHideBorderAnimator).reverse();
+ assertThat(mFullscreenMagnificationController.getState()).isEqualTo(DISABLED);
verify(mSurfaceControlViewHost).release();
verify(mIWindowManager).removeRotationWatcher(any(IRotationWatcher.class));
}
@Test
- public void onFullscreenMagnificationActivationChangeTrue_deactivating_reverseAnimator()
- throws InterruptedException {
- // Simulate the hiding border animation is running
- when(mShowHideBorderAnimator.isRunning()).thenReturn(true);
- CountDownLatch transactionCommittedLatch = new CountDownLatch(1);
- CountDownLatch animationEndLatch = new CountDownLatch(1);
- mTransaction.addTransactionCommittedListener(
- Runnable::run, transactionCommittedLatch::countDown);
- mShowHideBorderAnimator.addListener(new AnimatorListenerAdapter() {
- @Override
- public void onAnimationEnd(Animator animation) {
- animationEndLatch.countDown();
- }
- });
-
- getInstrumentation().runOnMainSync(
- () -> mFullscreenMagnificationController
- .onFullscreenMagnificationActivationChanged(true));
-
- assertWithMessage("Failed to wait for transaction committed")
- .that(transactionCommittedLatch.await(WAIT_TIMEOUT_S, TimeUnit.SECONDS))
- .isTrue();
- assertWithMessage("Failed to wait for animation to be finished")
- .that(animationEndLatch.await(ANIMATION_TIMEOUT_MS, TimeUnit.MILLISECONDS))
- .isTrue();
- verify(mShowHideBorderAnimator).reverse();
- }
-
- @Test
public void onScreenSizeChanged_activated_borderChangedToExpectedSize()
throws InterruptedException {
- CountDownLatch transactionCommittedLatch = new CountDownLatch(1);
- CountDownLatch animationEndLatch = new CountDownLatch(1);
- mTransaction.addTransactionCommittedListener(
- Runnable::run, transactionCommittedLatch::countDown);
- mShowHideBorderAnimator.addListener(new AnimatorListenerAdapter() {
- @Override
- public void onAnimationEnd(Animator animation) {
- animationEndLatch.countDown();
- }
- });
- getInstrumentation().runOnMainSync(() ->
- //Enable fullscreen magnification
- mFullscreenMagnificationController
- .onFullscreenMagnificationActivationChanged(true));
- assertWithMessage("Failed to wait for transaction committed")
- .that(transactionCommittedLatch.await(WAIT_TIMEOUT_S, TimeUnit.SECONDS))
- .isTrue();
- assertWithMessage("Failed to wait for animation to be finished")
- .that(animationEndLatch.await(ANIMATION_TIMEOUT_MS, TimeUnit.MILLISECONDS))
- .isTrue();
+ enableFullscreenMagnificationAndWaitForTransactionAndAnimation();
+
final Rect testWindowBounds = new Rect(
mWindowManager.getCurrentWindowMetrics().getBounds());
testWindowBounds.set(testWindowBounds.left, testWindowBounds.top,
@@ -304,29 +222,8 @@
@Test
public void enableFullscreenMagnification_applyPrimaryCornerRadius()
throws InterruptedException {
- CountDownLatch transactionCommittedLatch = new CountDownLatch(1);
- CountDownLatch animationEndLatch = new CountDownLatch(1);
- mTransaction.addTransactionCommittedListener(
- Runnable::run, transactionCommittedLatch::countDown);
- mShowHideBorderAnimator.addListener(new AnimatorListenerAdapter() {
- @Override
- public void onAnimationEnd(Animator animation) {
- animationEndLatch.countDown();
- }
- });
+ enableFullscreenMagnificationAndWaitForTransactionAndAnimation();
- getInstrumentation().runOnMainSync(() ->
- //Enable fullscreen magnification
- mFullscreenMagnificationController
- .onFullscreenMagnificationActivationChanged(true));
- assertWithMessage("Failed to wait for transaction committed")
- .that(transactionCommittedLatch.await(WAIT_TIMEOUT_S, TimeUnit.SECONDS))
- .isTrue();
- assertWithMessage("Failed to wait for animation to be finished")
- .that(animationEndLatch.await(ANIMATION_TIMEOUT_MS, TimeUnit.MILLISECONDS))
- .isTrue();
-
- // Verify the initial corner radius is applied
GradientDrawable backgroundDrawable =
(GradientDrawable) mSurfaceControlViewHost.getView().getBackground();
assertThat(backgroundDrawable.getCornerRadius()).isEqualTo(CORNER_RADIUS_PRIMARY);
@@ -334,28 +231,8 @@
@EnableFlags(Flags.FLAG_UPDATE_CORNER_RADIUS_ON_DISPLAY_CHANGED)
@Test
- public void onDisplayChanged_updateCornerRadiusToSecondary() throws InterruptedException {
- CountDownLatch transactionCommittedLatch = new CountDownLatch(1);
- CountDownLatch animationEndLatch = new CountDownLatch(1);
- mTransaction.addTransactionCommittedListener(
- Runnable::run, transactionCommittedLatch::countDown);
- mShowHideBorderAnimator.addListener(new AnimatorListenerAdapter() {
- @Override
- public void onAnimationEnd(Animator animation) {
- animationEndLatch.countDown();
- }
- });
-
- getInstrumentation().runOnMainSync(() ->
- //Enable fullscreen magnification
- mFullscreenMagnificationController
- .onFullscreenMagnificationActivationChanged(true));
- assertWithMessage("Failed to wait for transaction committed")
- .that(transactionCommittedLatch.await(WAIT_TIMEOUT_S, TimeUnit.SECONDS))
- .isTrue();
- assertWithMessage("Failed to wait for animation to be finished")
- .that(animationEndLatch.await(ANIMATION_TIMEOUT_MS, TimeUnit.MILLISECONDS))
- .isTrue();
+ public void onDisplayChanged_applyCornerRadiusToBorder() throws InterruptedException {
+ enableFullscreenMagnificationAndWaitForTransactionAndAnimation();
ArgumentCaptor<DisplayManager.DisplayListener> displayListenerCaptor =
ArgumentCaptor.forClass(DisplayManager.DisplayListener.class);
@@ -372,22 +249,34 @@
.addOverride(
com.android.internal.R.dimen.rounded_corner_radius,
CORNER_RADIUS_SECONDARY);
+
getInstrumentation().runOnMainSync(() ->
displayListenerCaptor.getValue().onDisplayChanged(Display.DEFAULT_DISPLAY));
waitForIdleSync();
+
// Verify the corner radius is updated
GradientDrawable backgroundDrawable2 =
(GradientDrawable) mSurfaceControlViewHost.getView().getBackground();
assertThat(backgroundDrawable2.getCornerRadius()).isEqualTo(CORNER_RADIUS_SECONDARY);
}
+ private void enableFullscreenMagnificationAndWaitForTransactionAndAnimation()
+ throws InterruptedException {
+ CountDownLatch transactionCommittedLatch = new CountDownLatch(1);
+ mTransaction.addTransactionCommittedListener(
+ Runnable::run, transactionCommittedLatch::countDown);
- private ValueAnimator newNullTargetObjectAnimator() {
- final ValueAnimator animator =
- ObjectAnimator.ofFloat(/* target= */ null, View.ALPHA, 0f, 1f);
- Interpolator interpolator = new DecelerateInterpolator(2.5f);
- animator.setInterpolator(interpolator);
- animator.setDuration(ANIMATION_DURATION_MS);
- return animator;
+ getInstrumentation().runOnMainSync(() ->
+ //Enable fullscreen magnification
+ mFullscreenMagnificationController
+ .onFullscreenMagnificationActivationChanged(true));
+
+ assertWithMessage("Failed to wait for transaction committed")
+ .that(transactionCommittedLatch.await(WAIT_TIMEOUT_S, TimeUnit.SECONDS))
+ .isTrue();
+ waitForIdleSync();
+ assertThat(mFullscreenMagnificationController.mShowHideBorderAnimator).isNotNull();
+ mFullscreenMagnificationController.mShowHideBorderAnimator.end();
+ waitForIdleSync();
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuAnimationControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuAnimationControllerTest.java
index 856c379..9f6ad56 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuAnimationControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuAnimationControllerTest.java
@@ -82,7 +82,7 @@
final WindowManager stubWindowManager = mContext.getSystemService(WindowManager.class);
final MenuViewAppearance stubMenuViewAppearance = new MenuViewAppearance(mContext,
stubWindowManager);
- final SecureSettings secureSettings = TestUtils.mockSecureSettings();
+ final SecureSettings secureSettings = TestUtils.mockSecureSettings(mContext);
final MenuViewModel stubMenuViewModel = new MenuViewModel(mContext, mAccessibilityManager,
secureSettings, mHearingAidDeviceManager);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuViewLayerTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuViewLayerTest.java
index 33cfb38..1500340 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuViewLayerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuViewLayerTest.java
@@ -144,7 +144,7 @@
private HearingAidDeviceManager mHearingAidDeviceManager;
@Mock
private PackageManager mMockPackageManager;
- private final SecureSettings mSecureSettings = TestUtils.mockSecureSettings();
+ private final SecureSettings mSecureSettings = TestUtils.mockSecureSettings(mContext);
private final NotificationManager mMockNotificationManager = mock(NotificationManager.class);
diff --git a/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java b/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java
index ef6f923..12c8f9c 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java
@@ -2538,7 +2538,7 @@
void wakeUp(int displayId, String reason) {
mPowerManager.wakeUp(SystemClock.uptimeMillis(), PowerManager.WAKE_REASON_APPLICATION,
- "android.server.am:TURN_ON:" + reason, displayId);
+ "android.server.wm:TURN_ON:" + reason, displayId);
}
/** Starts a batch of visibility updates. */
diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java
index 9c1cf6e..fe478c60 100644
--- a/services/core/java/com/android/server/wm/Task.java
+++ b/services/core/java/com/android/server/wm/Task.java
@@ -5230,9 +5230,15 @@
// to ensure any necessary pause logic occurs. In the case where the Activity will be
// shown regardless of the lock screen, the call to
// {@link ActivityTaskSupervisor#checkReadyForSleepLocked} is skipped.
- final ActivityRecord next = topRunningActivity(true /* focusableOnly */);
- if (next == null || !next.canTurnScreenOn()) {
- checkReadyForSleep();
+ if (shouldSleepActivities()) {
+ final ActivityRecord next = topRunningActivity(true /* focusableOnly */);
+ if (next != null && next.canTurnScreenOn()
+ && !mWmService.mPowerManager.isInteractive()) {
+ mTaskSupervisor.wakeUp(getDisplayId(), "resumeTop-turnScreenOnFlag");
+ next.setCurrentLaunchCanTurnScreenOn(false);
+ } else {
+ checkReadyForSleep();
+ }
}
} finally {
mInResumeTopActivity = false;