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

Commit 9bdd9880 authored by Marcello Galhardo's avatar Marcello Galhardo Committed by Android (Google) Code Review
Browse files

Merge "Update Note Task shortcut when Notes Role is changed" into udc-dev

parents 7a12e659 ddec7a40
Loading
Loading
Loading
Loading
+3 −0
Original line number Diff line number Diff line
@@ -235,7 +235,10 @@
    <uses-permission android:name="android.permission.MANAGE_NOTIFICATIONS" />
    <uses-permission android:name="android.permission.GET_RUNTIME_PERMISSIONS" />
    <uses-permission android:name="android.permission.POST_NOTIFICATIONS" />

    <!-- role holder APIs -->
    <uses-permission android:name="android.permission.MANAGE_ROLE_HOLDERS" />
    <uses-permission android:name="android.permission.OBSERVE_ROLE_HOLDERS" />

    <!-- It's like, reality, but, you know, virtual -->
    <uses-permission android:name="android.permission.ACCESS_VR_MANAGER" />
+34 −0
Original line number 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

/**
 * Marks declarations that are **internal** in note task API, which means that should not be used
 * outside of `com.android.systemui.notetask`.
 */
@Retention(value = AnnotationRetention.BINARY)
@Target(
    AnnotationTarget.CLASS,
    AnnotationTarget.FUNCTION,
    AnnotationTarget.TYPEALIAS,
    AnnotationTarget.PROPERTY
)
@RequiresOptIn(
    level = RequiresOptIn.Level.ERROR,
    message = "This is an internal API, do not it outside `com.android.systemui.notetask`",
)
internal annotation class InternalNoteTaskApi
+62 −14
Original line number Diff line number Diff line
@@ -14,18 +14,21 @@
 * limitations under the License.
 */

@file:OptIn(InternalNoteTaskApi::class)

package com.android.systemui.notetask

