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

Commit 26b19452 authored by Behnam Heydarshahi's avatar Behnam Heydarshahi Committed by Android (Google) Code Review
Browse files

Merge "Migrate QRCodeScannerTile" into main

parents 68a457d6 04245898
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