diff options
| author | 2022-05-03 14:13:57 +0000 | |
|---|---|---|
| committer | 2022-05-03 14:13:57 +0000 | |
| commit | 064b49a1a1cf9fdec2a2391336b61d6d6af04773 (patch) | |
| tree | ebce3fc8b89d76ff23fcba8434eb89a05d3f842f | |
| parent | 4f77430976e7ad8d7d803195225849d6dcefbf1d (diff) | |
| parent | 94f506aa2af9b4316326d94f089bf460c71041d6 (diff) | |
Merge "Add developer option for back animation DO NOT MERGE" into tm-dev
7 files changed, 151 insertions, 60 deletions
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java index 20a2bdf3b109..1ef1ac51544c 100644 --- a/core/java/android/provider/Settings.java +++ b/core/java/android/provider/Settings.java @@ -15040,6 +15040,14 @@ public final class Settings { */ public static final String DEVICE_CONFIG_SYNC_DISABLED = "device_config_sync_disabled"; + + /** + * Whether back preview animations are played when user does a back gesture or presses + * the back button. + * @hide + */ + public static final String ENABLE_BACK_ANIMATION = "enable_back_animation"; + /** @hide */ public static String zenModeToString(int mode) { if (mode == ZEN_MODE_IMPORTANT_INTERRUPTIONS) return "ZEN_MODE_IMPORTANT_INTERRUPTIONS"; if (mode == ZEN_MODE_ALARMS) return "ZEN_MODE_ALARMS"; diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationController.java index ced36a705df2..c3fbe5543630 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationController.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationController.java @@ -24,12 +24,18 @@ import android.annotation.Nullable; import android.app.ActivityTaskManager; import android.app.IActivityTaskManager; import android.app.WindowConfiguration; +import android.content.ContentResolver; import android.content.Context; +import android.database.ContentObserver; import android.graphics.Point; import android.graphics.PointF; import android.hardware.HardwareBuffer; +import android.net.Uri; +import android.os.Handler; import android.os.RemoteException; import android.os.SystemProperties; +import android.os.UserHandle; +import android.provider.Settings.Global; import android.util.Log; import android.view.MotionEvent; import android.view.RemoteAnimationTarget; @@ -42,22 +48,27 @@ import com.android.internal.annotations.VisibleForTesting; import com.android.internal.protolog.common.ProtoLog; import com.android.wm.shell.common.RemoteCallable; import com.android.wm.shell.common.ShellExecutor; +import com.android.wm.shell.common.annotations.ShellBackgroundThread; import com.android.wm.shell.common.annotations.ShellMainThread; +import java.util.concurrent.atomic.AtomicBoolean; + /** * Controls the window animation run when a user initiates a back gesture. */ public class BackAnimationController implements RemoteCallable<BackAnimationController> { private static final String TAG = "BackAnimationController"; + private static final int SETTING_VALUE_OFF = 0; + private static final int SETTING_VALUE_ON = 1; private static final String PREDICTIVE_BACK_PROGRESS_THRESHOLD_PROP = "persist.wm.debug.predictive_back_progress_threshold"; public static final boolean IS_ENABLED = - SystemProperties.getInt("persist.wm.debug.predictive_back", 1) != 0; + SystemProperties.getInt("persist.wm.debug.predictive_back", + SETTING_VALUE_ON) != SETTING_VALUE_OFF; private static final int PROGRESS_THRESHOLD = SystemProperties .getInt(PREDICTIVE_BACK_PROGRESS_THRESHOLD_PROP, -1); - @VisibleForTesting - boolean mEnableAnimations = SystemProperties.getInt( - "persist.wm.debug.predictive_back_anim", 0) != 0; + + private final AtomicBoolean mEnableAnimations = new AtomicBoolean(false); /** * Location of the initial touch event of the back gesture. @@ -87,21 +98,50 @@ public class BackAnimationController implements RemoteCallable<BackAnimationCont private float mProgressThreshold; public BackAnimationController( - @ShellMainThread ShellExecutor shellExecutor, + @NonNull @ShellMainThread ShellExecutor shellExecutor, + @NonNull @ShellBackgroundThread Handler backgroundHandler, Context context) { - this(shellExecutor, new SurfaceControl.Transaction(), ActivityTaskManager.getService(), - context); + this(shellExecutor, backgroundHandler, new SurfaceControl.Transaction(), + ActivityTaskManager.getService(), context, context.getContentResolver()); } @VisibleForTesting - BackAnimationController(@NonNull ShellExecutor shellExecutor, + BackAnimationController(@NonNull @ShellMainThread ShellExecutor shellExecutor, + @NonNull @ShellBackgroundThread Handler handler, @NonNull SurfaceControl.Transaction transaction, @NonNull IActivityTaskManager activityTaskManager, - Context context) { + Context context, ContentResolver contentResolver) { mShellExecutor = shellExecutor; mTransaction = transaction; mActivityTaskManager = activityTaskManager; mContext = context; + setupAnimationDeveloperSettingsObserver(contentResolver, handler); + } + + private void setupAnimationDeveloperSettingsObserver( + @NonNull ContentResolver contentResolver, + @NonNull @ShellBackgroundThread final Handler backgroundHandler) { + ContentObserver settingsObserver = new ContentObserver(backgroundHandler) { + @Override + public void onChange(boolean selfChange, Uri uri) { + updateEnableAnimationFromSetting(); + } + }; + contentResolver.registerContentObserver( + Global.getUriFor(Global.ENABLE_BACK_ANIMATION), + false, settingsObserver, UserHandle.USER_SYSTEM + ); + updateEnableAnimationFromSetting(); + } + + @ShellBackgroundThread + private void updateEnableAnimationFromSetting() { + int settingValue = Global.getInt(mContext.getContentResolver(), + Global.ENABLE_BACK_ANIMATION, SETTING_VALUE_OFF); + boolean isEnabled = settingValue == SETTING_VALUE_ON; + mEnableAnimations.set(isEnabled); + ProtoLog.d(WM_SHELL_BACK_PREVIEW, "Back animation enabled=%s", + isEnabled); } public BackAnimation getBackAnimationImpl() { @@ -340,12 +380,7 @@ public class BackAnimationController implements RemoteCallable<BackAnimationCont private boolean shouldDispatchToLauncher(int backType) { return backType == BackNavigationInfo.TYPE_RETURN_TO_HOME && mBackToLauncherCallback != null - && mEnableAnimations; - } - - @VisibleForTesting - void setEnableAnimations(boolean shouldEnable) { - mEnableAnimations = shouldEnable; + && mEnableAnimations.get(); } private static void dispatchOnBackStarted(IOnBackInvokedCallback callback) { 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 4ad08688bd51..333567320312 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 @@ -55,6 +55,7 @@ import com.android.wm.shell.common.SystemWindows; import com.android.wm.shell.common.TaskStackListenerImpl; import com.android.wm.shell.common.TransactionPool; import com.android.wm.shell.common.annotations.ShellAnimationThread; +import com.android.wm.shell.common.annotations.ShellBackgroundThread; import com.android.wm.shell.common.annotations.ShellMainThread; import com.android.wm.shell.common.annotations.ShellSplashscreenThread; import com.android.wm.shell.compatui.CompatUI; @@ -734,11 +735,12 @@ public abstract class WMShellBaseModule { @Provides static Optional<BackAnimationController> provideBackAnimationController( Context context, - @ShellMainThread ShellExecutor shellExecutor + @ShellMainThread ShellExecutor shellExecutor, + @ShellBackgroundThread Handler backgroundHandler ) { if (BackAnimationController.IS_ENABLED) { return Optional.of( - new BackAnimationController(shellExecutor, context)); + new BackAnimationController(shellExecutor, backgroundHandler, context)); } return Optional.empty(); } diff --git a/libs/WindowManager/Shell/tests/unittest/Android.bp b/libs/WindowManager/Shell/tests/unittest/Android.bp index fb53e5355c4a..a899709c1405 100644 --- a/libs/WindowManager/Shell/tests/unittest/Android.bp +++ b/libs/WindowManager/Shell/tests/unittest/Android.bp @@ -43,6 +43,7 @@ android_test { "truth-prebuilt", "testables", "platform-test-annotations", + "frameworks-base-testutils", ], libs: [ diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/BackAnimationControllerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/BackAnimationControllerTest.java index a905dcaebc6b..6cf88297f3b2 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/BackAnimationControllerTest.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/BackAnimationControllerTest.java @@ -26,17 +26,23 @@ import static org.mockito.Mockito.atLeastOnce; import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; +import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import android.app.IActivityTaskManager; import android.app.WindowConfiguration; -import android.content.Context; +import android.content.pm.ApplicationInfo; import android.graphics.Point; import android.graphics.Rect; import android.hardware.HardwareBuffer; +import android.os.Handler; import android.os.RemoteCallback; import android.os.RemoteException; +import android.provider.Settings; import android.testing.AndroidTestingRunner; +import android.testing.TestableContentResolver; +import android.testing.TestableContext; +import android.testing.TestableLooper; import android.view.MotionEvent; import android.view.RemoteAnimationTarget; import android.view.SurfaceControl; @@ -45,12 +51,14 @@ import android.window.BackNavigationInfo; import android.window.IOnBackInvokedCallback; import androidx.test.filters.SmallTest; +import androidx.test.platform.app.InstrumentationRegistry; +import com.android.internal.util.test.FakeSettingsProvider; import com.android.wm.shell.TestShellExecutor; -import com.android.wm.shell.common.ShellExecutor; import org.junit.Before; import org.junit.Ignore; +import org.junit.Rule; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.ArgumentCaptor; @@ -60,14 +68,17 @@ import org.mockito.MockitoAnnotations; /** * atest WMShellUnitTests:BackAnimationControllerTest */ +@TestableLooper.RunWithLooper @SmallTest @RunWith(AndroidTestingRunner.class) public class BackAnimationControllerTest { - private final ShellExecutor mShellExecutor = new TestShellExecutor(); + private static final String ANIMATION_ENABLED = "1"; + private final TestShellExecutor mShellExecutor = new TestShellExecutor(); - @Mock - private Context mContext; + @Rule + public TestableContext mContext = + new TestableContext(InstrumentationRegistry.getInstrumentation().getContext()); @Mock private SurfaceControl.Transaction mTransaction; @@ -80,18 +91,32 @@ public class BackAnimationControllerTest { private BackAnimationController mController; + private int mEventTime = 0; + private TestableContentResolver mContentResolver; + private TestableLooper mTestableLooper; + @Before public void setUp() throws Exception { MockitoAnnotations.initMocks(this); + mContext.getApplicationInfo().privateFlags |= ApplicationInfo.PRIVATE_FLAG_PRIVILEGED; + mContentResolver = new TestableContentResolver(mContext); + mContentResolver.addProvider(Settings.AUTHORITY, new FakeSettingsProvider()); + Settings.Global.putString(mContentResolver, Settings.Global.ENABLE_BACK_ANIMATION, + ANIMATION_ENABLED); + mTestableLooper = TestableLooper.get(this); mController = new BackAnimationController( - mShellExecutor, mTransaction, mActivityTaskManager, mContext); - mController.setEnableAnimations(true); + mShellExecutor, new Handler(mTestableLooper.getLooper()), mTransaction, + mActivityTaskManager, mContext, + mContentResolver); + mEventTime = 0; + mShellExecutor.flushAll(); } private void createNavigationInfo(RemoteAnimationTarget topAnimationTarget, SurfaceControl screenshotSurface, HardwareBuffer hardwareBuffer, - int backType) { + int backType, + IOnBackInvokedCallback onBackInvokedCallback) { BackNavigationInfo navigationInfo = new BackNavigationInfo( backType, topAnimationTarget, @@ -99,7 +124,7 @@ public class BackAnimationControllerTest { hardwareBuffer, new WindowConfiguration(), new RemoteCallback((bundle) -> {}), - null); + onBackInvokedCallback); try { doReturn(navigationInfo).when(mActivityTaskManager).startBackNavigation(); } catch (RemoteException ex) { @@ -124,15 +149,10 @@ public class BackAnimationControllerTest { } private void triggerBackGesture() { - MotionEvent event = MotionEvent.obtain(0, 0, MotionEvent.ACTION_DOWN, 0, 0, 0); - mController.onMotionEvent(event, event.getAction(), BackEvent.EDGE_LEFT); - - event = MotionEvent.obtain(10, 0, MotionEvent.ACTION_MOVE, 100, 100, 0); - mController.onMotionEvent(event, event.getAction(), BackEvent.EDGE_LEFT); - + doMotionEvent(MotionEvent.ACTION_DOWN, 0); + doMotionEvent(MotionEvent.ACTION_MOVE, 0); mController.setTriggerBack(true); - event = MotionEvent.obtain(10, 0, MotionEvent.ACTION_UP, 100, 100, 0); - mController.onMotionEvent(event, event.getAction(), BackEvent.EDGE_LEFT); + doMotionEvent(MotionEvent.ACTION_UP, 0); } @Test @@ -141,11 +161,8 @@ public class BackAnimationControllerTest { SurfaceControl screenshotSurface = new SurfaceControl(); HardwareBuffer hardwareBuffer = mock(HardwareBuffer.class); createNavigationInfo(createAnimationTarget(), screenshotSurface, hardwareBuffer, - BackNavigationInfo.TYPE_CROSS_ACTIVITY); - mController.onMotionEvent( - MotionEvent.obtain(0, 0, MotionEvent.ACTION_DOWN, 0, 0, 0), - MotionEvent.ACTION_DOWN, - BackEvent.EDGE_LEFT); + BackNavigationInfo.TYPE_CROSS_ACTIVITY, null); + doMotionEvent(MotionEvent.ACTION_DOWN, 0); verify(mTransaction).setBuffer(screenshotSurface, hardwareBuffer); verify(mTransaction).setVisibility(screenshotSurface, true); verify(mTransaction).apply(); @@ -157,15 +174,9 @@ public class BackAnimationControllerTest { HardwareBuffer hardwareBuffer = mock(HardwareBuffer.class); RemoteAnimationTarget animationTarget = createAnimationTarget(); createNavigationInfo(animationTarget, screenshotSurface, hardwareBuffer, - BackNavigationInfo.TYPE_CROSS_ACTIVITY); - mController.onMotionEvent( - MotionEvent.obtain(0, 0, MotionEvent.ACTION_DOWN, 0, 0, 0), - MotionEvent.ACTION_DOWN, - BackEvent.EDGE_LEFT); - mController.onMotionEvent( - MotionEvent.obtain(10, 0, MotionEvent.ACTION_MOVE, 100, 100, 0), - MotionEvent.ACTION_MOVE, - BackEvent.EDGE_LEFT); + BackNavigationInfo.TYPE_CROSS_ACTIVITY, null); + doMotionEvent(MotionEvent.ACTION_DOWN, 0); + doMotionEvent(MotionEvent.ACTION_MOVE, 100); // b/207481538, we check that the surface is not moved for now, we can re-enable this once // we implement the animation verify(mTransaction, never()).setScale(eq(screenshotSurface), anyInt(), anyInt()); @@ -196,30 +207,56 @@ public class BackAnimationControllerTest { mController.setBackToLauncherCallback(mIOnBackInvokedCallback); RemoteAnimationTarget animationTarget = createAnimationTarget(); createNavigationInfo(animationTarget, null, null, - BackNavigationInfo.TYPE_RETURN_TO_HOME); + BackNavigationInfo.TYPE_RETURN_TO_HOME, null); // Check that back start is dispatched. - mController.onMotionEvent( - MotionEvent.obtain(0, 0, MotionEvent.ACTION_DOWN, 0, 0, 0), - MotionEvent.ACTION_DOWN, - BackEvent.EDGE_LEFT); + doMotionEvent(MotionEvent.ACTION_DOWN, 0); verify(mIOnBackInvokedCallback).onBackStarted(); // Check that back progress is dispatched. - mController.onMotionEvent( - MotionEvent.obtain(10, 0, MotionEvent.ACTION_MOVE, 100, 100, 0), - MotionEvent.ACTION_MOVE, - BackEvent.EDGE_LEFT); + doMotionEvent(MotionEvent.ACTION_MOVE, 100); ArgumentCaptor<BackEvent> backEventCaptor = ArgumentCaptor.forClass(BackEvent.class); verify(mIOnBackInvokedCallback).onBackProgressed(backEventCaptor.capture()); assertEquals(animationTarget, backEventCaptor.getValue().getDepartingAnimationTarget()); // Check that back invocation is dispatched. mController.setTriggerBack(true); // Fake trigger back + doMotionEvent(MotionEvent.ACTION_UP, 0); + verify(mIOnBackInvokedCallback).onBackInvoked(); + } + + @Test + public void animationDisabledFromSettings() throws RemoteException { + // Toggle the setting off + Settings.Global.putString(mContentResolver, Settings.Global.ENABLE_BACK_ANIMATION, "0"); + mController = new BackAnimationController( + mShellExecutor, new Handler(mTestableLooper.getLooper()), mTransaction, + mActivityTaskManager, mContext, + mContentResolver); + mController.setBackToLauncherCallback(mIOnBackInvokedCallback); + + RemoteAnimationTarget animationTarget = createAnimationTarget(); + IOnBackInvokedCallback appCallback = mock(IOnBackInvokedCallback.class); + ArgumentCaptor<BackEvent> backEventCaptor = ArgumentCaptor.forClass(BackEvent.class); + createNavigationInfo(animationTarget, null, null, + BackNavigationInfo.TYPE_RETURN_TO_HOME, appCallback); + + triggerBackGesture(); + + verify(appCallback, never()).onBackStarted(); + verify(appCallback, never()).onBackProgressed(backEventCaptor.capture()); + verify(appCallback, times(1)).onBackInvoked(); + + verify(mIOnBackInvokedCallback, never()).onBackStarted(); + verify(mIOnBackInvokedCallback, never()).onBackProgressed(backEventCaptor.capture()); + verify(mIOnBackInvokedCallback, never()).onBackInvoked(); + } + + private void doMotionEvent(int actionDown, int coordinate) { mController.onMotionEvent( - MotionEvent.obtain(0, 0, MotionEvent.ACTION_UP, 0, 0, 0), - MotionEvent.ACTION_UP, + MotionEvent.obtain(0, mEventTime, actionDown, coordinate, coordinate, 0), + actionDown, BackEvent.EDGE_LEFT); - verify(mIOnBackInvokedCallback).onBackInvoked(); + mEventTime += 10; } } diff --git a/packages/SettingsLib/res/values/strings.xml b/packages/SettingsLib/res/values/strings.xml index df2685db17f5..a171f86dd217 100644 --- a/packages/SettingsLib/res/values/strings.xml +++ b/packages/SettingsLib/res/values/strings.xml @@ -1604,4 +1604,11 @@ <string name="bt_le_audio_broadcast_dialog_switch_app">Broadcast <xliff:g id="switchApp" example="App Name 2">%1$s</xliff:g></string> <!-- [CHAR LIMIT=NONE] Le audio broadcast dialog, different output. --> <string name="bt_le_audio_broadcast_dialog_different_output">Change output</string> + + <!-- Developer setting: enable animations when a back gesture is executed [CHAR LIMIT=50] --> + <string name="back_navigation_animation">Predictive back animations</string> + <!-- Developer setting: enable animations when a back gesture is executed [CHAR LIMIT=150] --> + <string name="back_navigation_animation_summary">Enable system animations for predictive back.</string> + <!-- Developer setting: enable animations when a back gesture is executed, full explanation[CHAR LIMIT=NONE] --> + <string name="back_navigation_animation_dialog">This setting enables system animations for predictive gesture animation. It requires setting per-app "enableOnBackInvokedCallback" to true in the manifest file.</string> </resources> diff --git a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java index 4b7d0d2a3c74..cce515444c1f 100644 --- a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java +++ b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java @@ -598,6 +598,7 @@ public class SettingsBackupTest { Settings.Global.WATCHDOG_TIMEOUT_MILLIS, Settings.Global.MANAGED_PROVISIONING_DEFER_PROVISIONING_TO_ROLE_HOLDER, Settings.Global.REVIEW_PERMISSIONS_NOTIFICATION_STATE, + Settings.Global.ENABLE_BACK_ANIMATION, // Temporary for T, dev option only Settings.Global.Wearable.BATTERY_SAVER_MODE, Settings.Global.Wearable.COMBINED_LOCATION_ENABLED, Settings.Global.Wearable.HAS_PAY_TOKENS, |