Support split-mode on Note Shortcut

Cherry picked I90adb9eaf7cb33968db38daa8322ecd8a0a62939 to master due to b/286408867.

Code was merged via `-s ours` and is missing even though the sha exists on the target branch. Cherry pick is required to bring in the code.

Manually remove the "Merged in" directive to allow for downstream propagation.

Test: atest NoteTaskInfoTest
Flag: not needed
Fixes: b/284296041
(cherry picked from https://googleplex-android-review.googlesource.com/q/commit:cb876c5e29567718cd41cc24d63f58928adcdc0f)
Change-Id: I588fbf8122300b4826db9913d742a9ca5ea7b5e8
This commit is contained in:
Marcello Galhardo
2023-06-02 14:55:33 +00:00
committed by Brian Egizi
parent 47df3d96a1
commit 7aae738d98
9 changed files with 159 additions and 112 deletions

View File

@@ -1005,7 +1005,6 @@
android:name=".notetask.shortcut.LaunchNoteTaskActivity"
android:exported="true"
android:excludeFromRecents="true"
android:resizeableActivity="false"
android:theme="@android:style/Theme.NoDisplay" >
<intent-filter>
@@ -1018,7 +1017,6 @@
android:name=".notetask.LaunchNotesRoleSettingsTrampolineActivity"
android:exported="true"
android:excludeFromRecents="true"
android:resizeableActivity="false"
android:theme="@android:style/Theme.NoDisplay" >
<intent-filter>
<action android:name="com.android.systemui.action.MANAGE_NOTES_ROLE_FROM_QUICK_AFFORDANCE" />

View File

@@ -46,7 +46,6 @@ import com.android.systemui.notetask.NoteTaskRoleManagerExt.getDefaultRoleHolder
import com.android.systemui.notetask.shortcut.CreateNoteTaskShortcutActivity
import com.android.systemui.settings.UserTracker
import com.android.systemui.shared.system.ActivityManagerKt.isInForeground
import com.android.systemui.util.kotlin.getOrNull
import com.android.systemui.util.settings.SecureSettings
import com.android.wm.shell.bubbles.Bubble
import com.android.wm.shell.bubbles.Bubbles.BubbleExpandListener
@@ -219,7 +218,7 @@ constructor(
debugLog { "onShowNoteTask - opened as app bubble: $info" }
}
is NoteTaskLaunchMode.Activity -> {
if (activityManager.isInForeground(info.packageName)) {
if (info.isKeyguardLocked && activityManager.isInForeground(info.packageName)) {
// Force note task into background by calling home.
val intent = createHomeIntent()
context.startActivityAsUser(intent, user)

View File

@@ -25,12 +25,14 @@ import com.android.systemui.screenshot.appclips.AppClipsTrampolineActivity
* 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 {
enum class NoteTaskEntryPoint {
/** @see [LaunchNoteTaskActivity] */
WIDGET_PICKER_SHORTCUT,
/** @see [LaunchNoteTaskActivity] */
WIDGET_PICKER_SHORTCUT_IN_MULTI_WINDOW_MODE,
/** @see [NoteTaskQuickAffordanceConfig] */
QUICK_AFFORDANCE,

View File

@@ -22,6 +22,8 @@ import com.android.systemui.notetask.NoteTaskEntryPoint.KEYBOARD_SHORTCUT
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.NoteTaskEntryPoint.WIDGET_PICKER_SHORTCUT_IN_MULTI_WINDOW_MODE
import com.android.systemui.notetask.NoteTaskEventLogger.NoteTaskUiEvent
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
@@ -41,40 +43,45 @@ class NoteTaskEventLogger @Inject constructor(private val uiEventLogger: UiEvent
/** 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
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,
WIDGET_PICKER_SHORTCUT_IN_MULTI_WINDOW_MODE -> NOTE_OPENED_VIA_SHORTCUT
QUICK_AFFORDANCE -> NOTE_OPENED_VIA_KEYGUARD_QUICK_AFFORDANCE
APP_CLIPS,
KEYBOARD_SHORTCUT,
null -> return
}
WIDGET_PICKER_SHORTCUT -> NOTE_OPENED_VIA_SHORTCUT
QUICK_AFFORDANCE -> NOTE_OPENED_VIA_KEYGUARD_QUICK_AFFORDANCE
APP_CLIPS -> return
KEYBOARD_SHORTCUT -> 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
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,
WIDGET_PICKER_SHORTCUT_IN_MULTI_WINDOW_MODE,
QUICK_AFFORDANCE,
APP_CLIPS,
KEYBOARD_SHORTCUT,
null -> return
}
WIDGET_PICKER_SHORTCUT -> return
QUICK_AFFORDANCE -> return
APP_CLIPS -> return
KEYBOARD_SHORTCUT -> return
null -> return
}
uiEventLogger.log(event, info.uid, info.packageName)
}

View File

@@ -16,6 +16,7 @@
package com.android.systemui.notetask
import android.os.UserHandle
import com.android.systemui.notetask.NoteTaskEntryPoint.WIDGET_PICKER_SHORTCUT_IN_MULTI_WINDOW_MODE
/** Contextual information required to launch a Note Task by [NoteTaskController]. */
data class NoteTaskInfo(
@@ -27,7 +28,7 @@ data class NoteTaskInfo(
) {
val launchMode: NoteTaskLaunchMode =
if (isKeyguardLocked) {
if (isKeyguardLocked || entryPoint == WIDGET_PICKER_SHORTCUT_IN_MULTI_WINDOW_MODE) {
NoteTaskLaunchMode.Activity
} else {
NoteTaskLaunchMode.AppBubble

View File

@@ -67,7 +67,8 @@ internal object NoteTaskRoleManagerExt {
val icon = Icon.createWithResource(context, R.drawable.ic_note_task_shortcut_widget)
return ShortcutInfo.Builder(context, NoteTaskController.SHORTCUT_ID)
.setIntent(LaunchNoteTaskActivity.newIntent(context = context))
.setIntent(LaunchNoteTaskActivity.createIntent(context))
.setActivity(LaunchNoteTaskActivity.createComponent(context))
.setShortLabel(shortLabel)
.setLongLabel(longLabel)
.setLongLived(true)

View File

@@ -16,6 +16,7 @@
package com.android.systemui.notetask.shortcut
import android.content.ComponentName
import android.content.Context
import android.content.Intent
import android.os.Bundle
@@ -30,18 +31,27 @@ class LaunchNoteTaskActivity @Inject constructor(private val controller: NoteTas
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
controller.showNoteTaskAsUser(entryPoint = NoteTaskEntryPoint.WIDGET_PICKER_SHORTCUT, user)
val entryPoint =
if (isInMultiWindowMode) {
NoteTaskEntryPoint.WIDGET_PICKER_SHORTCUT_IN_MULTI_WINDOW_MODE
} else {
NoteTaskEntryPoint.WIDGET_PICKER_SHORTCUT
}
controller.showNoteTaskAsUser(entryPoint, user)
finish()
}
companion object {
/** Creates a new [Intent] set to start [LaunchNoteTaskActivity]. */
fun newIntent(context: Context): Intent {
return Intent(context, LaunchNoteTaskActivity::class.java).apply {
fun createIntent(context: Context): Intent =
Intent(context, LaunchNoteTaskActivity::class.java).apply {
// Intent's action must be set in shortcuts, or an exception will be thrown.
action = Intent.ACTION_CREATE_NOTE
}
}
/** Creates a new [ComponentName] for [LaunchNoteTaskActivity]. */
fun createComponent(context: Context): ComponentName =
ComponentName(context, LaunchNoteTaskActivity::class.java)
}
}

View File

@@ -244,8 +244,38 @@ internal class NoteTaskControllerTest : SysuiTestCase() {
// endregion
// region showNoteTask
fun showNoteTaskAsUser_keyguardIsLocked_shouldStartActivityWithExpectedUserAndLogUiEvent() {
val user10 = UserHandle.of(/* userId= */ 10)
val expectedInfo =
NOTE_TASK_INFO.copy(
entryPoint = TAIL_BUTTON,
isKeyguardLocked = true,
user = user10,
)
whenever(keyguardManager.isKeyguardLocked).thenReturn(expectedInfo.isKeyguardLocked)
whenever(resolver.resolveInfo(any(), any(), any())).thenReturn(expectedInfo)
createNoteTaskController()
.showNoteTaskAsUser(entryPoint = expectedInfo.entryPoint!!, user = user10)
val intentCaptor = argumentCaptor<Intent>()
val userCaptor = argumentCaptor<UserHandle>()
verify(context).startActivityAsUser(capture(intentCaptor), capture(userCaptor))
assertThat(intentCaptor.value).run {
hasAction(ACTION_CREATE_NOTE)
hasPackage(NOTE_TASK_PACKAGE_NAME)
hasFlags(FLAG_ACTIVITY_NEW_TASK)
hasFlags(FLAG_ACTIVITY_MULTIPLE_TASK)
hasFlags(FLAG_ACTIVITY_NEW_DOCUMENT)
extras().bool(EXTRA_USE_STYLUS_MODE).isTrue()
}
assertThat(userCaptor.value).isEqualTo(user10)
verify(eventLogger).logNoteTaskOpened(expectedInfo)
verifyZeroInteractions(bubbles)
}
@Test
fun showNoteTask_keyguardIsLocked_shouldStartActivityAndLogUiEvent() {
fun showNoteTask_keyguardIsLocked_notesIsClosed_shouldStartActivityAndLogUiEvent() {
val expectedInfo = NOTE_TASK_INFO.copy(entryPoint = TAIL_BUTTON, isKeyguardLocked = true)
whenever(keyguardManager.isKeyguardLocked).thenReturn(expectedInfo.isKeyguardLocked)
whenever(resolver.resolveInfo(any(), any(), any())).thenReturn(expectedInfo)
@@ -268,6 +298,59 @@ internal class NoteTaskControllerTest : SysuiTestCase() {
verifyZeroInteractions(bubbles)
}
@Test
fun showNoteTask_keyguardIsLocked_noteIsOpen_shouldCloseActivityAndLogUiEvent() {
val expectedInfo = NOTE_TASK_INFO.copy(entryPoint = TAIL_BUTTON, isKeyguardLocked = true)
whenever(keyguardManager.isKeyguardLocked).thenReturn(expectedInfo.isKeyguardLocked)
whenever(resolver.resolveInfo(any(), any(), any())).thenReturn(expectedInfo)
whenever(activityManager.getRunningTasks(anyInt()))
.thenReturn(listOf(NOTE_RUNNING_TASK_INFO))
createNoteTaskController().showNoteTask(entryPoint = expectedInfo.entryPoint!!)
val intentCaptor = argumentCaptor<Intent>()
val userCaptor = argumentCaptor<UserHandle>()
verify(context).startActivityAsUser(capture(intentCaptor), capture(userCaptor))
assertThat(intentCaptor.value).run {
hasAction(ACTION_MAIN)
categories().contains(CATEGORY_HOME)
hasFlags(FLAG_ACTIVITY_NEW_TASK)
}
assertThat(userCaptor.value).isEqualTo(userTracker.userHandle)
verify(eventLogger).logNoteTaskClosed(expectedInfo)
verifyZeroInteractions(bubbles)
}
@Test
fun showNoteTask_keyguardIsUnlocked_noteIsClosed_shouldStartBubblesWithoutLoggingUiEvent() {
val expectedInfo = NOTE_TASK_INFO.copy(entryPoint = TAIL_BUTTON, isKeyguardLocked = false)
whenever(resolver.resolveInfo(any(), any(), any())).thenReturn(expectedInfo)
whenever(keyguardManager.isKeyguardLocked).thenReturn(expectedInfo.isKeyguardLocked)
createNoteTaskController().showNoteTask(entryPoint = expectedInfo.entryPoint!!)
// Context package name used to create bubble icon from drawable resource id
verify(context, atLeastOnce()).packageName
verifyNoteTaskOpenInBubbleInUser(userTracker.userHandle)
verifyZeroInteractions(eventLogger)
}
@Test
fun showNoteTask_keyguardIsUnlocked_noteIsOpen_shouldStartBubblesWithoutLoggingUiEvent() {
val expectedInfo = NOTE_TASK_INFO.copy(entryPoint = TAIL_BUTTON, isKeyguardLocked = false)
whenever(resolver.resolveInfo(any(), any(), any())).thenReturn(expectedInfo)
whenever(keyguardManager.isKeyguardLocked).thenReturn(expectedInfo.isKeyguardLocked)
whenever(activityManager.getRunningTasks(anyInt()))
.thenReturn(listOf(NOTE_RUNNING_TASK_INFO))
createNoteTaskController().showNoteTask(entryPoint = expectedInfo.entryPoint!!)
// Context package name used to create bubble icon from drawable resource id
verify(context, atLeastOnce()).packageName
verifyNoteTaskOpenInBubbleInUser(userTracker.userHandle)
verifyZeroInteractions(eventLogger)
}
@Test
fun showNoteTask_defaultUserSet_shouldStartActivityWithExpectedUserAndLogUiEvent() {
whenever(secureSettings.getInt(eq(Settings.Secure.DEFAULT_NOTE_TASK_PROFILE), any()))
@@ -306,70 +389,6 @@ internal class NoteTaskControllerTest : SysuiTestCase() {
verifyZeroInteractions(bubbles)
}
@Test
fun showNoteTaskWithUser_keyguardIsLocked_shouldStartActivityWithExpectedUserAndLogUiEvent() {
val user10 = UserHandle.of(/* userId= */ 10)
val expectedInfo =
NOTE_TASK_INFO.copy(entryPoint = TAIL_BUTTON, isKeyguardLocked = true, user = user10)
whenever(keyguardManager.isKeyguardLocked).thenReturn(expectedInfo.isKeyguardLocked)
whenever(resolver.resolveInfo(any(), any(), any())).thenReturn(expectedInfo)
createNoteTaskController()
.showNoteTaskAsUser(entryPoint = expectedInfo.entryPoint!!, user = user10)
val intentCaptor = argumentCaptor<Intent>()
val userCaptor = argumentCaptor<UserHandle>()
verify(context).startActivityAsUser(capture(intentCaptor), capture(userCaptor))
assertThat(intentCaptor.value).run {
hasAction(ACTION_CREATE_NOTE)
hasPackage(NOTE_TASK_PACKAGE_NAME)
hasFlags(FLAG_ACTIVITY_NEW_TASK)
hasFlags(FLAG_ACTIVITY_MULTIPLE_TASK)
hasFlags(FLAG_ACTIVITY_NEW_DOCUMENT)
extras().bool(EXTRA_USE_STYLUS_MODE).isTrue()
}
assertThat(userCaptor.value).isEqualTo(user10)
verify(eventLogger).logNoteTaskOpened(expectedInfo)
verifyZeroInteractions(bubbles)
}
@Test
fun showNoteTask_keyguardIsLocked_noteIsOpen_shouldCloseActivityAndLogUiEvent() {
val expectedInfo = NOTE_TASK_INFO.copy(entryPoint = TAIL_BUTTON, isKeyguardLocked = true)
whenever(keyguardManager.isKeyguardLocked).thenReturn(expectedInfo.isKeyguardLocked)
whenever(resolver.resolveInfo(any(), any(), any())).thenReturn(expectedInfo)
whenever(activityManager.getRunningTasks(anyInt()))
.thenReturn(listOf(NOTE_RUNNING_TASK_INFO))
createNoteTaskController().showNoteTask(entryPoint = expectedInfo.entryPoint!!)
val intentCaptor = argumentCaptor<Intent>()
val userCaptor = argumentCaptor<UserHandle>()
verify(context).startActivityAsUser(capture(intentCaptor), capture(userCaptor))
assertThat(intentCaptor.value).run {
hasAction(ACTION_MAIN)
categories().contains(CATEGORY_HOME)
hasFlags(FLAG_ACTIVITY_NEW_TASK)
}
assertThat(userCaptor.value).isEqualTo(userTracker.userHandle)
verify(eventLogger).logNoteTaskClosed(expectedInfo)
verifyZeroInteractions(bubbles)
}
@Test
fun showNoteTask_keyguardIsUnlocked_shouldStartBubblesWithoutLoggingUiEvent() {
val expectedInfo = NOTE_TASK_INFO.copy(entryPoint = TAIL_BUTTON, isKeyguardLocked = false)
whenever(resolver.resolveInfo(any(), any(), any())).thenReturn(expectedInfo)
whenever(keyguardManager.isKeyguardLocked).thenReturn(expectedInfo.isKeyguardLocked)
createNoteTaskController().showNoteTask(entryPoint = expectedInfo.entryPoint!!)
// Context package name used to create bubble icon from drawable resource id
verify(context, atLeastOnce()).packageName
verifyNoteTaskOpenInBubbleInUser(userTracker.userHandle)
verifyZeroInteractions(eventLogger)
}
@Test
fun showNoteTask_bubblesIsNull_shouldDoNothing() {
createNoteTaskController(bubbles = null).showNoteTask(entryPoint = TAIL_BUTTON)

View File

@@ -16,37 +16,47 @@
package com.android.systemui.notetask
import android.os.UserHandle
import android.test.suitebuilder.annotation.SmallTest
import androidx.test.runner.AndroidJUnit4
import android.testing.AndroidTestingRunner
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.notetask.NoteTaskEntryPoint.WIDGET_PICKER_SHORTCUT_IN_MULTI_WINDOW_MODE
import com.google.common.truth.Truth.assertThat
import org.junit.Test
import org.junit.runner.RunWith
/** atest SystemUITests:NoteTaskInfoTest */
@SmallTest
@RunWith(AndroidJUnit4::class)
@RunWith(AndroidTestingRunner::class)
internal class NoteTaskInfoTest : SysuiTestCase() {
private fun createNoteTaskInfo(): NoteTaskInfo =
NoteTaskInfo(packageName = NOTES_PACKAGE_NAME, uid = NOTES_UID, UserHandle.of(0))
@Test
fun launchMode_keyguardLocked_launchModeActivity() {
val underTest = createNoteTaskInfo().copy(isKeyguardLocked = true)
val underTest = DEFAULT_INFO.copy(isKeyguardLocked = true)
assertThat(underTest.launchMode).isEqualTo(NoteTaskLaunchMode.Activity)
}
@Test
fun launchMode_keyguardUnlocked_launchModeActivity() {
val underTest = createNoteTaskInfo().copy(isKeyguardLocked = false)
fun launchMode_multiWindowMode_launchModeActivity() {
val underTest = DEFAULT_INFO.copy(entryPoint = WIDGET_PICKER_SHORTCUT_IN_MULTI_WINDOW_MODE)
assertThat(underTest.launchMode).isEqualTo(NoteTaskLaunchMode.Activity)
}
@Test
fun launchMode_keyguardUnlocked_launchModeAppBubble() {
val underTest = DEFAULT_INFO.copy(isKeyguardLocked = false)
assertThat(underTest.launchMode).isEqualTo(NoteTaskLaunchMode.AppBubble)
}
private companion object {
const val NOTES_PACKAGE_NAME = "com.android.note.app"
const val NOTES_UID = 123456
val DEFAULT_INFO =
NoteTaskInfo(
packageName = "com.android.note.app",
uid = 123456,
user = UserHandle.of(0),
)
}
}