Merge "Controls in QS" into sc-dev

This commit is contained in:
Matt Pietal
2021-04-06 13:55:32 +00:00
committed by Android (Google) Code Review
26 changed files with 616 additions and 419 deletions

View File

@@ -757,6 +757,7 @@
android:excludeFromRecents="true" android:excludeFromRecents="true"
android:showWhenLocked="true" android:showWhenLocked="true"
android:showForAllUsers="true" android:showForAllUsers="true"
android:finishOnTaskLaunch="true"
android:launchMode="singleInstance" android:launchMode="singleInstance"
android:configChanges="screenSize|smallestScreenSize|screenLayout|orientation|keyboard|keyboardHidden" android:configChanges="screenSize|smallestScreenSize|screenLayout|orientation|keyboard|keyboardHidden"
android:visibleToInstantApps="true"> android:visibleToInstantApps="true">

View File

@@ -30,12 +30,6 @@
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content"/> 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> </LinearLayout>
</com.android.systemui.globalactions.MinHeightScrollView> </com.android.systemui.globalactions.MinHeightScrollView>
</LinearLayout> </LinearLayout>

View File

@@ -44,8 +44,9 @@ import com.android.systemui.controls.ui.ControlsUiController
import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Background import com.android.systemui.dagger.qualifiers.Background
import com.android.systemui.dump.DumpManager import com.android.systemui.dump.DumpManager
import com.android.systemui.globalactions.GlobalActionsDialog
import com.android.systemui.settings.UserTracker 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 com.android.systemui.util.concurrency.DelayableExecutor
import java.io.FileDescriptor import java.io.FileDescriptor
import java.io.PrintWriter 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 // When a component is uninstalled, allow seeding to happen again if the user
// reinstalls the app // reinstalls the app
val prefs = userStructure.userContext.getSharedPreferences( val prefs = userStructure.userContext.getSharedPreferences(
GlobalActionsDialog.PREFS_CONTROLS_FILE, Context.MODE_PRIVATE) PREFS_CONTROLS_FILE, Context.MODE_PRIVATE)
val completedSeedingPackageSet = prefs.getStringSet( val completedSeedingPackageSet = prefs.getStringSet(
GlobalActionsDialog.PREFS_CONTROLS_SEEDING_COMPLETED, mutableSetOf<String>()) PREFS_CONTROLS_SEEDING_COMPLETED, mutableSetOf<String>())
val servicePackageSet = serviceInfoSet.map { it.packageName } 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() completedSeedingPackageSet.intersect(servicePackageSet)).apply()
var changed = false var changed = false

View File

@@ -16,6 +16,7 @@
package com.android.systemui.controls.management package com.android.systemui.controls.management
import android.app.ActivityOptions
import android.content.ComponentName import android.content.ComponentName
import android.content.Intent import android.content.Intent
import android.os.Bundle 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.controller.StructureInfo
import com.android.systemui.controls.ui.ControlsActivity import com.android.systemui.controls.ui.ControlsActivity
import com.android.systemui.controls.ui.ControlsUiController import com.android.systemui.controls.ui.ControlsUiController
import com.android.systemui.globalactions.GlobalActionsComponent
import com.android.systemui.settings.CurrentUserTracker import com.android.systemui.settings.CurrentUserTracker
import com.android.systemui.util.LifecycleActivity import com.android.systemui.util.LifecycleActivity
import javax.inject.Inject import javax.inject.Inject
@@ -45,7 +45,6 @@ import javax.inject.Inject
class ControlsEditingActivity @Inject constructor( class ControlsEditingActivity @Inject constructor(
private val controller: ControlsControllerImpl, private val controller: ControlsControllerImpl,
private val broadcastDispatcher: BroadcastDispatcher, private val broadcastDispatcher: BroadcastDispatcher,
private val globalActionsComponent: GlobalActionsComponent,
private val customIconCache: CustomIconCache, private val customIconCache: CustomIconCache,
private val uiController: ControlsUiController private val uiController: ControlsUiController
) : LifecycleActivity() { ) : LifecycleActivity() {
@@ -62,7 +61,6 @@ class ControlsEditingActivity @Inject constructor(
private lateinit var model: FavoritesModel private lateinit var model: FavoritesModel
private lateinit var subtitle: TextView private lateinit var subtitle: TextView
private lateinit var saveButton: View private lateinit var saveButton: View
private var backToGlobalActions = true
private val currentUserTracker = object : CurrentUserTracker(broadcastDispatcher) { private val currentUserTracker = object : CurrentUserTracker(broadcastDispatcher) {
private val startingUser = controller.currentUserId private val startingUser = controller.currentUserId
@@ -86,11 +84,6 @@ class ControlsEditingActivity @Inject constructor(
structure = it structure = it
} ?: run(this::finish) } ?: run(this::finish)
backToGlobalActions = intent.getBooleanExtra(
ControlsUiController.BACK_TO_GLOBAL_ACTIONS,
true
)
bindViews() bindViews()
bindButtons() bindButtons()
@@ -109,15 +102,6 @@ class ControlsEditingActivity @Inject constructor(
} }
override fun onBackPressed() { 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() animateExitAndFinish()
} }
@@ -161,8 +145,12 @@ class ControlsEditingActivity @Inject constructor(
setText(R.string.save) setText(R.string.save)
setOnClickListener { setOnClickListener {
saveFavorites() saveFavorites()
startActivity(
Intent(applicationContext, ControlsActivity::class.java),
ActivityOptions
.makeSceneTransitionAnimation(this@ControlsEditingActivity).toBundle()
)
animateExitAndFinish() animateExitAndFinish()
globalActionsComponent.handleShowGlobalActionsMenu()
} }
} }
} }

View File

