Merge "Add SCREEN_STATE flags to QuickStepContract" into udc-dev
This commit is contained in:
committed by
Android (Google) Code Review
commit
8348312f8b
@@ -116,6 +116,27 @@ public class QuickStepContract {
|
|||||||
public static final int SYSUI_STATE_FREEFORM_ACTIVE_IN_DESKTOP_MODE = 1 << 26;
|
public static final int SYSUI_STATE_FREEFORM_ACTIVE_IN_DESKTOP_MODE = 1 << 26;
|
||||||
// Device dreaming state
|
// Device dreaming state
|
||||||
public static final int SYSUI_STATE_DEVICE_DREAMING = 1 << 27;
|
public static final int SYSUI_STATE_DEVICE_DREAMING = 1 << 27;
|
||||||
|
// Whether the screen is currently on. Note that the screen is considered on while turning on,
|
||||||
|
// but not while turning off.
|
||||||
|
public static final int SYSUI_STATE_SCREEN_ON = 1 << 28;
|
||||||
|
// Whether the screen is currently transitioning into the state indicated by
|
||||||
|
// SYSUI_STATE_SCREEN_ON.
|
||||||
|
public static final int SYSUI_STATE_SCREEN_TRANSITION = 1 << 29;
|
||||||
|
|
||||||
|
// Mask for SystemUiStateFlags to isolate SYSUI_STATE_SCREEN_ON and
|
||||||
|
// SYSUI_STATE_SCREEN_TRANSITION, to match SCREEN_STATE_*
|
||||||
|
public static final int SYSUI_STATE_SCREEN_STATE_MASK =
|
||||||
|
SYSUI_STATE_SCREEN_ON | SYSUI_STATE_SCREEN_TRANSITION;
|
||||||
|
// Screen is off.
|
||||||
|
public static final int SCREEN_STATE_OFF = 0;
|
||||||
|
// Screen is on.
|
||||||
|
public static final int SCREEN_STATE_ON = SYSUI_STATE_SCREEN_ON;
|
||||||
|
// Screen is still on, but transitioning to turn off.
|
||||||
|
public static final int SCREEN_STATE_TURNING_OFF = SYSUI_STATE_SCREEN_TRANSITION;
|
||||||
|
// Screen was off and is now turning on.
|
||||||
|
public static final int SCREEN_STATE_TURNING_ON =
|
||||||
|
SYSUI_STATE_SCREEN_TRANSITION | SYSUI_STATE_SCREEN_ON;
|
||||||
|
|
||||||
// Whether the back gesture is allowed (or ignored) by the Shade
|
// Whether the back gesture is allowed (or ignored) by the Shade
|
||||||
public static final boolean ALLOW_BACK_GESTURE_IN_SHADE = SystemProperties.getBoolean(
|
public static final boolean ALLOW_BACK_GESTURE_IN_SHADE = SystemProperties.getBoolean(
|
||||||
"persist.wm.debug.shade_allow_back_gesture", false);
|
"persist.wm.debug.shade_allow_back_gesture", false);
|
||||||
@@ -148,7 +169,9 @@ public class QuickStepContract {
|
|||||||
SYSUI_STATE_IMMERSIVE_MODE,
|
SYSUI_STATE_IMMERSIVE_MODE,
|
||||||
SYSUI_STATE_VOICE_INTERACTION_WINDOW_SHOWING,
|
SYSUI_STATE_VOICE_INTERACTION_WINDOW_SHOWING,
|
||||||
SYSUI_STATE_FREEFORM_ACTIVE_IN_DESKTOP_MODE,
|
SYSUI_STATE_FREEFORM_ACTIVE_IN_DESKTOP_MODE,
|
||||||
SYSUI_STATE_DEVICE_DREAMING
|
SYSUI_STATE_DEVICE_DREAMING,
|
||||||
|
SYSUI_STATE_SCREEN_ON,
|
||||||
|
SYSUI_STATE_SCREEN_TRANSITION,
|
||||||
})
|
})
|
||||||
public @interface SystemUiStateFlags {}
|
public @interface SystemUiStateFlags {}
|
||||||
|
|
||||||
@@ -187,6 +210,9 @@ public class QuickStepContract {
|
|||||||
str.add((flags & SYSUI_STATE_FREEFORM_ACTIVE_IN_DESKTOP_MODE) != 0
|
str.add((flags & SYSUI_STATE_FREEFORM_ACTIVE_IN_DESKTOP_MODE) != 0
|
||||||
? "freeform_active_in_desktop_mode" : "");
|
? "freeform_active_in_desktop_mode" : "");
|
||||||
str.add((flags & SYSUI_STATE_DEVICE_DREAMING) != 0 ? "device_dreaming" : "");
|
str.add((flags & SYSUI_STATE_DEVICE_DREAMING) != 0 ? "device_dreaming" : "");
|
||||||
|
str.add("screen_"
|
||||||
|
+ ((flags & SYSUI_STATE_SCREEN_TRANSITION) != 0 ? "turning_" : "")
|
||||||
|
+ ((flags & SYSUI_STATE_SCREEN_ON) != 0 ? "on" : "off"));
|
||||||
|
|
||||||
return str.toString();
|
return str.toString();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -31,6 +31,8 @@ import static com.android.systemui.shared.system.QuickStepContract.KEY_EXTRA_WIN
|
|||||||
import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_BOUNCER_SHOWING;
|
import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_BOUNCER_SHOWING;
|
||||||
import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_DEVICE_DOZING;
|
import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_DEVICE_DOZING;
|
||||||
import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_DEVICE_DREAMING;
|
import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_DEVICE_DREAMING;
|
||||||
|
import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_SCREEN_ON;
|
||||||
|
import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_SCREEN_TRANSITION;
|
||||||
import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_STATUS_BAR_KEYGUARD_SHOWING;
|
import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_STATUS_BAR_KEYGUARD_SHOWING;
|
||||||
import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_STATUS_BAR_KEYGUARD_SHOWING_OCCLUDED;
|
import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_STATUS_BAR_KEYGUARD_SHOWING_OCCLUDED;
|
||||||
import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_TRACING_ENABLED;
|
import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_TRACING_ENABLED;
|
||||||
@@ -121,7 +123,8 @@ import dagger.Lazy;
|
|||||||
public class OverviewProxyService implements CallbackController<OverviewProxyListener>,
|
public class OverviewProxyService implements CallbackController<OverviewProxyListener>,
|
||||||
NavigationModeController.ModeChangedListener, Dumpable {
|
NavigationModeController.ModeChangedListener, Dumpable {
|
||||||
|
|
||||||
private static final String ACTION_QUICKSTEP = "android.intent.action.QUICKSTEP_SERVICE";
|
@VisibleForTesting
|
||||||
|
static final String ACTION_QUICKSTEP = "android.intent.action.QUICKSTEP_SERVICE";
|
||||||
|
|
||||||
public static final String TAG_OPS = "OverviewProxyService";
|
public static final String TAG_OPS = "OverviewProxyService";
|
||||||
private static final long BACKOFF_MILLIS = 1000;
|
private static final long BACKOFF_MILLIS = 1000;
|
||||||
@@ -548,6 +551,7 @@ public class OverviewProxyService implements CallbackController<OverviewProxyLis
|
|||||||
mUiEventLogger = uiEventLogger;
|
mUiEventLogger = uiEventLogger;
|
||||||
mDisplayTracker = displayTracker;
|
mDisplayTracker = displayTracker;
|
||||||
mUnfoldTransitionProgressForwarder = unfoldTransitionProgressForwarder;
|
mUnfoldTransitionProgressForwarder = unfoldTransitionProgressForwarder;
|
||||||
|
mSysuiUnlockAnimationController = sysuiUnlockAnimationController;
|
||||||
|
|
||||||
dumpManager.registerDumpable(getClass().getSimpleName(), this);
|
dumpManager.registerDumpable(getClass().getSimpleName(), this);
|
||||||
|
|
||||||
@@ -596,7 +600,6 @@ public class OverviewProxyService implements CallbackController<OverviewProxyLis
|
|||||||
// Connect to the service
|
// Connect to the service
|
||||||
updateEnabledState();
|
updateEnabledState();
|
||||||
startConnectionToCurrentUser();
|
startConnectionToCurrentUser();
|
||||||
mSysuiUnlockAnimationController = sysuiUnlockAnimationController;
|
|
||||||
|
|
||||||
// Listen for assistant changes
|
// Listen for assistant changes
|
||||||
assistUtils.registerVoiceInteractionSessionListener(mVoiceInteractionSessionListener);
|
assistUtils.registerVoiceInteractionSessionListener(mVoiceInteractionSessionListener);
|
||||||
@@ -726,10 +729,8 @@ public class OverviewProxyService implements CallbackController<OverviewProxyLis
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
mHandler.removeCallbacks(mConnectionRunnable);
|
mHandler.removeCallbacks(mConnectionRunnable);
|
||||||
Intent launcherServiceIntent = new Intent(ACTION_QUICKSTEP)
|
|
||||||
.setPackage(mRecentsComponentName.getPackageName());
|
|
||||||
try {
|
try {
|
||||||
mBound = mContext.bindServiceAsUser(launcherServiceIntent,
|
mBound = mContext.bindServiceAsUser(mQuickStepIntent,
|
||||||
mOverviewServiceConnection,
|
mOverviewServiceConnection,
|
||||||
Context.BIND_AUTO_CREATE | Context.BIND_FOREGROUND_SERVICE_WHILE_AWAKE,
|
Context.BIND_AUTO_CREATE | Context.BIND_FOREGROUND_SERVICE_WHILE_AWAKE,
|
||||||
UserHandle.of(mUserTracker.getUserId()));
|
UserHandle.of(mUserTracker.getUserId()));
|
||||||
@@ -862,6 +863,11 @@ public class OverviewProxyService implements CallbackController<OverviewProxyLis
|
|||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public void onScreenTurnedOn() {
|
public void onScreenTurnedOn() {
|
||||||
|
mSysUiState
|
||||||
|
.setFlag(SYSUI_STATE_SCREEN_ON, true)
|
||||||
|
.setFlag(SYSUI_STATE_SCREEN_TRANSITION, false)
|
||||||
|
.commitUpdate(mContext.getDisplayId());
|
||||||
|
|
||||||
try {
|
try {
|
||||||
if (mOverviewProxy != null) {
|
if (mOverviewProxy != null) {
|
||||||
mOverviewProxy.onScreenTurnedOn();
|
mOverviewProxy.onScreenTurnedOn();
|
||||||
@@ -873,11 +879,27 @@ public class OverviewProxyService implements CallbackController<OverviewProxyLis
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Notifies the Launcher that screen turned off.
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public void onScreenTurnedOff() {
|
||||||
|
mSysUiState
|
||||||
|
.setFlag(SYSUI_STATE_SCREEN_ON, false)
|
||||||
|
.setFlag(SYSUI_STATE_SCREEN_TRANSITION, false)
|
||||||
|
.commitUpdate(mContext.getDisplayId());
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Notifies the Launcher that screen is starting to turn on.
|
* Notifies the Launcher that screen is starting to turn on.
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public void onScreenTurningOff() {
|
public void onScreenTurningOff() {
|
||||||
|
mSysUiState
|
||||||
|
.setFlag(SYSUI_STATE_SCREEN_ON, false)
|
||||||
|
.setFlag(SYSUI_STATE_SCREEN_TRANSITION, true)
|
||||||
|
.commitUpdate(mContext.getDisplayId());
|
||||||
|
|
||||||
try {
|
try {
|
||||||
if (mOverviewProxy != null) {
|
if (mOverviewProxy != null) {
|
||||||
mOverviewProxy.onScreenTurningOff();
|
mOverviewProxy.onScreenTurningOff();
|
||||||
@@ -894,6 +916,11 @@ public class OverviewProxyService implements CallbackController<OverviewProxyLis
|
|||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public void onScreenTurningOn() {
|
public void onScreenTurningOn() {
|
||||||
|
mSysUiState
|
||||||
|
.setFlag(SYSUI_STATE_SCREEN_ON, true)
|
||||||
|
.setFlag(SYSUI_STATE_SCREEN_TRANSITION, true)
|
||||||
|
.commitUpdate(mContext.getDisplayId());
|
||||||
|
|
||||||
try {
|
try {
|
||||||
if (mOverviewProxy != null) {
|
if (mOverviewProxy != null) {
|
||||||
mOverviewProxy.onScreenTurningOn();
|
mOverviewProxy.onScreenTurningOn();
|
||||||
@@ -1005,4 +1032,21 @@ public class OverviewProxyService implements CallbackController<OverviewProxyLis
|
|||||||
default void onAssistantGestureCompletion(float velocity) {}
|
default void onAssistantGestureCompletion(float velocity) {}
|
||||||
default void startAssistant(Bundle bundle) {}
|
default void startAssistant(Bundle bundle) {}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Shuts down this service at the end of a testcase.
|
||||||
|
* <p>
|
||||||
|
* The in-production service is never shuts down, and it was not designed with testing in mind.
|
||||||
|
* This unregisters the mechanisms by which the service will be revived after a testcase.
|
||||||
|
* <p>
|
||||||
|
* NOTE: This is a stop-gap introduced when first added some tests to this class. It should
|
||||||
|
* probably be replaced by proper lifecycle management on this class.
|
||||||
|
*/
|
||||||
|
@VisibleForTesting()
|
||||||
|
void shutdownForTest() {
|
||||||
|
mContext.unregisterReceiver(mLauncherStateChangedReceiver);
|
||||||
|
mIsEnabled = false;
|
||||||
|
mHandler.removeCallbacks(mConnectionRunnable);
|
||||||
|
disconnectFromLauncherService();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,186 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2023 The Android Open Source Project
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.android.systemui.recents
|
||||||
|
|
||||||
|
import android.content.ComponentName
|
||||||
|
import android.content.pm.PackageManager
|
||||||
|
import android.content.pm.ResolveInfo
|
||||||
|
import android.testing.AndroidTestingRunner
|
||||||
|
import android.testing.TestableContext
|
||||||
|
import android.testing.TestableLooper
|
||||||
|
import androidx.test.filters.SmallTest
|
||||||
|
import com.android.internal.app.AssistUtils
|
||||||
|
import com.android.internal.logging.UiEventLogger
|
||||||
|
import com.android.systemui.SysuiTestCase
|
||||||
|
import com.android.systemui.dagger.qualifiers.Main
|
||||||
|
import com.android.systemui.dump.DumpManager
|
||||||
|
import com.android.systemui.keyguard.KeyguardUnlockAnimationController
|
||||||
|
import com.android.systemui.keyguard.ScreenLifecycle
|
||||||
|
import com.android.systemui.model.SysUiState
|
||||||
|
import com.android.systemui.navigationbar.NavigationBarController
|
||||||
|
import com.android.systemui.navigationbar.NavigationModeController
|
||||||
|
import com.android.systemui.recents.OverviewProxyService.ACTION_QUICKSTEP
|
||||||
|
import com.android.systemui.settings.FakeDisplayTracker
|
||||||
|
import com.android.systemui.settings.UserTracker
|
||||||
|
import com.android.systemui.shared.recents.IOverviewProxy
|
||||||
|
import com.android.systemui.shared.system.QuickStepContract.SCREEN_STATE_OFF
|
||||||
|
import com.android.systemui.shared.system.QuickStepContract.SCREEN_STATE_ON
|
||||||
|
import com.android.systemui.shared.system.QuickStepContract.SCREEN_STATE_TURNING_OFF
|
||||||
|
import com.android.systemui.shared.system.QuickStepContract.SCREEN_STATE_TURNING_ON
|
||||||
|
import com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_SCREEN_STATE_MASK
|
||||||
|
import com.android.systemui.statusbar.CommandQueue
|
||||||
|
import com.android.systemui.statusbar.NotificationShadeWindowController
|
||||||
|
import com.android.systemui.statusbar.phone.CentralSurfaces
|
||||||
|
import com.android.systemui.unfold.progress.UnfoldTransitionProgressForwarder
|
||||||
|
import com.android.systemui.util.mockito.whenever
|
||||||
|
import com.android.wm.shell.sysui.ShellInterface
|
||||||
|
import com.google.common.util.concurrent.MoreExecutors
|
||||||
|
import dagger.Lazy
|
||||||
|
import java.util.Optional
|
||||||
|
import java.util.concurrent.Executor
|
||||||
|
import org.junit.After
|
||||||
|
import org.junit.Before
|
||||||
|
import org.junit.Test
|
||||||
|
import org.junit.runner.RunWith
|
||||||
|
import org.mockito.ArgumentMatchers
|
||||||
|
import org.mockito.Mock
|
||||||
|
import org.mockito.Mockito.any
|
||||||
|
import org.mockito.Mockito.anyInt
|
||||||
|
import org.mockito.Mockito.intThat
|
||||||
|
import org.mockito.Mockito.mock
|
||||||
|
import org.mockito.Mockito.verify
|
||||||
|
import org.mockito.MockitoAnnotations
|
||||||
|
|
||||||
|
@SmallTest
|
||||||
|
@RunWith(AndroidTestingRunner::class)
|
||||||
|
@TestableLooper.RunWithLooper(setAsMainLooper = true)
|
||||||
|
class OverviewProxyServiceTest : SysuiTestCase() {
|
||||||
|
|
||||||
|
@Main private val executor: Executor = MoreExecutors.directExecutor()
|
||||||
|
|
||||||
|
private lateinit var subject: OverviewProxyService
|
||||||
|
private val dumpManager = DumpManager()
|
||||||
|
private val displayTracker = FakeDisplayTracker(mContext)
|
||||||
|
private val sysUiState = SysUiState(displayTracker)
|
||||||
|
private val screenLifecycle = ScreenLifecycle(dumpManager)
|
||||||
|
|
||||||
|
@Mock private lateinit var overviewProxy: IOverviewProxy.Stub
|
||||||
|
@Mock private lateinit var packageManager: PackageManager
|
||||||
|
|
||||||
|
// The following mocks belong to not-yet-tested parts of OverviewProxyService.
|
||||||
|
@Mock private lateinit var commandQueue: CommandQueue
|
||||||
|
@Mock private lateinit var shellInterface: ShellInterface
|
||||||
|
@Mock private lateinit var navBarController: NavigationBarController
|
||||||
|
@Mock private lateinit var centralSurfaces: CentralSurfaces
|
||||||
|
@Mock private lateinit var navModeController: NavigationModeController
|
||||||
|
@Mock private lateinit var statusBarWinController: NotificationShadeWindowController
|
||||||
|
@Mock private lateinit var userTracker: UserTracker
|
||||||
|
@Mock private lateinit var uiEventLogger: UiEventLogger
|
||||||
|
@Mock private lateinit var sysuiUnlockAnimationController: KeyguardUnlockAnimationController
|
||||||
|
@Mock private lateinit var assistUtils: AssistUtils
|
||||||
|
@Mock
|
||||||
|
private lateinit var unfoldTransitionProgressForwarder:
|
||||||
|
Optional<UnfoldTransitionProgressForwarder>
|
||||||
|
|
||||||
|
@Before
|
||||||
|
fun setUp() {
|
||||||
|
MockitoAnnotations.initMocks(this)
|
||||||
|
|
||||||
|
val serviceComponent = ComponentName("test_package", "service_provider")
|
||||||
|
context.addMockService(serviceComponent, overviewProxy)
|
||||||
|
context.addMockServiceResolver(
|
||||||
|
TestableContext.MockServiceResolver {
|
||||||
|
if (it.action == ACTION_QUICKSTEP) serviceComponent else null
|
||||||
|
}
|
||||||
|
)
|
||||||
|
whenever(overviewProxy.queryLocalInterface(ArgumentMatchers.anyString()))
|
||||||
|
.thenReturn(overviewProxy)
|
||||||
|
whenever(overviewProxy.asBinder()).thenReturn(overviewProxy)
|
||||||
|
|
||||||
|
// packageManager.resolveServiceAsUser has to return non-null for
|
||||||
|
// OverviewProxyService#isEnabled to become true.
|
||||||
|
context.setMockPackageManager(packageManager)
|
||||||
|
whenever(packageManager.resolveServiceAsUser(any(), anyInt(), anyInt()))
|
||||||
|
.thenReturn(mock(ResolveInfo::class.java))
|
||||||
|
|
||||||
|
subject =
|
||||||
|
OverviewProxyService(
|
||||||
|
context,
|
||||||
|
executor,
|
||||||
|
commandQueue,
|
||||||
|
shellInterface,
|
||||||
|
Lazy { navBarController },
|
||||||
|
Lazy { Optional.of(centralSurfaces) },
|
||||||
|
navModeController,
|
||||||
|
statusBarWinController,
|
||||||
|
sysUiState,
|
||||||
|
userTracker,
|
||||||
|
screenLifecycle,
|
||||||
|
uiEventLogger,
|
||||||
|
displayTracker,
|
||||||
|
sysuiUnlockAnimationController,
|
||||||
|
assistUtils,
|
||||||
|
dumpManager,
|
||||||
|
unfoldTransitionProgressForwarder
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
@After
|
||||||
|
fun tearDown() {
|
||||||
|
subject.shutdownForTest()
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `ScreenLifecycle - screenTurnedOn triggers SysUI state flag changes `() {
|
||||||
|
screenLifecycle.dispatchScreenTurnedOn()
|
||||||
|
|
||||||
|
verify(overviewProxy)
|
||||||
|
.onSystemUiStateChanged(
|
||||||
|
intThat { it and SYSUI_STATE_SCREEN_STATE_MASK == SCREEN_STATE_ON }
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `ScreenLifecycle - screenTurningOn triggers SysUI state flag changes `() {
|
||||||
|
screenLifecycle.dispatchScreenTurningOn()
|
||||||
|
|
||||||
|
verify(overviewProxy)
|
||||||
|
.onSystemUiStateChanged(
|
||||||
|
intThat { it and SYSUI_STATE_SCREEN_STATE_MASK == SCREEN_STATE_TURNING_ON }
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `ScreenLifecycle - screenTurnedOff triggers SysUI state flag changes `() {
|
||||||
|
screenLifecycle.dispatchScreenTurnedOff()
|
||||||
|
|
||||||
|
verify(overviewProxy)
|
||||||
|
.onSystemUiStateChanged(
|
||||||
|
intThat { it and SYSUI_STATE_SCREEN_STATE_MASK == SCREEN_STATE_OFF }
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `ScreenLifecycle - screenTurningOff triggers SysUI state flag changes `() {
|
||||||
|
screenLifecycle.dispatchScreenTurningOff()
|
||||||
|
|
||||||
|
verify(overviewProxy)
|
||||||
|
.onSystemUiStateChanged(
|
||||||
|
intThat { it and SYSUI_STATE_SCREEN_STATE_MASK == SCREEN_STATE_TURNING_OFF }
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -33,11 +33,15 @@ import android.provider.Settings;
|
|||||||
import android.util.ArrayMap;
|
import android.util.ArrayMap;
|
||||||
import android.view.LayoutInflater;
|
import android.view.LayoutInflater;
|
||||||
|
|
||||||
|
import androidx.annotation.Nullable;
|
||||||
|
|
||||||
import org.junit.rules.TestRule;
|
import org.junit.rules.TestRule;
|
||||||
import org.junit.rules.TestWatcher;
|
import org.junit.rules.TestWatcher;
|
||||||
import org.junit.runner.Description;
|
import org.junit.runner.Description;
|
||||||
import org.junit.runners.model.Statement;
|
import org.junit.runners.model.Statement;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A ContextWrapper with utilities specifically designed to make Testing easier.
|
* A ContextWrapper with utilities specifically designed to make Testing easier.
|
||||||
*
|
*
|
||||||
@@ -61,6 +65,7 @@ public class TestableContext extends ContextWrapper implements TestRule {
|
|||||||
private final TestableContentResolver mTestableContentResolver;
|
private final TestableContentResolver mTestableContentResolver;
|
||||||
private final TestableSettingsProvider mSettingsProvider;
|
private final TestableSettingsProvider mSettingsProvider;
|
||||||
|
|
||||||
|
private ArrayList<MockServiceResolver> mMockServiceResolvers;
|
||||||
private ArrayMap<String, Object> mMockSystemServices;
|
private ArrayMap<String, Object> mMockSystemServices;
|
||||||
private ArrayMap<ComponentName, IBinder> mMockServices;
|
private ArrayMap<ComponentName, IBinder> mMockServices;
|
||||||
private ArrayMap<ServiceConnection, ComponentName> mActiveServices;
|
private ArrayMap<ServiceConnection, ComponentName> mActiveServices;
|
||||||
@@ -214,25 +219,54 @@ public class TestableContext extends ContextWrapper implements TestRule {
|
|||||||
/**
|
/**
|
||||||
* Adds a mock service to be connected to by a bindService call.
|
* Adds a mock service to be connected to by a bindService call.
|
||||||
* <p>
|
* <p>
|
||||||
* Normally a TestableContext will pass through all bind requests to the base context
|
* Normally a TestableContext will pass through all bind requests to the base context
|
||||||
* but when addMockService has been called for a ComponentName being bound, then
|
* but when addMockService has been called for a ComponentName being bound, then
|
||||||
* TestableContext will immediately trigger a {@link ServiceConnection#onServiceConnected}
|
* TestableContext will immediately trigger a {@link ServiceConnection#onServiceConnected}
|
||||||
* with the specified service, and will call {@link ServiceConnection#onServiceDisconnected}
|
* with the specified service, and will call {@link ServiceConnection#onServiceDisconnected}
|
||||||
* when the service is unbound.
|
* when the service is unbound.
|
||||||
* </p>
|
* </p>
|
||||||
|
*
|
||||||
|
* @see #addMockServiceResolver(MockServiceResolver) for custom resolution of service Intents to
|
||||||
|
* ComponentNames
|
||||||
*/
|
*/
|
||||||
public void addMockService(ComponentName component, IBinder service) {
|
public void addMockService(ComponentName component, IBinder service) {
|
||||||
if (mMockServices == null) mMockServices = new ArrayMap<>();
|
if (mMockServices == null) mMockServices = new ArrayMap<>();
|
||||||
mMockServices.put(component, service);
|
mMockServices.put(component, service);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Strategy to resolve a service {@link Intent} to a mock service {@link ComponentName}.
|
||||||
|
*/
|
||||||
|
public interface MockServiceResolver {
|
||||||
|
@Nullable
|
||||||
|
ComponentName resolve(Intent service);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Registers a strategy to resolve service intents to registered mock services.
|
||||||
|
* <p>
|
||||||
|
* The result of the first {@link MockServiceResolver} to return a non-null
|
||||||
|
* {@link ComponentName} is used to look up a mock service. The mock service must be registered
|
||||||
|
* via {@link #addMockService(ComponentName, IBinder)} separately, using the same component
|
||||||
|
* name.
|
||||||
|
*
|
||||||
|
* If none of the resolvers return a non-null value, or the first returned component name
|
||||||
|
* does not link to a registered mock service, the bind requests are passed to the base context
|
||||||
|
*
|
||||||
|
* The resolvers are queried in order of registration.
|
||||||
|
*/
|
||||||
|
public void addMockServiceResolver(MockServiceResolver resolver) {
|
||||||
|
if (mMockServiceResolvers == null) mMockServiceResolvers = new ArrayList<>();
|
||||||
|
mMockServiceResolvers.add(resolver);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @see #addMockService(ComponentName, IBinder)
|
* @see #addMockService(ComponentName, IBinder)
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public boolean bindService(Intent service, ServiceConnection conn, int flags) {
|
public boolean bindService(Intent service, ServiceConnection conn, int flags) {
|
||||||
if (mService != null) mService.getLeakInfo(conn).addAllocation(new Throwable());
|
if (mService != null) mService.getLeakInfo(conn).addAllocation(new Throwable());
|
||||||
if (checkMocks(service.getComponent(), conn)) return true;
|
if (checkMocks(service, conn)) return true;
|
||||||
return super.bindService(service, conn, flags);
|
return super.bindService(service, conn, flags);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -243,7 +277,7 @@ public class TestableContext extends ContextWrapper implements TestRule {
|
|||||||
public boolean bindServiceAsUser(Intent service, ServiceConnection conn, int flags,
|
public boolean bindServiceAsUser(Intent service, ServiceConnection conn, int flags,
|
||||||
Handler handler, UserHandle user) {
|
Handler handler, UserHandle user) {
|
||||||
if (mService != null) mService.getLeakInfo(conn).addAllocation(new Throwable());
|
if (mService != null) mService.getLeakInfo(conn).addAllocation(new Throwable());
|
||||||
if (checkMocks(service.getComponent(), conn)) return true;
|
if (checkMocks(service, conn)) return true;
|
||||||
return super.bindServiceAsUser(service, conn, flags, handler, user);
|
return super.bindServiceAsUser(service, conn, flags, handler, user);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -254,18 +288,36 @@ public class TestableContext extends ContextWrapper implements TestRule {
|
|||||||
public boolean bindServiceAsUser(Intent service, ServiceConnection conn, int flags,
|
public boolean bindServiceAsUser(Intent service, ServiceConnection conn, int flags,
|
||||||
UserHandle user) {
|
UserHandle user) {
|
||||||
if (mService != null) mService.getLeakInfo(conn).addAllocation(new Throwable());
|
if (mService != null) mService.getLeakInfo(conn).addAllocation(new Throwable());
|
||||||
if (checkMocks(service.getComponent(), conn)) return true;
|
if (checkMocks(service, conn)) return true;
|
||||||
return super.bindServiceAsUser(service, conn, flags, user);
|
return super.bindServiceAsUser(service, conn, flags, user);
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean checkMocks(ComponentName component, ServiceConnection conn) {
|
private boolean checkMocks(Intent service, ServiceConnection conn) {
|
||||||
if (mMockServices != null && component != null && mMockServices.containsKey(component)) {
|
if (mMockServices == null) return false;
|
||||||
if (mActiveServices == null) mActiveServices = new ArrayMap<>();
|
|
||||||
mActiveServices.put(conn, component);
|
ComponentName serviceComponent = resolveMockServiceComponent(service);
|
||||||
conn.onServiceConnected(component, mMockServices.get(component));
|
if (serviceComponent == null) return false;
|
||||||
return true;
|
|
||||||
|
IBinder serviceImpl = mMockServices.get(serviceComponent);
|
||||||
|
if (serviceImpl == null) return false;
|
||||||
|
|
||||||
|
if (mActiveServices == null) mActiveServices = new ArrayMap<>();
|
||||||
|
mActiveServices.put(conn, serviceComponent);
|
||||||
|
conn.onServiceConnected(serviceComponent, serviceImpl);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private ComponentName resolveMockServiceComponent(Intent service) {
|
||||||
|
ComponentName specifiedComponentName = service.getComponent();
|
||||||
|
if (specifiedComponentName != null) return specifiedComponentName;
|
||||||
|
|
||||||
|
if (mMockServiceResolvers == null) return null;
|
||||||
|
|
||||||
|
for (MockServiceResolver resolver : mMockServiceResolvers) {
|
||||||
|
ComponentName resolvedComponent = resolver.resolve(service);
|
||||||
|
if (resolvedComponent != null) return resolvedComponent;
|
||||||
}
|
}
|
||||||
return false;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
Reference in New Issue
Block a user