summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--packages/SystemUI/res/values/styles.xml1
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/policy/DevicePostureControllerExt.kt33
-rw-r--r--packages/SystemUI/src/com/android/systemui/volume/dialog/NewVolumeDialog.kt34
-rw-r--r--packages/SystemUI/src/com/android/systemui/volume/dialog/NewVolumeDialogPlugin.kt67
-rw-r--r--packages/SystemUI/src/com/android/systemui/volume/dialog/VolumeDialog.kt7
-rw-r--r--packages/SystemUI/src/com/android/systemui/volume/dialog/ui/binder/VolumeDialogBinder.kt86
-rw-r--r--packages/SystemUI/src/com/android/systemui/volume/dialog/ui/viewmodel/VolumeDialogGravityViewModel.kt92
7 files changed, 217 insertions, 103 deletions
diff --git a/packages/SystemUI/res/values/styles.xml b/packages/SystemUI/res/values/styles.xml
index 1c09f84b6fd0..94b0b5f67934 100644
--- a/packages/SystemUI/res/values/styles.xml
+++ b/packages/SystemUI/res/values/styles.xml
@@ -540,6 +540,7 @@
<!-- Overridden by values-television/styles.xml with tv-specific settings -->
<style name="volume_dialog_theme" parent="Theme.SystemUI">
<item name="android:windowIsFloating">true</item>
+ <item name="android:showWhenLocked">true</item>
</style>
<style name="Theme.SystemUI.DayNightDialog" parent="@android:style/Theme.DeviceDefault.Light.Dialog"/>
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/DevicePostureControllerExt.kt b/packages/SystemUI/src/com/android/systemui/statusbar/policy/DevicePostureControllerExt.kt
new file mode 100644
index 000000000000..441cbb33f821
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/DevicePostureControllerExt.kt
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2024 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.statusbar.policy
+
+import com.android.systemui.statusbar.policy.DevicePostureController.DevicePostureInt
+import com.android.systemui.utils.coroutines.flow.conflatedCallbackFlow
+import kotlinx.coroutines.channels.awaitClose
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.onStart
+
+/** [DevicePostureController.getDevicePosture] as a [Flow]. */
+@DevicePostureInt
+fun DevicePostureController.devicePosture(): Flow<Int> =
+ conflatedCallbackFlow {
+ val callback = DevicePostureController.Callback { posture -> trySend(posture) }
+ addCallback(callback)
+ awaitClose { removeCallback(callback) }
+ }
+ .onStart { emit(devicePosture) }
diff --git a/packages/SystemUI/src/com/android/systemui/volume/dialog/NewVolumeDialog.kt b/packages/SystemUI/src/com/android/systemui/volume/dialog/NewVolumeDialog.kt
deleted file mode 100644
index 869b3c65d70e..000000000000
--- a/packages/SystemUI/src/com/android/systemui/volume/dialog/NewVolumeDialog.kt
+++ /dev/null
@@ -1,34 +0,0 @@
-/*
- * Copyright (C) 2024 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.volume.dialog
-
-import android.app.Dialog
-import android.content.Context
-import android.os.Bundle
-import android.view.ContextThemeWrapper
-import com.android.systemui.dagger.qualifiers.Application
-import com.android.systemui.res.R
-import javax.inject.Inject
-
-class NewVolumeDialog @Inject constructor(@Application context: Context) :
- Dialog(ContextThemeWrapper(context, R.style.volume_dialog_theme)) {
-
- override fun onCreate(savedInstanceState: Bundle?) {
- super.onCreate(savedInstanceState)
- setContentView(R.layout.volume_dialog)
- }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/volume/dialog/NewVolumeDialogPlugin.kt b/packages/SystemUI/src/com/android/systemui/volume/dialog/NewVolumeDialogPlugin.kt
deleted file mode 100644
index b93714ae4fd4..000000000000
--- a/packages/SystemUI/src/com/android/systemui/volume/dialog/NewVolumeDialogPlugin.kt
+++ /dev/null
@@ -1,67 +0,0 @@
-/*
- * Copyright (C) 2024 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.volume.dialog
-
-import com.android.systemui.dagger.qualifiers.Application
-import com.android.systemui.plugins.VolumeDialog
-import com.android.systemui.volume.dialog.dagger.VolumeDialogComponent
-import com.android.systemui.volume.dialog.dagger.VolumeDialogPluginComponent
-import javax.inject.Inject
-import kotlinx.coroutines.CoroutineScope
-import kotlinx.coroutines.Job
-import kotlinx.coroutines.cancel
-import kotlinx.coroutines.coroutineScope
-import kotlinx.coroutines.launch
-
-class NewVolumeDialogPlugin
-@Inject
-constructor(
- @Application private val applicationCoroutineScope: CoroutineScope,
- private val volumeDialogPluginComponentFactory: VolumeDialogPluginComponent.Factory,
-) : VolumeDialog {
-
- private var volumeDialogPluginComponent: VolumeDialogPluginComponent? = null
- private var job: Job? = null
-
- override fun init(windowType: Int, callback: VolumeDialog.Callback?) {
- job =
- applicationCoroutineScope.launch {
- coroutineScope {
- volumeDialogPluginComponent = volumeDialogPluginComponentFactory.create(this)
- }
- }
- }
-
- private fun showDialog() {
- val volumeDialogPluginComponent =
- volumeDialogPluginComponent ?: error("Creating dialog before init was called")
- volumeDialogPluginComponent.coroutineScope().launch {
- coroutineScope {
- val volumeDialogComponent: VolumeDialogComponent =
- volumeDialogPluginComponent.volumeDialogComponentFactory().create(this)
- with(volumeDialogComponent.volumeDialog()) {
- setOnDismissListener { volumeDialogComponent.coroutineScope().cancel() }
- show()
- }
- }
- }
- }
-
- override fun destroy() {
- job?.cancel()
- }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/volume/dialog/VolumeDialog.kt b/packages/SystemUI/src/com/android/systemui/volume/dialog/VolumeDialog.kt
index 74e823e9f592..aa73dbd555d7 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/dialog/VolumeDialog.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/dialog/VolumeDialog.kt
@@ -22,13 +22,16 @@ import android.os.Bundle
import android.view.ContextThemeWrapper
import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.res.R
+import com.android.systemui.volume.dialog.ui.binder.VolumeDialogBinder
import javax.inject.Inject
-class VolumeDialog @Inject constructor(@Application context: Context) :
+class VolumeDialog
+@Inject
+constructor(@Application context: Context, private val dialogBinder: VolumeDialogBinder) :
Dialog(ContextThemeWrapper(context, R.style.volume_dialog_theme)) {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
- setContentView(R.layout.volume_dialog)
+ dialogBinder.bind(this)
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/volume/dialog/ui/binder/VolumeDialogBinder.kt b/packages/SystemUI/src/com/android/systemui/volume/dialog/ui/binder/VolumeDialogBinder.kt
new file mode 100644
index 000000000000..3f2c39bba6e7
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/volume/dialog/ui/binder/VolumeDialogBinder.kt
@@ -0,0 +1,86 @@
+/*
+ * Copyright (C) 2024 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.volume.dialog.ui.binder
+
+import android.app.Dialog
+import android.graphics.Color
+import android.graphics.PixelFormat
+import android.graphics.drawable.ColorDrawable
+import android.view.View
+import android.view.ViewGroup
+import android.view.Window
+import android.view.WindowManager
+import androidx.lifecycle.lifecycleScope
+import com.android.systemui.lifecycle.repeatWhenAttached
+import com.android.systemui.res.R
+import com.android.systemui.volume.dialog.dagger.scope.VolumeDialog
+import com.android.systemui.volume.dialog.dagger.scope.VolumeDialogScope
+import com.android.systemui.volume.dialog.ui.viewmodel.VolumeDialogGravityViewModel
+import javax.inject.Inject
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.flow.launchIn
+import kotlinx.coroutines.flow.onEach
+import kotlinx.coroutines.launch
+
+@VolumeDialogScope
+class VolumeDialogBinder
+@Inject
+constructor(
+ @VolumeDialog private val coroutineScope: CoroutineScope,
+ private val volumeDialogViewBinder: VolumeDialogViewBinder,
+ private val gravityViewModel: VolumeDialogGravityViewModel,
+) {
+
+ fun bind(dialog: Dialog) {
+ with(dialog) {
+ setupWindow(window!!)
+ dialog.setContentView(R.layout.volume_dialog)
+
+ val volumeDialogView: View = dialog.requireViewById(R.id.volume_dialog_container)
+ volumeDialogView.repeatWhenAttached {
+ lifecycleScope.launch { volumeDialogViewBinder.bind(volumeDialogView) }
+ }
+ }
+ }
+
+ /** Configures [Window] for the [Dialog]. */
+ private fun setupWindow(window: Window) =
+ with(window) {
+ clearFlags(WindowManager.LayoutParams.FLAG_DIM_BEHIND)
+ addFlags(
+ WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE or
+ WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL or
+ WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH or
+ WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED
+ )
+ addPrivateFlags(WindowManager.LayoutParams.PRIVATE_FLAG_TRUSTED_OVERLAY)
+
+ requestFeature(Window.FEATURE_NO_TITLE)
+ setBackgroundDrawable(ColorDrawable(Color.TRANSPARENT))
+ setType(WindowManager.LayoutParams.TYPE_VOLUME_OVERLAY)
+ setWindowAnimations(-1)
+ setFormat(PixelFormat.TRANSLUCENT)
+
+ attributes =
+ attributes.apply {
+ title = "VolumeDialog" // Not the same as Window#setTitle
+ }
+ setLayout(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT)
+
+ gravityViewModel.dialogGravity.onEach { window.setGravity(it) }.launchIn(coroutineScope)
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/volume/dialog/ui/viewmodel/VolumeDialogGravityViewModel.kt b/packages/SystemUI/src/com/android/systemui/volume/dialog/ui/viewmodel/VolumeDialogGravityViewModel.kt
new file mode 100644
index 000000000000..df6523c9d750
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/volume/dialog/ui/viewmodel/VolumeDialogGravityViewModel.kt
@@ -0,0 +1,92 @@
+/*
+ * Copyright (C) 2024 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.volume.dialog.ui.viewmodel
+
+import android.content.Context
+import android.content.res.Configuration
+import android.view.Gravity
+import androidx.annotation.GravityInt
+import com.android.systemui.dagger.qualifiers.Application
+import com.android.systemui.dagger.qualifiers.UiBackground
+import com.android.systemui.res.R
+import com.android.systemui.statusbar.policy.ConfigurationController
+import com.android.systemui.statusbar.policy.DevicePostureController
+import com.android.systemui.statusbar.policy.devicePosture
+import com.android.systemui.statusbar.policy.onConfigChanged
+import com.android.systemui.volume.dialog.dagger.scope.VolumeDialog
+import com.android.systemui.volume.dialog.dagger.scope.VolumeDialogScope
+import javax.inject.Inject
+import kotlin.coroutines.CoroutineContext
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.SharingStarted
+import kotlinx.coroutines.flow.combine
+import kotlinx.coroutines.flow.onEach
+import kotlinx.coroutines.flow.stateIn
+import kotlinx.coroutines.withContext
+
+@VolumeDialogScope
+class VolumeDialogGravityViewModel
+@Inject
+constructor(
+ @Application private val context: Context,
+ @VolumeDialog private val coroutineScope: CoroutineScope,
+ @UiBackground private val uiBackgroundCoroutineContext: CoroutineContext,
+ configurationController: ConfigurationController,
+ private val devicePostureController: DevicePostureController,
+) {
+
+ @GravityInt private var originalGravity: Int = context.getAbsoluteGravity()
+
+ val dialogGravity: Flow<Int> =
+ combine(
+ devicePostureController.devicePosture(),
+ configurationController.onConfigChanged.onEach { onConfigurationChanged() },
+ ) { devicePosture, configuration ->
+ context.calculateGravity(devicePosture, configuration)
+ }
+ .stateIn(
+ scope = coroutineScope,
+ started = SharingStarted.Eagerly,
+ context.calculateGravity(),
+ )
+
+ private suspend fun onConfigurationChanged() {
+ withContext(uiBackgroundCoroutineContext) { originalGravity = context.getAbsoluteGravity() }
+ }
+
+ @GravityInt
+ private fun Context.calculateGravity(
+ devicePosture: Int = devicePostureController.devicePosture,
+ config: Configuration = resources.configuration,
+ ): Int {
+ val isLandscape = config.orientation == Configuration.ORIENTATION_LANDSCAPE
+ val isHalfOpen = devicePosture == DevicePostureController.DEVICE_POSTURE_HALF_OPENED
+ val gravity =
+ if (isLandscape && isHalfOpen) {
+ originalGravity or Gravity.TOP
+ } else {
+ originalGravity
+ }
+ return getAbsoluteGravity(gravity)
+ }
+}
+
+@GravityInt
+private fun Context.getAbsoluteGravity(
+ gravity: Int = resources.getInteger(R.integer.volume_dialog_gravity)
+): Int = with(resources) { Gravity.getAbsoluteGravity(gravity, configuration.layoutDirection) }