Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 5aa29b9b authored by chelseahao's avatar chelseahao
Browse files

Show cached height for bluetooth tile dialog.

Test: atest -c com.android.systemui.qs.tiles.dialog.bluetooth
Bug: b/310135234
Change-Id: Ib471f3105b22b0bf9544e305f700f3d1540d3b67
parent 5a820f87
Loading
Loading
Loading
Loading
+2 −2
Original line number Diff line number Diff line
@@ -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"
+5 −5
Original line number Diff line number Diff line
@@ -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>
+23 −2
Original line number Diff line number Diff line
@@ -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)
+26 −1
Original line number Diff line number Diff line
@@ -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
+38 −5
Original line number Diff line number Diff line
@@ -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)
        }
    }
}
Loading