Merge "Use Note Role Holder when opening Note Task" into tm-qpr-dev am: 1a1482dffb
Original change: https://googleplex-android-review.googlesource.com/c/platform/frameworks/base/+/21019194 Change-Id: Ie5e6ff4d55e5a229df263370be4afdc07a17f762 Signed-off-by: Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
This commit is contained in:
@@ -17,10 +17,12 @@
|
|||||||
package com.android.systemui.notetask
|
package com.android.systemui.notetask
|
||||||
|
|
||||||
import android.app.KeyguardManager
|
import android.app.KeyguardManager
|
||||||
|
import android.content.ActivityNotFoundException
|
||||||
import android.content.ComponentName
|
import android.content.ComponentName
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.content.pm.PackageManager
|
import android.content.pm.PackageManager
|
||||||
import android.os.UserManager
|
import android.os.UserManager
|
||||||
|
import android.util.Log
|
||||||
import com.android.systemui.dagger.SysUISingleton
|
import com.android.systemui.dagger.SysUISingleton
|
||||||
import com.android.systemui.notetask.shortcut.CreateNoteTaskShortcutActivity
|
import com.android.systemui.notetask.shortcut.CreateNoteTaskShortcutActivity
|
||||||
import com.android.systemui.util.kotlin.getOrNull
|
import com.android.systemui.util.kotlin.getOrNull
|
||||||
@@ -57,7 +59,7 @@ constructor(
|
|||||||
* If the keyguard is locked, notes will open as a full screen experience. A locked device has
|
* If the keyguard is locked, notes will open as a full screen experience. A locked device has
|
||||||
* no contextual information which let us use the whole screen space available.
|
* no contextual information which let us use the whole screen space available.
|
||||||
*
|
*
|
||||||
* If no in multi-window or the keyguard is unlocked, notes will open as a bubble OR it will be
|
* If not in multi-window or the keyguard is unlocked, notes will open as a bubble OR it will be
|
||||||
* collapsed if the notes bubble is already opened.
|
* collapsed if the notes bubble is already opened.
|
||||||
*
|
*
|
||||||
* That will let users open other apps in full screen, and take contextual notes.
|
* That will let users open other apps in full screen, and take contextual notes.
|
||||||
@@ -68,16 +70,23 @@ constructor(
|
|||||||
val bubbles = optionalBubbles.getOrNull() ?: return
|
val bubbles = optionalBubbles.getOrNull() ?: return
|
||||||
val keyguardManager = optionalKeyguardManager.getOrNull() ?: return
|
val keyguardManager = optionalKeyguardManager.getOrNull() ?: return
|
||||||
val userManager = optionalUserManager.getOrNull() ?: return
|
val userManager = optionalUserManager.getOrNull() ?: return
|
||||||
val intent = intentResolver.resolveIntent() ?: return
|
|
||||||
|
|
||||||
// TODO(b/249954038): We should handle direct boot (isUserUnlocked). For now, we do nothing.
|
// TODO(b/249954038): We should handle direct boot (isUserUnlocked). For now, we do nothing.
|
||||||
if (!userManager.isUserUnlocked) return
|
if (!userManager.isUserUnlocked) return
|
||||||
|
|
||||||
if (isInMultiWindowMode || keyguardManager.isKeyguardLocked) {
|
val intent = intentResolver.resolveIntent() ?: return
|
||||||
context.startActivity(intent)
|
|
||||||
} else {
|
// TODO(b/266686199): We should handle when app not available. For now, we log.
|
||||||
// TODO(b/254606432): Should include Intent.EXTRA_FLOATING_WINDOW_MODE parameter.
|
try {
|
||||||
bubbles.showOrHideAppBubble(intent)
|
if (isInMultiWindowMode || keyguardManager.isKeyguardLocked) {
|
||||||
|
context.startActivity(intent)
|
||||||
|
} else {
|
||||||
|
bubbles.showOrHideAppBubble(intent)
|
||||||
|
}
|
||||||
|
} catch (e: ActivityNotFoundException) {
|
||||||
|
val message =
|
||||||
|
"Activity not found for action: ${NoteTaskIntentResolver.ACTION_CREATE_NOTE}."
|
||||||
|
Log.e(TAG, message, e)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -106,6 +115,8 @@ constructor(
|
|||||||
}
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
|
private val TAG = NoteTaskController::class.simpleName.orEmpty()
|
||||||
|
|
||||||
// TODO(b/254604589): Use final KeyEvent.KEYCODE_* instead.
|
// TODO(b/254604589): Use final KeyEvent.KEYCODE_* instead.
|
||||||
const val NOTE_TASK_KEY_EVENT = 311
|
const val NOTE_TASK_KEY_EVENT = 311
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,70 +16,39 @@
|
|||||||
|
|
||||||
package com.android.systemui.notetask
|
package com.android.systemui.notetask
|
||||||
|
|
||||||
import android.content.ComponentName
|
import android.app.role.RoleManager
|
||||||
|
import android.content.Context
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
import android.content.pm.ActivityInfo
|
|
||||||
import android.content.pm.PackageManager
|
|
||||||
import android.content.pm.PackageManager.ResolveInfoFlags
|
|
||||||
import com.android.systemui.notetask.NoteTaskIntentResolver.Companion.ACTION_CREATE_NOTE
|
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
/**
|
|
||||||
* Class responsible to query all apps and find one that can handle the [ACTION_CREATE_NOTE]. If
|
|
||||||
* found, an [Intent] ready for be launched will be returned. Otherwise, returns null.
|
|
||||||
*
|
|
||||||
* TODO(b/248274123): should be revisited once the notes role is implemented.
|
|
||||||
*/
|
|
||||||
internal class NoteTaskIntentResolver
|
internal class NoteTaskIntentResolver
|
||||||
@Inject
|
@Inject
|
||||||
constructor(
|
constructor(
|
||||||
private val packageManager: PackageManager,
|
private val context: Context,
|
||||||
|
private val roleManager: RoleManager,
|
||||||
) {
|
) {
|
||||||
|
|
||||||
fun resolveIntent(): Intent? {
|
fun resolveIntent(): Intent? {
|
||||||
val intent = Intent(ACTION_CREATE_NOTE)
|
val packageName = roleManager.getRoleHoldersAsUser(ROLE_NOTES, context.user).firstOrNull()
|
||||||
val flags = ResolveInfoFlags.of(PackageManager.MATCH_DEFAULT_ONLY.toLong())
|
|
||||||
val infoList = packageManager.queryIntentActivities(intent, flags)
|
|
||||||
|
|
||||||
for (info in infoList) {
|
if (packageName.isNullOrEmpty()) return null
|
||||||
val packageName = info.activityInfo.applicationInfo.packageName ?: continue
|
|
||||||
val activityName = resolveActivityNameForNotesAction(packageName) ?: continue
|
|
||||||
|
|
||||||
return Intent(ACTION_CREATE_NOTE)
|
return Intent(ACTION_CREATE_NOTE)
|
||||||
.setPackage(packageName)
|
.setPackage(packageName)
|
||||||
.setComponent(ComponentName(packageName, activityName))
|
.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
|
||||||
.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 null
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun resolveActivityNameForNotesAction(packageName: String): String? {
|
|
||||||
val intent = Intent(ACTION_CREATE_NOTE).setPackage(packageName)
|
|
||||||
val flags = ResolveInfoFlags.of(PackageManager.MATCH_DEFAULT_ONLY.toLong())
|
|
||||||
val resolveInfo = packageManager.resolveActivity(intent, flags)
|
|
||||||
|
|
||||||
val activityInfo = resolveInfo?.activityInfo ?: return null
|
|
||||||
if (activityInfo.name.isNullOrBlank()) return null
|
|
||||||
if (!activityInfo.exported) return null
|
|
||||||
if (!activityInfo.enabled) return null
|
|
||||||
if (!activityInfo.showWhenLocked) return null
|
|
||||||
if (!activityInfo.turnScreenOn) return null
|
|
||||||
|
|
||||||
return activityInfo.name
|
|
||||||
}
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
// TODO(b/254606432): Use Intent.ACTION_CREATE_NOTE instead.
|
// TODO(b/265912743): Use Intent.ACTION_CREATE_NOTE instead.
|
||||||
const val ACTION_CREATE_NOTE = "android.intent.action.CREATE_NOTE"
|
const val ACTION_CREATE_NOTE = "android.intent.action.CREATE_NOTE"
|
||||||
|
|
||||||
// TODO(b/265912743): Use RoleManager.NOTES_ROLE instead.
|
// TODO(b/265912743): Use RoleManager.NOTES_ROLE instead.
|
||||||
const val NOTE_ROLE = "android.app.role.NOTES"
|
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"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private val ActivityInfo.showWhenLocked: Boolean
|
|
||||||
get() = flags and ActivityInfo.FLAG_SHOW_WHEN_LOCKED != 0
|
|
||||||
|
|
||||||
private val ActivityInfo.turnScreenOn: Boolean
|
|
||||||
get() = flags and ActivityInfo.FLAG_TURN_SCREEN_ON != 0
|
|
||||||
|
|||||||
@@ -51,7 +51,7 @@ internal interface NoteTaskModule {
|
|||||||
featureFlags: FeatureFlags,
|
featureFlags: FeatureFlags,
|
||||||
roleManager: RoleManager,
|
roleManager: RoleManager,
|
||||||
): Boolean {
|
): Boolean {
|
||||||
val isRoleAvailable = roleManager.isRoleAvailable(NoteTaskIntentResolver.NOTE_ROLE)
|
val isRoleAvailable = roleManager.isRoleAvailable(NoteTaskIntentResolver.ROLE_NOTES)
|
||||||
val isFeatureEnabled = featureFlags.isEnabled(Flags.NOTE_TASKS)
|
val isFeatureEnabled = featureFlags.isEnabled(Flags.NOTE_TASKS)
|
||||||
return isRoleAvailable && isFeatureEnabled
|
return isRoleAvailable && isFeatureEnabled
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,17 +16,14 @@
|
|||||||
|
|
||||||
package com.android.systemui.notetask
|
package com.android.systemui.notetask
|
||||||
|
|
||||||
import android.content.ComponentName
|
import android.app.role.RoleManager
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
import android.content.pm.ActivityInfo
|
|
||||||
import android.content.pm.ApplicationInfo
|
|
||||||
import android.content.pm.PackageManager
|
import android.content.pm.PackageManager
|
||||||
import android.content.pm.PackageManager.ResolveInfoFlags
|
|
||||||
import android.content.pm.ResolveInfo
|
|
||||||
import android.test.suitebuilder.annotation.SmallTest
|
import android.test.suitebuilder.annotation.SmallTest
|
||||||
import androidx.test.runner.AndroidJUnit4
|
import androidx.test.runner.AndroidJUnit4
|
||||||
import com.android.systemui.SysuiTestCase
|
import com.android.systemui.SysuiTestCase
|
||||||
import com.android.systemui.notetask.NoteTaskIntentResolver.Companion.ACTION_CREATE_NOTE
|
import com.android.systemui.notetask.NoteTaskIntentResolver.Companion.ACTION_CREATE_NOTE
|
||||||
|
import com.android.systemui.util.mockito.eq
|
||||||
import com.android.systemui.util.mockito.whenever
|
import com.android.systemui.util.mockito.whenever
|
||||||
import com.google.common.truth.Truth.assertThat
|
import com.google.common.truth.Truth.assertThat
|
||||||
import org.junit.Before
|
import org.junit.Before
|
||||||
@@ -47,172 +44,39 @@ import org.mockito.MockitoAnnotations
|
|||||||
internal class NoteTaskIntentResolverTest : SysuiTestCase() {
|
internal class NoteTaskIntentResolverTest : SysuiTestCase() {
|
||||||
|
|
||||||
@Mock lateinit var packageManager: PackageManager
|
@Mock lateinit var packageManager: PackageManager
|
||||||
|
@Mock lateinit var roleManager: RoleManager
|
||||||
|
|
||||||
private lateinit var resolver: NoteTaskIntentResolver
|
private lateinit var underTest: NoteTaskIntentResolver
|
||||||
|
|
||||||
@Before
|
@Before
|
||||||
fun setUp() {
|
fun setUp() {
|
||||||
MockitoAnnotations.initMocks(this)
|
MockitoAnnotations.initMocks(this)
|
||||||
resolver = NoteTaskIntentResolver(packageManager)
|
underTest = NoteTaskIntentResolver(context, roleManager)
|
||||||
}
|
|
||||||
|
|
||||||
private fun createResolveInfo(
|
|
||||||
activityInfo: ActivityInfo? = createActivityInfo(),
|
|
||||||
): ResolveInfo {
|
|
||||||
return ResolveInfo().apply { this.activityInfo = activityInfo }
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun createActivityInfo(
|
|
||||||
packageName: String = "PackageName",
|
|
||||||
name: String? = "ActivityName",
|
|
||||||
exported: Boolean = true,
|
|
||||||
enabled: Boolean = true,
|
|
||||||
showWhenLocked: Boolean = true,
|
|
||||||
turnScreenOn: Boolean = true,
|
|
||||||
): ActivityInfo {
|
|
||||||
return ActivityInfo().apply {
|
|
||||||
this.name = name
|
|
||||||
this.exported = exported
|
|
||||||
this.enabled = enabled
|
|
||||||
if (showWhenLocked) {
|
|
||||||
flags = flags or ActivityInfo.FLAG_SHOW_WHEN_LOCKED
|
|
||||||
}
|
|
||||||
if (turnScreenOn) {
|
|
||||||
flags = flags or ActivityInfo.FLAG_TURN_SCREEN_ON
|
|
||||||
}
|
|
||||||
this.applicationInfo = ApplicationInfo().apply { this.packageName = packageName }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun givenQueryIntentActivities(block: () -> List<ResolveInfo>) {
|
|
||||||
whenever(packageManager.queryIntentActivities(any(), any<ResolveInfoFlags>()))
|
|
||||||
.thenReturn(block())
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun givenResolveActivity(block: () -> ResolveInfo?) {
|
|
||||||
whenever(packageManager.resolveActivity(any(), any<ResolveInfoFlags>())).thenReturn(block())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun resolveIntent_shouldReturnNotesIntent() {
|
fun resolveIntent_shouldReturnIntentInStylusMode() {
|
||||||
givenQueryIntentActivities { listOf(createResolveInfo()) }
|
val packageName = "com.android.note.app"
|
||||||
givenResolveActivity { createResolveInfo(activityInfo = createActivityInfo()) }
|
whenever(roleManager.getRoleHoldersAsUser(NoteTaskIntentResolver.ROLE_NOTES, context.user))
|
||||||
|
.then { listOf(packageName) }
|
||||||
|
|
||||||
val actual = resolver.resolveIntent()
|
val actual = underTest.resolveIntent()
|
||||||
|
|
||||||
val expected =
|
requireNotNull(actual) { "Intent must not be null" }
|
||||||
Intent(ACTION_CREATE_NOTE)
|
assertThat(actual.action).isEqualTo(ACTION_CREATE_NOTE)
|
||||||
.setPackage("PackageName")
|
assertThat(actual.`package`).isEqualTo(packageName)
|
||||||
.setComponent(ComponentName("PackageName", "ActivityName"))
|
val expectedExtra = actual.getExtra(NoteTaskIntentResolver.INTENT_EXTRA_USE_STYLUS_MODE)
|
||||||
.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
|
assertThat(expectedExtra).isEqualTo(true)
|
||||||
// Compares the string representation of both intents, as they are different instances.
|
val expectedFlag = actual.flags and Intent.FLAG_ACTIVITY_NEW_TASK
|
||||||
assertThat(actual.toString()).isEqualTo(expected.toString())
|
assertThat(expectedFlag).isEqualTo(Intent.FLAG_ACTIVITY_NEW_TASK)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun resolveIntent_activityInfoEnabledIsFalse_shouldReturnNull() {
|
fun resolveIntent_noRoleHolderIsSet_shouldReturnNull() {
|
||||||
givenQueryIntentActivities { listOf(createResolveInfo()) }
|
whenever(roleManager.getRoleHoldersAsUser(eq(NoteTaskIntentResolver.ROLE_NOTES), any()))
|
||||||
givenResolveActivity {
|
.then { listOf<String>() }
|
||||||
createResolveInfo(activityInfo = createActivityInfo(enabled = false))
|
|
||||||
}
|
|
||||||
|
|
||||||
val actual = resolver.resolveIntent()
|
val actual = underTest.resolveIntent()
|
||||||
|
|
||||||
assertThat(actual).isNull()
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun resolveIntent_activityInfoExportedIsFalse_shouldReturnNull() {
|
|
||||||
givenQueryIntentActivities { listOf(createResolveInfo()) }
|
|
||||||
givenResolveActivity {
|
|
||||||
createResolveInfo(activityInfo = createActivityInfo(exported = false))
|
|
||||||
}
|
|
||||||
|
|
||||||
val actual = resolver.resolveIntent()
|
|
||||||
|
|
||||||
assertThat(actual).isNull()
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun resolveIntent_activityInfoShowWhenLockedIsFalse_shouldReturnNull() {
|
|
||||||
givenQueryIntentActivities { listOf(createResolveInfo()) }
|
|
||||||
givenResolveActivity {
|
|
||||||
createResolveInfo(activityInfo = createActivityInfo(showWhenLocked = false))
|
|
||||||
}
|
|
||||||
|
|
||||||
val actual = resolver.resolveIntent()
|
|
||||||
|
|
||||||
assertThat(actual).isNull()
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun resolveIntent_activityInfoTurnScreenOnIsFalse_shouldReturnNull() {
|
|
||||||
givenQueryIntentActivities { listOf(createResolveInfo()) }
|
|
||||||
givenResolveActivity {
|
|
||||||
createResolveInfo(activityInfo = createActivityInfo(turnScreenOn = false))
|
|
||||||
}
|
|
||||||
|
|
||||||
val actual = resolver.resolveIntent()
|
|
||||||
|
|
||||||
assertThat(actual).isNull()
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun resolveIntent_activityInfoNameIsBlank_shouldReturnNull() {
|
|
||||||
givenQueryIntentActivities { listOf(createResolveInfo()) }
|
|
||||||
givenResolveActivity { createResolveInfo(activityInfo = createActivityInfo(name = "")) }
|
|
||||||
|
|
||||||
val actual = resolver.resolveIntent()
|
|
||||||
|
|
||||||
assertThat(actual).isNull()
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun resolveIntent_activityInfoNameIsNull_shouldReturnNull() {
|
|
||||||
givenQueryIntentActivities { listOf(createResolveInfo()) }
|
|
||||||
givenResolveActivity { createResolveInfo(activityInfo = createActivityInfo(name = null)) }
|
|
||||||
|
|
||||||
val actual = resolver.resolveIntent()
|
|
||||||
|
|
||||||
assertThat(actual).isNull()
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun resolveIntent_activityInfoIsNull_shouldReturnNull() {
|
|
||||||
givenQueryIntentActivities { listOf(createResolveInfo()) }
|
|
||||||
givenResolveActivity { createResolveInfo(activityInfo = null) }
|
|
||||||
|
|
||||||
val actual = resolver.resolveIntent()
|
|
||||||
|
|
||||||
assertThat(actual).isNull()
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun resolveIntent_resolveActivityIsNull_shouldReturnNull() {
|
|
||||||
givenQueryIntentActivities { listOf(createResolveInfo()) }
|
|
||||||
givenResolveActivity { null }
|
|
||||||
|
|
||||||
val actual = resolver.resolveIntent()
|
|
||||||
|
|
||||||
assertThat(actual).isNull()
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun resolveIntent_packageNameIsBlank_shouldReturnNull() {
|
|
||||||
givenQueryIntentActivities {
|
|
||||||
listOf(createResolveInfo(createActivityInfo(packageName = "")))
|
|
||||||
}
|
|
||||||
|
|
||||||
val actual = resolver.resolveIntent()
|
|
||||||
|
|
||||||
assertThat(actual).isNull()
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun resolveIntent_activityNotFoundForAction_shouldReturnNull() {
|
|
||||||
givenQueryIntentActivities { emptyList() }
|
|
||||||
|
|
||||||
val actual = resolver.resolveIntent()
|
|
||||||
|
|
||||||
assertThat(actual).isNull()
|
assertThat(actual).isNull()
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user