Merge "Controls UI - Support detail panels" into rvc-dev am: 47709f0e8b am: 091d515656
Change-Id: I6a041696fdfd9ce99223de8a9f3fbabe241e14ba
This commit is contained in:
26
packages/SystemUI/res/anim/bottomsheet_in.xml
Normal file
26
packages/SystemUI/res/anim/bottomsheet_in.xml
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!--
|
||||||
|
~ 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
|
||||||
|
-->
|
||||||
|
|
||||||
|
<set xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:interpolator="@*android:anim/accelerate_decelerate_interpolator"
|
||||||
|
android:zAdjustment="top">
|
||||||
|
|
||||||
|
<translate android:fromYDelta="100%"
|
||||||
|
android:toYDelta="0"
|
||||||
|
android:startOffset="@android:integer/config_shortAnimTime"
|
||||||
|
android:duration="@*android:integer/config_mediumAnimTime"/>
|
||||||
|
</set>
|
||||||
25
packages/SystemUI/res/anim/bottomsheet_out.xml
Normal file
25
packages/SystemUI/res/anim/bottomsheet_out.xml
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!--
|
||||||
|
~ 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
|
||||||
|
-->
|
||||||
|
<set xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:interpolator="@*android:anim/accelerate_interpolator"
|
||||||
|
android:zAdjustment="top">
|
||||||
|
|
||||||
|
<translate xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:fromYDelta="0"
|
||||||
|
android:toYDelta="100%"
|
||||||
|
android:duration="@*android:integer/config_shortAnimTime" />
|
||||||
|
</set>
|
||||||
22
packages/SystemUI/res/drawable/rounded_bg_top.xml
Normal file
22
packages/SystemUI/res/drawable/rounded_bg_top.xml
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!-- 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.
|
||||||
|
-->
|
||||||
|
<shape xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:shape="rectangle">
|
||||||
|
<solid android:color="?android:attr/colorPrimaryDark" />
|
||||||
|
<corners
|
||||||
|
android:topLeftRadius="?android:attr/dialogCornerRadius"
|
||||||
|
android:topRightRadius="?android:attr/dialogCornerRadius" />
|
||||||
|
</shape>
|
||||||
@@ -15,9 +15,76 @@
|
|||||||
limitations under the License.
|
limitations under the License.
|
||||||
-->
|
-->
|
||||||
|
|
||||||
<FrameLayout
|
<LinearLayout
|
||||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
android:id="@+id/controls_activity_view"
|
android:id="@+id/control_detail_root"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent" />
|
android:layout_height="match_parent"
|
||||||
|
android:layout_marginTop="@dimen/controls_activity_view_top_offset"
|
||||||
|
android:orientation="vertical">
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:orientation="horizontal"
|
||||||
|
android:layout_marginBottom="10dp">
|
||||||
|
<ImageView
|
||||||
|
android:id="@+id/control_detail_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="48dp"
|
||||||
|
android:layout_height="48dp"
|
||||||
|
android:padding="12dp" />
|
||||||
|
<Space
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_weight="1"
|
||||||
|
android:layout_height="1dp" />
|
||||||
|
<ImageView
|
||||||
|
android:id="@+id/control_detail_open_in_app"
|
||||||
|
android:src="@drawable/ic_open_in_new"
|
||||||
|
android:background="?android:attr/selectableItemBackgroundBorderless"
|
||||||
|
android:tint="@color/control_primary_text"
|
||||||
|
android:layout_width="48dp"
|
||||||
|
android:layout_height="48dp"
|
||||||
|
android:padding="12dp" />
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:paddingTop="@dimen/controls_activity_view_top_padding"
|
||||||
|
android:paddingLeft="@dimen/controls_activity_view_side_padding"
|
||||||
|
android:paddingRight="@dimen/controls_activity_view_side_padding"
|
||||||
|
android:background="@drawable/rounded_bg_top"
|
||||||
|
android:orientation="vertical">
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/title"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:textAppearance="@style/TextAppearance.ControlDialog"
|
||||||
|
android:clickable="false"
|
||||||
|
android:focusable="false"
|
||||||
|
android:maxLines="1"
|
||||||
|
android:ellipsize="end" />
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/subtitle"
|
||||||
|
android:layout_marginTop="6dp"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:textAppearance="@style/TextAppearance.ControlDialog"
|
||||||
|
android:clickable="false"
|
||||||
|
android:focusable="false"
|
||||||
|
android:maxLines="1"
|
||||||
|
android:ellipsize="end" />
|
||||||
|
|
||||||
|
<FrameLayout
|
||||||
|
android:id="@+id/controls_activity_view"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="0dp"
|
||||||
|
android:layout_marginTop="10dp"
|
||||||
|
android:layout_weight="1" />
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
|||||||
@@ -1250,6 +1250,12 @@
|
|||||||
<dimen name="control_base_item_margin">2dp</dimen>
|
<dimen name="control_base_item_margin">2dp</dimen>
|
||||||
<dimen name="control_status_padding">3dp</dimen>
|
<dimen name="control_status_padding">3dp</dimen>
|
||||||
|
|
||||||
|
<!-- Home Controls activity view detail panel-->
|
||||||
|
<dimen name="controls_activity_view_top_padding">25dp</dimen>
|
||||||
|
<dimen name="controls_activity_view_side_padding">12dp</dimen>
|
||||||
|
<dimen name="controls_activity_view_top_offset">200dp</dimen>
|
||||||
|
<dimen name="controls_activity_view_text_size">17sp</dimen>
|
||||||
|
|
||||||
<!-- Home Controls management screens -->
|
<!-- Home Controls management screens -->
|
||||||
<dimen name="controls_management_top_padding">48dp</dimen>
|
<dimen name="controls_management_top_padding">48dp</dimen>
|
||||||
<dimen name="controls_management_side_padding">8dp</dimen>
|
<dimen name="controls_management_side_padding">8dp</dimen>
|
||||||
|
|||||||
@@ -671,6 +671,19 @@
|
|||||||
<item name="android:fontFamily">@*android:string/config_bodyFontFamily</item>
|
<item name="android:fontFamily">@*android:string/config_bodyFontFamily</item>
|
||||||
</style>
|
</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>
|
||||||
|
<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="Control" />
|
<style name="Control" />
|
||||||
|
|
||||||
<style name="Control.MenuItem">
|
<style name="Control.MenuItem">
|
||||||
@@ -713,6 +726,11 @@
|
|||||||
<item name="android:textSize">@dimen/control_text_size</item>
|
<item name="android:textSize">@dimen/control_text_size</item>
|
||||||
<item name="android:textColor">@color/control_secondary_text</item>
|
<item name="android:textColor">@color/control_secondary_text</item>
|
||||||
</style>
|
</style>
|
||||||
|
<style name="TextAppearance.ControlDialog">
|
||||||
|
<item name="android:fontFamily">@*android:string/config_headlineFontFamilyMedium</item>
|
||||||
|
<item name="android:textSize">@dimen/controls_activity_view_text_size</item>
|
||||||
|
<item name="android:textColor">@color/control_primary_text</item>
|
||||||
|
</style>
|
||||||
<style name="Control.ListPopupWindow" parent="@*android:style/Widget.DeviceDefault.ListPopupWindow">
|
<style name="Control.ListPopupWindow" parent="@*android:style/Widget.DeviceDefault.ListPopupWindow">
|
||||||
<item name="android:overlapAnchor">true</item>
|
<item name="android:overlapAnchor">true</item>
|
||||||
|
|
||||||
|
|||||||
@@ -16,19 +16,27 @@
|
|||||||
|
|
||||||
package com.android.systemui.controls.ui
|
package com.android.systemui.controls.ui
|
||||||
|
|
||||||
|
import android.app.Dialog
|
||||||
import android.app.PendingIntent
|
import android.app.PendingIntent
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
import android.provider.Settings
|
import android.service.controls.Control
|
||||||
import android.service.controls.actions.BooleanAction
|
import android.service.controls.actions.BooleanAction
|
||||||
import android.service.controls.actions.CommandAction
|
import android.service.controls.actions.CommandAction
|
||||||
import android.util.Log
|
import android.util.Log
|
||||||
import android.view.HapticFeedbackConstants
|
import android.view.HapticFeedbackConstants
|
||||||
|
|
||||||
|
import com.android.systemui.R
|
||||||
|
|
||||||
object ControlActionCoordinator {
|
object ControlActionCoordinator {
|
||||||
public const val MIN_LEVEL = 0
|
public const val MIN_LEVEL = 0
|
||||||
public const val MAX_LEVEL = 10000
|
public const val MAX_LEVEL = 10000
|
||||||
|
|
||||||
private var useDetailDialog: Boolean? = null
|
private var dialog: Dialog? = null
|
||||||
|
|
||||||
|
fun closeDialog() {
|
||||||
|
dialog?.dismiss()
|
||||||
|
dialog = null
|
||||||
|
}
|
||||||
|
|
||||||
fun toggle(cvh: ControlViewHolder, templateId: String, isChecked: Boolean) {
|
fun toggle(cvh: ControlViewHolder, templateId: String, isChecked: Boolean) {
|
||||||
cvh.action(BooleanAction(templateId, !isChecked))
|
cvh.action(BooleanAction(templateId, !isChecked))
|
||||||
@@ -37,31 +45,39 @@ object ControlActionCoordinator {
|
|||||||
cvh.clipLayer.setLevel(nextLevel)
|
cvh.clipLayer.setLevel(nextLevel)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun touch(cvh: ControlViewHolder, templateId: String) {
|
fun touch(cvh: ControlViewHolder, templateId: String, control: Control) {
|
||||||
cvh.action(CommandAction(templateId))
|
if (cvh.usePanel()) {
|
||||||
|
showDialog(cvh, control.getAppIntent().getIntent())
|
||||||
|
} else {
|
||||||
|
cvh.action(CommandAction(templateId))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Allow apps to specify whether they would like to appear in a detail panel or within
|
||||||
|
* the full activity by setting the {@link Control#EXTRA_USE_PANEL} flag. In order for
|
||||||
|
* activities to determine how they are being launched, they should inspect the
|
||||||
|
* {@link Control#EXTRA_USE_PANEL} flag for a value of true.
|
||||||
|
*/
|
||||||
fun longPress(cvh: ControlViewHolder) {
|
fun longPress(cvh: ControlViewHolder) {
|
||||||
// Long press snould only be called when there is valid control state, otherwise ignore
|
// Long press snould only be called when there is valid control state, otherwise ignore
|
||||||
cvh.cws.control?.let {
|
cvh.cws.control?.let {
|
||||||
if (useDetailDialog == null) {
|
|
||||||
useDetailDialog = Settings.Secure.getInt(cvh.context.getContentResolver(),
|
|
||||||
"systemui.controls_use_detail_panel", 0) != 0
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
it.getAppIntent().send()
|
||||||
cvh.layout.performHapticFeedback(HapticFeedbackConstants.LONG_PRESS)
|
cvh.layout.performHapticFeedback(HapticFeedbackConstants.LONG_PRESS)
|
||||||
if (useDetailDialog!!) {
|
cvh.context.sendBroadcast(Intent(Intent.ACTION_CLOSE_SYSTEM_DIALOGS))
|
||||||
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) {
|
} catch (e: PendingIntent.CanceledException) {
|
||||||
Log.e(ControlsUiController.TAG, "Error sending pending intent", e)
|
Log.e(ControlsUiController.TAG, "Error sending pending intent", e)
|
||||||
cvh.setTransientStatus("Error opening application")
|
cvh.setTransientStatus(
|
||||||
|
cvh.context.resources.getString(R.string.controls_error_failed))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun showDialog(cvh: ControlViewHolder, intent: Intent) {
|
||||||
|
dialog = DetailDialog(cvh, intent).also {
|
||||||
|
it.setOnDismissListener { _ -> dialog = null }
|
||||||
|
it.show()
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -39,16 +39,29 @@ import com.android.systemui.R
|
|||||||
|
|
||||||
import kotlin.reflect.KClass
|
import kotlin.reflect.KClass
|
||||||
|
|
||||||
private const val UPDATE_DELAY_IN_MILLIS = 3000L
|
/**
|
||||||
private const val ALPHA_ENABLED = (255.0 * 0.2).toInt()
|
* Wraps the widgets that make up the UI representation of a {@link Control}. Updates to the view
|
||||||
private const val ALPHA_DISABLED = 255
|
* are signaled via calls to {@link #bindData}. Similar to the ViewHolder concept used in
|
||||||
|
* RecyclerViews.
|
||||||
|
*/
|
||||||
class ControlViewHolder(
|
class ControlViewHolder(
|
||||||
val layout: ViewGroup,
|
val layout: ViewGroup,
|
||||||
val controlsController: ControlsController,
|
val controlsController: ControlsController,
|
||||||
val uiExecutor: DelayableExecutor,
|
val uiExecutor: DelayableExecutor,
|
||||||
val bgExecutor: DelayableExecutor
|
val bgExecutor: DelayableExecutor,
|
||||||
|
val usePanels: Boolean
|
||||||
) {
|
) {
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
private const val UPDATE_DELAY_IN_MILLIS = 3000L
|
||||||
|
private const val ALPHA_ENABLED = (255.0 * 0.2).toInt()
|
||||||
|
private const val ALPHA_DISABLED = 255
|
||||||
|
private val FORCE_PANEL_DEVICES = setOf(
|
||||||
|
DeviceTypes.TYPE_THERMOSTAT,
|
||||||
|
DeviceTypes.TYPE_CAMERA
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
val icon: ImageView = layout.requireViewById(R.id.icon)
|
val icon: ImageView = layout.requireViewById(R.id.icon)
|
||||||
val status: TextView = layout.requireViewById(R.id.status)
|
val status: TextView = layout.requireViewById(R.id.status)
|
||||||
val title: TextView = layout.requireViewById(R.id.title)
|
val title: TextView = layout.requireViewById(R.id.title)
|
||||||
@@ -59,6 +72,8 @@ class ControlViewHolder(
|
|||||||
var cancelUpdate: Runnable? = null
|
var cancelUpdate: Runnable? = null
|
||||||
var behavior: Behavior? = null
|
var behavior: Behavior? = null
|
||||||
var lastAction: ControlAction? = null
|
var lastAction: ControlAction? = null
|
||||||
|
val deviceType: Int
|
||||||
|
get() = cws.control?.let { it.getDeviceType() } ?: cws.ci.deviceType
|
||||||
|
|
||||||
init {
|
init {
|
||||||
val ld = layout.getBackground() as LayerDrawable
|
val ld = layout.getBackground() as LayerDrawable
|
||||||
@@ -76,7 +91,7 @@ class ControlViewHolder(
|
|||||||
val (controlStatus, template) = cws.control?.let {
|
val (controlStatus, template) = cws.control?.let {
|
||||||
title.setText(it.getTitle())
|
title.setText(it.getTitle())
|
||||||
subtitle.setText(it.getSubtitle())
|
subtitle.setText(it.getSubtitle())
|
||||||
Pair(it.getStatus(), it.getControlTemplate())
|
Pair(it.status, it.controlTemplate)
|
||||||
} ?: run {
|
} ?: run {
|
||||||
title.setText(cws.ci.controlTitle)
|
title.setText(cws.ci.controlTitle)
|
||||||
subtitle.setText(cws.ci.controlSubtitle)
|
subtitle.setText(cws.ci.controlSubtitle)
|
||||||
@@ -91,7 +106,7 @@ class ControlViewHolder(
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
val clazz = findBehavior(controlStatus, template)
|
val clazz = findBehavior(controlStatus, template, deviceType)
|
||||||
if (behavior == null || behavior!!::class != clazz) {
|
if (behavior == null || behavior!!::class != clazz) {
|
||||||
// Behavior changes can signal a change in template from the app or
|
// Behavior changes can signal a change in template from the app or
|
||||||
// first time setup
|
// first time setup
|
||||||
@@ -126,9 +141,17 @@ class ControlViewHolder(
|
|||||||
controlsController.action(cws.componentName, cws.ci, action)
|
controlsController.action(cws.componentName, cws.ci, action)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun findBehavior(status: Int, template: ControlTemplate): KClass<out Behavior> {
|
fun usePanel(): Boolean =
|
||||||
|
usePanels && deviceType in ControlViewHolder.FORCE_PANEL_DEVICES
|
||||||
|
|
||||||
|
private fun findBehavior(
|
||||||
|
status: Int,
|
||||||
|
template: ControlTemplate,
|
||||||
|
deviceType: Int
|
||||||
|
): KClass<out Behavior> {
|
||||||
return when {
|
return when {
|
||||||
status == Control.STATUS_UNKNOWN -> UnknownBehavior::class
|
status == Control.STATUS_UNKNOWN -> UnknownBehavior::class
|
||||||
|
deviceType == DeviceTypes.TYPE_CAMERA -> TouchBehavior::class
|
||||||
template is ToggleTemplate -> ToggleBehavior::class
|
template is ToggleTemplate -> ToggleBehavior::class
|
||||||
template is StatelessTemplate -> TouchBehavior::class
|
template is StatelessTemplate -> TouchBehavior::class
|
||||||
template is ToggleRangeTemplate -> ToggleRangeBehavior::class
|
template is ToggleRangeTemplate -> ToggleRangeBehavior::class
|
||||||
@@ -140,7 +163,6 @@ class ControlViewHolder(
|
|||||||
internal fun applyRenderInfo(enabled: Boolean, offset: Int = 0) {
|
internal fun applyRenderInfo(enabled: Boolean, offset: Int = 0) {
|
||||||
setEnabled(enabled)
|
setEnabled(enabled)
|
||||||
|
|
||||||
val deviceType = cws.control?.let { it.getDeviceType() } ?: cws.ci.deviceType
|
|
||||||
val ri = RenderInfo.lookup(context, cws.componentName, deviceType, enabled, offset)
|
val ri = RenderInfo.lookup(context, cws.componentName, deviceType, enabled, offset)
|
||||||
|
|
||||||
val fg = context.getResources().getColorStateList(ri.foreground, context.getTheme())
|
val fg = context.getResources().getColorStateList(ri.foreground, context.getTheme())
|
||||||
|
|||||||
@@ -30,6 +30,7 @@ import android.content.res.Configuration
|
|||||||
import android.graphics.drawable.Drawable
|
import android.graphics.drawable.Drawable
|
||||||
import android.graphics.drawable.LayerDrawable
|
import android.graphics.drawable.LayerDrawable
|
||||||
import android.os.Process
|
import android.os.Process
|
||||||
|
import android.provider.Settings
|
||||||
import android.service.controls.Control
|
import android.service.controls.Control
|
||||||
import android.service.controls.actions.ControlAction
|
import android.service.controls.actions.ControlAction
|
||||||
import android.util.TypedValue
|
import android.util.TypedValue
|
||||||
@@ -85,6 +86,7 @@ class ControlsUiControllerImpl @Inject constructor (
|
|||||||
private const val PREF_COMPONENT = "controls_component"
|
private const val PREF_COMPONENT = "controls_component"
|
||||||
private const val PREF_STRUCTURE = "controls_structure"
|
private const val PREF_STRUCTURE = "controls_structure"
|
||||||
|
|
||||||
|
private const val USE_PANELS = "systemui.controls_use_panel"
|
||||||
private const val FADE_IN_MILLIS = 225L
|
private const val FADE_IN_MILLIS = 225L
|
||||||
|
|
||||||
private val EMPTY_COMPONENT = ComponentName("", "")
|
private val EMPTY_COMPONENT = ComponentName("", "")
|
||||||
@@ -407,6 +409,9 @@ class ControlsUiControllerImpl @Inject constructor (
|
|||||||
|
|
||||||
val maxColumns = findMaxColumns()
|
val maxColumns = findMaxColumns()
|
||||||
|
|
||||||
|
// use flag only temporarily for testing
|
||||||
|
val usePanels = Settings.Secure.getInt(context.contentResolver, USE_PANELS, 0) == 1
|
||||||
|
|
||||||
val listView = parent.requireViewById(R.id.global_actions_controls_list) as ViewGroup
|
val listView = parent.requireViewById(R.id.global_actions_controls_list) as ViewGroup
|
||||||
var lastRow: ViewGroup = createRow(inflater, listView)
|
var lastRow: ViewGroup = createRow(inflater, listView)
|
||||||
selectedStructure.controls.forEach {
|
selectedStructure.controls.forEach {
|
||||||
@@ -420,7 +425,8 @@ class ControlsUiControllerImpl @Inject constructor (
|
|||||||
baseLayout,
|
baseLayout,
|
||||||
controlsController.get(),
|
controlsController.get(),
|
||||||
uiExecutor,
|
uiExecutor,
|
||||||
bgExecutor
|
bgExecutor,
|
||||||
|
usePanels
|
||||||
)
|
)
|
||||||
val key = ControlKey(selectedStructure.componentName, it.controlId)
|
val key = ControlKey(selectedStructure.componentName, it.controlId)
|
||||||
cvh.bindData(controlsById.getValue(key))
|
cvh.bindData(controlsById.getValue(key))
|
||||||
@@ -500,6 +506,7 @@ class ControlsUiControllerImpl @Inject constructor (
|
|||||||
hidden = true
|
hidden = true
|
||||||
popup?.dismiss()
|
popup?.dismiss()
|
||||||
activeDialog?.dismiss()
|
activeDialog?.dismiss()
|
||||||
|
ControlActionCoordinator.closeDialog()
|
||||||
|
|
||||||
controlsController.get().unsubscribe()
|
controlsController.get().unsubscribe()
|
||||||
|
|
||||||
|
|||||||
@@ -17,18 +17,16 @@
|
|||||||
package com.android.systemui.controls.ui
|
package com.android.systemui.controls.ui
|
||||||
|
|
||||||
import android.app.ActivityView
|
import android.app.ActivityView
|
||||||
import android.app.ActivityOptions
|
|
||||||
import android.app.Dialog
|
import android.app.Dialog
|
||||||
import android.app.PendingIntent
|
|
||||||
import android.content.ComponentName
|
import android.content.ComponentName
|
||||||
import android.content.Context
|
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
|
import android.provider.Settings
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import android.view.ViewGroup
|
import android.view.ViewGroup
|
||||||
import android.view.ViewGroup.LayoutParams.MATCH_PARENT
|
import android.view.WindowInsets
|
||||||
import android.view.Window
|
|
||||||
import android.view.WindowManager
|
import android.view.WindowManager
|
||||||
import android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS
|
import android.widget.ImageView
|
||||||
|
import android.widget.TextView
|
||||||
|
|
||||||
import com.android.systemui.R
|
import com.android.systemui.R
|
||||||
|
|
||||||
@@ -38,20 +36,26 @@ import com.android.systemui.R
|
|||||||
* The activity being launched is specified by {@link android.service.controls.Control#getAppIntent}.
|
* The activity being launched is specified by {@link android.service.controls.Control#getAppIntent}.
|
||||||
*/
|
*/
|
||||||
class DetailDialog(
|
class DetailDialog(
|
||||||
val parentContext: Context,
|
val cvh: ControlViewHolder,
|
||||||
val intent: PendingIntent
|
val intent: Intent
|
||||||
) : Dialog(parentContext) {
|
) : Dialog(cvh.context, R.style.Theme_SystemUI_Dialog_Control_DetailPanel) {
|
||||||
|
|
||||||
var activityView: ActivityView
|
companion object {
|
||||||
|
private const val ALPHA = (0.8f * 255).toInt()
|
||||||
|
private const val PANEL_TOP_OFFSET = "systemui.controls_panel_top_offset"
|
||||||
|
}
|
||||||
|
|
||||||
|
lateinit var activityView: ActivityView
|
||||||
|
|
||||||
val stateCallback: ActivityView.StateCallback = object : ActivityView.StateCallback() {
|
val stateCallback: ActivityView.StateCallback = object : ActivityView.StateCallback() {
|
||||||
override fun onActivityViewReady(view: ActivityView) {
|
override fun onActivityViewReady(view: ActivityView) {
|
||||||
val fillInIntent = Intent()
|
val launchIntent = Intent(intent)
|
||||||
|
|
||||||
// Apply flags to make behaviour match documentLaunchMode=always.
|
// Apply flags to make behaviour match documentLaunchMode=always.
|
||||||
fillInIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
|
launchIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
|
||||||
fillInIntent.addFlags(Intent.FLAG_ACTIVITY_MULTIPLE_TASK)
|
launchIntent.addFlags(Intent.FLAG_ACTIVITY_MULTIPLE_TASK)
|
||||||
view.startActivity(intent, fillInIntent, ActivityOptions.makeBasic())
|
|
||||||
|
view.startActivity(launchIntent)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onActivityViewDestroyed(view: ActivityView) {}
|
override fun onActivityViewDestroyed(view: ActivityView) {}
|
||||||
@@ -61,28 +65,8 @@ class DetailDialog(
|
|||||||
override fun onTaskRemovalStarted(taskId: Int) {}
|
override fun onTaskRemovalStarted(taskId: Int) {}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Suppress("DEPRECATION")
|
|
||||||
private fun Window.setWindowParams() {
|
|
||||||
requestFeature(Window.FEATURE_NO_TITLE)
|
|
||||||
|
|
||||||
// Inflate the decor view, so the attributes below are not overwritten by the theme.
|
|
||||||
decorView
|
|
||||||
attributes.systemUiVisibility =
|
|
||||||
(attributes.systemUiVisibility
|
|
||||||
or View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
|
|
||||||
or View.SYSTEM_UI_FLAG_LAYOUT_STABLE)
|
|
||||||
|
|
||||||
setLayout(MATCH_PARENT, MATCH_PARENT)
|
|
||||||
clearFlags(WindowManager.LayoutParams.FLAG_DIM_BEHIND)
|
|
||||||
addFlags(WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN
|
|
||||||
or WindowManager.LayoutParams.FLAG_LAYOUT_INSET_DECOR
|
|
||||||
or WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED)
|
|
||||||
setType(WindowManager.LayoutParams.TYPE_VOLUME_OVERLAY)
|
|
||||||
getAttributes().setFitInsetsTypes(0 /* types */)
|
|
||||||
}
|
|
||||||
|
|
||||||
init {
|
init {
|
||||||
getWindow()?.setWindowParams()
|
window.setType(WindowManager.LayoutParams.TYPE_VOLUME_OVERLAY)
|
||||||
|
|
||||||
setContentView(R.layout.controls_detail_dialog)
|
setContentView(R.layout.controls_detail_dialog)
|
||||||
|
|
||||||
@@ -90,19 +74,61 @@ class DetailDialog(
|
|||||||
requireViewById<ViewGroup>(R.id.controls_activity_view).apply {
|
requireViewById<ViewGroup>(R.id.controls_activity_view).apply {
|
||||||
addView(activityView)
|
addView(activityView)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
requireViewById<ImageView>(R.id.control_detail_close).apply {
|
||||||
|
setOnClickListener { _: View -> dismiss() }
|
||||||
|
}
|
||||||
|
|
||||||
|
requireViewById<TextView>(R.id.title).apply {
|
||||||
|
setText(cvh.title.text)
|
||||||
|
}
|
||||||
|
|
||||||
|
requireViewById<TextView>(R.id.subtitle).apply {
|
||||||
|
setText(cvh.subtitle.text)
|
||||||
|
}
|
||||||
|
|
||||||
|
requireViewById<ImageView>(R.id.control_detail_open_in_app).apply {
|
||||||
|
setOnClickListener { v: View ->
|
||||||
|
dismiss()
|
||||||
|
context.sendBroadcast(Intent(Intent.ACTION_CLOSE_SYSTEM_DIALOGS))
|
||||||
|
v.context.startActivity(intent)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// consume all insets to achieve slide under effect
|
||||||
|
window.getDecorView().setOnApplyWindowInsetsListener {
|
||||||
|
v: View, insets: WindowInsets ->
|
||||||
|
activityView.apply {
|
||||||
|
val l = getPaddingLeft()
|
||||||
|
val t = getPaddingTop()
|
||||||
|
val r = getPaddingRight()
|
||||||
|
setPadding(l, t, r, insets.getSystemWindowInsets().bottom)
|
||||||
|
}
|
||||||
|
|
||||||
|
insets.consumeSystemWindowInsets()
|
||||||
|
}
|
||||||
|
|
||||||
|
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)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun show() {
|
override fun show() {
|
||||||
val attrs = getWindow()?.attributes
|
|
||||||
attrs?.layoutInDisplayCutoutMode = LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS
|
|
||||||
getWindow()?.attributes = attrs
|
|
||||||
|
|
||||||
activityView.setCallback(stateCallback)
|
activityView.setCallback(stateCallback)
|
||||||
|
|
||||||
super.show()
|
super.show()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun dismiss() {
|
override fun dismiss() {
|
||||||
|
if (!isShowing()) return
|
||||||
activityView.release()
|
activityView.release()
|
||||||
|
|
||||||
super.dismiss()
|
super.dismiss()
|
||||||
|
|||||||
@@ -33,6 +33,10 @@ class TemperatureControlBehavior : Behavior {
|
|||||||
|
|
||||||
override fun initialize(cvh: ControlViewHolder) {
|
override fun initialize(cvh: ControlViewHolder) {
|
||||||
this.cvh = cvh
|
this.cvh = cvh
|
||||||
|
|
||||||
|
cvh.layout.setOnClickListener { _ ->
|
||||||
|
ControlActionCoordinator.touch(cvh, template.getTemplateId(), control)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun bind(cws: ControlWithState) {
|
override fun bind(cws: ControlWithState) {
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ import android.graphics.drawable.Drawable
|
|||||||
import android.graphics.drawable.LayerDrawable
|
import android.graphics.drawable.LayerDrawable
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import android.service.controls.Control
|
import android.service.controls.Control
|
||||||
import android.service.controls.templates.StatelessTemplate
|
import android.service.controls.templates.ControlTemplate
|
||||||
|
|
||||||
import com.android.systemui.R
|
import com.android.systemui.R
|
||||||
import com.android.systemui.controls.ui.ControlActionCoordinator.MIN_LEVEL
|
import com.android.systemui.controls.ui.ControlActionCoordinator.MIN_LEVEL
|
||||||
@@ -31,7 +31,7 @@ import com.android.systemui.controls.ui.ControlActionCoordinator.MIN_LEVEL
|
|||||||
*/
|
*/
|
||||||
class TouchBehavior : Behavior {
|
class TouchBehavior : Behavior {
|
||||||
lateinit var clipLayer: Drawable
|
lateinit var clipLayer: Drawable
|
||||||
lateinit var template: StatelessTemplate
|
lateinit var template: ControlTemplate
|
||||||
lateinit var control: Control
|
lateinit var control: Control
|
||||||
lateinit var cvh: ControlViewHolder
|
lateinit var cvh: ControlViewHolder
|
||||||
|
|
||||||
@@ -40,14 +40,14 @@ class TouchBehavior : Behavior {
|
|||||||
cvh.applyRenderInfo(false)
|
cvh.applyRenderInfo(false)
|
||||||
|
|
||||||
cvh.layout.setOnClickListener(View.OnClickListener() {
|
cvh.layout.setOnClickListener(View.OnClickListener() {
|
||||||
ControlActionCoordinator.touch(cvh, template.getTemplateId())
|
ControlActionCoordinator.touch(cvh, template.getTemplateId(), control)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun bind(cws: ControlWithState) {
|
override fun bind(cws: ControlWithState) {
|
||||||
this.control = cws.control!!
|
this.control = cws.control!!
|
||||||
cvh.status.setText(control.getStatusText())
|
cvh.status.setText(control.getStatusText())
|
||||||
template = control.getControlTemplate() as StatelessTemplate
|
template = control.getControlTemplate()
|
||||||
|
|
||||||
val ld = cvh.layout.getBackground() as LayerDrawable
|
val ld = cvh.layout.getBackground() as LayerDrawable
|
||||||
clipLayer = ld.findDrawableByLayerId(R.id.clip_layer)
|
clipLayer = ld.findDrawableByLayerId(R.id.clip_layer)
|
||||||
|
|||||||
Reference in New Issue
Block a user