Merge "Controls UI - Lock screen support" into rvc-dev
This commit is contained in:
@@ -30,6 +30,8 @@ import com.android.systemui.controls.management.ControlsProviderSelectorActivity
|
||||
import com.android.systemui.controls.management.ControlsRequestDialog
|
||||
import com.android.systemui.controls.ui.ControlsUiController
|
||||
import com.android.systemui.controls.ui.ControlsUiControllerImpl
|
||||
import com.android.systemui.controls.ui.ControlActionCoordinator
|
||||
import com.android.systemui.controls.ui.ControlActionCoordinatorImpl
|
||||
import dagger.Binds
|
||||
import dagger.BindsOptionalOf
|
||||
import dagger.Module
|
||||
@@ -55,6 +57,11 @@ abstract class ControlsModule {
|
||||
@Binds
|
||||
abstract fun provideUiController(controller: ControlsUiControllerImpl): ControlsUiController
|
||||
|
||||
@Binds
|
||||
abstract fun provideControlActionCoordinator(
|
||||
coordinator: ControlActionCoordinatorImpl
|
||||
): ControlActionCoordinator
|
||||
|
||||
@BindsOptionalOf
|
||||
abstract fun optionalPersistenceWrapper(): ControlsFavoritePersistenceWrapper
|
||||
|
||||
@@ -85,4 +92,4 @@ abstract class ControlsModule {
|
||||
abstract fun provideControlsRequestDialog(
|
||||
activity: ControlsRequestDialog
|
||||
): Activity
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,89 +16,52 @@
|
||||
|
||||
package com.android.systemui.controls.ui
|
||||
|
||||
import android.app.Dialog
|
||||
import android.content.Intent
|
||||
import android.os.Vibrator
|
||||
import android.os.VibrationEffect
|
||||
import android.service.controls.Control
|
||||
import android.service.controls.actions.BooleanAction
|
||||
import android.service.controls.actions.CommandAction
|
||||
import android.view.HapticFeedbackConstants
|
||||
import com.android.systemui.controls.controller.ControlsController
|
||||
import com.android.systemui.util.concurrency.DelayableExecutor
|
||||
|
||||
object ControlActionCoordinator {
|
||||
const val MIN_LEVEL = 0
|
||||
const val MAX_LEVEL = 10000
|
||||
|
||||
private var dialog: Dialog? = null
|
||||
private var vibrator: Vibrator? = null
|
||||
|
||||
lateinit var bgExecutor: DelayableExecutor
|
||||
|
||||
fun closeDialog() {
|
||||
dialog?.dismiss()
|
||||
dialog = null
|
||||
}
|
||||
/**
|
||||
* All control interactions should be routed through this coordinator. It handles dispatching of
|
||||
* actions, haptic support, and all detail panels
|
||||
*/
|
||||
interface ControlActionCoordinator {
|
||||
|
||||
/**
|
||||
* Create custom vibrations, all intended to create very subtle feedback while interacting
|
||||
* with the controls.
|
||||
* Close any dialogs which may have been open
|
||||
*/
|
||||
fun initialize(vibrator: Vibrator, bgExecutor: DelayableExecutor) {
|
||||
this.vibrator = vibrator
|
||||
this.bgExecutor = bgExecutor
|
||||
}
|
||||
fun closeDialogs()
|
||||
|
||||
fun toggle(cvh: ControlViewHolder, templateId: String, isChecked: Boolean) {
|
||||
val effect = if (isChecked) Vibrations.toggleOnEffect else Vibrations.toggleOffEffect
|
||||
vibrate(effect)
|
||||
cvh.action(BooleanAction(templateId, !isChecked))
|
||||
}
|
||||
/**
|
||||
* Create a [BooleanAction], and inform the service of a request to change the device state
|
||||
*
|
||||
* @param cvh [ControlViewHolder] for the control
|
||||
* @param templateId id of the control's template, as given by the service
|
||||
* @param isChecked new requested state of the control
|
||||
*/
|
||||
fun toggle(cvh: ControlViewHolder, templateId: String, isChecked: Boolean)
|
||||
|
||||
fun touch(cvh: ControlViewHolder, templateId: String, control: Control) {
|
||||
vibrate(Vibrations.toggleOnEffect)
|
||||
if (cvh.usePanel()) {
|
||||
showDialog(cvh, control.getAppIntent().getIntent())
|
||||
} else {
|
||||
cvh.action(CommandAction(templateId))
|
||||
}
|
||||
}
|
||||
/**
|
||||
* For non-toggle controls, touching may create a dialog or invoke a [CommandAction].
|
||||
*
|
||||
* @param cvh [ControlViewHolder] for the control
|
||||
* @param templateId id of the control's template, as given by the service
|
||||
* @param control the control as sent by the service
|
||||
*/
|
||||
fun touch(cvh: ControlViewHolder, templateId: String, control: Control)
|
||||
|
||||
fun drag(isEdge: Boolean) {
|
||||
if (isEdge) {
|
||||
vibrate(Vibrations.rangeEdgeEffect)
|
||||
} else {
|
||||
vibrate(Vibrations.rangeMiddleEffect)
|
||||
}
|
||||
}
|
||||
/**
|
||||
* When a ToggleRange control is interacting with, a drag event is sent.
|
||||
*
|
||||
* @param isEdge did the drag event reach a control edge
|
||||
*/
|
||||
fun drag(isEdge: Boolean)
|
||||
|
||||
/**
|
||||
* All long presses will be shown in a 3/4 height bottomsheet panel, in order for the user to
|
||||
* retain context with their favorited controls in the power menu.
|
||||
*/
|
||||
fun longPress(cvh: ControlViewHolder) {
|
||||
// Long press snould only be called when there is valid control state, otherwise ignore
|
||||
cvh.cws.control?.let {
|
||||
cvh.layout.performHapticFeedback(HapticFeedbackConstants.LONG_PRESS)
|
||||
showDialog(cvh, it.getAppIntent().getIntent())
|
||||
}
|
||||
}
|
||||
fun longPress(cvh: ControlViewHolder)
|
||||
|
||||
private fun vibrate(effect: VibrationEffect) {
|
||||
vibrator?.let {
|
||||
bgExecutor.execute { it.vibrate(effect) }
|
||||
}
|
||||
}
|
||||
|
||||
private fun showDialog(cvh: ControlViewHolder, intent: Intent) {
|
||||
dialog = DetailDialog(cvh, intent).also {
|
||||
it.setOnDismissListener { _ -> dialog = null }
|
||||
it.show()
|
||||
}
|
||||
}
|
||||
|
||||
fun setFocusedElement(cvh: ControlViewHolder?, controlsController: ControlsController) {
|
||||
controlsController.onFocusChanged(cvh?.cws)
|
||||
}
|
||||
/**
|
||||
* Event to inform the UI that the user has has focused on a single control.
|
||||
*/
|
||||
fun setFocusedElement(cvh: ControlViewHolder?)
|
||||
}
|
||||
|
||||
@@ -0,0 +1,126 @@
|
||||
/*
|
||||
* Copyright (C) 2020 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.controls.ui
|
||||
|
||||
import android.app.Dialog
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.os.Vibrator
|
||||
import android.os.VibrationEffect
|
||||
import android.service.controls.Control
|
||||
import android.service.controls.actions.BooleanAction
|
||||
import android.service.controls.actions.CommandAction
|
||||
import android.util.Log
|
||||
import android.view.HapticFeedbackConstants
|
||||
import com.android.systemui.controls.controller.ControlsController
|
||||
import com.android.systemui.globalactions.GlobalActionsComponent
|
||||
import com.android.systemui.plugins.ActivityStarter
|
||||
import com.android.systemui.statusbar.policy.KeyguardStateController
|
||||
import com.android.systemui.util.concurrency.DelayableExecutor
|
||||
|
||||
import dagger.Lazy
|
||||
|
||||
import javax.inject.Inject
|
||||
import javax.inject.Singleton
|
||||
|
||||
@Singleton
|
||||
class ControlActionCoordinatorImpl @Inject constructor(
|
||||
private val context: Context,
|
||||
private val bgExecutor: DelayableExecutor,
|
||||
private val controlsController: Lazy<ControlsController>,
|
||||
private val activityStarter: ActivityStarter,
|
||||
private val keyguardStateController: KeyguardStateController,
|
||||
private val globalActionsComponent: GlobalActionsComponent
|
||||
) : ControlActionCoordinator {
|
||||
private var dialog: Dialog? = null
|
||||
private val vibrator = context.getSystemService(Context.VIBRATOR_SERVICE) as Vibrator
|
||||
private var lastAction: (() -> Unit)? = null
|
||||
|
||||
override fun closeDialogs() {
|
||||
dialog?.dismiss()
|
||||
dialog = null
|
||||
}
|
||||
|
||||
override fun toggle(cvh: ControlViewHolder, templateId: String, isChecked: Boolean) {
|
||||
bouncerOrRun {
|
||||
val effect = if (isChecked) Vibrations.toggleOnEffect else Vibrations.toggleOffEffect
|
||||
vibrate(effect)
|
||||
cvh.action(BooleanAction(templateId, !isChecked))
|
||||
}
|
||||
}
|
||||
|
||||
override fun touch(cvh: ControlViewHolder, templateId: String, control: Control) {
|
||||
vibrate(Vibrations.toggleOnEffect)
|
||||
|
||||
bouncerOrRun {
|
||||
if (cvh.usePanel()) {
|
||||
showDialog(cvh, control.getAppIntent().getIntent())
|
||||
} else {
|
||||
cvh.action(CommandAction(templateId))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun drag(isEdge: Boolean) {
|
||||
bouncerOrRun {
|
||||
if (isEdge) {
|
||||
vibrate(Vibrations.rangeEdgeEffect)
|
||||
} else {
|
||||
vibrate(Vibrations.rangeMiddleEffect)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun longPress(cvh: ControlViewHolder) {
|
||||
bouncerOrRun {
|
||||
// Long press snould only be called when there is valid control state, otherwise ignore
|
||||
cvh.cws.control?.let {
|
||||
cvh.layout.performHapticFeedback(HapticFeedbackConstants.LONG_PRESS)
|
||||
showDialog(cvh, it.getAppIntent().getIntent())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun setFocusedElement(cvh: ControlViewHolder?) {
|
||||
controlsController.get().onFocusChanged(cvh?.cws)
|
||||
}
|
||||
|
||||
private fun bouncerOrRun(f: () -> Unit) {
|
||||
if (!keyguardStateController.isUnlocked()) {
|
||||
context.sendBroadcast(Intent(Intent.ACTION_CLOSE_SYSTEM_DIALOGS))
|
||||
activityStarter.dismissKeyguardThenExecute({
|
||||
Log.d(ControlsUiController.TAG, "Device unlocked, invoking controls action")
|
||||
globalActionsComponent.handleShowGlobalActionsMenu()
|
||||
f()
|
||||
true
|
||||
}, null, true)
|
||||
} else {
|
||||
f()
|
||||
}
|
||||
}
|
||||
|
||||
private fun vibrate(effect: VibrationEffect) {
|
||||
bgExecutor.execute { vibrator.vibrate(effect) }
|
||||
}
|
||||
|
||||
private fun showDialog(cvh: ControlViewHolder, intent: Intent) {
|
||||
dialog = DetailDialog(cvh, intent).also {
|
||||
it.setOnDismissListener { _ -> dialog = null }
|
||||
it.show()
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -53,7 +53,8 @@ class ControlViewHolder(
|
||||
val layout: ViewGroup,
|
||||
val controlsController: ControlsController,
|
||||
val uiExecutor: DelayableExecutor,
|
||||
val bgExecutor: DelayableExecutor
|
||||
val bgExecutor: DelayableExecutor,
|
||||
val controlActionCoordinator: ControlActionCoordinator
|
||||
) {
|
||||
|
||||
companion object {
|
||||
@@ -65,6 +66,9 @@ class ControlViewHolder(
|
||||
DeviceTypes.TYPE_THERMOSTAT,
|
||||
DeviceTypes.TYPE_CAMERA
|
||||
)
|
||||
|
||||
const val MIN_LEVEL = 0
|
||||
const val MAX_LEVEL = 10000
|
||||
}
|
||||
|
||||
private val toggleBackgroundIntensity: Float = layout.context.resources
|
||||
@@ -121,7 +125,7 @@ class ControlViewHolder(
|
||||
cws.control?.let {
|
||||
layout.setClickable(true)
|
||||
layout.setOnLongClickListener(View.OnLongClickListener() {
|
||||
ControlActionCoordinator.longPress(this@ControlViewHolder)
|
||||
controlActionCoordinator.longPress(this@ControlViewHolder)
|
||||
true
|
||||
})
|
||||
}
|
||||
|
||||
@@ -29,7 +29,6 @@ import android.content.res.Configuration
|
||||
import android.graphics.drawable.Drawable
|
||||
import android.graphics.drawable.LayerDrawable
|
||||
import android.os.Process
|
||||
import android.os.Vibrator
|
||||
import android.service.controls.Control
|
||||
import android.util.Log
|
||||
import android.util.TypedValue
|
||||
@@ -59,6 +58,8 @@ import com.android.systemui.controls.management.ControlsListingController
|
||||
import com.android.systemui.controls.management.ControlsProviderSelectorActivity
|
||||
import com.android.systemui.dagger.qualifiers.Background
|
||||
import com.android.systemui.dagger.qualifiers.Main
|
||||
import com.android.systemui.plugins.ActivityStarter
|
||||
import com.android.systemui.statusbar.policy.KeyguardStateController
|
||||
import com.android.systemui.util.concurrency.DelayableExecutor
|
||||
import dagger.Lazy
|
||||
import java.text.Collator
|
||||
@@ -75,7 +76,10 @@ class ControlsUiControllerImpl @Inject constructor (
|
||||
@Main val uiExecutor: DelayableExecutor,
|
||||
@Background val bgExecutor: DelayableExecutor,
|
||||
val controlsListingController: Lazy<ControlsListingController>,
|
||||
@Main val sharedPreferences: SharedPreferences
|
||||
@Main val sharedPreferences: SharedPreferences,
|
||||
val controlActionCoordinator: ControlActionCoordinator,
|
||||
private val activityStarter: ActivityStarter,
|
||||
private val keyguardStateController: KeyguardStateController
|
||||
) : ControlsUiController {
|
||||
|
||||
companion object {
|
||||
@@ -107,11 +111,6 @@ class ControlsUiControllerImpl @Inject constructor (
|
||||
|
||||
private lateinit var listingCallback: ControlsListingController.ControlsListingCallback
|
||||
|
||||
init {
|
||||
val vibratorService = context.getSystemService(Context.VIBRATOR_SERVICE) as Vibrator
|
||||
ControlActionCoordinator.initialize(vibratorService, bgExecutor)
|
||||
}
|
||||
|
||||
private fun createCallback(
|
||||
onResult: (List<SelectionItem>) -> Unit
|
||||
): ControlsListingController.ControlsListingCallback {
|
||||
@@ -267,8 +266,16 @@ class ControlsUiControllerImpl @Inject constructor (
|
||||
private fun startActivity(context: Context, intent: Intent) {
|
||||
// Force animations when transitioning from a dialog to an activity
|
||||
intent.putExtra(ControlsUiController.EXTRA_ANIMATE, true)
|
||||
context.startActivity(intent)
|
||||
dismissGlobalActions.run()
|
||||
|
||||
if (!keyguardStateController.isUnlocked()) {
|
||||
activityStarter.dismissKeyguardThenExecute({
|
||||
context.startActivity(intent)
|
||||
true
|
||||
}, null, true)
|
||||
} else {
|
||||
context.startActivity(intent)
|
||||
}
|
||||
}
|
||||
|
||||
private fun showControlsView(items: List<SelectionItem>) {
|
||||
@@ -463,7 +470,8 @@ class ControlsUiControllerImpl @Inject constructor (
|
||||
baseLayout,
|
||||
controlsController.get(),
|
||||
uiExecutor,
|
||||
bgExecutor
|
||||
bgExecutor,
|
||||
controlActionCoordinator
|
||||
)
|
||||
cvh.bindData(it)
|
||||
controlViewsById.put(key, cvh)
|
||||
@@ -545,9 +553,7 @@ class ControlsUiControllerImpl @Inject constructor (
|
||||
controlViewsById.forEach {
|
||||
it.value.dismiss()
|
||||
}
|
||||
|
||||
ControlActionCoordinator.closeDialog()
|
||||
|
||||
controlActionCoordinator.closeDialogs()
|
||||
controlsController.get().unsubscribe()
|
||||
|
||||
parent.removeAllViews()
|
||||
|
||||
@@ -22,8 +22,8 @@ import android.service.controls.Control
|
||||
import android.service.controls.templates.TemperatureControlTemplate
|
||||
|
||||
import com.android.systemui.R
|
||||
import com.android.systemui.controls.ui.ControlActionCoordinator.MIN_LEVEL
|
||||
import com.android.systemui.controls.ui.ControlActionCoordinator.MAX_LEVEL
|
||||
import com.android.systemui.controls.ui.ControlViewHolder.Companion.MIN_LEVEL
|
||||
import com.android.systemui.controls.ui.ControlViewHolder.Companion.MAX_LEVEL
|
||||
|
||||
class TemperatureControlBehavior : Behavior {
|
||||
lateinit var clipLayer: Drawable
|
||||
@@ -35,7 +35,7 @@ class TemperatureControlBehavior : Behavior {
|
||||
this.cvh = cvh
|
||||
|
||||
cvh.layout.setOnClickListener { _ ->
|
||||
ControlActionCoordinator.touch(cvh, template.getTemplateId(), control)
|
||||
cvh.controlActionCoordinator.touch(cvh, template.getTemplateId(), control)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -22,7 +22,7 @@ import android.service.controls.Control
|
||||
import android.service.controls.templates.ToggleTemplate
|
||||
import android.view.View
|
||||
import com.android.systemui.R
|
||||
import com.android.systemui.controls.ui.ControlActionCoordinator.MAX_LEVEL
|
||||
import com.android.systemui.controls.ui.ControlViewHolder.Companion.MAX_LEVEL
|
||||
|
||||
class ToggleBehavior : Behavior {
|
||||
lateinit var clipLayer: Drawable
|
||||
@@ -35,7 +35,7 @@ class ToggleBehavior : Behavior {
|
||||
cvh.applyRenderInfo(false /* enabled */, 0 /* offset */, false /* animated */)
|
||||
|
||||
cvh.layout.setOnClickListener(View.OnClickListener() {
|
||||
ControlActionCoordinator.toggle(cvh, template.getTemplateId(), template.isChecked())
|
||||
cvh.controlActionCoordinator.toggle(cvh, template.getTemplateId(), template.isChecked())
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@@ -40,8 +40,8 @@ import android.view.accessibility.AccessibilityNodeInfo
|
||||
import android.widget.TextView
|
||||
import com.android.systemui.Interpolators
|
||||
import com.android.systemui.R
|
||||
import com.android.systemui.controls.ui.ControlActionCoordinator.MAX_LEVEL
|
||||
import com.android.systemui.controls.ui.ControlActionCoordinator.MIN_LEVEL
|
||||
import com.android.systemui.controls.ui.ControlViewHolder.Companion.MAX_LEVEL
|
||||
import com.android.systemui.controls.ui.ControlViewHolder.Companion.MIN_LEVEL
|
||||
import java.util.IllegalFormatException
|
||||
|
||||
class ToggleRangeBehavior : Behavior {
|
||||
@@ -141,7 +141,7 @@ class ToggleRangeBehavior : Behavior {
|
||||
): Boolean {
|
||||
val handled = when (action) {
|
||||
AccessibilityNodeInfo.ACTION_CLICK -> {
|
||||
ControlActionCoordinator.toggle(cvh, template.getTemplateId(),
|
||||
cvh.controlActionCoordinator.toggle(cvh, template.getTemplateId(),
|
||||
template.isChecked())
|
||||
true
|
||||
}
|
||||
@@ -175,7 +175,7 @@ class ToggleRangeBehavior : Behavior {
|
||||
fun beginUpdateRange() {
|
||||
status.setTextSize(TypedValue.COMPLEX_UNIT_PX, context.getResources()
|
||||
.getDimensionPixelSize(R.dimen.control_status_expanded).toFloat())
|
||||
ControlActionCoordinator.setFocusedElement(cvh, cvh.controlsController)
|
||||
cvh.controlActionCoordinator.setFocusedElement(cvh)
|
||||
}
|
||||
|
||||
fun updateRange(level: Int, checked: Boolean, isDragging: Boolean) {
|
||||
@@ -187,7 +187,7 @@ class ToggleRangeBehavior : Behavior {
|
||||
if (isDragging) {
|
||||
clipLayer.level = newLevel
|
||||
val isEdge = newLevel == MIN_LEVEL || newLevel == MAX_LEVEL
|
||||
ControlActionCoordinator.drag(isEdge)
|
||||
cvh.controlActionCoordinator.drag(isEdge)
|
||||
} else {
|
||||
rangeAnimator = ValueAnimator.ofInt(cvh.clipLayer.level, newLevel).apply {
|
||||
addUpdateListener {
|
||||
@@ -248,7 +248,7 @@ class ToggleRangeBehavior : Behavior {
|
||||
status.setText("$currentStatusText $currentRangeValue")
|
||||
cvh.action(FloatAction(rangeTemplate.getTemplateId(),
|
||||
findNearestStep(levelToRangeValue(clipLayer.getLevel()))))
|
||||
ControlActionCoordinator.setFocusedElement(null, cvh.controlsController)
|
||||
cvh.controlActionCoordinator.setFocusedElement(null)
|
||||
}
|
||||
|
||||
fun findNearestStep(value: Float): Float {
|
||||
@@ -282,7 +282,7 @@ class ToggleRangeBehavior : Behavior {
|
||||
if (isDragging) {
|
||||
return
|
||||
}
|
||||
ControlActionCoordinator.longPress(this@ToggleRangeBehavior.cvh)
|
||||
cvh.controlActionCoordinator.longPress(this@ToggleRangeBehavior.cvh)
|
||||
}
|
||||
|
||||
override fun onScroll(
|
||||
@@ -309,7 +309,7 @@ class ToggleRangeBehavior : Behavior {
|
||||
|
||||
override fun onSingleTapUp(e: MotionEvent): Boolean {
|
||||
val th = this@ToggleRangeBehavior
|
||||
ControlActionCoordinator.toggle(th.cvh, th.template.getTemplateId(),
|
||||
cvh.controlActionCoordinator.toggle(th.cvh, th.template.getTemplateId(),
|
||||
th.template.isChecked())
|
||||
return true
|
||||
}
|
||||
|
||||
@@ -23,7 +23,7 @@ import android.service.controls.Control
|
||||
import android.service.controls.templates.ControlTemplate
|
||||
|
||||
import com.android.systemui.R
|
||||
import com.android.systemui.controls.ui.ControlActionCoordinator.MIN_LEVEL
|
||||
import com.android.systemui.controls.ui.ControlViewHolder.Companion.MIN_LEVEL
|
||||
|
||||
/**
|
||||
* Supports touch events, but has no notion of state as the {@link ToggleBehavior} does. Must be
|
||||
@@ -40,7 +40,7 @@ class TouchBehavior : Behavior {
|
||||
cvh.applyRenderInfo(false /* enabled */, 0 /* offset */, false /* animated */)
|
||||
|
||||
cvh.layout.setOnClickListener(View.OnClickListener() {
|
||||
ControlActionCoordinator.touch(cvh, template.getTemplateId(), control)
|
||||
cvh.controlActionCoordinator.touch(cvh, template.getTemplateId(), control)
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@@ -24,6 +24,7 @@ import com.android.systemui.plugins.GlobalActions;
|
||||
import com.android.systemui.plugins.GlobalActions.GlobalActionsManager;
|
||||
import com.android.systemui.statusbar.CommandQueue;
|
||||
import com.android.systemui.statusbar.CommandQueue.Callbacks;
|
||||
import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager;
|
||||
import com.android.systemui.statusbar.policy.ExtensionController;
|
||||
import com.android.systemui.statusbar.policy.ExtensionController.Extension;
|
||||
|
||||
@@ -43,15 +44,18 @@ public class GlobalActionsComponent extends SystemUI implements Callbacks, Globa
|
||||
private GlobalActions mPlugin;
|
||||
private Extension<GlobalActions> mExtension;
|
||||
private IStatusBarService mBarService;
|
||||
private StatusBarKeyguardViewManager mStatusBarKeyguardViewManager;
|
||||
|
||||
@Inject
|
||||
public GlobalActionsComponent(Context context, CommandQueue commandQueue,
|
||||
ExtensionController extensionController,
|
||||
Provider<GlobalActions> globalActionsProvider) {
|
||||
Provider<GlobalActions> globalActionsProvider,
|
||||
StatusBarKeyguardViewManager statusBarKeyguardViewManager) {
|
||||
super(context);
|
||||
mCommandQueue = commandQueue;
|
||||
mExtensionController = extensionController;
|
||||
mGlobalActionsProvider = globalActionsProvider;
|
||||
mStatusBarKeyguardViewManager = statusBarKeyguardViewManager;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -81,6 +85,7 @@ public class GlobalActionsComponent extends SystemUI implements Callbacks, Globa
|
||||
|
||||
@Override
|
||||
public void handleShowGlobalActionsMenu() {
|
||||
mStatusBarKeyguardViewManager.setGlobalActionsVisible(true);
|
||||
mExtension.get().showGlobalActions(this);
|
||||
}
|
||||
|
||||
@@ -95,6 +100,7 @@ public class GlobalActionsComponent extends SystemUI implements Callbacks, Globa
|
||||
@Override
|
||||
public void onGlobalActionsHidden() {
|
||||
try {
|
||||
mStatusBarKeyguardViewManager.setGlobalActionsVisible(false);
|
||||
mBarService.onGlobalActionsHidden();
|
||||
} catch (RemoteException e) {
|
||||
}
|
||||
|
||||
@@ -234,6 +234,8 @@ public class GlobalActionsDialog implements DialogInterface.OnDismissListener,
|
||||
private SharedPreferences mControlsPreferences;
|
||||
private final RingerModeTracker mRingerModeTracker;
|
||||
private int mDialogPressDelay = DIALOG_PRESS_DELAY; // ms
|
||||
private Handler mMainHandler;
|
||||
private boolean mShowLockScreenCardsAndControls = false;
|
||||
|
||||
@VisibleForTesting
|
||||
public enum GlobalActionsEvent implements UiEventLogger.UiEventEnum {
|
||||
@@ -288,7 +290,7 @@ public class GlobalActionsDialog implements DialogInterface.OnDismissListener,
|
||||
@Background Executor backgroundExecutor,
|
||||
ControlsListingController controlsListingController,
|
||||
ControlsController controlsController, UiEventLogger uiEventLogger,
|
||||
RingerModeTracker ringerModeTracker) {
|
||||
RingerModeTracker ringerModeTracker, @Main Handler handler) {
|
||||
mContext = new ContextThemeWrapper(context, com.android.systemui.R.style.qs_theme);
|
||||
mWindowManagerFuncs = windowManagerFuncs;
|
||||
mAudioManager = audioManager;
|
||||
@@ -317,6 +319,7 @@ public class GlobalActionsDialog implements DialogInterface.OnDismissListener,
|
||||
mBlurUtils = blurUtils;
|
||||
mRingerModeTracker = ringerModeTracker;
|
||||
mControlsController = controlsController;
|
||||
mMainHandler = handler;
|
||||
|
||||
// receive broadcasts
|
||||
IntentFilter filter = new IntentFilter();
|
||||
@@ -352,10 +355,15 @@ public class GlobalActionsDialog implements DialogInterface.OnDismissListener,
|
||||
keyguardStateController.addCallback(new KeyguardStateController.Callback() {
|
||||
@Override
|
||||
public void onUnlockedChanged() {
|
||||
if (mDialog != null && mDialog.mPanelController != null) {
|
||||
if (mDialog != null) {
|
||||
boolean unlocked = keyguardStateController.isUnlocked()
|
||||
|| keyguardStateController.canDismissLockScreen();
|
||||
mDialog.mPanelController.onDeviceLockStateChanged(unlocked);
|
||||
if (mDialog.mPanelController != null) {
|
||||
mDialog.mPanelController.onDeviceLockStateChanged(unlocked);
|
||||
}
|
||||
if (!mDialog.isShowingControls() && shouldShowControls()) {
|
||||
mDialog.showControls(mControlsUiController);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
@@ -370,6 +378,17 @@ public class GlobalActionsDialog implements DialogInterface.OnDismissListener,
|
||||
mControlsPreferences = userContext.getSharedPreferences(PREFS_CONTROLS_FILE,
|
||||
Context.MODE_PRIVATE);
|
||||
|
||||
// Listen for changes to show controls on the power menu while locked
|
||||
onPowerMenuLockScreenSettingsChanged();
|
||||
mContext.getContentResolver().registerContentObserver(
|
||||
Settings.Secure.getUriFor(Settings.Secure.POWER_MENU_LOCKED_SHOW_CONTENT),
|
||||
false /* notifyForDescendants */,
|
||||
new ContentObserver(mMainHandler) {
|
||||
@Override
|
||||
public void onChange(boolean selfChange) {
|
||||
onPowerMenuLockScreenSettingsChanged();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void seedFavorites() {
|
||||
@@ -485,15 +504,7 @@ public class GlobalActionsDialog implements DialogInterface.OnDismissListener,
|
||||
@VisibleForTesting
|
||||
protected int getMaxShownPowerItems() {
|
||||
if (shouldUseControlsLayout()) {
|
||||
int maxColumns =
|
||||
mResources.getInteger(com.android.systemui.R.integer.power_menu_max_columns);
|
||||
// TODO: Overflow temporarily disabled on keyguard to prevent touch issues.
|
||||
// Show an extra item on the keyguard because the overflow button currently disabled.
|
||||
if (mKeyguardShowing) {
|
||||
return maxColumns + 1;
|
||||
} else {
|
||||
return maxColumns;
|
||||
}
|
||||
return mResources.getInteger(com.android.systemui.R.integer.power_menu_max_columns);
|
||||
} else {
|
||||
return Integer.MAX_VALUE;
|
||||
}
|
||||
@@ -1804,7 +1815,7 @@ public class GlobalActionsDialog implements DialogInterface.OnDismissListener,
|
||||
}
|
||||
};
|
||||
|
||||
private ContentObserver mAirplaneModeObserver = new ContentObserver(new Handler()) {
|
||||
private ContentObserver mAirplaneModeObserver = new ContentObserver(mMainHandler) {
|
||||
@Override
|
||||
public void onChange(boolean selfChange) {
|
||||
onAirplaneModeChanged();
|
||||
@@ -1950,6 +1961,15 @@ public class GlobalActionsDialog implements DialogInterface.OnDismissListener,
|
||||
initializeLayout();
|
||||
}
|
||||
|
||||
private boolean isShowingControls() {
|
||||
return mControlsUiController != null;
|
||||
}
|
||||
|
||||
private void showControls(ControlsUiController controller) {
|
||||
mControlsUiController = controller;
|
||||
mControlsUiController.show(mControlsView, this::dismissForControlsActivity);
|
||||
}
|
||||
|
||||
private boolean shouldUsePanel() {
|
||||
return mPanelController != null && mPanelController.getPanelContent() != null;
|
||||
}
|
||||
@@ -2066,8 +2086,7 @@ public class GlobalActionsDialog implements DialogInterface.OnDismissListener,
|
||||
View overflowButton = findViewById(
|
||||
com.android.systemui.R.id.global_actions_overflow_button);
|
||||
if (overflowButton != null) {
|
||||
// TODO: Overflow button hidden on keyguard to temporarily prevent touch issues.
|
||||
if (mOverflowAdapter.getCount() > 0 && !mKeyguardShowing) {
|
||||
if (mOverflowAdapter.getCount() > 0) {
|
||||
overflowButton.setOnClickListener((view) -> showPowerOverflowMenu());
|
||||
LinearLayout.LayoutParams params =
|
||||
(LinearLayout.LayoutParams) mGlobalActionsLayout.getLayoutParams();
|
||||
@@ -2354,7 +2373,9 @@ public class GlobalActionsDialog implements DialogInterface.OnDismissListener,
|
||||
|
||||
@VisibleForTesting
|
||||
protected boolean shouldShowControls() {
|
||||
return mKeyguardStateController.isUnlocked()
|
||||
boolean isUnlocked = mKeyguardStateController.isUnlocked()
|
||||
|| mKeyguardStateController.canDismissLockScreen();
|
||||
return (isUnlocked || mShowLockScreenCardsAndControls)
|
||||
&& mControlsUiController.getAvailable()
|
||||
&& !mControlsServiceInfos.isEmpty();
|
||||
}
|
||||
@@ -2363,4 +2384,9 @@ public class GlobalActionsDialog implements DialogInterface.OnDismissListener,
|
||||
// always use new controls layout
|
||||
return true;
|
||||
}
|
||||
|
||||
private void onPowerMenuLockScreenSettingsChanged() {
|
||||
mShowLockScreenCardsAndControls = Settings.Secure.getInt(mContentResolver,
|
||||
Settings.Secure.POWER_MENU_LOCKED_SHOW_CONTENT, 0) != 0;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -152,6 +152,8 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb
|
||||
protected boolean mShowing;
|
||||
protected boolean mOccluded;
|
||||
protected boolean mRemoteInputActive;
|
||||
private boolean mGlobalActionsVisible = false;
|
||||
private boolean mLastGlobalActionsVisible = false;
|
||||
private boolean mDozing;
|
||||
private boolean mPulsing;
|
||||
private boolean mGesturalNav;
|
||||
@@ -293,6 +295,14 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb
|
||||
updateLockIcon();
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the global actions visibility state in order to show the navBar when active.
|
||||
*/
|
||||
public void setGlobalActionsVisible(boolean isVisible) {
|
||||
mGlobalActionsVisible = isVisible;
|
||||
updateStates();
|
||||
}
|
||||
|
||||
private void updateLockIcon() {
|
||||
// Not all form factors have a lock icon
|
||||
if (mLockIconContainer == null) {
|
||||
@@ -820,6 +830,7 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb
|
||||
|
||||
mFirstUpdate = false;
|
||||
mLastShowing = showing;
|
||||
mLastGlobalActionsVisible = mGlobalActionsVisible;
|
||||
mLastOccluded = occluded;
|
||||
mLastBouncerShowing = bouncerShowing;
|
||||
mLastBouncerDismissible = bouncerDismissible;
|
||||
@@ -864,7 +875,8 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb
|
||||
boolean keyguardWithGestureNav = (keyguardShowing && !mDozing || mPulsing && !mIsDocked)
|
||||
&& mGesturalNav;
|
||||
return (!keyguardShowing && !hideWhileDozing || mBouncer.isShowing()
|
||||
|| mRemoteInputActive || keyguardWithGestureNav);
|
||||
|| mRemoteInputActive || keyguardWithGestureNav
|
||||
|| mGlobalActionsVisible);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -876,7 +888,8 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb
|
||||
boolean keyguardWithGestureNav = (keyguardShowing && !mLastDozing
|
||||
|| mLastPulsing && !mLastIsDocked) && mLastGesturalNav;
|
||||
return (!keyguardShowing && !hideWhileDozing || mLastBouncerShowing
|
||||
|| mLastRemoteInputActive || keyguardWithGestureNav);
|
||||
|| mLastRemoteInputActive || keyguardWithGestureNav
|
||||
|| mLastGlobalActionsVisible);
|
||||
}
|
||||
|
||||
public boolean shouldDismissOnMenuPressed() {
|
||||
|
||||
@@ -32,6 +32,7 @@ import android.content.ContentResolver;
|
||||
import android.content.res.Resources;
|
||||
import android.media.AudioManager;
|
||||
import android.net.ConnectivityManager;
|
||||
import android.os.Handler;
|
||||
import android.os.UserManager;
|
||||
import android.service.dreams.IDreamManager;
|
||||
import android.telephony.TelephonyManager;
|
||||
@@ -106,6 +107,7 @@ public class GlobalActionsDialogTest extends SysuiTestCase {
|
||||
@Mock private UiEventLogger mUiEventLogger;
|
||||
@Mock private RingerModeTracker mRingerModeTracker;
|
||||
@Mock private RingerModeLiveData mRingerModeLiveData;
|
||||
@Mock private Handler mHandler;
|
||||
|
||||
private TestableLooper mTestableLooper;
|
||||
|
||||
@@ -147,7 +149,8 @@ public class GlobalActionsDialogTest extends SysuiTestCase {
|
||||
mControlsListingController,
|
||||
mControlsController,
|
||||
mUiEventLogger,
|
||||
mRingerModeTracker
|
||||
mRingerModeTracker,
|
||||
mHandler
|
||||
);
|
||||
mGlobalActionsDialog.setZeroDialogPressDelayForTesting();
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user