diff options
6 files changed, 99 insertions, 15 deletions
diff --git a/packages/SystemUI/res/layout/bluetooth_device_item.xml b/packages/SystemUI/res/layout/bluetooth_device_item.xml index 4336ccc70c33..4535f67fa7f9 100644 --- a/packages/SystemUI/res/layout/bluetooth_device_item.xml +++ b/packages/SystemUI/res/layout/bluetooth_device_item.xml @@ -41,7 +41,7 @@ android:textDirection="locale" android:textAlignment="gravity" android:paddingStart="20dp" - android:paddingTop="10dp" + android:paddingTop="15dp" android:maxLines="1" android:ellipsize="end" app:layout_constraintTop_toTopOf="parent" @@ -56,7 +56,7 @@ android:id="@+id/bluetooth_device_summary" style="@style/BluetoothTileDialog.DeviceSummary" android:paddingStart="20dp" - android:paddingBottom="10dp" + android:paddingBottom="15dp" android:maxLines="1" android:ellipsize="end" app:layout_constraintTop_toBottomOf="@+id/bluetooth_device_name" diff --git a/packages/SystemUI/res/layout/bluetooth_tile_dialog.xml b/packages/SystemUI/res/layout/bluetooth_tile_dialog.xml index 50241cdca8b5..0534c6ee466f 100644 --- a/packages/SystemUI/res/layout/bluetooth_tile_dialog.xml +++ b/packages/SystemUI/res/layout/bluetooth_tile_dialog.xml @@ -51,13 +51,14 @@ android:textAppearance="@style/TextAppearance.Dialog.Body.Message" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" - app:layout_constraintBottom_toTopOf="@+id/scroll_view" app:layout_constraintTop_toBottomOf="@id/bluetooth_tile_dialog_title" /> <androidx.core.widget.NestedScrollView android:id="@+id/scroll_view" android:layout_width="match_parent" android:layout_height="wrap_content" + android:layout_marginTop="21dp" + android:fillViewport="true" app:layout_constrainedHeight="true" app:layout_constraintStart_toStartOf="parent" app:layout_constraintEnd_toEndOf="parent" @@ -84,13 +85,13 @@ android:textSize="16sp" app:layout_constraintEnd_toStartOf="@+id/bluetooth_toggle" app:layout_constraintStart_toStartOf="parent" - app:layout_constraintBottom_toTopOf="@+id/device_list" app:layout_constraintTop_toTopOf="parent" /> <Switch android:id="@+id/bluetooth_toggle" android:layout_width="wrap_content" android:layout_height="48dp" + android:paddingTop="10dp" android:gravity="start|center_vertical" android:paddingEnd="40dp" android:contentDescription="@string/turn_on_bluetooth" @@ -100,14 +101,13 @@ android:track="@drawable/settingslib_track_selector" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toEndOf="@+id/bluetooth_toggle_title" - app:layout_constraintBottom_toTopOf="@+id/device_list" app:layout_constraintTop_toTopOf="parent" /> <androidx.recyclerview.widget.RecyclerView android:id="@+id/device_list" android:layout_width="match_parent" android:layout_height="wrap_content" - android:layout_marginTop="10dp" + android:layout_marginTop="20dp" app:layout_constraintStart_toStartOf="parent" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintTop_toBottomOf="@+id/bluetooth_toggle" @@ -146,6 +146,7 @@ android:background="@drawable/bluetooth_tile_dialog_bg_off" android:layout_width="0dp" android:layout_height="64dp" + android:layout_marginBottom="9dp" android:contentDescription="@string/accessibility_bluetooth_device_settings_pair_new_device" app:layout_constraintStart_toStartOf="parent" app:layout_constraintEnd_toEndOf="parent" @@ -177,7 +178,6 @@ android:maxLines="1" android:text="@string/inline_done_button" app:layout_constraintEnd_toEndOf="parent" - app:layout_constraintTop_toBottomOf="@id/pair_new_device_button" app:layout_constraintBottom_toBottomOf="parent" /> </androidx.constraintlayout.widget.ConstraintLayout> </androidx.core.widget.NestedScrollView> 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 db3cf0f70f69..1805eb182cb1 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 @@ -24,6 +24,7 @@ import android.view.View.AccessibilityDelegate import android.view.View.GONE import android.view.View.VISIBLE import android.view.ViewGroup +import android.view.ViewGroup.LayoutParams.WRAP_CONTENT import android.view.accessibility.AccessibilityNodeInfo import android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction import android.widget.ImageView @@ -54,6 +55,7 @@ internal class BluetoothTileDialog constructor( private val bluetoothToggleInitialValue: Boolean, private val subtitleResIdInitialValue: Int, + private val cachedContentHeight: Int, private val bluetoothTileDialogCallback: BluetoothTileDialogCallback, @Main private val mainDispatcher: CoroutineDispatcher, private val systemClock: SystemClock, @@ -72,6 +74,11 @@ constructor( internal val deviceItemClick get() = mutableDeviceItemClick.asSharedFlow() + private val mutableContentHeight: MutableSharedFlow<Int> = + MutableSharedFlow(extraBufferCapacity = 1) + internal val contentHeight + get() = mutableContentHeight.asSharedFlow() + private val deviceItemAdapter: Adapter = Adapter(bluetoothTileDialogCallback) private var lastUiUpdateMs: Long = -1 @@ -84,6 +91,7 @@ constructor( private lateinit var seeAllButton: View private lateinit var pairNewDeviceButton: View private lateinit var deviceListView: RecyclerView + private lateinit var scrollViewContent: View override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) @@ -110,12 +118,23 @@ constructor( pairNewDeviceButton.setOnClickListener { bluetoothTileDialogCallback.onPairNewDeviceClicked(it) } + requireViewById<View>(R.id.scroll_view).apply { + scrollViewContent = this + layoutParams.height = cachedContentHeight + } } override fun start() { lastUiUpdateMs = systemClock.elapsedRealtime() } + override fun dismiss() { + if (::scrollViewContent.isInitialized) { + mutableContentHeight.tryEmit(scrollViewContent.measuredHeight) + } + super.dismiss() + } + internal suspend fun onDeviceItemUpdated( deviceItem: List<DeviceItem>, showSeeAll: Boolean, @@ -124,14 +143,16 @@ constructor( withContext(mainDispatcher) { val start = systemClock.elapsedRealtime() val itemRow = deviceItem.size + showSeeAll.toInt() + showPairNewDevice.toInt() - // Add a slight delay for smoother dialog height change - if (itemRow != lastItemRow) { + // If not the first load, add a slight delay for smoother dialog height change + if (itemRow != lastItemRow && lastItemRow != -1) { delay(MIN_HEIGHT_CHANGE_INTERVAL_MS - (start - lastUiUpdateMs)) } if (isActive) { deviceItemAdapter.refreshDeviceItemList(deviceItem) { seeAllButton.visibility = if (showSeeAll) VISIBLE else GONE pairNewDeviceButton.visibility = if (showPairNewDevice) VISIBLE else GONE + // Update the height after data is updated + scrollViewContent.layoutParams.height = WRAP_CONTENT lastUiUpdateMs = systemClock.elapsedRealtime() lastItemRow = itemRow logger.logDeviceUiUpdate(lastUiUpdateMs - start) 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 5d5e747ba979..6d08f591690f 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 @@ -18,14 +18,18 @@ package com.android.systemui.qs.tiles.dialog.bluetooth import android.content.Context import android.content.Intent +import android.content.SharedPreferences import android.os.Bundle import android.view.View +import android.view.ViewGroup import com.android.internal.jank.InteractionJankMonitor import com.android.internal.logging.UiEventLogger +import com.android.systemui.Prefs 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 +import com.android.systemui.dagger.qualifiers.Background import com.android.systemui.dagger.qualifiers.Main import com.android.systemui.plugins.ActivityStarter import com.android.systemui.qs.tiles.dialog.bluetooth.BluetoothTileDialog.Companion.ACTION_BLUETOOTH_DEVICE_DETAILS @@ -45,6 +49,7 @@ import kotlinx.coroutines.flow.filterNotNull import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.launch +import kotlinx.coroutines.withContext /** ViewModel for Bluetooth Dialog after clicking on the Bluetooth QS tile. */ @SysUISingleton @@ -60,6 +65,8 @@ constructor( private val logger: BluetoothTileDialogLogger, @Application private val coroutineScope: CoroutineScope, @Main private val mainDispatcher: CoroutineDispatcher, + @Background private val backgroundDispatcher: CoroutineDispatcher, + @Main private val sharedPreferences: SharedPreferences, ) : BluetoothTileDialogCallback { private var job: Job? = null @@ -145,14 +152,31 @@ constructor( .onEach { deviceItemInteractor.updateDeviceItemOnClick(it) } .launchIn(this) + dialog.contentHeight + .onEach { + withContext(backgroundDispatcher) { + sharedPreferences.edit().putInt(CONTENT_HEIGHT_PREF_KEY, it).apply() + } + } + .launchIn(this) + produce<Unit> { awaitClose { dialog.cancel() } } } } - private fun createBluetoothTileDialog(context: Context): BluetoothTileDialog { + private suspend fun createBluetoothTileDialog(context: Context): BluetoothTileDialog { + val cachedContentHeight = + withContext(backgroundDispatcher) { + sharedPreferences.getInt( + CONTENT_HEIGHT_PREF_KEY, + ViewGroup.LayoutParams.WRAP_CONTENT + ) + } + return BluetoothTileDialog( bluetoothStateInteractor.isBluetoothEnabled, getSubtitleResId(bluetoothStateInteractor.isBluetoothEnabled), + cachedContentHeight, this@BluetoothTileDialogViewModel, mainDispatcher, systemClock, @@ -205,6 +229,7 @@ constructor( companion object { private const val INTERACTION_JANK_TAG = "bluetooth_tile_dialog" + private const val CONTENT_HEIGHT_PREF_KEY = Prefs.Key.BLUETOOTH_TILE_DIALOG_CONTENT_HEIGHT private fun getSubtitleResId(isBluetoothEnabled: Boolean) = if (isBluetoothEnabled) R.string.quick_settings_bluetooth_tile_subtitle else R.string.bt_is_off 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 313ccb8a8717..154aa1cabb0c 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 @@ -23,6 +23,8 @@ import android.view.LayoutInflater import android.view.View import android.view.View.GONE import android.view.View.VISIBLE +import android.view.ViewGroup.LayoutParams.MATCH_PARENT +import android.view.ViewGroup.LayoutParams.WRAP_CONTENT import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.RecyclerView import androidx.test.filters.SmallTest @@ -54,6 +56,7 @@ class BluetoothTileDialogTest : SysuiTestCase() { const val DEVICE_NAME = "device" const val DEVICE_CONNECTION_SUMMARY = "active" const val ENABLED = true + const val CONTENT_HEIGHT = WRAP_CONTENT } @get:Rule val mockitoRule: MockitoRule = MockitoJUnit.rule() @@ -88,6 +91,7 @@ class BluetoothTileDialogTest : SysuiTestCase() { BluetoothTileDialog( ENABLED, subtitleResId, + CONTENT_HEIGHT, bluetoothTileDialogCallback, dispatcher, fakeSystemClock, @@ -116,9 +120,9 @@ class BluetoothTileDialogTest : SysuiTestCase() { assertThat(bluetoothTileDialog.isShowing).isTrue() assertThat(recyclerView).isNotNull() - assertThat(recyclerView?.visibility).isEqualTo(VISIBLE) - assertThat(recyclerView?.adapter).isNotNull() - assertThat(recyclerView?.layoutManager is LinearLayoutManager).isTrue() + assertThat(recyclerView.visibility).isEqualTo(VISIBLE) + assertThat(recyclerView.adapter).isNotNull() + assertThat(recyclerView.layoutManager is LinearLayoutManager).isTrue() } @Test @@ -128,6 +132,7 @@ class BluetoothTileDialogTest : SysuiTestCase() { BluetoothTileDialog( ENABLED, subtitleResId, + CONTENT_HEIGHT, bluetoothTileDialogCallback, dispatcher, fakeSystemClock, @@ -144,7 +149,7 @@ class BluetoothTileDialogTest : SysuiTestCase() { ) val recyclerView = bluetoothTileDialog.requireViewById<RecyclerView>(R.id.device_list) - val adapter = recyclerView?.adapter as BluetoothTileDialog.Adapter + val adapter = recyclerView.adapter as BluetoothTileDialog.Adapter assertThat(adapter.itemCount).isEqualTo(1) assertThat(adapter.getItem(0).deviceName).isEqualTo(DEVICE_NAME) assertThat(adapter.getItem(0).connectionSummary).isEqualTo(DEVICE_CONNECTION_SUMMARY) @@ -162,6 +167,7 @@ class BluetoothTileDialogTest : SysuiTestCase() { BluetoothTileDialog( ENABLED, subtitleResId, + CONTENT_HEIGHT, bluetoothTileDialogCallback, dispatcher, fakeSystemClock, @@ -189,6 +195,7 @@ class BluetoothTileDialogTest : SysuiTestCase() { BluetoothTileDialog( ENABLED, subtitleResId, + CONTENT_HEIGHT, bluetoothTileDialogCallback, dispatcher, fakeSystemClock, @@ -213,6 +220,7 @@ class BluetoothTileDialogTest : SysuiTestCase() { BluetoothTileDialog( ENABLED, subtitleResId, + CONTENT_HEIGHT, bluetoothTileDialogCallback, dispatcher, fakeSystemClock, @@ -232,13 +240,38 @@ class BluetoothTileDialogTest : SysuiTestCase() { val pairNewButton = bluetoothTileDialog.requireViewById<View>(R.id.pair_new_device_button) val recyclerView = bluetoothTileDialog.requireViewById<RecyclerView>(R.id.device_list) - val adapter = recyclerView?.adapter as BluetoothTileDialog.Adapter + val adapter = recyclerView.adapter as BluetoothTileDialog.Adapter + val scrollViewContent = bluetoothTileDialog.requireViewById<View>(R.id.scroll_view) assertThat(seeAllButton).isNotNull() assertThat(seeAllButton.visibility).isEqualTo(GONE) assertThat(pairNewButton).isNotNull() assertThat(pairNewButton.visibility).isEqualTo(VISIBLE) assertThat(adapter.itemCount).isEqualTo(1) + assertThat(scrollViewContent.layoutParams.height).isEqualTo(WRAP_CONTENT) + } + } + + @Test + fun testShowDialog_displayFromCachedHeight() { + testScope.runTest { + bluetoothTileDialog = + BluetoothTileDialog( + ENABLED, + subtitleResId, + MATCH_PARENT, + bluetoothTileDialogCallback, + dispatcher, + fakeSystemClock, + uiEventLogger, + logger, + mContext + ) + bluetoothTileDialog.show() + assertThat( + bluetoothTileDialog.requireViewById<View>(R.id.scroll_view).layoutParams.height + ) + .isEqualTo(MATCH_PARENT) } } } 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 99993f2b3eff..33066d2092b8 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 @@ -16,6 +16,7 @@ package com.android.systemui.qs.tiles.dialog.bluetooth +import android.content.SharedPreferences import android.testing.AndroidTestingRunner import android.testing.TestableLooper import android.view.View @@ -78,6 +79,8 @@ class BluetoothTileDialogViewModelTest : SysuiTestCase() { @Mock private lateinit var logger: BluetoothTileDialogLogger + @Mock private lateinit var sharedPreferences: SharedPreferences + private lateinit var scheduler: TestCoroutineScheduler private lateinit var dispatcher: CoroutineDispatcher private lateinit var testScope: TestScope @@ -98,6 +101,8 @@ class BluetoothTileDialogViewModelTest : SysuiTestCase() { logger, testScope.backgroundScope, dispatcher, + dispatcher, + sharedPreferences, ) `when`(deviceItemInteractor.deviceItemUpdate).thenReturn(MutableSharedFlow()) `when`(bluetoothStateInteractor.bluetoothStateUpdate) |