Controls in QS

Continue to support launching from both global actions as well as the
QS tile. Use an activity for QS tile launches, which supports
lockscreen visibility.

Bug: 172360102
Test: manual
Change-Id: I002aa32f9126745ee310e203ad45f9c28f39fa8d
This commit is contained in:
Matt Pietal
2021-03-10 09:00:51 -05:00
parent c7f9fc813a
commit 7d969181e9
20 changed files with 255 additions and 257 deletions

View File

@@ -751,6 +751,17 @@
android:visibleToInstantApps="true">
</activity>
<activity android:name=".controls.ui.ControlsActivity"
android:label="@string/quick_controls_title"
android:theme="@style/Theme.ControlsActivity"
android:excludeFromRecents="true"
android:showWhenLocked="true"
android:showForAllUsers="true"
android:launchMode="singleInstance"
android:configChanges="screenSize|smallestScreenSize|screenLayout|orientation|keyboard|keyboardHidden"
android:visibleToInstantApps="true">
</activity>
<receiver android:name=".controls.management.ControlsRequestReceiver"
android:exported="true">
<intent-filter>

View File

@@ -1,21 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
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.
-->
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<solid android:color="?android:attr/colorBackground" />
<corners android:radius="@dimen/notification_corner_radius" />
</shape>

View File

@@ -20,8 +20,8 @@
android:id="@+id/control_detail_root"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginTop="@dimen/controls_activity_view_top_offset"
android:orientation="vertical">
android:orientation="vertical"
android:background="@android:color/black">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
@@ -30,7 +30,7 @@
<ImageView
android:id="@+id/control_detail_close"
android:contentDescription="@string/accessibility_desc_close"
android:src="@drawable/ic_close"
android:src="@drawable/ic_arrow_back"
android:background="?android:attr/selectableItemBackgroundBorderless"
android:tint="@color/control_primary_text"
android:layout_width="48dp"
@@ -56,7 +56,6 @@
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"
android:background="@android:color/black"
android:orientation="vertical" />
</LinearLayout>

View File

@@ -20,11 +20,8 @@
android:id="@+id/control_detail_root"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginVertical="@dimen/controls_activity_view_top_offset"
android:layout_marginHorizontal="@dimen/controls_activity_view_side_offset"
android:padding="8dp"
android:orientation="vertical"
android:background="@drawable/controls_dialog_bg">
android:background="@android:color/black">
<com.android.systemui.globalactions.MinHeightScrollView
android:layout_width="match_parent"

View File

@@ -25,8 +25,21 @@
<!-- make sure the header stays centered in the layout by adding a spacer -->
<Space
android:id="@+id/controls_spacer"
android:layout_width="@dimen/controls_header_menu_size"
android:layout_height="1dp" />
android:layout_height="1dp"
android:visibility="gone" />
<ImageView
android:id="@+id/controls_close"
android:contentDescription="@string/accessibility_desc_close"
android:src="@drawable/ic_close"
android:background="?android:attr/selectableItemBackgroundBorderless"
android:tint="@color/control_primary_text"
android:layout_width="@dimen/controls_header_menu_size"
android:layout_height="@dimen/controls_header_menu_size"
android:padding="12dp"
android:visibility="gone" />
<!-- need to keep this outer view in order to have a correctly sized anchor
for the dropdown menu, as well as dropdown background in the right place -->
<LinearLayout

View File

