diff --git a/packages/SystemUI/res/layout/controls_detail_dialog.xml b/packages/SystemUI/res/layout/controls_detail_dialog.xml
new file mode 100644
index 0000000000000..f2de45a57ff64
--- /dev/null
+++ b/packages/SystemUI/res/layout/controls_detail_dialog.xml
@@ -0,0 +1,23 @@
+
+
+
+
+
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 59eb00dd3f5a7..eb84a8bcbfbdf 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/ui/ControlActionCoordinator.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlActionCoordinator.kt
@@ -18,6 +18,7 @@ package com.android.systemui.controls.ui
import android.app.PendingIntent
import android.content.Intent
+import android.provider.Settings
import android.service.controls.actions.BooleanAction
import android.util.Log
import android.view.HapticFeedbackConstants
@@ -26,6 +27,8 @@ object ControlActionCoordinator {
public const val MIN_LEVEL = 0
public const val MAX_LEVEL = 10000
+ private var useDetailDialog: Boolean? = null
+
fun toggle(cvh: ControlViewHolder, templateId: String, isChecked: Boolean) {
cvh.action(BooleanAction(templateId, !isChecked))
@@ -36,11 +39,20 @@ object ControlActionCoordinator {
fun longPress(cvh: ControlViewHolder) {
// Long press snould only be called when there is valid control state, otherwise ignore
cvh.cws.control?.let {
+ if (useDetailDialog == null) {
+ useDetailDialog = Settings.Secure.getInt(cvh.context.getContentResolver(),
+ "systemui.controls_use_detail_panel", 0) != 0
+ }
+
try {
cvh.layout.performHapticFeedback(HapticFeedbackConstants.LONG_PRESS)
- it.getAppIntent().send()
- val closeDialog = Intent(Intent.ACTION_CLOSE_SYSTEM_DIALOGS)
- cvh.context.sendBroadcast(closeDialog)
+ if (useDetailDialog!!) {
+ DetailDialog(cvh.context, it.getAppIntent()).show()
+ } else {
+ it.getAppIntent().send()
+ val closeDialog = Intent(Intent.ACTION_CLOSE_SYSTEM_DIALOGS)
+ cvh.context.sendBroadcast(closeDialog)
+ }
} catch (e: PendingIntent.CanceledException) {
Log.e(ControlsUiController.TAG, "Error sending pending intent", e)
cvh.setTransientStatus("Error opening application")
diff --git a/packages/SystemUI/src/com/android/systemui/controls/ui/DetailDialog.kt b/packages/SystemUI/src/com/android/systemui/controls/ui/DetailDialog.kt
new file mode 100644
index 0000000000000..071198bfd263d
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/controls/ui/DetailDialog.kt
@@ -0,0 +1,106 @@
+/*
+ * 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.ActivityView
+import android.app.ActivityOptions
+import android.app.Dialog
+import android.app.PendingIntent
+import android.content.ComponentName
+import android.content.Context
+import android.content.Intent
+import android.view.View
+import android.view.ViewGroup
+import android.view.ViewGroup.LayoutParams.MATCH_PARENT
+import android.view.Window
+import android.view.WindowManager
+import android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS
+
+import com.android.systemui.R
+
+/**
+ * A dialog that provides an {@link ActivityView}, 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 parentContext: Context,
+ val intent: PendingIntent
+) : Dialog(parentContext) {
+
+ lateinit var activityView: ActivityView
+
+ val stateCallback: ActivityView.StateCallback = object : ActivityView.StateCallback() {
+ override fun onActivityViewReady(view: ActivityView) {
+ val fillInIntent = Intent()
+
+ // Apply flags to make behaviour match documentLaunchMode=always.
+ fillInIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
+ fillInIntent.addFlags(Intent.FLAG_ACTIVITY_MULTIPLE_TASK)
+ view.startActivity(intent, fillInIntent, ActivityOptions.makeBasic())
+ }
+
+ override fun onActivityViewDestroyed(view: ActivityView) {}
+
+ override fun onTaskCreated(taskId: Int, componentName: ComponentName) {}
+
+ override fun onTaskRemovalStarted(taskId: Int) {}
+ }
+
+ init {
+ val window = getWindow()
+ window.requestFeature(Window.FEATURE_NO_TITLE)
+
+ // Inflate the decor view, so the attributes below are not overwritten by the theme.
+ window.getDecorView()
+ window.getAttributes().systemUiVisibility =
+ (window.getAttributes().systemUiVisibility
+ or View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
+ or View.SYSTEM_UI_FLAG_LAYOUT_STABLE)
+
+ window.setLayout(MATCH_PARENT, MATCH_PARENT)
+ window.clearFlags(WindowManager.LayoutParams.FLAG_DIM_BEHIND)
+ window.addFlags(WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN
+ or WindowManager.LayoutParams.FLAG_LAYOUT_INSET_DECOR
+ or WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED)
+ window.setType(WindowManager.LayoutParams.TYPE_VOLUME_OVERLAY)
+ window.getAttributes().setFitInsetsTypes(0 /* types */)
+
+ setContentView(R.layout.controls_detail_dialog)
+
+ activityView = ActivityView(context, null, 0, false)
+ requireViewById(R.id.controls_activity_view).apply {
+ addView(activityView)
+ }
+ }
+
+ override fun show() {
+ val attrs = getWindow().getAttributes()
+ attrs.layoutInDisplayCutoutMode = LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS
+ getWindow().setAttributes(attrs)
+
+ activityView.setCallback(stateCallback)
+
+ super.show()
+ }
+
+ override fun dismiss() {
+ activityView.release()
+
+ super.dismiss()
+ }
+}