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

Commit 4283472e authored by My Name's avatar My Name
Browse files

Refactor `showDetailsContent` in `BluetoothDetailsContentViewModel`.

Split `showDetailsContent` into two methods. One will be called by the dialog flow to show dialog, the other will be called by the details view flow to bind the view.

Test: BluetoothDetailsContentViewModelTest, BluetoothTileTest
Flag: NONE refactor
Change-Id: I8384bf509a8fd25ff59226ca6a1494626d05c005
parent 8dea4977
Loading
Loading
Loading
Loading
+2 −2
Original line number Diff line number Diff line
@@ -32,9 +32,9 @@ fun BluetoothDetailsContent(detailsContentViewModel: BluetoothDetailsContentView
            val view =
                LayoutInflater.from(context)
                    .inflate(R.layout.bluetooth_tile_dialog, /* root= */ null)
            detailsContentViewModel.showDetailsContent(/* expandable= */ null, view)
            detailsContentViewModel.bindDetailsView(view)
            view
        },
        onRelease = { detailsContentViewModel.contentManager.releaseView() },
        onRelease = { detailsContentViewModel.unbindDetailsView() },
    )
}
+190 −183
Original line number Diff line number Diff line
@@ -44,6 +44,7 @@ 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.flags.QsDetailedView
import com.android.systemui.res.R
import com.android.systemui.statusbar.phone.SystemUIDialog
import javax.inject.Inject
@@ -52,6 +53,7 @@ import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Job
import kotlinx.coroutines.channels.awaitClose
import kotlinx.coroutines.channels.produce
import kotlinx.coroutines.coroutineScope
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.emptyFlow
import kotlinx.coroutines.flow.filterNotNull
@@ -91,29 +93,36 @@ constructor(
    private var job: Job? = null

    /**
     * Shows the details content.
     * Binds the bluetooth details view with BluetoothDetailsContentManager.
     *
     * @param view The view from which the dialog is shown. If view is null, it should show the
     *   bluetooth tile details view.
     *
     * TODO: b/378513956 Refactor this method into 2. One is called by the dialog to show the
     *   dialog, another is called by the details view model to bind the view.
     * @param view The view from which the bluetooth details content is shown.
     */
    fun showDetailsContent(expandable: Expandable?, view: View?) {
    fun bindDetailsView(view: View) {
        // If `QsDetailedView` is not enabled, it should show the dialog.
        QsDetailedView.assertInNewMode()

        cancelJob()

        job =
            coroutineScope.launch(context = mainDispatcher) {
                var updateDeviceItemJob: Job?
                var updateDialogUiJob: Job? = null
                val dialog: SystemUIDialog?
                val context: Context
                contentManager = createContentManager()
                contentManager.bind(view)
                contentManager.start()
                updateDetailsUI(context = view.context, dialog = null)
            }
    }

    /** Shows the bluetooth dialog. */
    fun showDialog(expandable: Expandable?) {
        // If `QsDetailedView` is enabled, it should show the details view.
        QsDetailedView.assertInLegacyMode()

        cancelJob()

                if (view == null) {
                    // Render with dialog
        job =
            coroutineScope.launch(context = mainDispatcher) {
                val dialogDelegate = createBluetoothTileDialog()
                    dialog = dialogDelegate.createDialog()
                    context = dialog.context
                val dialog = dialogDelegate.createDialog()

                val controller =
                    expandable?.dialogTransitionController(
@@ -123,41 +132,41 @@ constructor(
                        )
                    )
                controller?.let {
                        dialogTransitionAnimator.show(
                            dialog,
                            it,
                            animateBackgroundBoundsChange = true,
                        )
                    dialogTransitionAnimator.show(dialog, it, animateBackgroundBoundsChange = true)
                } ?: dialog.show()
                // contentManager is created after dialog.show
                contentManager = dialogDelegate.contentManager
                } else {
                    // Render with tile details view
                    dialog = null
                    context = view.context
                    contentManager = createContentManager()
                    contentManager.bind(view)
                    contentManager.start()
                updateDetailsUI(dialog.context, dialog)
            }
    }

    /** Unbinds the details view when it goes away. */
    fun unbindDetailsView() {
        cancelJob()
        contentManager.releaseView()
    }

    private suspend fun updateDetailsUI(context: Context, dialog: SystemUIDialog?) {
        coroutineScope {
            var updateDeviceItemJob: Job?
            var updateDialogUiJob: Job? = null

            updateDeviceItemJob = launch {
                deviceItemInteractor.updateDeviceItems(context, DeviceFetchTrigger.FIRST_LOAD)
            }

            // deviceItemUpdate is emitted when device item list is done fetching, update UI and
            // stop the progress bar.
                combine(
                        deviceItemInteractor.deviceItemUpdate,
                        deviceItemInteractor.showSeeAllUpdate,
                    ) { deviceItem, showSeeAll ->
            combine(deviceItemInteractor.deviceItemUpdate, deviceItemInteractor.showSeeAllUpdate) {
                    deviceItem,
                    showSeeAll ->
                    updateDialogUiJob?.cancel()
                    updateDialogUiJob = launch {
                        contentManager.apply {
                            onDeviceItemUpdated(
                                deviceItem,
                                showSeeAll,
                                    showPairNewDevice =
                                        bluetoothStateInteractor.isBluetoothEnabled(),
                                showPairNewDevice = bluetoothStateInteractor.isBluetoothEnabled(),
                            )
                            animateProgressBar(false)
                        }
@@ -217,7 +226,7 @@ constructor(
                                }
                            }
                        }
                            .launchIn(this@launch)
                        .launchIn(this@coroutineScope)
                    launch { activate() }
                }
            }
@@ -264,8 +273,7 @@ constructor(
                        }

                        DeviceItemClick.Target.ACTION_ICON -> {
                                deviceItemActionInteractor.onActionIconClick(it.deviceItem) { intent
                                    ->
                            deviceItemActionInteractor.onActionIconClick(it.deviceItem) { intent ->
                                startSettingsActivity(intent, it.clickedView)
                            }
                        }
@@ -303,7 +311,6 @@ constructor(
                    .onEach { bluetoothAutoOnInteractor.setEnabled(it) }
                    .launchIn(this)
            }

            produce<Unit> { awaitClose { dialog?.cancel() } }
        }
    }
+1 −1
Original line number Diff line number Diff line
@@ -51,7 +51,7 @@ constructor(
            initialUiProperties: BluetoothDetailsContentViewModel.UiProperties,
            cachedContentHeight: Int,
            dialogCallback: BluetoothTileDialogCallback,
            dimissListener: Runnable,
            dismissListener: Runnable,
        ): BluetoothTileDialogDelegate
    }

+1 −3
Original line number Diff line number Diff line
@@ -156,15 +156,13 @@ public class BluetoothTile extends QSTileImpl<BooleanState> {

    private void handleClickEvent(@Nullable Expandable expandable) {
        if (mFeatureFlags.isEnabled(Flags.BLUETOOTH_QS_TILE_DIALOG)) {
            mDetailsContentViewModel.get().showDetailsContent(expandable, /* view= */ null);
            mDetailsContentViewModel.get().showDialog(expandable);
        } else {
            // Secondary clicks are header clicks, just toggle.
            toggleBluetooth();
        }
    }



    @Override
    public Intent getLongClickIntent() {
        return new Intent(Settings.ACTION_BLUETOOTH_SETTINGS);
+48 −13
Original line number Diff line number Diff line
@@ -28,9 +28,11 @@ import com.android.internal.logging.UiEventLogger
import com.android.settingslib.bluetooth.CachedBluetoothDevice
import com.android.settingslib.bluetooth.LocalBluetoothManager
import com.android.settingslib.flags.Flags
import com.android.systemui.Flags.FLAG_QS_TILE_DETAILED_VIEW
import com.android.systemui.SysuiTestCase
import com.android.systemui.animation.DialogTransitionAnimator
import com.android.systemui.animation.Expandable
import com.android.systemui.flags.EnableSceneContainer
import com.android.systemui.kosmos.testDispatcher
import com.android.systemui.kosmos.testScope
import com.android.systemui.plugins.ActivityStarter
@@ -194,9 +196,9 @@ class BluetoothDetailsContentViewModelTest : SysuiTestCase() {
    }

    @Test
    fun testShowDetailsContent_noAnimation() {
    fun testShowDialog_noAnimation() {
        testScope.runTest {
            bluetoothDetailsContentViewModel.showDetailsContent(null, null)
            bluetoothDetailsContentViewModel.showDialog(null)
            runCurrent()

            verify(mDialogTransitionAnimator, never()).show(any(), any(), any())
@@ -204,9 +206,9 @@ class BluetoothDetailsContentViewModelTest : SysuiTestCase() {
    }

    @Test
    fun testShowDetailsContent_animated() {
    fun testShowDialog_animated() {
        testScope.runTest {
            bluetoothDetailsContentViewModel.showDetailsContent(expandable, null)
            bluetoothDetailsContentViewModel.showDialog(expandable)
            runCurrent()

            verify(mDialogTransitionAnimator).show(any(), any(), anyBoolean())
@@ -214,9 +216,11 @@ class BluetoothDetailsContentViewModelTest : SysuiTestCase() {
    }

    @Test
    fun testShowDetailsContent_animated_inDetailsView() {
    @EnableSceneContainer
    @EnableFlags(FLAG_QS_TILE_DETAILED_VIEW)
    fun testBindDetailsView() {
        testScope.runTest {
            bluetoothDetailsContentViewModel.showDetailsContent(expandable, mockView)
            bluetoothDetailsContentViewModel.bindDetailsView(mockView)
            runCurrent()

            verify(bluetoothDetailsContentManager).bind(mockView)
@@ -225,10 +229,10 @@ class BluetoothDetailsContentViewModelTest : SysuiTestCase() {
    }

    @Test
    fun testShowDetailsContent_animated_callInBackgroundThread() {
    fun testShowDialog_animated_callInBackgroundThread() {
        testScope.runTest {
            backgroundExecutor.execute {
                bluetoothDetailsContentViewModel.showDetailsContent(expandable, null)
                bluetoothDetailsContentViewModel.showDialog(expandable)
                runCurrent()

                verify(mDialogTransitionAnimator).show(any(), any(), anyBoolean())
@@ -237,10 +241,12 @@ class BluetoothDetailsContentViewModelTest : SysuiTestCase() {
    }

    @Test
    fun testShowDetailsContent_animated_callInBackgroundThread_inDetailsView() {
    @EnableSceneContainer
    @EnableFlags(FLAG_QS_TILE_DETAILED_VIEW)
    fun testBindDetailsView_callInBackgroundThread() {
        testScope.runTest {
            backgroundExecutor.execute {
                bluetoothDetailsContentViewModel.showDetailsContent(expandable, mockView)
                bluetoothDetailsContentViewModel.bindDetailsView(mockView)
                runCurrent()

                verify(bluetoothDetailsContentManager).bind(mockView)
@@ -250,9 +256,21 @@ class BluetoothDetailsContentViewModelTest : SysuiTestCase() {
    }

    @Test
    fun testShowDetailsContent_fetchDeviceItem() {
    fun testShowDialog_fetchDeviceItem() {
        testScope.runTest {
            bluetoothDetailsContentViewModel.showDetailsContent(null, null)
            bluetoothDetailsContentViewModel.showDialog(null)
            runCurrent()

            verify(deviceItemInteractor).deviceItemUpdate
        }
    }

    @Test
    @EnableSceneContainer
    @EnableFlags(FLAG_QS_TILE_DETAILED_VIEW)
    fun testBindDetailsView_fetchDeviceItem() {
        testScope.runTest {
            bluetoothDetailsContentViewModel.bindDetailsView(mockView)
            runCurrent()

            verify(deviceItemInteractor).deviceItemUpdate
@@ -263,7 +281,24 @@ class BluetoothDetailsContentViewModelTest : SysuiTestCase() {
    fun testStartSettingsActivity_activityLaunched_dialogDismissed() {
        testScope.runTest {
            whenever(deviceItem.cachedBluetoothDevice).thenReturn(cachedBluetoothDevice)
            bluetoothDetailsContentViewModel.showDetailsContent(null, null)
            bluetoothDetailsContentViewModel.showDialog(null)
            runCurrent()

            val clickedView = View(context)
            bluetoothDetailsContentViewModel.onPairNewDeviceClicked(clickedView)

            verify(uiEventLogger).log(BluetoothTileDialogUiEvent.PAIR_NEW_DEVICE_CLICKED)
            verify(activityStarter).postStartActivityDismissingKeyguard(any(), anyInt(), nullable())
        }
    }

    @Test
    @EnableSceneContainer
    @EnableFlags(FLAG_QS_TILE_DETAILED_VIEW)
    fun testStartSettingsActivity_activityLaunched_detailsViewDismissed() {
        testScope.runTest {
            whenever(deviceItem.cachedBluetoothDevice).thenReturn(cachedBluetoothDevice)
            bluetoothDetailsContentViewModel.bindDetailsView(mockView)
            runCurrent()

            val clickedView = View(context)
Loading