@@ -643,6 +643,16 @@
<item name="wallpaperTextColor">@*android:color/primary_text_material_dark</item>
</style>
<style name="Theme.ControlsActivity" parent="@android:style/Theme.DeviceDefault.NoActionBar">
<item name="android:windowActivityTransitions">true</item>
<item name="android:windowContentTransitions">false</item>
<item name="android:windowIsTranslucent">false</item>
<item name="android:windowBackground">@android:color/transparent</item>
<item name="android:windowAnimationStyle">@null</item>
<item name="android:statusBarColor">@*android:color/transparent</item>
<item name="wallpaperTextColor">@*android:color/primary_text_material_dark</item>
</style>
<style name="Theme.CreateUser" parent="@style/Theme.SystemUI">
<item name="android:windowIsTranslucent">true</item>
<item name="android:windowBackground">#33000000</item>
@@ -654,30 +664,12 @@
<item name="android:fontFamily">@*android:string/config_bodyFontFamily</item>
</style>
<style name="Theme.SystemUI.Dialog.Control.DetailPanel" parent="@android:style/Theme.DeviceDefault.Dialog.NoActionBar">
<item name="android:windowAnimationStyle">@style/Animation.Bottomsheet</item>
<item name="android:windowFullscreen">true</item>
<style name="Theme.SystemUI.Dialog.Control.DetailPanel" parent="@android:style/Theme.DeviceDefault.Dialog.NoActionBar">
<item name="android:windowFullscreen">false</item>
<item name="android:windowIsFloating">false</item>
<item name="android:windowBackground">@null</item>
<item name="android:backgroundDimEnabled">true</item>
</style>
<style name="Animation.Bottomsheet">
<item name="android:windowEnterAnimation">@anim/bottomsheet_in</item>
<item name="android:windowExitAnimation">@anim/bottomsheet_out</item>
</style>
<style name="Theme.SystemUI.Dialog.Control.LockScreen" parent="@android:style/Theme.DeviceDefault.Dialog.NoActionBar">
<item name="android:windowAnimationStyle">@style/Animation.ControlDialog</item>
<item name="android:windowFullscreen">true</item>
<item name="android:windowIsFloating">false</item>
<item name="android:windowBackground">@null</item>
<item name="android:backgroundDimEnabled">true</item>
</style>
<style name="Animation.ControlDialog">
<item name="android:windowEnterAnimation">@*android:anim/dialog_enter</item>
<item name="android:windowExitAnimation">@*android:anim/dialog_exit</item>
<item name="android:windowBackground">@android:color/black</item>
<item name="android:backgroundDimEnabled">false</item>
<item name="android:windowAnimationStyle">@android:style/Animation.Dialog</item>
</style>
<style name="Control" />

View File

@@ -31,6 +31,7 @@ import com.android.systemui.controls.management.ControlsProviderSelectorActivity
import com.android.systemui.controls.management.ControlsRequestDialog
import com.android.systemui.controls.ui.ControlActionCoordinator
import com.android.systemui.controls.ui.ControlActionCoordinatorImpl
import com.android.systemui.controls.ui.ControlsActivity
import com.android.systemui.controls.ui.ControlsUiController
import com.android.systemui.controls.ui.ControlsUiControllerImpl
import com.android.systemui.dagger.SysUISingleton
@@ -113,4 +114,9 @@ abstract class ControlsModule {
abstract fun provideControlsRequestDialog(
activity: ControlsRequestDialog
): Activity
@Binds
@IntoMap
@ClassKey(ControlsActivity::class)
abstract fun provideControlsActivity(activity: ControlsActivity): Activity
}

View File

@@ -32,7 +32,7 @@ import com.android.systemui.broadcast.BroadcastDispatcher
import com.android.systemui.controls.CustomIconCache
import com.android.systemui.controls.controller.ControlsControllerImpl
import com.android.systemui.controls.controller.StructureInfo
import com.android.systemui.controls.ui.ControlsDialog
import com.android.systemui.controls.ui.ControlsActivity
import com.android.systemui.controls.ui.ControlsUiController
import com.android.systemui.globalactions.GlobalActionsComponent
import com.android.systemui.settings.CurrentUserTracker
@@ -112,7 +112,11 @@ class ControlsEditingActivity @Inject constructor(
if (backToGlobalActions) {
globalActionsComponent.handleShowGlobalActionsMenu()
} else {
ControlsDialog(applicationContext, broadcastDispatcher).show(uiController)
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()
}

View File

@@ -40,7 +40,7 @@ import com.android.systemui.controls.ControlsServiceInfo
import com.android.systemui.controls.TooltipManager
import com.android.systemui.controls.controller.ControlsControllerImpl
import com.android.systemui.controls.controller.StructureInfo
import com.android.systemui.controls.ui.ControlsDialog
import com.android.systemui.controls.ui.ControlsActivity
import com.android.systemui.controls.ui.ControlsUiController
import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.globalactions.GlobalActionsComponent
@@ -352,7 +352,11 @@ class ControlsFavoritingActivity @Inject constructor(
if (backToGlobalActions) {
globalActionsComponent.handleShowGlobalActionsMenu()
} else {
ControlsDialog(applicationContext, broadcastDispatcher).show(uiController)
val i = Intent().apply {
component = ComponentName(applicationContext, ControlsActivity::class.java)
addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP or Intent.FLAG_ACTIVITY_NEW_TASK)
}
startActivity(i)
}
}

