1 /*
2  * Copyright (C) 2024 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.systemui.qs.tiles.impl.qr.domain.interactor
18 
19 import android.os.UserHandle
20 import com.android.systemui.dagger.qualifiers.Application
21 import com.android.systemui.dagger.qualifiers.Background
22 import com.android.systemui.qrcodescanner.controller.QRCodeScannerController
23 import com.android.systemui.qrcodescanner.controller.QRCodeScannerController.DEFAULT_QR_CODE_SCANNER_CHANGE
24 import com.android.systemui.qs.tiles.base.interactor.DataUpdateTrigger
25 import com.android.systemui.qs.tiles.base.interactor.QSTileDataInteractor
26 import com.android.systemui.qs.tiles.impl.qr.domain.model.QRCodeScannerTileModel
27 import com.android.systemui.utils.coroutines.flow.conflatedCallbackFlow
28 import javax.inject.Inject
29 import kotlin.coroutines.CoroutineContext
30 import kotlinx.coroutines.CoroutineScope
31 import kotlinx.coroutines.channels.awaitClose
32 import kotlinx.coroutines.flow.Flow
33 import kotlinx.coroutines.flow.SharingStarted
34 import kotlinx.coroutines.flow.flowOf
35 import kotlinx.coroutines.flow.flowOn
36 import kotlinx.coroutines.flow.onStart
37 import kotlinx.coroutines.flow.stateIn
38 
39 /** Observes one qr scanner state changes providing the [QRCodeScannerTileModel]. */
40 class QRCodeScannerTileDataInteractor
41 @Inject
42 constructor(
43     @Background private val bgCoroutineContext: CoroutineContext,
44     @Application private val scope: CoroutineScope,
45     private val qrController: QRCodeScannerController,
46 ) : QSTileDataInteractor<QRCodeScannerTileModel> {
tileDatanull47     override fun tileData(
48         user: UserHandle,
49         triggers: Flow<DataUpdateTrigger>
50     ): Flow<QRCodeScannerTileModel> =
51         conflatedCallbackFlow {
52                 qrController.registerQRCodeScannerChangeObservers(DEFAULT_QR_CODE_SCANNER_CHANGE)
53                 val callback =
54                     object : QRCodeScannerController.Callback {
55                         override fun onQRCodeScannerActivityChanged() {
56                             trySend(generateModel())
57                         }
58                     }
59                 qrController.addCallback(callback)
60                 awaitClose {
61                     qrController.removeCallback(callback)
62                     qrController.unregisterQRCodeScannerChangeObservers(
63                         DEFAULT_QR_CODE_SCANNER_CHANGE
64                     )
65                 }
66             }
<lambda>null67             .onStart { emit(generateModel()) }
68             .flowOn(bgCoroutineContext)
69             .stateIn(
70                 scope,
71                 SharingStarted.WhileSubscribed(),
72                 QRCodeScannerTileModel.TemporarilyUnavailable
73             )
74 
availabilitynull75     override fun availability(user: UserHandle): Flow<Boolean> =
76         flowOf(qrController.isCameraAvailable)
77 
78     private fun generateModel(): QRCodeScannerTileModel {
79         val intent = qrController.intent
80 
81         return if (qrController.isAbleToLaunchScannerActivity && intent != null)
82             QRCodeScannerTileModel.Available(intent)
83         else QRCodeScannerTileModel.TemporarilyUnavailable
84     }
85 }
86