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

Commit 04245898 authored by Behnam Heydarshahi's avatar Behnam Heydarshahi
Browse files

Migrate QRCodeScannerTile

Bug: 301056111
Flag: ACONFIG com.android.systemui.qs_new_tiles_future DEVELOPMENT
Test: atest QRCodeScannerTileDataInteractorTest
QRCodeScannerTileDataInteractorTest QRCodeScannerTileMapperTest

Change-Id: I2bf1213d049f60aa3e60baeb1c154e7defd881db
parent 325f6d65
Loading
Loading
Loading
Loading
+9 −0
Original line number Diff line number Diff line
@@ -68,6 +68,15 @@ class QSTileIntentUserInputHandlerTest : SysuiTestCase() {
        verify(activityStarter).postStartActivityDismissingKeyguard(eq(intent), eq(0), any())
    }

    @Test
    fun testPassesIntentToStarter_dismissShadeAndShowOverLockScreenWhenLocked() {
        val intent = Intent("test.ACTION")

        underTest.handle(null, intent, true)

        verify(activityStarter).startActivity(eq(intent), eq(true), any(), eq(true))
    }

    @Test
    fun testPassesActivityPendingIntentToStarterAsPendingIntent() {
        val pendingIntent = mock<PendingIntent> { whenever(isActivity).thenReturn(true) }
+112 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2024 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.
 */

package com.android.systemui.qs.tiles.impl.qr.domain.interactor

import android.content.Intent
import android.os.UserHandle
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.qrcodescanner.controller.QRCodeScannerController
import com.android.systemui.qrcodescanner.controller.QRCodeScannerController.Callback
import com.android.systemui.qs.tiles.base.interactor.DataUpdateTrigger
import com.android.systemui.qs.tiles.impl.qr.domain.model.QRCodeScannerTileModel
import com.android.systemui.util.mockito.argumentCaptor
import com.android.systemui.util.mockito.mock
import com.android.systemui.util.mockito.whenever
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.flow.flowOf
import kotlinx.coroutines.test.StandardTestDispatcher
import kotlinx.coroutines.test.TestScope
import kotlinx.coroutines.test.runCurrent
import kotlinx.coroutines.test.runTest
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.Mockito.verify

@SmallTest
@RunWith(AndroidJUnit4::class)
class QRCodeScannerTileDataInteractorTest : SysuiTestCase() {

    private val testUser = UserHandle.of(1)!!
    private val testDispatcher = StandardTestDispatcher()
    private val scope = TestScope(testDispatcher)
    private val testIntent = mock<Intent>()
    private val qrCodeScannerController =
        mock<QRCodeScannerController> {
            whenever(intent).thenReturn(testIntent)
            whenever(isAbleToLaunchScannerActivity).thenReturn(false)
        }
    private val testAvailableModel = QRCodeScannerTileModel.Available(testIntent)
    private val testUnavailableModel = QRCodeScannerTileModel.TemporarilyUnavailable

    private val underTest: QRCodeScannerTileDataInteractor =
        QRCodeScannerTileDataInteractor(
            testDispatcher,
            scope.backgroundScope,
            qrCodeScannerController,
        )

    @Test
    fun availability_matchesController_cameraNotAvailable() =
        scope.runTest {
            val expectedAvailability = false
            whenever(qrCodeScannerController.isCameraAvailable).thenReturn(false)

            val availability by collectLastValue(underTest.availability(testUser))

            assertThat(availability).isEqualTo(expectedAvailability)
        }

    @Test
    fun availability_matchesController_cameraIsAvailable() =
        scope.runTest {
            val expectedAvailability = true
            whenever(qrCodeScannerController.isCameraAvailable).thenReturn(true)

            val availability by collectLastValue(underTest.availability(testUser))

            assertThat(availability).isEqualTo(expectedAvailability)
        }

    @Test
    fun data_matchesController() =
        scope.runTest {
            val captor = argumentCaptor<Callback>()
            val lastData by
                collectLastValue(
                    underTest.tileData(testUser, flowOf(DataUpdateTrigger.InitialRequest))
                )
            runCurrent()

            verify(qrCodeScannerController).addCallback(captor.capture())
            val callback = captor.value

            assertThat(lastData!!).isEqualTo(testUnavailableModel)

            whenever(qrCodeScannerController.isAbleToLaunchScannerActivity).thenReturn(true)
            callback.onQRCodeScannerActivityChanged()
            runCurrent()
            assertThat(lastData!!).isEqualTo(testAvailableModel)

            whenever(qrCodeScannerController.isAbleToLaunchScannerActivity).thenReturn(false)
            callback.onQRCodeScannerActivityChanged()
            runCurrent()
            assertThat(lastData!!).isEqualTo(testUnavailableModel)
        }
}
+81 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2024 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.
 */

package com.android.systemui.qs.tiles.impl.qr.domain.interactor

import android.content.Intent
import android.platform.test.annotations.EnabledOnRavenwood
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.kosmos.Kosmos
import com.android.systemui.qs.tiles.base.actions.QSTileIntentUserInputHandlerSubject
import com.android.systemui.qs.tiles.base.actions.qsTileIntentUserInputHandler
import com.android.systemui.qs.tiles.base.interactor.QSTileInputTestKtx
import com.android.systemui.qs.tiles.impl.qr.domain.model.QRCodeScannerTileModel
import com.android.systemui.qs.tiles.impl.qr.qrCodeScannerTileUserActionInteractor
import com.android.systemui.util.mockito.mock
import kotlinx.coroutines.test.runTest
import org.junit.Test
import org.junit.runner.RunWith

@SmallTest
@EnabledOnRavenwood
@RunWith(AndroidJUnit4::class)
class QRCodeScannerTileUserActionInteractorTest : SysuiTestCase() {
    val kosmos = Kosmos()
    private val inputHandler = kosmos.qsTileIntentUserInputHandler
    private val underTest = kosmos.qrCodeScannerTileUserActionInteractor
    private val intent = mock<Intent>()

    @Test
    fun handleClick_available() = runTest {
        val inputModel = QRCodeScannerTileModel.Available(intent)

        underTest.handleInput(QSTileInputTestKtx.click(inputModel))

        QSTileIntentUserInputHandlerSubject.assertThat(inputHandler).handledOneIntentInput {
            intent
        }
    }

    @Test
    fun handleClick_temporarilyUnavailable() = runTest {
        val inputModel = QRCodeScannerTileModel.TemporarilyUnavailable

        underTest.handleInput(QSTileInputTestKtx.click(inputModel))

        QSTileIntentUserInputHandlerSubject.assertThat(inputHandler).handledNoInputs()
    }

    @Test
    fun handleLongClick_available() = runTest {
        val inputModel = QRCodeScannerTileModel.Available(intent)

        underTest.handleInput(QSTileInputTestKtx.longClick(inputModel))

        QSTileIntentUserInputHandlerSubject.assertThat(inputHandler).handledNoInputs()
    }

    @Test
    fun handleLongClick_temporarilyUnavailable() = runTest {
        val inputModel = QRCodeScannerTileModel.TemporarilyUnavailable

        underTest.handleInput(QSTileInputTestKtx.longClick(inputModel))

        QSTileIntentUserInputHandlerSubject.assertThat(inputHandler).handledNoInputs()
    }
}
+114 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2024 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.
 */

package com.android.systemui.qs.tiles.impl.qr.ui

import android.content.Intent
import android.graphics.drawable.TestStubDrawable
import android.widget.Switch
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.common.shared.model.Icon
import com.android.systemui.kosmos.Kosmos
import com.android.systemui.qs.tiles.impl.custom.QSTileStateSubject
import com.android.systemui.qs.tiles.impl.qr.domain.model.QRCodeScannerTileModel
import com.android.systemui.qs.tiles.impl.qr.qsQRCodeScannerTileConfig
import com.android.systemui.qs.tiles.viewmodel.QSTileState
import com.android.systemui.util.mockito.mock
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith

@SmallTest
@RunWith(AndroidJUnit4::class)
class QRCodeScannerTileMapperTest : SysuiTestCase() {
    private val kosmos = Kosmos()
    private val config = kosmos.qsQRCodeScannerTileConfig

    private lateinit var mapper: QRCodeScannerTileMapper

    @Before
    fun setup() {
        mapper =
            QRCodeScannerTileMapper(
                context.orCreateTestableResources
                    .apply {
                        addOverride(
                            com.android.systemui.res.R.drawable.ic_qr_code_scanner,
                            TestStubDrawable()
                        )
                    }
                    .resources,
                context.theme
            )
    }

    @Test
    fun availableModel() {
        val mockIntent = mock<Intent>()
        val inputModel = QRCodeScannerTileModel.Available(mockIntent)

        val outputState = mapper.map(config, inputModel)

        val expectedState =
            createQRCodeScannerTileState(
                QSTileState.ActivationState.INACTIVE,
                null,
            )
        QSTileStateSubject.assertThat(outputState).isEqualTo(expectedState)
    }

    @Test
    fun temporarilyUnavailableModel() {
        val inputModel = QRCodeScannerTileModel.TemporarilyUnavailable

        val outputState = mapper.map(config, inputModel)

        val expectedState =
            createQRCodeScannerTileState(
                QSTileState.ActivationState.UNAVAILABLE,
                context.getString(
                    com.android.systemui.res.R.string.qr_code_scanner_updating_secondary_label
                )
            )
        QSTileStateSubject.assertThat(outputState).isEqualTo(expectedState)
    }

    private fun createQRCodeScannerTileState(
        activationState: QSTileState.ActivationState,
        secondaryLabel: String?,
    ): QSTileState {
        val label = context.getString(com.android.systemui.res.R.string.qr_code_scanner_title)
        return QSTileState(
            {
                Icon.Loaded(
                    context.getDrawable(com.android.systemui.res.R.drawable.ic_qr_code_scanner)!!,
                    null
                )
            },
            label,
            activationState,
            secondaryLabel,
            setOf(QSTileState.UserAction.CLICK),
            label,
            null,
            QSTileState.SideViewIcon.Chevron,
            QSTileState.EnabledState.ENABLED,
            Switch::class.qualifiedName
        )
    }
}
+27 −0
Original line number Diff line number Diff line
@@ -16,12 +16,20 @@

package com.android.systemui.qrcodescanner.dagger

import com.android.systemui.Flags
import com.android.systemui.qs.QsEventLogger
import com.android.systemui.qs.pipeline.shared.TileSpec
import com.android.systemui.qs.tileimpl.QSTileImpl
import com.android.systemui.qs.tiles.QRCodeScannerTile
import com.android.systemui.qs.tiles.base.viewmodel.QSTileViewModelFactory
import com.android.systemui.qs.tiles.impl.qr.domain.interactor.QRCodeScannerTileDataInteractor
import com.android.systemui.qs.tiles.impl.qr.domain.interactor.QRCodeScannerTileUserActionInteractor
import com.android.systemui.qs.tiles.impl.qr.domain.model.QRCodeScannerTileModel
import com.android.systemui.qs.tiles.impl.qr.ui.QRCodeScannerTileMapper
import com.android.systemui.qs.tiles.viewmodel.QSTileConfig
import com.android.systemui.qs.tiles.viewmodel.QSTileUIConfig
import com.android.systemui.qs.tiles.viewmodel.QSTileViewModel
import com.android.systemui.qs.tiles.viewmodel.StubQSTileViewModel
import com.android.systemui.res.R
import dagger.Binds
import dagger.Module
@@ -54,5 +62,24 @@ interface QRCodeScannerModule {
                    ),
                instanceId = uiEventLogger.getNewInstanceId(),
            )

        /** Inject QR Code Scanner Tile into tileViewModelMap in QSModule. */
        @Provides
        @IntoMap
        @StringKey(QR_CODE_SCANNER_TILE_SPEC)
        fun provideQRCodeScannerTileViewModel(
            factory: QSTileViewModelFactory.Static<QRCodeScannerTileModel>,
            mapper: QRCodeScannerTileMapper,
            stateInteractor: QRCodeScannerTileDataInteractor,
            userActionInteractor: QRCodeScannerTileUserActionInteractor
        ): QSTileViewModel =
            if (Flags.qsNewTilesFuture())
                factory.create(
                    TileSpec.create(QR_CODE_SCANNER_TILE_SPEC),
                    userActionInteractor,
                    stateInteractor,
                    mapper,
                )
            else StubQSTileViewModel
    }
}
Loading