View File

@@ -32,7 +32,7 @@ import androidx.recyclerview.widget.RecyclerView.AdapterDataObserver
import com.android.systemui.R
import com.android.systemui.broadcast.BroadcastDispatcher
import com.android.systemui.controls.controller.ControlsController
import com.android.systemui.controls.ui.ControlsDialog
import com.android.systemui.controls.ui.ControlsActivity
import com.android.systemui.controls.ui.ControlsUiController
import com.android.systemui.dagger.qualifiers.Background
import com.android.systemui.dagger.qualifiers.Main
@@ -116,7 +116,11 @@ class ControlsProviderSelectorActivity @Inject constructor(
if (backToGlobalActions) {
globalActionsComponent.handleShowGlobalActionsMenu()
} else {
ControlsDialog(applicationContext, broadcastDispatcher).show(uiController)
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()
}

View File

@@ -16,6 +16,7 @@
package com.android.systemui.controls.ui
import android.content.Context
import android.service.controls.Control
/**
@@ -24,8 +25,8 @@ import android.service.controls.Control
*/
interface ControlActionCoordinator {
// Handle actions launched from GlobalActionsDialog or ControlDialog
var startedFromGlobalActions: Boolean
// If launched from an Activity, continue within this stack
var activityContext: Context?
/**
* Close any dialogs which may have been open

View File

@@ -18,6 +18,7 @@ package com.android.systemui.controls.ui
import android.annotation.MainThread
import android.app.Dialog
import android.content.ComponentName
import android.content.Context
import android.content.Intent
import android.content.pm.PackageManager
@@ -60,7 +61,7 @@ class ControlActionCoordinatorImpl @Inject constructor(
private var pendingAction: Action? = null
private var actionsInProgress = mutableSetOf<String>()
override var startedFromGlobalActions: Boolean = true
override var activityContext: Context? = null
companion object {
private const val RESPONSE_TIMEOUT_IN_MILLIS = 3000L
@@ -83,7 +84,7 @@ class ControlActionCoordinatorImpl @Inject constructor(
bouncerOrRun(createAction(cvh.cws.ci.controlId, {
cvh.layout.performHapticFeedback(HapticFeedbackConstants.CONTEXT_CLICK)
if (cvh.usePanel()) {
showDialog(cvh, control.getAppIntent().getIntent())
showDetail(cvh, control.getAppIntent().getIntent())
} else {
cvh.action(CommandAction(templateId))
}
@@ -109,7 +110,7 @@ class ControlActionCoordinatorImpl @Inject constructor(
// 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())
showDetail(cvh, it.getAppIntent().getIntent())
}
}, false /* blockable */))
}
@@ -151,10 +152,16 @@ class ControlActionCoordinatorImpl @Inject constructor(
activityStarter.dismissKeyguardThenExecute({
Log.d(ControlsUiController.TAG, "Device unlocked, invoking controls action")
if (closeDialog) {
if (startedFromGlobalActions) {
activityContext?.let {
val i = Intent().apply {
component = ComponentName(context, ControlsActivity::class.java)
addFlags(
Intent.FLAG_ACTIVITY_CLEAR_TOP or Intent.FLAG_ACTIVITY_NEW_TASK)
putExtra(ControlsUiController.BACK_TO_GLOBAL_ACTIONS, false)
}
it.startActivity(i)
} ?: run {
globalActionsComponent.handleShowGlobalActionsMenu()
} else {
ControlsDialog(context, broadcastDispatcher).show(lazyUiController.get())
}
} else {
action.invoke()
@@ -170,9 +177,9 @@ class ControlActionCoordinatorImpl @Inject constructor(
bgExecutor.execute { vibrator.vibrate(effect) }
}
private fun showDialog(cvh: ControlViewHolder, intent: Intent) {
private fun showDetail(cvh: ControlViewHolder, intent: Intent) {
bgExecutor.execute {
val activities: List<ResolveInfo> = cvh.context.packageManager.queryIntentActivities(
val activities: List<ResolveInfo> = context.packageManager.queryIntentActivities(
intent,
PackageManager.MATCH_DEFAULT_ONLY
)
@@ -180,8 +187,8 @@ class ControlActionCoordinatorImpl @Inject constructor(
uiExecutor.execute {
// make sure the intent is valid before attempting to open the dialog
if (activities.isNotEmpty() && taskViewFactory.isPresent) {
taskViewFactory.get().create(cvh.context, uiExecutor, {
dialog = DetailDialog(cvh, it, intent).also {
taskViewFactory.get().create(context, uiExecutor, {
dialog = DetailDialog(activityContext, it, intent, cvh).also {
it.setOnDismissListener { _ -> dialog = null }
it.show()
}

View File

@@ -0,0 +1,86 @@
/*
* 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.controls.ui
import android.os.Bundle
import android.view.View
import android.view.ViewGroup
import android.view.WindowInsets
import android.view.WindowInsets.Type
import com.android.systemui.R
import com.android.systemui.controls.management.ControlsAnimations
import com.android.systemui.util.LifecycleActivity
import javax.inject.Inject
/**
* Displays Device Controls inside an activity
*/
class ControlsActivity @Inject constructor(
private val uiController: ControlsUiController
) : LifecycleActivity() {
private lateinit var parent: ViewGroup
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.controls_fullscreen)
requireViewById<ViewGroup>(R.id.control_detail_root).apply {
setOnApplyWindowInsetsListener {
v: View, insets: WindowInsets ->
v.apply {
val l = getPaddingLeft()
val t = getPaddingTop()
val r = getPaddingRight()
setPadding(l, t, r, insets.getInsets(Type.systemBars()).bottom)
}
WindowInsets.CONSUMED
}
}
}
override fun onStart() {
super.onStart()
parent = requireViewById<ViewGroup>(R.id.global_actions_controls)
parent.alpha = 0f
uiController.show(parent, { animateExitAndFinish() }, this)
}
override fun onResume() {
super.onResume()
ControlsAnimations.enterAnimation(parent).start()
}
override fun onBackPressed() {
animateExitAndFinish()
}
override fun onStop() {
super.onStop()
uiController.hide()
}
private fun animateExitAndFinish() {
ControlsAnimations.exitAnimation(parent, { finish() }).start()
}
}

View File

@@ -1,91 +0,0 @@
/*
* 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.controls.ui
import android.app.Dialog
import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
import android.content.IntentFilter
import android.view.View
import android.view.ViewGroup
import android.view.WindowManager
import com.android.systemui.Interpolators
import com.android.systemui.R
import com.android.systemui.broadcast.BroadcastDispatcher
import javax.inject.Inject
/**
* Show the controls space inside a dialog, as from the lock screen.
*/
class ControlsDialog @Inject constructor(
thisContext: Context,
val broadcastDispatcher: BroadcastDispatcher
) : Dialog(thisContext, R.style.Theme_SystemUI_Dialog_Control_LockScreen) {
private val receiver = object : BroadcastReceiver() {
override fun onReceive(context: Context, intent: Intent) {
val action = intent.getAction()
if (Intent.ACTION_CLOSE_SYSTEM_DIALOGS.equals(action)) {
dismiss()
}
}
}
init {
window.setType(WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG)
window.addFlags(WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM
or WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED)
setContentView(R.layout.controls_in_dialog)
requireViewById<ViewGroup>(R.id.control_detail_root).apply {
setOnClickListener { dismiss() }
(getParent() as View).setOnClickListener { dismiss() }
}
}
fun show(
controller: ControlsUiController
): ControlsDialog {
super.show()
val vg = requireViewById<ViewGroup>(com.android.systemui.R.id.global_actions_controls)
vg.alpha = 0f
controller.show(vg, { dismiss() }, false /* startedFromGlobalActions */)
vg.animate()
.alpha(1f)
.setInterpolator(Interpolators.LINEAR_OUT_SLOW_IN)
.setDuration(300)
val filter = IntentFilter()
filter.addAction(Intent.ACTION_CLOSE_SYSTEM_DIALOGS)
broadcastDispatcher.registerReceiver(receiver, filter)
return this
}
override fun dismiss() {
broadcastDispatcher.unregisterReceiver(receiver)
if (!isShowing()) return
super.dismiss()
}
}

View File

@@ -17,6 +17,7 @@
package com.android.systemui.controls.ui
import android.content.ComponentName
import android.content.Context
import android.service.controls.Control
import android.service.controls.actions.ControlAction
import android.view.ViewGroup
@@ -30,7 +31,7 @@ interface ControlsUiController {
public const val BACK_TO_GLOBAL_ACTIONS = "back_to_global_actions"
}
fun show(parent: ViewGroup, onDismiss: Runnable, startedFromGlobalActions: Boolean)
fun show(parent: ViewGroup, onDismiss: Runnable, activityContext: Context?)
fun hide()
/**

View File

@@ -148,7 +148,7 @@ class ControlsUiControllerImpl @Inject constructor (
override fun show(
parent: ViewGroup,
onDismiss: Runnable,
startedFromGlobalActions: Boolean
activityContext: Context?
) {
Log.d(ControlsUiController.TAG, "show()")
this.parent = parent
@@ -156,7 +156,7 @@ class ControlsUiControllerImpl @Inject constructor (
hidden = false
retainCache = false
controlActionCoordinator.startedFromGlobalActions = startedFromGlobalActions
controlActionCoordinator.activityContext = activityContext
allStructures = controlsController.get().getFavorites()
selectedStructure = loadPreference(allStructures)
@@ -193,7 +193,7 @@ class ControlsUiControllerImpl @Inject constructor (
controlViewsById.clear()
controlsById.clear()
show(parent, onDismiss, controlActionCoordinator.startedFromGlobalActions)
show(parent, onDismiss, controlActionCoordinator.activityContext)
val showAnim = ObjectAnimator.ofFloat(parent, "alpha", 0.0f, 1.0f)
showAnim.setInterpolator(DecelerateInterpolator(1.0f))
showAnim.setDuration(FADE_IN_MILLIS)
@@ -268,7 +268,7 @@ class ControlsUiControllerImpl @Inject constructor (
intent.putExtra(ControlsUiController.EXTRA_ANIMATE, true)
intent.putExtra(
ControlsUiController.BACK_TO_GLOBAL_ACTIONS,
controlActionCoordinator.startedFromGlobalActions
controlActionCoordinator.activityContext == null
)
onDismiss.run()
@@ -392,6 +392,17 @@ class ControlsUiControllerImpl @Inject constructor (
val inflater = LayoutInflater.from(context)
inflater.inflate(R.layout.controls_with_favorites, parent, true)
if (controlActionCoordinator.activityContext == null) {
parent.requireViewById<View>(R.id.controls_spacer).apply {
visibility = View.VISIBLE
}
} else {
parent.requireViewById<ImageView>(R.id.controls_close).apply {
setOnClickListener { _: View -> onDismiss.run() }
visibility = View.VISIBLE
}
}
val maxColumns = findMaxColumns()
val listView = parent.requireViewById(R.id.global_actions_controls_list) as ViewGroup
@@ -502,6 +513,8 @@ class ControlsUiControllerImpl @Inject constructor (
override fun hide() {
hidden = true
controlActionCoordinator.activityContext = null
closeDialogs(true)
controlsController.get().unsubscribe()

View File

@@ -22,8 +22,8 @@ import android.app.ActivityTaskManager.INVALID_TASK_ID
import android.app.Dialog
import android.app.PendingIntent
import android.content.ComponentName
import android.content.Context
import android.content.Intent
import android.provider.Settings
import android.view.View
import android.view.ViewGroup
import android.view.WindowInsets
@@ -35,18 +35,20 @@ import com.android.systemui.R
import com.android.wm.shell.TaskView
/**
* A dialog that provides an {@link ActivityView}, allowing the application to provide
* A dialog that provides an {@link TaskView}, allowing the application to provide
* additional information and actions pertaining to a {@link android.service.controls.Control}.
* The activity being launched is specified by {@link android.service.controls.Control#getAppIntent}.
*/
class DetailDialog(
val cvh: ControlViewHolder,
val activityView: TaskView,
val intent: Intent
) : Dialog(cvh.context, R.style.Theme_SystemUI_Dialog_Control_DetailPanel) {
val activityContext: Context?,
val taskView: TaskView,
val intent: Intent,
val cvh: ControlViewHolder
) : Dialog(
activityContext ?: cvh.context,
R.style.Theme_SystemUI_Dialog_Control_DetailPanel
) {
companion object {
private const val PANEL_TOP_OFFSET = "systemui.controls_panel_top_offset"
/*
* Indicate to the activity that it is being rendered in a bottomsheet, and they
* should optimize the layout for a smaller space.
@@ -71,10 +73,19 @@ class DetailDialog(
launchIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_DOCUMENT)
launchIntent.addFlags(Intent.FLAG_ACTIVITY_MULTIPLE_TASK)
activityView.startActivity(
PendingIntent.getActivity(context, 0, launchIntent,
PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE),
null, ActivityOptions.makeBasic())
val options = activityContext?.let {
ActivityOptions.makeCustomAnimation(
it,
0 /* enterResId */,
0 /* exitResId */
)
} ?: ActivityOptions.makeBasic()
taskView.startActivity(
PendingIntent.getActivity(context, 0, launchIntent,
PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE),
null,
options
)
}
override fun onTaskRemovalStarted(taskId: Int) {
@@ -92,7 +103,10 @@ class DetailDialog(
}
init {
window.setType(WindowManager.LayoutParams.TYPE_VOLUME_OVERLAY)
if (activityContext == null) {
window.setType(WindowManager.LayoutParams.TYPE_VOLUME_OVERLAY)
}
// To pass touches to the task inside TaskView.
window.addFlags(WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL)
window.addPrivateFlags(WindowManager.LayoutParams.PRIVATE_FLAG_TRUSTED_OVERLAY)
@@ -100,7 +114,7 @@ class DetailDialog(
setContentView(R.layout.controls_detail_dialog)
requireViewById<ViewGroup>(R.id.controls_activity_view).apply {
addView(activityView)
addView(taskView)
}
requireViewById<ImageView>(R.id.control_detail_close).apply {
@@ -120,48 +134,34 @@ class DetailDialog(
// consume all insets to achieve slide under effect
window.getDecorView().setOnApplyWindowInsetsListener {
_: View, insets: WindowInsets ->
activityView.apply {
v: View, insets: WindowInsets ->
taskView.apply {
val l = getPaddingLeft()
val t = getPaddingTop()
val r = getPaddingRight()
setPadding(l, t, r, insets.getInsets(Type.systemBars()).bottom)
}
val l = v.getPaddingLeft()
val b = v.getPaddingBottom()
val r = v.getPaddingRight()
v.setPadding(l, insets.getInsets(Type.systemBars()).top, r, b)
WindowInsets.CONSUMED
}
requireViewById<ViewGroup>(R.id.control_detail_root).apply {
// use flag only temporarily for testing
val resolver = cvh.context.contentResolver
val defaultOffsetInPx = cvh.context.resources
.getDimensionPixelSize(R.dimen.controls_activity_view_top_offset)
val offsetInPx = Settings.Secure.getInt(resolver, PANEL_TOP_OFFSET, defaultOffsetInPx)
val lp = getLayoutParams() as ViewGroup.MarginLayoutParams
lp.topMargin = offsetInPx
setLayoutParams(lp)
setOnClickListener { dismiss() }
(getParent() as View).setOnClickListener { dismiss() }
}
if (ScreenDecorationsUtils.supportsRoundedCornersOnWindows(context.getResources())) {
val cornerRadius = context.resources
.getDimensionPixelSize(R.dimen.controls_activity_view_corner_radius)
activityView.setCornerRadius(cornerRadius.toFloat())
taskView.setCornerRadius(cornerRadius.toFloat())
}
}
override fun show() {
activityView.setListener(cvh.uiExecutor, stateCallback)
super.show()
taskView.setListener(cvh.uiExecutor, stateCallback)
}
override fun dismiss() {
if (!isShowing()) return
activityView.release()
taskView.release()
super.dismiss()
}

View File

@@ -2236,7 +2236,7 @@ public class GlobalActionsDialog implements DialogInterface.OnDismissListener,
private void showControls(ControlsUiController controller) {
mControlsUiController = controller;
mControlsUiController.show(mControlsView, this::dismissForControlsActivity,
true /* startedFromGlobalActions */);
null /* activityContext */);
}
private boolean isWalletViewAvailable() {
@@ -2465,7 +2465,7 @@ public class GlobalActionsDialog implements DialogInterface.OnDismissListener,
});
if (mControlsUiController != null) {
mControlsUiController.show(mControlsView, this::dismissForControlsActivity,
true /* startedFromGlobalActions */);
null /* activityContext */);
}
mBackgroundDrawable.setAlpha(0);
@@ -2641,7 +2641,7 @@ public class GlobalActionsDialog implements DialogInterface.OnDismissListener,
mGlobalActionsLayout.updateList();
if (mControlsUiController != null) {
mControlsUiController.show(mControlsView, this::dismissForControlsActivity,
true /* startedFromGlobalActions */);
null /* activityContext */);
}
}

View File

@@ -16,6 +16,7 @@
package com.android.systemui.qs.tiles
import android.content.ComponentName
import android.content.Intent
import android.os.Handler
import android.os.Looper
@@ -27,7 +28,8 @@ import com.android.systemui.controls.ControlsServiceInfo
import com.android.systemui.controls.dagger.ControlsComponent
import com.android.systemui.controls.dagger.ControlsComponent.Visibility.AVAILABLE
import com.android.systemui.controls.management.ControlsListingController
import com.android.systemui.controls.ui.ControlsDialog
import com.android.systemui.controls.ui.ControlsActivity
import com.android.systemui.controls.ui.ControlsUiController
import com.android.systemui.dagger.qualifiers.Background
import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.plugins.ActivityStarter
@@ -40,7 +42,6 @@ import com.android.systemui.statusbar.FeatureFlags
import com.android.systemui.util.settings.GlobalSettings
import java.util.concurrent.atomic.AtomicBoolean
import javax.inject.Inject
import javax.inject.Provider
class DeviceControlsTile @Inject constructor(
host: QSHost,
@@ -52,7 +53,6 @@ class DeviceControlsTile @Inject constructor(
qsLogger: QSLogger,
private val controlsComponent: ControlsComponent,
private val featureFlags: FeatureFlags,
private val dialogProvider: Provider<ControlsDialog>,
globalSettings: GlobalSettings
) : QSTileImpl<QSTile.State>(
host,
@@ -72,7 +72,6 @@ class DeviceControlsTile @Inject constructor(
private var hasControlsApps = AtomicBoolean(false)
private val intent = Intent(Settings.ACTION_DEVICE_CONTROLS_SETTINGS)
private var controlsDialog: ControlsDialog? = null
private val icon = ResourceIcon.get(R.drawable.ic_device_light)
private val listingCallback = object : ControlsListingController.ControlsListingCallback {
@@ -102,27 +101,19 @@ class DeviceControlsTile @Inject constructor(
}
override fun handleDestroy() {
dismissDialog()
super.handleDestroy()
}
private fun createDialog() {
if (controlsDialog?.isShowing != true) {
controlsDialog = dialogProvider.get()
}
}
private fun dismissDialog() {
controlsDialog?.dismiss()?.also {
controlsDialog = null
}
}
override fun handleClick() {
if (state.state == Tile.STATE_ACTIVE) {
mUiHandler.post {
createDialog()
controlsDialog?.show(controlsComponent.getControlsUiController().get())
mHost.collapsePanels()
val i = Intent().apply {
component = ComponentName(mContext, ControlsActivity::class.java)
addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP or Intent.FLAG_ACTIVITY_NEW_TASK)
putExtra(ControlsUiController.BACK_TO_GLOBAL_ACTIONS, false)
}
mContext.startActivity(i)
}
}
}
@@ -133,9 +124,6 @@ class DeviceControlsTile @Inject constructor(
state.contentDescription = state.label
state.icon = icon
if (controlsComponent.isEnabled() && hasControlsApps.get()) {
if (controlsDialog == null) {
mUiHandler.post(this::createDialog)
}
if (controlsComponent.getVisibility() == AVAILABLE) {
state.state = Tile.STATE_ACTIVE
state.secondaryLabel = ""
@@ -146,7 +134,6 @@ class DeviceControlsTile @Inject constructor(
state.stateDescription = state.secondaryLabel
} else {
state.state = Tile.STATE_UNAVAILABLE
dismissDialog()
}
}

View File

@@ -17,6 +17,8 @@
package com.android.systemui.qs.tiles
import android.os.Handler
import android.content.Context
import android.content.Intent
import android.provider.Settings
import android.service.quicksettings.Tile
import android.testing.AndroidTestingRunner
@@ -30,7 +32,6 @@ 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.ControlsListingController
import com.android.systemui.controls.ui.ControlsDialog
import com.android.systemui.controls.ui.ControlsUiController
import com.android.systemui.plugins.ActivityStarter
import com.android.systemui.plugins.statusbar.StatusBarStateController
@@ -50,7 +51,9 @@ import org.mockito.ArgumentCaptor
import org.mockito.Captor
import org.mockito.Mock
import org.mockito.Mockito.`when`
import org.mockito.Mockito.doNothing
import org.mockito.Mockito.never
import org.mockito.Mockito.spy
import org.mockito.Mockito.verify
import org.mockito.MockitoAnnotations
import java.util.Optional
@@ -80,8 +83,6 @@ class DeviceControlsTileTest : SysuiTestCase() {
private lateinit var controlsController: ControlsController
@Mock
private lateinit var featureFlags: FeatureFlags
@Mock
private lateinit var controlsDialog: ControlsDialog
private lateinit var globalSettings: GlobalSettings
@Mock
private lateinit var serviceInfo: ControlsServiceInfo
@@ -95,6 +96,7 @@ class DeviceControlsTileTest : SysuiTestCase() {
private lateinit var tile: DeviceControlsTile
private lateinit var secureSettings: SecureSettings
private lateinit var spiedContext: Context
private var featureEnabled = true
@Before
@@ -103,7 +105,9 @@ class DeviceControlsTileTest : SysuiTestCase() {
testableLooper = TestableLooper.get(this)
secureSettings = FakeSettings()
`when`(qsHost.context).thenReturn(mContext)
spiedContext = spy(mContext)
doNothing().`when`(spiedContext).startActivity(any(Intent::class.java))
`when`(qsHost.context).thenReturn(spiedContext)
`when`(qsHost.uiEventLogger).thenReturn(uiEventLogger)
`when`(controlsController.available).thenReturn(true)
`when`(controlsComponent.isEnabled()).thenReturn(true)
@@ -276,7 +280,7 @@ class DeviceControlsTileTest : SysuiTestCase() {
tile.click()
testableLooper.processAllMessages()
verify(controlsDialog, never()).show(any(ControlsUiController::class.java))
verify(spiedContext, never()).startActivity(any(Intent::class.java))
}
@Test
@@ -293,7 +297,7 @@ class DeviceControlsTileTest : SysuiTestCase() {
tile.click()
testableLooper.processAllMessages()
verify(controlsDialog).show(controlsUiController)
verify(spiedContext).startActivity(any(Intent::class.java))
}
@Test
@@ -311,25 +315,7 @@ class DeviceControlsTileTest : SysuiTestCase() {
tile.click()
testableLooper.processAllMessages()
verify(controlsDialog, never()).show(any(ControlsUiController::class.java))
}
@Test
fun testDialogDismissedOnDestroy() {
verify(controlsListingController).observe(
any(LifecycleOwner::class.java),
capture(listingCallbackCaptor)
)
listingCallbackCaptor.value.onServicesUpdated(listOf(serviceInfo))
testableLooper.processAllMessages()
tile.click()
testableLooper.processAllMessages()
tile.destroy()
testableLooper.processAllMessages()
verify(controlsDialog).dismiss()
verify(spiedContext, never()).startActivity(any(Intent::class.java))
}
private fun createTile(): DeviceControlsTile {
@@ -343,7 +329,6 @@ class DeviceControlsTileTest : SysuiTestCase() {
qsLogger,
controlsComponent,
featureFlags,
{ controlsDialog },
globalSettings
)
}