Merge changes I6e7c349d,If6c74b3b,Ib276e9dc,Ida0f7cc0 into main
* changes: Override DisplayController on main thread. Add annotations for manipulating secure settings. Don't use UiThreadTest for Taskbar Unit tests. Suspend Launcher taskbar while removed for tests.
This commit is contained in:
committed by
Android (Google) Code Review
commit
f7ca8d60be
@@ -43,6 +43,8 @@ import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_N
|
||||
import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_VOICE_INTERACTION_WINDOW_SHOWING;
|
||||
import static com.android.wm.shell.Flags.enableTinyTaskbar;
|
||||
|
||||
import static java.lang.invoke.MethodHandles.Lookup.PROTECTED;
|
||||
|
||||
import android.animation.AnimatorSet;
|
||||
import android.animation.ValueAnimator;
|
||||
import android.app.ActivityOptions;
|
||||
@@ -1515,7 +1517,8 @@ public class TaskbarActivityContext extends BaseTaskbarContext {
|
||||
return mIsNavBarKidsMode && isThreeButtonNav();
|
||||
}
|
||||
|
||||
protected boolean isNavBarForceVisible() {
|
||||
@VisibleForTesting(otherwise = PROTECTED)
|
||||
public boolean isNavBarForceVisible() {
|
||||
return mIsNavBarForceVisible;
|
||||
}
|
||||
|
||||
|
||||
@@ -115,6 +115,7 @@ public class TaskbarManager {
|
||||
private WindowManager mWindowManager;
|
||||
private FrameLayout mTaskbarRootLayout;
|
||||
private boolean mAddedWindow;
|
||||
private boolean mIsSuspended;
|
||||
private final TaskbarNavButtonController mNavButtonController;
|
||||
private final ComponentCallbacks mComponentCallbacks;
|
||||
|
||||
@@ -443,6 +444,8 @@ public class TaskbarManager {
|
||||
*/
|
||||
@VisibleForTesting
|
||||
public synchronized void recreateTaskbar() {
|
||||
if (mIsSuspended) return;
|
||||
|
||||
Trace.beginSection("recreateTaskbar");
|
||||
try {
|
||||
DeviceProfile dp = mUserUnlocked ?
|
||||
@@ -648,8 +651,22 @@ public class TaskbarManager {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes Taskbar from the window manager and prevents recreation if {@code true}.
|
||||
* <p>
|
||||
* Suspending is for testing purposes only; avoid calling this method in production.
|
||||
*/
|
||||
@VisibleForTesting
|
||||
public void addTaskbarRootViewToWindow() {
|
||||
public void setSuspended(boolean isSuspended) {
|
||||
mIsSuspended = isSuspended;
|
||||
if (mIsSuspended) {
|
||||
removeTaskbarRootViewFromWindow();
|
||||
} else {
|
||||
addTaskbarRootViewToWindow();
|
||||
}
|
||||
}
|
||||
|
||||
private void addTaskbarRootViewToWindow() {
|
||||
if (enableTaskbarNoRecreate() && !mAddedWindow && mTaskbarActivityContext != null) {
|
||||
mWindowManager.addView(mTaskbarRootLayout,
|
||||
mTaskbarActivityContext.getWindowLayoutParams());
|
||||
@@ -657,8 +674,7 @@ public class TaskbarManager {
|
||||
}
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
public void removeTaskbarRootViewFromWindow() {
|
||||
private void removeTaskbarRootViewFromWindow() {
|
||||
if (enableTaskbarNoRecreate() && mAddedWindow) {
|
||||
mWindowManager.removeViewImmediate(mTaskbarRootLayout);
|
||||
mAddedWindow = false;
|
||||
|
||||
+63
-53
@@ -20,7 +20,6 @@ import android.animation.AnimatorTestRule
|
||||
import android.content.ComponentName
|
||||
import android.content.Intent
|
||||
import android.os.Process
|
||||
import androidx.test.annotation.UiThreadTest
|
||||
import androidx.test.platform.app.InstrumentationRegistry.getInstrumentation
|
||||
import com.android.launcher3.BubbleTextView
|
||||
import com.android.launcher3.appprediction.PredictionRowView
|
||||
@@ -34,6 +33,7 @@ import com.android.launcher3.taskbar.rules.TaskbarWindowSandboxContext
|
||||
import com.android.launcher3.util.LauncherMultivalentJUnit
|
||||
import com.android.launcher3.util.LauncherMultivalentJUnit.EmulatedDevices
|
||||
import com.android.launcher3.util.PackageUserKey
|
||||
import com.android.launcher3.util.TestUtil
|
||||
import com.google.common.truth.Truth.assertThat
|
||||
import org.junit.Rule
|
||||
import org.junit.Test
|
||||
@@ -55,17 +55,17 @@ class TaskbarAllAppsControllerTest {
|
||||
@InjectController lateinit var overlayController: TaskbarOverlayController
|
||||
|
||||
@Test
|
||||
@UiThreadTest
|
||||
fun testToggle_once_showsAllApps() {
|
||||
allAppsController.toggle()
|
||||
getInstrumentation().runOnMainSync { allAppsController.toggle() }
|
||||
assertThat(allAppsController.isOpen).isTrue()
|
||||
}
|
||||
|
||||
@Test
|
||||
@UiThreadTest
|
||||
fun testToggle_twice_closesAllApps() {
|
||||
allAppsController.toggle()
|
||||
allAppsController.toggle()
|
||||
getInstrumentation().runOnMainSync {
|
||||
allAppsController.toggle()
|
||||
allAppsController.toggle()
|
||||
}
|
||||
assertThat(allAppsController.isOpen).isFalse()
|
||||
}
|
||||
|
||||
@@ -77,54 +77,62 @@ class TaskbarAllAppsControllerTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
@UiThreadTest
|
||||
fun testSetApps_beforeOpened_cachesInfo() {
|
||||
allAppsController.setApps(TEST_APPS, 0, emptyMap())
|
||||
allAppsController.toggle()
|
||||
val overlayContext =
|
||||
TestUtil.getOnUiThread {
|
||||
allAppsController.setApps(TEST_APPS, 0, emptyMap())
|
||||
allAppsController.toggle()
|
||||
overlayController.requestWindow()
|
||||
}
|
||||
|
||||
val overlayContext = overlayController.requestWindow()
|
||||
assertThat(overlayContext.appsView.appsStore.apps).isEqualTo(TEST_APPS)
|
||||
}
|
||||
|
||||
@Test
|
||||
@UiThreadTest
|
||||
fun testSetApps_afterOpened_updatesStore() {
|
||||
allAppsController.toggle()
|
||||
allAppsController.setApps(TEST_APPS, 0, emptyMap())
|
||||
val overlayContext =
|
||||
TestUtil.getOnUiThread {
|
||||
allAppsController.toggle()
|
||||
allAppsController.setApps(TEST_APPS, 0, emptyMap())
|
||||
overlayController.requestWindow()
|
||||
}
|
||||
|
||||
val overlayContext = overlayController.requestWindow()
|
||||
assertThat(overlayContext.appsView.appsStore.apps).isEqualTo(TEST_APPS)
|
||||
}
|
||||
|
||||
@Test
|
||||
@UiThreadTest
|
||||
fun testSetPredictedApps_beforeOpened_cachesInfo() {
|
||||
allAppsController.setPredictedApps(TEST_PREDICTED_APPS)
|
||||
allAppsController.toggle()
|
||||
|
||||
val predictedApps =
|
||||
overlayController
|
||||
.requestWindow()
|
||||
.appsView
|
||||
.floatingHeaderView
|
||||
.findFixedRowByType(PredictionRowView::class.java)
|
||||
.predictedApps
|
||||
TestUtil.getOnUiThread {
|
||||
allAppsController.setPredictedApps(TEST_PREDICTED_APPS)
|
||||
allAppsController.toggle()
|
||||
|
||||
overlayController
|
||||
.requestWindow()
|
||||
.appsView
|
||||
.floatingHeaderView
|
||||
.findFixedRowByType(PredictionRowView::class.java)
|
||||
.predictedApps
|
||||
}
|
||||
|
||||
assertThat(predictedApps).isEqualTo(TEST_PREDICTED_APPS)
|
||||
}
|
||||
|
||||
@Test
|
||||
@UiThreadTest
|
||||
fun testSetPredictedApps_afterOpened_cachesInfo() {
|
||||
allAppsController.toggle()
|
||||
allAppsController.setPredictedApps(TEST_PREDICTED_APPS)
|
||||
|
||||
val predictedApps =
|
||||
overlayController
|
||||
.requestWindow()
|
||||
.appsView
|
||||
.floatingHeaderView
|
||||
.findFixedRowByType(PredictionRowView::class.java)
|
||||
.predictedApps
|
||||
TestUtil.getOnUiThread {
|
||||
allAppsController.toggle()
|
||||
allAppsController.setPredictedApps(TEST_PREDICTED_APPS)
|
||||
|
||||
overlayController
|
||||
.requestWindow()
|
||||
.appsView
|
||||
.floatingHeaderView
|
||||
.findFixedRowByType(PredictionRowView::class.java)
|
||||
.predictedApps
|
||||
}
|
||||
|
||||
assertThat(predictedApps).isEqualTo(TEST_PREDICTED_APPS)
|
||||
}
|
||||
|
||||
@@ -140,36 +148,38 @@ class TaskbarAllAppsControllerTest {
|
||||
}
|
||||
|
||||
// Ensure the recycler view fully inflates before trying to grab an icon.
|
||||
getInstrumentation().runOnMainSync {
|
||||
val btv =
|
||||
val btv =
|
||||
TestUtil.getOnUiThread {
|
||||
overlayController
|
||||
.requestWindow()
|
||||
.appsView
|
||||
.activeRecyclerView
|
||||
.findViewHolderForAdapterPosition(0)
|
||||
?.itemView as? BubbleTextView
|
||||
assertThat(btv?.hasDot()).isTrue()
|
||||
}
|
||||
}
|
||||
assertThat(btv?.hasDot()).isTrue()
|
||||
}
|
||||
|
||||
@Test
|
||||
@UiThreadTest
|
||||
fun testUpdateNotificationDots_predictedApp_hasDot() {
|
||||
allAppsController.setPredictedApps(TEST_PREDICTED_APPS)
|
||||
allAppsController.toggle()
|
||||
getInstrumentation().runOnMainSync {
|
||||
allAppsController.setPredictedApps(TEST_PREDICTED_APPS)
|
||||
allAppsController.toggle()
|
||||
taskbarUnitTestRule.activityContext.popupDataProvider.onNotificationPosted(
|
||||
PackageUserKey.fromItemInfo(TEST_PREDICTED_APPS[0]),
|
||||
NotificationKeyData("key"),
|
||||
)
|
||||
}
|
||||
|
||||
taskbarUnitTestRule.activityContext.popupDataProvider.onNotificationPosted(
|
||||
PackageUserKey.fromItemInfo(TEST_PREDICTED_APPS[0]),
|
||||
NotificationKeyData("key"),
|
||||
)
|
||||
|
||||
val predictionRowView =
|
||||
overlayController
|
||||
.requestWindow()
|
||||
.appsView
|
||||
.floatingHeaderView
|
||||
.findFixedRowByType(PredictionRowView::class.java)
|
||||
val btv = predictionRowView.getChildAt(0) as BubbleTextView
|
||||
val btv =
|
||||
TestUtil.getOnUiThread {
|
||||
overlayController
|
||||
.requestWindow()
|
||||
.appsView
|
||||
.floatingHeaderView
|
||||
.findFixedRowByType(PredictionRowView::class.java)
|
||||
.getChildAt(0) as BubbleTextView
|
||||
}
|
||||
assertThat(btv.hasDot()).isTrue()
|
||||
}
|
||||
|
||||
|
||||
+67
-69
@@ -18,7 +18,6 @@ package com.android.launcher3.taskbar.overlay
|
||||
|
||||
import android.app.ActivityManager.RunningTaskInfo
|
||||
import android.view.MotionEvent
|
||||
import androidx.test.annotation.UiThreadTest
|
||||
import androidx.test.platform.app.InstrumentationRegistry.getInstrumentation
|
||||
import com.android.launcher3.AbstractFloatingView
|
||||
import com.android.launcher3.AbstractFloatingView.TYPE_OPTIONS_POPUP
|
||||
@@ -31,7 +30,7 @@ import com.android.launcher3.taskbar.rules.TaskbarUnitTestRule.InjectController
|
||||
import com.android.launcher3.taskbar.rules.TaskbarWindowSandboxContext
|
||||
import com.android.launcher3.util.LauncherMultivalentJUnit
|
||||
import com.android.launcher3.util.LauncherMultivalentJUnit.EmulatedDevices
|
||||
import com.android.launcher3.views.BaseDragLayer
|
||||
import com.android.launcher3.util.TestUtil.getOnUiThread
|
||||
import com.android.systemui.shared.system.TaskStackChangeListeners
|
||||
import com.google.common.truth.Truth.assertThat
|
||||
import org.junit.Rule
|
||||
@@ -54,74 +53,69 @@ class TaskbarOverlayControllerTest {
|
||||
get() = taskbarUnitTestRule.activityContext
|
||||
|
||||
@Test
|
||||
@UiThreadTest
|
||||
fun testRequestWindow_twice_reusesWindow() {
|
||||
val context1 = overlayController.requestWindow()
|
||||
val context2 = overlayController.requestWindow()
|
||||
val (context1, context2) =
|
||||
getOnUiThread {
|
||||
Pair(overlayController.requestWindow(), overlayController.requestWindow())
|
||||
}
|
||||
assertThat(context1).isSameInstanceAs(context2)
|
||||
}
|
||||
|
||||
@Test
|
||||
@UiThreadTest
|
||||
fun testRequestWindow_afterHidingExistingWindow_createsNewWindow() {
|
||||
val context1 = overlayController.requestWindow()
|
||||
overlayController.hideWindow()
|
||||
val context1 = getOnUiThread { overlayController.requestWindow() }
|
||||
getInstrumentation().runOnMainSync { overlayController.hideWindow() }
|
||||
|
||||
val context2 = overlayController.requestWindow()
|
||||
val context2 = getOnUiThread { overlayController.requestWindow() }
|
||||
assertThat(context1).isNotSameInstanceAs(context2)
|
||||
}
|
||||
|
||||
@Test
|
||||
@UiThreadTest
|
||||
fun testRequestWindow_afterHidingOverlay_createsNewWindow() {
|
||||
val context1 = overlayController.requestWindow()
|
||||
TestOverlayView.show(context1)
|
||||
overlayController.hideWindow()
|
||||
val context1 = getOnUiThread { overlayController.requestWindow() }
|
||||
getInstrumentation().runOnMainSync {
|
||||
TestOverlayView.show(context1)
|
||||
overlayController.hideWindow()
|
||||
}
|
||||
|
||||
val context2 = overlayController.requestWindow()
|
||||
val context2 = getOnUiThread { overlayController.requestWindow() }
|
||||
assertThat(context1).isNotSameInstanceAs(context2)
|
||||
}
|
||||
|
||||
@Test
|
||||
@UiThreadTest
|
||||
fun testRequestWindow_addsProxyView() {
|
||||
TestOverlayView.show(overlayController.requestWindow())
|
||||
getInstrumentation().runOnMainSync {
|
||||
TestOverlayView.show(overlayController.requestWindow())
|
||||
}
|
||||
assertThat(hasOpenView(taskbarContext, TYPE_TASKBAR_OVERLAY_PROXY)).isTrue()
|
||||
}
|
||||
|
||||
@Test
|
||||
@UiThreadTest
|
||||
fun testRequestWindow_closeProxyView_closesOverlay() {
|
||||
val overlay = TestOverlayView.show(overlayController.requestWindow())
|
||||
AbstractFloatingView.closeOpenContainer(taskbarContext, TYPE_TASKBAR_OVERLAY_PROXY)
|
||||
val overlay = getOnUiThread { TestOverlayView.show(overlayController.requestWindow()) }
|
||||
getInstrumentation().runOnMainSync {
|
||||
AbstractFloatingView.closeOpenContainer(taskbarContext, TYPE_TASKBAR_OVERLAY_PROXY)
|
||||
}
|
||||
assertThat(overlay.isOpen).isFalse()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testRequestWindow_attachesDragLayer() {
|
||||
lateinit var dragLayer: BaseDragLayer<*>
|
||||
getInstrumentation().runOnMainSync {
|
||||
dragLayer = overlayController.requestWindow().dragLayer
|
||||
}
|
||||
|
||||
val dragLayer = getOnUiThread { overlayController.requestWindow().dragLayer }
|
||||
// Allow drag layer to attach before checking.
|
||||
getInstrumentation().runOnMainSync { assertThat(dragLayer.isAttachedToWindow).isTrue() }
|
||||
}
|
||||
|
||||
@Test
|
||||
@UiThreadTest
|
||||
fun testHideWindow_closesOverlay() {
|
||||
val overlay = TestOverlayView.show(overlayController.requestWindow())
|
||||
overlayController.hideWindow()
|
||||
val overlay = getOnUiThread { TestOverlayView.show(overlayController.requestWindow()) }
|
||||
getInstrumentation().runOnMainSync { overlayController.hideWindow() }
|
||||
assertThat(overlay.isOpen).isFalse()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testHideWindow_detachesDragLayer() {
|
||||
lateinit var dragLayer: BaseDragLayer<*>
|
||||
getInstrumentation().runOnMainSync {
|
||||
dragLayer = overlayController.requestWindow().dragLayer
|
||||
}
|
||||
val dragLayer = getOnUiThread { overlayController.requestWindow().dragLayer }
|
||||
|
||||
// Wait for drag layer to be attached to window before hiding.
|
||||
getInstrumentation().runOnMainSync {
|
||||
@@ -131,26 +125,30 @@ class TaskbarOverlayControllerTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
@UiThreadTest
|
||||
fun testTwoOverlays_closeOne_windowStaysOpen() {
|
||||
val context = overlayController.requestWindow()
|
||||
val overlay1 = TestOverlayView.show(context)
|
||||
val overlay2 = TestOverlayView.show(context)
|
||||
val (overlay1, overlay2) =
|
||||
getOnUiThread {
|
||||
val context = overlayController.requestWindow()
|
||||
Pair(TestOverlayView.show(context), TestOverlayView.show(context))
|
||||
}
|
||||
|
||||
overlay1.close(false)
|
||||
getInstrumentation().runOnMainSync { overlay1.close(false) }
|
||||
assertThat(overlay2.isOpen).isTrue()
|
||||
assertThat(hasOpenView(taskbarContext, TYPE_TASKBAR_OVERLAY_PROXY)).isTrue()
|
||||
}
|
||||
|
||||
@Test
|
||||
@UiThreadTest
|
||||
fun testTwoOverlays_closeAll_closesWindow() {
|
||||
val context = overlayController.requestWindow()
|
||||
val overlay1 = TestOverlayView.show(context)
|
||||
val overlay2 = TestOverlayView.show(context)
|
||||
val (overlay1, overlay2) =
|
||||
getOnUiThread {
|
||||
val context = overlayController.requestWindow()
|
||||
Pair(TestOverlayView.show(context), TestOverlayView.show(context))
|
||||
}
|
||||
|
||||
overlay1.close(false)
|
||||
overlay2.close(false)
|
||||
getInstrumentation().runOnMainSync {
|
||||
overlay1.close(false)
|
||||
overlay2.close(false)
|
||||
}
|
||||
assertThat(hasOpenView(taskbarContext, TYPE_TASKBAR_OVERLAY_PROXY)).isFalse()
|
||||
}
|
||||
|
||||
@@ -165,11 +163,7 @@ class TaskbarOverlayControllerTest {
|
||||
|
||||
@Test
|
||||
fun testTaskMovedToFront_closesOverlay() {
|
||||
lateinit var overlay: TestOverlayView
|
||||
getInstrumentation().runOnMainSync {
|
||||
overlay = TestOverlayView.show(overlayController.requestWindow())
|
||||
}
|
||||
|
||||
val overlay = getOnUiThread { TestOverlayView.show(overlayController.requestWindow()) }
|
||||
TaskStackChangeListeners.getInstance().listenerImpl.onTaskMovedToFront(RunningTaskInfo())
|
||||
// Make sure TaskStackChangeListeners' Handler posts the callback before checking state.
|
||||
getInstrumentation().runOnMainSync { assertThat(overlay.isOpen).isFalse() }
|
||||
@@ -177,9 +171,8 @@ class TaskbarOverlayControllerTest {
|
||||
|
||||
@Test
|
||||
fun testTaskStackChanged_allAppsClosed_overlayStaysOpen() {
|
||||
lateinit var overlay: TestOverlayView
|
||||
val overlay = getOnUiThread { TestOverlayView.show(overlayController.requestWindow()) }
|
||||
getInstrumentation().runOnMainSync {
|
||||
overlay = TestOverlayView.show(overlayController.requestWindow())
|
||||
taskbarContext.controllers.sharedState?.allAppsVisible = false
|
||||
}
|
||||
|
||||
@@ -189,9 +182,8 @@ class TaskbarOverlayControllerTest {
|
||||
|
||||
@Test
|
||||
fun testTaskStackChanged_allAppsOpen_closesOverlay() {
|
||||
lateinit var overlay: TestOverlayView
|
||||
val overlay = getOnUiThread { TestOverlayView.show(overlayController.requestWindow()) }
|
||||
getInstrumentation().runOnMainSync {
|
||||
overlay = TestOverlayView.show(overlayController.requestWindow())
|
||||
taskbarContext.controllers.sharedState?.allAppsVisible = true
|
||||
}
|
||||
|
||||
@@ -200,33 +192,39 @@ class TaskbarOverlayControllerTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
@UiThreadTest
|
||||
fun testUpdateLauncherDeviceProfile_overlayNotRebindSafe_closesOverlay() {
|
||||
val overlayContext = overlayController.requestWindow()
|
||||
val overlay = TestOverlayView.show(overlayContext).apply { type = TYPE_OPTIONS_POPUP }
|
||||
val context = getOnUiThread { overlayController.requestWindow() }
|
||||
val overlay = getOnUiThread {
|
||||
TestOverlayView.show(context).apply { type = TYPE_OPTIONS_POPUP }
|
||||
}
|
||||
|
||||
overlayController.updateLauncherDeviceProfile(
|
||||
overlayController.launcherDeviceProfile
|
||||
.toBuilder(overlayContext)
|
||||
.setGestureMode(false)
|
||||
.build()
|
||||
)
|
||||
getInstrumentation().runOnMainSync {
|
||||
overlayController.updateLauncherDeviceProfile(
|
||||
overlayController.launcherDeviceProfile
|
||||
.toBuilder(context)
|
||||
.setGestureMode(false)
|
||||
.build()
|
||||
)
|
||||
}
|
||||
|
||||
assertThat(overlay.isOpen).isFalse()
|
||||
}
|
||||
|
||||
@Test
|
||||
@UiThreadTest
|
||||
fun testUpdateLauncherDeviceProfile_overlayRebindSafe_overlayStaysOpen() {
|
||||
val overlayContext = overlayController.requestWindow()
|
||||
val overlay = TestOverlayView.show(overlayContext).apply { type = TYPE_TASKBAR_ALL_APPS }
|
||||
val context = getOnUiThread { overlayController.requestWindow() }
|
||||
val overlay = getOnUiThread {
|
||||
TestOverlayView.show(context).apply { type = TYPE_TASKBAR_ALL_APPS }
|
||||
}
|
||||
|
||||
overlayController.updateLauncherDeviceProfile(
|
||||
overlayController.launcherDeviceProfile
|
||||
.toBuilder(overlayContext)
|
||||
.setGestureMode(false)
|
||||
.build()
|
||||
)
|
||||
getInstrumentation().runOnMainSync {
|
||||
overlayController.updateLauncherDeviceProfile(
|
||||
overlayController.launcherDeviceProfile
|
||||
.toBuilder(context)
|
||||
.setGestureMode(false)
|
||||
.build()
|
||||
)
|
||||
}
|
||||
|
||||
assertThat(overlay.isOpen).isTrue()
|
||||
}
|
||||
|
||||
+19
-16
@@ -16,6 +16,7 @@
|
||||
|
||||
package com.android.launcher3.taskbar.rules
|
||||
|
||||
import androidx.test.platform.app.InstrumentationRegistry.getInstrumentation
|
||||
import com.android.launcher3.taskbar.rules.TaskbarModeRule.Mode
|
||||
import com.android.launcher3.taskbar.rules.TaskbarModeRule.TaskbarMode
|
||||
import com.android.launcher3.util.DisplayController
|
||||
@@ -59,23 +60,25 @@ class TaskbarModeRule(private val context: TaskbarWindowSandboxContext) : TestRu
|
||||
override fun evaluate() {
|
||||
val mode = taskbarMode.mode
|
||||
|
||||
context.applicationContext.putObject(
|
||||
DisplayController.INSTANCE,
|
||||
object : DisplayController(context) {
|
||||
override fun getInfo(): Info {
|
||||
return spy(super.getInfo()) {
|
||||
on { isTransientTaskbar } doReturn (mode == Mode.TRANSIENT)
|
||||
on { isPinnedTaskbar } doReturn (mode == Mode.PINNED)
|
||||
on { navigationMode } doReturn
|
||||
when (mode) {
|
||||
Mode.TRANSIENT,
|
||||
Mode.PINNED -> NavigationMode.NO_BUTTON
|
||||
Mode.THREE_BUTTONS -> NavigationMode.THREE_BUTTONS
|
||||
}
|
||||
getInstrumentation().runOnMainSync {
|
||||
context.applicationContext.putObject(
|
||||
DisplayController.INSTANCE,
|
||||
object : DisplayController(context) {
|
||||
override fun getInfo(): Info {
|
||||
return spy(super.getInfo()) {
|
||||
on { isTransientTaskbar } doReturn (mode == Mode.TRANSIENT)
|
||||
on { isPinnedTaskbar } doReturn (mode == Mode.PINNED)
|
||||
on { navigationMode } doReturn
|
||||
when (mode) {
|
||||
Mode.TRANSIENT,
|
||||
Mode.PINNED -> NavigationMode.NO_BUTTON
|
||||
Mode.THREE_BUTTONS -> NavigationMode.THREE_BUTTONS
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
)
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
base.evaluate()
|
||||
}
|
||||
|
||||
+72
-10
@@ -20,18 +20,25 @@ import android.app.Instrumentation
|
||||
import android.app.PendingIntent
|
||||
import android.content.IIntentSender
|
||||
import android.content.Intent
|
||||
import android.provider.Settings
|
||||
import android.provider.Settings.Secure.NAV_BAR_KIDS_MODE
|
||||
import android.provider.Settings.Secure.USER_SETUP_COMPLETE
|
||||
import androidx.test.platform.app.InstrumentationRegistry
|
||||
import androidx.test.rule.ServiceTestRule
|
||||
import com.android.launcher3.LauncherAppState
|
||||
import com.android.launcher3.taskbar.TaskbarActivityContext
|
||||
import com.android.launcher3.taskbar.TaskbarManager
|
||||
import com.android.launcher3.taskbar.TaskbarNavButtonController.TaskbarNavButtonCallbacks
|
||||
import com.android.launcher3.taskbar.rules.TaskbarUnitTestRule.InjectController
|
||||
import com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR
|
||||
import com.android.launcher3.util.LauncherMultivalentJUnit.Companion.isRunningInRobolectric
|
||||
import com.android.launcher3.util.ModelTestExtensions.loadModelSync
|
||||
import com.android.launcher3.util.TestUtil
|
||||
import com.android.quickstep.AllAppsActionManager
|
||||
import com.android.quickstep.TouchInteractionService
|
||||
import com.android.quickstep.TouchInteractionService.TISBinder
|
||||
import org.junit.Assume.assumeTrue
|
||||
import org.junit.rules.RuleChain
|
||||
import org.junit.rules.TestRule
|
||||
import org.junit.runner.Description
|
||||
import org.junit.runners.model.Statement
|
||||
@@ -48,12 +55,11 @@ import org.junit.runners.model.Statement
|
||||
* that code that is executed on the main thread in production should also happen on that thread
|
||||
* when tested.
|
||||
*
|
||||
* `@UiThreadTest` is a simple way to run an entire test body on the main thread. But if a test
|
||||
* executes code that appends message(s) to the main thread's `MessageQueue`, the annotation will
|
||||
* prevent those messages from being processed until after the test body finishes.
|
||||
* `@UiThreadTest` is incompatible with this rule. The annotation causes this rule to run on the
|
||||
* main thread, but it needs to be run on the test thread for it to work properly. Instead, only run
|
||||
* code that requires the main thread using something like [Instrumentation.runOnMainSync] or
|
||||
* [TestUtil.getOnUiThread].
|
||||
*
|
||||
* To test pending messages, instead use something like [Instrumentation.runOnMainSync] to perform
|
||||
* only sections of the test body on the main thread synchronously:
|
||||
* ```
|
||||
* @Test
|
||||
* fun example() {
|
||||
@@ -71,6 +77,10 @@ class TaskbarUnitTestRule(
|
||||
private val instrumentation = InstrumentationRegistry.getInstrumentation()
|
||||
private val serviceTestRule = ServiceTestRule()
|
||||
|
||||
private val userSetupCompleteRule = TaskbarSecureSettingRule(USER_SETUP_COMPLETE)
|
||||
private val kidsModeRule = TaskbarSecureSettingRule(NAV_BAR_KIDS_MODE)
|
||||
private val settingRules = RuleChain.outerRule(userSetupCompleteRule).around(kidsModeRule)
|
||||
|
||||
private lateinit var taskbarManager: TaskbarManager
|
||||
|
||||
val activityContext: TaskbarActivityContext
|
||||
@@ -80,15 +90,34 @@ class TaskbarUnitTestRule(
|
||||
}
|
||||
|
||||
override fun apply(base: Statement, description: Description): Statement {
|
||||
return settingRules.apply(createStatement(base, description), description)
|
||||
}
|
||||
|
||||
private fun createStatement(base: Statement, description: Description): Statement {
|
||||
return object : Statement() {
|
||||
override fun evaluate() {
|
||||
|
||||
// Only run test when Taskbar is enabled.
|
||||
instrumentation.runOnMainSync {
|
||||
assumeTrue(
|
||||
LauncherAppState.getIDP(context).getDeviceProfile(context).isTaskbarPresent
|
||||
)
|
||||
}
|
||||
|
||||
// Process secure setting annotations.
|
||||
instrumentation.runOnMainSync {
|
||||
userSetupCompleteRule.putInt(
|
||||
if (description.getAnnotation(UserSetupMode::class.java) != null) {
|
||||
0
|
||||
} else {
|
||||
1
|
||||
}
|
||||
)
|
||||
kidsModeRule.putInt(
|
||||
if (description.getAnnotation(NavBarKidsMode::class.java) != null) 1 else 0
|
||||
)
|
||||
}
|
||||
|
||||
// Check for existing Taskbar instance from Launcher process.
|
||||
val launcherTaskbarManager: TaskbarManager? =
|
||||
if (!isRunningInRobolectric) {
|
||||
@@ -105,8 +134,8 @@ class TaskbarUnitTestRule(
|
||||
null
|
||||
}
|
||||
|
||||
instrumentation.runOnMainSync {
|
||||
taskbarManager =
|
||||
taskbarManager =
|
||||
TestUtil.getOnUiThread {
|
||||
TaskbarManager(
|
||||
context,
|
||||
AllAppsActionManager(context, UI_HELPER_EXECUTOR) {
|
||||
@@ -114,12 +143,14 @@ class TaskbarUnitTestRule(
|
||||
},
|
||||
object : TaskbarNavButtonCallbacks {},
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
LauncherAppState.getInstance(context).model.loadModelSync()
|
||||
|
||||
// Replace Launcher Taskbar window with test instance.
|
||||
instrumentation.runOnMainSync {
|
||||
launcherTaskbarManager?.removeTaskbarRootViewFromWindow()
|
||||
launcherTaskbarManager?.setSuspended(true)
|
||||
taskbarManager.onUserUnlocked() // Required to complete initialization.
|
||||
}
|
||||
|
||||
@@ -129,7 +160,7 @@ class TaskbarUnitTestRule(
|
||||
// Revert Taskbar window.
|
||||
instrumentation.runOnMainSync {
|
||||
taskbarManager.destroy()
|
||||
launcherTaskbarManager?.addTaskbarRootViewToWindow()
|
||||
launcherTaskbarManager?.setSuspended(false)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -167,4 +198,35 @@ class TaskbarUnitTestRule(
|
||||
@Retention(AnnotationRetention.RUNTIME)
|
||||
@Target(AnnotationTarget.FIELD)
|
||||
annotation class InjectController
|
||||
|
||||
/** Overrides [USER_SETUP_COMPLETE] to be `false` for tests. */
|
||||
@Retention(AnnotationRetention.RUNTIME)
|
||||
@Target(AnnotationTarget.CLASS, AnnotationTarget.FUNCTION)
|
||||
annotation class UserSetupMode
|
||||
|
||||
/** Overrides [NAV_BAR_KIDS_MODE] to be `true` for tests. */
|
||||
@Retention(AnnotationRetention.RUNTIME)
|
||||
@Target(AnnotationTarget.CLASS, AnnotationTarget.FUNCTION)
|
||||
annotation class NavBarKidsMode
|
||||
|
||||
/** Rule for Taskbar integer-based secure settings. */
|
||||
private inner class TaskbarSecureSettingRule(private val settingName: String) : TestRule {
|
||||
|
||||
override fun apply(base: Statement, description: Description): Statement {
|
||||
return object : Statement() {
|
||||
override fun evaluate() {
|
||||
val originalValue =
|
||||
Settings.Secure.getInt(context.contentResolver, settingName, /* def= */ 0)
|
||||
try {
|
||||
base.evaluate()
|
||||
} finally {
|
||||
instrumentation.runOnMainSync { putInt(originalValue) }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** Puts [value] into secure settings under [settingName]. */
|
||||
fun putInt(value: Int) = Settings.Secure.putInt(context.contentResolver, settingName, value)
|
||||
}
|
||||
}
|
||||
|
||||
+35
-2
@@ -22,6 +22,8 @@ import com.android.launcher3.taskbar.TaskbarKeyguardController
|
||||
import com.android.launcher3.taskbar.TaskbarManager
|
||||
import com.android.launcher3.taskbar.TaskbarStashController
|
||||
import com.android.launcher3.taskbar.rules.TaskbarUnitTestRule.InjectController
|
||||
import com.android.launcher3.taskbar.rules.TaskbarUnitTestRule.NavBarKidsMode
|
||||
import com.android.launcher3.taskbar.rules.TaskbarUnitTestRule.UserSetupMode
|
||||
import com.android.launcher3.util.LauncherMultivalentJUnit
|
||||
import com.android.launcher3.util.LauncherMultivalentJUnit.EmulatedDevices
|
||||
import com.google.common.truth.Truth.assertThat
|
||||
@@ -125,9 +127,40 @@ class TaskbarUnitTestRuleTest {
|
||||
}
|
||||
}
|
||||
|
||||
/** Executes [runTest] after the [testRule] setup phase completes. */
|
||||
@Test
|
||||
fun testUserSetupMode_default_isComplete() {
|
||||
onSetup { assertThat(activityContext.isUserSetupComplete).isTrue() }
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testUserSetupMode_withAnnotation_isIncomplete() {
|
||||
@UserSetupMode class Mode
|
||||
onSetup(description = Description.createSuiteDescription(Mode::class.java)) {
|
||||
assertThat(activityContext.isUserSetupComplete).isFalse()
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testNavBarKidsMode_default_navBarNotForcedVisible() {
|
||||
onSetup { assertThat(activityContext.isNavBarForceVisible).isFalse() }
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testNavBarKidsMode_withAnnotation_navBarForcedVisible() {
|
||||
@NavBarKidsMode class Mode
|
||||
onSetup(description = Description.createSuiteDescription(Mode::class.java)) {
|
||||
assertThat(activityContext.isNavBarForceVisible).isTrue()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Executes [runTest] after the [testRule] setup phase completes.
|
||||
*
|
||||
* A [description] can also be provided to mimic annotating a test or test class.
|
||||
*/
|
||||
private fun onSetup(
|
||||
testRule: TaskbarUnitTestRule = TaskbarUnitTestRule(this, context),
|
||||
description: Description = DESCRIPTION,
|
||||
runTest: TaskbarUnitTestRule.() -> Unit,
|
||||
) {
|
||||
testRule
|
||||
@@ -135,7 +168,7 @@ class TaskbarUnitTestRuleTest {
|
||||
object : Statement() {
|
||||
override fun evaluate() = runTest(testRule)
|
||||
},
|
||||
DESCRIPTION,
|
||||
description,
|
||||
)
|
||||
.evaluate()
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user