diff --git a/packages/SystemUI/src/com/android/systemui/controls/dagger/ControlsModule.kt b/packages/SystemUI/src/com/android/systemui/controls/dagger/ControlsModule.kt index 3bed559123322..5765be57b5b0b 100644 --- a/packages/SystemUI/src/com/android/systemui/controls/dagger/ControlsModule.kt +++ b/packages/SystemUI/src/com/android/systemui/controls/dagger/ControlsModule.kt @@ -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 -} \ No newline at end of file +} diff --git a/packages/SystemUI/src/com/android/systemui/controls/ui/ControlActionCoordinator.kt b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlActionCoordinator.kt index a93d223d565e8..70092d31fe1ee 100644 --- a/packages/SystemUI/src/com/android/systemui/controls/ui/ControlActionCoordinator.kt +++ b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlActionCoordinator.kt @@ -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?) } diff --git a/packages/SystemUI/src/com/android/systemui/controls/ui/ControlActionCoordinatorImpl.kt b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlActionCoordinatorImpl.kt new file mode 100644 index 0000000000000..9e6f58851cafb --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlActionCoordinatorImpl.kt @@ -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, + 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() + } + } +} diff --git a/packages/SystemUI/src/com/android/systemui/controls/ui/ControlViewHolder.kt b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlViewHolder.kt index f5acc287262b8..a9b540eddb2cc 100644 --- a/packages/SystemUI/src/com/android/systemui/controls/ui/ControlViewHolder.kt +++ b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlViewHolder.kt @@ -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 }) } diff --git a/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsUiControllerImpl.kt b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsUiControllerImpl.kt index 25f4940e1f470..7108966072b90 100644 --- a/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsUiControllerImpl.kt +++ b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsUiControllerImpl.kt @@ -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, - @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) -> 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) { @@ -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() diff --git a/packages/SystemUI/src/com/android/systemui/controls/ui/TemperatureControlBehavior.kt b/packages/SystemUI/src/com/android/systemui/controls/ui/TemperatureControlBehavior.kt index 6340db1d756d2..b4d0e63496052 100644 --- a/packages/SystemUI/src/com/android/systemui/controls/ui/TemperatureControlBehavior.kt +++ b/packages/SystemUI/src/com/android/systemui/controls/ui/TemperatureControlBehavior.kt @@ -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) } } diff --git a/packages/SystemUI/src/com/android/systemui/controls/ui/ToggleBehavior.kt b/packages/SystemUI/src/com/android/systemui/controls/ui/ToggleBehavior.kt index 368d1399971d8..3e1669898a7ff 100644 --- a/packages/SystemUI/src/com/android/systemui/controls/ui/ToggleBehavior.kt +++ b/packages/SystemUI/src/com/android/systemui/controls/ui/ToggleBehavior.kt @@ -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()) }) } diff --git a/packages/SystemUI/src/com/android/systemui/controls/ui/ToggleRangeBehavior.kt b/packages/SystemUI/src/com/android/systemui/controls/ui/ToggleRangeBehavior.kt index 4ba2486193ca3..bfc06450b360d 100644 --- a/packages/SystemUI/src/com/android/systemui/controls/ui/ToggleRangeBehavior.kt +++ b/packages/SystemUI/src/com/android/systemui/controls/ui/ToggleRangeBehavior.kt @@ -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 } diff --git a/packages/SystemUI/src/com/android/systemui/controls/ui/TouchBehavior.kt b/packages/SystemUI/src/com/android/systemui/controls/ui/TouchBehavior.kt index fd96cea1541ea..7ae3df7514199 100644 --- a/packages/SystemUI/src/com/android/systemui/controls/ui/TouchBehavior.kt +++ b/packages/SystemUI/src/com/android/systemui/controls/ui/TouchBehavior.kt @@ -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) }) } diff --git a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsComponent.java b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsComponent.java index e949007a158bf..b29c5b07c765b 100644 --- a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsComponent.java +++ b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsComponent.java @@ -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 mExtension; private IStatusBarService mBarService; + private StatusBarKeyguardViewManager mStatusBarKeyguardViewManager; @Inject public GlobalActionsComponent(Context context, CommandQueue commandQueue, ExtensionController extensionController, - Provider globalActionsProvider) { + Provider 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) { } diff --git a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java index 80ebe5701c3c2..8123158408dda 100644 --- a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java +++ b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java @@ -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; + } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java index ed25db40fea6b..bc94cdeba37f1 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java @@ -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() { diff --git a/packages/SystemUI/tests/src/com/android/systemui/globalactions/GlobalActionsDialogTest.java b/packages/SystemUI/tests/src/com/android/systemui/globalactions/GlobalActionsDialogTest.java index b44b23a9f51e5..64acdcc30a12e 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/globalactions/GlobalActionsDialogTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/globalactions/GlobalActionsDialogTest.java @@ -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(); }