Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit f24155da authored by Anna Zappone's avatar Anna Zappone Committed by Android (Google) Code Review
Browse files

Merge "Log UI events for all note-taking entry points" into tm-qpr-dev

parents 45ea8390 6c4b646d
Loading
Loading
Loading
Loading
+50 −6
Original line number Diff line number Diff line
@@ -20,9 +20,12 @@ import android.app.KeyguardManager
import android.content.ActivityNotFoundException
import android.content.ComponentName
import android.content.Context
import android.content.Intent
import android.content.pm.PackageManager
import android.os.UserManager
import android.util.Log
import com.android.internal.logging.UiEvent
import com.android.internal.logging.UiEventLogger
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.notetask.shortcut.CreateNoteTaskShortcutActivity
import com.android.systemui.util.kotlin.getOrNull
@@ -42,11 +45,12 @@ internal class NoteTaskController
@Inject
constructor(
    private val context: Context,
    private val intentResolver: NoteTaskIntentResolver,
    private val resolver: NoteTaskInfoResolver,
    private val optionalBubbles: Optional<Bubbles>,
    private val optionalKeyguardManager: Optional<KeyguardManager>,
    private val optionalUserManager: Optional<UserManager>,
    @NoteTaskEnabledKey private val isEnabled: Boolean,
    private val uiEventLogger: UiEventLogger,
) {

    /**
@@ -64,7 +68,9 @@ constructor(
     *
     * That will let users open other apps in full screen, and take contextual notes.
     */
    fun showNoteTask(isInMultiWindowMode: Boolean = false) {
    @JvmOverloads
    fun showNoteTask(isInMultiWindowMode: Boolean = false, uiEvent: ShowNoteTaskUiEvent? = null) {

        if (!isEnabled) return

        val bubbles = optionalBubbles.getOrNull() ?: return
@@ -74,9 +80,12 @@ constructor(
        // TODO(b/249954038): We should handle direct boot (isUserUnlocked). For now, we do nothing.
        if (!userManager.isUserUnlocked) return

        val intent = intentResolver.resolveIntent() ?: return
        val noteTaskInfo = resolver.resolveInfo() ?: return

        uiEvent?.let { uiEventLogger.log(it, noteTaskInfo.uid, noteTaskInfo.packageName) }

        // TODO(b/266686199): We should handle when app not available. For now, we log.
        val intent = noteTaskInfo.toCreateNoteIntent()
        try {
            if (isInMultiWindowMode || keyguardManager.isKeyguardLocked) {
                context.startActivity(intent)
@@ -84,9 +93,7 @@ constructor(
                bubbles.showOrHideAppBubble(intent)
            }
        } catch (e: ActivityNotFoundException) {
            val message =
                "Activity not found for action: ${NoteTaskIntentResolver.ACTION_CREATE_NOTE}."
            Log.e(TAG, message, e)
            Log.e(TAG, "Activity not found for action: $ACTION_CREATE_NOTE.", e)
        }
    }

@@ -114,10 +121,47 @@ constructor(
        )
    }

    /** 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
    }

    companion object {
        private 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.
        const val NOTE_TASK_KEY_EVENT = 311

        // TODO(b/265912743): Use Intent.ACTION_CREATE_NOTE instead.
        const val ACTION_CREATE_NOTE = "android.intent.action.CREATE_NOTE"

        // TODO(b/265912743): Use Intent.INTENT_EXTRA_USE_STYLUS_MODE instead.
        const val INTENT_EXTRA_USE_STYLUS_MODE = "android.intent.extra.USE_STYLUS_MODE"
    }
}
+69 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2022 The Android Open Source Project
 * 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.
@@ -18,37 +18,52 @@ package com.android.systemui.notetask

import android.app.role.RoleManager
import android.content.Context
import android.content.Intent
import android.content.pm.PackageManager
import android.os.UserHandle
import android.util.Log
import javax.inject.Inject

internal class NoteTaskIntentResolver
internal class NoteTaskInfoResolver
@Inject
constructor(
    private val context: Context,
    private val roleManager: RoleManager,
    private val packageManager: PackageManager,
) {

    fun resolveIntent(): Intent? {
        val packageName = roleManager.getRoleHoldersAsUser(ROLE_NOTES, context.user).firstOrNull()
    fun resolveInfo(): NoteTaskInfo? {
        // TODO(b/267634412): Select UserHandle depending on where the user initiated note-taking.
        val user = context.user
        val packageName = roleManager.getRoleHoldersAsUser(ROLE_NOTES, user).firstOrNull()

        if (packageName.isNullOrEmpty()) return null

        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)
        return NoteTaskInfo(packageName, packageManager.getUidOf(packageName, user))
    }

    /** Package name and kernel user-ID of a note-taking app. */
    data class NoteTaskInfo(val packageName: String, val uid: Int)

    companion object {
        // TODO(b/265912743): Use Intent.ACTION_CREATE_NOTE instead.
        const val ACTION_CREATE_NOTE = "android.intent.action.CREATE_NOTE"
        private val TAG = NoteTaskInfoResolver::class.simpleName.orEmpty()

        private val EMPTY_APPLICATION_INFO_FLAGS = PackageManager.ApplicationInfoFlags.of(0)!!

        /**
         * Returns the kernel user-ID of [packageName] for a [user]. Returns zero if the app cannot
         * be found.
         */
        private fun PackageManager.getUidOf(packageName: String, user: UserHandle): Int {
            val applicationInfo =
                try {
                    getApplicationInfoAsUser(packageName, EMPTY_APPLICATION_INFO_FLAGS, user)
                } catch (e: PackageManager.NameNotFoundException) {
                    Log.e(TAG, "Couldn't find notes app UID", e)
                    return 0
                }
            return applicationInfo.uid
        }

        // TODO(b/265912743): Use RoleManager.NOTES_ROLE instead.
        const val ROLE_NOTES = "android.app.role.NOTES"

        // TODO(b/265912743): Use Intent.INTENT_EXTRA_USE_STYLUS_MODE instead.
        const val INTENT_EXTRA_USE_STYLUS_MODE = "android.intent.extra.USE_STYLUS_MODE"
    }
}
+18 −1
Original line number Diff line number Diff line
@@ -16,8 +16,10 @@

package com.android.systemui.notetask

import android.app.KeyguardManager
import androidx.annotation.VisibleForTesting
import com.android.systemui.statusbar.CommandQueue
import com.android.systemui.util.kotlin.getOrNull
import com.android.wm.shell.bubbles.Bubbles
import java.util.Optional
import javax.inject.Inject
@@ -30,6 +32,7 @@ constructor(
    private val noteTaskController: NoteTaskController,
    private val commandQueue: CommandQueue,
    @NoteTaskEnabledKey private val isEnabled: Boolean,
    private val optionalKeyguardManager: Optional<KeyguardManager>,
) {

    @VisibleForTesting
@@ -37,11 +40,21 @@ constructor(
        object : CommandQueue.Callbacks {
            override fun handleSystemKey(keyCode: Int) {
                if (keyCode == NoteTaskController.NOTE_TASK_KEY_EVENT) {
                    noteTaskController.showNoteTask()
                    showNoteTask()
                }
            }
        }

    private fun showNoteTask() {
        val uiEvent =
            if (optionalKeyguardManager.isKeyguardLocked) {
                NoteTaskController.ShowNoteTaskUiEvent.NOTE_OPENED_VIA_STYLUS_TAIL_BUTTON_LOCKED
            } else {
                NoteTaskController.ShowNoteTaskUiEvent.NOTE_OPENED_VIA_STYLUS_TAIL_BUTTON
            }
        noteTaskController.showNoteTask(uiEvent = uiEvent)
    }

    fun initialize() {
        if (isEnabled && optionalBubbles.isPresent) {
            commandQueue.addCallback(callbacks)
@@ -49,3 +62,7 @@ constructor(
        noteTaskController.setNoteTaskShortcutEnabled(isEnabled)
    }
}

private val Optional<KeyguardManager>.isKeyguardLocked: Boolean
    // If there's no KeyguardManager, assume that the keyguard is not locked.
    get() = getOrNull()?.isKeyguardLocked ?: false
+1 −1
Original line number Diff line number Diff line
@@ -51,7 +51,7 @@ internal interface NoteTaskModule {
            featureFlags: FeatureFlags,
            roleManager: RoleManager,
        ): Boolean {
            val isRoleAvailable = roleManager.isRoleAvailable(NoteTaskIntentResolver.ROLE_NOTES)
            val isRoleAvailable = roleManager.isRoleAvailable(NoteTaskInfoResolver.ROLE_NOTES)
            val isFeatureEnabled = featureFlags.isEnabled(Flags.NOTE_TASKS)
            return isRoleAvailable && isFeatureEnabled
        }
+4 −1
Original line number Diff line number Diff line
@@ -27,6 +27,7 @@ import com.android.systemui.keyguard.data.quickaffordance.KeyguardQuickAffordanc
import com.android.systemui.keyguard.data.quickaffordance.KeyguardQuickAffordanceConfig.OnTriggeredResult
import com.android.systemui.keyguard.data.quickaffordance.KeyguardQuickAffordanceConfig.PickerScreenState
import com.android.systemui.notetask.NoteTaskController
import com.android.systemui.notetask.NoteTaskController.ShowNoteTaskUiEvent
import com.android.systemui.notetask.NoteTaskEnabledKey
import javax.inject.Inject
import kotlinx.coroutines.flow.flowOf
@@ -64,7 +65,9 @@ constructor(
        }

    override fun onTriggered(expandable: Expandable?): OnTriggeredResult {
        noteTaskController.showNoteTask()
        noteTaskController.showNoteTask(
            uiEvent = ShowNoteTaskUiEvent.NOTE_OPENED_VIA_KEYGUARD_QUICK_AFFORDANCE
        )
        return OnTriggeredResult.Handled
    }
}
Loading