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