• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
<lambda>null2  * 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.communal.data.repository
18 
19 import android.app.smartspace.SmartspaceTarget
20 import android.os.Parcelable
21 import androidx.annotation.VisibleForTesting
22 import com.android.systemui.Flags.communalTimerFlickerFix
23 import com.android.systemui.communal.data.model.CommunalSmartspaceTimer
24 import com.android.systemui.communal.smartspace.CommunalSmartspaceController
25 import com.android.systemui.dagger.SysUISingleton
26 import com.android.systemui.dagger.qualifiers.Main
27 import com.android.systemui.log.LogBuffer
28 import com.android.systemui.log.core.Logger
29 import com.android.systemui.log.dagger.CommunalLog
30 import com.android.systemui.plugins.BcSmartspaceDataPlugin
31 import com.android.systemui.util.time.SystemClock
32 import java.util.concurrent.Executor
33 import javax.inject.Inject
34 import kotlinx.coroutines.flow.Flow
35 import kotlinx.coroutines.flow.MutableStateFlow
36 
37 interface CommunalSmartspaceRepository {
38     /** Smartspace timer targets for the communal surface. */
39     val timers: Flow<List<CommunalSmartspaceTimer>>
40 
41     /** Start listening for smartspace updates. */
42     fun startListening()
43 
44     /** Stop listening for smartspace updates. */
45     fun stopListening()
46 }
47 
48 @SysUISingleton
49 class CommunalSmartspaceRepositoryImpl
50 @Inject
51 constructor(
52     private val communalSmartspaceController: CommunalSmartspaceController,
53     @Main private val uiExecutor: Executor,
54     private val systemClock: SystemClock,
55     @CommunalLog logBuffer: LogBuffer,
56 ) : CommunalSmartspaceRepository, BcSmartspaceDataPlugin.SmartspaceTargetListener {
57 
58     private val logger = Logger(logBuffer, "CommunalSmartspaceRepository")
59 
60     private val _timers: MutableStateFlow<List<CommunalSmartspaceTimer>> =
61         MutableStateFlow(emptyList())
62     override val timers: Flow<List<CommunalSmartspaceTimer>> = _timers
63 
64     private var targetCreationTimes = emptyMap<String, Long>()
65 
onSmartspaceTargetsUpdatednull66     override fun onSmartspaceTargetsUpdated(targetsNullable: MutableList<out Parcelable>?) {
67         val targets = targetsNullable?.filterIsInstance<SmartspaceTarget>() ?: emptyList()
68         val timerTargets =
69             targets
70                 .filter { target ->
71                     target.featureType == SmartspaceTarget.FEATURE_TIMER &&
72                         target.remoteViews != null
73                 }
74                 .associateBy { stableId(it.smartspaceTargetId) }
75 
76         // The creation times from smartspace targets are unreliable (b/318535930). Therefore,
77         // SystemUI uses the timestamp of which a timer first appears, and caches these values to
78         // prevent timers from swapping positions in the hub.
79         targetCreationTimes =
80             timerTargets.mapValues { (stableId, _) ->
81                 targetCreationTimes[stableId] ?: systemClock.currentTimeMillis()
82             }
83 
84         _timers.value =
85             timerTargets
86                 .map { (stableId, target) ->
87                     CommunalSmartspaceTimer(
88                         // The view layer should have the instance based smartspaceTargetId instead
89                         // of stable id, so that when a new instance of the timer is created, for
90                         // example, when it is paused, the view should re-render its remote views.
91                         smartspaceTargetId =
92                             if (communalTimerFlickerFix()) stableId else target.smartspaceTargetId,
93                         createdTimestampMillis = targetCreationTimes[stableId]!!,
94                         remoteViews = target.remoteViews!!,
95                     )
96                 }
97                 .also { newVal ->
98                     // Only log when value changes to avoid filling up the buffer.
99                     if (newVal != _timers.value) {
100                         logger.d({ "Smartspace timers updated: $str1" }) {
101                             str1 = newVal.toString()
102                         }
103                     }
104                 }
105     }
106 
startListeningnull107     override fun startListening() {
108         if (android.app.smartspace.flags.Flags.remoteViews()) {
109             uiExecutor.execute {
110                 communalSmartspaceController.addListener(
111                     listener = this@CommunalSmartspaceRepositoryImpl
112                 )
113             }
114         }
115     }
116 
stopListeningnull117     override fun stopListening() {
118         uiExecutor.execute {
119             communalSmartspaceController.removeListener(
120                 listener = this@CommunalSmartspaceRepositoryImpl
121             )
122         }
123     }
124 
125     companion object {
126         /**
127          * The smartspace target id is instance-based, meaning a single timer (from the user's
128          * perspective) can have multiple instances. For example, when a timer is paused, a new
129          * instance is created. To address this, SystemUI manually removes the instance id to
130          * maintain a consistent id across sessions.
131          *
132          * It is assumed that timer target ids follow this format: timer-${stableId}-${instanceId}.
133          * This function returns timer-${stableId}, stripping out the instance id.
134          */
135         @VisibleForTesting
stableIdnull136         fun stableId(targetId: String): String {
137             return targetId.split("-").take(2).joinToString("-")
138         }
139     }
140 }
141