Loading core/java/android/provider/Settings.java +8 −0 Original line number Diff line number Diff line Loading @@ -15190,6 +15190,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"; libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationController.java +51 −15 Original line number Diff line number Diff line Loading @@ -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; Loading @@ -42,28 +48,34 @@ 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; /** * Max duration to wait for a transition to finish before accepting another gesture start * request. */ private static final long MAX_TRANSITION_DURATION = 2000; private final AtomicBoolean mEnableAnimations = new AtomicBoolean(false); /** * Location of the initial touch event of the back gesture. */ Loading Loading @@ -100,21 +112,50 @@ public class BackAnimationController implements RemoteCallable<BackAnimationCont }; 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() { Loading Loading @@ -364,12 +405,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) { Loading libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java +4 −2 Original line number Diff line number Diff line Loading @@ -53,6 +53,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; Loading Loading @@ -695,11 +696,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(); } Loading libs/WindowManager/Shell/tests/unittest/Android.bp +1 −0 Original line number Diff line number Diff line Loading @@ -44,6 +44,7 @@ android_test { "truth-prebuilt", "testables", "platform-test-annotations", "frameworks-base-testutils", ], libs: [ Loading libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/BackAnimationControllerTest.java +83 −44 Original line number Diff line number Diff line Loading @@ -27,18 +27,24 @@ import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; import static org.mockito.Mockito.reset; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verifyNoMoreInteractions; 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; Loading @@ -47,11 +53,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 org.junit.Before; import org.junit.Ignore; import org.junit.Rule; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.ArgumentCaptor; Loading @@ -61,14 +70,18 @@ import org.mockito.MockitoAnnotations; /** * atest WMShellUnitTests:BackAnimationControllerTest */ @TestableLooper.RunWithLooper @SmallTest @RunWith(AndroidTestingRunner.class) public class BackAnimationControllerTest { 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; Loading @@ -81,18 +94,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, Loading @@ -100,7 +127,7 @@ public class BackAnimationControllerTest { hardwareBuffer, new WindowConfiguration(), new RemoteCallback((bundle) -> {}), null); onBackInvokedCallback); try { doReturn(navigationInfo).when(mActivityTaskManager).startBackNavigation(); } catch (RemoteException ex) { Loading @@ -125,15 +152,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 Loading @@ -142,11 +164,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(); Loading @@ -158,15 +177,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()); Loading Loading @@ -197,18 +210,12 @@ public class BackAnimationControllerTest { mController.setBackToLauncherCallback(mIOnBackInvokedCallback); RemoteAnimationTarget animationTarget = createAnimationTarget(); createNavigationInfo(animationTarget, null, null, BackNavigationInfo.TYPE_RETURN_TO_HOME); BackNavigationInfo.TYPE_RETURN_TO_HOME, null); mController.onMotionEvent( MotionEvent.obtain(0, 0, MotionEvent.ACTION_DOWN, 0, 0, 0), MotionEvent.ACTION_DOWN, BackEvent.EDGE_LEFT); doMotionEvent(MotionEvent.ACTION_DOWN, 0); // Check that back start and progress is dispatched when first move. mController.onMotionEvent( MotionEvent.obtain(10, 0, MotionEvent.ACTION_MOVE, 100, 100, 0), MotionEvent.ACTION_MOVE, BackEvent.EDGE_LEFT); doMotionEvent(MotionEvent.ACTION_MOVE, 100); verify(mIOnBackInvokedCallback).onBackStarted(); ArgumentCaptor<BackEvent> backEventCaptor = ArgumentCaptor.forClass(BackEvent.class); verify(mIOnBackInvokedCallback).onBackProgressed(backEventCaptor.capture()); Loading @@ -216,10 +223,7 @@ public class BackAnimationControllerTest { // Check that back invocation is dispatched. mController.setTriggerBack(true); // Fake trigger back mController.onMotionEvent( MotionEvent.obtain(0, 0, MotionEvent.ACTION_UP, 0, 0, 0), MotionEvent.ACTION_UP, BackEvent.EDGE_LEFT); doMotionEvent(MotionEvent.ACTION_UP, 0); verify(mIOnBackInvokedCallback).onBackInvoked(); } Loading @@ -228,7 +232,7 @@ public class BackAnimationControllerTest { mController.setBackToLauncherCallback(mIOnBackInvokedCallback); RemoteAnimationTarget animationTarget = createAnimationTarget(); createNavigationInfo(animationTarget, null, null, BackNavigationInfo.TYPE_RETURN_TO_HOME); BackNavigationInfo.TYPE_RETURN_TO_HOME, null); triggerBackGesture(); // Check that back invocation is dispatched. Loading Loading @@ -261,7 +265,7 @@ public class BackAnimationControllerTest { mController.setBackToLauncherCallback(mIOnBackInvokedCallback); RemoteAnimationTarget animationTarget = createAnimationTarget(); createNavigationInfo(animationTarget, null, null, BackNavigationInfo.TYPE_RETURN_TO_HOME); BackNavigationInfo.TYPE_RETURN_TO_HOME, null); triggerBackGesture(); reset(mIOnBackInvokedCallback); Loading @@ -278,4 +282,39 @@ public class BackAnimationControllerTest { BackEvent.EDGE_LEFT); verify(mIOnBackInvokedCallback).onBackStarted(); } @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, mEventTime, actionDown, coordinate, coordinate, 0), actionDown, BackEvent.EDGE_LEFT); mEventTime += 10; } } Loading
core/java/android/provider/Settings.java +8 −0 Original line number Diff line number Diff line Loading @@ -15190,6 +15190,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";
libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationController.java +51 −15 Original line number Diff line number Diff line Loading @@ -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; Loading @@ -42,28 +48,34 @@ 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; /** * Max duration to wait for a transition to finish before accepting another gesture start * request. */ private static final long MAX_TRANSITION_DURATION = 2000; private final AtomicBoolean mEnableAnimations = new AtomicBoolean(false); /** * Location of the initial touch event of the back gesture. */ Loading Loading @@ -100,21 +112,50 @@ public class BackAnimationController implements RemoteCallable<BackAnimationCont }; 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() { Loading Loading @@ -364,12 +405,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) { Loading
libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java +4 −2 Original line number Diff line number Diff line Loading @@ -53,6 +53,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; Loading Loading @@ -695,11 +696,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(); } Loading
libs/WindowManager/Shell/tests/unittest/Android.bp +1 −0 Original line number Diff line number Diff line Loading @@ -44,6 +44,7 @@ android_test { "truth-prebuilt", "testables", "platform-test-annotations", "frameworks-base-testutils", ], libs: [ Loading
libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/BackAnimationControllerTest.java +83 −44 Original line number Diff line number Diff line Loading @@ -27,18 +27,24 @@ import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; import static org.mockito.Mockito.reset; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verifyNoMoreInteractions; 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; Loading @@ -47,11 +53,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 org.junit.Before; import org.junit.Ignore; import org.junit.Rule; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.ArgumentCaptor; Loading @@ -61,14 +70,18 @@ import org.mockito.MockitoAnnotations; /** * atest WMShellUnitTests:BackAnimationControllerTest */ @TestableLooper.RunWithLooper @SmallTest @RunWith(AndroidTestingRunner.class) public class BackAnimationControllerTest { 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; Loading @@ -81,18 +94,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, Loading @@ -100,7 +127,7 @@ public class BackAnimationControllerTest { hardwareBuffer, new WindowConfiguration(), new RemoteCallback((bundle) -> {}), null); onBackInvokedCallback); try { doReturn(navigationInfo).when(mActivityTaskManager).startBackNavigation(); } catch (RemoteException ex) { Loading @@ -125,15 +152,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 Loading @@ -142,11 +164,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(); Loading @@ -158,15 +177,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()); Loading Loading @@ -197,18 +210,12 @@ public class BackAnimationControllerTest { mController.setBackToLauncherCallback(mIOnBackInvokedCallback); RemoteAnimationTarget animationTarget = createAnimationTarget(); createNavigationInfo(animationTarget, null, null, BackNavigationInfo.TYPE_RETURN_TO_HOME); BackNavigationInfo.TYPE_RETURN_TO_HOME, null); mController.onMotionEvent( MotionEvent.obtain(0, 0, MotionEvent.ACTION_DOWN, 0, 0, 0), MotionEvent.ACTION_DOWN, BackEvent.EDGE_LEFT); doMotionEvent(MotionEvent.ACTION_DOWN, 0); // Check that back start and progress is dispatched when first move. mController.onMotionEvent( MotionEvent.obtain(10, 0, MotionEvent.ACTION_MOVE, 100, 100, 0), MotionEvent.ACTION_MOVE, BackEvent.EDGE_LEFT); doMotionEvent(MotionEvent.ACTION_MOVE, 100); verify(mIOnBackInvokedCallback).onBackStarted(); ArgumentCaptor<BackEvent> backEventCaptor = ArgumentCaptor.forClass(BackEvent.class); verify(mIOnBackInvokedCallback).onBackProgressed(backEventCaptor.capture()); Loading @@ -216,10 +223,7 @@ public class BackAnimationControllerTest { // Check that back invocation is dispatched. mController.setTriggerBack(true); // Fake trigger back mController.onMotionEvent( MotionEvent.obtain(0, 0, MotionEvent.ACTION_UP, 0, 0, 0), MotionEvent.ACTION_UP, BackEvent.EDGE_LEFT); doMotionEvent(MotionEvent.ACTION_UP, 0); verify(mIOnBackInvokedCallback).onBackInvoked(); } Loading @@ -228,7 +232,7 @@ public class BackAnimationControllerTest { mController.setBackToLauncherCallback(mIOnBackInvokedCallback); RemoteAnimationTarget animationTarget = createAnimationTarget(); createNavigationInfo(animationTarget, null, null, BackNavigationInfo.TYPE_RETURN_TO_HOME); BackNavigationInfo.TYPE_RETURN_TO_HOME, null); triggerBackGesture(); // Check that back invocation is dispatched. Loading Loading @@ -261,7 +265,7 @@ public class BackAnimationControllerTest { mController.setBackToLauncherCallback(mIOnBackInvokedCallback); RemoteAnimationTarget animationTarget = createAnimationTarget(); createNavigationInfo(animationTarget, null, null, BackNavigationInfo.TYPE_RETURN_TO_HOME); BackNavigationInfo.TYPE_RETURN_TO_HOME, null); triggerBackGesture(); reset(mIOnBackInvokedCallback); Loading @@ -278,4 +282,39 @@ public class BackAnimationControllerTest { BackEvent.EDGE_LEFT); verify(mIOnBackInvokedCallback).onBackStarted(); } @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, mEventTime, actionDown, coordinate, coordinate, 0), actionDown, BackEvent.EDGE_LEFT); mEventTime += 10; } }