Merge "Add Note Task shortcut" into tm-qpr-dev
This commit is contained in:
committed by
Android (Google) Code Review
commit
1225077abc
@@ -904,6 +904,29 @@
|
|||||||
<service android:name=".controls.controller.AuxiliaryPersistenceWrapper$DeletionJobService"
|
<service android:name=".controls.controller.AuxiliaryPersistenceWrapper$DeletionJobService"
|
||||||
android:permission="android.permission.BIND_JOB_SERVICE"/>
|
android:permission="android.permission.BIND_JOB_SERVICE"/>
|
||||||
|
|
||||||
|
<!-- region Note Task -->
|
||||||
|
<activity
|
||||||
|
android:name=".notetask.shortcut.CreateNoteTaskShortcutActivity"
|
||||||
|
android:enabled="false"
|
||||||
|
android:exported="true"
|
||||||
|
android:excludeFromRecents="true"
|
||||||
|
android:theme="@android:style/Theme.NoDisplay"
|
||||||
|
android:label="@string/note_task_button_label"
|
||||||
|
android:icon="@drawable/ic_note_task_button">
|
||||||
|
|
||||||
|
<intent-filter>
|
||||||
|
<action android:name="android.intent.action.CREATE_SHORTCUT" />
|
||||||
|
<category android:name="android.intent.category.DEFAULT" />
|
||||||
|
</intent-filter>
|
||||||
|
</activity>
|
||||||
|
|
||||||
|
<activity
|
||||||
|
android:name=".notetask.shortcut.LaunchNoteTaskActivity"
|
||||||
|
android:exported="true"
|
||||||
|
android:excludeFromRecents="true"
|
||||||
|
android:theme="@android:style/Theme.NoDisplay" />
|
||||||
|
<!-- endregion -->
|
||||||
|
|
||||||
<!-- started from ControlsRequestReceiver -->
|
<!-- started from ControlsRequestReceiver -->
|
||||||
<activity
|
<activity
|
||||||
android:name=".controls.management.ControlsRequestDialog"
|
android:name=".controls.management.ControlsRequestDialog"
|
||||||
|
|||||||
28
packages/SystemUI/res/drawable/ic_note_task_button.xml
Normal file
28
packages/SystemUI/res/drawable/ic_note_task_button.xml
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?><!--
|
||||||
|
~ Copyright (C) 2022 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.
|
||||||
|
-->
|
||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="24dp"
|
||||||
|
android:height="24dp"
|
||||||
|
android:viewportHeight="24"
|
||||||
|
android:viewportWidth="24">
|
||||||
|
<path
|
||||||
|
android:fillColor="#636C6F"
|
||||||
|
android:pathData="M17.6258,4.96L19.0358,6.37L7.4058,18.01L5.9958,16.6L17.6258,4.96ZM16.1358,3.62L4.1258,15.63L3.0158,19.83C2.9058,20.45 3.3858,21 3.9958,21C4.0558,21 4.1058,21 4.1658,20.99L8.3658,19.88L20.3758,7.86C20.7758,7.46 20.9958,6.93 20.9958,6.37C20.9958,5.81 20.7758,5.28 20.3758,4.88L19.1058,3.61C18.7158,3.22 18.1858,3 17.6258,3C17.0658,3 16.5358,3.22 16.1358,3.62Z" />
|
||||||
|
<path
|
||||||
|
android:fillColor="#636C6F"
|
||||||
|
android:fillType="evenOdd"
|
||||||
|
android:pathData="M20.1936,15.3369C20.3748,16.3837 19.9151,17.5414 18.8846,18.7597C19.1546,18.872 19.4576,18.9452 19.7724,18.9867C20.0839,19.0278 20.3683,19.0325 20.5749,19.0266C20.6772,19.0236 20.7578,19.0181 20.8101,19.0138C20.8362,19.0116 20.855,19.0097 20.8657,19.0085L20.8754,19.0074L20.875,19.0075C21.4217,18.9385 21.9214,19.325 21.9918,19.8718C22.0624,20.4195 21.6756,20.9208 21.1279,20.9914L21,19.9996C21.1279,20.9914 21.1265,20.9916 21.1265,20.9916L21.1249,20.9918L21.1211,20.9923L21.1107,20.9935L21.0795,20.997C21.0542,20.9998 21.0199,21.0032 20.9775,21.0067C20.8929,21.0138 20.7753,21.0216 20.6323,21.0257C20.3481,21.0339 19.9533,21.0279 19.5109,20.9695C18.873,20.8854 18.0393,20.6793 17.3106,20.1662C16.9605,20.3559 16.5876,20.4952 16.2299,20.6003C15.5742,20.7927 14.8754,20.8968 14.2534,20.9534C13.6801,21.0055 13.4553,21.0037 13.1015,21.0008C13.0689,21.0005 13.0352,21.0002 13,21H12.8594C12.8214,21.0002 12.785,21.0006 12.7504,21.0009C12.6524,21.0019 12.5683,21.0027 12.5,21H12.0562C12.0277,21.0003 12.0054,21.0006 11.9926,21.001L11.9751,21H9L11,19H11.9795C11.9929,18.9997 12.0064,18.9997 12.0199,19H12.4117C12.4534,18.9996 12.4864,18.9995 12.5,19H12.9675C12.977,18.9999 12.9878,18.9999 13,19C13.0446,19.0003 13.0859,19.0007 13.1249,19.0011C13.4259,19.0038 13.591,19.0054 14.0723,18.9616C14.6201,18.9118 15.1795,18.8242 15.6665,18.6813C15.753,18.6559 15.8346,18.6295 15.9114,18.6022C15.0315,17.2981 14.7125,16.1044 15.015,15.0829C15.4095,13.7511 16.6784,13.2418 17.7026,13.2864C18.7262,13.3309 19.954,13.9529 20.1936,15.3369ZM16.9327,15.6508C16.873,15.8523 16.8651,16.3878 17.4697,17.334C18.2007,16.4284 18.2585,15.8839 18.2229,15.6781C18.1939,15.5108 18.0297,15.3025 17.6157,15.2845C17.2025,15.2665 16.9885,15.4626 16.9327,15.6508Z" />
|
||||||
|
</vector>
|
||||||
@@ -2669,6 +2669,10 @@
|
|||||||
<xliff:g id="weather_condition" example="Partly cloudy">%1$s</xliff:g>, <xliff:g id="temperature" example="7°C">%2$s</xliff:g>
|
<xliff:g id="weather_condition" example="Partly cloudy">%1$s</xliff:g>, <xliff:g id="temperature" example="7°C">%2$s</xliff:g>
|
||||||
</string>
|
</string>
|
||||||
|
|
||||||
|
<!-- TODO(b/259369672): Replace with final resource. -->
|
||||||
|
<!-- [CHAR LIMIT=30] Label used to open Note Task -->
|
||||||
|
<string name="note_task_button_label">Notetaking</string>
|
||||||
|
|
||||||
<!-- [CHAR LIMIT=NONE] Le audio broadcast dialog, media app is broadcasting -->
|
<!-- [CHAR LIMIT=NONE] Le audio broadcast dialog, media app is broadcasting -->
|
||||||
<string name="broadcasting_description_is_broadcasting">Broadcasting</string>
|
<string name="broadcasting_description_is_broadcasting">Broadcasting</string>
|
||||||
<!-- [CHAR LIMIT=NONE] Le audio broadcast dialog, title -->
|
<!-- [CHAR LIMIT=NONE] Le audio broadcast dialog, title -->
|
||||||
|
|||||||
@@ -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.ComponentName
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
|
import android.content.pm.PackageManager
|
||||||
import android.os.UserManager
|
import android.os.UserManager
|
||||||
import android.view.KeyEvent
|
|
||||||
import com.android.systemui.dagger.SysUISingleton
|
import com.android.systemui.dagger.SysUISingleton
|
||||||
|
import com.android.systemui.notetask.shortcut.CreateNoteTaskShortcutActivity
|
||||||
import com.android.systemui.util.kotlin.getOrNull
|
import com.android.systemui.util.kotlin.getOrNull
|
||||||
import com.android.wm.shell.bubbles.Bubbles
|
import com.android.wm.shell.bubbles.Bubbles
|
||||||
import java.util.Optional
|
import java.util.Optional
|
||||||
@@ -45,15 +47,22 @@ constructor(
|
|||||||
@NoteTaskEnabledKey private val isEnabled: Boolean,
|
@NoteTaskEnabledKey private val isEnabled: Boolean,
|
||||||
) {
|
) {
|
||||||
|
|
||||||
fun handleSystemKey(keyCode: Int) {
|
/**
|
||||||
|
* Shows a note task. How the task is shown will depend on when the method is invoked.
|
||||||
|
*
|
||||||
|
* If in multi-window mode, notes will open as a full screen experience. That is particularly
|
||||||
|
* important for Large screen devices. These devices may support a taskbar that let users to
|
||||||
|
* drag and drop a shortcut into multi-window mode, and notes should comply with this behaviour.
|
||||||
|
*
|
||||||
|
* 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.
|
||||||
|
*
|
||||||
|
* If no in multi-window or the keyguard is unlocked, notes will open as a floating experience.
|
||||||
|
* That will let users open other apps in full screen, and take contextual notes.
|
||||||
|
*/
|
||||||
|
fun showNoteTask(isInMultiWindowMode: Boolean = false) {
|
||||||
if (!isEnabled) return
|
if (!isEnabled) return
|
||||||
|
|
||||||
if (keyCode == KeyEvent.KEYCODE_VIDEO_APP_1) {
|
|
||||||
showNoteTask()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun showNoteTask() {
|
|
||||||
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
|
||||||
@@ -62,11 +71,35 @@ constructor(
|
|||||||
// 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 (keyguardManager.isKeyguardLocked) {
|
if (isInMultiWindowMode || keyguardManager.isKeyguardLocked) {
|
||||||
context.startActivity(intent)
|
context.startActivity(intent)
|
||||||
} else {
|
} else {
|
||||||
// TODO(b/254606432): Should include Intent.EXTRA_FLOATING_WINDOW_MODE parameter.
|
// TODO(b/254606432): Should include Intent.EXTRA_FLOATING_WINDOW_MODE parameter.
|
||||||
bubbles.showAppBubble(intent)
|
bubbles.showAppBubble(intent)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set `android:enabled` property in the `AndroidManifest` associated with the Shortcut
|
||||||
|
* component to [value].
|
||||||
|
*
|
||||||
|
* If the shortcut entry `android:enabled` is set to `true`, the shortcut will be visible in the
|
||||||
|
* Widget Picker to all users.
|
||||||
|
*/
|
||||||
|
fun setNoteTaskShortcutEnabled(value: Boolean) {
|
||||||
|
val componentName = ComponentName(context, CreateNoteTaskShortcutActivity::class.java)
|
||||||
|
|
||||||
|
val enabledState =
|
||||||
|
if (value) {
|
||||||
|
PackageManager.COMPONENT_ENABLED_STATE_ENABLED
|
||||||
|
} else {
|
||||||
|
PackageManager.COMPONENT_ENABLED_STATE_DISABLED
|
||||||
|
}
|
||||||
|
|
||||||
|
context.packageManager.setComponentEnabledSetting(
|
||||||
|
componentName,
|
||||||
|
enabledState,
|
||||||
|
PackageManager.DONT_KILL_APP,
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,9 +16,10 @@
|
|||||||
|
|
||||||
package com.android.systemui.notetask
|
package com.android.systemui.notetask
|
||||||
|
|
||||||
|
import android.view.KeyEvent
|
||||||
|
import androidx.annotation.VisibleForTesting
|
||||||
import com.android.systemui.statusbar.CommandQueue
|
import com.android.systemui.statusbar.CommandQueue
|
||||||
import com.android.wm.shell.bubbles.Bubbles
|
import com.android.wm.shell.bubbles.Bubbles
|
||||||
import dagger.Lazy
|
|
||||||
import java.util.Optional
|
import java.util.Optional
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
@@ -27,15 +28,18 @@ internal class NoteTaskInitializer
|
|||||||
@Inject
|
@Inject
|
||||||
constructor(
|
constructor(
|
||||||
private val optionalBubbles: Optional<Bubbles>,
|
private val optionalBubbles: Optional<Bubbles>,
|
||||||
private val lazyNoteTaskController: Lazy<NoteTaskController>,
|
private val noteTaskController: NoteTaskController,
|
||||||
private val commandQueue: CommandQueue,
|
private val commandQueue: CommandQueue,
|
||||||
@NoteTaskEnabledKey private val isEnabled: Boolean,
|
@NoteTaskEnabledKey private val isEnabled: Boolean,
|
||||||
) {
|
) {
|
||||||
|
|
||||||
private val callbacks =
|
@VisibleForTesting
|
||||||
|
val callbacks =
|
||||||
object : CommandQueue.Callbacks {
|
object : CommandQueue.Callbacks {
|
||||||
override fun handleSystemKey(keyCode: Int) {
|
override fun handleSystemKey(keyCode: Int) {
|
||||||
lazyNoteTaskController.get().handleSystemKey(keyCode)
|
if (keyCode == KeyEvent.KEYCODE_VIDEO_APP_1) {
|
||||||
|
noteTaskController.showNoteTask()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -43,5 +47,6 @@ constructor(
|
|||||||
if (isEnabled && optionalBubbles.isPresent) {
|
if (isEnabled && optionalBubbles.isPresent) {
|
||||||
commandQueue.addCallback(callbacks)
|
commandQueue.addCallback(callbacks)
|
||||||
}
|
}
|
||||||
|
noteTaskController.setNoteTaskShortcutEnabled(isEnabled)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,19 +16,33 @@
|
|||||||
|
|
||||||
package com.android.systemui.notetask
|
package com.android.systemui.notetask
|
||||||
|
|
||||||
|
import android.app.Activity
|
||||||
import android.app.KeyguardManager
|
import android.app.KeyguardManager
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.os.UserManager
|
import android.os.UserManager
|
||||||
import androidx.core.content.getSystemService
|
import androidx.core.content.getSystemService
|
||||||
import com.android.systemui.flags.FeatureFlags
|
import com.android.systemui.flags.FeatureFlags
|
||||||
import com.android.systemui.flags.Flags
|
import com.android.systemui.flags.Flags
|
||||||
|
import com.android.systemui.notetask.shortcut.CreateNoteTaskShortcutActivity
|
||||||
|
import com.android.systemui.notetask.shortcut.LaunchNoteTaskActivity
|
||||||
|
import dagger.Binds
|
||||||
import dagger.Module
|
import dagger.Module
|
||||||
import dagger.Provides
|
import dagger.Provides
|
||||||
import java.util.*
|
import dagger.multibindings.ClassKey
|
||||||
|
import dagger.multibindings.IntoMap
|
||||||
|
import java.util.Optional
|
||||||
|
|
||||||
/** Compose all dependencies required by Note Task feature. */
|
/** Compose all dependencies required by Note Task feature. */
|
||||||
@Module
|
@Module
|
||||||
internal class NoteTaskModule {
|
internal interface NoteTaskModule {
|
||||||
|
|
||||||
|
@[Binds IntoMap ClassKey(LaunchNoteTaskActivity::class)]
|
||||||
|
fun bindNoteTaskLauncherActivity(activity: LaunchNoteTaskActivity): Activity?
|
||||||
|
|
||||||
|
@[Binds IntoMap ClassKey(CreateNoteTaskShortcutActivity::class)]
|
||||||
|
fun bindNoteTaskShortcutActivity(activity: CreateNoteTaskShortcutActivity): Activity?
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
|
||||||
@[Provides NoteTaskEnabledKey]
|
@[Provides NoteTaskEnabledKey]
|
||||||
fun provideIsNoteTaskEnabled(featureFlags: FeatureFlags): Boolean {
|
fun provideIsNoteTaskEnabled(featureFlags: FeatureFlags): Boolean {
|
||||||
@@ -45,3 +59,4 @@ internal class NoteTaskModule {
|
|||||||
return Optional.ofNullable(context.getSystemService())
|
return Optional.ofNullable(context.getSystemService())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -0,0 +1,79 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2022 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.shortcut
|
||||||
|
|
||||||
|
import android.app.Activity
|
||||||
|
import android.content.Intent
|
||||||
|
import android.os.Bundle
|
||||||
|
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 javax.inject.Inject
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Activity responsible for create a shortcut for notes action. If the shortcut is enabled, a new
|
||||||
|
* shortcut will appear in the widget picker. If the shortcut is selected, the Activity here will be
|
||||||
|
* launched, creating a new shortcut for [CreateNoteTaskShortcutActivity], and will finish.
|
||||||
|
*
|
||||||
|
* @see <a
|
||||||
|
* href="https://developer.android.com/develop/ui/views/launch/shortcuts/creating-shortcuts#custom-pinned">Creating
|
||||||
|
* a custom shortcut activity</a>
|
||||||
|
*/
|
||||||
|
internal class CreateNoteTaskShortcutActivity @Inject constructor() : 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_button,
|
||||||
|
)
|
||||||
|
setResult(Activity.RESULT_OK, intent)
|
||||||
|
|
||||||
|
finish()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun createShortcutIntent(
|
||||||
|
id: String,
|
||||||
|
shortLabel: String,
|
||||||
|
intent: Intent,
|
||||||
|
@DrawableRes iconResource: Int,
|
||||||
|
): Intent {
|
||||||
|
val shortcutInfo =
|
||||||
|
ShortcutInfoCompat.Builder(this, id)
|
||||||
|
.setIntent(intent)
|
||||||
|
.setShortLabel(shortLabel)
|
||||||
|
.setLongLived(true)
|
||||||
|
.setIcon(IconCompat.createWithResource(this, iconResource))
|
||||||
|
.build()
|
||||||
|
|
||||||
|
return ShortcutManagerCompat.createShortcutResultIntent(
|
||||||
|
this,
|
||||||
|
shortcutInfo,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
private companion object {
|
||||||
|
private const val SHORTCUT_ID = "note-task-shortcut-id"
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,53 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2022 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.shortcut
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import android.content.Intent
|
||||||
|
import android.os.Bundle
|
||||||
|
import androidx.activity.ComponentActivity
|
||||||
|
import com.android.systemui.notetask.NoteTaskController
|
||||||
|
import com.android.systemui.notetask.NoteTaskIntentResolver
|
||||||
|
import javax.inject.Inject
|
||||||
|
|
||||||
|
/** Activity responsible for launching the note experience, and finish. */
|
||||||
|
internal class LaunchNoteTaskActivity
|
||||||
|
@Inject
|
||||||
|
constructor(
|
||||||
|
private val noteTaskController: NoteTaskController,
|
||||||
|
) : ComponentActivity() {
|
||||||
|
|
||||||
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
|
super.onCreate(savedInstanceState)
|
||||||
|
|
||||||
|
noteTaskController.showNoteTask(isInMultiWindowMode)
|
||||||
|
|
||||||
|
finish()
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
|
||||||
|
/** Creates a new [Intent] set to start [LaunchNoteTaskActivity]. */
|
||||||
|
fun newIntent(context: Context): Intent {
|
||||||
|
return Intent(context, LaunchNoteTaskActivity::class.java).apply {
|
||||||
|
// Intent's action must be set in shortcuts, or an exception will be thrown.
|
||||||
|
// TODO(b/254606432): Use Intent.ACTION_NOTES instead.
|
||||||
|
action = NoteTaskIntentResolver.NOTES_ACTION
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -16,16 +16,21 @@
|
|||||||
package com.android.systemui.notetask
|
package com.android.systemui.notetask
|
||||||
|
|
||||||
import android.app.KeyguardManager
|
import android.app.KeyguardManager
|
||||||
|
import android.content.ComponentName
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
|
import android.content.pm.PackageManager
|
||||||
import android.os.UserManager
|
import android.os.UserManager
|
||||||
import android.test.suitebuilder.annotation.SmallTest
|
import android.test.suitebuilder.annotation.SmallTest
|
||||||
import android.view.KeyEvent
|
|
||||||
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.NOTES_ACTION
|
import com.android.systemui.notetask.NoteTaskIntentResolver.Companion.NOTES_ACTION
|
||||||
|
import com.android.systemui.notetask.shortcut.CreateNoteTaskShortcutActivity
|
||||||
|
import com.android.systemui.util.mockito.argumentCaptor
|
||||||
|
import com.android.systemui.util.mockito.eq
|
||||||
import com.android.systemui.util.mockito.whenever
|
import com.android.systemui.util.mockito.whenever
|
||||||
import com.android.wm.shell.bubbles.Bubbles
|
import com.android.wm.shell.bubbles.Bubbles
|
||||||
|
import com.google.common.truth.Truth.assertThat
|
||||||
import java.util.Optional
|
import java.util.Optional
|
||||||
import org.junit.Before
|
import org.junit.Before
|
||||||
import org.junit.Test
|
import org.junit.Test
|
||||||
@@ -48,6 +53,7 @@ internal class NoteTaskControllerTest : SysuiTestCase() {
|
|||||||
private val notesIntent = Intent(NOTES_ACTION)
|
private val notesIntent = Intent(NOTES_ACTION)
|
||||||
|
|
||||||
@Mock lateinit var context: Context
|
@Mock lateinit var context: Context
|
||||||
|
@Mock lateinit var packageManager: PackageManager
|
||||||
@Mock lateinit var noteTaskIntentResolver: NoteTaskIntentResolver
|
@Mock lateinit var noteTaskIntentResolver: NoteTaskIntentResolver
|
||||||
@Mock lateinit var bubbles: Bubbles
|
@Mock lateinit var bubbles: Bubbles
|
||||||
@Mock lateinit var optionalBubbles: Optional<Bubbles>
|
@Mock lateinit var optionalBubbles: Optional<Bubbles>
|
||||||
@@ -60,6 +66,7 @@ internal class NoteTaskControllerTest : SysuiTestCase() {
|
|||||||
fun setUp() {
|
fun setUp() {
|
||||||
MockitoAnnotations.initMocks(this)
|
MockitoAnnotations.initMocks(this)
|
||||||
|
|
||||||
|
whenever(context.packageManager).thenReturn(packageManager)
|
||||||
whenever(noteTaskIntentResolver.resolveIntent()).thenReturn(notesIntent)
|
whenever(noteTaskIntentResolver.resolveIntent()).thenReturn(notesIntent)
|
||||||
whenever(optionalBubbles.orElse(null)).thenReturn(bubbles)
|
whenever(optionalBubbles.orElse(null)).thenReturn(bubbles)
|
||||||
whenever(optionalKeyguardManager.orElse(null)).thenReturn(keyguardManager)
|
whenever(optionalKeyguardManager.orElse(null)).thenReturn(keyguardManager)
|
||||||
@@ -78,89 +85,125 @@ internal class NoteTaskControllerTest : SysuiTestCase() {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// region showNoteTask
|
||||||
@Test
|
@Test
|
||||||
fun handleSystemKey_keyguardIsLocked_shouldStartActivity() {
|
fun showNoteTask_keyguardIsLocked_shouldStartActivity() {
|
||||||
whenever(keyguardManager.isKeyguardLocked).thenReturn(true)
|
whenever(keyguardManager.isKeyguardLocked).thenReturn(true)
|
||||||
|
|
||||||
createNoteTaskController().handleSystemKey(KeyEvent.KEYCODE_VIDEO_APP_1)
|
createNoteTaskController().showNoteTask(isInMultiWindowMode = false)
|
||||||
|
|
||||||
verify(context).startActivity(notesIntent)
|
verify(context).startActivity(notesIntent)
|
||||||
verify(bubbles, never()).showAppBubble(notesIntent)
|
verify(bubbles, never()).showAppBubble(notesIntent)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun handleSystemKey_keyguardIsUnlocked_shouldStartBubbles() {
|
fun showNoteTask_keyguardIsUnlocked_shouldStartBubbles() {
|
||||||
whenever(keyguardManager.isKeyguardLocked).thenReturn(false)
|
whenever(keyguardManager.isKeyguardLocked).thenReturn(false)
|
||||||
|
|
||||||
createNoteTaskController().handleSystemKey(KeyEvent.KEYCODE_VIDEO_APP_1)
|
createNoteTaskController().showNoteTask(isInMultiWindowMode = false)
|
||||||
|
|
||||||
verify(bubbles).showAppBubble(notesIntent)
|
verify(bubbles).showAppBubble(notesIntent)
|
||||||
verify(context, never()).startActivity(notesIntent)
|
verify(context, never()).startActivity(notesIntent)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun handleSystemKey_receiveInvalidSystemKey_shouldDoNothing() {
|
fun showNoteTask_isInMultiWindowMode_shouldStartActivity() {
|
||||||
createNoteTaskController().handleSystemKey(KeyEvent.KEYCODE_UNKNOWN)
|
whenever(keyguardManager.isKeyguardLocked).thenReturn(false)
|
||||||
|
|
||||||
verify(context, never()).startActivity(notesIntent)
|
createNoteTaskController().showNoteTask(isInMultiWindowMode = true)
|
||||||
|
|
||||||
|
verify(context).startActivity(notesIntent)
|
||||||
verify(bubbles, never()).showAppBubble(notesIntent)
|
verify(bubbles, never()).showAppBubble(notesIntent)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun handleSystemKey_bubblesIsNull_shouldDoNothing() {
|
fun showNoteTask_bubblesIsNull_shouldDoNothing() {
|
||||||
whenever(optionalBubbles.orElse(null)).thenReturn(null)
|
whenever(optionalBubbles.orElse(null)).thenReturn(null)
|
||||||
|
|
||||||
createNoteTaskController().handleSystemKey(KeyEvent.KEYCODE_VIDEO_APP_1)
|
createNoteTaskController().showNoteTask(isInMultiWindowMode = false)
|
||||||
|
|
||||||
verify(context, never()).startActivity(notesIntent)
|
verify(context, never()).startActivity(notesIntent)
|
||||||
verify(bubbles, never()).showAppBubble(notesIntent)
|
verify(bubbles, never()).showAppBubble(notesIntent)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun handleSystemKey_keyguardManagerIsNull_shouldDoNothing() {
|
fun showNoteTask_keyguardManagerIsNull_shouldDoNothing() {
|
||||||
whenever(optionalKeyguardManager.orElse(null)).thenReturn(null)
|
whenever(optionalKeyguardManager.orElse(null)).thenReturn(null)
|
||||||
|
|
||||||
createNoteTaskController().handleSystemKey(KeyEvent.KEYCODE_VIDEO_APP_1)
|
createNoteTaskController().showNoteTask(isInMultiWindowMode = false)
|
||||||
|
|
||||||
verify(context, never()).startActivity(notesIntent)
|
verify(context, never()).startActivity(notesIntent)
|
||||||
verify(bubbles, never()).showAppBubble(notesIntent)
|
verify(bubbles, never()).showAppBubble(notesIntent)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun handleSystemKey_userManagerIsNull_shouldDoNothing() {
|
fun showNoteTask_userManagerIsNull_shouldDoNothing() {
|
||||||
whenever(optionalUserManager.orElse(null)).thenReturn(null)
|
whenever(optionalUserManager.orElse(null)).thenReturn(null)
|
||||||
|
|
||||||
createNoteTaskController().handleSystemKey(KeyEvent.KEYCODE_VIDEO_APP_1)
|
createNoteTaskController().showNoteTask(isInMultiWindowMode = false)
|
||||||
|
|
||||||
verify(context, never()).startActivity(notesIntent)
|
verify(context, never()).startActivity(notesIntent)
|
||||||
verify(bubbles, never()).showAppBubble(notesIntent)
|
verify(bubbles, never()).showAppBubble(notesIntent)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun handleSystemKey_intentResolverReturnsNull_shouldDoNothing() {
|
fun showNoteTask_intentResolverReturnsNull_shouldDoNothing() {
|
||||||
whenever(noteTaskIntentResolver.resolveIntent()).thenReturn(null)
|
whenever(noteTaskIntentResolver.resolveIntent()).thenReturn(null)
|
||||||
|
|
||||||
createNoteTaskController().handleSystemKey(KeyEvent.KEYCODE_VIDEO_APP_1)
|
createNoteTaskController().showNoteTask(isInMultiWindowMode = false)
|
||||||
|
|
||||||
verify(context, never()).startActivity(notesIntent)
|
verify(context, never()).startActivity(notesIntent)
|
||||||
verify(bubbles, never()).showAppBubble(notesIntent)
|
verify(bubbles, never()).showAppBubble(notesIntent)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun handleSystemKey_flagDisabled_shouldDoNothing() {
|
fun showNoteTask_flagDisabled_shouldDoNothing() {
|
||||||
createNoteTaskController(isEnabled = false).handleSystemKey(KeyEvent.KEYCODE_VIDEO_APP_1)
|
createNoteTaskController(isEnabled = false).showNoteTask()
|
||||||
|
|
||||||
verify(context, never()).startActivity(notesIntent)
|
verify(context, never()).startActivity(notesIntent)
|
||||||
verify(bubbles, never()).showAppBubble(notesIntent)
|
verify(bubbles, never()).showAppBubble(notesIntent)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun handleSystemKey_userIsLocked_shouldDoNothing() {
|
fun showNoteTask_userIsLocked_shouldDoNothing() {
|
||||||
whenever(userManager.isUserUnlocked).thenReturn(false)
|
whenever(userManager.isUserUnlocked).thenReturn(false)
|
||||||
|
|
||||||
createNoteTaskController().handleSystemKey(KeyEvent.KEYCODE_VIDEO_APP_1)
|
createNoteTaskController().showNoteTask(isInMultiWindowMode = false)
|
||||||
|
|
||||||
verify(context, never()).startActivity(notesIntent)
|
verify(context, never()).startActivity(notesIntent)
|
||||||
verify(bubbles, never()).showAppBubble(notesIntent)
|
verify(bubbles, never()).showAppBubble(notesIntent)
|
||||||
}
|
}
|
||||||
|
// endregion
|
||||||
|
|
||||||
|
// region setNoteTaskShortcutEnabled
|
||||||
|
@Test
|
||||||
|
fun setNoteTaskShortcutEnabled_setTrue() {
|
||||||
|
createNoteTaskController().setNoteTaskShortcutEnabled(value = true)
|
||||||
|
|
||||||
|
val argument = argumentCaptor<ComponentName>()
|
||||||
|
verify(context.packageManager)
|
||||||
|
.setComponentEnabledSetting(
|
||||||
|
argument.capture(),
|
||||||
|
eq(PackageManager.COMPONENT_ENABLED_STATE_ENABLED),
|
||||||
|
eq(PackageManager.DONT_KILL_APP),
|
||||||
|
)
|
||||||
|
val expected = ComponentName(context, CreateNoteTaskShortcutActivity::class.java)
|
||||||
|
assertThat(argument.value.flattenToString()).isEqualTo(expected.flattenToString())
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun setNoteTaskShortcutEnabled_setFalse() {
|
||||||
|
createNoteTaskController().setNoteTaskShortcutEnabled(value = false)
|
||||||
|
|
||||||
|
val argument = argumentCaptor<ComponentName>()
|
||||||
|
verify(context.packageManager)
|
||||||
|
.setComponentEnabledSetting(
|
||||||
|
argument.capture(),
|
||||||
|
eq(PackageManager.COMPONENT_ENABLED_STATE_DISABLED),
|
||||||
|
eq(PackageManager.DONT_KILL_APP),
|
||||||
|
)
|
||||||
|
val expected = ComponentName(context, CreateNoteTaskShortcutActivity::class.java)
|
||||||
|
assertThat(argument.value.flattenToString()).isEqualTo(expected.flattenToString())
|
||||||
|
}
|
||||||
|
// endregion
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,10 +16,10 @@
|
|||||||
package com.android.systemui.notetask
|
package com.android.systemui.notetask
|
||||||
|
|
||||||
import android.test.suitebuilder.annotation.SmallTest
|
import android.test.suitebuilder.annotation.SmallTest
|
||||||
|
import android.view.KeyEvent
|
||||||
import androidx.test.runner.AndroidJUnit4
|
import androidx.test.runner.AndroidJUnit4
|
||||||
import com.android.systemui.SysuiTestCase
|
import com.android.systemui.SysuiTestCase
|
||||||
import com.android.systemui.statusbar.CommandQueue
|
import com.android.systemui.statusbar.CommandQueue
|
||||||
import com.android.systemui.util.mockito.mock
|
|
||||||
import com.android.systemui.util.mockito.whenever
|
import com.android.systemui.util.mockito.whenever
|
||||||
import com.android.wm.shell.bubbles.Bubbles
|
import com.android.wm.shell.bubbles.Bubbles
|
||||||
import java.util.Optional
|
import java.util.Optional
|
||||||
@@ -45,6 +45,7 @@ internal class NoteTaskInitializerTest : SysuiTestCase() {
|
|||||||
@Mock lateinit var commandQueue: CommandQueue
|
@Mock lateinit var commandQueue: CommandQueue
|
||||||
@Mock lateinit var bubbles: Bubbles
|
@Mock lateinit var bubbles: Bubbles
|
||||||
@Mock lateinit var optionalBubbles: Optional<Bubbles>
|
@Mock lateinit var optionalBubbles: Optional<Bubbles>
|
||||||
|
@Mock lateinit var noteTaskController: NoteTaskController
|
||||||
|
|
||||||
@Before
|
@Before
|
||||||
fun setUp() {
|
fun setUp() {
|
||||||
@@ -57,12 +58,13 @@ internal class NoteTaskInitializerTest : SysuiTestCase() {
|
|||||||
private fun createNoteTaskInitializer(isEnabled: Boolean = true): NoteTaskInitializer {
|
private fun createNoteTaskInitializer(isEnabled: Boolean = true): NoteTaskInitializer {
|
||||||
return NoteTaskInitializer(
|
return NoteTaskInitializer(
|
||||||
optionalBubbles = optionalBubbles,
|
optionalBubbles = optionalBubbles,
|
||||||
lazyNoteTaskController = mock(),
|
noteTaskController = noteTaskController,
|
||||||
commandQueue = commandQueue,
|
commandQueue = commandQueue,
|
||||||
isEnabled = isEnabled,
|
isEnabled = isEnabled,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// region initializer
|
||||||
@Test
|
@Test
|
||||||
fun initialize_shouldAddCallbacks() {
|
fun initialize_shouldAddCallbacks() {
|
||||||
createNoteTaskInitializer().initialize()
|
createNoteTaskInitializer().initialize()
|
||||||
@@ -85,4 +87,35 @@ internal class NoteTaskInitializerTest : SysuiTestCase() {
|
|||||||
|
|
||||||
verify(commandQueue, never()).addCallback(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)
|
||||||
|
}
|
||||||
|
// endregion
|
||||||
|
|
||||||
|
// region handleSystemKey
|
||||||
|
@Test
|
||||||
|
fun handleSystemKey_receiveValidSystemKey_shouldShowNoteTask() {
|
||||||
|
createNoteTaskInitializer().callbacks.handleSystemKey(KeyEvent.KEYCODE_VIDEO_APP_1)
|
||||||
|
|
||||||
|
verify(noteTaskController).showNoteTask()
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun handleSystemKey_receiveInvalidSystemKey_shouldDoNothing() {
|
||||||
|
createNoteTaskInitializer().callbacks.handleSystemKey(KeyEvent.KEYCODE_UNKNOWN)
|
||||||
|
|
||||||
|
verify(noteTaskController, never()).showNoteTask()
|
||||||
|
}
|
||||||
|
// endregion
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user