Loading packages/SystemUI/src/com/android/systemui/notetask/NoteTaskController.kt +75 −47 Original line number Original line Diff line number Diff line Loading @@ -22,15 +22,18 @@ import android.content.ComponentName import android.content.Context import android.content.Context import android.content.Intent import android.content.Intent import android.content.pm.PackageManager import android.content.pm.PackageManager import android.os.Build import android.os.UserManager import android.os.UserManager import android.util.Log import android.util.Log import com.android.internal.logging.UiEvent import androidx.annotation.VisibleForTesting import com.android.internal.logging.UiEventLogger import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dagger.SysUISingleton import com.android.systemui.notetask.shortcut.CreateNoteTaskShortcutActivity import com.android.systemui.notetask.shortcut.CreateNoteTaskShortcutActivity import com.android.systemui.util.kotlin.getOrNull import com.android.systemui.util.kotlin.getOrNull import com.android.wm.shell.bubbles.Bubble import com.android.wm.shell.bubbles.Bubbles import com.android.wm.shell.bubbles.Bubbles import com.android.wm.shell.bubbles.Bubbles.BubbleExpandListener import java.util.Optional import java.util.Optional import java.util.concurrent.atomic.AtomicReference import javax.inject.Inject import javax.inject.Inject /** /** Loading @@ -41,18 +44,40 @@ import javax.inject.Inject * Currently, we only support a single task per time. * Currently, we only support a single task per time. */ */ @SysUISingleton @SysUISingleton internal class NoteTaskController class NoteTaskController @Inject @Inject constructor( constructor( private val context: Context, private val context: Context, private val resolver: NoteTaskInfoResolver, private val resolver: NoteTaskInfoResolver, private val eventLogger: NoteTaskEventLogger, private val optionalBubbles: Optional<Bubbles>, private val optionalBubbles: Optional<Bubbles>, private val optionalKeyguardManager: Optional<KeyguardManager>, private val optionalUserManager: Optional<UserManager>, private val optionalUserManager: Optional<UserManager>, private val optionalKeyguardManager: Optional<KeyguardManager>, @NoteTaskEnabledKey private val isEnabled: Boolean, @NoteTaskEnabledKey private val isEnabled: Boolean, private val uiEventLogger: UiEventLogger, ) { ) { @VisibleForTesting val infoReference = AtomicReference<NoteTaskInfo?>() /** @see BubbleExpandListener */ fun onBubbleExpandChanged(isExpanding: Boolean, key: String?) { if (!isEnabled) return if (key != Bubble.KEY_APP_BUBBLE) return val info = infoReference.getAndSet(null) // Safe guard mechanism, this callback should only be called for app bubbles. if (info?.launchMode != NoteTaskLaunchMode.AppBubble) return if (isExpanding) { logDebug { "onBubbleExpandChanged - expanding: $info" } eventLogger.logNoteTaskOpened(info) } else { logDebug { "onBubbleExpandChanged - collapsing: $info" } eventLogger.logNoteTaskClosed(info) } } /** /** * Shows a note task. How the task is shown will depend on when the method is invoked. * Shows a note task. How the task is shown will depend on when the method is invoked. * * Loading @@ -69,32 +94,50 @@ constructor( * That will let users open other apps in full screen, and take contextual notes. * That will let users open other apps in full screen, and take contextual notes. */ */ @JvmOverloads @JvmOverloads fun showNoteTask(isInMultiWindowMode: Boolean = false, uiEvent: ShowNoteTaskUiEvent? = null) { fun showNoteTask( entryPoint: NoteTaskEntryPoint, isInMultiWindowMode: Boolean = false, ) { if (!isEnabled) return if (!isEnabled) return val bubbles = optionalBubbles.getOrNull() ?: return val bubbles = optionalBubbles.getOrNull() ?: return val keyguardManager = optionalKeyguardManager.getOrNull() ?: return val userManager = optionalUserManager.getOrNull() ?: return val userManager = optionalUserManager.getOrNull() ?: return val keyguardManager = optionalKeyguardManager.getOrNull() ?: return // TODO(b/249954038): We should handle direct boot (isUserUnlocked). For now, we do nothing. // TODO(b/249954038): We should handle direct boot (isUserUnlocked). For now, we do nothing. if (!userManager.isUserUnlocked) return if (!userManager.isUserUnlocked) return val noteTaskInfo = resolver.resolveInfo() ?: return val info = resolver.resolveInfo( entryPoint = entryPoint, isInMultiWindowMode = isInMultiWindowMode, isKeyguardLocked = keyguardManager.isKeyguardLocked, ) ?: return uiEvent?.let { uiEventLogger.log(it, noteTaskInfo.uid, noteTaskInfo.packageName) } infoReference.set(info) // TODO(b/266686199): We should handle when app not available. For now, we log. // TODO(b/266686199): We should handle when app not available. For now, we log. val intent = noteTaskInfo.toCreateNoteIntent() val intent = createNoteIntent(info) try { try { if (isInMultiWindowMode || keyguardManager.isKeyguardLocked) { logDebug { "onShowNoteTask - start: $info" } context.startActivity(intent) when (info.launchMode) { } else { is NoteTaskLaunchMode.AppBubble -> { bubbles.showOrHideAppBubble(intent) bubbles.showOrHideAppBubble(intent) // App bubble logging happens on `onBubbleExpandChanged`. logDebug { "onShowNoteTask - opened as app bubble: $info" } } } is NoteTaskLaunchMode.Activity -> { context.startActivity(intent) eventLogger.logNoteTaskOpened(info) logDebug { "onShowNoteTask - opened as activity: $info" } } } logDebug { "onShowNoteTask - success: $info" } } catch (e: ActivityNotFoundException) { } catch (e: ActivityNotFoundException) { Log.e(TAG, "Activity not found for action: $ACTION_CREATE_NOTE.", e) logDebug { "onShowNoteTask - failed: $info" } } } logDebug { "onShowNoteTask - compoleted: $info" } } } /** /** Loading @@ -119,41 +162,12 @@ constructor( enabledState, enabledState, PackageManager.DONT_KILL_APP, PackageManager.DONT_KILL_APP, ) ) } /** IDs of UI events accepted by [showNoteTask]. */ enum class ShowNoteTaskUiEvent(private val _id: Int) : UiEventLogger.UiEventEnum { @UiEvent(doc = "User opened a note by tapping on the lockscreen shortcut.") NOTE_OPENED_VIA_KEYGUARD_QUICK_AFFORDANCE(1294), /* ktlint-disable max-line-length */ @UiEvent( doc = "User opened a note by pressing the stylus tail button while the screen was unlocked." ) NOTE_OPENED_VIA_STYLUS_TAIL_BUTTON(1295), @UiEvent( doc = "User opened a note by pressing the stylus tail button while the screen was locked." ) NOTE_OPENED_VIA_STYLUS_TAIL_BUTTON_LOCKED(1296), @UiEvent(doc = "User opened a note by tapping on an app shortcut.") NOTE_OPENED_VIA_SHORTCUT(1297); override fun getId() = _id logDebug { "setNoteTaskShortcutEnabled - completed: $isEnabled" } } } companion object { companion object { private val TAG = NoteTaskController::class.simpleName.orEmpty() val TAG = NoteTaskController::class.simpleName.orEmpty() private fun NoteTaskInfoResolver.NoteTaskInfo.toCreateNoteIntent(): Intent { return Intent(ACTION_CREATE_NOTE) .setPackage(packageName) .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) // EXTRA_USE_STYLUS_MODE does not mean a stylus is in-use, but a stylus entrypoint // was used to start it. .putExtra(INTENT_EXTRA_USE_STYLUS_MODE, true) } // TODO(b/254604589): Use final KeyEvent.KEYCODE_* instead. // TODO(b/254604589): Use final KeyEvent.KEYCODE_* instead. const val NOTE_TASK_KEY_EVENT = 311 const val NOTE_TASK_KEY_EVENT = 311 Loading @@ -165,3 +179,17 @@ constructor( const val INTENT_EXTRA_USE_STYLUS_MODE = "android.intent.extra.USE_STYLUS_MODE" const val INTENT_EXTRA_USE_STYLUS_MODE = "android.intent.extra.USE_STYLUS_MODE" } } } } private fun createNoteIntent(info: NoteTaskInfo): Intent = Intent(NoteTaskController.ACTION_CREATE_NOTE) .setPackage(info.packageName) .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) // EXTRA_USE_STYLUS_MODE does not mean a stylus is in-use, but a stylus entrypoint // was used to start it. .putExtra(NoteTaskController.INTENT_EXTRA_USE_STYLUS_MODE, true) private inline fun logDebug(message: () -> String) { if (Build.IS_DEBUGGABLE) { Log.d(NoteTaskController.TAG, message()) } } packages/SystemUI/src/com/android/systemui/notetask/NoteTaskEnabledKey.kt +1 −1 Original line number Original line Diff line number Diff line Loading @@ -19,4 +19,4 @@ package com.android.systemui.notetask import javax.inject.Qualifier import javax.inject.Qualifier /** Key associated with a [Boolean] flag that enables or disables the note task feature. */ /** Key associated with a [Boolean] flag that enables or disables the note task feature. */ @Qualifier internal annotation class NoteTaskEnabledKey @Qualifier annotation class NoteTaskEnabledKey packages/SystemUI/src/com/android/systemui/notetask/NoteTaskEntryPoint.kt 0 → 100644 +41 −0 Original line number Original line Diff line number Diff line /* * Copyright (C) 2023 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.notetask import com.android.systemui.notetask.quickaffordance.NoteTaskQuickAffordanceConfig import com.android.systemui.notetask.shortcut.LaunchNoteTaskActivity import com.android.systemui.screenshot.AppClipsTrampolineActivity /** * Supported entry points for [NoteTaskController.showNoteTask]. * * An entry point represents where the note task has ben called from. In rare cases, it may * represent a "re-entry" (i.e., [APP_CLIPS]). */ enum class NoteTaskEntryPoint { /** @see [LaunchNoteTaskActivity] */ WIDGET_PICKER_SHORTCUT, /** @see [NoteTaskQuickAffordanceConfig] */ QUICK_AFFORDANCE, /** @see [NoteTaskInitializer.callbacks] */ TAIL_BUTTON, /** @see [AppClipsTrampolineActivity] */ APP_CLIPS, } packages/SystemUI/src/com/android/systemui/notetask/NoteTaskEventLogger.kt 0 → 100644 +101 −0 Original line number Original line Diff line number Diff line /* * Copyright (C) 2023 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.notetask import com.android.internal.logging.UiEvent import com.android.internal.logging.UiEventLogger import com.android.systemui.notetask.NoteTaskEntryPoint.APP_CLIPS import com.android.systemui.notetask.NoteTaskEntryPoint.QUICK_AFFORDANCE import com.android.systemui.notetask.NoteTaskEntryPoint.TAIL_BUTTON import com.android.systemui.notetask.NoteTaskEntryPoint.WIDGET_PICKER_SHORTCUT import com.android.systemui.notetask.NoteTaskEventLogger.NoteTaskUiEvent.NOTE_OPENED_VIA_KEYGUARD_QUICK_AFFORDANCE import com.android.systemui.notetask.NoteTaskEventLogger.NoteTaskUiEvent.NOTE_OPENED_VIA_SHORTCUT import com.android.systemui.notetask.NoteTaskEventLogger.NoteTaskUiEvent.NOTE_OPENED_VIA_STYLUS_TAIL_BUTTON import com.android.systemui.notetask.NoteTaskEventLogger.NoteTaskUiEvent.NOTE_OPENED_VIA_STYLUS_TAIL_BUTTON_LOCKED import javax.inject.Inject /** * A wrapper around [UiEventLogger] specialized in the note taking UI events. * * if the accepted [NoteTaskInfo] contains a [NoteTaskInfo.entryPoint], it will be logged as the * correct [NoteTaskUiEvent]. If null, it will be ignored. * * @see NoteTaskController for usage examples. */ class NoteTaskEventLogger @Inject constructor(private val uiEventLogger: UiEventLogger) { /** Logs a [NoteTaskInfo] as an **open** [NoteTaskUiEvent], including package name and uid. */ fun logNoteTaskOpened(info: NoteTaskInfo) { val event = when (info.entryPoint) { TAIL_BUTTON -> { if (info.isKeyguardLocked) { NOTE_OPENED_VIA_STYLUS_TAIL_BUTTON_LOCKED } else { NOTE_OPENED_VIA_STYLUS_TAIL_BUTTON } } WIDGET_PICKER_SHORTCUT -> NOTE_OPENED_VIA_SHORTCUT QUICK_AFFORDANCE -> NOTE_OPENED_VIA_KEYGUARD_QUICK_AFFORDANCE APP_CLIPS -> return null -> return } uiEventLogger.log(event, info.uid, info.packageName) } /** Logs a [NoteTaskInfo] as a **closed** [NoteTaskUiEvent], including package name and uid. */ fun logNoteTaskClosed(info: NoteTaskInfo) { val event = when (info.entryPoint) { TAIL_BUTTON -> { if (info.isKeyguardLocked) { NoteTaskUiEvent.NOTE_CLOSED_VIA_STYLUS_TAIL_BUTTON_LOCKED } else { NoteTaskUiEvent.NOTE_CLOSED_VIA_STYLUS_TAIL_BUTTON } } WIDGET_PICKER_SHORTCUT -> return QUICK_AFFORDANCE -> return APP_CLIPS -> return null -> return } uiEventLogger.log(event, info.uid, info.packageName) } /** IDs of UI events accepted by [NoteTaskController]. */ enum class NoteTaskUiEvent(private val _id: Int) : UiEventLogger.UiEventEnum { @UiEvent(doc = "User opened a note by tapping on the lockscreen shortcut.") NOTE_OPENED_VIA_KEYGUARD_QUICK_AFFORDANCE(1294), @UiEvent(doc = "User opened a note by pressing the stylus tail button while the screen was unlocked.") // ktlint-disable max-line-length NOTE_OPENED_VIA_STYLUS_TAIL_BUTTON(1295), @UiEvent(doc = "User opened a note by pressing the stylus tail button while the screen was locked.") // ktlint-disable max-line-length NOTE_OPENED_VIA_STYLUS_TAIL_BUTTON_LOCKED(1296), @UiEvent(doc = "User opened a note by tapping on an app shortcut.") NOTE_OPENED_VIA_SHORTCUT(1297), @UiEvent(doc = "Note closed via a tail button while device is unlocked") NOTE_CLOSED_VIA_STYLUS_TAIL_BUTTON(1311), @UiEvent(doc = "Note closed via a tail button while device is locked") NOTE_CLOSED_VIA_STYLUS_TAIL_BUTTON_LOCKED(1312); override fun getId() = _id } } packages/SystemUI/src/com/android/systemui/notetask/NoteTaskInfo.kt 0 → 100644 +33 −0 Original line number Original line Diff line number Diff line /* * Copyright (C) 2023 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.notetask /** Contextual information required to launch a Note Task by [NoteTaskController]. */ data class NoteTaskInfo( val packageName: String, val uid: Int, val entryPoint: NoteTaskEntryPoint? = null, val isInMultiWindowMode: Boolean = false, val isKeyguardLocked: Boolean = false, ) { val launchMode: NoteTaskLaunchMode = if (isInMultiWindowMode || isKeyguardLocked) { NoteTaskLaunchMode.Activity } else { NoteTaskLaunchMode.AppBubble } } Loading
packages/SystemUI/src/com/android/systemui/notetask/NoteTaskController.kt +75 −47 Original line number Original line Diff line number Diff line Loading @@ -22,15 +22,18 @@ import android.content.ComponentName import android.content.Context import android.content.Context import android.content.Intent import android.content.Intent import android.content.pm.PackageManager import android.content.pm.PackageManager import android.os.Build import android.os.UserManager import android.os.UserManager import android.util.Log import android.util.Log import com.android.internal.logging.UiEvent import androidx.annotation.VisibleForTesting import com.android.internal.logging.UiEventLogger import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dagger.SysUISingleton import com.android.systemui.notetask.shortcut.CreateNoteTaskShortcutActivity import com.android.systemui.notetask.shortcut.CreateNoteTaskShortcutActivity import com.android.systemui.util.kotlin.getOrNull import com.android.systemui.util.kotlin.getOrNull import com.android.wm.shell.bubbles.Bubble import com.android.wm.shell.bubbles.Bubbles import com.android.wm.shell.bubbles.Bubbles import com.android.wm.shell.bubbles.Bubbles.BubbleExpandListener import java.util.Optional import java.util.Optional import java.util.concurrent.atomic.AtomicReference import javax.inject.Inject import javax.inject.Inject /** /** Loading @@ -41,18 +44,40 @@ import javax.inject.Inject * Currently, we only support a single task per time. * Currently, we only support a single task per time. */ */ @SysUISingleton @SysUISingleton internal class NoteTaskController class NoteTaskController @Inject @Inject constructor( constructor( private val context: Context, private val context: Context, private val resolver: NoteTaskInfoResolver, private val resolver: NoteTaskInfoResolver, private val eventLogger: NoteTaskEventLogger, private val optionalBubbles: Optional<Bubbles>, private val optionalBubbles: Optional<Bubbles>, private val optionalKeyguardManager: Optional<KeyguardManager>, private val optionalUserManager: Optional<UserManager>, private val optionalUserManager: Optional<UserManager>, private val optionalKeyguardManager: Optional<KeyguardManager>, @NoteTaskEnabledKey private val isEnabled: Boolean, @NoteTaskEnabledKey private val isEnabled: Boolean, private val uiEventLogger: UiEventLogger, ) { ) { @VisibleForTesting val infoReference = AtomicReference<NoteTaskInfo?>() /** @see BubbleExpandListener */ fun onBubbleExpandChanged(isExpanding: Boolean, key: String?) { if (!isEnabled) return if (key != Bubble.KEY_APP_BUBBLE) return val info = infoReference.getAndSet(null) // Safe guard mechanism, this callback should only be called for app bubbles. if (info?.launchMode != NoteTaskLaunchMode.AppBubble) return if (isExpanding) { logDebug { "onBubbleExpandChanged - expanding: $info" } eventLogger.logNoteTaskOpened(info) } else { logDebug { "onBubbleExpandChanged - collapsing: $info" } eventLogger.logNoteTaskClosed(info) } } /** /** * Shows a note task. How the task is shown will depend on when the method is invoked. * Shows a note task. How the task is shown will depend on when the method is invoked. * * Loading @@ -69,32 +94,50 @@ constructor( * That will let users open other apps in full screen, and take contextual notes. * That will let users open other apps in full screen, and take contextual notes. */ */ @JvmOverloads @JvmOverloads fun showNoteTask(isInMultiWindowMode: Boolean = false, uiEvent: ShowNoteTaskUiEvent? = null) { fun showNoteTask( entryPoint: NoteTaskEntryPoint, isInMultiWindowMode: Boolean = false, ) { if (!isEnabled) return if (!isEnabled) return val bubbles = optionalBubbles.getOrNull() ?: return val bubbles = optionalBubbles.getOrNull() ?: return val keyguardManager = optionalKeyguardManager.getOrNull() ?: return val userManager = optionalUserManager.getOrNull() ?: return val userManager = optionalUserManager.getOrNull() ?: return val keyguardManager = optionalKeyguardManager.getOrNull() ?: return // TODO(b/249954038): We should handle direct boot (isUserUnlocked). For now, we do nothing. // TODO(b/249954038): We should handle direct boot (isUserUnlocked). For now, we do nothing. if (!userManager.isUserUnlocked) return if (!userManager.isUserUnlocked) return val noteTaskInfo = resolver.resolveInfo() ?: return val info = resolver.resolveInfo( entryPoint = entryPoint, isInMultiWindowMode = isInMultiWindowMode, isKeyguardLocked = keyguardManager.isKeyguardLocked, ) ?: return uiEvent?.let { uiEventLogger.log(it, noteTaskInfo.uid, noteTaskInfo.packageName) } infoReference.set(info) // TODO(b/266686199): We should handle when app not available. For now, we log. // TODO(b/266686199): We should handle when app not available. For now, we log. val intent = noteTaskInfo.toCreateNoteIntent() val intent = createNoteIntent(info) try { try { if (isInMultiWindowMode || keyguardManager.isKeyguardLocked) { logDebug { "onShowNoteTask - start: $info" } context.startActivity(intent) when (info.launchMode) { } else { is NoteTaskLaunchMode.AppBubble -> { bubbles.showOrHideAppBubble(intent) bubbles.showOrHideAppBubble(intent) // App bubble logging happens on `onBubbleExpandChanged`. logDebug { "onShowNoteTask - opened as app bubble: $info" } } } is NoteTaskLaunchMode.Activity -> { context.startActivity(intent) eventLogger.logNoteTaskOpened(info) logDebug { "onShowNoteTask - opened as activity: $info" } } } logDebug { "onShowNoteTask - success: $info" } } catch (e: ActivityNotFoundException) { } catch (e: ActivityNotFoundException) { Log.e(TAG, "Activity not found for action: $ACTION_CREATE_NOTE.", e) logDebug { "onShowNoteTask - failed: $info" } } } logDebug { "onShowNoteTask - compoleted: $info" } } } /** /** Loading @@ -119,41 +162,12 @@ constructor( enabledState, enabledState, PackageManager.DONT_KILL_APP, PackageManager.DONT_KILL_APP, ) ) } /** IDs of UI events accepted by [showNoteTask]. */ enum class ShowNoteTaskUiEvent(private val _id: Int) : UiEventLogger.UiEventEnum { @UiEvent(doc = "User opened a note by tapping on the lockscreen shortcut.") NOTE_OPENED_VIA_KEYGUARD_QUICK_AFFORDANCE(1294), /* ktlint-disable max-line-length */ @UiEvent( doc = "User opened a note by pressing the stylus tail button while the screen was unlocked." ) NOTE_OPENED_VIA_STYLUS_TAIL_BUTTON(1295), @UiEvent( doc = "User opened a note by pressing the stylus tail button while the screen was locked." ) NOTE_OPENED_VIA_STYLUS_TAIL_BUTTON_LOCKED(1296), @UiEvent(doc = "User opened a note by tapping on an app shortcut.") NOTE_OPENED_VIA_SHORTCUT(1297); override fun getId() = _id logDebug { "setNoteTaskShortcutEnabled - completed: $isEnabled" } } } companion object { companion object { private val TAG = NoteTaskController::class.simpleName.orEmpty() val TAG = NoteTaskController::class.simpleName.orEmpty() private fun NoteTaskInfoResolver.NoteTaskInfo.toCreateNoteIntent(): Intent { return Intent(ACTION_CREATE_NOTE) .setPackage(packageName) .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) // EXTRA_USE_STYLUS_MODE does not mean a stylus is in-use, but a stylus entrypoint // was used to start it. .putExtra(INTENT_EXTRA_USE_STYLUS_MODE, true) } // TODO(b/254604589): Use final KeyEvent.KEYCODE_* instead. // TODO(b/254604589): Use final KeyEvent.KEYCODE_* instead. const val NOTE_TASK_KEY_EVENT = 311 const val NOTE_TASK_KEY_EVENT = 311 Loading @@ -165,3 +179,17 @@ constructor( const val INTENT_EXTRA_USE_STYLUS_MODE = "android.intent.extra.USE_STYLUS_MODE" const val INTENT_EXTRA_USE_STYLUS_MODE = "android.intent.extra.USE_STYLUS_MODE" } } } } private fun createNoteIntent(info: NoteTaskInfo): Intent = Intent(NoteTaskController.ACTION_CREATE_NOTE) .setPackage(info.packageName) .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) // EXTRA_USE_STYLUS_MODE does not mean a stylus is in-use, but a stylus entrypoint // was used to start it. .putExtra(NoteTaskController.INTENT_EXTRA_USE_STYLUS_MODE, true) private inline fun logDebug(message: () -> String) { if (Build.IS_DEBUGGABLE) { Log.d(NoteTaskController.TAG, message()) } }
packages/SystemUI/src/com/android/systemui/notetask/NoteTaskEnabledKey.kt +1 −1 Original line number Original line Diff line number Diff line Loading @@ -19,4 +19,4 @@ package com.android.systemui.notetask import javax.inject.Qualifier import javax.inject.Qualifier /** Key associated with a [Boolean] flag that enables or disables the note task feature. */ /** Key associated with a [Boolean] flag that enables or disables the note task feature. */ @Qualifier internal annotation class NoteTaskEnabledKey @Qualifier annotation class NoteTaskEnabledKey
packages/SystemUI/src/com/android/systemui/notetask/NoteTaskEntryPoint.kt 0 → 100644 +41 −0 Original line number Original line Diff line number Diff line /* * Copyright (C) 2023 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.notetask import com.android.systemui.notetask.quickaffordance.NoteTaskQuickAffordanceConfig import com.android.systemui.notetask.shortcut.LaunchNoteTaskActivity import com.android.systemui.screenshot.AppClipsTrampolineActivity /** * Supported entry points for [NoteTaskController.showNoteTask]. * * An entry point represents where the note task has ben called from. In rare cases, it may * represent a "re-entry" (i.e., [APP_CLIPS]). */ enum class NoteTaskEntryPoint { /** @see [LaunchNoteTaskActivity] */ WIDGET_PICKER_SHORTCUT, /** @see [NoteTaskQuickAffordanceConfig] */ QUICK_AFFORDANCE, /** @see [NoteTaskInitializer.callbacks] */ TAIL_BUTTON, /** @see [AppClipsTrampolineActivity] */ APP_CLIPS, }
packages/SystemUI/src/com/android/systemui/notetask/NoteTaskEventLogger.kt 0 → 100644 +101 −0 Original line number Original line Diff line number Diff line /* * Copyright (C) 2023 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.notetask import com.android.internal.logging.UiEvent import com.android.internal.logging.UiEventLogger import com.android.systemui.notetask.NoteTaskEntryPoint.APP_CLIPS import com.android.systemui.notetask.NoteTaskEntryPoint.QUICK_AFFORDANCE import com.android.systemui.notetask.NoteTaskEntryPoint.TAIL_BUTTON import com.android.systemui.notetask.NoteTaskEntryPoint.WIDGET_PICKER_SHORTCUT import com.android.systemui.notetask.NoteTaskEventLogger.NoteTaskUiEvent.NOTE_OPENED_VIA_KEYGUARD_QUICK_AFFORDANCE import com.android.systemui.notetask.NoteTaskEventLogger.NoteTaskUiEvent.NOTE_OPENED_VIA_SHORTCUT import com.android.systemui.notetask.NoteTaskEventLogger.NoteTaskUiEvent.NOTE_OPENED_VIA_STYLUS_TAIL_BUTTON import com.android.systemui.notetask.NoteTaskEventLogger.NoteTaskUiEvent.NOTE_OPENED_VIA_STYLUS_TAIL_BUTTON_LOCKED import javax.inject.Inject /** * A wrapper around [UiEventLogger] specialized in the note taking UI events. * * if the accepted [NoteTaskInfo] contains a [NoteTaskInfo.entryPoint], it will be logged as the * correct [NoteTaskUiEvent]. If null, it will be ignored. * * @see NoteTaskController for usage examples. */ class NoteTaskEventLogger @Inject constructor(private val uiEventLogger: UiEventLogger) { /** Logs a [NoteTaskInfo] as an **open** [NoteTaskUiEvent], including package name and uid. */ fun logNoteTaskOpened(info: NoteTaskInfo) { val event = when (info.entryPoint) { TAIL_BUTTON -> { if (info.isKeyguardLocked) { NOTE_OPENED_VIA_STYLUS_TAIL_BUTTON_LOCKED } else { NOTE_OPENED_VIA_STYLUS_TAIL_BUTTON } } WIDGET_PICKER_SHORTCUT -> NOTE_OPENED_VIA_SHORTCUT QUICK_AFFORDANCE -> NOTE_OPENED_VIA_KEYGUARD_QUICK_AFFORDANCE APP_CLIPS -> return null -> return } uiEventLogger.log(event, info.uid, info.packageName) } /** Logs a [NoteTaskInfo] as a **closed** [NoteTaskUiEvent], including package name and uid. */ fun logNoteTaskClosed(info: NoteTaskInfo) { val event = when (info.entryPoint) { TAIL_BUTTON -> { if (info.isKeyguardLocked) { NoteTaskUiEvent.NOTE_CLOSED_VIA_STYLUS_TAIL_BUTTON_LOCKED } else { NoteTaskUiEvent.NOTE_CLOSED_VIA_STYLUS_TAIL_BUTTON } } WIDGET_PICKER_SHORTCUT -> return QUICK_AFFORDANCE -> return APP_CLIPS -> return null -> return } uiEventLogger.log(event, info.uid, info.packageName) } /** IDs of UI events accepted by [NoteTaskController]. */ enum class NoteTaskUiEvent(private val _id: Int) : UiEventLogger.UiEventEnum { @UiEvent(doc = "User opened a note by tapping on the lockscreen shortcut.") NOTE_OPENED_VIA_KEYGUARD_QUICK_AFFORDANCE(1294), @UiEvent(doc = "User opened a note by pressing the stylus tail button while the screen was unlocked.") // ktlint-disable max-line-length NOTE_OPENED_VIA_STYLUS_TAIL_BUTTON(1295), @UiEvent(doc = "User opened a note by pressing the stylus tail button while the screen was locked.") // ktlint-disable max-line-length NOTE_OPENED_VIA_STYLUS_TAIL_BUTTON_LOCKED(1296), @UiEvent(doc = "User opened a note by tapping on an app shortcut.") NOTE_OPENED_VIA_SHORTCUT(1297), @UiEvent(doc = "Note closed via a tail button while device is unlocked") NOTE_CLOSED_VIA_STYLUS_TAIL_BUTTON(1311), @UiEvent(doc = "Note closed via a tail button while device is locked") NOTE_CLOSED_VIA_STYLUS_TAIL_BUTTON_LOCKED(1312); override fun getId() = _id } }
packages/SystemUI/src/com/android/systemui/notetask/NoteTaskInfo.kt 0 → 100644 +33 −0 Original line number Original line Diff line number Diff line /* * Copyright (C) 2023 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.notetask /** Contextual information required to launch a Note Task by [NoteTaskController]. */ data class NoteTaskInfo( val packageName: String, val uid: Int, val entryPoint: NoteTaskEntryPoint? = null, val isInMultiWindowMode: Boolean = false, val isKeyguardLocked: Boolean = false, ) { val launchMode: NoteTaskLaunchMode = if (isInMultiWindowMode || isKeyguardLocked) { NoteTaskLaunchMode.Activity } else { NoteTaskLaunchMode.AppBubble } }