import android.app.KeyguardManager
import android.app.admin.DevicePolicyManager
import android.app.role.OnRoleHoldersChangedListener
import android.app.role.RoleManager
import android.app.role.RoleManager.ROLE_NOTES
import android.content.ActivityNotFoundException
import android.content.ComponentName
import android.content.Context
import android.content.Intent
import android.content.Intent.FLAG_ACTIVITY_MULTIPLE_TASK
import android.content.Intent.FLAG_ACTIVITY_NEW_DOCUMENT
import android.content.Intent.FLAG_ACTIVITY_NEW_TASK
import android.content.pm.PackageManager
import android.content.pm.ShortcutManager
import android.os.Build
import android.os.UserHandle
import android.os.UserManager
@@ -33,6 +36,8 @@ import android.util.Log
import androidx.annotation.VisibleForTesting
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.devicepolicy.areKeyguardShortcutsDisabled
import com.android.systemui.notetask.NoteTaskRoleManagerExt.createNoteShortcutInfoAsUser
import com.android.systemui.notetask.NoteTaskRoleManagerExt.getDefaultRoleHolderAsUser
import com.android.systemui.notetask.shortcut.CreateNoteTaskShortcutActivity
import com.android.systemui.settings.UserTracker
import com.android.systemui.util.kotlin.getOrNull
@@ -55,6 +60,8 @@ class NoteTaskController
@Inject
constructor(
    private val context: Context,
    private val roleManager: RoleManager,
    private val shortcutManager: ShortcutManager,
    private val resolver: NoteTaskInfoResolver,
    private val eventLogger: NoteTaskEventLogger,
    private val optionalBubbles: Optional<Bubbles>,
@@ -133,7 +140,7 @@ constructor(
        infoReference.set(info)

        // TODO(b/266686199): We should handle when app not available. For now, we log.
        val intent = createNoteIntent(info)
        val intent = createNoteTaskIntent(info)
        try {
            logDebug { "onShowNoteTask - start: $info on user#${user.identifier}" }
            when (info.launchMode) {
@@ -182,30 +189,71 @@ constructor(
        logDebug { "setNoteTaskShortcutEnabled - completed: $isEnabled" }
    }

    /**
     * Updates all [NoteTaskController] related information, including but not exclusively the
     * widget shortcut created by the [user] - by default it will use the current user.
     *
     * Keep in mind the shortcut API has a
     * [rate limiting](https://developer.android.com/develop/ui/views/launch/shortcuts/managing-shortcuts#rate-limiting)
     * and may not be updated in real-time. To reduce the chance of stale shortcuts, we run the
     * function during System UI initialization.
     */
    fun updateNoteTaskAsUser(user: UserHandle) {
        val packageName = roleManager.getDefaultRoleHolderAsUser(ROLE_NOTES, user)
        val hasNotesRoleHolder = isEnabled && !packageName.isNullOrEmpty()

        setNoteTaskShortcutEnabled(hasNotesRoleHolder)

        if (hasNotesRoleHolder) {
            shortcutManager.enableShortcuts(listOf(SHORTCUT_ID))
            val updatedShortcut = roleManager.createNoteShortcutInfoAsUser(context, user)
            shortcutManager.updateShortcuts(listOf(updatedShortcut))
        } else {
            shortcutManager.disableShortcuts(listOf(SHORTCUT_ID))
        }
    }

    /** @see OnRoleHoldersChangedListener */
    fun onRoleHoldersChanged(roleName: String, user: UserHandle) {
        if (roleName == ROLE_NOTES) updateNoteTaskAsUser(user)
    }

    companion object {
        val TAG = NoteTaskController::class.simpleName.orEmpty()

        const val SHORTCUT_ID = "note_task_shortcut_id"

        /**
         * Shortcut extra which can point to a package name and can be used to indicate an alternate
         * badge info. Launcher only reads this if the shortcut comes from a system app.
         *
         * Duplicated from [com.android.launcher3.icons.IconCache].
         *
         * @see com.android.launcher3.icons.IconCache.EXTRA_SHORTCUT_BADGE_OVERRIDE_PACKAGE
         */
        const val EXTRA_SHORTCUT_BADGE_OVERRIDE_PACKAGE = "extra_shortcut_badge_override_package"
    }
}

private fun createNoteIntent(info: NoteTaskInfo): Intent =
/** Creates an [Intent] for [ROLE_NOTES]. */
private fun createNoteTaskIntent(info: NoteTaskInfo): Intent =
    Intent(Intent.ACTION_CREATE_NOTE).apply {
        setPackage(info.packageName)

        // EXTRA_USE_STYLUS_MODE does not mean a stylus is in-use, but a stylus entrypoint
        // was used to start it.
        // was used to start the note task.
        putExtra(Intent.EXTRA_USE_STYLUS_MODE, true)

        addFlags(FLAG_ACTIVITY_NEW_TASK)
        // We should ensure the note experience can be open both as a full screen (lock screen)
        addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
        // We should ensure the note experience can be opened both as a full screen (lockscreen)
        // and inside the app bubble (contextual). These additional flags will do that.
        if (info.launchMode == NoteTaskLaunchMode.Activity) {
            addFlags(FLAG_ACTIVITY_MULTIPLE_TASK)
            addFlags(FLAG_ACTIVITY_NEW_DOCUMENT)
            addFlags(Intent.FLAG_ACTIVITY_MULTIPLE_TASK)
            addFlags(Intent.FLAG_ACTIVITY_NEW_DOCUMENT)
        }
    }

private inline fun logDebug(message: () -> String) {
    if (Build.IS_DEBUGGABLE) {
        Log.d(NoteTaskController.TAG, message())
    }
/** [Log.println] a [Log.DEBUG] message, only when [Build.IS_DEBUGGABLE]. */
private inline fun Any.logDebug(message: () -> String) {
    if (Build.IS_DEBUGGABLE) Log.d(this::class.java.simpleName.orEmpty(), message())
}
+6 −3
Original line number Diff line number Diff line
@@ -14,13 +14,17 @@
 * limitations under the License.
 */

@file:OptIn(InternalNoteTaskApi::class)

package com.android.systemui.notetask

import android.app.role.RoleManager
import android.app.role.RoleManager.ROLE_NOTES
import android.content.pm.PackageManager
import android.content.pm.PackageManager.ApplicationInfoFlags
import android.os.UserHandle
import android.util.Log
import com.android.systemui.notetask.NoteTaskRoleManagerExt.getDefaultRoleHolderAsUser
import com.android.systemui.settings.UserTracker
import javax.inject.Inject

@@ -36,10 +40,9 @@ constructor(
        entryPoint: NoteTaskEntryPoint? = null,
        isKeyguardLocked: Boolean = false,
    ): NoteTaskInfo? {
        // TODO(b/267634412): Select UserHandle depending on where the user initiated note-taking.
        val user = userTracker.userHandle
        val packageName =
            roleManager.getRoleHoldersAsUser(RoleManager.ROLE_NOTES, user).firstOrNull()

        val packageName = roleManager.getDefaultRoleHolderAsUser(ROLE_NOTES, user)

        if (packageName.isNullOrEmpty()) return null

+12 −2
Original line number Diff line number Diff line
@@ -15,11 +15,15 @@
 */
package com.android.systemui.notetask

import android.app.role.RoleManager
import android.os.UserHandle
import android.view.KeyEvent
import androidx.annotation.VisibleForTesting
import com.android.systemui.dagger.qualifiers.Background
import com.android.systemui.statusbar.CommandQueue
import com.android.wm.shell.bubbles.Bubbles
import java.util.Optional
import java.util.concurrent.Executor
import javax.inject.Inject

/** Class responsible to "glue" all note task dependencies. */
@@ -27,8 +31,10 @@ internal class NoteTaskInitializer
@Inject
constructor(
    private val controller: NoteTaskController,
    private val roleManager: RoleManager,
    private val commandQueue: CommandQueue,
    private val optionalBubbles: Optional<Bubbles>,
    @Background private val backgroundExecutor: Executor,
    @NoteTaskEnabledKey private val isEnabled: Boolean,
) {

@@ -43,11 +49,15 @@ constructor(
        }

    fun initialize() {
        controller.setNoteTaskShortcutEnabled(isEnabled)

        // Guard against feature not being enabled or mandatory dependencies aren't available.
        if (!isEnabled || optionalBubbles.isEmpty) return

        controller.setNoteTaskShortcutEnabled(true)
        commandQueue.addCallback(callbacks)
        roleManager.addOnRoleHoldersChangedListenerAsUser(
            backgroundExecutor,
            controller::onRoleHoldersChanged,
            UserHandle.ALL,
        )
    }
}
Loading