summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author chelseahao <chelseahao@google.com> 2023-09-26 18:26:37 +0800
committer Chelsea Hao <chelseahao@google.com> 2023-10-02 04:16:53 +0000
commitb1a3482ea1d1a97814a41e69dab92bb4557c66da (patch)
tree77635335fbab7a026cdb88733094dc56d7dcc7c9
parent3526aabf0ed026937e3e75a45971b49ee2e5a47c (diff)
Resolved some TODOs.
Used `AsyncListDiff` to update device list and removed unused root layout. Also used a new background to replace alpha and fixed a subtitle bug. Test: atest -c com.android.systemui.qs.tiles.dialog.bluetooth Bug: b/298124674 b/299400510 Change-Id: Iee413b9f53125f4ed9a4bedbf397a4d735426f3a
-rw-r--r--packages/SystemUI/res/drawable/bluetooth_tile_dialog_bg_off.xml26
-rw-r--r--packages/SystemUI/res/drawable/bluetooth_tile_dialog_bg_off_busy.xml25
-rw-r--r--packages/SystemUI/res/layout/bluetooth_device_item.xml133
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/bluetooth/BluetoothStateInteractor.kt2
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/bluetooth/BluetoothTileDialog.kt85
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/bluetooth/BluetoothTileDialogViewModel.kt43
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/bluetooth/DeviceItem.kt6
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/bluetooth/DeviceItemFactory.kt15
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/bluetooth/DeviceItemInteractor.kt21
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/bluetooth/BluetoothTileDialogTest.kt60
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/bluetooth/BluetoothTileDialogViewModelTest.kt11
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/bluetooth/DeviceItemFactoryTest.kt2
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/bluetooth/DeviceItemInteractorTest.kt20
13 files changed, 270 insertions, 179 deletions
diff --git a/packages/SystemUI/res/drawable/bluetooth_tile_dialog_bg_off.xml b/packages/SystemUI/res/drawable/bluetooth_tile_dialog_bg_off.xml
new file mode 100644
index 000000000000..d4916b47158c
--- /dev/null
+++ b/packages/SystemUI/res/drawable/bluetooth_tile_dialog_bg_off.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2023 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.
+ -->
+
+<ripple xmlns:android="http://schemas.android.com/apk/res/android"
+ android:color="?android:attr/colorControlHighlight">
+ <item android:id="@android:id/mask">
+ <shape android:shape="rectangle">
+ <solid android:color="@android:color/white"/>
+ <corners android:radius="@dimen/settingslib_switch_bar_radius"/>
+ </shape>
+ </item>
+</ripple> \ No newline at end of file
diff --git a/packages/SystemUI/res/drawable/bluetooth_tile_dialog_bg_off_busy.xml b/packages/SystemUI/res/drawable/bluetooth_tile_dialog_bg_off_busy.xml
new file mode 100644
index 000000000000..7bc120ee8aad
--- /dev/null
+++ b/packages/SystemUI/res/drawable/bluetooth_tile_dialog_bg_off_busy.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2023 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.
+ -->
+
+<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
+ <item android:id="@android:id/background">
+ <shape>
+ <solid android:color="?android:attr/colorControlHighlight" />
+ <corners android:radius="@dimen/settingslib_switch_bar_radius"/>
+ </shape>
+ </item>
+</layer-list> \ No newline at end of file
diff --git a/packages/SystemUI/res/layout/bluetooth_device_item.xml b/packages/SystemUI/res/layout/bluetooth_device_item.xml
index 6dd44fbe645d..1c7e9977afe5 100644
--- a/packages/SystemUI/res/layout/bluetooth_device_item.xml
+++ b/packages/SystemUI/res/layout/bluetooth_device_item.xml
@@ -14,81 +14,74 @@
~ limitations under the License.
-->
-<!-- TODO(b/298124674) remove this root -->
-<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+<androidx.constraintlayout.widget.ConstraintLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
- android:id="@+id/bluetooth_device_list_container"
+ android:id="@+id/bluetooth_device_row"
+ style="@style/BluetoothTileDialog.Device"
android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:orientation="vertical"
+ android:layout_height="@dimen/bluetooth_dialog_device_height"
+ android:paddingEnd="24dp"
+ android:paddingStart="20dp"
android:layout_marginBottom="4dp">
- <androidx.constraintlayout.widget.ConstraintLayout
- android:id="@+id/bluetooth_device_row"
- style="@style/BluetoothTileDialog.Device"
- android:layout_height="@dimen/bluetooth_dialog_device_height"
- android:paddingEnd="24dp"
- android:paddingStart="20dp"
- android:baselineAligned="false">
-
- <ImageView
- android:id="@+id/bluetooth_device_icon"
- android:contentDescription="@string/accessibility_bluetooth_device_icon"
- android:layout_width="24dp"
- android:layout_height="24dp"
- app:layout_constraintStart_toStartOf="parent"
- app:layout_constraintTop_toTopOf="parent"
- app:layout_constraintBottom_toBottomOf="parent"
- android:layout_gravity="center_vertical" />
+ <ImageView
+ android:id="@+id/bluetooth_device_icon"
+ android:contentDescription="@string/accessibility_bluetooth_device_icon"
+ android:layout_width="24dp"
+ android:layout_height="24dp"
+ app:layout_constraintStart_toStartOf="parent"
+ app:layout_constraintTop_toTopOf="parent"
+ app:layout_constraintBottom_toBottomOf="parent"
+ android:layout_gravity="center_vertical" />
- <View
- android:id="@+id/bluetooth_device"
- android:layout_width="0dp"
- android:layout_height="0dp"
- app:layout_constraintTop_toTopOf="@+id/bluetooth_device_name"
- app:layout_constraintBottom_toBottomOf="@+id/bluetooth_device_summary"
- app:layout_constraintStart_toStartOf="@+id/bluetooth_device_name"
- app:layout_constraintEnd_toEndOf="@+id/bluetooth_device_name" />
+ <TextView
+ android:layout_width="0dp"
+ android:id="@+id/bluetooth_device_name"
+ style="@style/BluetoothTileDialog.DeviceName"
+ android:paddingStart="20dp"
+ android:paddingTop="10dp"
+ app:layout_constraintWidth_percent="0.7"
+ app:layout_constraintTop_toTopOf="parent"
+ app:layout_constraintStart_toEndOf="@+id/bluetooth_device_icon"
+ app:layout_constraintEnd_toStartOf="@+id/gear_icon"
+ app:layout_constraintBottom_toTopOf="@+id/bluetooth_device_summary"
+ android:gravity="center_vertical"
+ android:textSize="14sp" />
- <TextView
- android:layout_width="0dp"
- android:id="@+id/bluetooth_device_name"
- style="@style/BluetoothTileDialog.DeviceName"
- android:paddingStart="20dp"
- android:paddingTop="10dp"
- app:layout_constraintWidth_percent="0.7"
- app:layout_constraintTop_toTopOf="parent"
- app:layout_constraintStart_toEndOf="@+id/bluetooth_device_icon"
- app:layout_constraintEnd_toStartOf="@+id/gear_icon"
- app:layout_constraintBottom_toTopOf="@+id/bluetooth_device_summary"
- android:gravity="center_vertical"
- android:textSize="14sp" />
+ <TextView
+ android:layout_width="0dp"
+ android:id="@+id/bluetooth_device_summary"
+ style="@style/BluetoothTileDialog.DeviceSummary"
+ android:paddingStart="20dp"
+ android:paddingBottom="10dp"
+ app:layout_constraintWidth_percent="0.7"
+ app:layout_constraintTop_toBottomOf="@+id/bluetooth_device_name"
+ app:layout_constraintStart_toEndOf="@+id/bluetooth_device_icon"
+ app:layout_constraintEnd_toStartOf="@+id/gear_icon"
+ app:layout_constraintBottom_toBottomOf="parent"
+ android:gravity="center_vertical" />
- <TextView
- android:layout_width="0dp"
- android:id="@+id/bluetooth_device_summary"
- style="@style/BluetoothTileDialog.DeviceSummary"
- android:paddingStart="20dp"
- android:paddingBottom="10dp"
- app:layout_constraintWidth_percent="0.7"
- app:layout_constraintTop_toBottomOf="@+id/bluetooth_device_name"
- app:layout_constraintStart_toEndOf="@+id/bluetooth_device_icon"
- app:layout_constraintEnd_toStartOf="@+id/gear_icon"
- app:layout_constraintBottom_toBottomOf="parent"
- android:gravity="center_vertical" />
+ <View
+ android:id="@+id/gear_icon"
+ android:layout_width="0dp"
+ android:layout_height="0dp"
+ app:layout_constraintStart_toEndOf="@+id/bluetooth_device_name"
+ app:layout_constraintEnd_toEndOf="@+id/gear_icon_image"
+ app:layout_constraintTop_toTopOf="parent"
+ app:layout_constraintBottom_toBottomOf="parent" />
- <ImageView
- android:id="@+id/gear_icon"
- android:src="@drawable/ic_settings_24dp"
- android:contentDescription="@string/accessibility_bluetooth_device_settings_gear"
- android:layout_width="0dp"
- android:layout_height="24dp"
- app:layout_constraintStart_toEndOf="@+id/bluetooth_device_name"
- app:layout_constraintEnd_toEndOf="parent"
- app:layout_constraintTop_toTopOf="parent"
- app:layout_constraintBottom_toBottomOf="parent"
- app:layout_constraintWidth_percent="0.3"
- android:gravity="center_vertical"
- android:paddingStart="10dp" />
- </androidx.constraintlayout.widget.ConstraintLayout>
-</LinearLayout> \ No newline at end of file
+ <ImageView
+ android:id="@+id/gear_icon_image"
+ android:src="@drawable/ic_settings_24dp"
+ android:contentDescription="@string/accessibility_bluetooth_device_settings_gear"
+ android:layout_width="0dp"
+ android:layout_height="24dp"
+ app:layout_constraintStart_toEndOf="@+id/bluetooth_device_name"
+ app:layout_constraintEnd_toEndOf="parent"
+ app:layout_constraintTop_toTopOf="parent"
+ app:layout_constraintBottom_toBottomOf="parent"
+ app:layout_constraintWidth_percent="0.3"
+ android:gravity="center_vertical"
+ android:paddingStart="10dp" />
+</androidx.constraintlayout.widget.ConstraintLayout> \ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/bluetooth/BluetoothStateInteractor.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/bluetooth/BluetoothStateInteractor.kt
index efad9ec548f9..8957fc3efb9c 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/bluetooth/BluetoothStateInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/bluetooth/BluetoothStateInteractor.kt
@@ -40,7 +40,7 @@ constructor(
@Application private val coroutineScope: CoroutineScope,
) {
- internal val updateBluetoothStateFlow: StateFlow<Boolean?> =
+ internal val bluetoothStateUpdate: StateFlow<Boolean?> =
conflatedCallbackFlow {
val listener =
object : BluetoothCallback {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/bluetooth/BluetoothTileDialog.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/bluetooth/BluetoothTileDialog.kt
index 6815a7325081..8ae2dc227998 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/bluetooth/BluetoothTileDialog.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/bluetooth/BluetoothTileDialog.kt
@@ -26,6 +26,8 @@ import android.view.ViewGroup
import android.widget.ImageView
import android.widget.Switch
import android.widget.TextView
+import androidx.recyclerview.widget.AsyncListDiffer
+import androidx.recyclerview.widget.DiffUtil
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import com.android.internal.logging.UiEventLogger
@@ -42,24 +44,26 @@ import kotlinx.coroutines.flow.asStateFlow
internal class BluetoothTileDialog
constructor(
private val bluetoothToggleInitialValue: Boolean,
+ private val subtitleResIdInitialValue: Int,
private val bluetoothTileDialogCallback: BluetoothTileDialogCallback,
private val uiEventLogger: UiEventLogger,
context: Context,
) : SystemUIDialog(context, DEFAULT_THEME, DEFAULT_DISMISS_ON_DEVICE_LOCK) {
- private val mutableBluetoothStateSwitchedFlow: MutableStateFlow<Boolean> =
+ private val mutableBluetoothStateToggle: MutableStateFlow<Boolean> =
MutableStateFlow(bluetoothToggleInitialValue)
- internal val bluetoothStateSwitchedFlow
- get() = mutableBluetoothStateSwitchedFlow.asStateFlow()
+ internal val bluetoothStateToggle
+ get() = mutableBluetoothStateToggle.asStateFlow()
- private val mutableClickedFlow: MutableSharedFlow<Pair<DeviceItem, Int>> =
+ private val mutableDeviceItemClick: MutableSharedFlow<DeviceItem> =
MutableSharedFlow(extraBufferCapacity = 1)
- internal val deviceItemClickedFlow
- get() = mutableClickedFlow.asSharedFlow()
+ internal val deviceItemClick
+ get() = mutableDeviceItemClick.asSharedFlow()
private val deviceItemAdapter: Adapter = Adapter(bluetoothTileDialogCallback)
private lateinit var toggleView: Switch
+ private lateinit var subtitleTextView: TextView
private lateinit var doneButton: View
private lateinit var seeAllViewGroup: View
private lateinit var pairNewDeviceViewGroup: View
@@ -74,6 +78,7 @@ constructor(
setContentView(LayoutInflater.from(context).inflate(R.layout.bluetooth_tile_dialog, null))
toggleView = requireViewById(R.id.bluetooth_toggle)
+ subtitleTextView = requireViewById(R.id.bluetooth_tile_dialog_subtitle) as TextView
doneButton = requireViewById(R.id.done_button)
seeAllViewGroup = requireViewById(R.id.see_all_layout_group)
pairNewDeviceViewGroup = requireViewById(R.id.pair_new_device_layout_group)
@@ -84,6 +89,7 @@ constructor(
setupToggle()
setupRecyclerView()
+ subtitleTextView.text = context.getString(subtitleResIdInitialValue)
doneButton.setOnClickListener { dismiss() }
seeAllText.setOnClickListener { bluetoothTileDialogCallback.onSeeAllClicked(it) }
pairNewDeviceText.setOnClickListener {
@@ -91,7 +97,6 @@ constructor(
}
}
- // TODO(b/298124674): use DiffUtil or AsyncListDiffer to avoid updating the whole list
internal fun onDeviceItemUpdated(
deviceItem: List<DeviceItem>,
showSeeAll: Boolean,
@@ -102,18 +107,15 @@ constructor(
deviceItemAdapter.refreshDeviceItemList(deviceItem)
}
- internal fun onDeviceItemUpdatedAtPosition(deviceItem: DeviceItem, position: Int) {
- deviceItemAdapter.refreshDeviceItem(deviceItem, position)
- }
-
- internal fun onBluetoothStateUpdated(isEnabled: Boolean) {
+ internal fun onBluetoothStateUpdated(isEnabled: Boolean, subtitleResId: Int) {
toggleView.isChecked = isEnabled
+ subtitleTextView.text = context.getString(subtitleResId)
}
private fun setupToggle() {
toggleView.isChecked = bluetoothToggleInitialValue
toggleView.setOnCheckedChangeListener { _, isChecked ->
- mutableBluetoothStateSwitchedFlow.value = isChecked
+ mutableBluetoothStateToggle.value = isChecked
uiEventLogger.log(BluetoothTileDialogUiEvent.BLUETOOTH_TOGGLE_CLICKED)
}
}
@@ -128,7 +130,32 @@ constructor(
internal inner class Adapter(private val onClickCallback: BluetoothTileDialogCallback) :
RecyclerView.Adapter<Adapter.DeviceItemViewHolder>() {
- private val deviceItem: MutableList<DeviceItem> = mutableListOf()
+ private val diffUtilCallback =
+ object : DiffUtil.ItemCallback<DeviceItem>() {
+ override fun areItemsTheSame(
+ deviceItem1: DeviceItem,
+ deviceItem2: DeviceItem
+ ): Boolean {
+ return deviceItem1.cachedBluetoothDevice == deviceItem2.cachedBluetoothDevice
+ }
+
+ override fun areContentsTheSame(
+ deviceItem1: DeviceItem,
+ deviceItem2: DeviceItem
+ ): Boolean {
+ return deviceItem1.type == deviceItem2.type &&
+ deviceItem1.cachedBluetoothDevice == deviceItem2.cachedBluetoothDevice &&
+ deviceItem1.deviceName == deviceItem2.deviceName &&
+ deviceItem1.connectionSummary == deviceItem2.connectionSummary &&
+ // Ignored the icon drawable
+ deviceItem1.iconWithDescription?.second ==
+ deviceItem2.iconWithDescription?.second &&
+ deviceItem1.background == deviceItem2.background &&
+ deviceItem1.isEnabled == deviceItem2.isEnabled
+ }
+ }
+
+ private val asyncListDiffer = AsyncListDiffer(this, diffUtilCallback)
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): DeviceItemViewHolder {
val view =
@@ -137,29 +164,21 @@ constructor(
return DeviceItemViewHolder(view)
}
- override fun getItemCount() = deviceItem.size
+ override fun getItemCount() = asyncListDiffer.currentList.size
override fun onBindViewHolder(holder: DeviceItemViewHolder, position: Int) {
val item = getItem(position)
- holder.bind(item, position, onClickCallback)
+ holder.bind(item, onClickCallback)
}
- internal fun getItem(position: Int) = deviceItem[position]
+ internal fun getItem(position: Int) = asyncListDiffer.currentList[position]
internal fun refreshDeviceItemList(updated: List<DeviceItem>) {
- deviceItem.clear()
- deviceItem.addAll(updated)
- notifyDataSetChanged()
- }
-
- internal fun refreshDeviceItem(updated: DeviceItem, position: Int) {
- deviceItem[position] = updated
- notifyItemChanged(position)
+ asyncListDiffer.submitList(updated)
}
internal inner class DeviceItemViewHolder(view: View) : RecyclerView.ViewHolder(view) {
private val container = view.requireViewById<View>(R.id.bluetooth_device_row)
- private val deviceView = view.requireViewById<View>(R.id.bluetooth_device)
private val nameView = view.requireViewById<TextView>(R.id.bluetooth_device_name)
private val summaryView = view.requireViewById<TextView>(R.id.bluetooth_device_summary)
private val iconView = view.requireViewById<ImageView>(R.id.bluetooth_device_icon)
@@ -167,17 +186,15 @@ constructor(
internal fun bind(
item: DeviceItem,
- position: Int,
deviceItemOnClickCallback: BluetoothTileDialogCallback
) {
container.apply {
isEnabled = item.isEnabled
- alpha = item.alpha
- background = item.background
- }
- deviceView.setOnClickListener {
- mutableClickedFlow.tryEmit(Pair(item, position))
- uiEventLogger.log(BluetoothTileDialogUiEvent.DEVICE_CLICKED)
+ background = item.background?.let { context.getDrawable(it) }
+ setOnClickListener {
+ mutableDeviceItemClick.tryEmit(item)
+ uiEventLogger.log(BluetoothTileDialogUiEvent.DEVICE_CLICKED)
+ }
}
nameView.text = item.deviceName
summaryView.text = item.connectionSummary
@@ -195,8 +212,6 @@ constructor(
}
internal companion object {
- const val ENABLED_ALPHA = 1.0f
- const val DISABLED_ALPHA = 0.3f
const val MAX_DEVICE_ITEM_ENTRY = 3
const val ACTION_BLUETOOTH_DEVICE_DETAILS =
"com.android.settings.BLUETOOTH_DEVICE_DETAIL_SETTINGS"
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/bluetooth/BluetoothTileDialogViewModel.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/bluetooth/BluetoothTileDialogViewModel.kt
index 012484f2f0a6..97e178371f2d 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/bluetooth/BluetoothTileDialogViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/bluetooth/BluetoothTileDialogViewModel.kt
@@ -21,7 +21,9 @@ import android.content.Intent
import android.os.Bundle
import android.view.View
import androidx.annotation.VisibleForTesting
+import com.android.internal.jank.InteractionJankMonitor
import com.android.internal.logging.UiEventLogger
+import com.android.systemui.animation.DialogCuj
import com.android.systemui.animation.DialogLaunchAnimator
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application
@@ -31,6 +33,7 @@ import com.android.systemui.qs.tiles.dialog.bluetooth.BluetoothTileDialog.Compan
import com.android.systemui.qs.tiles.dialog.bluetooth.BluetoothTileDialog.Companion.ACTION_PAIR_NEW_DEVICE
import com.android.systemui.qs.tiles.dialog.bluetooth.BluetoothTileDialog.Companion.ACTION_PREVIOUSLY_CONNECTED_DEVICE
import com.android.systemui.qs.tiles.dialog.bluetooth.BluetoothTileDialog.Companion.MAX_DEVICE_ITEM_ENTRY
+import com.android.systemui.res.R
import com.android.systemui.statusbar.phone.SystemUIDialog
import javax.inject.Inject
import kotlinx.coroutines.CoroutineDispatcher
@@ -73,14 +76,26 @@ constructor(
job =
coroutineScope.launch(mainDispatcher) {
dialog = createBluetoothTileDialog(context)
- view?.let { dialogLaunchAnimator.showFromView(dialog!!, it) } ?: dialog!!.show()
+ view?.let {
+ dialogLaunchAnimator.showFromView(
+ dialog!!,
+ it,
+ animateBackgroundBoundsChange = true,
+ cuj =
+ DialogCuj(
+ InteractionJankMonitor.CUJ_SHADE_DIALOG_OPEN,
+ INTERACTION_JANK_TAG
+ )
+ )
+ }
+ ?: dialog!!.show()
updateDeviceItemJob?.cancel()
updateDeviceItemJob = launch { deviceItemInteractor.updateDeviceItems(context) }
- bluetoothStateInteractor.updateBluetoothStateFlow
+ bluetoothStateInteractor.bluetoothStateUpdate
.filterNotNull()
.onEach {
- dialog!!.onBluetoothStateUpdated(it)
+ dialog!!.onBluetoothStateUpdated(it, getSubtitleResId(it))
updateDeviceItemJob?.cancel()
updateDeviceItemJob = launch {
deviceItemInteractor.updateDeviceItems(context)
@@ -88,7 +103,7 @@ constructor(
}
.launchIn(this)
- deviceItemInteractor.updateDeviceItemsFlow
+ deviceItemInteractor.deviceItemUpdateRequest
.onEach {
updateDeviceItemJob?.cancel()
updateDeviceItemJob = launch {
@@ -97,7 +112,7 @@ constructor(
}
.launchIn(this)
- deviceItemInteractor.deviceItemFlow
+ deviceItemInteractor.deviceItemUpdate
.filterNotNull()
.onEach {
dialog!!.onDeviceItemUpdated(
@@ -109,17 +124,13 @@ constructor(
.launchIn(this)
dialog!!
- .bluetoothStateSwitchedFlow
+ .bluetoothStateToggle
.onEach { bluetoothStateInteractor.isBluetoothEnabled = it }
.launchIn(this)
dialog!!
- .deviceItemClickedFlow
- .onEach {
- if (deviceItemInteractor.updateDeviceItemOnClick(it.first)) {
- dialog!!.onDeviceItemUpdatedAtPosition(it.first, it.second)
- }
- }
+ .deviceItemClick
+ .onEach { deviceItemInteractor.updateDeviceItemOnClick(it) }
.launchIn(this)
}
}
@@ -127,6 +138,7 @@ constructor(
private fun createBluetoothTileDialog(context: Context): BluetoothTileDialog {
return BluetoothTileDialog(
bluetoothStateInteractor.isBluetoothEnabled,
+ getSubtitleResId(bluetoothStateInteractor.isBluetoothEnabled),
this@BluetoothTileDialogViewModel,
uiEventLogger,
context
@@ -175,6 +187,13 @@ constructor(
)
}
}
+
+ companion object {
+ private const val INTERACTION_JANK_TAG = "bluetooth_tile_dialog"
+ private fun getSubtitleResId(isBluetoothEnabled: Boolean) =
+ if (isBluetoothEnabled) R.string.quick_settings_bluetooth_tile_subtitle
+ else R.string.bt_is_off
+ }
}
internal interface BluetoothTileDialogCallback {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/bluetooth/DeviceItem.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/bluetooth/DeviceItem.kt
index 03ae5e88f1b0..50eaf38f20d6 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/bluetooth/DeviceItem.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/bluetooth/DeviceItem.kt
@@ -34,7 +34,6 @@ package com.android.systemui.qs.tiles.dialog.bluetooth
import android.graphics.drawable.Drawable
import com.android.settingslib.bluetooth.CachedBluetoothDevice
-import com.android.systemui.qs.tiles.dialog.bluetooth.BluetoothTileDialog.Companion.ENABLED_ALPHA
enum class DeviceItemType {
AVAILABLE_MEDIA_BLUETOOTH_DEVICE,
@@ -48,7 +47,6 @@ data class DeviceItem(
val deviceName: String = "",
val connectionSummary: String = "",
val iconWithDescription: Pair<Drawable, String>? = null,
- val background: Drawable? = null,
- var isEnabled: Boolean = true,
- var alpha: Float = ENABLED_ALPHA
+ val background: Int? = null,
+ var isEnabled: Boolean = true
)
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/bluetooth/DeviceItemFactory.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/bluetooth/DeviceItemFactory.kt
index a16a9f1f1017..8c22614f9c31 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/bluetooth/DeviceItemFactory.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/bluetooth/DeviceItemFactory.kt
@@ -24,6 +24,8 @@ import com.android.settingslib.bluetooth.CachedBluetoothDevice
import com.android.systemui.res.R
private val backgroundOn = R.drawable.settingslib_switch_bar_bg_on
+private val backgroundOff = R.drawable.bluetooth_tile_dialog_bg_off
+private val backgroundOffBusy = R.drawable.bluetooth_tile_dialog_bg_off_busy
private val connected = R.string.quick_settings_bluetooth_device_connected
private val saved = R.string.quick_settings_bluetooth_device_saved
@@ -57,11 +59,8 @@ internal class AvailableMediaDeviceItemFactory : DeviceItemFactory() {
BluetoothUtils.getBtClassDrawableWithDescription(context, cachedDevice).let { p ->
Pair(p.first, p.second)
},
- background = context.getDrawable(backgroundOn),
+ background = backgroundOn,
isEnabled = !cachedDevice.isBusy,
- alpha =
- if (cachedDevice.isBusy) BluetoothTileDialog.DISABLED_ALPHA
- else BluetoothTileDialog.ENABLED_ALPHA,
)
}
}
@@ -85,10 +84,8 @@ internal class ConnectedDeviceItemFactory : DeviceItemFactory() {
BluetoothUtils.getBtClassDrawableWithDescription(context, cachedDevice).let { p ->
Pair(p.first, p.second)
},
+ background = backgroundOn,
isEnabled = !cachedDevice.isBusy,
- alpha =
- if (cachedDevice.isBusy) BluetoothTileDialog.DISABLED_ALPHA
- else BluetoothTileDialog.ENABLED_ALPHA,
)
}
}
@@ -112,10 +109,8 @@ internal class SavedDeviceItemFactory : DeviceItemFactory() {
BluetoothUtils.getBtClassDrawableWithDescription(context, cachedDevice).let { p ->
Pair(p.first, p.second)
},
+ background = if (cachedDevice.isBusy) backgroundOffBusy else backgroundOff,
isEnabled = !cachedDevice.isBusy,
- alpha =
- if (cachedDevice.isBusy) BluetoothTileDialog.DISABLED_ALPHA
- else BluetoothTileDialog.ENABLED_ALPHA,
)
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/bluetooth/DeviceItemInteractor.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/bluetooth/DeviceItemInteractor.kt
index fcd0ce6807fd..14d24f952c8d 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/bluetooth/DeviceItemInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/bluetooth/DeviceItemInteractor.kt
@@ -55,11 +55,12 @@ constructor(
@Background private val backgroundDispatcher: CoroutineDispatcher,
) {
- private val mutableDeviceItemFlow: MutableStateFlow<List<DeviceItem>?> = MutableStateFlow(null)
- internal val deviceItemFlow
- get() = mutableDeviceItemFlow.asStateFlow()
+ private val mutableDeviceItemUpdate: MutableStateFlow<List<DeviceItem>?> =
+ MutableStateFlow(null)
+ internal val deviceItemUpdate
+ get() = mutableDeviceItemUpdate.asStateFlow()
- internal val updateDeviceItemsFlow: SharedFlow<Unit> =
+ internal val deviceItemUpdateRequest: SharedFlow<Unit> =
conflatedCallbackFlow {
val listener =
object : BluetoothCallback {
@@ -120,7 +121,7 @@ constructor(
withContext(backgroundDispatcher) {
val mostRecentlyConnectedDevices = bluetoothAdapter?.mostRecentlyConnectedDevices
- mutableDeviceItemFlow.value =
+ mutableDeviceItemUpdate.value =
bluetoothTileDialogRepository.cachedDevices
.mapNotNull { cachedDevice ->
deviceItemFactoryList
@@ -143,28 +144,20 @@ constructor(
)
}
- internal fun updateDeviceItemOnClick(deviceItem: DeviceItem): Boolean {
- var isClicked = false
+ internal fun updateDeviceItemOnClick(deviceItem: DeviceItem) {
when (deviceItem.type) {
DeviceItemType.AVAILABLE_MEDIA_BLUETOOTH_DEVICE -> {
if (!BluetoothUtils.isActiveMediaDevice(deviceItem.cachedBluetoothDevice)) {
deviceItem.cachedBluetoothDevice.setActive()
uiEventLogger.log(BluetoothTileDialogUiEvent.CONNECTED_DEVICE_SET_ACTIVE)
- isClicked = true
}
}
DeviceItemType.CONNECTED_BLUETOOTH_DEVICE -> {}
DeviceItemType.SAVED_BLUETOOTH_DEVICE -> {
deviceItem.cachedBluetoothDevice.connect()
uiEventLogger.log(BluetoothTileDialogUiEvent.SAVED_DEVICE_CONNECT)
- isClicked = true
}
}
- if (isClicked) {
- deviceItem.isEnabled = false
- deviceItem.alpha = BluetoothTileDialog.DISABLED_ALPHA
- }
- return isClicked
}
internal fun setDeviceItemFactoryListForTesting(list: List<DeviceItemFactory>) {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/bluetooth/BluetoothTileDialogTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/bluetooth/BluetoothTileDialogTest.kt
index 89fa55b319ba..8b6604048ea8 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/bluetooth/BluetoothTileDialogTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/bluetooth/BluetoothTileDialogTest.kt
@@ -29,8 +29,6 @@ import androidx.test.filters.SmallTest
import com.android.internal.logging.UiEventLogger
import com.android.settingslib.bluetooth.CachedBluetoothDevice
import com.android.systemui.SysuiTestCase
-import com.android.systemui.qs.tiles.dialog.bluetooth.BluetoothTileDialog.Companion.DISABLED_ALPHA
-import com.android.systemui.qs.tiles.dialog.bluetooth.BluetoothTileDialog.Companion.ENABLED_ALPHA
import com.android.systemui.res.R
import com.google.common.truth.Truth.assertThat
import org.junit.Before
@@ -62,6 +60,8 @@ class BluetoothTileDialogTest : SysuiTestCase() {
@Mock private lateinit var uiEventLogger: UiEventLogger
+ private val subtitleResId = R.string.quick_settings_bluetooth_tile_subtitle
+
private lateinit var icon: Pair<Drawable, String>
private lateinit var bluetoothTileDialog: BluetoothTileDialog
private lateinit var deviceItem: DeviceItem
@@ -69,7 +69,13 @@ class BluetoothTileDialogTest : SysuiTestCase() {
@Before
fun setUp() {
bluetoothTileDialog =
- BluetoothTileDialog(ENABLED, bluetoothTileDialogCallback, uiEventLogger, mContext)
+ BluetoothTileDialog(
+ ENABLED,
+ subtitleResId,
+ bluetoothTileDialogCallback,
+ uiEventLogger,
+ mContext
+ )
icon = Pair(drawable, DEVICE_NAME)
deviceItem =
DeviceItem(
@@ -99,7 +105,13 @@ class BluetoothTileDialogTest : SysuiTestCase() {
@Test
fun testShowDialog_displayBluetoothDevice() {
bluetoothTileDialog =
- BluetoothTileDialog(ENABLED, bluetoothTileDialogCallback, uiEventLogger, mContext)
+ BluetoothTileDialog(
+ ENABLED,
+ subtitleResId,
+ bluetoothTileDialogCallback,
+ uiEventLogger,
+ mContext
+ )
bluetoothTileDialog.show()
bluetoothTileDialog.onDeviceItemUpdated(
listOf(deviceItem),
@@ -118,49 +130,61 @@ class BluetoothTileDialogTest : SysuiTestCase() {
@Test
fun testDeviceItemViewHolder_cachedDeviceNotBusy() {
deviceItem.isEnabled = true
- deviceItem.alpha = ENABLED_ALPHA
val view =
LayoutInflater.from(mContext).inflate(R.layout.bluetooth_device_item, null, false)
val viewHolder =
- BluetoothTileDialog(ENABLED, bluetoothTileDialogCallback, uiEventLogger, mContext)
+ BluetoothTileDialog(
+ ENABLED,
+ subtitleResId,
+ bluetoothTileDialogCallback,
+ uiEventLogger,
+ mContext
+ )
.Adapter(bluetoothTileDialogCallback)
.DeviceItemViewHolder(view)
- viewHolder.bind(deviceItem, 0, bluetoothTileDialogCallback)
- val container = view.requireViewById<View>(R.id.bluetooth_device)
- val deviceView = view.requireViewById<View>(R.id.bluetooth_device)
+ viewHolder.bind(deviceItem, bluetoothTileDialogCallback)
+ val container = view.requireViewById<View>(R.id.bluetooth_device_row)
assertThat(container).isNotNull()
assertThat(container.isEnabled).isTrue()
- assertThat(container.alpha).isEqualTo(ENABLED_ALPHA)
- assertThat(deviceView.hasOnClickListeners()).isTrue()
+ assertThat(container.hasOnClickListeners()).isTrue()
}
@Test
fun testDeviceItemViewHolder_cachedDeviceBusy() {
deviceItem.isEnabled = false
- deviceItem.alpha = DISABLED_ALPHA
val view =
LayoutInflater.from(mContext).inflate(R.layout.bluetooth_device_item, null, false)
val viewHolder =
- BluetoothTileDialog(ENABLED, bluetoothTileDialogCallback, uiEventLogger, mContext)
+ BluetoothTileDialog(
+ ENABLED,
+ subtitleResId,
+ bluetoothTileDialogCallback,
+ uiEventLogger,
+ mContext
+ )
.Adapter(bluetoothTileDialogCallback)
.DeviceItemViewHolder(view)
- viewHolder.bind(deviceItem, 0, bluetoothTileDialogCallback)
+ viewHolder.bind(deviceItem, bluetoothTileDialogCallback)
val container = view.requireViewById<View>(R.id.bluetooth_device_row)
- val deviceView = view.requireViewById<View>(R.id.bluetooth_device)
assertThat(container).isNotNull()
assertThat(container.isEnabled).isFalse()
- assertThat(container.alpha).isEqualTo(DISABLED_ALPHA)
- assertThat(deviceView.hasOnClickListeners()).isTrue()
+ assertThat(container.hasOnClickListeners()).isTrue()
}
@Test
fun testOnDeviceUpdated_hideSeeAll_showPairNew() {
bluetoothTileDialog =
- BluetoothTileDialog(ENABLED, bluetoothTileDialogCallback, uiEventLogger, mContext)
+ BluetoothTileDialog(
+ ENABLED,
+ subtitleResId,
+ bluetoothTileDialogCallback,
+ uiEventLogger,
+ mContext
+ )
bluetoothTileDialog.show()
bluetoothTileDialog.onDeviceItemUpdated(
listOf(deviceItem),
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/bluetooth/BluetoothTileDialogViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/bluetooth/BluetoothTileDialogViewModelTest.kt
index 7157cce8e607..a0ff2ab330b8 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/bluetooth/BluetoothTileDialogViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/bluetooth/BluetoothTileDialogViewModelTest.kt
@@ -95,10 +95,11 @@ class BluetoothTileDialogViewModelTest : SysuiTestCase() {
testScope.backgroundScope,
dispatcher,
)
- `when`(deviceItemInteractor.deviceItemFlow).thenReturn(MutableStateFlow(null).asStateFlow())
- `when`(bluetoothStateInteractor.updateBluetoothStateFlow)
+ `when`(deviceItemInteractor.deviceItemUpdate)
.thenReturn(MutableStateFlow(null).asStateFlow())
- `when`(deviceItemInteractor.updateDeviceItemsFlow)
+ `when`(bluetoothStateInteractor.bluetoothStateUpdate)
+ .thenReturn(MutableStateFlow(null).asStateFlow())
+ `when`(deviceItemInteractor.deviceItemUpdateRequest)
.thenReturn(MutableStateFlow(Unit).asStateFlow())
`when`(bluetoothStateInteractor.isBluetoothEnabled).thenReturn(true)
}
@@ -143,7 +144,7 @@ class BluetoothTileDialogViewModelTest : SysuiTestCase() {
bluetoothTileDialogViewModel.showDialog(context, null)
assertThat(bluetoothTileDialogViewModel.dialog).isNotNull()
- verify(deviceItemInteractor).deviceItemFlow
+ verify(deviceItemInteractor).deviceItemUpdate
}
}
@@ -153,7 +154,7 @@ class BluetoothTileDialogViewModelTest : SysuiTestCase() {
bluetoothTileDialogViewModel.showDialog(context, null)
assertThat(bluetoothTileDialogViewModel.dialog).isNotNull()
- verify(bluetoothStateInteractor).updateBluetoothStateFlow
+ verify(bluetoothStateInteractor).bluetoothStateUpdate
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/bluetooth/DeviceItemFactoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/bluetooth/DeviceItemFactoryTest.kt
index 34519023e316..92c73261a95e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/bluetooth/DeviceItemFactoryTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/bluetooth/DeviceItemFactoryTest.kt
@@ -69,7 +69,7 @@ class DeviceItemFactoryTest : SysuiTestCase() {
val deviceItem = savedDeviceItemFactory.create(context, cachedDevice)
assertDeviceItem(deviceItem, DeviceItemType.SAVED_BLUETOOTH_DEVICE)
- assertThat(deviceItem.background).isNull()
+ assertThat(deviceItem.background).isNotNull()
}
private fun assertDeviceItem(deviceItem: DeviceItem?, deviceItemType: DeviceItemType) {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/bluetooth/DeviceItemInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/bluetooth/DeviceItemInteractorTest.kt
index 07a95ae330c6..3593075c70fd 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/bluetooth/DeviceItemInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/bluetooth/DeviceItemInteractorTest.kt
@@ -109,7 +109,7 @@ class DeviceItemInteractorTest : SysuiTestCase() {
interactor.updateDeviceItems(mContext)
- assertThat(interactor.deviceItemFlow.value).isEmpty()
+ assertThat(interactor.deviceItemUpdate.value).isEmpty()
}
}
@@ -123,7 +123,7 @@ class DeviceItemInteractorTest : SysuiTestCase() {
interactor.updateDeviceItems(mContext)
- assertThat(interactor.deviceItemFlow.value).isEmpty()
+ assertThat(interactor.deviceItemUpdate.value).isEmpty()
}
}
@@ -137,8 +137,8 @@ class DeviceItemInteractorTest : SysuiTestCase() {
interactor.updateDeviceItems(mContext)
- assertThat(interactor.deviceItemFlow.value).hasSize(1)
- assertThat(interactor.deviceItemFlow.value!![0]).isEqualTo(deviceItem1)
+ assertThat(interactor.deviceItemUpdate.value).hasSize(1)
+ assertThat(interactor.deviceItemUpdate.value!![0]).isEqualTo(deviceItem1)
}
}
@@ -152,9 +152,9 @@ class DeviceItemInteractorTest : SysuiTestCase() {
interactor.updateDeviceItems(mContext)
- assertThat(interactor.deviceItemFlow.value).hasSize(2)
- assertThat(interactor.deviceItemFlow.value!![0]).isEqualTo(deviceItem2)
- assertThat(interactor.deviceItemFlow.value!![1]).isEqualTo(deviceItem2)
+ assertThat(interactor.deviceItemUpdate.value).hasSize(2)
+ assertThat(interactor.deviceItemUpdate.value!![0]).isEqualTo(deviceItem2)
+ assertThat(interactor.deviceItemUpdate.value!![1]).isEqualTo(deviceItem2)
}
}
@@ -179,7 +179,8 @@ class DeviceItemInteractorTest : SysuiTestCase() {
interactor.updateDeviceItems(mContext)
- assertThat(interactor.deviceItemFlow.value).isEqualTo(listOf(deviceItem2, deviceItem1))
+ assertThat(interactor.deviceItemUpdate.value)
+ .isEqualTo(listOf(deviceItem2, deviceItem1))
}
}
@@ -201,7 +202,8 @@ class DeviceItemInteractorTest : SysuiTestCase() {
interactor.updateDeviceItems(mContext)
- assertThat(interactor.deviceItemFlow.value).isEqualTo(listOf(deviceItem2, deviceItem1))
+ assertThat(interactor.deviceItemUpdate.value)
+ .isEqualTo(listOf(deviceItem2, deviceItem1))
}
}