diff options
16 files changed, 749 insertions, 5 deletions
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/base/actions/QSTileIntentUserInputHandlerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/base/actions/QSTileIntentUserInputHandlerTest.kt index bf48784407b8..02a81419ea78 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/base/actions/QSTileIntentUserInputHandlerTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/base/actions/QSTileIntentUserInputHandlerTest.kt @@ -69,6 +69,15 @@ class QSTileIntentUserInputHandlerTest : SysuiTestCase() { } @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) } diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/qr/domain/interactor/QRCodeScannerTileDataInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/qr/domain/interactor/QRCodeScannerTileDataInteractorTest.kt new file mode 100644 index 000000000000..c41ce2f7854c --- /dev/null +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/qr/domain/interactor/QRCodeScannerTileDataInteractorTest.kt @@ -0,0 +1,112 @@ +/* + * 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) + } +} diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/qr/domain/interactor/QRCodeScannerTileUserActionInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/qr/domain/interactor/QRCodeScannerTileUserActionInteractorTest.kt new file mode 100644 index 000000000000..312f18029570 --- /dev/null +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/qr/domain/interactor/QRCodeScannerTileUserActionInteractorTest.kt @@ -0,0 +1,81 @@ +/* + * 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() + } +} diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/qr/ui/QRCodeScannerTileMapperTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/qr/ui/QRCodeScannerTileMapperTest.kt new file mode 100644 index 000000000000..d26a21365f54 --- /dev/null +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/qr/ui/QRCodeScannerTileMapperTest.kt @@ -0,0 +1,114 @@ +/* + * 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 + ) + } +} diff --git a/packages/SystemUI/src/com/android/systemui/qrcodescanner/dagger/QRCodeScannerModule.kt b/packages/SystemUI/src/com/android/systemui/qrcodescanner/dagger/QRCodeScannerModule.kt index 3907a7240258..5e6ee4d3c700 100644 --- a/packages/SystemUI/src/com/android/systemui/qrcodescanner/dagger/QRCodeScannerModule.kt +++ b/packages/SystemUI/src/com/android/systemui/qrcodescanner/dagger/QRCodeScannerModule.kt @@ -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 } } diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/base/actions/QSTileIntentUserInputHandler.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/base/actions/QSTileIntentUserInputHandler.kt index 2d3120a1dcce..972b20e138d9 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/base/actions/QSTileIntentUserInputHandler.kt +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/base/actions/QSTileIntentUserInputHandler.kt @@ -29,11 +29,15 @@ import javax.inject.Inject /** * Provides a shortcut to start an activity from [QSTileUserActionInteractor]. It supports keyguard - * dismissing and tile from-view animations. + * dismissing and tile from-view animations, as well as the option to show over lockscreen. */ interface QSTileIntentUserInputHandler { - fun handle(expandable: Expandable?, intent: Intent) + fun handle( + expandable: Expandable?, + intent: Intent, + dismissShadeShowOverLockScreenWhenLocked: Boolean = false + ) /** @param requestLaunchingDefaultActivity used in case !pendingIndent.isActivity */ fun handle( @@ -52,12 +56,25 @@ constructor( private val userHandle: UserHandle, ) : QSTileIntentUserInputHandler { - override fun handle(expandable: Expandable?, intent: Intent) { + override fun handle( + expandable: Expandable?, + intent: Intent, + dismissShadeShowOverLockScreenWhenLocked: Boolean + ) { val animationController: ActivityTransitionAnimator.Controller? = expandable?.activityTransitionController( InteractionJankMonitor.CUJ_SHADE_APP_LAUNCH_FROM_QS_TILE ) - activityStarter.postStartActivityDismissingKeyguard(intent, 0, animationController) + if (dismissShadeShowOverLockScreenWhenLocked) { + activityStarter.startActivity( + intent, + true /* dismissShade */, + animationController, + true /* showOverLockscreenWhenLocked */ + ) + } else { + activityStarter.postStartActivityDismissingKeyguard(intent, 0, animationController) + } } // TODO(b/249804373): make sure to allow showing activities over the lockscreen. See b/292112939 diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/qr/domain/interactor/QRCodeScannerTileDataInteractor.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/qr/domain/interactor/QRCodeScannerTileDataInteractor.kt new file mode 100644 index 000000000000..1e8ce588b4e0 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/qr/domain/interactor/QRCodeScannerTileDataInteractor.kt @@ -0,0 +1,85 @@ +/* + * 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.os.UserHandle +import com.android.systemui.dagger.qualifiers.Application +import com.android.systemui.dagger.qualifiers.Background +import com.android.systemui.qrcodescanner.controller.QRCodeScannerController +import com.android.systemui.qrcodescanner.controller.QRCodeScannerController.DEFAULT_QR_CODE_SCANNER_CHANGE +import com.android.systemui.qs.tiles.base.interactor.DataUpdateTrigger +import com.android.systemui.qs.tiles.base.interactor.QSTileDataInteractor +import com.android.systemui.qs.tiles.impl.qr.domain.model.QRCodeScannerTileModel +import com.android.systemui.utils.coroutines.flow.conflatedCallbackFlow +import javax.inject.Inject +import kotlin.coroutines.CoroutineContext +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.channels.awaitClose +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.SharingStarted +import kotlinx.coroutines.flow.flowOf +import kotlinx.coroutines.flow.flowOn +import kotlinx.coroutines.flow.onStart +import kotlinx.coroutines.flow.stateIn + +/** Observes one qr scanner state changes providing the [QRCodeScannerTileModel]. */ +class QRCodeScannerTileDataInteractor +@Inject +constructor( + @Background private val bgCoroutineContext: CoroutineContext, + @Application private val scope: CoroutineScope, + private val qrController: QRCodeScannerController, +) : QSTileDataInteractor<QRCodeScannerTileModel> { + override fun tileData( + user: UserHandle, + triggers: Flow<DataUpdateTrigger> + ): Flow<QRCodeScannerTileModel> = + conflatedCallbackFlow { + qrController.registerQRCodeScannerChangeObservers(DEFAULT_QR_CODE_SCANNER_CHANGE) + val callback = + object : QRCodeScannerController.Callback { + override fun onQRCodeScannerActivityChanged() { + trySend(generateModel()) + } + } + qrController.addCallback(callback) + awaitClose { + qrController.removeCallback(callback) + qrController.unregisterQRCodeScannerChangeObservers( + DEFAULT_QR_CODE_SCANNER_CHANGE + ) + } + } + .onStart { emit(generateModel()) } + .flowOn(bgCoroutineContext) + .stateIn( + scope, + SharingStarted.WhileSubscribed(), + QRCodeScannerTileModel.TemporarilyUnavailable + ) + + override fun availability(user: UserHandle): Flow<Boolean> = + flowOf(qrController.isCameraAvailable) + + private fun generateModel(): QRCodeScannerTileModel { + val intent = qrController.intent + + return if (qrController.isAbleToLaunchScannerActivity && intent != null) + QRCodeScannerTileModel.Available(intent) + else QRCodeScannerTileModel.TemporarilyUnavailable + } +} diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/qr/domain/interactor/QRCodeScannerTileUserActionInteractor.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/qr/domain/interactor/QRCodeScannerTileUserActionInteractor.kt new file mode 100644 index 000000000000..7c0c41eca4bc --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/qr/domain/interactor/QRCodeScannerTileUserActionInteractor.kt @@ -0,0 +1,50 @@ +/* + * 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 com.android.systemui.qs.tiles.base.actions.QSTileIntentUserInputHandler +import com.android.systemui.qs.tiles.base.interactor.QSTileInput +import com.android.systemui.qs.tiles.base.interactor.QSTileUserActionInteractor +import com.android.systemui.qs.tiles.impl.qr.domain.model.QRCodeScannerTileModel +import com.android.systemui.qs.tiles.viewmodel.QSTileUserAction +import javax.inject.Inject + +/** Handles qr tile clicks. */ +class QRCodeScannerTileUserActionInteractor +@Inject +constructor( + private val qsTileIntentUserActionHandler: QSTileIntentUserInputHandler, +) : QSTileUserActionInteractor<QRCodeScannerTileModel> { + + override suspend fun handleInput(input: QSTileInput<QRCodeScannerTileModel>): Unit = + with(input) { + when (action) { + is QSTileUserAction.Click -> { + when (data) { + is QRCodeScannerTileModel.Available -> + qsTileIntentUserActionHandler.handle( + action.expandable, + data.intent, + true + ) + is QRCodeScannerTileModel.TemporarilyUnavailable -> {} // no-op + } + } + is QSTileUserAction.LongClick -> {} // no-op + } + } +} diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/qr/domain/model/QRCodeScannerTileModel.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/qr/domain/model/QRCodeScannerTileModel.kt new file mode 100644 index 000000000000..22c9b66b2806 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/qr/domain/model/QRCodeScannerTileModel.kt @@ -0,0 +1,25 @@ +/* + * 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.model + +import android.content.Intent + +/** qr scanner tile model. */ +sealed interface QRCodeScannerTileModel { + data class Available(val intent: Intent) : QRCodeScannerTileModel + data object TemporarilyUnavailable : QRCodeScannerTileModel +} diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/qr/ui/QRCodeScannerTileMapper.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/qr/ui/QRCodeScannerTileMapper.kt new file mode 100644 index 000000000000..45a77179fb3f --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/qr/ui/QRCodeScannerTileMapper.kt @@ -0,0 +1,59 @@ +/* + * 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.res.Resources +import com.android.systemui.common.shared.model.Icon +import com.android.systemui.dagger.qualifiers.Main +import com.android.systemui.qs.tiles.base.interactor.QSTileDataToStateMapper +import com.android.systemui.qs.tiles.impl.qr.domain.model.QRCodeScannerTileModel +import com.android.systemui.qs.tiles.viewmodel.QSTileConfig +import com.android.systemui.qs.tiles.viewmodel.QSTileState +import com.android.systemui.res.R +import javax.inject.Inject + +/** Maps [QRCodeScannerTileModel] to [QSTileState]. */ +class QRCodeScannerTileMapper +@Inject +constructor( + @Main private val resources: Resources, + private val theme: Resources.Theme, +) : QSTileDataToStateMapper<QRCodeScannerTileModel> { + + override fun map(config: QSTileConfig, data: QRCodeScannerTileModel): QSTileState = + QSTileState.build(resources, theme, config.uiConfig) { + label = resources.getString(R.string.qr_code_scanner_title) + contentDescription = label + icon = { + Icon.Loaded(resources.getDrawable(R.drawable.ic_qr_code_scanner, theme), null) + } + sideViewIcon = QSTileState.SideViewIcon.Chevron + supportedActions = setOf(QSTileState.UserAction.CLICK) + + when (data) { + is QRCodeScannerTileModel.Available -> { + activationState = QSTileState.ActivationState.INACTIVE + secondaryLabel = null + } + is QRCodeScannerTileModel.TemporarilyUnavailable -> { + activationState = QSTileState.ActivationState.UNAVAILABLE + secondaryLabel = + resources.getString(R.string.qr_code_scanner_updating_secondary_label) + } + } + } +} diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qrcodescanner/QRCodeScannerControllerKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/qrcodescanner/QRCodeScannerControllerKosmos.kt new file mode 100644 index 000000000000..8ad6087cfe82 --- /dev/null +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/qrcodescanner/QRCodeScannerControllerKosmos.kt @@ -0,0 +1,23 @@ +/* + * 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.qrcodescanner + +import com.android.systemui.kosmos.Kosmos +import com.android.systemui.qrcodescanner.controller.QRCodeScannerController +import com.android.systemui.util.mockito.mock + +val Kosmos.qrCodeScannerController by Kosmos.Fixture { mock<QRCodeScannerController>() } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/base/actions/FakeQSTileIntentUserInputHandler.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/base/actions/FakeQSTileIntentUserInputHandler.kt index c4bf8ff12817..f50443ec4e86 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/base/actions/FakeQSTileIntentUserInputHandler.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/base/actions/FakeQSTileIntentUserInputHandler.kt @@ -31,7 +31,11 @@ class FakeQSTileIntentUserInputHandler : QSTileIntentUserInputHandler { private val mutableInputs = mutableListOf<Input>() - override fun handle(expandable: Expandable?, intent: Intent) { + override fun handle( + expandable: Expandable?, + intent: Intent, + handleDismissShadeShowOverLockScreenWhenLocked: Boolean + ) { mutableInputs.add(Input.Intent(expandable, intent)) } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/base/actions/QSTileIntentUserInputHandlerKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/base/actions/QSTileIntentUserInputHandlerKosmos.kt new file mode 100644 index 000000000000..ccfb6092a2e3 --- /dev/null +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/base/actions/QSTileIntentUserInputHandlerKosmos.kt @@ -0,0 +1,21 @@ +/* + * 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.base.actions + +import com.android.systemui.kosmos.Kosmos + +val Kosmos.qsTileIntentUserInputHandler by Kosmos.Fixture { FakeQSTileIntentUserInputHandler() } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/base/analytics/QSTileAnalyticsKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/base/analytics/QSTileAnalyticsKosmos.kt new file mode 100644 index 000000000000..146c1ad6ab70 --- /dev/null +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/base/analytics/QSTileAnalyticsKosmos.kt @@ -0,0 +1,22 @@ +/* + * 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.base.analytics + +import com.android.systemui.kosmos.Kosmos +import com.android.systemui.util.mockito.mock + +val Kosmos.qsTileAnalytics by Kosmos.Fixture { mock<QSTileAnalytics>() } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/base/interactor/DisabledByPolicyInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/base/interactor/DisabledByPolicyInteractorKosmos.kt new file mode 100644 index 000000000000..9ad49f052c9e --- /dev/null +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/base/interactor/DisabledByPolicyInteractorKosmos.kt @@ -0,0 +1,21 @@ +/* + * 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.base.interactor + +import com.android.systemui.kosmos.Kosmos + +val Kosmos.fakeDisabledByPolicyInteractor by Kosmos.Fixture { FakeDisabledByPolicyInteractor() } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/impl/qr/QRCodeScannerTileKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/impl/qr/QRCodeScannerTileKosmos.kt new file mode 100644 index 000000000000..dcfcce77942e --- /dev/null +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/impl/qr/QRCodeScannerTileKosmos.kt @@ -0,0 +1,74 @@ +/* + * 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 + +import android.content.res.mainResources +import com.android.systemui.classifier.fakeFalsingManager +import com.android.systemui.kosmos.Kosmos +import com.android.systemui.kosmos.applicationCoroutineScope +import com.android.systemui.kosmos.backgroundCoroutineContext +import com.android.systemui.kosmos.testDispatcher +import com.android.systemui.kosmos.testScope +import com.android.systemui.qrcodescanner.dagger.QRCodeScannerModule +import com.android.systemui.qrcodescanner.qrCodeScannerController +import com.android.systemui.qs.qsEventLogger +import com.android.systemui.qs.tiles.base.actions.qsTileIntentUserInputHandler +import com.android.systemui.qs.tiles.base.analytics.qsTileAnalytics +import com.android.systemui.qs.tiles.base.interactor.fakeDisabledByPolicyInteractor +import com.android.systemui.qs.tiles.base.viewmodel.QSTileViewModelImpl +import com.android.systemui.qs.tiles.impl.custom.qsTileLogger +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.ui.QRCodeScannerTileMapper +import com.android.systemui.user.data.repository.fakeUserRepository +import com.android.systemui.util.time.systemClock + +val Kosmos.qsQRCodeScannerTileConfig by + Kosmos.Fixture { QRCodeScannerModule.provideQRCodeScannerTileConfig(qsEventLogger) } + +val Kosmos.qrCodeScannerTileDataInteractor by + Kosmos.Fixture { + QRCodeScannerTileDataInteractor( + backgroundCoroutineContext, + applicationCoroutineScope, + qrCodeScannerController + ) + } + +val Kosmos.qrCodeScannerTileUserActionInteractor by + Kosmos.Fixture { QRCodeScannerTileUserActionInteractor(qsTileIntentUserInputHandler) } + +val Kosmos.qrCodeScannerTileMapper by + Kosmos.Fixture { QRCodeScannerTileMapper(mainResources, mainResources.newTheme()) } + +val Kosmos.qsQRCodeScannerViewModel by + Kosmos.Fixture { + QSTileViewModelImpl( + qsQRCodeScannerTileConfig, + { qrCodeScannerTileUserActionInteractor }, + { qrCodeScannerTileDataInteractor }, + { qrCodeScannerTileMapper }, + fakeDisabledByPolicyInteractor, + fakeUserRepository, + fakeFalsingManager, + qsTileAnalytics, + qsTileLogger, + systemClock, + testDispatcher, + testScope.backgroundScope, + ) + } |