diff options
| author | 2023-09-26 18:26:37 +0800 | |
|---|---|---|
| committer | 2023-10-02 04:16:53 +0000 | |
| commit | b1a3482ea1d1a97814a41e69dab92bb4557c66da (patch) | |
| tree | 77635335fbab7a026cdb88733094dc56d7dcc7c9 | |
| parent | 3526aabf0ed026937e3e75a45971b49ee2e5a47c (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
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)) } } |