@@ -43,7 +43,6 @@ import com.android.systemui.controls.controller.StructureInfo
import com.android.systemui.controls.ui.ControlsActivity import com.android.systemui.controls.ui.ControlsActivity
import com.android.systemui.controls.ui.ControlsUiController import com.android.systemui.controls.ui.ControlsUiController
import com.android.systemui.dagger.qualifiers.Main import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.globalactions.GlobalActionsComponent
import com.android.systemui.settings.CurrentUserTracker import com.android.systemui.settings.CurrentUserTracker
import com.android.systemui.util.LifecycleActivity import com.android.systemui.util.LifecycleActivity
import java.text.Collator import java.text.Collator
@@ -56,7 +55,6 @@ class ControlsFavoritingActivity @Inject constructor(
private val controller: ControlsControllerImpl, private val controller: ControlsControllerImpl,
private val listingController: ControlsListingController, private val listingController: ControlsListingController,
private val broadcastDispatcher: BroadcastDispatcher, private val broadcastDispatcher: BroadcastDispatcher,
private val globalActionsComponent: GlobalActionsComponent,
private val uiController: ControlsUiController private val uiController: ControlsUiController
) : LifecycleActivity() { ) : LifecycleActivity() {
@@ -92,7 +90,6 @@ class ControlsFavoritingActivity @Inject constructor(
private lateinit var comparator: Comparator<StructureContainer> private lateinit var comparator: Comparator<StructureContainer>
private var cancelLoadRunnable: Runnable? = null private var cancelLoadRunnable: Runnable? = null
private var isPagerLoaded = false private var isPagerLoaded = false
private var backToGlobalActions = true
private val currentUserTracker = object : CurrentUserTracker(broadcastDispatcher) { private val currentUserTracker = object : CurrentUserTracker(broadcastDispatcher) {
private val startingUser = controller.currentUserId private val startingUser = controller.currentUserId
@@ -133,11 +130,6 @@ class ControlsFavoritingActivity @Inject constructor(
component = intent.getParcelableExtra<ComponentName>(Intent.EXTRA_COMPONENT_NAME) component = intent.getParcelableExtra<ComponentName>(Intent.EXTRA_COMPONENT_NAME)
fromProviderSelector = intent.getBooleanExtra(EXTRA_FROM_PROVIDER_SELECTOR, false) fromProviderSelector = intent.getBooleanExtra(EXTRA_FROM_PROVIDER_SELECTOR, false)
backToGlobalActions = intent.getBooleanExtra(
ControlsUiController.BACK_TO_GLOBAL_ACTIONS,
true
)
bindViews() bindViews()
} }
@@ -311,13 +303,6 @@ class ControlsFavoritingActivity @Inject constructor(
private fun bindButtons() { private fun bindButtons() {
otherAppsButton = requireViewById<Button>(R.id.other_apps).apply { otherAppsButton = requireViewById<Button>(R.id.other_apps).apply {
setOnClickListener { setOnClickListener {
val i = Intent().apply {
component = ComponentName(context, ControlsProviderSelectorActivity::class.java)
putExtra(
ControlsUiController.BACK_TO_GLOBAL_ACTIONS,
backToGlobalActions
)
}
if (doneButton.isEnabled) { if (doneButton.isEnabled) {
// The user has made changes // The user has made changes
Toast.makeText( Toast.makeText(
@@ -326,8 +311,11 @@ class ControlsFavoritingActivity @Inject constructor(
Toast.LENGTH_SHORT Toast.LENGTH_SHORT
).show() ).show()
} }
startActivity(i, ActivityOptions startActivity(
.makeSceneTransitionAnimation(this@ControlsFavoritingActivity).toBundle()) Intent(context, ControlsProviderSelectorActivity::class.java),
ActivityOptions
.makeSceneTransitionAnimation(this@ControlsFavoritingActivity).toBundle()
)
animateExitAndFinish() animateExitAndFinish()
} }
} }
@@ -349,15 +337,10 @@ class ControlsFavoritingActivity @Inject constructor(
} }
private fun openControlsOrigin() { private fun openControlsOrigin() {
if (backToGlobalActions) { startActivity(
globalActionsComponent.handleShowGlobalActionsMenu() Intent(applicationContext, ControlsActivity::class.java),
} else { ActivityOptions.makeSceneTransitionAnimation(this).toBundle()
val i = Intent().apply { )
component = ComponentName(applicationContext, ControlsActivity::class.java)
addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP or Intent.FLAG_ACTIVITY_NEW_TASK)
}
startActivity(i)
}
} }
override fun onPause() { override fun onPause() {

View File

@@ -36,7 +36,6 @@ import com.android.systemui.controls.ui.ControlsActivity
import com.android.systemui.controls.ui.ControlsUiController import com.android.systemui.controls.ui.ControlsUiController
import com.android.systemui.dagger.qualifiers.Background import com.android.systemui.dagger.qualifiers.Background
import com.android.systemui.dagger.qualifiers.Main import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.globalactions.GlobalActionsComponent
import com.android.systemui.settings.CurrentUserTracker import com.android.systemui.settings.CurrentUserTracker
import com.android.systemui.util.LifecycleActivity import com.android.systemui.util.LifecycleActivity
import java.util.concurrent.Executor import java.util.concurrent.Executor
@@ -50,7 +49,6 @@ class ControlsProviderSelectorActivity @Inject constructor(
@Background private val backExecutor: Executor, @Background private val backExecutor: Executor,
private val listingController: ControlsListingController, private val listingController: ControlsListingController,
private val controlsController: ControlsController, private val controlsController: ControlsController,
private val globalActionsComponent: GlobalActionsComponent,
private val broadcastDispatcher: BroadcastDispatcher, private val broadcastDispatcher: BroadcastDispatcher,
private val uiController: ControlsUiController private val uiController: ControlsUiController
) : LifecycleActivity() { ) : LifecycleActivity() {
@@ -59,7 +57,6 @@ class ControlsProviderSelectorActivity @Inject constructor(
private const val TAG = "ControlsProviderSelectorActivity" private const val TAG = "ControlsProviderSelectorActivity"
} }
private var backToGlobalActions = true
private lateinit var recyclerView: RecyclerView private lateinit var recyclerView: RecyclerView
private val currentUserTracker = object : CurrentUserTracker(broadcastDispatcher) { private val currentUserTracker = object : CurrentUserTracker(broadcastDispatcher) {
private val startingUser = listingController.currentUserId private val startingUser = listingController.currentUserId
@@ -105,23 +102,13 @@ class ControlsProviderSelectorActivity @Inject constructor(
} }
} }
requireViewById<View>(R.id.done).visibility = View.GONE requireViewById<View>(R.id.done).visibility = View.GONE
backToGlobalActions = intent.getBooleanExtra(
ControlsUiController.BACK_TO_GLOBAL_ACTIONS,
true
)
} }
override fun onBackPressed() { override fun onBackPressed() {
if (backToGlobalActions) { val i = Intent().apply {
globalActionsComponent.handleShowGlobalActionsMenu() component = ComponentName(applicationContext, ControlsActivity::class.java)
} 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(i, ActivityOptions.makeSceneTransitionAnimation(this).toBundle())
animateExitAndFinish() animateExitAndFinish()
} }
@@ -169,10 +156,6 @@ class ControlsProviderSelectorActivity @Inject constructor(
listingController.getAppLabel(it)) listingController.getAppLabel(it))
putExtra(Intent.EXTRA_COMPONENT_NAME, it) putExtra(Intent.EXTRA_COMPONENT_NAME, it)
putExtra(ControlsFavoritingActivity.EXTRA_FROM_PROVIDER_SELECTOR, true) putExtra(ControlsFavoritingActivity.EXTRA_FROM_PROVIDER_SELECTOR, true)
putExtra(
ControlsUiController.BACK_TO_GLOBAL_ACTIONS,
backToGlobalActions
)
} }
startActivity(intent, ActivityOptions.makeSceneTransitionAnimation(this).toBundle()) startActivity(intent, ActivityOptions.makeSceneTransitionAnimation(this).toBundle())
animateExitAndFinish() animateExitAndFinish()

View File

@@ -26,7 +26,7 @@ import android.service.controls.Control
interface ControlActionCoordinator { interface ControlActionCoordinator {
// If launched from an Activity, continue within this stack // If launched from an Activity, continue within this stack
var activityContext: Context? var activityContext: Context
/** /**
* Close any dialogs which may have been open * Close any dialogs which may have been open

View File

@@ -63,7 +63,7 @@ class ControlActionCoordinatorImpl @Inject constructor(
private var actionsInProgress = mutableSetOf<String>() private var actionsInProgress = mutableSetOf<String>()
private val isLocked: Boolean private val isLocked: Boolean
get() = !keyguardStateController.isUnlocked() get() = !keyguardStateController.isUnlocked()
override var activityContext: Context? = null override lateinit var activityContext: Context
companion object { companion object {
private const val RESPONSE_TIMEOUT_IN_MILLIS = 3000L 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 // pending actions will only run after the control state has been refreshed
pendingAction = action pendingAction = action
} }
val wasLocked = isLocked
activityStarter.dismissKeyguardThenExecute({ activityStarter.dismissKeyguardThenExecute({
Log.d(ControlsUiController.TAG, "Device unlocked, invoking controls action") Log.d(ControlsUiController.TAG, "Device unlocked, invoking controls action")
if (wasLocked && activityContext == null) { action.invoke()
globalActionsComponent.handleShowGlobalActionsMenu()
} else {
action.invoke()
}
true true
}, { pendingAction = null }, true /* afterKeyguardGone */) }, { pendingAction = null }, true /* afterKeyguardGone */)
} else { } else {

View File

@@ -41,6 +41,14 @@ class ControlsActivity @Inject constructor(
setContentView(R.layout.controls_fullscreen) 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 { requireViewById<ViewGroup>(R.id.control_detail_root).apply {
setOnApplyWindowInsetsListener { setOnApplyWindowInsetsListener {
v: View, insets: WindowInsets -> v: View, insets: WindowInsets ->
@@ -61,7 +69,7 @@ class ControlsActivity @Inject constructor(
parent = requireViewById<ViewGroup>(R.id.global_actions_controls) parent = requireViewById<ViewGroup>(R.id.global_actions_controls)
parent.alpha = 0f parent.alpha = 0f
uiController.show(parent, { animateExitAndFinish() }, this) uiController.show(parent, { finish() }, this)
} }
override fun onResume() { override fun onResume() {
@@ -71,7 +79,7 @@ class ControlsActivity @Inject constructor(
} }
override fun onBackPressed() { override fun onBackPressed() {
animateExitAndFinish() finish()
} }
override fun onStop() { override fun onStop() {
@@ -79,8 +87,4 @@ class ControlsActivity @Inject constructor(
uiController.hide() uiController.hide()
} }
private fun animateExitAndFinish() {
ControlsAnimations.exitAnimation(parent, { finish() }).start()
}
} }

View File

@@ -28,10 +28,9 @@ interface ControlsUiController {
companion object { companion object {
public const val TAG = "ControlsUiController" public const val TAG = "ControlsUiController"
public const val EXTRA_ANIMATE = "extra_animate" 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() fun hide()
/** /**

View File

@@ -19,6 +19,8 @@ package com.android.systemui.controls.ui
import android.animation.Animator import android.animation.Animator
import android.animation.AnimatorListenerAdapter import android.animation.AnimatorListenerAdapter
import android.animation.ObjectAnimator import android.animation.ObjectAnimator
import android.app.Activity
import android.app.ActivityOptions
import android.content.ComponentName import android.content.ComponentName
import android.content.Context import android.content.Context
import android.content.Intent import android.content.Intent
@@ -129,6 +131,7 @@ class ControlsUiControllerImpl @Inject constructor (
override val available: Boolean override val available: Boolean
get() = controlsController.get().available get() = controlsController.get().available
private lateinit var activityContext: Context
private lateinit var listingCallback: ControlsListingController.ControlsListingCallback private lateinit var listingCallback: ControlsListingController.ControlsListingCallback
private fun createCallback( private fun createCallback(
@@ -153,11 +156,12 @@ class ControlsUiControllerImpl @Inject constructor (
override fun show( override fun show(
parent: ViewGroup, parent: ViewGroup,
onDismiss: Runnable, onDismiss: Runnable,
activityContext: Context? activityContext: Context
) { ) {
Log.d(ControlsUiController.TAG, "show()") Log.d(ControlsUiController.TAG, "show()")
this.parent = parent this.parent = parent
this.onDismiss = onDismiss this.onDismiss = onDismiss
this.activityContext = activityContext
hidden = false hidden = false
retainCache = false retainCache = false
@@ -198,7 +202,7 @@ class ControlsUiControllerImpl @Inject constructor (
controlViewsById.clear() controlViewsById.clear()
controlsById.clear() controlsById.clear()
show(parent, onDismiss, controlActionCoordinator.activityContext) show(parent, onDismiss, activityContext)
val showAnim = ObjectAnimator.ofFloat(parent, "alpha", 0.0f, 1.0f) val showAnim = ObjectAnimator.ofFloat(parent, "alpha", 0.0f, 1.0f)
showAnim.setInterpolator(DecelerateInterpolator(1.0f)) showAnim.setInterpolator(DecelerateInterpolator(1.0f))
showAnim.setDuration(FADE_IN_MILLIS) showAnim.setDuration(FADE_IN_MILLIS)
@@ -220,7 +224,7 @@ class ControlsUiControllerImpl @Inject constructor (
inflater.inflate(R.layout.controls_no_favorites, parent, true) inflater.inflate(R.layout.controls_no_favorites, parent, true)
val viewGroup = parent.requireViewById(R.id.controls_no_favorites_group) as ViewGroup 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) val subtitle = parent.requireViewById<TextView>(R.id.controls_subtitle)
subtitle.setText(context.resources.getString(R.string.quick_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) { private fun startFavoritingActivity(si: StructureInfo) {
startTargetedActivity(context, si, ControlsFavoritingActivity::class.java) startTargetedActivity(si, ControlsFavoritingActivity::class.java)
} }
private fun startEditingActivity(context: Context, si: StructureInfo) { private fun startEditingActivity(si: StructureInfo) {
startTargetedActivity(context, si, ControlsEditingActivity::class.java) startTargetedActivity(si, ControlsEditingActivity::class.java)
} }
private fun startTargetedActivity(context: Context, si: StructureInfo, klazz: Class<*>) { private fun startTargetedActivity(si: StructureInfo, klazz: Class<*>) {
val i = Intent(context, klazz).apply { val i = Intent(activityContext, klazz)
addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP or Intent.FLAG_ACTIVITY_NEW_TASK)
}
putIntentExtras(i, si) putIntentExtras(i, si)
startActivity(context, i) startActivity(i)
retainCache = true retainCache = true
} }
@@ -261,27 +263,22 @@ class ControlsUiControllerImpl @Inject constructor (
} }
} }
private fun startProviderSelectorActivity(context: Context) { private fun startProviderSelectorActivity() {
val i = Intent(context, ControlsProviderSelectorActivity::class.java).apply { startActivity(Intent(activityContext, ControlsProviderSelectorActivity::class.java))
addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP or Intent.FLAG_ACTIVITY_NEW_TASK)
}
startActivity(context, i)
} }
private fun startActivity(context: Context, intent: Intent) { private fun startActivity(intent: Intent) {
// Force animations when transitioning from a dialog to an activity // Force animations when transitioning from a dialog to an activity
intent.putExtra(ControlsUiController.EXTRA_ANIMATE, true) intent.putExtra(ControlsUiController.EXTRA_ANIMATE, true)
intent.putExtra(
ControlsUiController.BACK_TO_GLOBAL_ACTIONS,
controlActionCoordinator.activityContext == null
)
onDismiss.run()
activityStarter.dismissKeyguardThenExecute({ if (keyguardStateController.isUnlocked()) {
shadeController.collapsePanel(false) activityContext.startActivity(
context.startActivity(intent) intent,
true ActivityOptions.makeSceneTransitionAnimation(activityContext as Activity).toBundle()
}, null, true) )
} else {
activityStarter.postStartActivityDismissingKeyguard(intent, 0 /* delay */)
}
} }
private fun showControlsView(items: List<SelectionItem>) { private fun showControlsView(items: List<SelectionItem>) {
@@ -328,9 +325,9 @@ class ControlsUiControllerImpl @Inject constructor (
) { ) {
when (pos) { when (pos) {
// 0: Add Control // 0: Add Control
0 -> startFavoritingActivity(view.context, selectedStructure) 0 -> startFavoritingActivity(selectedStructure)
// 1: Edit controls // 1: Edit controls
1 -> startEditingActivity(view.context, selectedStructure) 1 -> startEditingActivity(selectedStructure)
} }
dismiss() dismiss()
} }
@@ -399,15 +396,9 @@ class ControlsUiControllerImpl @Inject constructor (
val inflater = LayoutInflater.from(context) val inflater = LayoutInflater.from(context)
inflater.inflate(R.layout.controls_with_favorites, parent, true) inflater.inflate(R.layout.controls_with_favorites, parent, true)
if (controlActionCoordinator.activityContext == null) { parent.requireViewById<ImageView>(R.id.controls_close).apply {
parent.requireViewById<View>(R.id.controls_spacer).apply { setOnClickListener { _: View -> onDismiss.run() }
visibility = View.VISIBLE visibility = View.VISIBLE
}
} else {
parent.requireViewById<ImageView>(R.id.controls_close).apply {
setOnClickListener { _: View -> onDismiss.run() }
visibility = View.VISIBLE
}
} }
val maxColumns = findMaxColumns() val maxColumns = findMaxColumns()
@@ -522,8 +513,6 @@ class ControlsUiControllerImpl @Inject constructor (
override fun hide() { override fun hide() {
hidden = true hidden = true
controlActionCoordinator.activityContext = null
closeDialogs(true) closeDialogs(true)
controlsController.get().unsubscribe() controlsController.get().unsubscribe()

View File

@@ -17,8 +17,6 @@ package com.android.systemui.globalactions;
import static android.view.ViewGroup.LayoutParams.MATCH_PARENT; 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.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 static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_GLOBAL_ACTIONS_SHOWING;
import android.animation.Animator; import android.animation.Animator;
@@ -30,10 +28,8 @@ import android.app.IActivityManager;
import android.app.PendingIntent; import android.app.PendingIntent;
import android.app.admin.DevicePolicyManager; import android.app.admin.DevicePolicyManager;
import android.app.trust.TrustManager; import android.app.trust.TrustManager;
import android.content.ComponentName;
import android.content.Context; import android.content.Context;
import android.content.DialogInterface; import android.content.DialogInterface;
import android.content.SharedPreferences;
import android.content.res.Resources; import android.content.res.Resources;
import android.database.ContentObserver; import android.database.ContentObserver;
import android.graphics.drawable.Drawable; import android.graphics.drawable.Drawable;
@@ -47,7 +43,6 @@ import android.telecom.TelecomManager;
import android.transition.AutoTransition; import android.transition.AutoTransition;
import android.transition.TransitionManager; import android.transition.TransitionManager;
import android.transition.TransitionSet; import android.transition.TransitionSet;
import android.util.Log;
import android.view.IWindowManager; import android.view.IWindowManager;
import android.view.View; import android.view.View;
import android.view.ViewGroup; import android.view.ViewGroup;
@@ -69,18 +64,12 @@ import com.android.internal.widget.LockPatternUtils;
import com.android.systemui.Interpolators; import com.android.systemui.Interpolators;
import com.android.systemui.broadcast.BroadcastDispatcher; import com.android.systemui.broadcast.BroadcastDispatcher;
import com.android.systemui.colorextraction.SysuiColorExtractor; 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.Background;
import com.android.systemui.dagger.qualifiers.Main; import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.model.SysUiState; import com.android.systemui.model.SysUiState;
import com.android.systemui.plugins.ActivityStarter; import com.android.systemui.plugins.ActivityStarter;
import com.android.systemui.plugins.GlobalActions.GlobalActionsManager; import com.android.systemui.plugins.GlobalActions.GlobalActionsManager;
import com.android.systemui.plugins.GlobalActionsPanelPlugin; import com.android.systemui.plugins.GlobalActionsPanelPlugin;
import com.android.systemui.settings.UserContextProvider;
import com.android.systemui.statusbar.NotificationShadeDepthController; import com.android.systemui.statusbar.NotificationShadeDepthController;
import com.android.systemui.statusbar.NotificationShadeWindowController; import com.android.systemui.statusbar.NotificationShadeWindowController;
import com.android.systemui.statusbar.policy.ConfigurationController; 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.GlobalSettings;
import com.android.systemui.util.settings.SecureSettings; 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 java.util.concurrent.Executor;
import javax.inject.Inject; 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 * 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. * 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 public class GlobalActionsDialog extends GlobalActionsDialogLite
implements DialogInterface.OnDismissListener, implements DialogInterface.OnDismissListener,
@@ -116,10 +99,6 @@ public class GlobalActionsDialog extends GlobalActionsDialogLite
private static final String TAG = "GlobalActionsDialog"; 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 LockPatternUtils mLockPatternUtils;
private final KeyguardStateController mKeyguardStateController; private final KeyguardStateController mKeyguardStateController;
private final NotificationShadeDepthController mDepthController; private final NotificationShadeDepthController mDepthController;
@@ -129,13 +108,9 @@ public class GlobalActionsDialog extends GlobalActionsDialogLite
private final IStatusBarService mStatusBarService; private final IStatusBarService mStatusBarService;
private final NotificationShadeWindowController mNotificationShadeWindowController; private final NotificationShadeWindowController mNotificationShadeWindowController;
private GlobalActionsPanelPlugin mWalletPlugin; private GlobalActionsPanelPlugin mWalletPlugin;
private Optional<ControlsUiController> mControlsUiControllerOptional;
private List<ControlsServiceInfo> mControlsServiceInfos = new ArrayList<>();
private ControlsComponent mControlsComponent;
private Optional<ControlsController> mControlsControllerOptional;
private UserContextProvider mUserContextProvider;
@VisibleForTesting @VisibleForTesting
boolean mShowLockScreenCardsAndControls = false; boolean mShowLockScreenCards = false;
/** /**
* @param context everything needs a context :( * @param context everything needs a context :(
@@ -158,9 +133,7 @@ public class GlobalActionsDialog extends GlobalActionsDialogLite
IWindowManager iWindowManager, IWindowManager iWindowManager,
@Background Executor backgroundExecutor, @Background Executor backgroundExecutor,
UiEventLogger uiEventLogger, UiEventLogger uiEventLogger,
RingerModeTracker ringerModeTracker, SysUiState sysUiState, @Main Handler handler, RingerModeTracker ringerModeTracker, SysUiState sysUiState, @Main Handler handler) {
ControlsComponent controlsComponent,
UserContextProvider userContextProvider) {
super(context, windowManagerFuncs, super(context, windowManagerFuncs,
audioManager, iDreamManager, audioManager, iDreamManager,
@@ -186,11 +159,7 @@ public class GlobalActionsDialog extends GlobalActionsDialogLite
mSysuiColorExtractor = colorExtractor; mSysuiColorExtractor = colorExtractor;
mStatusBarService = statusBarService; mStatusBarService = statusBarService;
mNotificationShadeWindowController = notificationShadeWindowController; mNotificationShadeWindowController = notificationShadeWindowController;
mControlsComponent = controlsComponent;
mControlsUiControllerOptional = controlsComponent.getControlsUiController();
mControlsControllerOptional = controlsComponent.getControlsController();
mSysUiState = sysUiState; mSysUiState = sysUiState;
mUserContextProvider = userContextProvider;
mActivityStarter = activityStarter; mActivityStarter = activityStarter;
keyguardStateController.addCallback(new KeyguardStateController.Callback() { keyguardStateController.addCallback(new KeyguardStateController.Callback() {
@Override @Override
@@ -201,10 +170,7 @@ public class GlobalActionsDialog extends GlobalActionsDialogLite
if (dialog.mWalletViewController != null) { if (dialog.mWalletViewController != null) {
dialog.mWalletViewController.onDeviceLockStateChanged(!unlocked); dialog.mWalletViewController.onDeviceLockStateChanged(!unlocked);
} }
if (!dialog.isShowingControls()
&& mControlsComponent.getVisibility() == AVAILABLE) {
dialog.showControls(mControlsUiControllerOptional.get());
}
if (unlocked) { if (unlocked) {
dialog.hideLockMessage(); dialog.hideLockMessage();
} }
@@ -212,25 +178,7 @@ public class GlobalActionsDialog extends GlobalActionsDialogLite
} }
}); });
if (mControlsComponent.getControlsListingController().isPresent()) { // Listen for changes to show pay on the power menu while locked
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
onPowerMenuLockScreenSettingsChanged(); onPowerMenuLockScreenSettingsChanged();
mGlobalSettings.registerContentObserver( mGlobalSettings.registerContentObserver(
Settings.Secure.getUriFor(Settings.Secure.POWER_MENU_LOCKED_SHOW_CONTENT), 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) * Show the global actions dialog (creating if necessary)
* *
@@ -313,12 +202,6 @@ public class GlobalActionsDialog extends GlobalActionsDialogLite
super.showOrHideDialog(keyguardShowing, isDeviceProvisioned); 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 * Returns the maximum number of power menu items to show based on which GlobalActions
* layout is being used. * layout is being used.
@@ -340,14 +223,9 @@ public class GlobalActionsDialog extends GlobalActionsDialogLite
initDialogItems(); initDialogItems();
mDepthController.setShowingHomeControls(true); mDepthController.setShowingHomeControls(true);
ControlsUiController uiController = null;
if (mControlsComponent.getVisibility() == AVAILABLE) {
uiController = mControlsUiControllerOptional.get();
}
ActionsDialog dialog = new ActionsDialog(getContext(), mAdapter, mOverflowAdapter, ActionsDialog dialog = new ActionsDialog(getContext(), mAdapter, mOverflowAdapter,
this::getWalletViewController, mDepthController, mSysuiColorExtractor, this::getWalletViewController, mDepthController, mSysuiColorExtractor,
mStatusBarService, mNotificationShadeWindowController, mStatusBarService, mNotificationShadeWindowController,
controlsAvailable(), uiController,
mSysUiState, this::onRotate, isKeyguardShowing(), mPowerAdapter); mSysUiState, this::onRotate, isKeyguardShowing(), mPowerAdapter);
if (shouldShowLockMessage(dialog)) { if (shouldShowLockMessage(dialog)) {
@@ -407,10 +285,6 @@ public class GlobalActionsDialog extends GlobalActionsDialogLite
private final Provider<GlobalActionsPanelPlugin.PanelViewController> mWalletFactory; private final Provider<GlobalActionsPanelPlugin.PanelViewController> mWalletFactory;
@Nullable private GlobalActionsPanelPlugin.PanelViewController mWalletViewController; @Nullable private GlobalActionsPanelPlugin.PanelViewController mWalletViewController;
private ResetOrientationData mResetOrientationData; private ResetOrientationData mResetOrientationData;
private final boolean mControlsAvailable;
private ControlsUiController mControlsUiController;
private ViewGroup mControlsView;
@VisibleForTesting ViewGroup mLockMessageContainer; @VisibleForTesting ViewGroup mLockMessageContainer;
private TextView mLockMessage; private TextView mLockMessage;
@@ -419,15 +293,12 @@ public class GlobalActionsDialog extends GlobalActionsDialogLite
NotificationShadeDepthController depthController, NotificationShadeDepthController depthController,
SysuiColorExtractor sysuiColorExtractor, IStatusBarService statusBarService, SysuiColorExtractor sysuiColorExtractor, IStatusBarService statusBarService,
NotificationShadeWindowController notificationShadeWindowController, NotificationShadeWindowController notificationShadeWindowController,
boolean controlsAvailable, @Nullable ControlsUiController controlsUiController,
SysUiState sysuiState, Runnable onRotateCallback, boolean keyguardShowing, SysUiState sysuiState, Runnable onRotateCallback, boolean keyguardShowing,
MyPowerOptionsAdapter powerAdapter) { MyPowerOptionsAdapter powerAdapter) {
super(context, com.android.systemui.R.style.Theme_SystemUI_Dialog_GlobalActions, super(context, com.android.systemui.R.style.Theme_SystemUI_Dialog_GlobalActions,
adapter, overflowAdapter, depthController, sysuiColorExtractor, adapter, overflowAdapter, depthController, sysuiColorExtractor,
statusBarService, notificationShadeWindowController, sysuiState, statusBarService, notificationShadeWindowController, sysuiState,
onRotateCallback, keyguardShowing, powerAdapter); onRotateCallback, keyguardShowing, powerAdapter);
mControlsAvailable = controlsAvailable;
mControlsUiController = controlsUiController;
mWalletFactory = walletFactory; mWalletFactory = walletFactory;
// Update window attributes // Update window attributes
@@ -448,16 +319,6 @@ public class GlobalActionsDialog extends GlobalActionsDialogLite
initializeLayout(); initializeLayout();
} }
private boolean isShowingControls() {
return mControlsUiController != null;
}
private void showControls(ControlsUiController controller) {
mControlsUiController = controller;
mControlsUiController.show(mControlsView, this::dismissForControlsActivity,
null /* activityContext */);
}
private boolean isWalletViewAvailable() { private boolean isWalletViewAvailable() {
return mWalletViewController != null && mWalletViewController.getPanelContent() != null; return mWalletViewController != null && mWalletViewController.getPanelContent() != null;
} }
@@ -524,10 +385,8 @@ public class GlobalActionsDialog extends GlobalActionsDialogLite
new FrameLayout.LayoutParams( new FrameLayout.LayoutParams(
FrameLayout.LayoutParams.MATCH_PARENT, FrameLayout.LayoutParams.MATCH_PARENT,
FrameLayout.LayoutParams.MATCH_PARENT); FrameLayout.LayoutParams.MATCH_PARENT);
if (!mControlsAvailable) { panelParams.topMargin = mContext.getResources().getDimensionPixelSize(
panelParams.topMargin = mContext.getResources().getDimensionPixelSize( com.android.systemui.R.dimen.global_actions_wallet_top_margin);
com.android.systemui.R.dimen.global_actions_wallet_top_margin);
}
View walletView = mWalletViewController.getPanelContent(); View walletView = mWalletViewController.getPanelContent();
panelContainer.addView(walletView, panelParams); panelContainer.addView(walletView, panelParams);
// Smooth transitions when wallet is resized, which can happen when a card is added // Smooth transitions when wallet is resized, which can happen when a card is added
@@ -554,7 +413,6 @@ public class GlobalActionsDialog extends GlobalActionsDialogLite
@Override @Override
protected void initializeLayout() { protected void initializeLayout() {
super.initializeLayout(); super.initializeLayout();
mControlsView = findViewById(com.android.systemui.R.id.global_actions_controls);
mLockMessageContainer = requireViewById( mLockMessageContainer = requireViewById(
com.android.systemui.R.id.global_actions_lock_message_container); com.android.systemui.R.id.global_actions_lock_message_container);
mLockMessage = requireViewById(com.android.systemui.R.id.global_actions_lock_message); mLockMessage = requireViewById(com.android.systemui.R.id.global_actions_lock_message);
@@ -577,10 +435,6 @@ public class GlobalActionsDialog extends GlobalActionsDialogLite
windowInsets.getStableInsetBottom()); windowInsets.getStableInsetBottom());
return WindowInsets.CONSUMED; return WindowInsets.CONSUMED;
}); });
if (mControlsUiController != null) {
mControlsUiController.show(mControlsView, this::dismissForControlsActivity,
null /* activityContext */);
}
mBackgroundDrawable.setAlpha(0); mBackgroundDrawable.setAlpha(0);
float xOffset = mGlobalActionsLayout.getAnimationOffsetX(); float xOffset = mGlobalActionsLayout.getAnimationOffsetX();
@@ -609,20 +463,11 @@ public class GlobalActionsDialog extends GlobalActionsDialogLite
@Override @Override
protected void dismissInternal() { protected void dismissInternal() {
super.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 @Override
protected void completeDismiss() { protected void completeDismiss() {
dismissWallet(); dismissWallet();
if (mControlsUiController != null) mControlsUiController.hide();
resetOrientation(); resetOrientation();
super.completeDismiss(); super.completeDismiss();
} }
@@ -647,15 +492,7 @@ public class GlobalActionsDialog extends GlobalActionsDialogLite
public void refreshDialog() { public void refreshDialog() {
// ensure dropdown menus are dismissed before re-initializing the dialog // ensure dropdown menus are dismissed before re-initializing the dialog
dismissWallet(); dismissWallet();
if (mControlsUiController != null) {
mControlsUiController.hide();
}
super.refreshDialog(); super.refreshDialog();
if (mControlsUiController != null) {
mControlsUiController.show(mControlsView, this::dismissForControlsActivity,
null /* activityContext */);
}
} }
void hideLockMessage() { void hideLockMessage() {
@@ -699,15 +536,8 @@ public class GlobalActionsDialog extends GlobalActionsDialogLite
return isPanelDebugModeEnabled(context); return isPanelDebugModeEnabled(context);
} }
private boolean controlsAvailable() {
return isDeviceProvisioned()
&& mControlsComponent.isEnabled()
&& !mControlsServiceInfos.isEmpty();
}
private boolean shouldShowLockMessage(ActionsDialog dialog) { private boolean shouldShowLockMessage(ActionsDialog dialog) {
return mControlsComponent.getVisibility() == AVAILABLE_AFTER_UNLOCK return isWalletAvailableAfterUnlock(dialog);
|| isWalletAvailableAfterUnlock(dialog);
} }
// Temporary while we move items out of the power menu // 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) boolean isLockedAfterBoot = mLockPatternUtils.getStrongAuthForUser(getCurrentUser().id)
== STRONG_AUTH_REQUIRED_AFTER_BOOT; == STRONG_AUTH_REQUIRED_AFTER_BOOT;
return !mKeyguardStateController.isUnlocked() return !mKeyguardStateController.isUnlocked()
&& (!mShowLockScreenCardsAndControls || isLockedAfterBoot) && (!mShowLockScreenCards || isLockedAfterBoot)
&& dialog.isWalletViewAvailable(); && dialog.isWalletViewAvailable();
} }
private void onPowerMenuLockScreenSettingsChanged() { private void onPowerMenuLockScreenSettingsChanged() {
mShowLockScreenCardsAndControls = mSecureSettings.getInt( mShowLockScreenCards = mSecureSettings.getInt(
Settings.Secure.POWER_MENU_LOCKED_SHOW_CONTENT, 0) != 0; Settings.Secure.POWER_MENU_LOCKED_SHOW_CONTENT, 0) != 0;
} }
} }

View File

@@ -23,7 +23,6 @@ import android.os.Handler;
import android.os.Looper; import android.os.Looper;
import android.os.UserHandle; import android.os.UserHandle;
import android.os.UserManager; import android.os.UserManager;
import android.provider.Settings;
import android.provider.Settings.Secure; import android.provider.Settings.Secure;
import android.service.quicksettings.Tile; import android.service.quicksettings.Tile;
import android.text.TextUtils; 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;
import com.android.systemui.tuner.TunerService.Tunable; import com.android.systemui.tuner.TunerService.Tunable;
import com.android.systemui.util.leak.GarbageMonitor; import com.android.systemui.util.leak.GarbageMonitor;
import com.android.systemui.util.settings.SecureSettings;
import java.io.FileDescriptor; import java.io.FileDescriptor;
import java.io.PrintWriter; 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 boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
private static final int MAX_QS_INSTANCE_ID = 1 << 20; 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; public static final String TILES_SETTING = Secure.QS_TILES;
private final Context mContext; private final Context mContext;
@@ -101,6 +102,7 @@ public class QSTileHost implements QSHost, Tunable, PluginListener<QSFactory>, D
private final Optional<StatusBar> mStatusBarOptional; private final Optional<StatusBar> mStatusBarOptional;
private Context mUserContext; private Context mUserContext;
private UserTracker mUserTracker; private UserTracker mUserTracker;
private SecureSettings mSecureSettings;
@Inject @Inject
public QSTileHost(Context context, public QSTileHost(Context context,
@@ -116,7 +118,8 @@ public class QSTileHost implements QSHost, Tunable, PluginListener<QSFactory>, D
Optional<StatusBar> statusBarOptional, Optional<StatusBar> statusBarOptional,
QSLogger qsLogger, QSLogger qsLogger,
UiEventLogger uiEventLogger, UiEventLogger uiEventLogger,
UserTracker userTracker) { UserTracker userTracker,
SecureSettings secureSettings) {
mIconController = iconController; mIconController = iconController;
mContext = context; mContext = context;
mUserContext = context; mUserContext = context;
@@ -135,6 +138,7 @@ public class QSTileHost implements QSHost, Tunable, PluginListener<QSFactory>, D
pluginManager.addPluginListener(this, QSFactory.class, true); pluginManager.addPluginListener(this, QSFactory.class, true);
mDumpManager.registerDumpable(TAG, this); mDumpManager.registerDumpable(TAG, this);
mUserTracker = userTracker; mUserTracker = userTracker;
mSecureSettings = secureSettings;
mainHandler.post(() -> { mainHandler.post(() -> {
// This is technically a hack to avoid circular dependency of // 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); 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) { 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, * Add a tile into the requested spot, or at the end if the position is greater than the number
TextUtils.join(",", tileSpecs), null /* tag */, * of tiles.
false /* default */, mCurrentUser, true /* overrideable by restore */); * @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) { private void changeTileSpecs(Predicate<List<String>> changeFunction) {
final String setting = Settings.Secure.getStringForUser(mContext.getContentResolver(), final String setting = mSecureSettings.getStringForUser(TILES_SETTING, mCurrentUser);
TILES_SETTING, mCurrentUser);
final List<String> tileSpecs = loadTileSpecs(mContext, setting); final List<String> tileSpecs = loadTileSpecs(mContext, setting);
if (changeFunction.test(tileSpecs)) { if (changeFunction.test(tileSpecs)) {
saveTilesToSettings(tileSpecs); saveTilesToSettings(tileSpecs);

View File

@@ -32,6 +32,7 @@ import com.android.systemui.statusbar.phone.AutoTileManager;
import com.android.systemui.statusbar.phone.ManagedProfileController; import com.android.systemui.statusbar.phone.ManagedProfileController;
import com.android.systemui.statusbar.policy.CastController; import com.android.systemui.statusbar.policy.CastController;
import com.android.systemui.statusbar.policy.DataSaverController; 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.statusbar.policy.HotspotController;
import com.android.systemui.util.settings.SecureSettings; import com.android.systemui.util.settings.SecureSettings;
@@ -61,6 +62,7 @@ public interface QSModule {
NightDisplayListener nightDisplayListener, NightDisplayListener nightDisplayListener,
CastController castController, CastController castController,
ReduceBrightColorsController reduceBrightColorsController, ReduceBrightColorsController reduceBrightColorsController,
DeviceControlsController deviceControlsController,
@Named(RBC_AVAILABLE) boolean isReduceBrightColorsAvailable) { @Named(RBC_AVAILABLE) boolean isReduceBrightColorsAvailable) {
AutoTileManager manager = new AutoTileManager( AutoTileManager manager = new AutoTileManager(
context, context,
@@ -74,6 +76,7 @@ public interface QSModule {
nightDisplayListener, nightDisplayListener,
castController, castController,
reduceBrightColorsController, reduceBrightColorsController,
deviceControlsController,
isReduceBrightColorsAvailable isReduceBrightColorsAvailable
); );
manager.init(); manager.init();

View File

@@ -39,8 +39,6 @@ import com.android.systemui.plugins.statusbar.StatusBarStateController
import com.android.systemui.qs.QSHost import com.android.systemui.qs.QSHost
import com.android.systemui.qs.logging.QSLogger import com.android.systemui.qs.logging.QSLogger
import com.android.systemui.qs.tileimpl.QSTileImpl 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 java.util.concurrent.atomic.AtomicBoolean
import javax.inject.Inject import javax.inject.Inject
@@ -53,9 +51,7 @@ class DeviceControlsTile @Inject constructor(
statusBarStateController: StatusBarStateController, statusBarStateController: StatusBarStateController,
activityStarter: ActivityStarter, activityStarter: ActivityStarter,
qsLogger: QSLogger, qsLogger: QSLogger,
private val controlsComponent: ControlsComponent, private val controlsComponent: ControlsComponent
private val featureFlags: FeatureFlags,
globalSettings: GlobalSettings
) : QSTileImpl<QSTile.State>( ) : QSTileImpl<QSTile.State>(
host, host,
backgroundLooper, backgroundLooper,
@@ -67,11 +63,6 @@ class DeviceControlsTile @Inject constructor(
qsLogger qsLogger
) { ) {
companion object {
const val SETTINGS_FLAG = "controls_lockscreen"
}
private val controlsLockscreen = globalSettings.getInt(SETTINGS_FLAG, 0) != 0
private var hasControlsApps = AtomicBoolean(false) private var hasControlsApps = AtomicBoolean(false)
private val intent = Intent(Settings.ACTION_DEVICE_CONTROLS_SETTINGS) private val intent = Intent(Settings.ACTION_DEVICE_CONTROLS_SETTINGS)
@@ -92,9 +83,7 @@ class DeviceControlsTile @Inject constructor(
} }
override fun isAvailable(): Boolean { override fun isAvailable(): Boolean {
return featureFlags.isKeyguardLayoutEnabled && return controlsComponent.getControlsController().isPresent
controlsLockscreen &&
controlsComponent.getControlsController().isPresent
} }
override fun newTileState(): QSTile.State { override fun newTileState(): QSTile.State {
@@ -114,7 +103,7 @@ class DeviceControlsTile @Inject constructor(
val i = Intent().apply { val i = Intent().apply {
component = ComponentName(mContext, ControlsActivity::class.java) component = ComponentName(mContext, ControlsActivity::class.java)
addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP or Intent.FLAG_ACTIVITY_NEW_TASK) 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) mContext.startActivity(i)
} }

View File

@@ -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.CastController.CastDevice;
import com.android.systemui.statusbar.policy.DataSaverController; import com.android.systemui.statusbar.policy.DataSaverController;
import com.android.systemui.statusbar.policy.DataSaverController.Listener; 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;
import com.android.systemui.statusbar.policy.HotspotController.Callback; import com.android.systemui.statusbar.policy.HotspotController.Callback;
import com.android.systemui.util.UserAwareController; 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 WORK = "work";
public static final String NIGHT = "night"; public static final String NIGHT = "night";
public static final String CAST = "cast"; public static final String CAST = "cast";
public static final String DEVICE_CONTROLS = "controls";
public static final String BRIGHTNESS = "reduce_brightness"; public static final String BRIGHTNESS = "reduce_brightness";
static final String SETTING_SEPARATOR = ":"; static final String SETTING_SEPARATOR = ":";
@@ -74,6 +76,7 @@ public class AutoTileManager implements UserAwareController {
private final ManagedProfileController mManagedProfileController; private final ManagedProfileController mManagedProfileController;
private final NightDisplayListener mNightDisplayListener; private final NightDisplayListener mNightDisplayListener;
private final CastController mCastController; private final CastController mCastController;
private final DeviceControlsController mDeviceControlsController;
private final ReduceBrightColorsController mReduceBrightColorsController; private final ReduceBrightColorsController mReduceBrightColorsController;
private final boolean mIsReduceBrightColorsAvailable; private final boolean mIsReduceBrightColorsAvailable;
private final ArrayList<AutoAddSetting> mAutoAddSettingList = new ArrayList<>(); private final ArrayList<AutoAddSetting> mAutoAddSettingList = new ArrayList<>();
@@ -88,6 +91,7 @@ public class AutoTileManager implements UserAwareController {
NightDisplayListener nightDisplayListener, NightDisplayListener nightDisplayListener,
CastController castController, CastController castController,
ReduceBrightColorsController reduceBrightColorsController, ReduceBrightColorsController reduceBrightColorsController,
DeviceControlsController deviceControlsController,
@Named(RBC_AVAILABLE) boolean isReduceBrightColorsAvailable) { @Named(RBC_AVAILABLE) boolean isReduceBrightColorsAvailable) {
mContext = context; mContext = context;
mHost = host; mHost = host;
@@ -102,6 +106,7 @@ public class AutoTileManager implements UserAwareController {
mCastController = castController; mCastController = castController;
mReduceBrightColorsController = reduceBrightColorsController; mReduceBrightColorsController = reduceBrightColorsController;
mIsReduceBrightColorsAvailable = isReduceBrightColorsAvailable; mIsReduceBrightColorsAvailable = isReduceBrightColorsAvailable;
mDeviceControlsController = deviceControlsController;
} }
/** /**
@@ -138,6 +143,9 @@ public class AutoTileManager implements UserAwareController {
if (!mAutoTracker.isAdded(BRIGHTNESS) && mIsReduceBrightColorsAvailable) { if (!mAutoTracker.isAdded(BRIGHTNESS) && mIsReduceBrightColorsAvailable) {
mReduceBrightColorsController.addCallback(mReduceBrightColorsCallback); mReduceBrightColorsController.addCallback(mReduceBrightColorsCallback);
} }
if (!mAutoTracker.isAdded(DEVICE_CONTROLS)) {
mDeviceControlsController.setCallback(mDeviceControlsCallback);
}
int settingsN = mAutoAddSettingList.size(); int settingsN = mAutoAddSettingList.size();
for (int i = 0; i < settingsN; i++) { for (int i = 0; i < settingsN; i++) {
@@ -158,6 +166,7 @@ public class AutoTileManager implements UserAwareController {
mReduceBrightColorsController.removeCallback(mReduceBrightColorsCallback); mReduceBrightColorsController.removeCallback(mReduceBrightColorsCallback);
} }
mCastController.removeCallback(mCastCallback); mCastController.removeCallback(mCastCallback);
mDeviceControlsController.removeCallback();
int settingsN = mAutoAddSettingList.size(); int settingsN = mAutoAddSettingList.size();
for (int i = 0; i < settingsN; i++) { for (int i = 0; i < settingsN; i++) {
mAutoAddSettingList.get(i).setListening(false); 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 @VisibleForTesting
final NightDisplayListener.Callback mNightDisplayCallback = final NightDisplayListener.Callback mNightDisplayCallback =
new NightDisplayListener.Callback() { new NightDisplayListener.Callback() {

View File

@@ -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()
}

View File

@@ -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()
}
}

View File

@@ -26,6 +26,8 @@ import com.android.systemui.statusbar.policy.BluetoothController;
import com.android.systemui.statusbar.policy.BluetoothControllerImpl; import com.android.systemui.statusbar.policy.BluetoothControllerImpl;
import com.android.systemui.statusbar.policy.CastController; import com.android.systemui.statusbar.policy.CastController;
import com.android.systemui.statusbar.policy.CastControllerImpl; 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.ExtensionController;
import com.android.systemui.statusbar.policy.ExtensionControllerImpl; import com.android.systemui.statusbar.policy.ExtensionControllerImpl;
import com.android.systemui.statusbar.policy.FlashlightController; 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.RotationLockControllerImpl;
import com.android.systemui.statusbar.policy.SecurityController; import com.android.systemui.statusbar.policy.SecurityController;
import com.android.systemui.statusbar.policy.SecurityControllerImpl; 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.UserInfoController;
import com.android.systemui.statusbar.policy.UserInfoControllerImpl; import com.android.systemui.statusbar.policy.UserInfoControllerImpl;
import com.android.systemui.statusbar.policy.ZenModeController; import com.android.systemui.statusbar.policy.ZenModeController;
@@ -113,6 +113,11 @@ public interface StatusBarPolicyModule {
@Binds @Binds
ZenModeController provideZenModeController(ZenModeControllerImpl controllerImpl); ZenModeController provideZenModeController(ZenModeControllerImpl controllerImpl);
/** */
@Binds
DeviceControlsController provideDeviceControlsController(
DeviceControlsControllerImpl controllerImpl);
/** */ /** */
@Binds @Binds
NetworkController.AccessPointController provideAccessPointController( NetworkController.AccessPointController provideAccessPointController(

View File

@@ -58,15 +58,10 @@ import com.android.internal.widget.LockPatternUtils;
import com.android.systemui.SysuiTestCase; import com.android.systemui.SysuiTestCase;
import com.android.systemui.broadcast.BroadcastDispatcher; import com.android.systemui.broadcast.BroadcastDispatcher;
import com.android.systemui.colorextraction.SysuiColorExtractor; 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.model.SysUiState;
import com.android.systemui.plugins.ActivityStarter; import com.android.systemui.plugins.ActivityStarter;
import com.android.systemui.plugins.GlobalActions; import com.android.systemui.plugins.GlobalActions;
import com.android.systemui.plugins.GlobalActionsPanelPlugin; import com.android.systemui.plugins.GlobalActionsPanelPlugin;
import com.android.systemui.settings.UserContextProvider;
import com.android.systemui.settings.UserTracker; import com.android.systemui.settings.UserTracker;
import com.android.systemui.statusbar.NotificationShadeDepthController; import com.android.systemui.statusbar.NotificationShadeDepthController;
import com.android.systemui.statusbar.NotificationShadeWindowController; import com.android.systemui.statusbar.NotificationShadeWindowController;
@@ -118,11 +113,8 @@ public class GlobalActionsDialogTest extends SysuiTestCase {
@Mock private SysuiColorExtractor mColorExtractor; @Mock private SysuiColorExtractor mColorExtractor;
@Mock private IStatusBarService mStatusBarService; @Mock private IStatusBarService mStatusBarService;
@Mock private NotificationShadeWindowController mNotificationShadeWindowController; @Mock private NotificationShadeWindowController mNotificationShadeWindowController;
@Mock private ControlsUiController mControlsUiController;
@Mock private IWindowManager mWindowManager; @Mock private IWindowManager mWindowManager;
@Mock private Executor mBackgroundExecutor; @Mock private Executor mBackgroundExecutor;
@Mock private ControlsListingController mControlsListingController;
@Mock private ControlsController mControlsController;
@Mock private UiEventLogger mUiEventLogger; @Mock private UiEventLogger mUiEventLogger;
@Mock private RingerModeTracker mRingerModeTracker; @Mock private RingerModeTracker mRingerModeTracker;
@Mock private RingerModeLiveData mRingerModeLiveData; @Mock private RingerModeLiveData mRingerModeLiveData;
@@ -130,10 +122,8 @@ public class GlobalActionsDialogTest extends SysuiTestCase {
@Mock GlobalActionsPanelPlugin mWalletPlugin; @Mock GlobalActionsPanelPlugin mWalletPlugin;
@Mock GlobalActionsPanelPlugin.PanelViewController mWalletController; @Mock GlobalActionsPanelPlugin.PanelViewController mWalletController;
@Mock private Handler mHandler; @Mock private Handler mHandler;
@Mock private UserContextProvider mUserContextProvider;
@Mock private UserTracker mUserTracker; @Mock private UserTracker mUserTracker;
@Mock private SecureSettings mSecureSettings; @Mock private SecureSettings mSecureSettings;
private ControlsComponent mControlsComponent;
private TestableLooper mTestableLooper; private TestableLooper mTestableLooper;
@@ -144,18 +134,6 @@ public class GlobalActionsDialogTest extends SysuiTestCase {
allowTestableLooperAsMainThread(); allowTestableLooperAsMainThread();
when(mRingerModeTracker.getRingerMode()).thenReturn(mRingerModeLiveData); 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, mGlobalActionsDialog = new GlobalActionsDialog(mContext,
mWindowManagerFuncs, mWindowManagerFuncs,
@@ -186,9 +164,7 @@ public class GlobalActionsDialogTest extends SysuiTestCase {
mUiEventLogger, mUiEventLogger,
mRingerModeTracker, mRingerModeTracker,
mSysUiState, mSysUiState,
mHandler, mHandler
mControlsComponent,
mUserContextProvider
); );
mGlobalActionsDialog.setZeroDialogPressDelayForTesting(); mGlobalActionsDialog.setZeroDialogPressDelayForTesting();
@@ -511,7 +487,7 @@ public class GlobalActionsDialogTest extends SysuiTestCase {
when(mKeyguardStateController.isUnlocked()).thenReturn(false); when(mKeyguardStateController.isUnlocked()).thenReturn(false);
when(mActivityManager.getCurrentUser()).thenReturn(newUserInfo()); when(mActivityManager.getCurrentUser()).thenReturn(newUserInfo());
when(mLockPatternUtils.getStrongAuthForUser(anyInt())).thenReturn(STRONG_AUTH_NOT_REQUIRED); when(mLockPatternUtils.getStrongAuthForUser(anyInt())).thenReturn(STRONG_AUTH_NOT_REQUIRED);
mGlobalActionsDialog.mShowLockScreenCardsAndControls = false; mGlobalActionsDialog.mShowLockScreenCards = false;
setupDefaultActions(); setupDefaultActions();
when(mWalletPlugin.onPanelShown(any(), anyBoolean())).thenReturn(mWalletController); when(mWalletPlugin.onPanelShown(any(), anyBoolean())).thenReturn(mWalletController);
when(mWalletController.getPanelContent()).thenReturn(new FrameLayout(mContext)); when(mWalletController.getPanelContent()).thenReturn(new FrameLayout(mContext));
@@ -528,14 +504,14 @@ public class GlobalActionsDialogTest extends SysuiTestCase {
} }
@Test @Test
public void testShouldNotShowLockScreenMessage_whenWalletOrControlsShownOnLockScreen() public void testShouldNotShowLockScreenMessage_whenWalletShownOnLockScreen()
throws RemoteException { throws RemoteException {
mGlobalActionsDialog = spy(mGlobalActionsDialog); mGlobalActionsDialog = spy(mGlobalActionsDialog);
mGlobalActionsDialog.mDialog = null; mGlobalActionsDialog.mDialog = null;
when(mKeyguardStateController.isUnlocked()).thenReturn(false); when(mKeyguardStateController.isUnlocked()).thenReturn(false);
when(mActivityManager.getCurrentUser()).thenReturn(newUserInfo()); when(mActivityManager.getCurrentUser()).thenReturn(newUserInfo());
when(mLockPatternUtils.getStrongAuthForUser(anyInt())).thenReturn(STRONG_AUTH_NOT_REQUIRED); when(mLockPatternUtils.getStrongAuthForUser(anyInt())).thenReturn(STRONG_AUTH_NOT_REQUIRED);
mGlobalActionsDialog.mShowLockScreenCardsAndControls = true; mGlobalActionsDialog.mShowLockScreenCards = true;
setupDefaultActions(); setupDefaultActions();
when(mWalletPlugin.onPanelShown(any(), anyBoolean())).thenReturn(mWalletController); when(mWalletPlugin.onPanelShown(any(), anyBoolean())).thenReturn(mWalletController);
when(mWalletController.getPanelContent()).thenReturn(new FrameLayout(mContext)); when(mWalletController.getPanelContent()).thenReturn(new FrameLayout(mContext));
@@ -552,7 +528,7 @@ public class GlobalActionsDialogTest extends SysuiTestCase {
} }
@Test @Test
public void testShouldNotShowLockScreenMessage_whenControlsAndWalletBothDisabled() public void testShouldNotShowLockScreenMessage_whenWalletBothDisabled()
throws RemoteException { throws RemoteException {
mGlobalActionsDialog = spy(mGlobalActionsDialog); mGlobalActionsDialog = spy(mGlobalActionsDialog);
mGlobalActionsDialog.mDialog = null; mGlobalActionsDialog.mDialog = null;
@@ -560,11 +536,10 @@ public class GlobalActionsDialogTest extends SysuiTestCase {
when(mActivityManager.getCurrentUser()).thenReturn(newUserInfo()); when(mActivityManager.getCurrentUser()).thenReturn(newUserInfo());
when(mLockPatternUtils.getStrongAuthForUser(anyInt())).thenReturn(STRONG_AUTH_NOT_REQUIRED); when(mLockPatternUtils.getStrongAuthForUser(anyInt())).thenReturn(STRONG_AUTH_NOT_REQUIRED);
mGlobalActionsDialog.mShowLockScreenCardsAndControls = true; mGlobalActionsDialog.mShowLockScreenCards = true;
setupDefaultActions(); setupDefaultActions();
when(mWalletPlugin.onPanelShown(any(), anyBoolean())).thenReturn(mWalletController); when(mWalletPlugin.onPanelShown(any(), anyBoolean())).thenReturn(mWalletController);
when(mWalletController.getPanelContent()).thenReturn(null); when(mWalletController.getPanelContent()).thenReturn(null);
when(mControlsUiController.getAvailable()).thenReturn(false);
mGlobalActionsDialog.showOrHideDialog(false, true, mWalletPlugin); mGlobalActionsDialog.showOrHideDialog(false, true, mWalletPlugin);

View File

@@ -61,6 +61,7 @@ import com.android.systemui.statusbar.policy.RemoteInputQuickSettingsDisabler;
import com.android.systemui.statusbar.policy.UserSwitcherController; import com.android.systemui.statusbar.policy.UserSwitcherController;
import com.android.systemui.tuner.TunerService; import com.android.systemui.tuner.TunerService;
import com.android.systemui.util.InjectionInflationController; import com.android.systemui.util.InjectionInflationController;
import com.android.systemui.util.settings.SecureSettings;
import org.junit.Before; import org.junit.Before;
import org.junit.Ignore; import org.junit.Ignore;
@@ -130,7 +131,8 @@ public class QSFragmentTest extends SysuiBaseFragmentTest {
mock(PluginManager.class), mock(TunerService.class), mock(PluginManager.class), mock(TunerService.class),
() -> mock(AutoTileManager.class), mock(DumpManager.class), () -> mock(AutoTileManager.class), mock(DumpManager.class),
mock(BroadcastDispatcher.class), Optional.of(mock(StatusBar.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.setHost(host);
qs.setListening(true); qs.setListening(true);

View File

@@ -21,20 +21,21 @@ import static junit.framework.Assert.assertEquals;
import static junit.framework.Assert.assertTrue; import static junit.framework.Assert.assertTrue;
import static junit.framework.TestCase.assertFalse; import static junit.framework.TestCase.assertFalse;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.ArgumentMatchers.isNull; import static org.mockito.ArgumentMatchers.isNull;
import static org.mockito.Mockito.atLeastOnce;
import static org.mockito.Mockito.mock; import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never; import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when; import static org.mockito.Mockito.when;
import android.app.ActivityManager;
import android.content.ComponentName; import android.content.ComponentName;
import android.content.Context; import android.content.Context;
import android.content.Intent; import android.content.Intent;
import android.os.Handler; import android.os.Handler;
import android.os.Looper; import android.os.Looper;
import android.provider.Settings;
import android.testing.AndroidTestingRunner; import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper; import android.testing.TestableLooper;
import android.testing.TestableLooper.RunWithLooper; 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.StatusBar;
import com.android.systemui.statusbar.phone.StatusBarIconController; import com.android.systemui.statusbar.phone.StatusBarIconController;
import com.android.systemui.tuner.TunerService; import com.android.systemui.tuner.TunerService;
import com.android.systemui.util.settings.SecureSettings;
import org.junit.After; import org.junit.After;
import org.junit.Before; import org.junit.Before;
import org.junit.Test; import org.junit.Test;
import org.junit.runner.RunWith; import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
import org.mockito.Mock; import org.mockito.Mock;
import org.mockito.MockitoAnnotations; import org.mockito.MockitoAnnotations;
import org.mockito.MockitoSession; import org.mockito.MockitoSession;
@@ -78,7 +81,6 @@ import java.io.FileDescriptor;
import java.io.PrintWriter; import java.io.PrintWriter;
import java.io.StringWriter; import java.io.StringWriter;
import java.util.List; import java.util.List;
import java.util.Objects;
import java.util.Optional; import java.util.Optional;
import javax.inject.Provider; import javax.inject.Provider;
@@ -119,6 +121,8 @@ public class QSTileHostTest extends SysuiTestCase {
private UiEventLogger mUiEventLogger; private UiEventLogger mUiEventLogger;
@Mock @Mock
private UserTracker mUserTracker; private UserTracker mUserTracker;
@Mock
private SecureSettings mSecureSettings;
private Handler mHandler; private Handler mHandler;
private TestableLooper mLooper; private TestableLooper mLooper;
@@ -138,11 +142,12 @@ public class QSTileHostTest extends SysuiTestCase {
mHandler = new Handler(mLooper.getLooper()); mHandler = new Handler(mLooper.getLooper());
mQSTileHost = new TestQSTileHost(mContext, mIconController, mDefaultFactory, mHandler, mQSTileHost = new TestQSTileHost(mContext, mIconController, mDefaultFactory, mHandler,
mLooper.getLooper(), mPluginManager, mTunerService, mAutoTiles, mDumpManager, mLooper.getLooper(), mPluginManager, mTunerService, mAutoTiles, mDumpManager,
mBroadcastDispatcher, mStatusBar, mQSLogger, mUiEventLogger, mUserTracker); mBroadcastDispatcher, mStatusBar, mQSLogger, mUiEventLogger, mUserTracker,
mSecureSettings);
setUpTileFactory(); setUpTileFactory();
Settings.Secure.putStringForUser(mContext.getContentResolver(), QSTileHost.TILES_SETTING, when(mSecureSettings.getStringForUser(eq(QSTileHost.TILES_SETTING), anyInt()))
"", ActivityManager.getCurrentUser()); .thenReturn("");
} }
@After @After
@@ -162,6 +167,8 @@ public class QSTileHostTest extends SysuiTestCase {
return new TestTile1(mQSTileHost); return new TestTile1(mQSTileHost);
} else if ("spec2".equals(spec)) { } else if ("spec2".equals(spec)) {
return new TestTile2(mQSTileHost); return new TestTile2(mQSTileHost);
} else if ("spec3".equals(spec)) {
return new TestTile3(mQSTileHost);
} else if ("na".equals(spec)) { } else if ("na".equals(spec)) {
return new NotAvailableTile(mQSTileHost); return new NotAvailableTile(mQSTileHost);
} else if (CUSTOM_TILE_SPEC.equals(spec)) { } else if (CUSTOM_TILE_SPEC.equals(spec)) {
@@ -232,6 +239,8 @@ public class QSTileHostTest extends SysuiTestCase {
@Test @Test
public void testNoRepeatedSpecs_addTile() { public void testNoRepeatedSpecs_addTile() {
mContext.getOrCreateTestableResources()
.addOverride(R.string.quick_settings_tiles, "spec1,spec2");
mQSTileHost.onTuningChanged(QSTileHost.TILES_SETTING, "spec1,spec2"); mQSTileHost.onTuningChanged(QSTileHost.TILES_SETTING, "spec1,spec2");
mQSTileHost.addTile("spec1"); mQSTileHost.addTile("spec1");
@@ -241,6 +250,48 @@ public class QSTileHostTest extends SysuiTestCase {
assertEquals("spec2", mQSTileHost.mTileSpecs.get(1)); 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 @Test
public void testNoRepeatedSpecs_customTile() { public void testNoRepeatedSpecs_customTile() {
mQSTileHost.onTuningChanged(QSTileHost.TILES_SETTING, CUSTOM_TILE_SPEC); mQSTileHost.onTuningChanged(QSTileHost.TILES_SETTING, CUSTOM_TILE_SPEC);
@@ -318,16 +369,17 @@ public class QSTileHostTest extends SysuiTestCase {
verify(mQSLogger, never()).logTileDestroyed(isNull(), anyString()); verify(mQSLogger, never()).logTileDestroyed(isNull(), anyString());
} }
private static class TestQSTileHost extends QSTileHost { private class TestQSTileHost extends QSTileHost {
TestQSTileHost(Context context, StatusBarIconController iconController, TestQSTileHost(Context context, StatusBarIconController iconController,
QSFactory defaultFactory, Handler mainHandler, Looper bgLooper, QSFactory defaultFactory, Handler mainHandler, Looper bgLooper,
PluginManager pluginManager, TunerService tunerService, PluginManager pluginManager, TunerService tunerService,
Provider<AutoTileManager> autoTiles, DumpManager dumpManager, Provider<AutoTileManager> autoTiles, DumpManager dumpManager,
BroadcastDispatcher broadcastDispatcher, StatusBar statusBar, QSLogger qsLogger, BroadcastDispatcher broadcastDispatcher, StatusBar statusBar, QSLogger qsLogger,
UiEventLogger uiEventLogger, UserTracker userTracker) { UiEventLogger uiEventLogger, UserTracker userTracker,
SecureSettings secureSettings) {
super(context, iconController, defaultFactory, mainHandler, bgLooper, pluginManager, super(context, iconController, defaultFactory, mainHandler, bgLooper, pluginManager,
tunerService, autoTiles, dumpManager, broadcastDispatcher, tunerService, autoTiles, dumpManager, broadcastDispatcher,
Optional.of(statusBar), qsLogger, uiEventLogger, userTracker); Optional.of(statusBar), qsLogger, uiEventLogger, userTracker, secureSettings);
} }
@Override @Override
@@ -339,18 +391,16 @@ public class QSTileHostTest extends SysuiTestCase {
} }
@Override @Override
public void changeTiles(List<String> previousTiles, List<String> newTiles) { void saveTilesToSettings(List<String> tileSpecs) {
String previousSetting = Settings.Secure.getStringForUser( super.saveTilesToSettings(tileSpecs);
getContext().getContentResolver(), TILES_SETTING,
ActivityManager.getCurrentUser()); ArgumentCaptor<String> specs = ArgumentCaptor.forClass(String.class);
super.changeTiles(previousTiles, newTiles); 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 // After tiles are changed, make sure to call onTuningChanged with the new setting if it
// changed // changed
String newSetting = Settings.Secure.getStringForUser(getContext().getContentResolver(), onTuningChanged(TILES_SETTING, specs.getValue());
TILES_SETTING, ActivityManager.getCurrentUser());
if (!Objects.equals(newSetting, previousSetting)) {
onTuningChanged(TILES_SETTING, newSetting);
}
} }
} }
@@ -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 { private class NotAvailableTile extends TestTile {
protected NotAvailableTile(QSHost host) { protected NotAvailableTile(QSHost host) {

View File

@@ -53,6 +53,7 @@ import com.android.systemui.statusbar.phone.StatusBar;
import com.android.systemui.statusbar.phone.StatusBarIconController; import com.android.systemui.statusbar.phone.StatusBarIconController;
import com.android.systemui.statusbar.policy.BluetoothController; import com.android.systemui.statusbar.policy.BluetoothController;
import com.android.systemui.tuner.TunerService; import com.android.systemui.tuner.TunerService;
import com.android.systemui.util.settings.SecureSettings;
import org.junit.After; import org.junit.After;
import org.junit.Before; import org.junit.Before;
@@ -95,6 +96,8 @@ public class TileServicesTest extends SysuiTestCase {
private UiEventLogger mUiEventLogger; private UiEventLogger mUiEventLogger;
@Mock @Mock
private UserTracker mUserTracker; private UserTracker mUserTracker;
@Mock
private SecureSettings mSecureSettings;
@Before @Before
public void setUp() throws Exception { public void setUp() throws Exception {
@@ -114,7 +117,8 @@ public class TileServicesTest extends SysuiTestCase {
Optional.of(mStatusBar), Optional.of(mStatusBar),
mQSLogger, mQSLogger,
mUiEventLogger, mUiEventLogger,
mUserTracker); mUserTracker,
mSecureSettings);
mTileService = new TestTileServices(host, Looper.getMainLooper(), mBroadcastDispatcher, mTileService = new TestTileServices(host, Looper.getMainLooper(), mBroadcastDispatcher,
mUserTracker); mUserTracker);
} }

View File

@@ -38,11 +38,9 @@ import com.android.systemui.plugins.ActivityStarter
import com.android.systemui.plugins.statusbar.StatusBarStateController import com.android.systemui.plugins.statusbar.StatusBarStateController
import com.android.systemui.qs.QSHost import com.android.systemui.qs.QSHost
import com.android.systemui.qs.logging.QSLogger 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.any
import com.android.systemui.util.mockito.capture import com.android.systemui.util.mockito.capture
import com.android.systemui.util.settings.FakeSettings import com.android.systemui.util.settings.FakeSettings
import com.android.systemui.util.settings.GlobalSettings
import com.android.systemui.util.settings.SecureSettings import com.android.systemui.util.settings.SecureSettings
import com.google.common.truth.Truth.assertThat import com.google.common.truth.Truth.assertThat
import org.junit.Before import org.junit.Before
@@ -83,9 +81,6 @@ class DeviceControlsTileTest : SysuiTestCase() {
@Mock @Mock
private lateinit var controlsController: ControlsController private lateinit var controlsController: ControlsController
@Mock @Mock
private lateinit var featureFlags: FeatureFlags
private lateinit var globalSettings: GlobalSettings
@Mock
private lateinit var serviceInfo: ControlsServiceInfo private lateinit var serviceInfo: ControlsServiceInfo
@Mock @Mock
private lateinit var uiEventLogger: UiEventLogger private lateinit var uiEventLogger: UiEventLogger
@@ -116,11 +111,6 @@ class DeviceControlsTileTest : SysuiTestCase() {
setupControlsComponent() setupControlsComponent()
globalSettings = FakeSettings()
globalSettings.putInt(DeviceControlsTile.SETTINGS_FLAG, 1)
`when`(featureFlags.isKeyguardLayoutEnabled).thenReturn(true)
tile = createTile() tile = createTile()
} }
@@ -155,13 +145,6 @@ class DeviceControlsTileTest : SysuiTestCase() {
assertThat(tile.isAvailable).isTrue() assertThat(tile.isAvailable).isTrue()
} }
@Test
fun testNotAvailableFeature() {
`when`(featureFlags.isKeyguardLayoutEnabled).thenReturn(false)
assertThat(tile.isAvailable).isFalse()
}
@Test @Test
fun testNotAvailableControls() { fun testNotAvailableControls() {
featureEnabled = false featureEnabled = false
@@ -178,14 +161,6 @@ class DeviceControlsTileTest : SysuiTestCase() {
assertThat(tile.isAvailable).isTrue() assertThat(tile.isAvailable).isTrue()
} }
@Test
fun testNotAvailableControlsLockscreenFlag() {
globalSettings.putInt(DeviceControlsTile.SETTINGS_FLAG, 0)
tile = createTile()
assertThat(tile.isAvailable).isFalse()
}
@Test @Test
fun testObservingCallback() { fun testObservingCallback() {
verify(controlsListingController).observe( verify(controlsListingController).observe(
@@ -329,9 +304,7 @@ class DeviceControlsTileTest : SysuiTestCase() {
statusBarStateController, statusBarStateController,
activityStarter, activityStarter,
qsLogger, qsLogger,
controlsComponent, controlsComponent
featureFlags,
globalSettings
) )
} }
} }

View File

@@ -54,6 +54,7 @@ import com.android.systemui.qs.SecureSetting;
import com.android.systemui.statusbar.policy.CastController; import com.android.systemui.statusbar.policy.CastController;
import com.android.systemui.statusbar.policy.CastController.CastDevice; import com.android.systemui.statusbar.policy.CastController.CastDevice;
import com.android.systemui.statusbar.policy.DataSaverController; 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.statusbar.policy.HotspotController;
import com.android.systemui.util.settings.FakeSettings; import com.android.systemui.util.settings.FakeSettings;
import com.android.systemui.util.settings.SecureSettings; import com.android.systemui.util.settings.SecureSettings;
@@ -94,6 +95,7 @@ public class AutoTileManagerTest extends SysuiTestCase {
@Mock private ManagedProfileController mManagedProfileController; @Mock private ManagedProfileController mManagedProfileController;
@Mock private NightDisplayListener mNightDisplayListener; @Mock private NightDisplayListener mNightDisplayListener;
@Mock private ReduceBrightColorsController mReduceBrightColorsController; @Mock private ReduceBrightColorsController mReduceBrightColorsController;
@Mock private DeviceControlsController mDeviceControlsController;;
@Mock(answer = Answers.RETURNS_SELF) @Mock(answer = Answers.RETURNS_SELF)
private AutoAddTracker.Builder mAutoAddTrackerBuilder; private AutoAddTracker.Builder mAutoAddTrackerBuilder;
@Mock private Context mUserContext; @Mock private Context mUserContext;
@@ -139,6 +141,7 @@ public class AutoTileManagerTest extends SysuiTestCase {
NightDisplayListener nightDisplayListener, NightDisplayListener nightDisplayListener,
CastController castController, CastController castController,
ReduceBrightColorsController reduceBrightColorsController, ReduceBrightColorsController reduceBrightColorsController,
DeviceControlsController deviceControlsController,
@Named(RBC_AVAILABLE) boolean isReduceBrightColorsAvailable) { @Named(RBC_AVAILABLE) boolean isReduceBrightColorsAvailable) {
return new AutoTileManager(context, autoAddTrackerBuilder, mQsTileHost, return new AutoTileManager(context, autoAddTrackerBuilder, mQsTileHost,
Handler.createAsync(TestableLooper.get(this).getLooper()), Handler.createAsync(TestableLooper.get(this).getLooper()),
@@ -149,13 +152,15 @@ public class AutoTileManagerTest extends SysuiTestCase {
nightDisplayListener, nightDisplayListener,
castController, castController,
reduceBrightColorsController, reduceBrightColorsController,
deviceControlsController,
isReduceBrightColorsAvailable); isReduceBrightColorsAvailable);
} }
private AutoTileManager createAutoTileManager(Context context) { private AutoTileManager createAutoTileManager(Context context) {
return createAutoTileManager(context, mAutoAddTrackerBuilder, mHotspotController, return createAutoTileManager(context, mAutoAddTrackerBuilder, mHotspotController,
mDataSaverController, mManagedProfileController, mNightDisplayListener, mDataSaverController, mManagedProfileController, mNightDisplayListener,
mCastController, mReduceBrightColorsController, mIsReduceBrightColorsAvailable); mCastController, mReduceBrightColorsController, mDeviceControlsController,
mIsReduceBrightColorsAvailable);
} }
@Test @Test
@@ -169,10 +174,11 @@ public class AutoTileManagerTest extends SysuiTestCase {
NightDisplayListener nDS = mock(NightDisplayListener.class); NightDisplayListener nDS = mock(NightDisplayListener.class);
CastController cC = mock(CastController.class); CastController cC = mock(CastController.class);
ReduceBrightColorsController rBC = mock(ReduceBrightColorsController.class); ReduceBrightColorsController rBC = mock(ReduceBrightColorsController.class);
DeviceControlsController dCC = mock(DeviceControlsController.class);
AutoTileManager manager = AutoTileManager manager =
createAutoTileManager(mock(Context.class), builder, hC, dSC, mPC, nDS, cC, rBC, createAutoTileManager(mock(Context.class), builder, hC, dSC, mPC, nDS, cC, rBC,
true); dCC, true);
verify(tracker, never()).initialize(); verify(tracker, never()).initialize();
verify(hC, never()).addCallback(any()); verify(hC, never()).addCallback(any());
@@ -181,6 +187,7 @@ public class AutoTileManagerTest extends SysuiTestCase {
verify(nDS, never()).setCallback(any()); verify(nDS, never()).setCallback(any());
verify(cC, never()).addCallback(any()); verify(cC, never()).addCallback(any());
verify(rBC, never()).addCallback(any()); verify(rBC, never()).addCallback(any());
verify(dCC, never()).setCallback(any());
assertNull(manager.getSecureSettingForKey(TEST_SETTING)); assertNull(manager.getSecureSettingForKey(TEST_SETTING));
assertNull(manager.getSecureSettingForKey(TEST_SETTING_COMPONENT)); assertNull(manager.getSecureSettingForKey(TEST_SETTING_COMPONENT));
} }
@@ -229,6 +236,10 @@ public class AutoTileManagerTest extends SysuiTestCase {
inOrderCast.verify(mCastController).removeCallback(any()); inOrderCast.verify(mCastController).removeCallback(any());
inOrderCast.verify(mCastController).addCallback(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); SecureSetting setting = mAutoTileManager.getSecureSettingForKey(TEST_SETTING);
assertEquals(USER + 1, setting.getCurrentUser()); assertEquals(USER + 1, setting.getCurrentUser());
assertTrue(setting.isListening()); assertTrue(setting.isListening());
@@ -273,6 +284,10 @@ public class AutoTileManagerTest extends SysuiTestCase {
inOrderCast.verify(mCastController).removeCallback(any()); inOrderCast.verify(mCastController).removeCallback(any());
inOrderCast.verify(mCastController, never()).addCallback(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); SecureSetting setting = mAutoTileManager.getSecureSettingForKey(TEST_SETTING);
assertEquals(USER + 1, setting.getCurrentUser()); assertEquals(USER + 1, setting.getCurrentUser());
assertFalse(setting.isListening()); assertFalse(setting.isListening());

View File

@@ -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)
}
}