1 /* <lambda>null2 * Copyright (C) 2025 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.statusbar.chips.uievents 18 19 import com.android.internal.logging.InstanceId 20 import com.android.internal.logging.InstanceIdSequence 21 import com.android.internal.logging.UiEventLogger 22 import com.android.systemui.dagger.SysUISingleton 23 import com.android.systemui.statusbar.chips.call.ui.viewmodel.CallChipViewModel 24 import com.android.systemui.statusbar.chips.casttootherdevice.ui.viewmodel.CastToOtherDeviceChipViewModel 25 import com.android.systemui.statusbar.chips.screenrecord.ui.viewmodel.ScreenRecordChipViewModel 26 import com.android.systemui.statusbar.chips.sharetoapp.ui.viewmodel.ShareToAppChipViewModel 27 import com.android.systemui.statusbar.pipeline.shared.ui.model.ChipsVisibilityModel 28 import com.android.systemui.util.kotlin.pairwise 29 import javax.inject.Inject 30 import kotlinx.coroutines.coroutineScope 31 import kotlinx.coroutines.flow.Flow 32 import kotlinx.coroutines.flow.distinctUntilChanged 33 import kotlinx.coroutines.flow.map 34 import kotlinx.coroutines.launch 35 36 /** Does all the UiEvent-related logging for the status bar chips. */ 37 @SysUISingleton 38 class StatusBarChipsUiEventLogger @Inject constructor(private val logger: UiEventLogger) { 39 private val instanceIdSequence = InstanceIdSequence(INSTANCE_ID_MAX) 40 41 /** Get a new instance ID for a status bar chip. */ 42 fun createNewInstanceId(): InstanceId { 43 return instanceIdSequence.newInstanceId() 44 } 45 46 /** Logs that the chip with the given ID was tapped to show additional information. */ 47 fun logChipTapToShow(instanceId: InstanceId?) { 48 logger.log(StatusBarChipUiEvent.STATUS_BAR_CHIP_TAP_TO_SHOW, instanceId) 49 } 50 51 /** 52 * Logs that the chip with the given ID was tapped to hide the additional information that was 53 * previously shown. 54 */ 55 fun logChipTapToHide(instanceId: InstanceId?) { 56 logger.log(StatusBarChipUiEvent.STATUS_BAR_CHIP_TAP_TO_HIDE, instanceId) 57 } 58 59 /** Starts UiEvent logging for the chips. */ 60 suspend fun hydrateUiEventLogging(chipsFlow: Flow<ChipsVisibilityModel>) { 61 coroutineScope { 62 launch { 63 chipsFlow 64 .map { it.chips } 65 .distinctUntilChanged() 66 .pairwise() 67 .collect { (old, new) -> 68 val oldActive: Map<String, Pair<InstanceId?, Int>> = 69 old.active.withIndex().associate { 70 it.value.key to Pair(it.value.instanceId, it.index) 71 } 72 val newActive: Map<String, Pair<InstanceId?, Int>> = 73 new.active.withIndex().associate { 74 it.value.key to Pair(it.value.instanceId, it.index) 75 } 76 77 // Newly active keys 78 newActive.keys.minus(oldActive.keys).forEach { key -> 79 val uiEvent = key.getUiEventForNewChip() 80 val instanceId = newActive[key]!!.first 81 val position = newActive[key]!!.second 82 logger.logWithInstanceIdAndPosition( 83 uiEvent, 84 /* uid= */ 0, 85 /* packageName= */ null, 86 instanceId, 87 position, 88 ) 89 } 90 91 // Newly inactive keys 92 oldActive.keys.minus(newActive.keys).forEach { key -> 93 val instanceId = oldActive[key]?.first 94 logger.log(StatusBarChipUiEvent.STATUS_BAR_CHIP_REMOVED, instanceId) 95 } 96 } 97 } 98 } 99 } 100 101 companion object { 102 private const val INSTANCE_ID_MAX = 1 shl 20 103 104 /** 105 * Given a key from an [OngoingActivityChipModel.Active] instance that was just added, 106 * return the right UiEvent type to log. 107 */ 108 private fun String.getUiEventForNewChip(): StatusBarChipUiEvent { 109 return when { 110 this == ScreenRecordChipViewModel.KEY -> 111 StatusBarChipUiEvent.STATUS_BAR_NEW_CHIP_SCREEN_RECORD 112 this == ShareToAppChipViewModel.KEY -> 113 StatusBarChipUiEvent.STATUS_BAR_NEW_CHIP_SHARE_TO_APP 114 this == CastToOtherDeviceChipViewModel.KEY -> 115 StatusBarChipUiEvent.STATUS_BAR_NEW_CHIP_CAST_TO_OTHER_DEVICE 116 this.startsWith(CallChipViewModel.KEY_PREFIX) -> 117 StatusBarChipUiEvent.STATUS_BAR_NEW_CHIP_CALL 118 else -> StatusBarChipUiEvent.STATUS_BAR_NEW_CHIP_NOTIFICATION 119 } 120 } 121 } 122 } 123