Controls in QS
1. Migrate Android 11 users to a priority QS tile position 2. New Android 12 control users will see a tile appear at the end, with seeding already initiated. 3. Add a new method to QSTileHost to position a tile in the middle 4. Remove all Controls code from GlobalActionsDialog Bug: 172360102 Test: atest QSTileHostTest TileServicesTest QSFragmentTest GlobalActionsDialogTest DeviceControlsTileTest Change-Id: I1e52681b2cf7948b732808b1ff426d09b379ed93
This commit is contained in:
@@ -757,6 +757,7 @@
|
||||
android:excludeFromRecents="true"
|
||||
android:showWhenLocked="true"
|
||||
android:showForAllUsers="true"
|
||||
android:finishOnTaskLaunch="true"
|
||||
android:launchMode="singleInstance"
|
||||
android:configChanges="screenSize|smallestScreenSize|screenLayout|orientation|keyboard|keyboardHidden"
|
||||
android:visibleToInstantApps="true">
|
||||
|
||||
@@ -30,12 +30,6 @@
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"/>
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/global_actions_controls"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical"/>
|
||||
|
||||
</LinearLayout>
|
||||
</com.android.systemui.globalactions.MinHeightScrollView>
|
||||
</LinearLayout>
|
||||
|
||||
@@ -44,8 +44,9 @@ import com.android.systemui.controls.ui.ControlsUiController
|
||||
import com.android.systemui.dagger.SysUISingleton
|
||||
import com.android.systemui.dagger.qualifiers.Background
|
||||
import com.android.systemui.dump.DumpManager
|
||||
import com.android.systemui.globalactions.GlobalActionsDialog
|
||||
import com.android.systemui.settings.UserTracker
|
||||
import com.android.systemui.statusbar.policy.DeviceControlsControllerImpl.Companion.PREFS_CONTROLS_FILE
|
||||
import com.android.systemui.statusbar.policy.DeviceControlsControllerImpl.Companion.PREFS_CONTROLS_SEEDING_COMPLETED
|
||||
import com.android.systemui.util.concurrency.DelayableExecutor
|
||||
import java.io.FileDescriptor
|
||||
import java.io.PrintWriter
|
||||
@@ -198,11 +199,11 @@ class ControlsControllerImpl @Inject constructor (
|
||||
// When a component is uninstalled, allow seeding to happen again if the user
|
||||
// reinstalls the app
|
||||
val prefs = userStructure.userContext.getSharedPreferences(
|
||||
GlobalActionsDialog.PREFS_CONTROLS_FILE, Context.MODE_PRIVATE)
|
||||
PREFS_CONTROLS_FILE, Context.MODE_PRIVATE)
|
||||
val completedSeedingPackageSet = prefs.getStringSet(
|
||||
GlobalActionsDialog.PREFS_CONTROLS_SEEDING_COMPLETED, mutableSetOf<String>())
|
||||
PREFS_CONTROLS_SEEDING_COMPLETED, mutableSetOf<String>())
|
||||
val servicePackageSet = serviceInfoSet.map { it.packageName }
|
||||
prefs.edit().putStringSet(GlobalActionsDialog.PREFS_CONTROLS_SEEDING_COMPLETED,
|
||||
prefs.edit().putStringSet(PREFS_CONTROLS_SEEDING_COMPLETED,
|
||||
completedSeedingPackageSet.intersect(servicePackageSet)).apply()
|
||||
|
||||
var changed = false
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
|
||||
package com.android.systemui.controls.management
|
||||
|
||||
import android.app.ActivityOptions
|
||||
import android.content.ComponentName
|
||||
import android.content.Intent
|
||||
import android.os.Bundle
|
||||
@@ -34,7 +35,6 @@ import com.android.systemui.controls.controller.ControlsControllerImpl
|
||||
import com.android.systemui.controls.controller.StructureInfo
|
||||
import com.android.systemui.controls.ui.ControlsActivity
|
||||
import com.android.systemui.controls.ui.ControlsUiController
|
||||
import com.android.systemui.globalactions.GlobalActionsComponent
|
||||
import com.android.systemui.settings.CurrentUserTracker
|
||||
import com.android.systemui.util.LifecycleActivity
|
||||
import javax.inject.Inject
|
||||
@@ -45,7 +45,6 @@ import javax.inject.Inject
|
||||
class ControlsEditingActivity @Inject constructor(
|
||||
private val controller: ControlsControllerImpl,
|
||||
private val broadcastDispatcher: BroadcastDispatcher,
|
||||
private val globalActionsComponent: GlobalActionsComponent,
|
||||
private val customIconCache: CustomIconCache,
|
||||
private val uiController: ControlsUiController
|
||||
) : LifecycleActivity() {
|
||||
@@ -62,7 +61,6 @@ class ControlsEditingActivity @Inject constructor(
|
||||
private lateinit var model: FavoritesModel
|
||||
private lateinit var subtitle: TextView
|
||||
private lateinit var saveButton: View
|
||||
private var backToGlobalActions = true
|
||||
|
||||
private val currentUserTracker = object : CurrentUserTracker(broadcastDispatcher) {
|
||||
private val startingUser = controller.currentUserId
|
||||
@@ -86,11 +84,6 @@ class ControlsEditingActivity @Inject constructor(
|
||||
structure = it
|
||||
} ?: run(this::finish)
|
||||
|
||||
backToGlobalActions = intent.getBooleanExtra(
|
||||
ControlsUiController.BACK_TO_GLOBAL_ACTIONS,
|
||||
true
|
||||
)
|
||||
|
||||
bindViews()
|
||||
|
||||
bindButtons()
|
||||
@@ -109,15 +102,6 @@ class ControlsEditingActivity @Inject constructor(
|
||||
}
|
||||
|
||||
override fun onBackPressed() {
|
||||
if (backToGlobalActions) {
|
||||
globalActionsComponent.handleShowGlobalActionsMenu()
|
||||
} else {
|
||||
val i = Intent().apply {
|
||||
component = ComponentName(applicationContext, ControlsActivity::class.java)
|
||||
addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP or Intent.FLAG_ACTIVITY_NEW_TASK)
|
||||
}
|
||||
startActivity(i)
|
||||
}
|
||||
animateExitAndFinish()
|
||||
}
|
||||
|
||||
@@ -161,8 +145,12 @@ class ControlsEditingActivity @Inject constructor(
|
||||
setText(R.string.save)
|
||||
setOnClickListener {
|
||||
saveFavorites()
|
||||
startActivity(
|
||||
Intent(applicationContext, ControlsActivity::class.java),
|
||||
ActivityOptions
|
||||
.makeSceneTransitionAnimation(this@ControlsEditingActivity).toBundle()
|
||||
)
|
||||
animateExitAndFinish()
|
||||
globalActionsComponent.handleShowGlobalActionsMenu()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -43,7 +43,6 @@ import com.android.systemui.controls.controller.StructureInfo
|
||||
import com.android.systemui.controls.ui.ControlsActivity
|
||||
import com.android.systemui.controls.ui.ControlsUiController
|
||||
import com.android.systemui.dagger.qualifiers.Main
|
||||
import com.android.systemui.globalactions.GlobalActionsComponent
|
||||
import com.android.systemui.settings.CurrentUserTracker
|
||||
import com.android.systemui.util.LifecycleActivity
|
||||
import java.text.Collator
|
||||
@@ -56,7 +55,6 @@ class ControlsFavoritingActivity @Inject constructor(
|
||||
private val controller: ControlsControllerImpl,
|
||||
private val listingController: ControlsListingController,
|
||||
private val broadcastDispatcher: BroadcastDispatcher,
|
||||
private val globalActionsComponent: GlobalActionsComponent,
|
||||
private val uiController: ControlsUiController
|
||||
) : LifecycleActivity() {
|
||||
|
||||
@@ -92,7 +90,6 @@ class ControlsFavoritingActivity @Inject constructor(
|
||||
private lateinit var comparator: Comparator<StructureContainer>
|
||||
private var cancelLoadRunnable: Runnable? = null
|
||||
private var isPagerLoaded = false
|
||||
private var backToGlobalActions = true
|
||||
|
||||
private val currentUserTracker = object : CurrentUserTracker(broadcastDispatcher) {
|
||||
private val startingUser = controller.currentUserId
|
||||
@@ -133,11 +130,6 @@ class ControlsFavoritingActivity @Inject constructor(
|
||||
component = intent.getParcelableExtra<ComponentName>(Intent.EXTRA_COMPONENT_NAME)
|
||||
fromProviderSelector = intent.getBooleanExtra(EXTRA_FROM_PROVIDER_SELECTOR, false)
|
||||
|
||||
backToGlobalActions = intent.getBooleanExtra(
|
||||
ControlsUiController.BACK_TO_GLOBAL_ACTIONS,
|
||||
true
|
||||
)
|
||||
|
||||
bindViews()
|
||||
}
|
||||
|
||||
@@ -311,13 +303,6 @@ class ControlsFavoritingActivity @Inject constructor(
|
||||
private fun bindButtons() {
|
||||
otherAppsButton = requireViewById<Button>(R.id.other_apps).apply {
|
||||
setOnClickListener {
|
||||
val i = Intent().apply {
|
||||
component = ComponentName(context, ControlsProviderSelectorActivity::class.java)
|
||||
putExtra(
|
||||
ControlsUiController.BACK_TO_GLOBAL_ACTIONS,
|
||||
backToGlobalActions
|
||||
)
|
||||
}
|
||||
if (doneButton.isEnabled) {
|
||||
// The user has made changes
|
||||
Toast.makeText(
|
||||
@@ -326,8 +311,11 @@ class ControlsFavoritingActivity @Inject constructor(
|
||||
Toast.LENGTH_SHORT
|
||||
).show()
|
||||
}
|
||||
startActivity(i, ActivityOptions
|
||||
.makeSceneTransitionAnimation(this@ControlsFavoritingActivity).toBundle())
|
||||
startActivity(
|
||||
Intent(context, ControlsProviderSelectorActivity::class.java),
|
||||
ActivityOptions
|
||||
.makeSceneTransitionAnimation(this@ControlsFavoritingActivity).toBundle()
|
||||
)
|
||||
animateExitAndFinish()
|
||||
}
|
||||
}
|
||||
@@ -349,15 +337,10 @@ class ControlsFavoritingActivity @Inject constructor(
|
||||
}
|
||||
|
||||
private fun openControlsOrigin() {
|
||||
if (backToGlobalActions) {
|
||||
globalActionsComponent.handleShowGlobalActionsMenu()
|
||||
} else {
|
||||
val i = Intent().apply {
|
||||
component = ComponentName(applicationContext, ControlsActivity::class.java)
|
||||
addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP or Intent.FLAG_ACTIVITY_NEW_TASK)
|
||||
}
|
||||
startActivity(i)
|
||||
}
|
||||
startActivity(
|
||||
Intent(applicationContext, ControlsActivity::class.java),
|
||||
ActivityOptions.makeSceneTransitionAnimation(this).toBundle()
|
||||
)
|
||||
}
|
||||
|
||||
override fun onPause() {
|
||||
|
||||
@@ -36,7 +36,6 @@ import com.android.systemui.controls.ui.ControlsActivity
|
||||
import com.android.systemui.controls.ui.ControlsUiController
|
||||
import com.android.systemui.dagger.qualifiers.Background
|
||||
import com.android.systemui.dagger.qualifiers.Main
|
||||
import com.android.systemui.globalactions.GlobalActionsComponent
|
||||
import com.android.systemui.settings.CurrentUserTracker
|
||||
import com.android.systemui.util.LifecycleActivity
|
||||
import java.util.concurrent.Executor
|
||||
@@ -50,7 +49,6 @@ class ControlsProviderSelectorActivity @Inject constructor(
|
||||
@Background private val backExecutor: Executor,
|
||||
private val listingController: ControlsListingController,
|
||||
private val controlsController: ControlsController,
|
||||
private val globalActionsComponent: GlobalActionsComponent,
|
||||
private val broadcastDispatcher: BroadcastDispatcher,
|
||||
private val uiController: ControlsUiController
|
||||
) : LifecycleActivity() {
|
||||
@@ -59,7 +57,6 @@ class ControlsProviderSelectorActivity @Inject constructor(
|
||||
private const val TAG = "ControlsProviderSelectorActivity"
|
||||
}
|
||||
|
||||
private var backToGlobalActions = true
|
||||
private lateinit var recyclerView: RecyclerView
|
||||
private val currentUserTracker = object : CurrentUserTracker(broadcastDispatcher) {
|
||||
private val startingUser = listingController.currentUserId
|
||||
@@ -105,23 +102,13 @@ class ControlsProviderSelectorActivity @Inject constructor(
|
||||
}
|
||||
}
|
||||
requireViewById<View>(R.id.done).visibility = View.GONE
|
||||
|
||||
backToGlobalActions = intent.getBooleanExtra(
|
||||
ControlsUiController.BACK_TO_GLOBAL_ACTIONS,
|
||||
true
|
||||
)
|
||||
}
|
||||
|
||||
override fun onBackPressed() {
|
||||
if (backToGlobalActions) {
|
||||
globalActionsComponent.handleShowGlobalActionsMenu()
|
||||
} else {
|
||||
val i = Intent().apply {
|
||||
component = ComponentName(applicationContext, ControlsActivity::class.java)
|
||||
addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP or Intent.FLAG_ACTIVITY_NEW_TASK)
|
||||
}
|
||||
startActivity(i)
|
||||
val i = Intent().apply {
|
||||
component = ComponentName(applicationContext, ControlsActivity::class.java)
|
||||
}
|
||||
startActivity(i, ActivityOptions.makeSceneTransitionAnimation(this).toBundle())
|
||||
animateExitAndFinish()
|
||||
}
|
||||
|
||||
@@ -169,10 +156,6 @@ class ControlsProviderSelectorActivity @Inject constructor(
|
||||
listingController.getAppLabel(it))
|
||||
putExtra(Intent.EXTRA_COMPONENT_NAME, it)
|
||||
putExtra(ControlsFavoritingActivity.EXTRA_FROM_PROVIDER_SELECTOR, true)
|
||||
putExtra(
|
||||
ControlsUiController.BACK_TO_GLOBAL_ACTIONS,
|
||||
backToGlobalActions
|
||||
)
|
||||
}
|
||||
startActivity(intent, ActivityOptions.makeSceneTransitionAnimation(this).toBundle())
|
||||
animateExitAndFinish()
|
||||
|
||||
@@ -26,7 +26,7 @@ import android.service.controls.Control
|
||||
interface ControlActionCoordinator {
|
||||
|
||||
// If launched from an Activity, continue within this stack
|
||||
var activityContext: Context?
|
||||
var activityContext: Context
|
||||
|
||||
/**
|
||||
* Close any dialogs which may have been open
|
||||
|
||||
@@ -63,7 +63,7 @@ class ControlActionCoordinatorImpl @Inject constructor(
|
||||
private var actionsInProgress = mutableSetOf<String>()
|
||||
private val isLocked: Boolean
|
||||
get() = !keyguardStateController.isUnlocked()
|
||||
override var activityContext: Context? = null
|
||||
override lateinit var activityContext: Context
|
||||
|
||||
companion object {
|
||||
private const val RESPONSE_TIMEOUT_IN_MILLIS = 3000L
|
||||
@@ -153,14 +153,9 @@ class ControlActionCoordinatorImpl @Inject constructor(
|
||||
// pending actions will only run after the control state has been refreshed
|
||||
pendingAction = action
|
||||
}
|
||||
val wasLocked = isLocked
|
||||
activityStarter.dismissKeyguardThenExecute({
|
||||
Log.d(ControlsUiController.TAG, "Device unlocked, invoking controls action")
|
||||
if (wasLocked && activityContext == null) {
|
||||
globalActionsComponent.handleShowGlobalActionsMenu()
|
||||
} else {
|
||||
action.invoke()
|
||||
}
|
||||
action.invoke()
|
||||
true
|
||||
}, { pendingAction = null }, true /* afterKeyguardGone */)
|
||||
} else {
|
||||
|
||||
@@ -41,6 +41,14 @@ class ControlsActivity @Inject constructor(
|
||||
|
||||
setContentView(R.layout.controls_fullscreen)
|
||||
|
||||
getLifecycle().addObserver(
|
||||
ControlsAnimations.observerForAnimations(
|
||||
requireViewById<ViewGroup>(R.id.control_detail_root),
|
||||
window,
|
||||
intent
|
||||
)
|
||||
)
|
||||
|
||||
requireViewById<ViewGroup>(R.id.control_detail_root).apply {
|
||||
setOnApplyWindowInsetsListener {
|
||||
v: View, insets: WindowInsets ->
|
||||
@@ -61,7 +69,7 @@ class ControlsActivity @Inject constructor(
|
||||
|
||||
parent = requireViewById<ViewGroup>(R.id.global_actions_controls)
|
||||
parent.alpha = 0f
|
||||
uiController.show(parent, { animateExitAndFinish() }, this)
|
||||
uiController.show(parent, { finish() }, this)
|
||||
}
|
||||
|
||||
override fun onResume() {
|
||||
@@ -71,7 +79,7 @@ class ControlsActivity @Inject constructor(
|
||||
}
|
||||
|
||||
override fun onBackPressed() {
|
||||
animateExitAndFinish()
|
||||
finish()
|
||||
}
|
||||
|
||||
override fun onStop() {
|
||||
@@ -79,8 +87,4 @@ class ControlsActivity @Inject constructor(
|
||||
|
||||
uiController.hide()
|
||||
}
|
||||
|
||||
private fun animateExitAndFinish() {
|
||||
ControlsAnimations.exitAnimation(parent, { finish() }).start()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -28,10 +28,9 @@ interface ControlsUiController {
|
||||
companion object {
|
||||
public const val TAG = "ControlsUiController"
|
||||
public const val EXTRA_ANIMATE = "extra_animate"
|
||||
public const val BACK_TO_GLOBAL_ACTIONS = "back_to_global_actions"
|
||||
}
|
||||
|
||||
fun show(parent: ViewGroup, onDismiss: Runnable, activityContext: Context?)
|
||||
fun show(parent: ViewGroup, onDismiss: Runnable, activityContext: Context)
|
||||
fun hide()
|
||||
|
||||
/**
|
||||
|
||||
@@ -19,6 +19,8 @@ package com.android.systemui.controls.ui
|
||||
import android.animation.Animator
|
||||
import android.animation.AnimatorListenerAdapter
|
||||
import android.animation.ObjectAnimator
|
||||
import android.app.Activity
|
||||
import android.app.ActivityOptions
|
||||
import android.content.ComponentName
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
@@ -129,6 +131,7 @@ class ControlsUiControllerImpl @Inject constructor (
|
||||
override val available: Boolean
|
||||
get() = controlsController.get().available
|
||||
|
||||
private lateinit var activityContext: Context
|
||||
private lateinit var listingCallback: ControlsListingController.ControlsListingCallback
|
||||
|
||||
private fun createCallback(
|
||||
@@ -153,11 +156,12 @@ class ControlsUiControllerImpl @Inject constructor (
|
||||
override fun show(
|
||||
parent: ViewGroup,
|
||||
onDismiss: Runnable,
|
||||
activityContext: Context?
|
||||
activityContext: Context
|
||||
) {
|
||||
Log.d(ControlsUiController.TAG, "show()")
|
||||
this.parent = parent
|
||||
this.onDismiss = onDismiss
|
||||
this.activityContext = activityContext
|
||||
hidden = false
|
||||
retainCache = false
|
||||
|
||||
@@ -198,7 +202,7 @@ class ControlsUiControllerImpl @Inject constructor (
|
||||
controlViewsById.clear()
|
||||
controlsById.clear()
|
||||
|
||||
show(parent, onDismiss, controlActionCoordinator.activityContext)
|
||||
show(parent, onDismiss, activityContext)
|
||||
val showAnim = ObjectAnimator.ofFloat(parent, "alpha", 0.0f, 1.0f)
|
||||
showAnim.setInterpolator(DecelerateInterpolator(1.0f))
|
||||
showAnim.setDuration(FADE_IN_MILLIS)
|
||||
@@ -220,7 +224,7 @@ class ControlsUiControllerImpl @Inject constructor (
|
||||
inflater.inflate(R.layout.controls_no_favorites, parent, true)
|
||||
|
||||
val viewGroup = parent.requireViewById(R.id.controls_no_favorites_group) as ViewGroup
|
||||
viewGroup.setOnClickListener { v: View -> startProviderSelectorActivity(v.context) }
|
||||
viewGroup.setOnClickListener { _: View -> startProviderSelectorActivity() }
|
||||
|
||||
val subtitle = parent.requireViewById<TextView>(R.id.controls_subtitle)
|
||||
subtitle.setText(context.resources.getString(R.string.quick_controls_subtitle))
|
||||
@@ -234,20 +238,18 @@ class ControlsUiControllerImpl @Inject constructor (
|
||||
}
|
||||
}
|
||||
|
||||
private fun startFavoritingActivity(context: Context, si: StructureInfo) {
|
||||
startTargetedActivity(context, si, ControlsFavoritingActivity::class.java)
|
||||
private fun startFavoritingActivity(si: StructureInfo) {
|
||||
startTargetedActivity(si, ControlsFavoritingActivity::class.java)
|
||||
}
|
||||
|
||||
private fun startEditingActivity(context: Context, si: StructureInfo) {
|
||||
startTargetedActivity(context, si, ControlsEditingActivity::class.java)
|
||||
private fun startEditingActivity(si: StructureInfo) {
|
||||
startTargetedActivity(si, ControlsEditingActivity::class.java)
|
||||
}
|
||||
|
||||
private fun startTargetedActivity(context: Context, si: StructureInfo, klazz: Class<*>) {
|
||||
val i = Intent(context, klazz).apply {
|
||||
addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP or Intent.FLAG_ACTIVITY_NEW_TASK)
|
||||
}
|
||||
private fun startTargetedActivity(si: StructureInfo, klazz: Class<*>) {
|
||||
val i = Intent(activityContext, klazz)
|
||||
putIntentExtras(i, si)
|
||||
startActivity(context, i)
|
||||
startActivity(i)
|
||||
|
||||
retainCache = true
|
||||
}
|
||||
@@ -261,27 +263,22 @@ class ControlsUiControllerImpl @Inject constructor (
|
||||
}
|
||||
}
|
||||
|
||||
private fun startProviderSelectorActivity(context: Context) {
|
||||
val i = Intent(context, ControlsProviderSelectorActivity::class.java).apply {
|
||||
addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP or Intent.FLAG_ACTIVITY_NEW_TASK)
|
||||
}
|
||||
startActivity(context, i)
|
||||
private fun startProviderSelectorActivity() {
|
||||
startActivity(Intent(activityContext, ControlsProviderSelectorActivity::class.java))
|
||||
}
|
||||
|
||||
private fun startActivity(context: Context, intent: Intent) {
|
||||
private fun startActivity(intent: Intent) {
|
||||
// Force animations when transitioning from a dialog to an activity
|
||||
intent.putExtra(ControlsUiController.EXTRA_ANIMATE, true)
|
||||
intent.putExtra(
|
||||
ControlsUiController.BACK_TO_GLOBAL_ACTIONS,
|
||||
controlActionCoordinator.activityContext == null
|
||||
)
|
||||
onDismiss.run()
|
||||
|
||||
activityStarter.dismissKeyguardThenExecute({
|
||||
shadeController.collapsePanel(false)
|
||||
context.startActivity(intent)
|
||||
true
|
||||
}, null, true)
|
||||
if (keyguardStateController.isUnlocked()) {
|
||||
activityContext.startActivity(
|
||||
intent,
|
||||
ActivityOptions.makeSceneTransitionAnimation(activityContext as Activity).toBundle()
|
||||
)
|
||||
} else {
|
||||
activityStarter.postStartActivityDismissingKeyguard(intent, 0 /* delay */)
|
||||
}
|
||||
}
|
||||
|
||||
private fun showControlsView(items: List<SelectionItem>) {
|
||||
@@ -328,9 +325,9 @@ class ControlsUiControllerImpl @Inject constructor (
|
||||
) {
|
||||
when (pos) {
|
||||
// 0: Add Control
|
||||
0 -> startFavoritingActivity(view.context, selectedStructure)
|
||||
0 -> startFavoritingActivity(selectedStructure)
|
||||
// 1: Edit controls
|
||||
1 -> startEditingActivity(view.context, selectedStructure)
|
||||
1 -> startEditingActivity(selectedStructure)
|
||||
}
|
||||
dismiss()
|
||||
}
|
||||
@@ -399,15 +396,9 @@ class ControlsUiControllerImpl @Inject constructor (
|
||||
val inflater = LayoutInflater.from(context)
|
||||
inflater.inflate(R.layout.controls_with_favorites, parent, true)
|
||||
|
||||
if (controlActionCoordinator.activityContext == null) {
|
||||
parent.requireViewById<View>(R.id.controls_spacer).apply {
|
||||
visibility = View.VISIBLE
|
||||
}
|
||||
} else {
|
||||
parent.requireViewById<ImageView>(R.id.controls_close).apply {
|
||||
setOnClickListener { _: View -> onDismiss.run() }
|
||||
visibility = View.VISIBLE
|
||||
}
|
||||
parent.requireViewById<ImageView>(R.id.controls_close).apply {
|
||||
setOnClickListener { _: View -> onDismiss.run() }
|
||||
visibility = View.VISIBLE
|
||||
}
|
||||
|
||||
val maxColumns = findMaxColumns()
|
||||
@@ -522,8 +513,6 @@ class ControlsUiControllerImpl @Inject constructor (
|
||||
override fun hide() {
|
||||
hidden = true
|
||||
|
||||
controlActionCoordinator.activityContext = null
|
||||
|
||||
closeDialogs(true)
|
||||
controlsController.get().unsubscribe()
|
||||
|
||||
|
||||
@@ -17,8 +17,6 @@ package com.android.systemui.globalactions;
|
||||
import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
|
||||
|
||||
import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_BOOT;
|
||||
import static com.android.systemui.controls.dagger.ControlsComponent.Visibility.AVAILABLE;
|
||||
import static com.android.systemui.controls.dagger.ControlsComponent.Visibility.AVAILABLE_AFTER_UNLOCK;
|
||||
import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_GLOBAL_ACTIONS_SHOWING;
|
||||
|
||||
import android.animation.Animator;
|
||||
@@ -30,10 +28,8 @@ import android.app.IActivityManager;
|
||||
import android.app.PendingIntent;
|
||||
import android.app.admin.DevicePolicyManager;
|
||||
import android.app.trust.TrustManager;
|
||||
import android.content.ComponentName;
|
||||
import android.content.Context;
|
||||
import android.content.DialogInterface;
|
||||
import android.content.SharedPreferences;
|
||||
import android.content.res.Resources;
|
||||
import android.database.ContentObserver;
|
||||
import android.graphics.drawable.Drawable;
|
||||
@@ -47,7 +43,6 @@ import android.telecom.TelecomManager;
|
||||
import android.transition.AutoTransition;
|
||||
import android.transition.TransitionManager;
|
||||
import android.transition.TransitionSet;
|
||||
import android.util.Log;
|
||||
import android.view.IWindowManager;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
@@ -69,18 +64,12 @@ import com.android.internal.widget.LockPatternUtils;
|
||||
import com.android.systemui.Interpolators;
|
||||
import com.android.systemui.broadcast.BroadcastDispatcher;
|
||||
import com.android.systemui.colorextraction.SysuiColorExtractor;
|
||||
import com.android.systemui.controls.ControlsServiceInfo;
|
||||
import com.android.systemui.controls.controller.ControlsController;
|
||||
import com.android.systemui.controls.dagger.ControlsComponent;
|
||||
import com.android.systemui.controls.management.ControlsAnimations;
|
||||
import com.android.systemui.controls.ui.ControlsUiController;
|
||||
import com.android.systemui.dagger.qualifiers.Background;
|
||||
import com.android.systemui.dagger.qualifiers.Main;
|
||||
import com.android.systemui.model.SysUiState;
|
||||
import com.android.systemui.plugins.ActivityStarter;
|
||||
import com.android.systemui.plugins.GlobalActions.GlobalActionsManager;
|
||||
import com.android.systemui.plugins.GlobalActionsPanelPlugin;
|
||||
import com.android.systemui.settings.UserContextProvider;
|
||||
import com.android.systemui.statusbar.NotificationShadeDepthController;
|
||||
import com.android.systemui.statusbar.NotificationShadeWindowController;
|
||||
import com.android.systemui.statusbar.policy.ConfigurationController;
|
||||
@@ -91,12 +80,6 @@ import com.android.systemui.util.leak.RotationUtils;
|
||||
import com.android.systemui.util.settings.GlobalSettings;
|
||||
import com.android.systemui.util.settings.SecureSettings;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.Executor;
|
||||
|
||||
import javax.inject.Inject;
|
||||
@@ -105,7 +88,7 @@ import javax.inject.Provider;
|
||||
/**
|
||||
* Helper to show the global actions dialog. Each item is an {@link Action} that may show depending
|
||||
* on whether the keyguard is showing, and whether the device is provisioned.
|
||||
* This version includes wallet and controls.
|
||||
* This version includes wallet.
|
||||
*/
|
||||
public class GlobalActionsDialog extends GlobalActionsDialogLite
|
||||
implements DialogInterface.OnDismissListener,
|
||||
@@ -116,10 +99,6 @@ public class GlobalActionsDialog extends GlobalActionsDialogLite
|
||||
|
||||
private static final String TAG = "GlobalActionsDialog";
|
||||
|
||||
public static final String PREFS_CONTROLS_SEEDING_COMPLETED = "SeedingCompleted";
|
||||
public static final String PREFS_CONTROLS_FILE = "controls_prefs";
|
||||
private static final int SEEDING_MAX = 2;
|
||||
|
||||
private final LockPatternUtils mLockPatternUtils;
|
||||
private final KeyguardStateController mKeyguardStateController;
|
||||
private final NotificationShadeDepthController mDepthController;
|
||||
@@ -129,13 +108,9 @@ public class GlobalActionsDialog extends GlobalActionsDialogLite
|
||||
private final IStatusBarService mStatusBarService;
|
||||
private final NotificationShadeWindowController mNotificationShadeWindowController;
|
||||
private GlobalActionsPanelPlugin mWalletPlugin;
|
||||
private Optional<ControlsUiController> mControlsUiControllerOptional;
|
||||
private List<ControlsServiceInfo> mControlsServiceInfos = new ArrayList<>();
|
||||
private ControlsComponent mControlsComponent;
|
||||
private Optional<ControlsController> mControlsControllerOptional;
|
||||
private UserContextProvider mUserContextProvider;
|
||||
|
||||
@VisibleForTesting
|
||||
boolean mShowLockScreenCardsAndControls = false;
|
||||
boolean mShowLockScreenCards = false;
|
||||
|
||||
/**
|
||||
* @param context everything needs a context :(
|
||||
@@ -158,9 +133,7 @@ public class GlobalActionsDialog extends GlobalActionsDialogLite
|
||||
IWindowManager iWindowManager,
|
||||
@Background Executor backgroundExecutor,
|
||||
UiEventLogger uiEventLogger,
|
||||
RingerModeTracker ringerModeTracker, SysUiState sysUiState, @Main Handler handler,
|
||||
ControlsComponent controlsComponent,
|
||||
UserContextProvider userContextProvider) {
|
||||
RingerModeTracker ringerModeTracker, SysUiState sysUiState, @Main Handler handler) {
|
||||
|
||||
super(context, windowManagerFuncs,
|
||||
audioManager, iDreamManager,
|
||||
@@ -186,11 +159,7 @@ public class GlobalActionsDialog extends GlobalActionsDialogLite
|
||||
mSysuiColorExtractor = colorExtractor;
|
||||
mStatusBarService = statusBarService;
|
||||
mNotificationShadeWindowController = notificationShadeWindowController;
|
||||
mControlsComponent = controlsComponent;
|
||||
mControlsUiControllerOptional = controlsComponent.getControlsUiController();
|
||||
mControlsControllerOptional = controlsComponent.getControlsController();
|
||||
mSysUiState = sysUiState;
|
||||
mUserContextProvider = userContextProvider;
|
||||
mActivityStarter = activityStarter;
|
||||
keyguardStateController.addCallback(new KeyguardStateController.Callback() {
|
||||
@Override
|
||||
@@ -201,10 +170,7 @@ public class GlobalActionsDialog extends GlobalActionsDialogLite
|
||||
if (dialog.mWalletViewController != null) {
|
||||
dialog.mWalletViewController.onDeviceLockStateChanged(!unlocked);
|
||||
}
|
||||
if (!dialog.isShowingControls()
|
||||
&& mControlsComponent.getVisibility() == AVAILABLE) {
|
||||
dialog.showControls(mControlsUiControllerOptional.get());
|
||||
}
|
||||
|
||||
if (unlocked) {
|
||||
dialog.hideLockMessage();
|
||||
}
|
||||
@@ -212,25 +178,7 @@ public class GlobalActionsDialog extends GlobalActionsDialogLite
|
||||
}
|
||||
});
|
||||
|
||||
if (mControlsComponent.getControlsListingController().isPresent()) {
|
||||
mControlsComponent.getControlsListingController().get()
|
||||
.addCallback(list -> {
|
||||
mControlsServiceInfos = list;
|
||||
// This callback may occur after the dialog has been shown. If so, add
|
||||
// controls into the already visible space or show the lock msg if needed.
|
||||
if (mDialog != null) {
|
||||
ActionsDialog dialog = (ActionsDialog) mDialog;
|
||||
if (!dialog.isShowingControls()
|
||||
&& mControlsComponent.getVisibility() == AVAILABLE) {
|
||||
dialog.showControls(mControlsUiControllerOptional.get());
|
||||
} else if (shouldShowLockMessage(dialog)) {
|
||||
dialog.showLockMessage();
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Listen for changes to show controls on the power menu while locked
|
||||
// Listen for changes to show pay on the power menu while locked
|
||||
onPowerMenuLockScreenSettingsChanged();
|
||||
mGlobalSettings.registerContentObserver(
|
||||
Settings.Secure.getUriFor(Settings.Secure.POWER_MENU_LOCKED_SHOW_CONTENT),
|
||||
@@ -243,65 +191,6 @@ public class GlobalActionsDialog extends GlobalActionsDialogLite
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* See if any available control service providers match one of the preferred components. If
|
||||
* they do, and there are no current favorites for that component, query the preferred
|
||||
* component for a limited number of suggested controls.
|
||||
*/
|
||||
private void seedFavorites() {
|
||||
if (!mControlsControllerOptional.isPresent()
|
||||
|| mControlsServiceInfos.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
String[] preferredControlsPackages = getContext().getResources()
|
||||
.getStringArray(com.android.systemui.R.array.config_controlsPreferredPackages);
|
||||
|
||||
SharedPreferences prefs = mUserContextProvider.getUserContext()
|
||||
.getSharedPreferences(PREFS_CONTROLS_FILE, Context.MODE_PRIVATE);
|
||||
Set<String> seededPackages = prefs.getStringSet(PREFS_CONTROLS_SEEDING_COMPLETED,
|
||||
Collections.emptySet());
|
||||
|
||||
List<ComponentName> componentsToSeed = new ArrayList<>();
|
||||
for (int i = 0; i < Math.min(SEEDING_MAX, preferredControlsPackages.length); i++) {
|
||||
String pkg = preferredControlsPackages[i];
|
||||
for (ControlsServiceInfo info : mControlsServiceInfos) {
|
||||
if (!pkg.equals(info.componentName.getPackageName())) continue;
|
||||
if (seededPackages.contains(pkg)) {
|
||||
break;
|
||||
} else if (mControlsControllerOptional.get()
|
||||
.countFavoritesForComponent(info.componentName) > 0) {
|
||||
// When there are existing controls but no saved preference, assume it
|
||||
// is out of sync, perhaps through a device restore, and update the
|
||||
// preference
|
||||
addPackageToSeededSet(prefs, pkg);
|
||||
break;
|
||||
}
|
||||
componentsToSeed.add(info.componentName);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (componentsToSeed.isEmpty()) return;
|
||||
|
||||
mControlsControllerOptional.get().seedFavoritesForComponents(
|
||||
componentsToSeed,
|
||||
(response) -> {
|
||||
Log.d(TAG, "Controls seeded: " + response);
|
||||
if (response.getAccepted()) {
|
||||
addPackageToSeededSet(prefs, response.getPackageName());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void addPackageToSeededSet(SharedPreferences prefs, String pkg) {
|
||||
Set<String> seededPackages = prefs.getStringSet(PREFS_CONTROLS_SEEDING_COMPLETED,
|
||||
Collections.emptySet());
|
||||
Set<String> updatedPkgs = new HashSet<>(seededPackages);
|
||||
updatedPkgs.add(pkg);
|
||||
prefs.edit().putStringSet(PREFS_CONTROLS_SEEDING_COMPLETED, updatedPkgs).apply();
|
||||
}
|
||||
|
||||
/**
|
||||
* Show the global actions dialog (creating if necessary)
|
||||
*
|
||||
@@ -313,12 +202,6 @@ public class GlobalActionsDialog extends GlobalActionsDialogLite
|
||||
super.showOrHideDialog(keyguardShowing, isDeviceProvisioned);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void handleShow() {
|
||||
seedFavorites();
|
||||
super.handleShow();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the maximum number of power menu items to show based on which GlobalActions
|
||||
* layout is being used.
|
||||
@@ -340,14 +223,9 @@ public class GlobalActionsDialog extends GlobalActionsDialogLite
|
||||
initDialogItems();
|
||||
|
||||
mDepthController.setShowingHomeControls(true);
|
||||
ControlsUiController uiController = null;
|
||||
if (mControlsComponent.getVisibility() == AVAILABLE) {
|
||||
uiController = mControlsUiControllerOptional.get();
|
||||
}
|
||||
ActionsDialog dialog = new ActionsDialog(getContext(), mAdapter, mOverflowAdapter,
|
||||
this::getWalletViewController, mDepthController, mSysuiColorExtractor,
|
||||
mStatusBarService, mNotificationShadeWindowController,
|
||||
controlsAvailable(), uiController,
|
||||
mSysUiState, this::onRotate, isKeyguardShowing(), mPowerAdapter);
|
||||
|
||||
if (shouldShowLockMessage(dialog)) {
|
||||
@@ -407,10 +285,6 @@ public class GlobalActionsDialog extends GlobalActionsDialogLite
|
||||
private final Provider<GlobalActionsPanelPlugin.PanelViewController> mWalletFactory;
|
||||
@Nullable private GlobalActionsPanelPlugin.PanelViewController mWalletViewController;
|
||||
private ResetOrientationData mResetOrientationData;
|
||||
private final boolean mControlsAvailable;
|
||||
|
||||
private ControlsUiController mControlsUiController;
|
||||
private ViewGroup mControlsView;
|
||||
@VisibleForTesting ViewGroup mLockMessageContainer;
|
||||
private TextView mLockMessage;
|
||||
|
||||
@@ -419,15 +293,12 @@ public class GlobalActionsDialog extends GlobalActionsDialogLite
|
||||
NotificationShadeDepthController depthController,
|
||||
SysuiColorExtractor sysuiColorExtractor, IStatusBarService statusBarService,
|
||||
NotificationShadeWindowController notificationShadeWindowController,
|
||||
boolean controlsAvailable, @Nullable ControlsUiController controlsUiController,
|
||||
SysUiState sysuiState, Runnable onRotateCallback, boolean keyguardShowing,
|
||||
MyPowerOptionsAdapter powerAdapter) {
|
||||
super(context, com.android.systemui.R.style.Theme_SystemUI_Dialog_GlobalActions,
|
||||
adapter, overflowAdapter, depthController, sysuiColorExtractor,
|
||||
statusBarService, notificationShadeWindowController, sysuiState,
|
||||
onRotateCallback, keyguardShowing, powerAdapter);
|
||||
mControlsAvailable = controlsAvailable;
|
||||
mControlsUiController = controlsUiController;
|
||||
mWalletFactory = walletFactory;
|
||||
|
||||
// Update window attributes
|
||||
@@ -448,16 +319,6 @@ public class GlobalActionsDialog extends GlobalActionsDialogLite
|
||||
initializeLayout();
|
||||
}
|
||||
|
||||
private boolean isShowingControls() {
|
||||
return mControlsUiController != null;
|
||||
}
|
||||
|
||||
private void showControls(ControlsUiController controller) {
|
||||
mControlsUiController = controller;
|
||||
mControlsUiController.show(mControlsView, this::dismissForControlsActivity,
|
||||
null /* activityContext */);
|
||||
}
|
||||
|
||||
private boolean isWalletViewAvailable() {
|
||||
return mWalletViewController != null && mWalletViewController.getPanelContent() != null;
|
||||
}
|
||||
@@ -524,10 +385,8 @@ public class GlobalActionsDialog extends GlobalActionsDialogLite
|
||||
new FrameLayout.LayoutParams(
|
||||
FrameLayout.LayoutParams.MATCH_PARENT,
|
||||
FrameLayout.LayoutParams.MATCH_PARENT);
|
||||
if (!mControlsAvailable) {
|
||||
panelParams.topMargin = mContext.getResources().getDimensionPixelSize(
|
||||
com.android.systemui.R.dimen.global_actions_wallet_top_margin);
|
||||
}
|
||||
panelParams.topMargin = mContext.getResources().getDimensionPixelSize(
|
||||
com.android.systemui.R.dimen.global_actions_wallet_top_margin);
|
||||
View walletView = mWalletViewController.getPanelContent();
|
||||
panelContainer.addView(walletView, panelParams);
|
||||
// Smooth transitions when wallet is resized, which can happen when a card is added
|
||||
@@ -554,7 +413,6 @@ public class GlobalActionsDialog extends GlobalActionsDialogLite
|
||||
@Override
|
||||
protected void initializeLayout() {
|
||||
super.initializeLayout();
|
||||
mControlsView = findViewById(com.android.systemui.R.id.global_actions_controls);
|
||||
mLockMessageContainer = requireViewById(
|
||||
com.android.systemui.R.id.global_actions_lock_message_container);
|
||||
mLockMessage = requireViewById(com.android.systemui.R.id.global_actions_lock_message);
|
||||
@@ -577,10 +435,6 @@ public class GlobalActionsDialog extends GlobalActionsDialogLite
|
||||
windowInsets.getStableInsetBottom());
|
||||
return WindowInsets.CONSUMED;
|
||||
});
|
||||
if (mControlsUiController != null) {
|
||||
mControlsUiController.show(mControlsView, this::dismissForControlsActivity,
|
||||
null /* activityContext */);
|
||||
}
|
||||
|
||||
mBackgroundDrawable.setAlpha(0);
|
||||
float xOffset = mGlobalActionsLayout.getAnimationOffsetX();
|
||||
@@ -609,20 +463,11 @@ public class GlobalActionsDialog extends GlobalActionsDialogLite
|
||||
@Override
|
||||
protected void dismissInternal() {
|
||||
super.dismissInternal();
|
||||
if (mControlsUiController != null) mControlsUiController.closeDialogs(false);
|
||||
}
|
||||
|
||||
private void dismissForControlsActivity() {
|
||||
dismissWithAnimation(() -> {
|
||||
ViewGroup root = (ViewGroup) mGlobalActionsLayout.getParent();
|
||||
ControlsAnimations.exitAnimation(root, this::completeDismiss).start();
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void completeDismiss() {
|
||||
dismissWallet();
|
||||
if (mControlsUiController != null) mControlsUiController.hide();
|
||||
resetOrientation();
|
||||
super.completeDismiss();
|
||||
}
|
||||
@@ -647,15 +492,7 @@ public class GlobalActionsDialog extends GlobalActionsDialogLite
|
||||
public void refreshDialog() {
|
||||
// ensure dropdown menus are dismissed before re-initializing the dialog
|
||||
dismissWallet();
|
||||
if (mControlsUiController != null) {
|
||||
mControlsUiController.hide();
|
||||
}
|
||||
|
||||
super.refreshDialog();
|
||||
if (mControlsUiController != null) {
|
||||
mControlsUiController.show(mControlsView, this::dismissForControlsActivity,
|
||||
null /* activityContext */);
|
||||
}
|
||||
}
|
||||
|
||||
void hideLockMessage() {
|
||||
@@ -699,15 +536,8 @@ public class GlobalActionsDialog extends GlobalActionsDialogLite
|
||||
return isPanelDebugModeEnabled(context);
|
||||
}
|
||||
|
||||
private boolean controlsAvailable() {
|
||||
return isDeviceProvisioned()
|
||||
&& mControlsComponent.isEnabled()
|
||||
&& !mControlsServiceInfos.isEmpty();
|
||||
}
|
||||
|
||||
private boolean shouldShowLockMessage(ActionsDialog dialog) {
|
||||
return mControlsComponent.getVisibility() == AVAILABLE_AFTER_UNLOCK
|
||||
|| isWalletAvailableAfterUnlock(dialog);
|
||||
return isWalletAvailableAfterUnlock(dialog);
|
||||
}
|
||||
|
||||
// Temporary while we move items out of the power menu
|
||||
@@ -715,12 +545,12 @@ public class GlobalActionsDialog extends GlobalActionsDialogLite
|
||||
boolean isLockedAfterBoot = mLockPatternUtils.getStrongAuthForUser(getCurrentUser().id)
|
||||
== STRONG_AUTH_REQUIRED_AFTER_BOOT;
|
||||
return !mKeyguardStateController.isUnlocked()
|
||||
&& (!mShowLockScreenCardsAndControls || isLockedAfterBoot)
|
||||
&& (!mShowLockScreenCards || isLockedAfterBoot)
|
||||
&& dialog.isWalletViewAvailable();
|
||||
}
|
||||
|
||||
private void onPowerMenuLockScreenSettingsChanged() {
|
||||
mShowLockScreenCardsAndControls = mSecureSettings.getInt(
|
||||
mShowLockScreenCards = mSecureSettings.getInt(
|
||||
Settings.Secure.POWER_MENU_LOCKED_SHOW_CONTENT, 0) != 0;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -23,7 +23,6 @@ import android.os.Handler;
|
||||
import android.os.Looper;
|
||||
import android.os.UserHandle;
|
||||
import android.os.UserManager;
|
||||
import android.provider.Settings;
|
||||
import android.provider.Settings.Secure;
|
||||
import android.service.quicksettings.Tile;
|
||||
import android.text.TextUtils;
|
||||
@@ -57,6 +56,7 @@ import com.android.systemui.statusbar.phone.StatusBarIconController;
|
||||
import com.android.systemui.tuner.TunerService;
|
||||
import com.android.systemui.tuner.TunerService.Tunable;
|
||||
import com.android.systemui.util.leak.GarbageMonitor;
|
||||
import com.android.systemui.util.settings.SecureSettings;
|
||||
|
||||
import java.io.FileDescriptor;
|
||||
import java.io.PrintWriter;
|
||||
@@ -79,6 +79,7 @@ public class QSTileHost implements QSHost, Tunable, PluginListener<QSFactory>, D
|
||||
private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
|
||||
private static final int MAX_QS_INSTANCE_ID = 1 << 20;
|
||||
|
||||
public static final int POSITION_AT_END = -1;
|
||||
public static final String TILES_SETTING = Secure.QS_TILES;
|
||||
|
||||
private final Context mContext;
|
||||
@@ -101,6 +102,7 @@ public class QSTileHost implements QSHost, Tunable, PluginListener<QSFactory>, D
|
||||
private final Optional<StatusBar> mStatusBarOptional;
|
||||
private Context mUserContext;
|
||||
private UserTracker mUserTracker;
|
||||
private SecureSettings mSecureSettings;
|
||||
|
||||
@Inject
|
||||
public QSTileHost(Context context,
|
||||
@@ -116,7 +118,8 @@ public class QSTileHost implements QSHost, Tunable, PluginListener<QSFactory>, D
|
||||
Optional<StatusBar> statusBarOptional,
|
||||
QSLogger qsLogger,
|
||||
UiEventLogger uiEventLogger,
|
||||
UserTracker userTracker) {
|
||||
UserTracker userTracker,
|
||||
SecureSettings secureSettings) {
|
||||
mIconController = iconController;
|
||||
mContext = context;
|
||||
mUserContext = context;
|
||||
@@ -135,6 +138,7 @@ public class QSTileHost implements QSHost, Tunable, PluginListener<QSFactory>, D
|
||||
pluginManager.addPluginListener(this, QSFactory.class, true);
|
||||
mDumpManager.registerDumpable(TAG, this);
|
||||
mUserTracker = userTracker;
|
||||
mSecureSettings = secureSettings;
|
||||
|
||||
mainHandler.post(() -> {
|
||||
// This is technically a hack to avoid circular dependency of
|
||||
@@ -343,19 +347,43 @@ public class QSTileHost implements QSHost, Tunable, PluginListener<QSFactory>, D
|
||||
if (mAutoTiles != null) mAutoTiles.unmarkTileAsAutoAdded(spec);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a tile to the end
|
||||
*
|
||||
* @param spec string matching a pre-defined tilespec
|
||||
*/
|
||||
public void addTile(String spec) {
|
||||
changeTileSpecs(tileSpecs-> !tileSpecs.contains(spec) && tileSpecs.add(spec));
|
||||
addTile(spec, POSITION_AT_END);
|
||||
}
|
||||
|
||||
private void saveTilesToSettings(List<String> tileSpecs) {
|
||||
Settings.Secure.putStringForUser(mContext.getContentResolver(), TILES_SETTING,
|
||||
TextUtils.join(",", tileSpecs), null /* tag */,
|
||||
false /* default */, mCurrentUser, true /* overrideable by restore */);
|
||||
/**
|
||||
* Add a tile into the requested spot, or at the end if the position is greater than the number
|
||||
* of tiles.
|
||||
* @param spec string matching a pre-defined tilespec
|
||||
* @param requestPosition -1 for end, 0 for beginning, or X for insertion at position X
|
||||
*/
|
||||
public void addTile(String spec, int requestPosition) {
|
||||
changeTileSpecs(tileSpecs -> {
|
||||
if (tileSpecs.contains(spec)) return false;
|
||||
|
||||
int size = tileSpecs.size();
|
||||
if (requestPosition == POSITION_AT_END || requestPosition >= size) {
|
||||
tileSpecs.add(spec);
|
||||
} else {
|
||||
tileSpecs.add(requestPosition, spec);
|
||||
}
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
void saveTilesToSettings(List<String> tileSpecs) {
|
||||
mSecureSettings.putStringForUser(TILES_SETTING, TextUtils.join(",", tileSpecs),
|
||||
null /* tag */, false /* default */, mCurrentUser,
|
||||
true /* overrideable by restore */);
|
||||
}
|
||||
|
||||
private void changeTileSpecs(Predicate<List<String>> changeFunction) {
|
||||
final String setting = Settings.Secure.getStringForUser(mContext.getContentResolver(),
|
||||
TILES_SETTING, mCurrentUser);
|
||||
final String setting = mSecureSettings.getStringForUser(TILES_SETTING, mCurrentUser);
|
||||
final List<String> tileSpecs = loadTileSpecs(mContext, setting);
|
||||
if (changeFunction.test(tileSpecs)) {
|
||||
saveTilesToSettings(tileSpecs);
|
||||
|
||||
@@ -32,6 +32,7 @@ import com.android.systemui.statusbar.phone.AutoTileManager;
|
||||
import com.android.systemui.statusbar.phone.ManagedProfileController;
|
||||
import com.android.systemui.statusbar.policy.CastController;
|
||||
import com.android.systemui.statusbar.policy.DataSaverController;
|
||||
import com.android.systemui.statusbar.policy.DeviceControlsController;
|
||||
import com.android.systemui.statusbar.policy.HotspotController;
|
||||
import com.android.systemui.util.settings.SecureSettings;
|
||||
|
||||
@@ -61,6 +62,7 @@ public interface QSModule {
|
||||
NightDisplayListener nightDisplayListener,
|
||||
CastController castController,
|
||||
ReduceBrightColorsController reduceBrightColorsController,
|
||||
DeviceControlsController deviceControlsController,
|
||||
@Named(RBC_AVAILABLE) boolean isReduceBrightColorsAvailable) {
|
||||
AutoTileManager manager = new AutoTileManager(
|
||||
context,
|
||||
@@ -74,6 +76,7 @@ public interface QSModule {
|
||||
nightDisplayListener,
|
||||
castController,
|
||||
reduceBrightColorsController,
|
||||
deviceControlsController,
|
||||
isReduceBrightColorsAvailable
|
||||
);
|
||||
manager.init();
|
||||
|
||||
@@ -39,8 +39,6 @@ import com.android.systemui.plugins.statusbar.StatusBarStateController
|
||||
import com.android.systemui.qs.QSHost
|
||||
import com.android.systemui.qs.logging.QSLogger
|
||||
import com.android.systemui.qs.tileimpl.QSTileImpl
|
||||
import com.android.systemui.statusbar.FeatureFlags
|
||||
import com.android.systemui.util.settings.GlobalSettings
|
||||
import java.util.concurrent.atomic.AtomicBoolean
|
||||
import javax.inject.Inject
|
||||
|
||||
@@ -53,9 +51,7 @@ class DeviceControlsTile @Inject constructor(
|
||||
statusBarStateController: StatusBarStateController,
|
||||
activityStarter: ActivityStarter,
|
||||
qsLogger: QSLogger,
|
||||
private val controlsComponent: ControlsComponent,
|
||||
private val featureFlags: FeatureFlags,
|
||||
globalSettings: GlobalSettings
|
||||
private val controlsComponent: ControlsComponent
|
||||
) : QSTileImpl<QSTile.State>(
|
||||
host,
|
||||
backgroundLooper,
|
||||
@@ -67,11 +63,6 @@ class DeviceControlsTile @Inject constructor(
|
||||
qsLogger
|
||||
) {
|
||||
|
||||
companion object {
|
||||
const val SETTINGS_FLAG = "controls_lockscreen"
|
||||
}
|
||||
|
||||
private val controlsLockscreen = globalSettings.getInt(SETTINGS_FLAG, 0) != 0
|
||||
private var hasControlsApps = AtomicBoolean(false)
|
||||
private val intent = Intent(Settings.ACTION_DEVICE_CONTROLS_SETTINGS)
|
||||
|
||||
@@ -92,9 +83,7 @@ class DeviceControlsTile @Inject constructor(
|
||||
}
|
||||
|
||||
override fun isAvailable(): Boolean {
|
||||
return featureFlags.isKeyguardLayoutEnabled &&
|
||||
controlsLockscreen &&
|
||||
controlsComponent.getControlsController().isPresent
|
||||
return controlsComponent.getControlsController().isPresent
|
||||
}
|
||||
|
||||
override fun newTileState(): QSTile.State {
|
||||
@@ -114,7 +103,7 @@ class DeviceControlsTile @Inject constructor(
|
||||
val i = Intent().apply {
|
||||
component = ComponentName(mContext, ControlsActivity::class.java)
|
||||
addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP or Intent.FLAG_ACTIVITY_NEW_TASK)
|
||||
putExtra(ControlsUiController.BACK_TO_GLOBAL_ACTIONS, false)
|
||||
putExtra(ControlsUiController.EXTRA_ANIMATE, true)
|
||||
}
|
||||
mContext.startActivity(i)
|
||||
}
|
||||
|
||||
@@ -36,6 +36,7 @@ import com.android.systemui.statusbar.policy.CastController;
|
||||
import com.android.systemui.statusbar.policy.CastController.CastDevice;
|
||||
import com.android.systemui.statusbar.policy.DataSaverController;
|
||||
import com.android.systemui.statusbar.policy.DataSaverController.Listener;
|
||||
import com.android.systemui.statusbar.policy.DeviceControlsController;
|
||||
import com.android.systemui.statusbar.policy.HotspotController;
|
||||
import com.android.systemui.statusbar.policy.HotspotController.Callback;
|
||||
import com.android.systemui.util.UserAwareController;
|
||||
@@ -58,6 +59,7 @@ public class AutoTileManager implements UserAwareController {
|
||||
public static final String WORK = "work";
|
||||
public static final String NIGHT = "night";
|
||||
public static final String CAST = "cast";
|
||||
public static final String DEVICE_CONTROLS = "controls";
|
||||
public static final String BRIGHTNESS = "reduce_brightness";
|
||||
static final String SETTING_SEPARATOR = ":";
|
||||
|
||||
@@ -74,6 +76,7 @@ public class AutoTileManager implements UserAwareController {
|
||||
private final ManagedProfileController mManagedProfileController;
|
||||
private final NightDisplayListener mNightDisplayListener;
|
||||
private final CastController mCastController;
|
||||
private final DeviceControlsController mDeviceControlsController;
|
||||
private final ReduceBrightColorsController mReduceBrightColorsController;
|
||||
private final boolean mIsReduceBrightColorsAvailable;
|
||||
private final ArrayList<AutoAddSetting> mAutoAddSettingList = new ArrayList<>();
|
||||
@@ -88,6 +91,7 @@ public class AutoTileManager implements UserAwareController {
|
||||
NightDisplayListener nightDisplayListener,
|
||||
CastController castController,
|
||||
ReduceBrightColorsController reduceBrightColorsController,
|
||||
DeviceControlsController deviceControlsController,
|
||||
@Named(RBC_AVAILABLE) boolean isReduceBrightColorsAvailable) {
|
||||
mContext = context;
|
||||
mHost = host;
|
||||
@@ -102,6 +106,7 @@ public class AutoTileManager implements UserAwareController {
|
||||
mCastController = castController;
|
||||
mReduceBrightColorsController = reduceBrightColorsController;
|
||||
mIsReduceBrightColorsAvailable = isReduceBrightColorsAvailable;
|
||||
mDeviceControlsController = deviceControlsController;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -138,6 +143,9 @@ public class AutoTileManager implements UserAwareController {
|
||||
if (!mAutoTracker.isAdded(BRIGHTNESS) && mIsReduceBrightColorsAvailable) {
|
||||
mReduceBrightColorsController.addCallback(mReduceBrightColorsCallback);
|
||||
}
|
||||
if (!mAutoTracker.isAdded(DEVICE_CONTROLS)) {
|
||||
mDeviceControlsController.setCallback(mDeviceControlsCallback);
|
||||
}
|
||||
|
||||
int settingsN = mAutoAddSettingList.size();
|
||||
for (int i = 0; i < settingsN; i++) {
|
||||
@@ -158,6 +166,7 @@ public class AutoTileManager implements UserAwareController {
|
||||
mReduceBrightColorsController.removeCallback(mReduceBrightColorsCallback);
|
||||
}
|
||||
mCastController.removeCallback(mCastCallback);
|
||||
mDeviceControlsController.removeCallback();
|
||||
int settingsN = mAutoAddSettingList.size();
|
||||
for (int i = 0; i < settingsN; i++) {
|
||||
mAutoAddSettingList.get(i).setListening(false);
|
||||
@@ -274,6 +283,17 @@ public class AutoTileManager implements UserAwareController {
|
||||
}
|
||||
};
|
||||
|
||||
private final DeviceControlsController.Callback mDeviceControlsCallback =
|
||||
new DeviceControlsController.Callback() {
|
||||
@Override
|
||||
public void onControlsAvailable(int position) {
|
||||
if (mAutoTracker.isAdded(DEVICE_CONTROLS)) return;
|
||||
mHost.addTile(DEVICE_CONTROLS, position);
|
||||
mAutoTracker.setTileAdded(DEVICE_CONTROLS);
|
||||
mHandler.post(() -> mDeviceControlsController.removeCallback());
|
||||
}
|
||||
};
|
||||
|
||||
@VisibleForTesting
|
||||
final NightDisplayListener.Callback mNightDisplayCallback =
|
||||
new NightDisplayListener.Callback() {
|
||||
|
||||
@@ -0,0 +1,35 @@
|
||||
/*
|
||||
* Copyright (C) 2021 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
|
||||
|
||||
/**
|
||||
* Supports adding a DeviceControls QS tile
|
||||
*/
|
||||
interface DeviceControlsController {
|
||||
interface Callback {
|
||||
/**
|
||||
* If controls become available, initiate this callback with the desired position
|
||||
*/
|
||||
fun onControlsAvailable(position: Int)
|
||||
}
|
||||
|
||||
/** Add callback, supporting only a single callback at once */
|
||||
fun setCallback(callback: Callback)
|
||||
|
||||
/** Remove any existing callback, if any */
|
||||
fun removeCallback()
|
||||
}
|
||||
@@ -0,0 +1,173 @@
|
||||
/*
|
||||
* Copyright (C) 2021 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.content.ComponentName
|
||||
import android.content.Context
|
||||
import android.content.SharedPreferences
|
||||
import android.util.Log
|
||||
|
||||
import com.android.systemui.R
|
||||
import com.android.systemui.controls.ControlsServiceInfo
|
||||
import com.android.systemui.controls.dagger.ControlsComponent
|
||||
import com.android.systemui.controls.management.ControlsListingController
|
||||
import com.android.systemui.dagger.SysUISingleton
|
||||
import com.android.systemui.qs.QSTileHost.POSITION_AT_END
|
||||
import com.android.systemui.settings.UserContextProvider
|
||||
import com.android.systemui.statusbar.policy.DeviceControlsController.Callback
|
||||
|
||||
import javax.inject.Inject
|
||||
|
||||
/**
|
||||
* Watches for Device Controls QS Tile activation, which can happen in two ways:
|
||||
* <ol>
|
||||
* <li>Migration from Power Menu - For existing Android 11 users, create a tile in a high
|
||||
* priority position.
|
||||
* <li>Device controls service becomes available - For non-migrated users, create a tile and
|
||||
* place at the end of active tiles, and initiate seeding where possible.
|
||||
* </ol>
|
||||
*/
|
||||
@SysUISingleton
|
||||
public class DeviceControlsControllerImpl @Inject constructor(
|
||||
private val context: Context,
|
||||
private val controlsComponent: ControlsComponent,
|
||||
private val userContextProvider: UserContextProvider
|
||||
) : DeviceControlsController {
|
||||
|
||||
private var callback: Callback? = null
|
||||
internal var position: Int? = null
|
||||
|
||||
private val listingCallback = object : ControlsListingController.ControlsListingCallback {
|
||||
override fun onServicesUpdated(serviceInfos: List<ControlsServiceInfo>) {
|
||||
if (!serviceInfos.isEmpty()) {
|
||||
seedFavorites(serviceInfos)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
private const val TAG = "DeviceControlsControllerImpl"
|
||||
internal const val QS_PRIORITY_POSITION = 3
|
||||
internal const val QS_DEFAULT_POSITION = POSITION_AT_END
|
||||
|
||||
internal const val PREFS_CONTROLS_SEEDING_COMPLETED = "SeedingCompleted"
|
||||
internal const val PREFS_CONTROLS_FILE = "controls_prefs"
|
||||
private const val SEEDING_MAX = 2
|
||||
}
|
||||
|
||||
private fun checkMigrationToQs() {
|
||||
controlsComponent.getControlsController().ifPresent {
|
||||
if (!it.getFavorites().isEmpty()) {
|
||||
position = QS_PRIORITY_POSITION
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This migration logic assumes that something like [AutoTileManager] is tracking state
|
||||
* externally, and won't call this method after receiving a response via
|
||||
* [Callback#onControlsAvailable], once per user. Otherwise the calculated position may be
|
||||
* incorrect.
|
||||
*/
|
||||
override fun setCallback(callback: Callback) {
|
||||
// Treat any additional call as a reset before recalculating
|
||||
removeCallback()
|
||||
|
||||
checkMigrationToQs()
|
||||
controlsComponent.getControlsListingController().ifPresent {
|
||||
it.addCallback(listingCallback)
|
||||
}
|
||||
|
||||
this.callback = callback
|
||||
fireControlsAvailable()
|
||||
}
|
||||
|
||||
override fun removeCallback() {
|
||||
position = null
|
||||
callback = null
|
||||
controlsComponent.getControlsListingController().ifPresent {
|
||||
it.removeCallback(listingCallback)
|
||||
}
|
||||
}
|
||||
|
||||
private fun fireControlsAvailable() {
|
||||
position?.let {
|
||||
Log.i(TAG, "Setting DeviceControlsTile position: $it")
|
||||
callback?.onControlsAvailable(it)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* See if any available control service providers match one of the preferred components. If
|
||||
* they do, and there are no current favorites for that component, query the preferred
|
||||
* component for a limited number of suggested controls.
|
||||
*/
|
||||
private fun seedFavorites(serviceInfos: List<ControlsServiceInfo>) {
|
||||
val preferredControlsPackages = context.getResources().getStringArray(
|
||||
R.array.config_controlsPreferredPackages)
|
||||
|
||||
val prefs = userContextProvider.userContext.getSharedPreferences(
|
||||
PREFS_CONTROLS_FILE, Context.MODE_PRIVATE)
|
||||
val seededPackages = prefs.getStringSet(PREFS_CONTROLS_SEEDING_COMPLETED, emptySet())
|
||||
|
||||
val controlsController = controlsComponent.getControlsController().get()
|
||||
val componentsToSeed = mutableListOf<ComponentName>()
|
||||
var i = 0
|
||||
while (i < Math.min(SEEDING_MAX, preferredControlsPackages.size)) {
|
||||
val pkg = preferredControlsPackages[i]
|
||||
serviceInfos.forEach {
|
||||
if (pkg.equals(it.componentName.packageName) && !seededPackages.contains(pkg)) {
|
||||
if (controlsController.countFavoritesForComponent(it.componentName) > 0) {
|
||||
// When there are existing controls but no saved preference, assume it
|
||||
// is out of sync, perhaps through a device restore, and update the
|
||||
// preference
|
||||
addPackageToSeededSet(prefs, pkg)
|
||||
} else {
|
||||
componentsToSeed.add(it.componentName)
|
||||
}
|
||||
}
|
||||
}
|
||||
i++
|
||||
}
|
||||
|
||||
if (componentsToSeed.isEmpty()) return
|
||||
|
||||
controlsController.seedFavoritesForComponents(
|
||||
componentsToSeed,
|
||||
{ response ->
|
||||
Log.d(TAG, "Controls seeded: $response")
|
||||
if (response.accepted) {
|
||||
addPackageToSeededSet(prefs, response.packageName)
|
||||
if (position == null) {
|
||||
position = QS_DEFAULT_POSITION
|
||||
}
|
||||
fireControlsAvailable()
|
||||
|
||||
controlsComponent.getControlsListingController().ifPresent {
|
||||
it.removeCallback(listingCallback)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
private fun addPackageToSeededSet(prefs: SharedPreferences, pkg: String) {
|
||||
val seededPackages = prefs.getStringSet(PREFS_CONTROLS_SEEDING_COMPLETED, emptySet())
|
||||
val updatedPkgs = seededPackages.toMutableSet()
|
||||
updatedPkgs.add(pkg)
|
||||
prefs.edit().putStringSet(PREFS_CONTROLS_SEEDING_COMPLETED, updatedPkgs).apply()
|
||||
}
|
||||
}
|
||||
@@ -26,6 +26,8 @@ import com.android.systemui.statusbar.policy.BluetoothController;
|
||||
import com.android.systemui.statusbar.policy.BluetoothControllerImpl;
|
||||
import com.android.systemui.statusbar.policy.CastController;
|
||||
import com.android.systemui.statusbar.policy.CastControllerImpl;
|
||||
import com.android.systemui.statusbar.policy.DeviceControlsController;
|
||||
import com.android.systemui.statusbar.policy.DeviceControlsControllerImpl;
|
||||
import com.android.systemui.statusbar.policy.ExtensionController;
|
||||
import com.android.systemui.statusbar.policy.ExtensionControllerImpl;
|
||||
import com.android.systemui.statusbar.policy.FlashlightController;
|
||||
@@ -44,8 +46,6 @@ import com.android.systemui.statusbar.policy.RotationLockController;
|
||||
import com.android.systemui.statusbar.policy.RotationLockControllerImpl;
|
||||
import com.android.systemui.statusbar.policy.SecurityController;
|
||||
import com.android.systemui.statusbar.policy.SecurityControllerImpl;
|
||||
import com.android.systemui.statusbar.policy.SensorPrivacyController;
|
||||
import com.android.systemui.statusbar.policy.SensorPrivacyControllerImpl;
|
||||
import com.android.systemui.statusbar.policy.UserInfoController;
|
||||
import com.android.systemui.statusbar.policy.UserInfoControllerImpl;
|
||||
import com.android.systemui.statusbar.policy.ZenModeController;
|
||||
@@ -113,6 +113,11 @@ public interface StatusBarPolicyModule {
|
||||
@Binds
|
||||
ZenModeController provideZenModeController(ZenModeControllerImpl controllerImpl);
|
||||
|
||||
/** */
|
||||
@Binds
|
||||
DeviceControlsController provideDeviceControlsController(
|
||||
DeviceControlsControllerImpl controllerImpl);
|
||||
|
||||
/** */
|
||||
@Binds
|
||||
NetworkController.AccessPointController provideAccessPointController(
|
||||
|
||||
@@ -58,15 +58,10 @@ import com.android.internal.widget.LockPatternUtils;
|
||||
import com.android.systemui.SysuiTestCase;
|
||||
import com.android.systemui.broadcast.BroadcastDispatcher;
|
||||
import com.android.systemui.colorextraction.SysuiColorExtractor;
|
||||
import com.android.systemui.controls.controller.ControlsController;
|
||||
import com.android.systemui.controls.dagger.ControlsComponent;
|
||||
import com.android.systemui.controls.management.ControlsListingController;
|
||||
import com.android.systemui.controls.ui.ControlsUiController;
|
||||
import com.android.systemui.model.SysUiState;
|
||||
import com.android.systemui.plugins.ActivityStarter;
|
||||
import com.android.systemui.plugins.GlobalActions;
|
||||
import com.android.systemui.plugins.GlobalActionsPanelPlugin;
|
||||
import com.android.systemui.settings.UserContextProvider;
|
||||
import com.android.systemui.settings.UserTracker;
|
||||
import com.android.systemui.statusbar.NotificationShadeDepthController;
|
||||
import com.android.systemui.statusbar.NotificationShadeWindowController;
|
||||
@@ -118,11 +113,8 @@ public class GlobalActionsDialogTest extends SysuiTestCase {
|
||||
@Mock private SysuiColorExtractor mColorExtractor;
|
||||
@Mock private IStatusBarService mStatusBarService;
|
||||
@Mock private NotificationShadeWindowController mNotificationShadeWindowController;
|
||||
@Mock private ControlsUiController mControlsUiController;
|
||||
@Mock private IWindowManager mWindowManager;
|
||||
@Mock private Executor mBackgroundExecutor;
|
||||
@Mock private ControlsListingController mControlsListingController;
|
||||
@Mock private ControlsController mControlsController;
|
||||
@Mock private UiEventLogger mUiEventLogger;
|
||||
@Mock private RingerModeTracker mRingerModeTracker;
|
||||
@Mock private RingerModeLiveData mRingerModeLiveData;
|
||||
@@ -130,10 +122,8 @@ public class GlobalActionsDialogTest extends SysuiTestCase {
|
||||
@Mock GlobalActionsPanelPlugin mWalletPlugin;
|
||||
@Mock GlobalActionsPanelPlugin.PanelViewController mWalletController;
|
||||
@Mock private Handler mHandler;
|
||||
@Mock private UserContextProvider mUserContextProvider;
|
||||
@Mock private UserTracker mUserTracker;
|
||||
@Mock private SecureSettings mSecureSettings;
|
||||
private ControlsComponent mControlsComponent;
|
||||
|
||||
private TestableLooper mTestableLooper;
|
||||
|
||||
@@ -144,18 +134,6 @@ public class GlobalActionsDialogTest extends SysuiTestCase {
|
||||
allowTestableLooperAsMainThread();
|
||||
|
||||
when(mRingerModeTracker.getRingerMode()).thenReturn(mRingerModeLiveData);
|
||||
when(mUserContextProvider.getUserContext()).thenReturn(mContext);
|
||||
mControlsComponent = new ControlsComponent(
|
||||
true,
|
||||
mContext,
|
||||
() -> mControlsController,
|
||||
() -> mControlsUiController,
|
||||
() -> mControlsListingController,
|
||||
mLockPatternUtils,
|
||||
mKeyguardStateController,
|
||||
mUserTracker,
|
||||
mSecureSettings
|
||||
);
|
||||
|
||||
mGlobalActionsDialog = new GlobalActionsDialog(mContext,
|
||||
mWindowManagerFuncs,
|
||||
@@ -186,9 +164,7 @@ public class GlobalActionsDialogTest extends SysuiTestCase {
|
||||
mUiEventLogger,
|
||||
mRingerModeTracker,
|
||||
mSysUiState,
|
||||
mHandler,
|
||||
mControlsComponent,
|
||||
mUserContextProvider
|
||||
mHandler
|
||||
);
|
||||
mGlobalActionsDialog.setZeroDialogPressDelayForTesting();
|
||||
|
||||
@@ -511,7 +487,7 @@ public class GlobalActionsDialogTest extends SysuiTestCase {
|
||||
when(mKeyguardStateController.isUnlocked()).thenReturn(false);
|
||||
when(mActivityManager.getCurrentUser()).thenReturn(newUserInfo());
|
||||
when(mLockPatternUtils.getStrongAuthForUser(anyInt())).thenReturn(STRONG_AUTH_NOT_REQUIRED);
|
||||
mGlobalActionsDialog.mShowLockScreenCardsAndControls = false;
|
||||
mGlobalActionsDialog.mShowLockScreenCards = false;
|
||||
setupDefaultActions();
|
||||
when(mWalletPlugin.onPanelShown(any(), anyBoolean())).thenReturn(mWalletController);
|
||||
when(mWalletController.getPanelContent()).thenReturn(new FrameLayout(mContext));
|
||||
@@ -528,14 +504,14 @@ public class GlobalActionsDialogTest extends SysuiTestCase {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testShouldNotShowLockScreenMessage_whenWalletOrControlsShownOnLockScreen()
|
||||
public void testShouldNotShowLockScreenMessage_whenWalletShownOnLockScreen()
|
||||
throws RemoteException {
|
||||
mGlobalActionsDialog = spy(mGlobalActionsDialog);
|
||||
mGlobalActionsDialog.mDialog = null;
|
||||
when(mKeyguardStateController.isUnlocked()).thenReturn(false);
|
||||
when(mActivityManager.getCurrentUser()).thenReturn(newUserInfo());
|
||||
when(mLockPatternUtils.getStrongAuthForUser(anyInt())).thenReturn(STRONG_AUTH_NOT_REQUIRED);
|
||||
mGlobalActionsDialog.mShowLockScreenCardsAndControls = true;
|
||||
mGlobalActionsDialog.mShowLockScreenCards = true;
|
||||
setupDefaultActions();
|
||||
when(mWalletPlugin.onPanelShown(any(), anyBoolean())).thenReturn(mWalletController);
|
||||
when(mWalletController.getPanelContent()).thenReturn(new FrameLayout(mContext));
|
||||
@@ -552,7 +528,7 @@ public class GlobalActionsDialogTest extends SysuiTestCase {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testShouldNotShowLockScreenMessage_whenControlsAndWalletBothDisabled()
|
||||
public void testShouldNotShowLockScreenMessage_whenWalletBothDisabled()
|
||||
throws RemoteException {
|
||||
mGlobalActionsDialog = spy(mGlobalActionsDialog);
|
||||
mGlobalActionsDialog.mDialog = null;
|
||||
@@ -560,11 +536,10 @@ public class GlobalActionsDialogTest extends SysuiTestCase {
|
||||
|
||||
when(mActivityManager.getCurrentUser()).thenReturn(newUserInfo());
|
||||
when(mLockPatternUtils.getStrongAuthForUser(anyInt())).thenReturn(STRONG_AUTH_NOT_REQUIRED);
|
||||
mGlobalActionsDialog.mShowLockScreenCardsAndControls = true;
|
||||
mGlobalActionsDialog.mShowLockScreenCards = true;
|
||||
setupDefaultActions();
|
||||
when(mWalletPlugin.onPanelShown(any(), anyBoolean())).thenReturn(mWalletController);
|
||||
when(mWalletController.getPanelContent()).thenReturn(null);
|
||||
when(mControlsUiController.getAvailable()).thenReturn(false);
|
||||
|
||||
mGlobalActionsDialog.showOrHideDialog(false, true, mWalletPlugin);
|
||||
|
||||
|
||||
@@ -61,6 +61,7 @@ import com.android.systemui.statusbar.policy.RemoteInputQuickSettingsDisabler;
|
||||
import com.android.systemui.statusbar.policy.UserSwitcherController;
|
||||
import com.android.systemui.tuner.TunerService;
|
||||
import com.android.systemui.util.InjectionInflationController;
|
||||
import com.android.systemui.util.settings.SecureSettings;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Ignore;
|
||||
@@ -130,7 +131,8 @@ public class QSFragmentTest extends SysuiBaseFragmentTest {
|
||||
mock(PluginManager.class), mock(TunerService.class),
|
||||
() -> mock(AutoTileManager.class), mock(DumpManager.class),
|
||||
mock(BroadcastDispatcher.class), Optional.of(mock(StatusBar.class)),
|
||||
mock(QSLogger.class), mock(UiEventLogger.class), mock(UserTracker.class));
|
||||
mock(QSLogger.class), mock(UiEventLogger.class), mock(UserTracker.class),
|
||||
mock(SecureSettings.class));
|
||||
qs.setHost(host);
|
||||
|
||||
qs.setListening(true);
|
||||
|
||||
@@ -21,20 +21,21 @@ import static junit.framework.Assert.assertEquals;
|
||||
import static junit.framework.Assert.assertTrue;
|
||||
import static junit.framework.TestCase.assertFalse;
|
||||
|
||||
import static org.mockito.ArgumentMatchers.anyInt;
|
||||
import static org.mockito.ArgumentMatchers.anyString;
|
||||
import static org.mockito.ArgumentMatchers.eq;
|
||||
import static org.mockito.ArgumentMatchers.isNull;
|
||||
import static org.mockito.Mockito.atLeastOnce;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.never;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import android.app.ActivityManager;
|
||||
import android.content.ComponentName;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.os.Handler;
|
||||
import android.os.Looper;
|
||||
import android.provider.Settings;
|
||||
import android.testing.AndroidTestingRunner;
|
||||
import android.testing.TestableLooper;
|
||||
import android.testing.TestableLooper.RunWithLooper;
|
||||
@@ -64,11 +65,13 @@ import com.android.systemui.statusbar.phone.AutoTileManager;
|
||||
import com.android.systemui.statusbar.phone.StatusBar;
|
||||
import com.android.systemui.statusbar.phone.StatusBarIconController;
|
||||
import com.android.systemui.tuner.TunerService;
|
||||
import com.android.systemui.util.settings.SecureSettings;
|
||||
|
||||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.ArgumentCaptor;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.MockitoAnnotations;
|
||||
import org.mockito.MockitoSession;
|
||||
@@ -78,7 +81,6 @@ import java.io.FileDescriptor;
|
||||
import java.io.PrintWriter;
|
||||
import java.io.StringWriter;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
|
||||
import javax.inject.Provider;
|
||||
@@ -119,6 +121,8 @@ public class QSTileHostTest extends SysuiTestCase {
|
||||
private UiEventLogger mUiEventLogger;
|
||||
@Mock
|
||||
private UserTracker mUserTracker;
|
||||
@Mock
|
||||
private SecureSettings mSecureSettings;
|
||||
|
||||
private Handler mHandler;
|
||||
private TestableLooper mLooper;
|
||||
@@ -138,11 +142,12 @@ public class QSTileHostTest extends SysuiTestCase {
|
||||
mHandler = new Handler(mLooper.getLooper());
|
||||
mQSTileHost = new TestQSTileHost(mContext, mIconController, mDefaultFactory, mHandler,
|
||||
mLooper.getLooper(), mPluginManager, mTunerService, mAutoTiles, mDumpManager,
|
||||
mBroadcastDispatcher, mStatusBar, mQSLogger, mUiEventLogger, mUserTracker);
|
||||
mBroadcastDispatcher, mStatusBar, mQSLogger, mUiEventLogger, mUserTracker,
|
||||
mSecureSettings);
|
||||
setUpTileFactory();
|
||||
|
||||
Settings.Secure.putStringForUser(mContext.getContentResolver(), QSTileHost.TILES_SETTING,
|
||||
"", ActivityManager.getCurrentUser());
|
||||
when(mSecureSettings.getStringForUser(eq(QSTileHost.TILES_SETTING), anyInt()))
|
||||
.thenReturn("");
|
||||
}
|
||||
|
||||
@After
|
||||
@@ -162,6 +167,8 @@ public class QSTileHostTest extends SysuiTestCase {
|
||||
return new TestTile1(mQSTileHost);
|
||||
} else if ("spec2".equals(spec)) {
|
||||
return new TestTile2(mQSTileHost);
|
||||
} else if ("spec3".equals(spec)) {
|
||||
return new TestTile3(mQSTileHost);
|
||||
} else if ("na".equals(spec)) {
|
||||
return new NotAvailableTile(mQSTileHost);
|
||||
} else if (CUSTOM_TILE_SPEC.equals(spec)) {
|
||||
@@ -232,6 +239,8 @@ public class QSTileHostTest extends SysuiTestCase {
|
||||
|
||||
@Test
|
||||
public void testNoRepeatedSpecs_addTile() {
|
||||
mContext.getOrCreateTestableResources()
|
||||
.addOverride(R.string.quick_settings_tiles, "spec1,spec2");
|
||||
mQSTileHost.onTuningChanged(QSTileHost.TILES_SETTING, "spec1,spec2");
|
||||
|
||||
mQSTileHost.addTile("spec1");
|
||||
@@ -241,6 +250,48 @@ public class QSTileHostTest extends SysuiTestCase {
|
||||
assertEquals("spec2", mQSTileHost.mTileSpecs.get(1));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAddTileAtValidPosition() {
|
||||
mContext.getOrCreateTestableResources()
|
||||
.addOverride(R.string.quick_settings_tiles, "spec1,spec3");
|
||||
mQSTileHost.onTuningChanged(QSTileHost.TILES_SETTING, "spec1,spec3");
|
||||
|
||||
mQSTileHost.addTile("spec2", 1);
|
||||
|
||||
assertEquals(3, mQSTileHost.mTileSpecs.size());
|
||||
assertEquals("spec1", mQSTileHost.mTileSpecs.get(0));
|
||||
assertEquals("spec2", mQSTileHost.mTileSpecs.get(1));
|
||||
assertEquals("spec3", mQSTileHost.mTileSpecs.get(2));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAddTileAtInvalidPositionAddsToEnd() {
|
||||
mContext.getOrCreateTestableResources()
|
||||
.addOverride(R.string.quick_settings_tiles, "spec1,spec3");
|
||||
mQSTileHost.onTuningChanged(QSTileHost.TILES_SETTING, "spec1,spec3");
|
||||
|
||||
mQSTileHost.addTile("spec2", 100);
|
||||
|
||||
assertEquals(3, mQSTileHost.mTileSpecs.size());
|
||||
assertEquals("spec1", mQSTileHost.mTileSpecs.get(0));
|
||||
assertEquals("spec3", mQSTileHost.mTileSpecs.get(1));
|
||||
assertEquals("spec2", mQSTileHost.mTileSpecs.get(2));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAddTileAtEnd() {
|
||||
mContext.getOrCreateTestableResources()
|
||||
.addOverride(R.string.quick_settings_tiles, "spec1,spec3");
|
||||
mQSTileHost.onTuningChanged(QSTileHost.TILES_SETTING, "spec1,spec3");
|
||||
|
||||
mQSTileHost.addTile("spec2", QSTileHost.POSITION_AT_END);
|
||||
|
||||
assertEquals(3, mQSTileHost.mTileSpecs.size());
|
||||
assertEquals("spec1", mQSTileHost.mTileSpecs.get(0));
|
||||
assertEquals("spec3", mQSTileHost.mTileSpecs.get(1));
|
||||
assertEquals("spec2", mQSTileHost.mTileSpecs.get(2));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testNoRepeatedSpecs_customTile() {
|
||||
mQSTileHost.onTuningChanged(QSTileHost.TILES_SETTING, CUSTOM_TILE_SPEC);
|
||||
@@ -318,16 +369,17 @@ public class QSTileHostTest extends SysuiTestCase {
|
||||
verify(mQSLogger, never()).logTileDestroyed(isNull(), anyString());
|
||||
}
|
||||
|
||||
private static class TestQSTileHost extends QSTileHost {
|
||||
private class TestQSTileHost extends QSTileHost {
|
||||
TestQSTileHost(Context context, StatusBarIconController iconController,
|
||||
QSFactory defaultFactory, Handler mainHandler, Looper bgLooper,
|
||||
PluginManager pluginManager, TunerService tunerService,
|
||||
Provider<AutoTileManager> autoTiles, DumpManager dumpManager,
|
||||
BroadcastDispatcher broadcastDispatcher, StatusBar statusBar, QSLogger qsLogger,
|
||||
UiEventLogger uiEventLogger, UserTracker userTracker) {
|
||||
UiEventLogger uiEventLogger, UserTracker userTracker,
|
||||
SecureSettings secureSettings) {
|
||||
super(context, iconController, defaultFactory, mainHandler, bgLooper, pluginManager,
|
||||
tunerService, autoTiles, dumpManager, broadcastDispatcher,
|
||||
Optional.of(statusBar), qsLogger, uiEventLogger, userTracker);
|
||||
Optional.of(statusBar), qsLogger, uiEventLogger, userTracker, secureSettings);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -339,18 +391,16 @@ public class QSTileHostTest extends SysuiTestCase {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void changeTiles(List<String> previousTiles, List<String> newTiles) {
|
||||
String previousSetting = Settings.Secure.getStringForUser(
|
||||
getContext().getContentResolver(), TILES_SETTING,
|
||||
ActivityManager.getCurrentUser());
|
||||
super.changeTiles(previousTiles, newTiles);
|
||||
void saveTilesToSettings(List<String> tileSpecs) {
|
||||
super.saveTilesToSettings(tileSpecs);
|
||||
|
||||
ArgumentCaptor<String> specs = ArgumentCaptor.forClass(String.class);
|
||||
verify(mSecureSettings, atLeastOnce()).putStringForUser(eq(QSTileHost.TILES_SETTING),
|
||||
specs.capture(), isNull(), eq(false), anyInt(), eq(true));
|
||||
|
||||
// After tiles are changed, make sure to call onTuningChanged with the new setting if it
|
||||
// changed
|
||||
String newSetting = Settings.Secure.getStringForUser(getContext().getContentResolver(),
|
||||
TILES_SETTING, ActivityManager.getCurrentUser());
|
||||
if (!Objects.equals(newSetting, previousSetting)) {
|
||||
onTuningChanged(TILES_SETTING, newSetting);
|
||||
}
|
||||
onTuningChanged(TILES_SETTING, specs.getValue());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -415,6 +465,13 @@ public class QSTileHostTest extends SysuiTestCase {
|
||||
}
|
||||
}
|
||||
|
||||
private class TestTile3 extends TestTile {
|
||||
|
||||
protected TestTile3(QSHost host) {
|
||||
super(host);
|
||||
}
|
||||
}
|
||||
|
||||
private class NotAvailableTile extends TestTile {
|
||||
|
||||
protected NotAvailableTile(QSHost host) {
|
||||
|
||||
@@ -53,6 +53,7 @@ import com.android.systemui.statusbar.phone.StatusBar;
|
||||
import com.android.systemui.statusbar.phone.StatusBarIconController;
|
||||
import com.android.systemui.statusbar.policy.BluetoothController;
|
||||
import com.android.systemui.tuner.TunerService;
|
||||
import com.android.systemui.util.settings.SecureSettings;
|
||||
|
||||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
@@ -95,6 +96,8 @@ public class TileServicesTest extends SysuiTestCase {
|
||||
private UiEventLogger mUiEventLogger;
|
||||
@Mock
|
||||
private UserTracker mUserTracker;
|
||||
@Mock
|
||||
private SecureSettings mSecureSettings;
|
||||
|
||||
@Before
|
||||
public void setUp() throws Exception {
|
||||
@@ -114,7 +117,8 @@ public class TileServicesTest extends SysuiTestCase {
|
||||
Optional.of(mStatusBar),
|
||||
mQSLogger,
|
||||
mUiEventLogger,
|
||||
mUserTracker);
|
||||
mUserTracker,
|
||||
mSecureSettings);
|
||||
mTileService = new TestTileServices(host, Looper.getMainLooper(), mBroadcastDispatcher,
|
||||
mUserTracker);
|
||||
}
|
||||
|
||||
@@ -38,11 +38,9 @@ import com.android.systemui.plugins.ActivityStarter
|
||||
import com.android.systemui.plugins.statusbar.StatusBarStateController
|
||||
import com.android.systemui.qs.QSHost
|
||||
import com.android.systemui.qs.logging.QSLogger
|
||||
import com.android.systemui.statusbar.FeatureFlags
|
||||
import com.android.systemui.util.mockito.any
|
||||
import com.android.systemui.util.mockito.capture
|
||||
import com.android.systemui.util.settings.FakeSettings
|
||||
import com.android.systemui.util.settings.GlobalSettings
|
||||
import com.android.systemui.util.settings.SecureSettings
|
||||
import com.google.common.truth.Truth.assertThat
|
||||
import org.junit.Before
|
||||
@@ -83,9 +81,6 @@ class DeviceControlsTileTest : SysuiTestCase() {
|
||||
@Mock
|
||||
private lateinit var controlsController: ControlsController
|
||||
@Mock
|
||||
private lateinit var featureFlags: FeatureFlags
|
||||
private lateinit var globalSettings: GlobalSettings
|
||||
@Mock
|
||||
private lateinit var serviceInfo: ControlsServiceInfo
|
||||
@Mock
|
||||
private lateinit var uiEventLogger: UiEventLogger
|
||||
@@ -116,11 +111,6 @@ class DeviceControlsTileTest : SysuiTestCase() {
|
||||
|
||||
setupControlsComponent()
|
||||
|
||||
globalSettings = FakeSettings()
|
||||
|
||||
globalSettings.putInt(DeviceControlsTile.SETTINGS_FLAG, 1)
|
||||
`when`(featureFlags.isKeyguardLayoutEnabled).thenReturn(true)
|
||||
|
||||
tile = createTile()
|
||||
}
|
||||
|
||||
@@ -155,13 +145,6 @@ class DeviceControlsTileTest : SysuiTestCase() {
|
||||
assertThat(tile.isAvailable).isTrue()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testNotAvailableFeature() {
|
||||
`when`(featureFlags.isKeyguardLayoutEnabled).thenReturn(false)
|
||||
|
||||
assertThat(tile.isAvailable).isFalse()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testNotAvailableControls() {
|
||||
featureEnabled = false
|
||||
@@ -178,14 +161,6 @@ class DeviceControlsTileTest : SysuiTestCase() {
|
||||
assertThat(tile.isAvailable).isTrue()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testNotAvailableControlsLockscreenFlag() {
|
||||
globalSettings.putInt(DeviceControlsTile.SETTINGS_FLAG, 0)
|
||||
tile = createTile()
|
||||
|
||||
assertThat(tile.isAvailable).isFalse()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testObservingCallback() {
|
||||
verify(controlsListingController).observe(
|
||||
@@ -329,9 +304,7 @@ class DeviceControlsTileTest : SysuiTestCase() {
|
||||
statusBarStateController,
|
||||
activityStarter,
|
||||
qsLogger,
|
||||
controlsComponent,
|
||||
featureFlags,
|
||||
globalSettings
|
||||
controlsComponent
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -54,6 +54,7 @@ import com.android.systemui.qs.SecureSetting;
|
||||
import com.android.systemui.statusbar.policy.CastController;
|
||||
import com.android.systemui.statusbar.policy.CastController.CastDevice;
|
||||
import com.android.systemui.statusbar.policy.DataSaverController;
|
||||
import com.android.systemui.statusbar.policy.DeviceControlsController;
|
||||
import com.android.systemui.statusbar.policy.HotspotController;
|
||||
import com.android.systemui.util.settings.FakeSettings;
|
||||
import com.android.systemui.util.settings.SecureSettings;
|
||||
@@ -94,6 +95,7 @@ public class AutoTileManagerTest extends SysuiTestCase {
|
||||
@Mock private ManagedProfileController mManagedProfileController;
|
||||
@Mock private NightDisplayListener mNightDisplayListener;
|
||||
@Mock private ReduceBrightColorsController mReduceBrightColorsController;
|
||||
@Mock private DeviceControlsController mDeviceControlsController;;
|
||||
@Mock(answer = Answers.RETURNS_SELF)
|
||||
private AutoAddTracker.Builder mAutoAddTrackerBuilder;
|
||||
@Mock private Context mUserContext;
|
||||
@@ -139,6 +141,7 @@ public class AutoTileManagerTest extends SysuiTestCase {
|
||||
NightDisplayListener nightDisplayListener,
|
||||
CastController castController,
|
||||
ReduceBrightColorsController reduceBrightColorsController,
|
||||
DeviceControlsController deviceControlsController,
|
||||
@Named(RBC_AVAILABLE) boolean isReduceBrightColorsAvailable) {
|
||||
return new AutoTileManager(context, autoAddTrackerBuilder, mQsTileHost,
|
||||
Handler.createAsync(TestableLooper.get(this).getLooper()),
|
||||
@@ -149,13 +152,15 @@ public class AutoTileManagerTest extends SysuiTestCase {
|
||||
nightDisplayListener,
|
||||
castController,
|
||||
reduceBrightColorsController,
|
||||
deviceControlsController,
|
||||
isReduceBrightColorsAvailable);
|
||||
}
|
||||
|
||||
private AutoTileManager createAutoTileManager(Context context) {
|
||||
return createAutoTileManager(context, mAutoAddTrackerBuilder, mHotspotController,
|
||||
mDataSaverController, mManagedProfileController, mNightDisplayListener,
|
||||
mCastController, mReduceBrightColorsController, mIsReduceBrightColorsAvailable);
|
||||
mCastController, mReduceBrightColorsController, mDeviceControlsController,
|
||||
mIsReduceBrightColorsAvailable);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -169,10 +174,11 @@ public class AutoTileManagerTest extends SysuiTestCase {
|
||||
NightDisplayListener nDS = mock(NightDisplayListener.class);
|
||||
CastController cC = mock(CastController.class);
|
||||
ReduceBrightColorsController rBC = mock(ReduceBrightColorsController.class);
|
||||
DeviceControlsController dCC = mock(DeviceControlsController.class);
|
||||
|
||||
AutoTileManager manager =
|
||||
createAutoTileManager(mock(Context.class), builder, hC, dSC, mPC, nDS, cC, rBC,
|
||||
true);
|
||||
dCC, true);
|
||||
|
||||
verify(tracker, never()).initialize();
|
||||
verify(hC, never()).addCallback(any());
|
||||
@@ -181,6 +187,7 @@ public class AutoTileManagerTest extends SysuiTestCase {
|
||||
verify(nDS, never()).setCallback(any());
|
||||
verify(cC, never()).addCallback(any());
|
||||
verify(rBC, never()).addCallback(any());
|
||||
verify(dCC, never()).setCallback(any());
|
||||
assertNull(manager.getSecureSettingForKey(TEST_SETTING));
|
||||
assertNull(manager.getSecureSettingForKey(TEST_SETTING_COMPONENT));
|
||||
}
|
||||
@@ -229,6 +236,10 @@ public class AutoTileManagerTest extends SysuiTestCase {
|
||||
inOrderCast.verify(mCastController).removeCallback(any());
|
||||
inOrderCast.verify(mCastController).addCallback(any());
|
||||
|
||||
InOrder inOrderDevices = inOrder(mDeviceControlsController);
|
||||
inOrderDevices.verify(mDeviceControlsController).removeCallback();
|
||||
inOrderDevices.verify(mDeviceControlsController).setCallback(any());
|
||||
|
||||
SecureSetting setting = mAutoTileManager.getSecureSettingForKey(TEST_SETTING);
|
||||
assertEquals(USER + 1, setting.getCurrentUser());
|
||||
assertTrue(setting.isListening());
|
||||
@@ -273,6 +284,10 @@ public class AutoTileManagerTest extends SysuiTestCase {
|
||||
inOrderCast.verify(mCastController).removeCallback(any());
|
||||
inOrderCast.verify(mCastController, never()).addCallback(any());
|
||||
|
||||
InOrder inOrderDevices = inOrder(mDeviceControlsController);
|
||||
inOrderDevices.verify(mDeviceControlsController).removeCallback();
|
||||
inOrderDevices.verify(mDeviceControlsController).setCallback(any());
|
||||
|
||||
SecureSetting setting = mAutoTileManager.getSecureSettingForKey(TEST_SETTING);
|
||||
assertEquals(USER + 1, setting.getCurrentUser());
|
||||
assertFalse(setting.isListening());
|
||||
|
||||
@@ -0,0 +1,151 @@
|
||||
/*
|
||||
* Copyright (C) 2021 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.content.ComponentName
|
||||
import android.content.Context
|
||||
import android.content.pm.ServiceInfo
|
||||
import android.testing.AndroidTestingRunner
|
||||
|
||||
import androidx.test.filters.SmallTest
|
||||
|
||||
import com.android.systemui.R
|
||||
import com.android.systemui.SysuiTestCase
|
||||
import com.android.systemui.controls.ControlsServiceInfo
|
||||
import com.android.systemui.controls.dagger.ControlsComponent
|
||||
import com.android.systemui.controls.controller.ControlsController
|
||||
import com.android.systemui.controls.controller.SeedResponse
|
||||
import com.android.systemui.controls.controller.StructureInfo
|
||||
import com.android.systemui.controls.management.ControlsListingController
|
||||
import com.android.systemui.controls.management.ControlsListingController.ControlsListingCallback
|
||||
import com.android.systemui.settings.UserContextProvider
|
||||
import com.android.systemui.statusbar.policy.DeviceControlsControllerImpl.Companion.PREFS_CONTROLS_FILE
|
||||
import com.android.systemui.statusbar.policy.DeviceControlsControllerImpl.Companion.PREFS_CONTROLS_SEEDING_COMPLETED
|
||||
import com.android.systemui.statusbar.policy.DeviceControlsControllerImpl.Companion.QS_DEFAULT_POSITION
|
||||
import com.android.systemui.statusbar.policy.DeviceControlsControllerImpl.Companion.QS_PRIORITY_POSITION
|
||||
|
||||
import java.util.Optional
|
||||
import java.util.function.Consumer
|
||||
|
||||
import org.junit.Before
|
||||
import org.junit.Test
|
||||
import org.junit.runner.RunWith
|
||||
|
||||
import org.mockito.ArgumentCaptor
|
||||
import org.mockito.Captor
|
||||
import org.mockito.Mock
|
||||
import org.mockito.Mockito
|
||||
import org.mockito.MockitoAnnotations
|
||||
import org.mockito.Mockito.never
|
||||
import org.mockito.Mockito.times
|
||||
import org.mockito.Mockito.verify
|
||||
import org.mockito.Mockito.`when`
|
||||
import org.mockito.ArgumentMatchers.anyInt
|
||||
|
||||
@SmallTest
|
||||
@RunWith(AndroidTestingRunner::class)
|
||||
class DeviceControlsControllerImplTest : SysuiTestCase() {
|
||||
|
||||
@Mock
|
||||
private lateinit var controlsComponent: ControlsComponent
|
||||
@Mock
|
||||
private lateinit var controlsController: ControlsController
|
||||
@Mock
|
||||
private lateinit var controlsListingController: ControlsListingController
|
||||
@Mock
|
||||
private lateinit var callback: DeviceControlsController.Callback
|
||||
@Captor
|
||||
private lateinit var listingCallbackCaptor: ArgumentCaptor<ControlsListingCallback>
|
||||
@Mock
|
||||
private lateinit var structureInfo: StructureInfo
|
||||
@Mock
|
||||
private lateinit var serviceInfo: ServiceInfo
|
||||
@Mock
|
||||
private lateinit var userContextProvider: UserContextProvider
|
||||
@Captor
|
||||
private lateinit var seedCallback: ArgumentCaptor<Consumer<SeedResponse>>
|
||||
|
||||
private lateinit var controlsServiceInfo: ControlsServiceInfo
|
||||
private lateinit var controller: DeviceControlsControllerImpl
|
||||
|
||||
companion object {
|
||||
fun <T> capture(argumentCaptor: ArgumentCaptor<T>): T = argumentCaptor.capture()
|
||||
fun <T> eq(value: T): T = Mockito.eq(value) ?: value
|
||||
private val TEST_PKG = "test.pkg"
|
||||
private val TEST_COMPONENT = ComponentName(TEST_PKG, "test.class")
|
||||
}
|
||||
|
||||
@Before
|
||||
fun setUp() {
|
||||
MockitoAnnotations.initMocks(this)
|
||||
|
||||
`when`(controlsComponent.getControlsController())
|
||||
.thenReturn(Optional.of(controlsController))
|
||||
`when`(controlsComponent.getControlsListingController())
|
||||
.thenReturn(Optional.of(controlsListingController))
|
||||
|
||||
controller = DeviceControlsControllerImpl(mContext, controlsComponent, userContextProvider)
|
||||
|
||||
`when`(serviceInfo.componentName).thenReturn(TEST_COMPONENT)
|
||||
controlsServiceInfo = ControlsServiceInfo(mContext, serviceInfo)
|
||||
|
||||
`when`(userContextProvider.userContext).thenReturn(mContext)
|
||||
mContext.getSharedPreferences(PREFS_CONTROLS_FILE, Context.MODE_PRIVATE).edit()
|
||||
.putStringSet(PREFS_CONTROLS_SEEDING_COMPLETED, emptySet())
|
||||
.apply()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testNoCallbackWhenNoServicesAvailable() {
|
||||
`when`(controlsController.getFavorites()).thenReturn(emptyList())
|
||||
controller.setCallback(callback)
|
||||
|
||||
verify(controlsListingController).addCallback(capture(listingCallbackCaptor))
|
||||
listingCallbackCaptor.value.onServicesUpdated(emptyList())
|
||||
verify(callback, never()).onControlsAvailable(anyInt())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testSetPriorityPositionIsSetWhenFavoritesAreAvailable() {
|
||||
`when`(controlsController.getFavorites()).thenReturn(listOf(structureInfo))
|
||||
controller.setCallback(callback)
|
||||
|
||||
verify(controlsListingController).addCallback(capture(listingCallbackCaptor))
|
||||
listingCallbackCaptor.value.onServicesUpdated(listOf(controlsServiceInfo))
|
||||
verify(callback).onControlsAvailable(QS_PRIORITY_POSITION)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testSetDefaultPositionIsSetWhenNoFavoritesAreAvailable() {
|
||||
`when`(controlsController.getFavorites()).thenReturn(emptyList())
|
||||
controller.setCallback(callback)
|
||||
|
||||
mContext.getOrCreateTestableResources().addOverride(
|
||||
R.array.config_controlsPreferredPackages,
|
||||
arrayOf(TEST_PKG))
|
||||
|
||||
verify(controlsListingController).addCallback(capture(listingCallbackCaptor))
|
||||
listingCallbackCaptor.value.onServicesUpdated(listOf(controlsServiceInfo))
|
||||
|
||||
verify(controlsController).seedFavoritesForComponents(
|
||||
eq(listOf(TEST_COMPONENT)),
|
||||
capture(seedCallback)
|
||||
)
|
||||
seedCallback.value.accept(SeedResponse(TEST_PKG, true))
|
||||
verify(callback).onControlsAvailable(QS_DEFAULT_POSITION)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user