Loading packages/SystemUI/multivalentTests/src/com/android/systemui/ambientcue/data/repository/AmbientCueRepositoryTest.kt +62 −0 Original line number Diff line number Diff line Loading @@ -25,6 +25,7 @@ import android.app.smartspace.SmartspaceSession import android.app.smartspace.SmartspaceSession.OnTargetsAvailableListener import android.app.smartspace.SmartspaceTarget import android.content.Intent import android.content.applicationContext import android.content.testableContext import android.os.Binder import android.os.Bundle Loading @@ -36,11 +37,15 @@ import androidx.test.filters.SmallTest import com.android.systemui.LauncherProxyService import com.android.systemui.LauncherProxyService.LauncherProxyListener import com.android.systemui.SysuiTestCase import com.android.systemui.ambientcue.data.logger.ambientCueLogger import com.android.systemui.ambientcue.data.repository.AmbientCueRepositoryImpl.Companion.AMBIENT_CUE_SURFACE import com.android.systemui.ambientcue.data.repository.AmbientCueRepositoryImpl.Companion.DEBOUNCE_DELAY_MS import com.android.systemui.ambientcue.data.repository.AmbientCueRepositoryImpl.Companion.EXTRA_ACTION_TYPE import com.android.systemui.ambientcue.data.repository.AmbientCueRepositoryImpl.Companion.EXTRA_ACTIVITY_ID import com.android.systemui.ambientcue.data.repository.AmbientCueRepositoryImpl.Companion.EXTRA_ATTRIBUTION_DIALOG_PENDING_INTENT import com.android.systemui.ambientcue.data.repository.AmbientCueRepositoryImpl.Companion.EXTRA_AUTOFILL_ID import com.android.systemui.ambientcue.data.repository.AmbientCueRepositoryImpl.Companion.MA_ACTION_TYPE_NAME import com.android.systemui.ambientcue.data.repository.AmbientCueRepositoryImpl.Companion.MR_ACTION_TYPE_NAME import com.android.systemui.ambientcue.shared.model.ActionModel import com.android.systemui.concurrency.fakeExecutor import com.android.systemui.dump.DumpManager Loading Loading @@ -99,6 +104,7 @@ class AmbientCueRepositoryTest : SysuiTestCase() { taskStackChangeListeners = kosmos.taskStackChangeListeners, backgroundDispatcher = kosmos.testDispatcher, secureSettingsRepository = kosmos.secureSettingsRepository, ambientCueLogger = kosmos.ambientCueLogger, ) @Test Loading Loading @@ -301,6 +307,36 @@ class AmbientCueRepositoryTest : SysuiTestCase() { verify(pendingIntent).send(any<Bundle>()) } @Test fun action_ma_performMaLogger() = kosmos.runTest { val actions by collectLastValue(underTest.actions) runCurrent() verify(smartSpaceSession) .addOnTargetsAvailableListener(any(), onTargetsAvailableListenerCaptor.capture()) onTargetsAvailableListenerCaptor.firstValue.onTargetsAvailable(listOf(maLoggerTarget)) val action: ActionModel = actions!!.first() action.onPerformAction() runCurrent() verify(kosmos.ambientCueLogger).setFulfilledWithMaStatus() } @Test fun action_mr_performMrLogger() = kosmos.runTest { val actions by collectLastValue(underTest.actions) runCurrent() verify(smartSpaceSession) .addOnTargetsAvailableListener(any(), onTargetsAvailableListenerCaptor.capture()) onTargetsAvailableListenerCaptor.firstValue.onTargetsAvailable(listOf(mrLoggerTarget)) val action: ActionModel = actions!!.first() action.onPerformAction() runCurrent() verify(kosmos.ambientCueLogger).setFulfilledWithMrStatus() } @Test fun action_performLongClick_pendingIntentSent() = kosmos.runTest { Loading Loading @@ -439,6 +475,32 @@ class AmbientCueRepositoryTest : SysuiTestCase() { .build() ) } private val maLoggerTarget = mock<SmartspaceTarget> { on { smartspaceTargetId } doReturn AMBIENT_CUE_SURFACE on { actionChips } doReturn listOf( SmartspaceAction.Builder("action1-id", "title 1") .setSubtitle("subtitle 1") .setExtras( Bundle().apply { putString(EXTRA_ACTION_TYPE, MA_ACTION_TYPE_NAME) } ) .build() ) } private val mrLoggerTarget = mock<SmartspaceTarget> { on { smartspaceTargetId } doReturn AMBIENT_CUE_SURFACE on { actionChips } doReturn listOf( SmartspaceAction.Builder("action1-id", "title 1") .setSubtitle("subtitle 1") .setExtras( Bundle().apply { putString(EXTRA_ACTION_TYPE, MR_ACTION_TYPE_NAME) } ) .build() ) } private val launchIntent = Intent() private val intentTarget = Loading packages/SystemUI/src/com/android/systemui/ambientcue/AmbientCueModule.kt +4 −0 Original line number Diff line number Diff line Loading @@ -19,6 +19,8 @@ package com.android.systemui.ambientcue import com.android.systemui.CoreStartable import com.android.systemui.ambientcue.data.repository.AmbientCueRepository import com.android.systemui.ambientcue.data.repository.AmbientCueRepositoryImpl import com.android.systemui.ambientcue.data.logger.AmbientCueLogger import com.android.systemui.ambientcue.data.logger.AmbientCueLoggerImpl import com.android.systemui.ambientcue.ui.startable.AmbientCueCoreStartable import dagger.Binds import dagger.Module Loading @@ -34,4 +36,6 @@ interface AmbientCueModule { fun bindAmbientCueCoreStartable(startable: AmbientCueCoreStartable): CoreStartable @Binds fun bindsAmbientCueRepository(impl: AmbientCueRepositoryImpl): AmbientCueRepository @Binds fun bindsAmbientCueLogger(impl: AmbientCueLoggerImpl): AmbientCueLogger } packages/SystemUI/src/com/android/systemui/ambientcue/data/logger/AmbientCueLogger.kt 0 → 100644 +140 −0 Original line number Diff line number Diff line /* * Copyright 2025 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.ambientcue.data.logger import com.android.internal.util.FrameworkStatsLog import com.android.systemui.util.time.SystemClock import javax.inject.Inject /** * Data object corresponding to a AmbientCueEventReported atom. * * Fields must be kept in sync with stats/atoms/ambientcue/amibent_cue_extension_atoms.proto. */ data class AmbientCueEventReported( var displayDurationMillis: Long = 0L, var fulfilledWithMaIntentMillis: Long = 0L, var fulfilledWithMrIntentMillis: Long = 0L, var loseFocusMillis: Long = 0L, var maCount: Int = 0, var mrCount: Int = 0, var fulfilledWithMaIntent: Boolean = false, var fulfilledWithMrIntent: Boolean = false, var clickedCloseButton: Boolean = false, var reachedTimeout: Boolean = false, ) /** * Interface for writing AmbientCueEventReported atoms to statsd log. Use AmbientCueLoggerImpl in * production. */ interface AmbientCueLogger { /** * Sets AmbientCue display events. * * @param maCount Number of ma actions generated and displayed. * @param mrCount Number of mr actions suggestions generated and displayed. */ fun setAmbientCueDisplayStatus(maCount: Int, mrCount: Int) /** * Sets AmbientCue lose focus time. * * @param loseFocusMillis The time in milliseconds that the cue bar lost focus. */ fun setLoseFocusMillis(loseFocusMillis: Long) /** Sets fulfilled with ma intent events. */ fun setFulfilledWithMaStatus() /** Sets fulfilled with mr intent events. */ fun setFulfilledWithMrStatus() /** Sets clicked close button events. */ fun setClickedCloseButtonStatus() /** Sets reached timeout events. */ fun setReachedTimeoutStatus() /** Flushes a AmbientCueEventReported atom. */ fun flushAmbientCueEventReported() /** Clears all saved status. */ fun clear() } /** Implementation for logging UI events related to controls. */ class AmbientCueLoggerImpl @Inject constructor(private val systemClock: SystemClock) : AmbientCueLogger { private var report = AmbientCueEventReported() private var displayTimeMillis: Long = 0L /** {@see AmbientCueLogger#setAmbientCueDisplayStatus} */ override fun setAmbientCueDisplayStatus(maCount: Int, mrCount: Int) { this.displayTimeMillis = systemClock.currentTimeMillis() report.maCount = maCount report.mrCount = mrCount } /** {@see AmbientCueLogger#setLoseFocusMillis} */ override fun setLoseFocusMillis(loseFocusMillis: Long) { // TODO(b/425279501): Count loseFocusMillis. } /** {@see AmbientCueLogger#setFulfilledWithMaStatus} */ override fun setFulfilledWithMaStatus() { // TODO(b/425279501): Count fulfilledWithMaIntentMillis. report.fulfilledWithMaIntent = true } /** {@see AmbientCueLogger#setFulfilledWithMrStatus} */ override fun setFulfilledWithMrStatus() { // TODO(b/425279501): Count fulfilledWithMrIntentMillis. report.fulfilledWithMrIntent = true } override fun setClickedCloseButtonStatus() { // TODO(b/425279501): Add logic to set clickedCloseButton } override fun setReachedTimeoutStatus() { // TODO(b/425279501): Add logic to set reachedTimeout } /** {@see AmbientCueLogger#flushAmbientCueEventReported} */ override fun flushAmbientCueEventReported() { FrameworkStatsLog.write( FrameworkStatsLog.AMBIENT_CUE_EVENT_REPORTED, report.displayDurationMillis, report.fulfilledWithMaIntentMillis, report.fulfilledWithMrIntentMillis, report.loseFocusMillis, report.maCount, report.mrCount, report.fulfilledWithMaIntent, report.fulfilledWithMrIntent, report.clickedCloseButton, report.reachedTimeout, ) } /** {@see AmbientCueLogger#clear} */ override fun clear() { report = AmbientCueEventReported() displayTimeMillis = 0L } } packages/SystemUI/src/com/android/systemui/ambientcue/data/repository/AmbientCueRepository.kt +33 −2 Original line number Diff line number Diff line Loading @@ -35,6 +35,7 @@ import androidx.tracing.trace import com.android.systemui.Dumpable import com.android.systemui.LauncherProxyService import com.android.systemui.LauncherProxyService.LauncherProxyListener import com.android.systemui.ambientcue.data.logger.AmbientCueLogger import com.android.systemui.ambientcue.shared.model.ActionModel import com.android.systemui.ambientcue.shared.model.IconModel import com.android.systemui.dagger.SysUISingleton Loading Loading @@ -114,6 +115,7 @@ constructor( private val taskStackChangeListeners: TaskStackChangeListeners, @Background backgroundDispatcher: CoroutineDispatcher, secureSettingsRepository: SecureSettingsRepository, private val ambientCueLogger: AmbientCueLogger, ) : AmbientCueRepository, Dumpable { init { Loading Loading @@ -194,7 +196,13 @@ constructor( launchPendingIntent(pendingIntent) } else if (intent != null) { activityStarter.startActivity(intent, false) } else {} } if (actionType == MA_ACTION_TYPE_NAME) { ambientCueLogger.setFulfilledWithMaStatus() } if (actionType == MR_ACTION_TYPE_NAME) { ambientCueLogger.setFulfilledWithMrStatus() } }, onPerformLongClick = { Loading Loading @@ -300,6 +308,7 @@ constructor( ) val targetTaskId: MutableStateFlow<Int> = MutableStateFlow(INVALID_TASK_ID) var isSessionStarted = false override val isAmbientCueEnabled: StateFlow<Boolean> = secureSettingsRepository Loading @@ -323,6 +332,26 @@ constructor( !isDeactivated && globallyFocusedTaskId == targetTaskId.value } .onEach { isAttached -> if (isAttached && !isSessionStarted) { isSessionStarted = true var maCount = 0 var mrCount = 0 actions.value.forEach { action -> when (action.actionType) { MA_ACTION_TYPE_NAME -> maCount++ MR_ACTION_TYPE_NAME -> mrCount++ else -> {} } } ambientCueLogger.setAmbientCueDisplayStatus(maCount, mrCount) } if (!isAttached && isSessionStarted) { ambientCueLogger.flushAmbientCueEventReported() ambientCueLogger.clear() isSessionStarted = false } } .stateIn( scope = backgroundScope, started = SharingStarted.WhileSubscribed(), Loading Loading @@ -350,7 +379,7 @@ constructor( @VisibleForTesting const val EXTRA_AUTOFILL_ID = "autofillId" @VisibleForTesting const val EXTRA_ATTRIBUTION_DIALOG_PENDING_INTENT = "attributionDialogPendingIntent" private const val EXTRA_ACTION_TYPE = "actionType" @VisibleForTesting const val EXTRA_ACTION_TYPE = "actionType" // Timeout to hide cuebar if it wasn't interacted with private const val TAG = "AmbientCueRepository" Loading @@ -360,5 +389,7 @@ constructor( @VisibleForTesting const val OPTED_IN = 0x10 @VisibleForTesting const val OPTED_OUT = 0x01 const val DEBOUNCE_DELAY_MS = 100L @VisibleForTesting const val MA_ACTION_TYPE_NAME = "ma" @VisibleForTesting const val MR_ACTION_TYPE_NAME = "mr" } } packages/SystemUI/src/com/android/systemui/ambientcue/ui/viewmodel/AmbientCueViewModel.kt +2 −0 Original line number Diff line number Diff line Loading @@ -146,6 +146,7 @@ constructor( } fun hide() { // TODO(b/425279501) Log ambient cue close button click status. ambientCueInteractor.setDeactivated(true) isExpanded = false } Loading @@ -162,6 +163,7 @@ constructor( coroutineScopeTraced("AmbientCueViewModel") { deactivateCueBarJob = launch { delay(AMBIENT_CUE_TIMEOUT_SEC) // TODO(b/425279501) Log ambient cue timeout status. ambientCueInteractor.setDeactivated(true) } } Loading Loading
packages/SystemUI/multivalentTests/src/com/android/systemui/ambientcue/data/repository/AmbientCueRepositoryTest.kt +62 −0 Original line number Diff line number Diff line Loading @@ -25,6 +25,7 @@ import android.app.smartspace.SmartspaceSession import android.app.smartspace.SmartspaceSession.OnTargetsAvailableListener import android.app.smartspace.SmartspaceTarget import android.content.Intent import android.content.applicationContext import android.content.testableContext import android.os.Binder import android.os.Bundle Loading @@ -36,11 +37,15 @@ import androidx.test.filters.SmallTest import com.android.systemui.LauncherProxyService import com.android.systemui.LauncherProxyService.LauncherProxyListener import com.android.systemui.SysuiTestCase import com.android.systemui.ambientcue.data.logger.ambientCueLogger import com.android.systemui.ambientcue.data.repository.AmbientCueRepositoryImpl.Companion.AMBIENT_CUE_SURFACE import com.android.systemui.ambientcue.data.repository.AmbientCueRepositoryImpl.Companion.DEBOUNCE_DELAY_MS import com.android.systemui.ambientcue.data.repository.AmbientCueRepositoryImpl.Companion.EXTRA_ACTION_TYPE import com.android.systemui.ambientcue.data.repository.AmbientCueRepositoryImpl.Companion.EXTRA_ACTIVITY_ID import com.android.systemui.ambientcue.data.repository.AmbientCueRepositoryImpl.Companion.EXTRA_ATTRIBUTION_DIALOG_PENDING_INTENT import com.android.systemui.ambientcue.data.repository.AmbientCueRepositoryImpl.Companion.EXTRA_AUTOFILL_ID import com.android.systemui.ambientcue.data.repository.AmbientCueRepositoryImpl.Companion.MA_ACTION_TYPE_NAME import com.android.systemui.ambientcue.data.repository.AmbientCueRepositoryImpl.Companion.MR_ACTION_TYPE_NAME import com.android.systemui.ambientcue.shared.model.ActionModel import com.android.systemui.concurrency.fakeExecutor import com.android.systemui.dump.DumpManager Loading Loading @@ -99,6 +104,7 @@ class AmbientCueRepositoryTest : SysuiTestCase() { taskStackChangeListeners = kosmos.taskStackChangeListeners, backgroundDispatcher = kosmos.testDispatcher, secureSettingsRepository = kosmos.secureSettingsRepository, ambientCueLogger = kosmos.ambientCueLogger, ) @Test Loading Loading @@ -301,6 +307,36 @@ class AmbientCueRepositoryTest : SysuiTestCase() { verify(pendingIntent).send(any<Bundle>()) } @Test fun action_ma_performMaLogger() = kosmos.runTest { val actions by collectLastValue(underTest.actions) runCurrent() verify(smartSpaceSession) .addOnTargetsAvailableListener(any(), onTargetsAvailableListenerCaptor.capture()) onTargetsAvailableListenerCaptor.firstValue.onTargetsAvailable(listOf(maLoggerTarget)) val action: ActionModel = actions!!.first() action.onPerformAction() runCurrent() verify(kosmos.ambientCueLogger).setFulfilledWithMaStatus() } @Test fun action_mr_performMrLogger() = kosmos.runTest { val actions by collectLastValue(underTest.actions) runCurrent() verify(smartSpaceSession) .addOnTargetsAvailableListener(any(), onTargetsAvailableListenerCaptor.capture()) onTargetsAvailableListenerCaptor.firstValue.onTargetsAvailable(listOf(mrLoggerTarget)) val action: ActionModel = actions!!.first() action.onPerformAction() runCurrent() verify(kosmos.ambientCueLogger).setFulfilledWithMrStatus() } @Test fun action_performLongClick_pendingIntentSent() = kosmos.runTest { Loading Loading @@ -439,6 +475,32 @@ class AmbientCueRepositoryTest : SysuiTestCase() { .build() ) } private val maLoggerTarget = mock<SmartspaceTarget> { on { smartspaceTargetId } doReturn AMBIENT_CUE_SURFACE on { actionChips } doReturn listOf( SmartspaceAction.Builder("action1-id", "title 1") .setSubtitle("subtitle 1") .setExtras( Bundle().apply { putString(EXTRA_ACTION_TYPE, MA_ACTION_TYPE_NAME) } ) .build() ) } private val mrLoggerTarget = mock<SmartspaceTarget> { on { smartspaceTargetId } doReturn AMBIENT_CUE_SURFACE on { actionChips } doReturn listOf( SmartspaceAction.Builder("action1-id", "title 1") .setSubtitle("subtitle 1") .setExtras( Bundle().apply { putString(EXTRA_ACTION_TYPE, MR_ACTION_TYPE_NAME) } ) .build() ) } private val launchIntent = Intent() private val intentTarget = Loading
packages/SystemUI/src/com/android/systemui/ambientcue/AmbientCueModule.kt +4 −0 Original line number Diff line number Diff line Loading @@ -19,6 +19,8 @@ package com.android.systemui.ambientcue import com.android.systemui.CoreStartable import com.android.systemui.ambientcue.data.repository.AmbientCueRepository import com.android.systemui.ambientcue.data.repository.AmbientCueRepositoryImpl import com.android.systemui.ambientcue.data.logger.AmbientCueLogger import com.android.systemui.ambientcue.data.logger.AmbientCueLoggerImpl import com.android.systemui.ambientcue.ui.startable.AmbientCueCoreStartable import dagger.Binds import dagger.Module Loading @@ -34,4 +36,6 @@ interface AmbientCueModule { fun bindAmbientCueCoreStartable(startable: AmbientCueCoreStartable): CoreStartable @Binds fun bindsAmbientCueRepository(impl: AmbientCueRepositoryImpl): AmbientCueRepository @Binds fun bindsAmbientCueLogger(impl: AmbientCueLoggerImpl): AmbientCueLogger }
packages/SystemUI/src/com/android/systemui/ambientcue/data/logger/AmbientCueLogger.kt 0 → 100644 +140 −0 Original line number Diff line number Diff line /* * Copyright 2025 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.ambientcue.data.logger import com.android.internal.util.FrameworkStatsLog import com.android.systemui.util.time.SystemClock import javax.inject.Inject /** * Data object corresponding to a AmbientCueEventReported atom. * * Fields must be kept in sync with stats/atoms/ambientcue/amibent_cue_extension_atoms.proto. */ data class AmbientCueEventReported( var displayDurationMillis: Long = 0L, var fulfilledWithMaIntentMillis: Long = 0L, var fulfilledWithMrIntentMillis: Long = 0L, var loseFocusMillis: Long = 0L, var maCount: Int = 0, var mrCount: Int = 0, var fulfilledWithMaIntent: Boolean = false, var fulfilledWithMrIntent: Boolean = false, var clickedCloseButton: Boolean = false, var reachedTimeout: Boolean = false, ) /** * Interface for writing AmbientCueEventReported atoms to statsd log. Use AmbientCueLoggerImpl in * production. */ interface AmbientCueLogger { /** * Sets AmbientCue display events. * * @param maCount Number of ma actions generated and displayed. * @param mrCount Number of mr actions suggestions generated and displayed. */ fun setAmbientCueDisplayStatus(maCount: Int, mrCount: Int) /** * Sets AmbientCue lose focus time. * * @param loseFocusMillis The time in milliseconds that the cue bar lost focus. */ fun setLoseFocusMillis(loseFocusMillis: Long) /** Sets fulfilled with ma intent events. */ fun setFulfilledWithMaStatus() /** Sets fulfilled with mr intent events. */ fun setFulfilledWithMrStatus() /** Sets clicked close button events. */ fun setClickedCloseButtonStatus() /** Sets reached timeout events. */ fun setReachedTimeoutStatus() /** Flushes a AmbientCueEventReported atom. */ fun flushAmbientCueEventReported() /** Clears all saved status. */ fun clear() } /** Implementation for logging UI events related to controls. */ class AmbientCueLoggerImpl @Inject constructor(private val systemClock: SystemClock) : AmbientCueLogger { private var report = AmbientCueEventReported() private var displayTimeMillis: Long = 0L /** {@see AmbientCueLogger#setAmbientCueDisplayStatus} */ override fun setAmbientCueDisplayStatus(maCount: Int, mrCount: Int) { this.displayTimeMillis = systemClock.currentTimeMillis() report.maCount = maCount report.mrCount = mrCount } /** {@see AmbientCueLogger#setLoseFocusMillis} */ override fun setLoseFocusMillis(loseFocusMillis: Long) { // TODO(b/425279501): Count loseFocusMillis. } /** {@see AmbientCueLogger#setFulfilledWithMaStatus} */ override fun setFulfilledWithMaStatus() { // TODO(b/425279501): Count fulfilledWithMaIntentMillis. report.fulfilledWithMaIntent = true } /** {@see AmbientCueLogger#setFulfilledWithMrStatus} */ override fun setFulfilledWithMrStatus() { // TODO(b/425279501): Count fulfilledWithMrIntentMillis. report.fulfilledWithMrIntent = true } override fun setClickedCloseButtonStatus() { // TODO(b/425279501): Add logic to set clickedCloseButton } override fun setReachedTimeoutStatus() { // TODO(b/425279501): Add logic to set reachedTimeout } /** {@see AmbientCueLogger#flushAmbientCueEventReported} */ override fun flushAmbientCueEventReported() { FrameworkStatsLog.write( FrameworkStatsLog.AMBIENT_CUE_EVENT_REPORTED, report.displayDurationMillis, report.fulfilledWithMaIntentMillis, report.fulfilledWithMrIntentMillis, report.loseFocusMillis, report.maCount, report.mrCount, report.fulfilledWithMaIntent, report.fulfilledWithMrIntent, report.clickedCloseButton, report.reachedTimeout, ) } /** {@see AmbientCueLogger#clear} */ override fun clear() { report = AmbientCueEventReported() displayTimeMillis = 0L } }
packages/SystemUI/src/com/android/systemui/ambientcue/data/repository/AmbientCueRepository.kt +33 −2 Original line number Diff line number Diff line Loading @@ -35,6 +35,7 @@ import androidx.tracing.trace import com.android.systemui.Dumpable import com.android.systemui.LauncherProxyService import com.android.systemui.LauncherProxyService.LauncherProxyListener import com.android.systemui.ambientcue.data.logger.AmbientCueLogger import com.android.systemui.ambientcue.shared.model.ActionModel import com.android.systemui.ambientcue.shared.model.IconModel import com.android.systemui.dagger.SysUISingleton Loading Loading @@ -114,6 +115,7 @@ constructor( private val taskStackChangeListeners: TaskStackChangeListeners, @Background backgroundDispatcher: CoroutineDispatcher, secureSettingsRepository: SecureSettingsRepository, private val ambientCueLogger: AmbientCueLogger, ) : AmbientCueRepository, Dumpable { init { Loading Loading @@ -194,7 +196,13 @@ constructor( launchPendingIntent(pendingIntent) } else if (intent != null) { activityStarter.startActivity(intent, false) } else {} } if (actionType == MA_ACTION_TYPE_NAME) { ambientCueLogger.setFulfilledWithMaStatus() } if (actionType == MR_ACTION_TYPE_NAME) { ambientCueLogger.setFulfilledWithMrStatus() } }, onPerformLongClick = { Loading Loading @@ -300,6 +308,7 @@ constructor( ) val targetTaskId: MutableStateFlow<Int> = MutableStateFlow(INVALID_TASK_ID) var isSessionStarted = false override val isAmbientCueEnabled: StateFlow<Boolean> = secureSettingsRepository Loading @@ -323,6 +332,26 @@ constructor( !isDeactivated && globallyFocusedTaskId == targetTaskId.value } .onEach { isAttached -> if (isAttached && !isSessionStarted) { isSessionStarted = true var maCount = 0 var mrCount = 0 actions.value.forEach { action -> when (action.actionType) { MA_ACTION_TYPE_NAME -> maCount++ MR_ACTION_TYPE_NAME -> mrCount++ else -> {} } } ambientCueLogger.setAmbientCueDisplayStatus(maCount, mrCount) } if (!isAttached && isSessionStarted) { ambientCueLogger.flushAmbientCueEventReported() ambientCueLogger.clear() isSessionStarted = false } } .stateIn( scope = backgroundScope, started = SharingStarted.WhileSubscribed(), Loading Loading @@ -350,7 +379,7 @@ constructor( @VisibleForTesting const val EXTRA_AUTOFILL_ID = "autofillId" @VisibleForTesting const val EXTRA_ATTRIBUTION_DIALOG_PENDING_INTENT = "attributionDialogPendingIntent" private const val EXTRA_ACTION_TYPE = "actionType" @VisibleForTesting const val EXTRA_ACTION_TYPE = "actionType" // Timeout to hide cuebar if it wasn't interacted with private const val TAG = "AmbientCueRepository" Loading @@ -360,5 +389,7 @@ constructor( @VisibleForTesting const val OPTED_IN = 0x10 @VisibleForTesting const val OPTED_OUT = 0x01 const val DEBOUNCE_DELAY_MS = 100L @VisibleForTesting const val MA_ACTION_TYPE_NAME = "ma" @VisibleForTesting const val MR_ACTION_TYPE_NAME = "mr" } }
packages/SystemUI/src/com/android/systemui/ambientcue/ui/viewmodel/AmbientCueViewModel.kt +2 −0 Original line number Diff line number Diff line Loading @@ -146,6 +146,7 @@ constructor( } fun hide() { // TODO(b/425279501) Log ambient cue close button click status. ambientCueInteractor.setDeactivated(true) isExpanded = false } Loading @@ -162,6 +163,7 @@ constructor( coroutineScopeTraced("AmbientCueViewModel") { deactivateCueBarJob = launch { delay(AMBIENT_CUE_TIMEOUT_SEC) // TODO(b/425279501) Log ambient cue timeout status. ambientCueInteractor.setDeactivated(true) } } Loading