• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2022 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.notetask
18 
19 import android.app.KeyguardManager
20 import android.content.ActivityNotFoundException
21 import android.content.ComponentName
22 import android.content.Context
23 import android.content.Intent
24 import android.content.pm.PackageManager
25 import android.os.UserManager
26 import android.util.Log
27 import com.android.internal.logging.UiEvent
28 import com.android.internal.logging.UiEventLogger
29 import com.android.systemui.dagger.SysUISingleton
30 import com.android.systemui.notetask.shortcut.CreateNoteTaskShortcutActivity
31 import com.android.systemui.util.kotlin.getOrNull
32 import com.android.wm.shell.bubbles.Bubbles
33 import java.util.Optional
34 import javax.inject.Inject
35 
36 /**
37  * Entry point for creating and managing note.
38  *
39  * The controller decides how a note is launched based in the device state: locked or unlocked.
40  *
41  * Currently, we only support a single task per time.
42  */
43 @SysUISingleton
44 internal class NoteTaskController
45 @Inject
46 constructor(
47     private val context: Context,
48     private val resolver: NoteTaskInfoResolver,
49     private val optionalBubbles: Optional<Bubbles>,
50     private val optionalKeyguardManager: Optional<KeyguardManager>,
51     private val optionalUserManager: Optional<UserManager>,
52     @NoteTaskEnabledKey private val isEnabled: Boolean,
53     private val uiEventLogger: UiEventLogger,
54 ) {
55 
56     /**
57      * Shows a note task. How the task is shown will depend on when the method is invoked.
58      *
59      * If in multi-window mode, notes will open as a full screen experience. That is particularly
60      * important for Large screen devices. These devices may support a taskbar that let users to
61      * drag and drop a shortcut into multi-window mode, and notes should comply with this behaviour.
62      *
63      * If the keyguard is locked, notes will open as a full screen experience. A locked device has
64      * no contextual information which let us use the whole screen space available.
65      *
66      * If not in multi-window or the keyguard is unlocked, notes will open as a bubble OR it will be
67      * collapsed if the notes bubble is already opened.
68      *
69      * That will let users open other apps in full screen, and take contextual notes.
70      */
71     @JvmOverloads
showNoteTasknull72     fun showNoteTask(isInMultiWindowMode: Boolean = false, uiEvent: ShowNoteTaskUiEvent? = null) {
73 
74         if (!isEnabled) return
75 
76         val bubbles = optionalBubbles.getOrNull() ?: return
77         val keyguardManager = optionalKeyguardManager.getOrNull() ?: return
78         val userManager = optionalUserManager.getOrNull() ?: return
79 
80         // TODO(b/249954038): We should handle direct boot (isUserUnlocked). For now, we do nothing.
81         if (!userManager.isUserUnlocked) return
82 
83         val noteTaskInfo = resolver.resolveInfo() ?: return
84 
85         uiEvent?.let { uiEventLogger.log(it, noteTaskInfo.uid, noteTaskInfo.packageName) }
86 
87         // TODO(b/266686199): We should handle when app not available. For now, we log.
88         val intent = noteTaskInfo.toCreateNoteIntent()
89         try {
90             if (isInMultiWindowMode || keyguardManager.isKeyguardLocked) {
91                 context.startActivity(intent)
92             } else {
93                 bubbles.showOrHideAppBubble(intent)
94             }
95         } catch (e: ActivityNotFoundException) {
96             Log.e(TAG, "Activity not found for action: $ACTION_CREATE_NOTE.", e)
97         }
98     }
99 
100     /**
101      * Set `android:enabled` property in the `AndroidManifest` associated with the Shortcut
102      * component to [value].
103      *
104      * If the shortcut entry `android:enabled` is set to `true`, the shortcut will be visible in the
105      * Widget Picker to all users.
106      */
setNoteTaskShortcutEnablednull107     fun setNoteTaskShortcutEnabled(value: Boolean) {
108         val componentName = ComponentName(context, CreateNoteTaskShortcutActivity::class.java)
109 
110         val enabledState =
111             if (value) {
112                 PackageManager.COMPONENT_ENABLED_STATE_ENABLED
113             } else {
114                 PackageManager.COMPONENT_ENABLED_STATE_DISABLED
115             }
116 
117         context.packageManager.setComponentEnabledSetting(
118             componentName,
119             enabledState,
120             PackageManager.DONT_KILL_APP,
121         )
122     }
123 
124     /** IDs of UI events accepted by [showNoteTask]. */
125     enum class ShowNoteTaskUiEvent(private val _id: Int) : UiEventLogger.UiEventEnum {
126         @UiEvent(doc = "User opened a note by tapping on the lockscreen shortcut.")
127         NOTE_OPENED_VIA_KEYGUARD_QUICK_AFFORDANCE(1294),
128 
129         /* ktlint-disable max-line-length */
130         @UiEvent(
131             doc =
132                 "User opened a note by pressing the stylus tail button while the screen was unlocked."
133         )
134         NOTE_OPENED_VIA_STYLUS_TAIL_BUTTON(1295),
135         @UiEvent(
136             doc =
137                 "User opened a note by pressing the stylus tail button while the screen was locked."
138         )
139         NOTE_OPENED_VIA_STYLUS_TAIL_BUTTON_LOCKED(1296),
140         @UiEvent(doc = "User opened a note by tapping on an app shortcut.")
141         NOTE_OPENED_VIA_SHORTCUT(1297);
142 
getIdnull143         override fun getId() = _id
144     }
145 
146     companion object {
147         private val TAG = NoteTaskController::class.simpleName.orEmpty()
148 
149         private fun NoteTaskInfoResolver.NoteTaskInfo.toCreateNoteIntent(): Intent {
150             return Intent(ACTION_CREATE_NOTE)
151                 .setPackage(packageName)
152                 .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
153                 // EXTRA_USE_STYLUS_MODE does not mean a stylus is in-use, but a stylus entrypoint
154                 // was used to start it.
155                 .putExtra(INTENT_EXTRA_USE_STYLUS_MODE, true)
156         }
157 
158         // TODO(b/254604589): Use final KeyEvent.KEYCODE_* instead.
159         const val NOTE_TASK_KEY_EVENT = 311
160 
161         // TODO(b/265912743): Use Intent.ACTION_CREATE_NOTE instead.
162         const val ACTION_CREATE_NOTE = "android.intent.action.CREATE_NOTE"
163 
164         // TODO(b/265912743): Use Intent.INTENT_EXTRA_USE_STYLUS_MODE instead.
165         const val INTENT_EXTRA_USE_STYLUS_MODE = "android.intent.extra.USE_STYLUS_MODE"
166     }
167 }
168