Support split-mode on Note Shortcut

Test: atest NoteTaskInfoTest
Flag: not needed
Fixes: b/284296041
Change-Id: I90adb9eaf7cb33968db38daa8322ecd8a0a62939
This commit is contained in:
Marcello Galhardo
2023-06-02 14:55:33 +00:00
parent 9ce13dd292
commit cb876c5e29
9 changed files with 159 additions and 112 deletions

View File

@@ -1003,7 +1003,6 @@
android:name=".notetask.shortcut.LaunchNoteTaskActivity"
android:exported="true"
android:excludeFromRecents="true"
android:resizeableActivity="false"
android:theme="@android:style/Theme.NoDisplay" >
<intent-filter>
@@ -1016,7 +1015,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),
)
}
}