Merge "Update Note Task shortcut when Notes Role is changed" into udc-dev
This commit is contained in:
committed by
Android (Google) Code Review
commit
9bdd988037
@@ -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" />
|
||||
|
||||
@@ -0,0 +1,34 @@
|
||||
/*
|
||||
* 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
|
||||
@@ -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())
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,61 @@
|
||||
/*
|
||||
* 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
|
||||
|
||||
import android.app.role.RoleManager
|
||||
import android.app.role.RoleManager.ROLE_NOTES
|
||||
import android.content.Context
|
||||
import android.content.pm.ShortcutInfo
|
||||
import android.graphics.drawable.Icon
|
||||
import android.os.PersistableBundle
|
||||
import android.os.UserHandle
|
||||
import com.android.systemui.R
|
||||
import com.android.systemui.notetask.shortcut.LaunchNoteTaskActivity
|
||||
|
||||
/** Extension functions for [RoleManager] used **internally** by note task. */
|
||||
@InternalNoteTaskApi
|
||||
internal object NoteTaskRoleManagerExt {
|
||||
|
||||
/**
|
||||
* Gets package name of the default (first) app holding the [role]. If none, returns either an
|
||||
* empty string or null.
|
||||
*/
|
||||
fun RoleManager.getDefaultRoleHolderAsUser(role: String, user: UserHandle): String? =
|
||||
getRoleHoldersAsUser(role, user).firstOrNull()
|
||||
|
||||
/** Creates a [ShortcutInfo] for [ROLE_NOTES]. */
|
||||
fun RoleManager.createNoteShortcutInfoAsUser(
|
||||
context: Context,
|
||||
user: UserHandle,
|
||||
): ShortcutInfo {
|
||||
val extras = PersistableBundle()
|
||||
getDefaultRoleHolderAsUser(ROLE_NOTES, user)?.let { packageName ->
|
||||
// Set custom app badge using the icon from ROLES_NOTES default app.
|
||||
extras.putString(NoteTaskController.EXTRA_SHORTCUT_BADGE_OVERRIDE_PACKAGE, packageName)
|
||||
}
|
||||
|
||||
val icon = Icon.createWithResource(context, R.drawable.ic_note_task_shortcut_widget)
|
||||
|
||||
return ShortcutInfo.Builder(context, NoteTaskController.SHORTCUT_ID)
|
||||
.setIntent(LaunchNoteTaskActivity.newIntent(context = context))
|
||||
.setShortLabel(context.getString(R.string.note_task_button_label))
|
||||
.setLongLived(true)
|
||||
.setIcon(icon)
|
||||
.setExtras(extras)
|
||||
.build()
|
||||
}
|
||||
}
|
||||
@@ -14,19 +14,17 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
@file:OptIn(InternalNoteTaskApi::class)
|
||||
|
||||
package com.android.systemui.notetask.shortcut
|
||||
|
||||
import android.app.Activity
|
||||
import android.app.role.RoleManager
|
||||
import android.content.Intent
|
||||
import android.content.pm.ShortcutManager
|
||||
import android.os.Bundle
|
||||
import android.os.PersistableBundle
|
||||
import androidx.activity.ComponentActivity
|
||||
import androidx.annotation.DrawableRes
|
||||
import androidx.core.content.pm.ShortcutInfoCompat
|
||||
import androidx.core.content.pm.ShortcutManagerCompat
|
||||
import androidx.core.graphics.drawable.IconCompat
|
||||
import com.android.systemui.R
|
||||
import com.android.systemui.notetask.InternalNoteTaskApi
|
||||
import com.android.systemui.notetask.NoteTaskRoleManagerExt.createNoteShortcutInfoAsUser
|
||||
import javax.inject.Inject
|
||||
|
||||
/**
|
||||
@@ -42,62 +40,16 @@ class CreateNoteTaskShortcutActivity
|
||||
@Inject
|
||||
constructor(
|
||||
private val roleManager: RoleManager,
|
||||
private val shortcutManager: ShortcutManager,
|
||||
) : ComponentActivity() {
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
|
||||
val intent =
|
||||
createShortcutIntent(
|
||||
id = SHORTCUT_ID,
|
||||
shortLabel = getString(R.string.note_task_button_label),
|
||||
intent = LaunchNoteTaskActivity.newIntent(context = this),
|
||||
iconResource = R.drawable.ic_note_task_shortcut_widget,
|
||||
)
|
||||
setResult(Activity.RESULT_OK, intent)
|
||||
val shortcutInfo = roleManager.createNoteShortcutInfoAsUser(context = this, user)
|
||||
val shortcutIntent = shortcutManager.createShortcutResultIntent(shortcutInfo)
|
||||
setResult(Activity.RESULT_OK, shortcutIntent)
|
||||
|
||||
finish()
|
||||
}
|
||||
|
||||
private fun createShortcutIntent(
|
||||
id: String,
|
||||
shortLabel: String,
|
||||
intent: Intent,
|
||||
@DrawableRes iconResource: Int,
|
||||
): Intent {
|
||||
val extras = PersistableBundle()
|
||||
|
||||
roleManager.getRoleHoldersAsUser(RoleManager.ROLE_NOTES, user).firstOrNull()?.let { name ->
|
||||
extras.putString(EXTRA_SHORTCUT_BADGE_OVERRIDE_PACKAGE, name)
|
||||
}
|
||||
|
||||
val shortcutInfo =
|
||||
ShortcutInfoCompat.Builder(this, id)
|
||||
.setIntent(intent)
|
||||
.setShortLabel(shortLabel)
|
||||
.setLongLived(true)
|
||||
.setIcon(IconCompat.createWithResource(this, iconResource))
|
||||
.setExtras(extras)
|
||||
.build()
|
||||
|
||||
return ShortcutManagerCompat.createShortcutResultIntent(
|
||||
this,
|
||||
shortcutInfo,
|
||||
)
|
||||
}
|
||||
|
||||
private companion object {
|
||||
private 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
|
||||
*/
|
||||
private const val EXTRA_SHORTCUT_BADGE_OVERRIDE_PACKAGE =
|
||||
"extra_shortcut_badge_override_package"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,10 +13,14 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* 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.RoleManager
|
||||
import android.app.role.RoleManager.ROLE_NOTES
|
||||
import android.content.ComponentName
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
@@ -24,12 +28,20 @@ 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.PackageManager.COMPONENT_ENABLED_STATE_DISABLED
|
||||
import android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_ENABLED
|
||||
import android.content.pm.ShortcutInfo
|
||||
import android.content.pm.ShortcutManager
|
||||
import android.os.UserHandle
|
||||
import android.os.UserManager
|
||||
import androidx.test.filters.SmallTest
|
||||
import androidx.test.runner.AndroidJUnit4
|
||||
import com.android.systemui.R
|
||||
import com.android.systemui.SysuiTestCase
|
||||
import com.android.systemui.notetask.NoteTaskController.Companion.EXTRA_SHORTCUT_BADGE_OVERRIDE_PACKAGE
|
||||
import com.android.systemui.notetask.NoteTaskController.Companion.SHORTCUT_ID
|
||||
import com.android.systemui.notetask.shortcut.CreateNoteTaskShortcutActivity
|
||||
import com.android.systemui.notetask.shortcut.LaunchNoteTaskActivity
|
||||
import com.android.systemui.settings.FakeUserTracker
|
||||
import com.android.systemui.settings.UserTracker
|
||||
import com.android.systemui.util.mockito.any
|
||||
@@ -47,6 +59,7 @@ import org.junit.runner.RunWith
|
||||
import org.mockito.ArgumentMatchers.anyInt
|
||||
import org.mockito.Mock
|
||||
import org.mockito.Mockito.isNull
|
||||
import org.mockito.Mockito.never
|
||||
import org.mockito.Mockito.verify
|
||||
import org.mockito.Mockito.verifyZeroInteractions
|
||||
import org.mockito.MockitoAnnotations
|
||||
@@ -63,15 +76,17 @@ internal class NoteTaskControllerTest : SysuiTestCase() {
|
||||
@Mock lateinit var keyguardManager: KeyguardManager
|
||||
@Mock lateinit var userManager: UserManager
|
||||
@Mock lateinit var eventLogger: NoteTaskEventLogger
|
||||
@Mock lateinit var roleManager: RoleManager
|
||||
@Mock lateinit var shortcutManager: ShortcutManager
|
||||
@Mock private lateinit var devicePolicyManager: DevicePolicyManager
|
||||
private val userTracker: UserTracker = FakeUserTracker()
|
||||
|
||||
private val noteTaskInfo = NoteTaskInfo(packageName = NOTES_PACKAGE_NAME, uid = NOTES_UID)
|
||||
|
||||
@Before
|
||||
fun setUp() {
|
||||
MockitoAnnotations.initMocks(this)
|
||||
|
||||
whenever(context.getString(R.string.note_task_button_label)).thenReturn(NOTES_SHORT_LABEL)
|
||||
whenever(context.packageManager).thenReturn(packageManager)
|
||||
whenever(resolver.resolveInfo(any(), any())).thenReturn(noteTaskInfo)
|
||||
whenever(userManager.isUserUnlocked).thenReturn(true)
|
||||
@@ -82,6 +97,8 @@ internal class NoteTaskControllerTest : SysuiTestCase() {
|
||||
)
|
||||
)
|
||||
.thenReturn(DevicePolicyManager.KEYGUARD_DISABLE_FEATURES_NONE)
|
||||
whenever(roleManager.getRoleHoldersAsUser(ROLE_NOTES, userTracker.userHandle))
|
||||
.thenReturn(listOf(NOTES_PACKAGE_NAME))
|
||||
}
|
||||
|
||||
private fun createNoteTaskController(
|
||||
@@ -98,6 +115,8 @@ internal class NoteTaskControllerTest : SysuiTestCase() {
|
||||
isEnabled = isEnabled,
|
||||
devicePolicyManager = devicePolicyManager,
|
||||
userTracker = userTracker,
|
||||
roleManager = roleManager,
|
||||
shortcutManager = shortcutManager,
|
||||
)
|
||||
|
||||
// region onBubbleExpandChanged
|
||||
@@ -132,7 +151,7 @@ internal class NoteTaskControllerTest : SysuiTestCase() {
|
||||
}
|
||||
|
||||
@Test
|
||||
fun onBubbleExpandChanged_expandingAndKeyguardLocked_doNothing() {
|
||||
fun onBubbleExpandChanged_expandingAndKeyguardLocked_shouldDoNothing() {
|
||||
val expectedInfo = noteTaskInfo.copy(isKeyguardLocked = true)
|
||||
|
||||
createNoteTaskController()
|
||||
@@ -146,7 +165,7 @@ internal class NoteTaskControllerTest : SysuiTestCase() {
|
||||
}
|
||||
|
||||
@Test
|
||||
fun onBubbleExpandChanged_notExpandingAndKeyguardLocked_doNothing() {
|
||||
fun onBubbleExpandChanged_notExpandingAndKeyguardLocked_shouldDoNothing() {
|
||||
val expectedInfo = noteTaskInfo.copy(isKeyguardLocked = true)
|
||||
|
||||
createNoteTaskController()
|
||||
@@ -268,8 +287,8 @@ internal class NoteTaskControllerTest : SysuiTestCase() {
|
||||
|
||||
verifyZeroInteractions(context)
|
||||
val intentCaptor = argumentCaptor<Intent>()
|
||||
verify(bubbles).showOrHideAppBubble(capture(intentCaptor), eq(userTracker.userHandle),
|
||||
isNull())
|
||||
verify(bubbles)
|
||||
.showOrHideAppBubble(capture(intentCaptor), eq(userTracker.userHandle), isNull())
|
||||
intentCaptor.value.let { intent ->
|
||||
assertThat(intent.action).isEqualTo(Intent.ACTION_CREATE_NOTE)
|
||||
assertThat(intent.`package`).isEqualTo(NOTES_PACKAGE_NAME)
|
||||
@@ -333,11 +352,11 @@ internal class NoteTaskControllerTest : SysuiTestCase() {
|
||||
verify(context.packageManager)
|
||||
.setComponentEnabledSetting(
|
||||
argument.capture(),
|
||||
eq(PackageManager.COMPONENT_ENABLED_STATE_ENABLED),
|
||||
eq(COMPONENT_ENABLED_STATE_ENABLED),
|
||||
eq(PackageManager.DONT_KILL_APP),
|
||||
)
|
||||
val expected = ComponentName(context, CreateNoteTaskShortcutActivity::class.java)
|
||||
assertThat(argument.value.flattenToString()).isEqualTo(expected.flattenToString())
|
||||
assertThat(argument.value.className)
|
||||
.isEqualTo(CreateNoteTaskShortcutActivity::class.java.name)
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -348,11 +367,11 @@ internal class NoteTaskControllerTest : SysuiTestCase() {
|
||||
verify(context.packageManager)
|
||||
.setComponentEnabledSetting(
|
||||
argument.capture(),
|
||||
eq(PackageManager.COMPONENT_ENABLED_STATE_DISABLED),
|
||||
eq(COMPONENT_ENABLED_STATE_DISABLED),
|
||||
eq(PackageManager.DONT_KILL_APP),
|
||||
)
|
||||
val expected = ComponentName(context, CreateNoteTaskShortcutActivity::class.java)
|
||||
assertThat(argument.value.flattenToString()).isEqualTo(expected.flattenToString())
|
||||
assertThat(argument.value.className)
|
||||
.isEqualTo(CreateNoteTaskShortcutActivity::class.java.name)
|
||||
}
|
||||
// endregion
|
||||
|
||||
@@ -403,8 +422,8 @@ internal class NoteTaskControllerTest : SysuiTestCase() {
|
||||
createNoteTaskController().showNoteTask(entryPoint = NoteTaskEntryPoint.QUICK_AFFORDANCE)
|
||||
|
||||
val intentCaptor = argumentCaptor<Intent>()
|
||||
verify(bubbles).showOrHideAppBubble(capture(intentCaptor), eq(userTracker.userHandle),
|
||||
isNull())
|
||||
verify(bubbles)
|
||||
.showOrHideAppBubble(capture(intentCaptor), eq(userTracker.userHandle), isNull())
|
||||
intentCaptor.value.let { intent ->
|
||||
assertThat(intent.action).isEqualTo(Intent.ACTION_CREATE_NOTE)
|
||||
assertThat(intent.`package`).isEqualTo(NOTES_PACKAGE_NAME)
|
||||
@@ -427,8 +446,8 @@ internal class NoteTaskControllerTest : SysuiTestCase() {
|
||||
createNoteTaskController().showNoteTask(entryPoint = NoteTaskEntryPoint.QUICK_AFFORDANCE)
|
||||
|
||||
val intentCaptor = argumentCaptor<Intent>()
|
||||
verify(bubbles).showOrHideAppBubble(capture(intentCaptor), eq(userTracker.userHandle),
|
||||
isNull())
|
||||
verify(bubbles)
|
||||
.showOrHideAppBubble(capture(intentCaptor), eq(userTracker.userHandle), isNull())
|
||||
intentCaptor.value.let { intent ->
|
||||
assertThat(intent.action).isEqualTo(Intent.ACTION_CREATE_NOTE)
|
||||
assertThat(intent.`package`).isEqualTo(NOTES_PACKAGE_NAME)
|
||||
@@ -438,7 +457,78 @@ internal class NoteTaskControllerTest : SysuiTestCase() {
|
||||
}
|
||||
// endregion
|
||||
|
||||
// region updateNoteTaskAsUser
|
||||
@Test
|
||||
fun updateNoteTaskAsUser_withNotesRole_withShortcuts_shouldUpdateShortcuts() {
|
||||
createNoteTaskController(isEnabled = true).updateNoteTaskAsUser(userTracker.userHandle)
|
||||
|
||||
val actualComponent = argumentCaptor<ComponentName>()
|
||||
verify(context.packageManager)
|
||||
.setComponentEnabledSetting(
|
||||
actualComponent.capture(),
|
||||
eq(COMPONENT_ENABLED_STATE_ENABLED),
|
||||
eq(PackageManager.DONT_KILL_APP),
|
||||
)
|
||||
assertThat(actualComponent.value.className)
|
||||
.isEqualTo(CreateNoteTaskShortcutActivity::class.java.name)
|
||||
verify(shortcutManager, never()).disableShortcuts(any())
|
||||
verify(shortcutManager).enableShortcuts(listOf(SHORTCUT_ID))
|
||||
val actualShortcuts = argumentCaptor<List<ShortcutInfo>>()
|
||||
verify(shortcutManager).updateShortcuts(actualShortcuts.capture())
|
||||
val actualShortcut = actualShortcuts.value.first()
|
||||
assertThat(actualShortcut.id).isEqualTo(SHORTCUT_ID)
|
||||
assertThat(actualShortcut.intent?.component?.className)
|
||||
.isEqualTo(LaunchNoteTaskActivity::class.java.name)
|
||||
assertThat(actualShortcut.intent?.action).isEqualTo(Intent.ACTION_CREATE_NOTE)
|
||||
assertThat(actualShortcut.shortLabel).isEqualTo(NOTES_SHORT_LABEL)
|
||||
assertThat(actualShortcut.isLongLived).isEqualTo(true)
|
||||
assertThat(actualShortcut.icon.resId).isEqualTo(R.drawable.ic_note_task_shortcut_widget)
|
||||
assertThat(actualShortcut.extras?.getString(EXTRA_SHORTCUT_BADGE_OVERRIDE_PACKAGE))
|
||||
.isEqualTo(NOTES_PACKAGE_NAME)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun updateNoteTaskAsUser_noNotesRole_shouldDisableShortcuts() {
|
||||
whenever(roleManager.getRoleHoldersAsUser(ROLE_NOTES, userTracker.userHandle))
|
||||
.thenReturn(emptyList())
|
||||
|
||||
createNoteTaskController(isEnabled = true).updateNoteTaskAsUser(userTracker.userHandle)
|
||||
|
||||
val argument = argumentCaptor<ComponentName>()
|
||||
verify(context.packageManager)
|
||||
.setComponentEnabledSetting(
|
||||
argument.capture(),
|
||||
eq(COMPONENT_ENABLED_STATE_DISABLED),
|
||||
eq(PackageManager.DONT_KILL_APP),
|
||||
)
|
||||
assertThat(argument.value.className)
|
||||
.isEqualTo(CreateNoteTaskShortcutActivity::class.java.name)
|
||||
verify(shortcutManager).disableShortcuts(listOf(SHORTCUT_ID))
|
||||
verify(shortcutManager, never()).enableShortcuts(any())
|
||||
verify(shortcutManager, never()).updateShortcuts(any())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun updateNoteTaskAsUser_flagDisabled_shouldDisableShortcuts() {
|
||||
createNoteTaskController(isEnabled = false).updateNoteTaskAsUser(userTracker.userHandle)
|
||||
|
||||
val argument = argumentCaptor<ComponentName>()
|
||||
verify(context.packageManager)
|
||||
.setComponentEnabledSetting(
|
||||
argument.capture(),
|
||||
eq(COMPONENT_ENABLED_STATE_DISABLED),
|
||||
eq(PackageManager.DONT_KILL_APP),
|
||||
)
|
||||
assertThat(argument.value.className)
|
||||
.isEqualTo(CreateNoteTaskShortcutActivity::class.java.name)
|
||||
verify(shortcutManager).disableShortcuts(listOf(SHORTCUT_ID))
|
||||
verify(shortcutManager, never()).enableShortcuts(any())
|
||||
verify(shortcutManager, never()).updateShortcuts(any())
|
||||
}
|
||||
// endregion
|
||||
|
||||
private companion object {
|
||||
const val NOTES_SHORT_LABEL = "Notetaking"
|
||||
const val NOTES_PACKAGE_NAME = "com.android.note.app"
|
||||
const val NOTES_UID = 123456
|
||||
}
|
||||
|
||||
@@ -15,36 +15,37 @@
|
||||
*/
|
||||
package com.android.systemui.notetask
|
||||
|
||||
import android.app.role.RoleManager
|
||||
import android.test.suitebuilder.annotation.SmallTest
|
||||
import android.view.KeyEvent
|
||||
import androidx.test.runner.AndroidJUnit4
|
||||
import com.android.systemui.SysuiTestCase
|
||||
import com.android.systemui.statusbar.CommandQueue
|
||||
import com.android.systemui.util.concurrency.FakeExecutor
|
||||
import com.android.systemui.util.mockito.any
|
||||
import com.android.systemui.util.time.FakeSystemClock
|
||||
import com.android.wm.shell.bubbles.Bubbles
|
||||
import java.util.Optional
|
||||
import org.junit.Before
|
||||
import org.junit.Test
|
||||
import org.junit.runner.RunWith
|
||||
import org.mockito.ArgumentMatchers.any
|
||||
import org.mockito.Mock
|
||||
import org.mockito.Mockito.never
|
||||
import org.mockito.Mockito.verify
|
||||
import org.mockito.Mockito.verifyZeroInteractions
|
||||
import org.mockito.MockitoAnnotations
|
||||
|
||||
/**
|
||||
* Tests for [NoteTaskController].
|
||||
*
|
||||
* Build/Install/Run:
|
||||
* - atest SystemUITests:NoteTaskInitializerTest
|
||||
*/
|
||||
/** atest SystemUITests:NoteTaskInitializerTest */
|
||||
@SmallTest
|
||||
@RunWith(AndroidJUnit4::class)
|
||||
internal class NoteTaskInitializerTest : SysuiTestCase() {
|
||||
|
||||
@Mock lateinit var commandQueue: CommandQueue
|
||||
@Mock lateinit var bubbles: Bubbles
|
||||
@Mock lateinit var noteTaskController: NoteTaskController
|
||||
@Mock lateinit var controller: NoteTaskController
|
||||
@Mock lateinit var roleManager: RoleManager
|
||||
private val clock = FakeSystemClock()
|
||||
private val executor = FakeExecutor(clock)
|
||||
|
||||
@Before
|
||||
fun setUp() {
|
||||
@@ -56,47 +57,41 @@ internal class NoteTaskInitializerTest : SysuiTestCase() {
|
||||
bubbles: Bubbles? = this.bubbles,
|
||||
): NoteTaskInitializer {
|
||||
return NoteTaskInitializer(
|
||||
controller = noteTaskController,
|
||||
controller = controller,
|
||||
commandQueue = commandQueue,
|
||||
optionalBubbles = Optional.ofNullable(bubbles),
|
||||
isEnabled = isEnabled,
|
||||
roleManager = roleManager,
|
||||
backgroundExecutor = executor,
|
||||
)
|
||||
}
|
||||
|
||||
// region initializer
|
||||
@Test
|
||||
fun initialize_shouldAddCallbacks() {
|
||||
fun initialize() {
|
||||
createNoteTaskInitializer().initialize()
|
||||
|
||||
verify(controller).setNoteTaskShortcutEnabled(true)
|
||||
verify(commandQueue).addCallback(any())
|
||||
verify(roleManager).addOnRoleHoldersChangedListenerAsUser(any(), any(), any())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun initialize_flagDisabled_shouldDoNothing() {
|
||||
fun initialize_flagDisabled() {
|
||||
createNoteTaskInitializer(isEnabled = false).initialize()
|
||||
|
||||
verify(controller, never()).setNoteTaskShortcutEnabled(any())
|
||||
verify(commandQueue, never()).addCallback(any())
|
||||
verify(roleManager, never()).addOnRoleHoldersChangedListenerAsUser(any(), any(), any())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun initialize_bubblesNotPresent_shouldDoNothing() {
|
||||
fun initialize_bubblesNotPresent() {
|
||||
createNoteTaskInitializer(bubbles = null).initialize()
|
||||
|
||||
verify(controller, never()).setNoteTaskShortcutEnabled(any())
|
||||
verify(commandQueue, never()).addCallback(any())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun initialize_flagEnabled_shouldEnableShortcut() {
|
||||
createNoteTaskInitializer().initialize()
|
||||
|
||||
verify(noteTaskController).setNoteTaskShortcutEnabled(true)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun initialize_flagDisabled_shouldDisableShortcut() {
|
||||
createNoteTaskInitializer(isEnabled = false).initialize()
|
||||
|
||||
verify(noteTaskController).setNoteTaskShortcutEnabled(false)
|
||||
verify(roleManager, never()).addOnRoleHoldersChangedListenerAsUser(any(), any(), any())
|
||||
}
|
||||
// endregion
|
||||
|
||||
@@ -105,14 +100,14 @@ internal class NoteTaskInitializerTest : SysuiTestCase() {
|
||||
fun handleSystemKey_receiveValidSystemKey_shouldShowNoteTask() {
|
||||
createNoteTaskInitializer().callbacks.handleSystemKey(KeyEvent.KEYCODE_STYLUS_BUTTON_TAIL)
|
||||
|
||||
verify(noteTaskController).showNoteTask(entryPoint = NoteTaskEntryPoint.TAIL_BUTTON)
|
||||
verify(controller).showNoteTask(entryPoint = NoteTaskEntryPoint.TAIL_BUTTON)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun handleSystemKey_receiveInvalidSystemKey_shouldDoNothing() {
|
||||
createNoteTaskInitializer().callbacks.handleSystemKey(KeyEvent.KEYCODE_UNKNOWN)
|
||||
|
||||
verifyZeroInteractions(noteTaskController)
|
||||
verifyZeroInteractions(controller)
|
||||
}
|
||||
// endregion
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user