Add some fakes to SystemUI-test-utils am: e496b54c21
Original change: https://googleplex-android-review.googlesource.com/c/platform/frameworks/base/+/19675484 Change-Id: Idb853d64168c87cf4dbd8e3006dd3283ec90745e Signed-off-by: Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
This commit is contained in:
committed by
Automerger Merge Worker
commit
5e09b18dc6
@@ -68,9 +68,73 @@ import java.util.Objects
|
||||
import java.util.concurrent.Executor
|
||||
import javax.inject.Inject
|
||||
import kotlin.math.max
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
import kotlinx.coroutines.flow.asStateFlow
|
||||
|
||||
/** A controller for the dealing with services running in the foreground. */
|
||||
interface FgsManagerController {
|
||||
/** Whether the TaskManager (and therefore this controller) is actually available. */
|
||||
val isAvailable: StateFlow<Boolean>
|
||||
|
||||
/** The number of packages with a service running in the foreground. */
|
||||
val numRunningPackages: Int
|
||||
|
||||
/**
|
||||
* Whether there were new changes to the foreground services since the last [shown][showDialog]
|
||||
* dialog was dismissed.
|
||||
*/
|
||||
val newChangesSinceDialogWasDismissed: Boolean
|
||||
|
||||
/**
|
||||
* Whether we should show a dot to indicate when [newChangesSinceDialogWasDismissed] is true.
|
||||
*/
|
||||
val showFooterDot: StateFlow<Boolean>
|
||||
|
||||
/**
|
||||
* Initialize this controller. This should be called once, before this controller is used for
|
||||
* the first time.
|
||||
*/
|
||||
fun init()
|
||||
|
||||
/**
|
||||
* Show the foreground services dialog. The dialog will be expanded from [viewLaunchedFrom] if
|
||||
* it's not `null`.
|
||||
*/
|
||||
fun showDialog(viewLaunchedFrom: View?)
|
||||
|
||||
/** Add a [OnNumberOfPackagesChangedListener]. */
|
||||
fun addOnNumberOfPackagesChangedListener(listener: OnNumberOfPackagesChangedListener)
|
||||
|
||||
/** Remove a [OnNumberOfPackagesChangedListener]. */
|
||||
fun removeOnNumberOfPackagesChangedListener(listener: OnNumberOfPackagesChangedListener)
|
||||
|
||||
/** Add a [OnDialogDismissedListener]. */
|
||||
fun addOnDialogDismissedListener(listener: OnDialogDismissedListener)
|
||||
|
||||
/** Remove a [OnDialogDismissedListener]. */
|
||||
fun removeOnDialogDismissedListener(listener: OnDialogDismissedListener)
|
||||
|
||||
/** Whether we should update the footer visibility. */
|
||||
// TODO(b/242040009): Remove this.
|
||||
fun shouldUpdateFooterVisibility(): Boolean
|
||||
|
||||
@VisibleForTesting
|
||||
fun visibleButtonsCount(): Int
|
||||
|
||||
interface OnNumberOfPackagesChangedListener {
|
||||
/** Called when [numRunningPackages] changed. */
|
||||
fun onNumberOfPackagesChanged(numPackages: Int)
|
||||
}
|
||||
|
||||
interface OnDialogDismissedListener {
|
||||
/** Called when a dialog shown using [showDialog] was dismissed. */
|
||||
fun onDialogDismissed()
|
||||
}
|
||||
}
|
||||
|
||||
@SysUISingleton
|
||||
class FgsManagerController @Inject constructor(
|
||||
class FgsManagerControllerImpl @Inject constructor(
|
||||
private val context: Context,
|
||||
@Main private val mainExecutor: Executor,
|
||||
@Background private val backgroundExecutor: Executor,
|
||||
@@ -82,25 +146,32 @@ class FgsManagerController @Inject constructor(
|
||||
private val dialogLaunchAnimator: DialogLaunchAnimator,
|
||||
private val broadcastDispatcher: BroadcastDispatcher,
|
||||
private val dumpManager: DumpManager
|
||||
) : IForegroundServiceObserver.Stub(), Dumpable {
|
||||
) : IForegroundServiceObserver.Stub(), Dumpable, FgsManagerController {
|
||||
|
||||
companion object {
|
||||
private const val INTERACTION_JANK_TAG = "active_background_apps"
|
||||
private val LOG_TAG = FgsManagerController::class.java.simpleName
|
||||
private const val DEFAULT_TASK_MANAGER_ENABLED = true
|
||||
private const val DEFAULT_TASK_MANAGER_SHOW_FOOTER_DOT = false
|
||||
private const val DEFAULT_TASK_MANAGER_SHOW_STOP_BUTTON_FOR_USER_ALLOWLISTED_APPS = true
|
||||
}
|
||||
|
||||
var changesSinceDialog = false
|
||||
override var newChangesSinceDialogWasDismissed = false
|
||||
private set
|
||||
|
||||
var isAvailable = false
|
||||
private set
|
||||
var showFooterDot = false
|
||||
private set
|
||||
var showStopBtnForUserAllowlistedApps = false
|
||||
private set
|
||||
val _isAvailable = MutableStateFlow(false)
|
||||
override val isAvailable: StateFlow<Boolean> = _isAvailable.asStateFlow()
|
||||
|
||||
val _showFooterDot = MutableStateFlow(false)
|
||||
override val showFooterDot: StateFlow<Boolean> = _showFooterDot.asStateFlow()
|
||||
|
||||
private var showStopBtnForUserAllowlistedApps = false
|
||||
|
||||
override val numRunningPackages: Int
|
||||
get() {
|
||||
synchronized(lock) {
|
||||
return getNumVisiblePackagesLocked()
|
||||
}
|
||||
}
|
||||
|
||||
private val lock = Any()
|
||||
|
||||
@@ -138,15 +209,7 @@ class FgsManagerController @Inject constructor(
|
||||
}
|
||||
}
|
||||
|
||||
interface OnNumberOfPackagesChangedListener {
|
||||
fun onNumberOfPackagesChanged(numPackages: Int)
|
||||
}
|
||||
|
||||
interface OnDialogDismissedListener {
|
||||
fun onDialogDismissed()
|
||||
}
|
||||
|
||||
fun init() {
|
||||
override fun init() {
|
||||
synchronized(lock) {
|
||||
if (initialized) {
|
||||
return
|
||||
@@ -165,19 +228,19 @@ class FgsManagerController @Inject constructor(
|
||||
NAMESPACE_SYSTEMUI,
|
||||
backgroundExecutor
|
||||
) {
|
||||
isAvailable = it.getBoolean(TASK_MANAGER_ENABLED, isAvailable)
|
||||
showFooterDot =
|
||||
it.getBoolean(TASK_MANAGER_SHOW_FOOTER_DOT, showFooterDot)
|
||||
_isAvailable.value = it.getBoolean(TASK_MANAGER_ENABLED, _isAvailable.value)
|
||||
_showFooterDot.value =
|
||||
it.getBoolean(TASK_MANAGER_SHOW_FOOTER_DOT, _showFooterDot.value)
|
||||
showStopBtnForUserAllowlistedApps = it.getBoolean(
|
||||
TASK_MANAGER_SHOW_STOP_BUTTON_FOR_USER_ALLOWLISTED_APPS,
|
||||
showStopBtnForUserAllowlistedApps)
|
||||
}
|
||||
|
||||
isAvailable = deviceConfigProxy.getBoolean(
|
||||
_isAvailable.value = deviceConfigProxy.getBoolean(
|
||||
NAMESPACE_SYSTEMUI,
|
||||
TASK_MANAGER_ENABLED, DEFAULT_TASK_MANAGER_ENABLED
|
||||
)
|
||||
showFooterDot = deviceConfigProxy.getBoolean(
|
||||
_showFooterDot.value = deviceConfigProxy.getBoolean(
|
||||
NAMESPACE_SYSTEMUI,
|
||||
TASK_MANAGER_SHOW_FOOTER_DOT, DEFAULT_TASK_MANAGER_SHOW_FOOTER_DOT
|
||||
)
|
||||
@@ -232,42 +295,45 @@ class FgsManagerController @Inject constructor(
|
||||
}
|
||||
|
||||
@GuardedBy("lock")
|
||||
val onNumberOfPackagesChangedListeners: MutableSet<OnNumberOfPackagesChangedListener> =
|
||||
mutableSetOf()
|
||||
private val onNumberOfPackagesChangedListeners =
|
||||
mutableSetOf<FgsManagerController.OnNumberOfPackagesChangedListener>()
|
||||
|
||||
@GuardedBy("lock")
|
||||
val onDialogDismissedListeners: MutableSet<OnDialogDismissedListener> = mutableSetOf()
|
||||
private val onDialogDismissedListeners =
|
||||
mutableSetOf<FgsManagerController.OnDialogDismissedListener>()
|
||||
|
||||
fun addOnNumberOfPackagesChangedListener(listener: OnNumberOfPackagesChangedListener) {
|
||||
override fun addOnNumberOfPackagesChangedListener(
|
||||
listener: FgsManagerController.OnNumberOfPackagesChangedListener
|
||||
) {
|
||||
synchronized(lock) {
|
||||
onNumberOfPackagesChangedListeners.add(listener)
|
||||
}
|
||||
}
|
||||
|
||||
fun removeOnNumberOfPackagesChangedListener(listener: OnNumberOfPackagesChangedListener) {
|
||||
override fun removeOnNumberOfPackagesChangedListener(
|
||||
listener: FgsManagerController.OnNumberOfPackagesChangedListener
|
||||
) {
|
||||
synchronized(lock) {
|
||||
onNumberOfPackagesChangedListeners.remove(listener)
|
||||
}
|
||||
}
|
||||
|
||||
fun addOnDialogDismissedListener(listener: OnDialogDismissedListener) {
|
||||
override fun addOnDialogDismissedListener(
|
||||
listener: FgsManagerController.OnDialogDismissedListener
|
||||
) {
|
||||
synchronized(lock) {
|
||||
onDialogDismissedListeners.add(listener)
|
||||
}
|
||||
}
|
||||
|
||||
fun removeOnDialogDismissedListener(listener: OnDialogDismissedListener) {
|
||||
override fun removeOnDialogDismissedListener(
|
||||
listener: FgsManagerController.OnDialogDismissedListener
|
||||
) {
|
||||
synchronized(lock) {
|
||||
onDialogDismissedListeners.remove(listener)
|
||||
}
|
||||
}
|
||||
|
||||
fun getNumRunningPackages(): Int {
|
||||
synchronized(lock) {
|
||||
return getNumVisiblePackagesLocked()
|
||||
}
|
||||
}
|
||||
|
||||
private fun getNumVisiblePackagesLocked(): Int {
|
||||
return runningServiceTokens.keys.count {
|
||||
it.uiControl != UIControl.HIDE_ENTRY && currentProfileIds.contains(it.userId)
|
||||
@@ -278,7 +344,7 @@ class FgsManagerController @Inject constructor(
|
||||
val num = getNumVisiblePackagesLocked()
|
||||
if (num != lastNumberOfVisiblePackages) {
|
||||
lastNumberOfVisiblePackages = num
|
||||
changesSinceDialog = true
|
||||
newChangesSinceDialogWasDismissed = true
|
||||
onNumberOfPackagesChangedListeners.forEach {
|
||||
backgroundExecutor.execute {
|
||||
it.onNumberOfPackagesChanged(num)
|
||||
@@ -287,9 +353,7 @@ class FgsManagerController @Inject constructor(
|
||||
}
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
@JvmName("getNumVisibleButtons")
|
||||
internal fun getNumVisibleButtons(): Int {
|
||||
override fun visibleButtonsCount(): Int {
|
||||
synchronized(lock) {
|
||||
return getNumVisibleButtonsLocked()
|
||||
}
|
||||
@@ -301,9 +365,9 @@ class FgsManagerController @Inject constructor(
|
||||
}
|
||||
}
|
||||
|
||||
fun shouldUpdateFooterVisibility() = dialog == null
|
||||
override fun shouldUpdateFooterVisibility() = dialog == null
|
||||
|
||||
fun showDialog(viewLaunchedFrom: View?) {
|
||||
override fun showDialog(viewLaunchedFrom: View?) {
|
||||
synchronized(lock) {
|
||||
if (dialog == null) {
|
||||
|
||||
@@ -328,7 +392,7 @@ class FgsManagerController @Inject constructor(
|
||||
this.dialog = dialog
|
||||
|
||||
dialog.setOnDismissListener {
|
||||
changesSinceDialog = false
|
||||
newChangesSinceDialogWasDismissed = false
|
||||
synchronized(lock) {
|
||||
this.dialog = null
|
||||
updateAppItemsLocked()
|
||||
@@ -656,7 +720,7 @@ class FgsManagerController @Inject constructor(
|
||||
val pw = IndentingPrintWriter(printwriter)
|
||||
synchronized(lock) {
|
||||
pw.println("current user profiles = $currentProfileIds")
|
||||
pw.println("changesSinceDialog=$changesSinceDialog")
|
||||
pw.println("newChangesSinceDialogWasShown=$newChangesSinceDialogWasDismissed")
|
||||
pw.println("Running service tokens: [")
|
||||
pw.indentIfPossible {
|
||||
runningServiceTokens.forEach { (userPackage, startTimeAndTokens) ->
|
||||
|
||||
@@ -149,9 +149,11 @@ public class QSFgsManagerFooter implements View.OnClickListener,
|
||||
mNumberView.setContentDescription(text);
|
||||
if (mFgsManagerController.shouldUpdateFooterVisibility()) {
|
||||
mRootView.setVisibility(mNumPackages > 0
|
||||
&& mFgsManagerController.isAvailable() ? View.VISIBLE : View.GONE);
|
||||
int dotVis = mFgsManagerController.getShowFooterDot()
|
||||
&& mFgsManagerController.getChangesSinceDialog() ? View.VISIBLE : View.GONE;
|
||||
&& mFgsManagerController.isAvailable().getValue() ? View.VISIBLE
|
||||
: View.GONE);
|
||||
int dotVis = mFgsManagerController.getShowFooterDot().getValue()
|
||||
&& mFgsManagerController.getNewChangesSinceDialogWasDismissed()
|
||||
? View.VISIBLE : View.GONE;
|
||||
mDotView.setVisibility(dotVis);
|
||||
mCollapsedDotView.setVisibility(dotVis);
|
||||
if (mVisibilityChangedListener != null) {
|
||||
|
||||
@@ -28,6 +28,8 @@ import com.android.systemui.battery.BatteryMeterView;
|
||||
import com.android.systemui.dagger.qualifiers.RootView;
|
||||
import com.android.systemui.plugins.qs.QS;
|
||||
import com.android.systemui.privacy.OngoingPrivacyChip;
|
||||
import com.android.systemui.qs.FgsManagerController;
|
||||
import com.android.systemui.qs.FgsManagerControllerImpl;
|
||||
import com.android.systemui.qs.FooterActionsView;
|
||||
import com.android.systemui.qs.QSContainerImpl;
|
||||
import com.android.systemui.qs.QSFooter;
|
||||
@@ -194,4 +196,8 @@ public interface QSFragmentModule {
|
||||
) {
|
||||
return layoutInflater.inflate(R.layout.fgs_footer, footerActionsView, false);
|
||||
}
|
||||
|
||||
/** */
|
||||
@Binds
|
||||
FgsManagerController bindFgsManagerController(FgsManagerControllerImpl impl);
|
||||
}
|
||||
@@ -188,9 +188,9 @@ public class FgsManagerControllerTest extends SysuiTestCase {
|
||||
public void testChangesSinceLastDialog() throws RemoteException {
|
||||
setUserProfiles(0);
|
||||
|
||||
Assert.assertFalse(mFmc.getChangesSinceDialog());
|
||||
Assert.assertFalse(mFmc.getNewChangesSinceDialogWasDismissed());
|
||||
mIForegroundServiceObserver.onForegroundStateChanged(new Binder(), "pkg", 0, true);
|
||||
Assert.assertTrue(mFmc.getChangesSinceDialog());
|
||||
Assert.assertTrue(mFmc.getNewChangesSinceDialogWasDismissed());
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -233,14 +233,14 @@ public class FgsManagerControllerTest extends SysuiTestCase {
|
||||
final Binder binder = new Binder();
|
||||
setShowStopButtonForUserAllowlistedApps(true);
|
||||
mIForegroundServiceObserver.onForegroundStateChanged(binder, "pkg", 0, true);
|
||||
Assert.assertEquals(1, mFmc.getNumVisibleButtons());
|
||||
Assert.assertEquals(1, mFmc.visibleButtonsCount());
|
||||
|
||||
mIForegroundServiceObserver.onForegroundStateChanged(binder, "pkg", 0, false);
|
||||
Assert.assertEquals(0, mFmc.getNumVisibleButtons());
|
||||
Assert.assertEquals(0, mFmc.visibleButtonsCount());
|
||||
|
||||
setShowStopButtonForUserAllowlistedApps(false);
|
||||
mIForegroundServiceObserver.onForegroundStateChanged(binder, "pkg", 0, true);
|
||||
Assert.assertEquals(0, mFmc.getNumVisibleButtons());
|
||||
Assert.assertEquals(0, mFmc.visibleButtonsCount());
|
||||
}
|
||||
|
||||
private void setShowStopButtonForUserAllowlistedApps(boolean enable) {
|
||||
@@ -269,7 +269,7 @@ public class FgsManagerControllerTest extends SysuiTestCase {
|
||||
ArgumentCaptor<BroadcastReceiver> showFgsManagerReceiverArgumentCaptor =
|
||||
ArgumentCaptor.forClass(BroadcastReceiver.class);
|
||||
|
||||
FgsManagerController result = new FgsManagerController(
|
||||
FgsManagerController result = new FgsManagerControllerImpl(
|
||||
mContext,
|
||||
mMainExecutor,
|
||||
mBackgroundExecutor,
|
||||
|
||||
@@ -56,7 +56,6 @@ class FakeFeatureFlags : FeatureFlags {
|
||||
stringFlags.put(flag.id, value)
|
||||
}
|
||||
|
||||
|
||||
override fun isEnabled(flag: UnreleasedFlag): Boolean = requireBooleanValue(flag.id)
|
||||
|
||||
override fun isEnabled(flag: ReleasedFlag): Boolean = requireBooleanValue(flag.id)
|
||||
@@ -0,0 +1,80 @@
|
||||
/*
|
||||
* Copyright (C) 2022 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.qs
|
||||
|
||||
import android.view.View
|
||||
import com.android.systemui.qs.FgsManagerController.OnDialogDismissedListener
|
||||
import com.android.systemui.qs.FgsManagerController.OnNumberOfPackagesChangedListener
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
|
||||
/** A fake [FgsManagerController] to be used in tests. */
|
||||
class FakeFgsManagerController(
|
||||
isAvailable: Boolean = true,
|
||||
showFooterDot: Boolean = false,
|
||||
numRunningPackages: Int = 0,
|
||||
) : FgsManagerController {
|
||||
override val isAvailable: MutableStateFlow<Boolean> = MutableStateFlow(isAvailable)
|
||||
|
||||
override var numRunningPackages = numRunningPackages
|
||||
set(value) {
|
||||
if (value != field) {
|
||||
field = value
|
||||
newChangesSinceDialogWasDismissed = true
|
||||
numRunningPackagesListeners.forEach { it.onNumberOfPackagesChanged(value) }
|
||||
}
|
||||
}
|
||||
|
||||
override var newChangesSinceDialogWasDismissed = false
|
||||
private set
|
||||
|
||||
override val showFooterDot: MutableStateFlow<Boolean> = MutableStateFlow(showFooterDot)
|
||||
|
||||
private val numRunningPackagesListeners = LinkedHashSet<OnNumberOfPackagesChangedListener>()
|
||||
private val dialogDismissedListeners = LinkedHashSet<OnDialogDismissedListener>()
|
||||
|
||||
/** Simulate that a fgs dialog was just dismissed. */
|
||||
fun simulateDialogDismiss() {
|
||||
newChangesSinceDialogWasDismissed = false
|
||||
dialogDismissedListeners.forEach { it.onDialogDismissed() }
|
||||
}
|
||||
|
||||
override fun init() {}
|
||||
|
||||
override fun showDialog(viewLaunchedFrom: View?) {}
|
||||
|
||||
override fun addOnNumberOfPackagesChangedListener(listener: OnNumberOfPackagesChangedListener) {
|
||||
numRunningPackagesListeners.add(listener)
|
||||
}
|
||||
|
||||
override fun removeOnNumberOfPackagesChangedListener(
|
||||
listener: OnNumberOfPackagesChangedListener
|
||||
) {
|
||||
numRunningPackagesListeners.remove(listener)
|
||||
}
|
||||
|
||||
override fun addOnDialogDismissedListener(listener: OnDialogDismissedListener) {
|
||||
dialogDismissedListeners.add(listener)
|
||||
}
|
||||
|
||||
override fun removeOnDialogDismissedListener(listener: OnDialogDismissedListener) {
|
||||
dialogDismissedListeners.remove(listener)
|
||||
}
|
||||
|
||||
override fun shouldUpdateFooterVisibility(): Boolean = false
|
||||
|
||||
override fun visibleButtonsCount(): Int = 0
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
/*
|
||||
* Copyright (C) 2022 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.settings
|
||||
|
||||
import android.content.ContentResolver
|
||||
import android.content.Context
|
||||
import android.content.pm.UserInfo
|
||||
import android.os.UserHandle
|
||||
import android.test.mock.MockContentResolver
|
||||
import com.android.systemui.util.mockito.mock
|
||||
import java.util.concurrent.Executor
|
||||
|
||||
/** A fake [UserTracker] to be used in tests. */
|
||||
class FakeUserTracker(
|
||||
userId: Int = 0,
|
||||
userHandle: UserHandle = UserHandle.of(userId),
|
||||
userInfo: UserInfo = mock(),
|
||||
userProfiles: List<UserInfo> = emptyList(),
|
||||
userContentResolver: ContentResolver = MockContentResolver(),
|
||||
userContext: Context = mock(),
|
||||
private val onCreateCurrentUserContext: (Context) -> Context = { mock() },
|
||||
) : UserTracker {
|
||||
val callbacks = mutableListOf<UserTracker.Callback>()
|
||||
|
||||
override val userId: Int = userId
|
||||
override val userHandle: UserHandle = userHandle
|
||||
override val userInfo: UserInfo = userInfo
|
||||
override val userProfiles: List<UserInfo> = userProfiles
|
||||
|
||||
override val userContentResolver: ContentResolver = userContentResolver
|
||||
override val userContext: Context = userContext
|
||||
|
||||
override fun addCallback(callback: UserTracker.Callback, executor: Executor) {
|
||||
callbacks.add(callback)
|
||||
}
|
||||
|
||||
override fun removeCallback(callback: UserTracker.Callback) {
|
||||
callbacks.remove(callback)
|
||||
}
|
||||
|
||||
override fun createCurrentUserContext(context: Context): Context {
|
||||
return onCreateCurrentUserContext(context)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,118 @@
|
||||
/*
|
||||
* Copyright (C) 2022 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.statusbar.policy
|
||||
|
||||
import android.app.admin.DeviceAdminInfo
|
||||
import android.content.ComponentName
|
||||
import android.graphics.drawable.Drawable
|
||||
import java.io.PrintWriter
|
||||
|
||||
/** A fake [SecurityController] to be used in tests. */
|
||||
class FakeSecurityController(
|
||||
private val fakeState: FakeState = FakeState(),
|
||||
) : SecurityController {
|
||||
private val callbacks = LinkedHashSet<SecurityController.SecurityControllerCallback>()
|
||||
|
||||
override fun addCallback(callback: SecurityController.SecurityControllerCallback) {
|
||||
callbacks.add(callback)
|
||||
}
|
||||
|
||||
override fun removeCallback(callback: SecurityController.SecurityControllerCallback) {
|
||||
callbacks.remove(callback)
|
||||
}
|
||||
|
||||
/** Update [fakeState], then notify the callbacks. */
|
||||
fun updateState(f: FakeState.() -> Unit) {
|
||||
fakeState.f()
|
||||
callbacks.forEach { it.onStateChanged() }
|
||||
}
|
||||
|
||||
override fun dump(pw: PrintWriter, args: Array<out String>) {}
|
||||
|
||||
override fun isDeviceManaged(): Boolean = fakeState.isDeviceManaged
|
||||
|
||||
override fun hasProfileOwner(): Boolean = fakeState.hasProfileOwner
|
||||
|
||||
override fun hasWorkProfile(): Boolean = fakeState.hasWorkProfile
|
||||
|
||||
override fun isWorkProfileOn(): Boolean = fakeState.isWorkProfileOn
|
||||
|
||||
override fun isProfileOwnerOfOrganizationOwnedDevice(): Boolean =
|
||||
fakeState.isProfileOwnerOfOrganizationOwnedDevice
|
||||
|
||||
override fun getDeviceOwnerName(): String? = fakeState.deviceOwnerName
|
||||
|
||||
override fun getProfileOwnerName(): String? = fakeState.profileOwnerName
|
||||
|
||||
override fun getDeviceOwnerOrganizationName(): String? = fakeState.deviceOwnerOrganizationName
|
||||
|
||||
override fun getWorkProfileOrganizationName(): String? = fakeState.workProfileOrganizationName
|
||||
|
||||
override fun getDeviceOwnerComponentOnAnyUser(): ComponentName? =
|
||||
fakeState.deviceOwnerComponentOnAnyUser
|
||||
|
||||
override fun getDeviceOwnerType(admin: ComponentName?): Int = 0
|
||||
|
||||
override fun isNetworkLoggingEnabled(): Boolean = fakeState.isNetworkLoggingEnabled
|
||||
|
||||
override fun isVpnEnabled(): Boolean = fakeState.isVpnEnabled
|
||||
|
||||
override fun isVpnRestricted(): Boolean = fakeState.isVpnRestricted
|
||||
|
||||
override fun isVpnBranded(): Boolean = fakeState.isVpnBranded
|
||||
|
||||
override fun getPrimaryVpnName(): String? = fakeState.primaryVpnName
|
||||
|
||||
override fun getWorkProfileVpnName(): String? = fakeState.workProfileVpnName
|
||||
|
||||
override fun hasCACertInCurrentUser(): Boolean = fakeState.hasCACertInCurrentUser
|
||||
|
||||
override fun hasCACertInWorkProfile(): Boolean = fakeState.hasCACertInWorkProfile
|
||||
|
||||
override fun onUserSwitched(newUserId: Int) {}
|
||||
|
||||
override fun isParentalControlsEnabled(): Boolean = fakeState.isParentalControlsEnabled
|
||||
|
||||
override fun getDeviceAdminInfo(): DeviceAdminInfo? = fakeState.deviceAdminInfo
|
||||
|
||||
override fun getIcon(info: DeviceAdminInfo?): Drawable? = null
|
||||
|
||||
override fun getLabel(info: DeviceAdminInfo?): CharSequence? = null
|
||||
|
||||
class FakeState(
|
||||
var isDeviceManaged: Boolean = false,
|
||||
var hasProfileOwner: Boolean = false,
|
||||
var hasWorkProfile: Boolean = false,
|
||||
var isWorkProfileOn: Boolean = false,
|
||||
var isProfileOwnerOfOrganizationOwnedDevice: Boolean = false,
|
||||
var deviceOwnerName: String? = null,
|
||||
var profileOwnerName: String? = null,
|
||||
var deviceOwnerOrganizationName: String? = null,
|
||||
var workProfileOrganizationName: String? = null,
|
||||
var deviceOwnerComponentOnAnyUser: ComponentName? = null,
|
||||
var isNetworkLoggingEnabled: Boolean = false,
|
||||
var isVpnEnabled: Boolean = false,
|
||||
var isVpnRestricted: Boolean = false,
|
||||
var isVpnBranded: Boolean = false,
|
||||
var primaryVpnName: String? = null,
|
||||
var workProfileVpnName: String? = null,
|
||||
var hasCACertInCurrentUser: Boolean = false,
|
||||
var hasCACertInWorkProfile: Boolean = false,
|
||||
var isParentalControlsEnabled: Boolean = false,
|
||||
var deviceAdminInfo: DeviceAdminInfo? = null,
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
/*
|
||||
* Copyright (C) 2022 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.statusbar.policy
|
||||
|
||||
import android.graphics.drawable.Drawable
|
||||
import com.android.systemui.util.mockito.mock
|
||||
|
||||
/** A fake [UserInfoController] to be used in tests. */
|
||||
class FakeUserInfoController(
|
||||
private val fakeInfo: FakeInfo = FakeInfo(),
|
||||
) : UserInfoController {
|
||||
private val listeners = LinkedHashSet<UserInfoController.OnUserInfoChangedListener>()
|
||||
|
||||
/** Update [fakeInfo], then notify the listeners. */
|
||||
fun updateInfo(f: FakeInfo.() -> Unit) {
|
||||
fakeInfo.f()
|
||||
notifyListeners()
|
||||
}
|
||||
|
||||
private fun notifyListeners() {
|
||||
listeners.forEach { listener ->
|
||||
listener.onUserInfoChanged(fakeInfo.name, fakeInfo.picture, fakeInfo.userAccount)
|
||||
}
|
||||
}
|
||||
|
||||
override fun addCallback(listener: UserInfoController.OnUserInfoChangedListener) {
|
||||
listeners.add(listener)
|
||||
|
||||
// The actual implementation notifies the listener when adding it.
|
||||
listener.onUserInfoChanged(fakeInfo.name, fakeInfo.picture, fakeInfo.userAccount)
|
||||
}
|
||||
|
||||
override fun removeCallback(listener: UserInfoController.OnUserInfoChangedListener) {
|
||||
listeners.remove(listener)
|
||||
}
|
||||
|
||||
override fun reloadUserInfo() {}
|
||||
|
||||
class FakeInfo(
|
||||
var name: String = "",
|
||||
var picture: Drawable = mock(),
|
||||
var userAccount: String = "",
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
/*
|
||||
* Copyright (C) 2022 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.statusbar.policy
|
||||
|
||||
import com.android.systemui.statusbar.policy.UserSwitcherController.UserSwitchCallback
|
||||
import com.android.systemui.util.mockito.any
|
||||
import com.android.systemui.util.mockito.mock
|
||||
import org.mockito.Mockito.`when` as whenever
|
||||
|
||||
/**
|
||||
* A wrapper around a mocked [UserSwitcherController] to be used in tests.
|
||||
*
|
||||
* Note that this was implemented as a mock wrapper instead of fake implementation of a common
|
||||
* interface given how big the UserSwitcherController grew.
|
||||
*/
|
||||
class MockUserSwitcherControllerWrapper(
|
||||
currentUserName: String = "",
|
||||
) {
|
||||
val controller: UserSwitcherController = mock()
|
||||
private val callbacks = LinkedHashSet<UserSwitchCallback>()
|
||||
|
||||
var currentUserName = currentUserName
|
||||
set(value) {
|
||||
if (value != field) {
|
||||
field = value
|
||||
notifyCallbacks()
|
||||
}
|
||||
}
|
||||
|
||||
private fun notifyCallbacks() {
|
||||
callbacks.forEach { it.onUserSwitched() }
|
||||
}
|
||||
|
||||
init {
|
||||
whenever(controller.addUserSwitchCallback(any())).then { invocation ->
|
||||
callbacks.add(invocation.arguments.first() as UserSwitchCallback)
|
||||
}
|
||||
|
||||
whenever(controller.removeUserSwitchCallback(any())).then { invocation ->
|
||||
callbacks.remove(invocation.arguments.first() as UserSwitchCallback)
|
||||
}
|
||||
|
||||
whenever(controller.currentUserName).thenAnswer { this.currentUserName }
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user