summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--apex/Android.bp3
-rw-r--r--apex/permission/framework/Android.bp98
-rw-r--r--core/java/android/os/storage/StorageManager.java7
-rw-r--r--core/java/com/android/internal/BrightnessSynchronizer.java61
-rw-r--r--core/res/res/values/strings.xml12
-rw-r--r--core/res/res/values/symbols.xml2
-rw-r--r--packages/SystemUI/AndroidManifest.xml1
-rw-r--r--packages/SystemUI/res/values/ids.xml3
-rw-r--r--packages/SystemUI/res/values/strings.xml2
-rw-r--r--packages/SystemUI/src/com/android/systemui/bubbles/Bubble.java119
-rw-r--r--packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java98
-rw-r--r--packages/SystemUI/src/com/android/systemui/bubbles/BubbleData.java24
-rw-r--r--packages/SystemUI/src/com/android/systemui/bubbles/BubbleDataRepository.kt6
-rw-r--r--packages/SystemUI/src/com/android/systemui/bubbles/BubbleExpandedView.java9
-rw-r--r--packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java35
-rw-r--r--packages/SystemUI/src/com/android/systemui/bubbles/BubbleViewInfoTask.java52
-rw-r--r--packages/SystemUI/src/com/android/systemui/controls/management/AllModel.kt2
-rw-r--r--packages/SystemUI/src/com/android/systemui/controls/management/ControlAdapter.kt96
-rw-r--r--packages/SystemUI/src/com/android/systemui/controls/management/ControlsModel.kt30
-rw-r--r--packages/SystemUI/src/com/android/systemui/controls/management/FavoritesModel.kt31
-rw-r--r--packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshot.java46
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/ActionClickLogger.kt88
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/NotificationRemoteInputManager.java27
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/dagger/StatusBarDependenciesModule.java7
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/BubbleCoordinator.java6
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationGroupManager.java3
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarRemoteInputCallback.java9
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleControllerTest.java29
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleDataTest.java45
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/bubbles/NewNotifPipelineBubbleControllerTest.java23
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationRemoteInputManagerTest.java22
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/SmartReplyControllerTest.java3
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarRemoteInputCallbackTest.java4
-rw-r--r--services/core/java/com/android/server/display/LocalDisplayAdapter.java12
-rw-r--r--services/core/java/com/android/server/stats/pull/StatsPullAtomService.java36
-rw-r--r--services/core/java/com/android/server/wm/ActivityRecord.java7
-rw-r--r--services/core/java/com/android/server/wm/WindowContainer.java11
-rw-r--r--services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java55
-rw-r--r--services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java17
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/AppWindowTokenTests.java26
40 files changed, 636 insertions, 531 deletions
diff --git a/apex/Android.bp b/apex/Android.bp
index e8afa1dabb25..f511af570bb1 100644
--- a/apex/Android.bp
+++ b/apex/Android.bp
@@ -92,6 +92,9 @@ java_defaults {
sdk_version: "module_current",
},
+ // Configure framework module specific metalava options.
+ droiddoc_options: [mainline_stubs_args],
+
// The stub libraries must be visible to frameworks/base so they can be combined
// into API specific libraries.
stubs_library_visibility: [
diff --git a/apex/permission/framework/Android.bp b/apex/permission/framework/Android.bp
index 68c27a8327cb..c43fabde81da 100644
--- a/apex/permission/framework/Android.bp
+++ b/apex/permission/framework/Android.bp
@@ -21,12 +21,18 @@ filegroup {
path: "java",
}
-java_library {
+java_sdk_library {
name: "framework-permission",
+ defaults: ["framework-module-defaults"],
srcs: [
":framework-permission-sources",
],
- sdk_version: "module_current",
+
+ // TODO(b/155480189) - Remove naming_scheme once references have been resolved.
+ // Temporary java_sdk_library component naming scheme to use to ease the transition from separate
+ // modules to java_sdk_library.
+ naming_scheme: "framework-modules",
+
apex_available: [
"com.android.permission",
"test_com.android.permission",
@@ -40,91 +46,5 @@ java_library {
visibility: [
"//frameworks/base/apex/permission:__subpackages__",
],
-}
-
-stubs_defaults {
- name: "framework-permission-stubs-defaults",
- srcs: [ ":framework-permission-sources" ],
- libs: [ "framework-annotations-lib" ],
- dist: { dest: "framework-permission.txt" },
-}
-
-droidstubs {
- name: "framework-permission-stubs-srcs-publicapi",
- defaults: [
- "framework-module-stubs-defaults-publicapi",
- "framework-permission-stubs-defaults",
- ],
- check_api: {
- last_released: {
- api_file: ":framework-permission.api.public.latest",
- removed_api_file: ":framework-permission-removed.api.public.latest",
- },
- api_lint: {
- new_since: ":framework-permission.api.public.latest",
- },
- },
-}
-
-droidstubs {
- name: "framework-permission-stubs-srcs-systemapi",
- defaults: [
- "framework-module-stubs-defaults-systemapi",
- "framework-permission-stubs-defaults",
- ],
- check_api: {
- last_released: {
- api_file: ":framework-permission.api.system.latest",
- removed_api_file: ":framework-permission-removed.api.system.latest",
- },
- api_lint: {
- new_since: ":framework-permission.api.system.latest",
- },
- },
-}
-
-droidstubs {
- name: "framework-permission-api-module_libs_api",
- defaults: [
- "framework-module-api-defaults-module_libs_api",
- "framework-permission-stubs-defaults",
- ],
- check_api: {
- last_released: {
- api_file: ":framework-permission.api.module-lib.latest",
- removed_api_file: ":framework-permission-removed.api.module-lib.latest",
- },
- api_lint: {
- new_since: ":framework-permission.api.module-lib.latest",
- },
- },
-}
-
-droidstubs {
- name: "framework-permission-stubs-srcs-module_libs_api",
- defaults: [
- "framework-module-stubs-defaults-module_libs_api",
- "framework-permission-stubs-defaults",
- ],
-}
-
-java_library {
- name: "framework-permission-stubs-publicapi",
- srcs: [ ":framework-permission-stubs-srcs-publicapi" ],
- defaults: ["framework-module-stubs-lib-defaults-publicapi"],
- dist: { dest: "framework-permission.jar" },
-}
-
-java_library {
- name: "framework-permission-stubs-systemapi",
- srcs: [ ":framework-permission-stubs-srcs-systemapi" ],
- defaults: ["framework-module-stubs-lib-defaults-systemapi"],
- dist: { dest: "framework-permission.jar" },
-}
-
-java_library {
- name: "framework-permission-stubs-module_libs_api",
- srcs: [ ":framework-permission-stubs-srcs-module_libs_api" ],
- defaults: ["framework-module-stubs-lib-defaults-module_libs_api"],
- dist: { dest: "framework-permission.jar" },
+ stubs_library_visibility: ["//visibility:public"],
}
diff --git a/core/java/android/os/storage/StorageManager.java b/core/java/android/os/storage/StorageManager.java
index a1a11ed54609..16e5156c10de 100644
--- a/core/java/android/os/storage/StorageManager.java
+++ b/core/java/android/os/storage/StorageManager.java
@@ -226,9 +226,10 @@ public class StorageManager {
* <p>
* This intent should be launched using
* {@link Activity#startActivityForResult(Intent, int)} so that the user
- * knows which app is requesting to clear cache. The returned result will
- * be {@link Activity#RESULT_OK} if the activity was launched and the user accepted to clear
- * cache, or {@link Activity#RESULT_CANCELED} otherwise.
+ * knows which app is requesting to clear cache. The returned result will be:
+ * {@link Activity#RESULT_OK} if the activity was launched and all cache was cleared,
+ * {@link OsConstants#EIO} if an error occurred while clearing the cache or
+ * {@link Activity#RESULT_CANCELED} otherwise.
*/
@RequiresPermission(android.Manifest.permission.MANAGE_EXTERNAL_STORAGE)
@SdkConstant(SdkConstant.SdkConstantType.ACTIVITY_INTENT_ACTION)
diff --git a/core/java/com/android/internal/BrightnessSynchronizer.java b/core/java/com/android/internal/BrightnessSynchronizer.java
index aa23251b9b85..42724bede481 100644
--- a/core/java/com/android/internal/BrightnessSynchronizer.java
+++ b/core/java/com/android/internal/BrightnessSynchronizer.java
@@ -84,17 +84,17 @@ public class BrightnessSynchronizer{
* Converts between the int brightness system and the float brightness system.
*/
public static float brightnessIntToFloat(Context context, int brightnessInt) {
- PowerManager pm = context.getSystemService(PowerManager.class);
- float pmMinBrightness = pm.getBrightnessConstraint(
+ final PowerManager pm = context.getSystemService(PowerManager.class);
+ final float pmMinBrightness = pm.getBrightnessConstraint(
PowerManager.BRIGHTNESS_CONSTRAINT_TYPE_MINIMUM);
- float pmMaxBrightness = pm.getBrightnessConstraint(
+ final float pmMaxBrightness = pm.getBrightnessConstraint(
PowerManager.BRIGHTNESS_CONSTRAINT_TYPE_MAXIMUM);
- int minBrightnessInt = brightnessFloatToInt(pmMinBrightness, PowerManager.BRIGHTNESS_MIN,
- PowerManager.BRIGHTNESS_MAX, PowerManager.BRIGHTNESS_OFF + 1,
- PowerManager.BRIGHTNESS_ON);
- int maxBrightnessInt = brightnessFloatToInt(pmMaxBrightness, PowerManager.BRIGHTNESS_MIN,
- PowerManager.BRIGHTNESS_MAX, PowerManager.BRIGHTNESS_OFF + 1,
- PowerManager.BRIGHTNESS_ON);
+ final int minBrightnessInt = Math.round(brightnessFloatToIntRange(pmMinBrightness,
+ PowerManager.BRIGHTNESS_MIN, PowerManager.BRIGHTNESS_MAX,
+ PowerManager.BRIGHTNESS_OFF + 1, PowerManager.BRIGHTNESS_ON));
+ final int maxBrightnessInt = Math.round(brightnessFloatToIntRange(pmMaxBrightness,
+ PowerManager.BRIGHTNESS_MIN, PowerManager.BRIGHTNESS_MAX,
+ PowerManager.BRIGHTNESS_OFF + 1, PowerManager.BRIGHTNESS_ON));
return brightnessIntToFloat(brightnessInt, minBrightnessInt, maxBrightnessInt,
pmMinBrightness, pmMaxBrightness);
@@ -119,34 +119,43 @@ public class BrightnessSynchronizer{
* Converts between the float brightness system and the int brightness system.
*/
public static int brightnessFloatToInt(Context context, float brightnessFloat) {
- PowerManager pm = context.getSystemService(PowerManager.class);
- float pmMinBrightness = pm.getBrightnessConstraint(
+ return Math.round(brightnessFloatToIntRange(context, brightnessFloat));
+ }
+
+ /**
+ * Converts between the float brightness system and the int brightness system, but returns
+ * the converted value as a float within the int-system's range. This method helps with
+ * conversions from one system to the other without losing the floating-point precision.
+ */
+ public static float brightnessFloatToIntRange(Context context, float brightnessFloat) {
+ final PowerManager pm = context.getSystemService(PowerManager.class);
+ final float minFloat = pm.getBrightnessConstraint(
PowerManager.BRIGHTNESS_CONSTRAINT_TYPE_MINIMUM);
- float pmMaxBrightness = pm.getBrightnessConstraint(
+ final float maxFloat = pm.getBrightnessConstraint(
PowerManager.BRIGHTNESS_CONSTRAINT_TYPE_MAXIMUM);
- int minBrightnessInt = brightnessFloatToInt(pmMinBrightness, PowerManager.BRIGHTNESS_MIN,
- PowerManager.BRIGHTNESS_MAX, PowerManager.BRIGHTNESS_OFF + 1,
- PowerManager.BRIGHTNESS_ON);
- int maxBrightnessInt = brightnessFloatToInt(pmMaxBrightness, PowerManager.BRIGHTNESS_MIN,
- PowerManager.BRIGHTNESS_MAX, PowerManager.BRIGHTNESS_OFF + 1,
- PowerManager.BRIGHTNESS_ON);
-
- return brightnessFloatToInt(brightnessFloat, pmMinBrightness, pmMaxBrightness,
- minBrightnessInt, maxBrightnessInt);
+ final float minInt = brightnessFloatToIntRange(minFloat,
+ PowerManager.BRIGHTNESS_MIN, PowerManager.BRIGHTNESS_MAX,
+ PowerManager.BRIGHTNESS_OFF + 1, PowerManager.BRIGHTNESS_ON);
+ final float maxInt = brightnessFloatToIntRange(maxFloat,
+ PowerManager.BRIGHTNESS_MIN, PowerManager.BRIGHTNESS_MAX,
+ PowerManager.BRIGHTNESS_OFF + 1, PowerManager.BRIGHTNESS_ON);
+ return brightnessFloatToIntRange(brightnessFloat, minFloat, maxFloat, minInt, maxInt);
}
/**
- * Converts between the float brightness system and the int brightness system.
+ * Translates specified value from the float brightness system to the int brightness system,
+ * given the min/max of each range. Accounts for special values such as OFF and invalid values.
+ * Value returned as a float privimite (to preserve precision), but is a value within the
+ * int-system range.
*/
- public static int brightnessFloatToInt(float brightnessFloat, float minFloat, float maxFloat,
- int minInt, int maxInt) {
+ private static float brightnessFloatToIntRange(float brightnessFloat, float minFloat,
+ float maxFloat, float minInt, float maxInt) {
if (floatEquals(brightnessFloat, PowerManager.BRIGHTNESS_OFF_FLOAT)) {
return PowerManager.BRIGHTNESS_OFF;
} else if (Float.isNaN(brightnessFloat)) {
return PowerManager.BRIGHTNESS_INVALID;
} else {
- return Math.round(MathUtils.constrainedMap((float) minInt, (float) maxInt, minFloat,
- maxFloat, brightnessFloat));
+ return MathUtils.constrainedMap(minInt, maxInt, minFloat, maxFloat, brightnessFloat);
}
}
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index 54d14f892e25..233f72eaae1f 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -451,10 +451,14 @@
<string name="personal_apps_suspension_text">
Your personal apps are blocked until you turn on your work profile</string>
<!-- Notification text. This notification lets a user know that their apps will be blocked
- tomorrow due to a work policy from their IT admin, and that they need to turn on their work
- profile to prevent the apps from being blocked. [CHAR LIMIT=NONE] -->
- <string name="personal_apps_suspension_tomorrow_text">
- Your personal apps will be blocked tomorrow</string>
+ at a particular time due to a work policy from their IT admin, and that they need to turn on
+ their work profile to prevent the apps from being blocked. It also explains for how many
+ days the profile is allowed to be off and this number is at least 3. [CHAR LIMIT=NONE] -->
+ <string name="personal_apps_suspension_soon_text">
+ Personal apps will be blocked on <xliff:g id="date" example="May 29">%1$s</xliff:g> at
+ <xliff:g id="time" example="5:20 PM">%2$s</xliff:g>. Your work profile can\u2019t stay off
+ for more than <xliff:g id="number" example="3">%3$d</xliff:g> days.
+ </string>
<!-- Title for the button that turns work profile on. To be used in a notification
[CHAR LIMIT=NONE] -->
<string name="personal_apps_suspended_turn_profile_on">Turn on work profile</string>
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 64905ec9db7a..b483ee04a7ec 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -1200,7 +1200,7 @@
<java-symbol type="string" name="location_changed_notification_title" />
<java-symbol type="string" name="location_changed_notification_text" />
<java-symbol type="string" name="personal_apps_suspension_title" />
- <java-symbol type="string" name="personal_apps_suspension_tomorrow_text" />
+ <java-symbol type="string" name="personal_apps_suspension_soon_text" />
<java-symbol type="string" name="personal_apps_suspension_text" />
<java-symbol type="string" name="personal_apps_suspended_turn_profile_on" />
<java-symbol type="string" name="notification_work_profile_content_description" />
diff --git a/packages/SystemUI/AndroidManifest.xml b/packages/SystemUI/AndroidManifest.xml
index e670f1f83a52..4510b87556c7 100644
--- a/packages/SystemUI/AndroidManifest.xml
+++ b/packages/SystemUI/AndroidManifest.xml
@@ -685,6 +685,7 @@
</activity>
<activity android:name=".controls.management.ControlsEditingActivity"
+ android:label="@string/controls_menu_edit"
android:theme="@style/Theme.ControlsManagement"
android:excludeFromRecents="true"
android:noHistory="true"
diff --git a/packages/SystemUI/res/values/ids.xml b/packages/SystemUI/res/values/ids.xml
index 76ca385bd9d9..09918e764140 100644
--- a/packages/SystemUI/res/values/ids.xml
+++ b/packages/SystemUI/res/values/ids.xml
@@ -169,5 +169,8 @@
<item type="id" name="screen_recording_options" />
<item type="id" name="screen_recording_dialog_source_text" />
<item type="id" name="screen_recording_dialog_source_description" />
+
+ <item type="id" name="accessibility_action_controls_move_before" />
+ <item type="id" name="accessibility_action_controls_move_after" />
</resources>
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index 8c10f61db7a0..8bbcfa0e7898 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -2711,6 +2711,8 @@
<string name="accessibility_control_change_favorite">favorite</string>
<!-- a11y action to unfavorite a control. It will read as "Double-tap to unfavorite" in screen readers [CHAR LIMIT=NONE] -->
<string name="accessibility_control_change_unfavorite">unfavorite</string>
+ <!-- a11y action to move a control to the position specified by the parameter [CHAR LIMIT=NONE] -->
+ <string name="accessibility_control_move">Move to position <xliff:g id="number" example="1">%d</xliff:g></string>
<!-- Controls management controls screen default title [CHAR LIMIT=30] -->
<string name="controls_favorite_default_title">Controls</string>
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/Bubble.java b/packages/SystemUI/src/com/android/systemui/bubbles/Bubble.java
index f0443892bfaf..f1b401e77fbc 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/Bubble.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/Bubble.java
@@ -16,7 +16,6 @@
package com.android.systemui.bubbles;
-import static android.app.Notification.FLAG_BUBBLE;
import static android.os.AsyncTask.Status.FINISHED;
import static android.view.Display.INVALID_DISPLAY;
@@ -28,7 +27,6 @@ import android.app.Notification;
import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
-import android.content.pm.ApplicationInfo;
import android.content.pm.LauncherApps;
import android.content.pm.PackageManager;
import android.content.pm.ShortcutInfo;
@@ -57,11 +55,6 @@ import java.util.Objects;
class Bubble implements BubbleViewProvider {
private static final String TAG = "Bubble";
- /**
- * NotificationEntry associated with the bubble. A null value implies this bubble is loaded
- * from disk.
- */
- @Nullable
private NotificationEntry mEntry;
private final String mKey;
private final String mGroupId;
@@ -102,56 +95,18 @@ class Bubble implements BubbleViewProvider {
private Bitmap mBadgedImage;
private int mDotColor;
private Path mDotPath;
- private int mFlags;
- /**
- * Extract GroupId from {@link NotificationEntry}. See also {@link #groupId(ShortcutInfo)}.
- */
+
public static String groupId(NotificationEntry entry) {
UserHandle user = entry.getSbn().getUser();
return user.getIdentifier() + "|" + entry.getSbn().getPackageName();
}
- /**
- * Extract GroupId from {@link ShortcutInfo}. This should match the one generated from
- * {@link NotificationEntry}. See also {@link #groupId(NotificationEntry)}.
- */
- @NonNull
- public static String groupId(@NonNull final ShortcutInfo shortcutInfo) {
- return shortcutInfo.getUserId() + "|" + shortcutInfo.getPackage();
- }
-
- /**
- * Generate a unique identifier for this bubble based on given {@link NotificationEntry}. If
- * {@link ShortcutInfo} was found in the notification entry, the identifier would be generated
- * from {@link ShortcutInfo} instead. See also {@link #key(ShortcutInfo)}.
- */
- @NonNull
- public static String key(@NonNull final NotificationEntry entry) {
- final ShortcutInfo shortcutInfo = entry.getRanking().getShortcutInfo();
- if (shortcutInfo != null) return key(shortcutInfo);
- return entry.getKey();
- }
-
- /**
- * Generate a unique identifier for this bubble based on given {@link ShortcutInfo}.
- * See also {@link #key(NotificationEntry)}.
- */
- @NonNull
- public static String key(@NonNull final ShortcutInfo shortcutInfo) {
- return shortcutInfo.getUserId() + "|" + shortcutInfo.getPackage() + "|"
- + shortcutInfo.getId();
- }
-
- /**
- * Create a bubble with limited information based on given {@link ShortcutInfo}.
- * Note: Currently this is only being used when the bubble is persisted to disk.
- */
+ // TODO: Decouple Bubble from NotificationEntry and transform ShortcutInfo into Bubble
Bubble(ShortcutInfo shortcutInfo) {
mShortcutInfo = shortcutInfo;
- mKey = key(shortcutInfo);
- mGroupId = groupId(shortcutInfo);
- mFlags = 0;
+ mKey = shortcutInfo.getId();
+ mGroupId = shortcutInfo.getId();
}
/** Used in tests when no UI is required. */
@@ -159,11 +114,10 @@ class Bubble implements BubbleViewProvider {
Bubble(NotificationEntry e,
BubbleController.NotificationSuppressionChangedListener listener) {
mEntry = e;
- mKey = key(e);
+ mKey = e.getKey();
mLastUpdated = e.getSbn().getPostTime();
mGroupId = groupId(e);
mSuppressionListener = listener;
- mFlags = e.getSbn().getNotification().flags;
}
@Override
@@ -171,26 +125,16 @@ class Bubble implements BubbleViewProvider {
return mKey;
}
- @Nullable
public NotificationEntry getEntry() {
return mEntry;
}
- @Nullable
- public UserHandle getUser() {
- if (mEntry != null) return mEntry.getSbn().getUser();
- if (mShortcutInfo != null) return mShortcutInfo.getUserHandle();
- return null;
- }
-
public String getGroupId() {
return mGroupId;
}
public String getPackageName() {
- return mEntry == null
- ? mShortcutInfo == null ? null : mShortcutInfo.getPackage()
- : mEntry.getSbn().getPackageName();
+ return mEntry.getSbn().getPackageName();
}
@Override
@@ -274,8 +218,7 @@ class Bubble implements BubbleViewProvider {
void inflate(BubbleViewInfoTask.Callback callback,
Context context,
BubbleStackView stackView,
- BubbleIconFactory iconFactory,
- boolean skipInflation) {
+ BubbleIconFactory iconFactory) {
if (isBubbleLoading()) {
mInflationTask.cancel(true /* mayInterruptIfRunning */);
}
@@ -283,7 +226,6 @@ class Bubble implements BubbleViewProvider {
context,
stackView,
iconFactory,
- skipInflation,
callback);
if (mInflateSynchronously) {
mInflationTask.onPostExecute(mInflationTask.doInBackground());
@@ -403,7 +345,6 @@ class Bubble implements BubbleViewProvider {
* Whether this notification should be shown in the shade.
*/
boolean showInShade() {
- if (mEntry == null) return false;
return !shouldSuppressNotification() || !mEntry.isClearable();
}
@@ -411,8 +352,8 @@ class Bubble implements BubbleViewProvider {
* Sets whether this notification should be suppressed in the shade.
*/
void setSuppressNotification(boolean suppressNotification) {
- if (mEntry == null) return;
boolean prevShowInShade = showInShade();
+
Notification.BubbleMetadata data = mEntry.getBubbleMetadata();
int flags = data.getFlags();
if (suppressNotification) {
@@ -443,7 +384,6 @@ class Bubble implements BubbleViewProvider {
*/
@Override
public boolean showDot() {
- if (mEntry == null) return false;
return mShowBubbleUpdateDot
&& !mEntry.shouldSuppressNotificationDot()
&& !shouldSuppressNotification();
@@ -453,7 +393,6 @@ class Bubble implements BubbleViewProvider {
* Whether the flyout for the bubble should be shown.
*/
boolean showFlyout() {
- if (mEntry == null) return false;
return !mSuppressFlyout && !mEntry.shouldSuppressPeek()
&& !shouldSuppressNotification()
&& !mEntry.shouldSuppressNotificationList();
@@ -477,13 +416,11 @@ class Bubble implements BubbleViewProvider {
* is an ongoing bubble.
*/
boolean isOngoing() {
- if (mEntry == null) return false;
int flags = mEntry.getSbn().getNotification().flags;
return (flags & Notification.FLAG_FOREGROUND_SERVICE) != 0;
}
float getDesiredHeight(Context context) {
- if (mEntry == null) return 0;
Notification.BubbleMetadata data = mEntry.getBubbleMetadata();
boolean useRes = data.getDesiredHeightResId() != 0;
if (useRes) {
@@ -497,7 +434,6 @@ class Bubble implements BubbleViewProvider {
}
String getDesiredHeightString() {
- if (mEntry == null) return String.valueOf(0);
Notification.BubbleMetadata data = mEntry.getBubbleMetadata();
boolean useRes = data.getDesiredHeightResId() != 0;
if (useRes) {
@@ -514,13 +450,11 @@ class Bubble implements BubbleViewProvider {
* To populate the icon use {@link LauncherApps#getShortcutIconDrawable(ShortcutInfo, int)}.
*/
boolean usingShortcutInfo() {
- return mEntry != null && mEntry.getBubbleMetadata().getShortcutId() != null
- || mShortcutInfo != null;
+ return mEntry.getBubbleMetadata().getShortcutId() != null;
}
@Nullable
PendingIntent getBubbleIntent() {
- if (mEntry == null) return null;
Notification.BubbleMetadata data = mEntry.getBubbleMetadata();
if (data != null) {
return data.getIntent();
@@ -528,32 +462,16 @@ class Bubble implements BubbleViewProvider {
return null;
}
- Intent getSettingsIntent(final Context context) {
+ Intent getSettingsIntent() {
final Intent intent = new Intent(Settings.ACTION_APP_NOTIFICATION_BUBBLE_SETTINGS);
intent.putExtra(Settings.EXTRA_APP_PACKAGE, getPackageName());
- final int uid = getUid(context);
- if (uid != -1) {
- intent.putExtra(Settings.EXTRA_APP_UID, uid);
- }
+ intent.putExtra(Settings.EXTRA_APP_UID, mEntry.getSbn().getUid());
intent.addFlags(Intent.FLAG_ACTIVITY_MULTIPLE_TASK);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
intent.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);
return intent;
}
- private int getUid(final Context context) {
- if (mEntry != null) return mEntry.getSbn().getUid();
- final PackageManager pm = context.getPackageManager();
- if (pm == null) return -1;
- try {
- final ApplicationInfo info = pm.getApplicationInfo(mShortcutInfo.getPackage(), 0);
- return info.uid;
- } catch (PackageManager.NameNotFoundException e) {
- Log.e(TAG, "cannot find uid", e);
- }
- return -1;
- }
-
private int getDimenForPackageUser(Context context, int resId, String pkg, int userId) {
PackageManager pm = context.getPackageManager();
Resources r;
@@ -575,30 +493,15 @@ class Bubble implements BubbleViewProvider {
}
private boolean shouldSuppressNotification() {
- if (mEntry == null) return false;
return mEntry.getBubbleMetadata() != null
&& mEntry.getBubbleMetadata().isNotificationSuppressed();
}
boolean shouldAutoExpand() {
- if (mEntry == null) return false;
Notification.BubbleMetadata metadata = mEntry.getBubbleMetadata();
return metadata != null && metadata.getAutoExpandBubble();
}
- public boolean isBubble() {
- if (mEntry == null) return (mFlags & FLAG_BUBBLE) != 0;
- return (mEntry.getSbn().getNotification().flags & FLAG_BUBBLE) != 0;
- }
-
- public void enable(int option) {
- mFlags |= option;
- }
-
- public void disable(int option) {
- mFlags &= ~option;
- }
-
@Override
public String toString() {
return "Bubble{" + mKey + '}';
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java
index 8707d389858b..a578f337cca2 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java
@@ -42,7 +42,6 @@ import static java.lang.annotation.ElementType.LOCAL_VARIABLE;
import static java.lang.annotation.ElementType.PARAMETER;
import static java.lang.annotation.RetentionPolicy.SOURCE;
-import android.annotation.NonNull;
import android.annotation.UserIdInt;
import android.app.ActivityManager.RunningTaskInfo;
import android.app.INotificationManager;
@@ -243,7 +242,7 @@ public class BubbleController implements ConfigurationController.ConfigurationLi
* This can happen when an app cancels a bubbled notification or when the user dismisses a
* bubble.
*/
- void removeNotification(@NonNull NotificationEntry entry, int reason);
+ void removeNotification(NotificationEntry entry, int reason);
/**
* Called when a bubbled notification has changed whether it should be
@@ -259,7 +258,7 @@ public class BubbleController implements ConfigurationController.ConfigurationLi
* removes all remnants of the group's summary from the notification pipeline.
* TODO: (b/145659174) Only old pipeline needs this - delete post-migration.
*/
- void maybeCancelSummary(@NonNull NotificationEntry entry);
+ void maybeCancelSummary(NotificationEntry entry);
}
/**
@@ -482,7 +481,7 @@ public class BubbleController implements ConfigurationController.ConfigurationLi
addNotifCallback(new NotifCallback() {
@Override
- public void removeNotification(@NonNull final NotificationEntry entry, int reason) {
+ public void removeNotification(NotificationEntry entry, int reason) {
mNotificationEntryManager.performRemoveNotification(entry.getSbn(),
reason);
}
@@ -493,7 +492,7 @@ public class BubbleController implements ConfigurationController.ConfigurationLi
}
@Override
- public void maybeCancelSummary(@NonNull final NotificationEntry entry) {
+ public void maybeCancelSummary(NotificationEntry entry) {
// Check if removed bubble has an associated suppressed group summary that needs
// to be removed now.
final String groupKey = entry.getSbn().getGroupKey();
@@ -702,12 +701,10 @@ public class BubbleController implements ConfigurationController.ConfigurationLi
mBubbleIconFactory = new BubbleIconFactory(mContext);
// Reload each bubble
for (Bubble b: mBubbleData.getBubbles()) {
- b.inflate(null /* callback */, mContext, mStackView, mBubbleIconFactory,
- false /* skipInflation */);
+ b.inflate(null /* callback */, mContext, mStackView, mBubbleIconFactory);
}
for (Bubble b: mBubbleData.getOverflowBubbles()) {
- b.inflate(null /* callback */, mContext, mStackView, mBubbleIconFactory,
- false /* skipInflation */);
+ b.inflate(null /* callback */, mContext, mStackView, mBubbleIconFactory);
}
}
@@ -806,7 +803,7 @@ public class BubbleController implements ConfigurationController.ConfigurationLi
if (bubble != null) {
mBubbleData.promoteBubbleFromOverflow(bubble, mStackView, mBubbleIconFactory);
}
- } else if (bubble.isBubble()) {
+ } else if (bubble.getEntry().isBubble()){
mBubbleData.setSelectedBubble(bubble);
}
mBubbleData.setExpanded(true);
@@ -835,33 +832,10 @@ public class BubbleController implements ConfigurationController.ConfigurationLi
updateBubble(notif, suppressFlyout, true /* showInShade */);
}
- /**
- * Fills the overflow bubbles by loading them from disk.
- */
- void loadOverflowBubblesFromDisk() {
- if (!mBubbleData.getOverflowBubbles().isEmpty()) {
- // we don't need to load overflow bubbles from disk if it is already in memory
- return;
- }
- mDataRepository.loadBubbles((bubbles) -> {
- bubbles.forEach(bubble -> {
- if (mBubbleData.getBubbles().contains(bubble)) {
- // if the bubble is already active, there's no need to push it to overflow
- return;
- }
- bubble.inflate((b) -> mBubbleData.overflowBubble(DISMISS_AGED, bubble),
- mContext, mStackView, mBubbleIconFactory, true /* skipInflation */);
- });
- return null;
- });
- }
-
void updateBubble(NotificationEntry notif, boolean suppressFlyout, boolean showInShade) {
if (mStackView == null) {
// Lazy init stack view when a bubble is created
ensureStackViewCreated();
- // Lazy load overflow bubbles from disk
- loadOverflowBubblesFromDisk();
}
// If this is an interruptive notif, mark that it's interrupted
if (notif.getImportance() >= NotificationManager.IMPORTANCE_HIGH) {
@@ -881,11 +855,11 @@ public class BubbleController implements ConfigurationController.ConfigurationLi
return;
}
mHandler.post(
- () -> removeBubble(bubble.getKey(),
+ () -> removeBubble(bubble.getEntry(),
BubbleController.DISMISS_INVALID_INTENT));
});
},
- mContext, mStackView, mBubbleIconFactory, false /* skipInflation */);
+ mContext, mStackView, mBubbleIconFactory);
}
/**
@@ -897,10 +871,7 @@ public class BubbleController implements ConfigurationController.ConfigurationLi
* @param entry the notification to change bubble state for.
* @param shouldBubble whether the notification should show as a bubble or not.
*/
- public void onUserChangedBubble(@Nullable final NotificationEntry entry, boolean shouldBubble) {
- if (entry == null) {
- return;
- }
+ public void onUserChangedBubble(NotificationEntry entry, boolean shouldBubble) {
NotificationChannel channel = entry.getChannel();
final String appPkg = entry.getSbn().getPackageName();
final int appUid = entry.getSbn().getUid();
@@ -939,14 +910,14 @@ public class BubbleController implements ConfigurationController.ConfigurationLi
}
/**
- * Removes the bubble with the given key.
+ * Removes the bubble with the given NotificationEntry.
* <p>
* Must be called from the main thread.
*/
@MainThread
- void removeBubble(String key, int reason) {
- if (mBubbleData.hasAnyBubbleWithKey(key)) {
- mBubbleData.notificationEntryRemoved(key, reason);
+ void removeBubble(NotificationEntry entry, int reason) {
+ if (mBubbleData.hasAnyBubbleWithKey(entry.getKey())) {
+ mBubbleData.notificationEntryRemoved(entry, reason);
}
}
@@ -962,7 +933,7 @@ public class BubbleController implements ConfigurationController.ConfigurationLi
&& canLaunchInActivityView(mContext, entry);
if (!shouldBubble && mBubbleData.hasAnyBubbleWithKey(entry.getKey())) {
// It was previously a bubble but no longer a bubble -- lets remove it
- removeBubble(entry.getKey(), DISMISS_NO_LONGER_BUBBLE);
+ removeBubble(entry, DISMISS_NO_LONGER_BUBBLE);
} else if (shouldBubble) {
updateBubble(entry);
}
@@ -976,10 +947,10 @@ public class BubbleController implements ConfigurationController.ConfigurationLi
// Remove any associated bubble children with the summary
final List<Bubble> bubbleChildren = mBubbleData.getBubblesInGroup(groupKey);
for (int i = 0; i < bubbleChildren.size(); i++) {
- removeBubble(bubbleChildren.get(i).getKey(), DISMISS_GROUP_CANCELLED);
+ removeBubble(bubbleChildren.get(i).getEntry(), DISMISS_GROUP_CANCELLED);
}
} else {
- removeBubble(entry.getKey(), DISMISS_NOTIF_CANCEL);
+ removeBubble(entry, DISMISS_NOTIF_CANCEL);
}
}
@@ -1001,8 +972,7 @@ public class BubbleController implements ConfigurationController.ConfigurationLi
rankingMap.getRanking(key, mTmpRanking);
boolean isActiveBubble = mBubbleData.hasAnyBubbleWithKey(key);
if (isActiveBubble && !mTmpRanking.canBubble()) {
- mBubbleData.notificationEntryRemoved(entry.getKey(),
- BubbleController.DISMISS_BLOCKED);
+ mBubbleData.notificationEntryRemoved(entry, BubbleController.DISMISS_BLOCKED);
} else if (entry != null && mTmpRanking.isBubble() && !isActiveBubble) {
entry.setFlagBubble(true);
onEntryUpdated(entry);
@@ -1012,15 +982,9 @@ public class BubbleController implements ConfigurationController.ConfigurationLi
private void setIsBubble(Bubble b, boolean isBubble) {
if (isBubble) {
- if (b.getEntry() != null) {
- b.getEntry().getSbn().getNotification().flags |= FLAG_BUBBLE;
- }
- b.enable(FLAG_BUBBLE);
+ b.getEntry().getSbn().getNotification().flags |= FLAG_BUBBLE;
} else {
- if (b.getEntry() != null) {
- b.getEntry().getSbn().getNotification().flags &= ~FLAG_BUBBLE;
- }
- b.disable(FLAG_BUBBLE);
+ b.getEntry().getSbn().getNotification().flags &= ~FLAG_BUBBLE;
}
try {
mBarService.onNotificationBubbleChanged(b.getKey(), isBubble, 0);
@@ -1068,27 +1032,23 @@ public class BubbleController implements ConfigurationController.ConfigurationLi
// The bubble is now gone & the notification is hidden from the shade, so
// time to actually remove it
for (NotifCallback cb : mCallbacks) {
- if (bubble.getEntry() != null) {
- cb.removeNotification(bubble.getEntry(), REASON_CANCEL);
- }
+ cb.removeNotification(bubble.getEntry(), REASON_CANCEL);
}
} else {
- if (bubble.isBubble() && bubble.showInShade()) {
+ if (bubble.getEntry().isBubble() && bubble.showInShade()) {
setIsBubble(bubble, /* isBubble */ false);
}
- if (bubble.getEntry() != null && bubble.getEntry().getRow() != null) {
+ if (bubble.getEntry().getRow() != null) {
bubble.getEntry().getRow().updateBubbleButton();
}
}
}
- if (bubble.getEntry() != null) {
- final String groupKey = bubble.getEntry().getSbn().getGroupKey();
- if (mBubbleData.getBubblesInGroup(groupKey).isEmpty()) {
- // Time to potentially remove the summary
- for (NotifCallback cb : mCallbacks) {
- cb.maybeCancelSummary(bubble.getEntry());
- }
+ final String groupKey = bubble.getEntry().getSbn().getGroupKey();
+ if (mBubbleData.getBubblesInGroup(groupKey).isEmpty()) {
+ // Time to potentially remove the summary
+ for (NotifCallback cb : mCallbacks) {
+ cb.maybeCancelSummary(bubble.getEntry());
}
}
}
@@ -1113,7 +1073,7 @@ public class BubbleController implements ConfigurationController.ConfigurationLi
if (update.selectionChanged) {
mStackView.setSelectedBubble(update.selectedBubble);
- if (update.selectedBubble != null && update.selectedBubble.getEntry() != null) {
+ if (update.selectedBubble != null) {
mNotificationGroupManager.updateSuppression(
update.selectedBubble.getEntry());
}
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleData.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleData.java
index 7b323ce8f609..65d5bebc625d 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleData.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleData.java
@@ -23,7 +23,6 @@ import static com.android.systemui.bubbles.BubbleDebugConfig.TAG_WITH_CLASS_NAME
import static java.util.stream.Collectors.toList;
-import android.annotation.NonNull;
import android.app.Notification;
import android.app.PendingIntent;
import android.content.Context;
@@ -224,7 +223,7 @@ public class BubbleData {
false, /* showInShade */ true);
setSelectedBubble(bubble);
},
- mContext, stack, factory, false /* skipInflation */);
+ mContext, stack, factory);
dispatchPendingChanges();
}
@@ -279,8 +278,7 @@ public class BubbleData {
}
mPendingBubbles.remove(bubble); // No longer pending once we're here
Bubble prevBubble = getBubbleInStackWithKey(bubble.getKey());
- suppressFlyout |= bubble.getEntry() == null
- || !bubble.getEntry().getRanking().visuallyInterruptive();
+ suppressFlyout |= !bubble.getEntry().getRanking().visuallyInterruptive();
if (prevBubble == null) {
// Create a new bubble
@@ -309,14 +307,11 @@ public class BubbleData {
dispatchPendingChanges();
}
- /**
- * Called when a notification associated with a bubble is removed.
- */
- public void notificationEntryRemoved(String key, @DismissReason int reason) {
+ public void notificationEntryRemoved(NotificationEntry entry, @DismissReason int reason) {
if (DEBUG_BUBBLE_DATA) {
- Log.d(TAG, "notificationEntryRemoved: key=" + key + " reason=" + reason);
+ Log.d(TAG, "notificationEntryRemoved: entry=" + entry + " reason=" + reason);
}
- doRemove(key, reason);
+ doRemove(entry.getKey(), reason);
dispatchPendingChanges();
}
@@ -364,7 +359,7 @@ public class BubbleData {
return bubbleChildren;
}
for (Bubble b : mBubbles) {
- if (b.getEntry() != null && groupKey.equals(b.getEntry().getSbn().getGroupKey())) {
+ if (groupKey.equals(b.getEntry().getSbn().getGroupKey())) {
bubbleChildren.add(b);
}
}
@@ -475,9 +470,7 @@ public class BubbleData {
Bubble newSelected = mBubbles.get(newIndex);
setSelectedBubbleInternal(newSelected);
}
- if (bubbleToRemove.getEntry() != null) {
- maybeSendDeleteIntent(reason, bubbleToRemove.getEntry());
- }
+ maybeSendDeleteIntent(reason, bubbleToRemove.getEntry());
}
void overflowBubble(@DismissReason int reason, Bubble bubble) {
@@ -751,8 +744,7 @@ public class BubbleData {
return true;
}
- private void maybeSendDeleteIntent(@DismissReason int reason,
- @NonNull final NotificationEntry entry) {
+ private void maybeSendDeleteIntent(@DismissReason int reason, NotificationEntry entry) {
if (reason == BubbleController.DISMISS_USER_GESTURE) {
Notification.BubbleMetadata bubbleMetadata = entry.getBubbleMetadata();
PendingIntent deleteIntent = bubbleMetadata != null
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleDataRepository.kt b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleDataRepository.kt
index 6df313212c76..ba93f4125ea5 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleDataRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleDataRepository.kt
@@ -74,10 +74,7 @@ internal class BubbleDataRepository @Inject constructor(
private fun transform(userId: Int, bubbles: List<Bubble>): List<BubbleEntity> {
return bubbles.mapNotNull { b ->
- var shortcutId = b.shortcutInfo?.id
- if (shortcutId == null) shortcutId = b.entry?.bubbleMetadata?.shortcutId
- if (shortcutId == null) shortcutId = b.entry?.ranking?.shortcutInfo?.id
- if (shortcutId == null) return@mapNotNull null
+ val shortcutId = b.shortcutInfo?.id ?: return@mapNotNull null
BubbleEntity(userId, b.packageName, shortcutId)
}
}
@@ -111,6 +108,7 @@ internal class BubbleDataRepository @Inject constructor(
/**
* Load bubbles from disk.
*/
+ // TODO: call this method from BubbleController and update UI
@SuppressLint("WrongConstant")
fun loadBubbles(cb: (List<Bubble>) -> Unit) = ioScope.launch {
/**
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExpandedView.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExpandedView.java
index 0a1a0f77b32e..baf92dc7abe7 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExpandedView.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExpandedView.java
@@ -65,6 +65,7 @@ import com.android.systemui.Dependency;
import com.android.systemui.R;
import com.android.systemui.recents.TriangleShape;
import com.android.systemui.statusbar.AlphaOptimizedButton;
+import com.android.systemui.statusbar.notification.collection.NotificationEntry;
/**
* Container for the expanded bubble view, handles rendering the caret and settings icon.
@@ -160,7 +161,7 @@ public class BubbleExpandedView extends LinearLayout {
// the bubble again so we'll just remove it.
Log.w(TAG, "Exception while displaying bubble: " + getBubbleKey()
+ ", " + e.getMessage() + "; removing bubble");
- mBubbleController.removeBubble(getBubbleKey(),
+ mBubbleController.removeBubble(getBubbleEntry(),
BubbleController.DISMISS_INVALID_INTENT);
}
});
@@ -204,7 +205,7 @@ public class BubbleExpandedView extends LinearLayout {
}
if (mBubble != null) {
// Must post because this is called from a binder thread.
- post(() -> mBubbleController.removeBubble(mBubble.getKey(),
+ post(() -> mBubbleController.removeBubble(mBubble.getEntry(),
BubbleController.DISMISS_TASK_FINISHED));
}
}
@@ -291,6 +292,10 @@ public class BubbleExpandedView extends LinearLayout {
return mBubble != null ? mBubble.getKey() : "null";
}
+ private NotificationEntry getBubbleEntry() {
+ return mBubble != null ? mBubble.getEntry() : null;
+ }
+
void setManageClickListener(OnClickListener manageClickListener) {
findViewById(R.id.settings_button).setOnClickListener(manageClickListener);
}
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java
index e35b20392380..88f5eb0b250c 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java
@@ -49,6 +49,7 @@ import android.graphics.RectF;
import android.graphics.Region;
import android.os.Bundle;
import android.os.Vibrator;
+import android.service.notification.StatusBarNotification;
import android.util.Log;
import android.view.Choreographer;
import android.view.DisplayCutout;
@@ -918,10 +919,10 @@ public class BubbleStackView extends FrameLayout
showManageMenu(false /* show */);
final Bubble bubble = mBubbleData.getSelectedBubble();
if (bubble != null && mBubbleData.hasBubbleInStackWithKey(bubble.getKey())) {
- final Intent intent = bubble.getSettingsIntent(mContext);
+ final Intent intent = bubble.getSettingsIntent();
collapseStack(() -> {
-
- mContext.startActivityAsUser(intent, bubble.getUser());
+ mContext.startActivityAsUser(
+ intent, bubble.getEntry().getSbn().getUser());
logBubbleClickEvent(
bubble,
SysUiStatsLog.BUBBLE_UICHANGED__ACTION__HEADER_GO_TO_SETTINGS);
@@ -1178,19 +1179,13 @@ public class BubbleStackView extends FrameLayout
for (int i = 0; i < mBubbleData.getBubbles().size(); i++) {
final Bubble bubble = mBubbleData.getBubbles().get(i);
final String appName = bubble.getAppName();
+ final Notification notification = bubble.getEntry().getSbn().getNotification();
+ final CharSequence titleCharSeq =
+ notification.extras.getCharSequence(Notification.EXTRA_TITLE);
- final CharSequence titleCharSeq;
- if (bubble.getEntry() == null) {
- titleCharSeq = null;
- } else {
- titleCharSeq = bubble.getEntry().getSbn().getNotification().extras.getCharSequence(
- Notification.EXTRA_TITLE);
- }
- final String titleStr;
+ String titleStr = getResources().getString(R.string.notification_bubble_title);
if (titleCharSeq != null) {
titleStr = titleCharSeq.toString();
- } else {
- titleStr = getResources().getString(R.string.notification_bubble_title);
}
if (bubble.getIconView() != null) {
@@ -1803,7 +1798,7 @@ public class BubbleStackView extends FrameLayout
private void dismissBubbleIfExists(@Nullable Bubble bubble) {
if (bubble != null && mBubbleData.hasBubbleInStackWithKey(bubble.getKey())) {
mBubbleData.notificationEntryRemoved(
- bubble.getKey(), BubbleController.DISMISS_USER_GESTURE);
+ bubble.getEntry(), BubbleController.DISMISS_USER_GESTURE);
}
}
@@ -2301,12 +2296,18 @@ public class BubbleStackView extends FrameLayout
* @param action the user interaction enum.
*/
private void logBubbleClickEvent(Bubble bubble, int action) {
- bubble.logUIEvent(
+ StatusBarNotification notification = bubble.getEntry().getSbn();
+ SysUiStatsLog.write(SysUiStatsLog.BUBBLE_UI_CHANGED,
+ notification.getPackageName(),
+ notification.getNotification().getChannelId(),
+ notification.getId(),
+ getBubbleIndex(getExpandedBubble()),
getBubbleCount(),
action,
getNormalizedXPosition(),
getNormalizedYPosition(),
- getBubbleIndex(getExpandedBubble())
- );
+ bubble.showInShade(),
+ bubble.isOngoing(),
+ false /* isAppForeground (unused) */);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleViewInfoTask.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleViewInfoTask.java
index 525d5b56cc8e..8a57a735f6cb 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleViewInfoTask.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleViewInfoTask.java
@@ -37,7 +37,6 @@ import android.graphics.drawable.Drawable;
import android.graphics.drawable.Icon;
import android.os.AsyncTask;
import android.os.Parcelable;
-import android.os.UserHandle;
import android.service.notification.StatusBarNotification;
import android.text.TextUtils;
import android.util.Log;
@@ -75,7 +74,6 @@ public class BubbleViewInfoTask extends AsyncTask<Void, Void, BubbleViewInfoTask
private WeakReference<Context> mContext;
private WeakReference<BubbleStackView> mStackView;
private BubbleIconFactory mIconFactory;
- private boolean mSkipInflation;
private Callback mCallback;
/**
@@ -86,20 +84,17 @@ public class BubbleViewInfoTask extends AsyncTask<Void, Void, BubbleViewInfoTask
Context context,
BubbleStackView stackView,
BubbleIconFactory factory,
- boolean skipInflation,
Callback c) {
mBubble = b;
mContext = new WeakReference<>(context);
mStackView = new WeakReference<>(stackView);
mIconFactory = factory;
- mSkipInflation = skipInflation;
mCallback = c;
}
@Override
protected BubbleViewInfo doInBackground(Void... voids) {
- return BubbleViewInfo.populate(mContext.get(), mStackView.get(), mIconFactory, mBubble,
- mSkipInflation);
+ return BubbleViewInfo.populate(mContext.get(), mStackView.get(), mIconFactory, mBubble);
}
@Override
@@ -128,36 +123,11 @@ public class BubbleViewInfoTask extends AsyncTask<Void, Void, BubbleViewInfoTask
@Nullable
static BubbleViewInfo populate(Context c, BubbleStackView stackView,
- BubbleIconFactory iconFactory, Bubble b, boolean skipInflation) {
- final NotificationEntry entry = b.getEntry();
- if (entry == null) {
- // populate from ShortcutInfo when NotificationEntry is not available
- final ShortcutInfo s = b.getShortcutInfo();
- return populate(c, stackView, iconFactory, skipInflation || b.isInflated(),
- s.getPackage(), s.getUserHandle(), s, null);
- }
- final StatusBarNotification sbn = entry.getSbn();
- final String bubbleShortcutId = entry.getBubbleMetadata().getShortcutId();
- final ShortcutInfo si = bubbleShortcutId == null
- ? null : entry.getRanking().getShortcutInfo();
- return populate(
- c, stackView, iconFactory, skipInflation || b.isInflated(),
- sbn.getPackageName(), sbn.getUser(), si, entry);
- }
-
- private static BubbleViewInfo populate(
- @NonNull final Context c,
- @NonNull final BubbleStackView stackView,
- @NonNull final BubbleIconFactory iconFactory,
- final boolean isInflated,
- @NonNull final String packageName,
- @NonNull final UserHandle user,
- @Nullable final ShortcutInfo shortcutInfo,
- @Nullable final NotificationEntry entry) {
+ BubbleIconFactory iconFactory, Bubble b) {
BubbleViewInfo info = new BubbleViewInfo();
// View inflation: only should do this once per bubble
- if (!isInflated) {
+ if (!b.isInflated()) {
LayoutInflater inflater = LayoutInflater.from(c);
info.imageView = (BadgedImageView) inflater.inflate(
R.layout.bubble_view, stackView, false /* attachToRoot */);
@@ -167,8 +137,12 @@ public class BubbleViewInfoTask extends AsyncTask<Void, Void, BubbleViewInfoTask
info.expandedView.setStackView(stackView);
}
- if (shortcutInfo != null) {
- info.shortcutInfo = shortcutInfo;
+ StatusBarNotification sbn = b.getEntry().getSbn();
+ String packageName = sbn.getPackageName();
+
+ String bubbleShortcutId = b.getEntry().getBubbleMetadata().getShortcutId();
+ if (bubbleShortcutId != null) {
+ info.shortcutInfo = b.getEntry().getRanking().getShortcutInfo();
}
// App name & app icon
@@ -187,7 +161,7 @@ public class BubbleViewInfoTask extends AsyncTask<Void, Void, BubbleViewInfoTask
info.appName = String.valueOf(pm.getApplicationLabel(appInfo));
}
appIcon = pm.getApplicationIcon(packageName);
- badgedIcon = pm.getUserBadgedIcon(appIcon, user);
+ badgedIcon = pm.getUserBadgedIcon(appIcon, sbn.getUser());
} catch (PackageManager.NameNotFoundException exception) {
// If we can't find package... don't think we should show the bubble.
Log.w(TAG, "Unable to find package: " + packageName);
@@ -196,7 +170,7 @@ public class BubbleViewInfoTask extends AsyncTask<Void, Void, BubbleViewInfoTask
// Badged bubble image
Drawable bubbleDrawable = iconFactory.getBubbleDrawable(c, info.shortcutInfo,
- entry == null ? null : entry.getBubbleMetadata());
+ b.getEntry().getBubbleMetadata());
if (bubbleDrawable == null) {
// Default to app icon
bubbleDrawable = appIcon;
@@ -222,9 +196,7 @@ public class BubbleViewInfoTask extends AsyncTask<Void, Void, BubbleViewInfoTask
Color.WHITE, WHITE_SCRIM_ALPHA);
// Flyout
- if (entry != null) {
- info.flyoutMessage = extractFlyoutMessage(c, entry);
- }
+ info.flyoutMessage = extractFlyoutMessage(c, b.getEntry());
return info;
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/controls/management/AllModel.kt b/packages/SystemUI/src/com/android/systemui/controls/management/AllModel.kt
index 175ed061c714..00a406e4dbc0 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/management/AllModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/management/AllModel.kt
@@ -48,6 +48,8 @@ class AllModel(
private var modified = false
+ override val moveHelper = null
+
override val favorites: List<ControlInfo>
get() = favoriteIds.mapNotNull { id ->
val control = controls.firstOrNull { it.control.controlId == id }?.control
diff --git a/packages/SystemUI/src/com/android/systemui/controls/management/ControlAdapter.kt b/packages/SystemUI/src/com/android/systemui/controls/management/ControlAdapter.kt
index 4b283d607bb8..2f917107cc23 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/management/ControlAdapter.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/management/ControlAdapter.kt
@@ -18,6 +18,7 @@ package com.android.systemui.controls.management
import android.content.ComponentName
import android.graphics.Rect
+import android.os.Bundle
import android.service.controls.Control
import android.service.controls.DeviceTypes
import android.view.LayoutInflater
@@ -78,7 +79,7 @@ class ControlAdapter(
background = parent.context.getDrawable(
R.drawable.control_background_ripple)
},
- model is FavoritesModel // Indicates that position information is needed
+ model?.moveHelper // Indicates that position information is needed
) { id, favorite ->
model?.changeFavoriteStatus(id, favorite)
}
@@ -176,12 +177,14 @@ private class ZoneHolder(view: View) : Holder(view) {
/**
* Holder for using with [ControlStatusWrapper] to display names of zones.
+ * @param moveHelper a helper interface to facilitate a11y rearranging. Null indicates no
+ * rearranging
* @param favoriteCallback this callback will be called whenever the favorite state of the
* [Control] this view represents changes.
*/
internal class ControlHolder(
view: View,
- val withPosition: Boolean,
+ val moveHelper: ControlsModel.MoveHelper?,
val favoriteCallback: ModelFavoriteChanger
) : Holder(view) {
private val favoriteStateDescription =
@@ -197,7 +200,11 @@ internal class ControlHolder(
visibility = View.VISIBLE
}
- private val accessibilityDelegate = ControlHolderAccessibilityDelegate(this::stateDescription)
+ private val accessibilityDelegate = ControlHolderAccessibilityDelegate(
+ this::stateDescription,
+ this::getLayoutPosition,
+ moveHelper
+ )
init {
ViewCompat.setAccessibilityDelegate(itemView, accessibilityDelegate)
@@ -207,7 +214,7 @@ internal class ControlHolder(
private fun stateDescription(favorite: Boolean): CharSequence? {
if (!favorite) {
return notFavoriteStateDescription
- } else if (!withPosition) {
+ } else if (moveHelper == null) {
return favoriteStateDescription
} else {
val position = layoutPosition + 1
@@ -256,15 +263,67 @@ internal class ControlHolder(
}
}
+/**
+ * Accessibility delegate for [ControlHolder].
+ *
+ * Provides the following functionality:
+ * * Sets the state description indicating whether the controls is Favorited or Unfavorited
+ * * Adds the position to the state description if necessary.
+ * * Adds context action for moving (rearranging) a control.
+ *
+ * @param stateRetriever function to determine the state description based on the favorite state
+ * @param positionRetriever function to obtain the position of this control. It only has to be
+ * correct in controls that are currently favorites (and therefore can
+ * be moved).
+ * @param moveHelper helper interface to determine if a control can be moved and actually move it.
+ */
private class ControlHolderAccessibilityDelegate(
- val stateRetriever: (Boolean) -> CharSequence?
+ val stateRetriever: (Boolean) -> CharSequence?,
+ val positionRetriever: () -> Int,
+ val moveHelper: ControlsModel.MoveHelper?
) : AccessibilityDelegateCompat() {
var isFavorite = false
+ companion object {
+ private val MOVE_BEFORE_ID = R.id.accessibility_action_controls_move_before
+ private val MOVE_AFTER_ID = R.id.accessibility_action_controls_move_after
+ }
+
override fun onInitializeAccessibilityNodeInfo(host: View, info: AccessibilityNodeInfoCompat) {
super.onInitializeAccessibilityNodeInfo(host, info)
+ info.isContextClickable = false
+ addClickAction(host, info)
+ maybeAddMoveBeforeAction(host, info)
+ maybeAddMoveAfterAction(host, info)
+
+ // Determine the stateDescription based on the holder information
+ info.stateDescription = stateRetriever(isFavorite)
+ // Remove the information at the end indicating row and column.
+ info.setCollectionItemInfo(null)
+
+ info.className = Switch::class.java.name
+ }
+
+ override fun performAccessibilityAction(host: View?, action: Int, args: Bundle?): Boolean {
+ if (super.performAccessibilityAction(host, action, args)) {
+ return true
+ }
+ return when (action) {
+ MOVE_BEFORE_ID -> {
+ moveHelper?.moveBefore(positionRetriever())
+ true
+ }
+ MOVE_AFTER_ID -> {
+ moveHelper?.moveAfter(positionRetriever())
+ true
+ }
+ else -> false
+ }
+ }
+
+ private fun addClickAction(host: View, info: AccessibilityNodeInfoCompat) {
// Change the text for the double-tap action
val clickActionString = if (isFavorite) {
host.context.getString(R.string.accessibility_control_change_unfavorite)
@@ -276,13 +335,30 @@ private class ControlHolderAccessibilityDelegate(
// “favorite/unfavorite”
clickActionString)
info.addAction(click)
+ }
- // Determine the stateDescription based on the holder information
- info.stateDescription = stateRetriever(isFavorite)
- // Remove the information at the end indicating row and column.
- info.setCollectionItemInfo(null)
+ private fun maybeAddMoveBeforeAction(host: View, info: AccessibilityNodeInfoCompat) {
+ if (moveHelper?.canMoveBefore(positionRetriever()) ?: false) {
+ val newPosition = positionRetriever() + 1 - 1
+ val moveBefore = AccessibilityNodeInfoCompat.AccessibilityActionCompat(
+ MOVE_BEFORE_ID,
+ host.context.getString(R.string.accessibility_control_move, newPosition)
+ )
+ info.addAction(moveBefore)
+ info.isContextClickable = true
+ }
+ }
- info.className = Switch::class.java.name
+ private fun maybeAddMoveAfterAction(host: View, info: AccessibilityNodeInfoCompat) {
+ if (moveHelper?.canMoveAfter(positionRetriever()) ?: false) {
+ val newPosition = positionRetriever() + 1 + 1
+ val moveAfter = AccessibilityNodeInfoCompat.AccessibilityActionCompat(
+ MOVE_AFTER_ID,
+ host.context.getString(R.string.accessibility_control_move, newPosition)
+ )
+ info.addAction(moveAfter)
+ info.isContextClickable = true
+ }
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/controls/management/ControlsModel.kt b/packages/SystemUI/src/com/android/systemui/controls/management/ControlsModel.kt
index 37b6d15c0afe..254395368bf9 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/management/ControlsModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/management/ControlsModel.kt
@@ -42,6 +42,8 @@ interface ControlsModel {
*/
val elements: List<ElementWrapper>
+ val moveHelper: MoveHelper?
+
/**
* Change the favorite status of a particular control.
*/
@@ -69,6 +71,34 @@ interface ControlsModel {
*/
fun onFirstChange()
}
+
+ /**
+ * Interface to facilitate moving controls from an [AccessibilityDelegate].
+ *
+ * All positions should be 0 based.
+ */
+ interface MoveHelper {
+
+ /**
+ * Whether the control in `position` can be moved to the position before it.
+ */
+ fun canMoveBefore(position: Int): Boolean
+
+ /**
+ * Whether the control in `position` can be moved to the position after it.
+ */
+ fun canMoveAfter(position: Int): Boolean
+
+ /**
+ * Move the control in `position` to the position before it.
+ */
+ fun moveBefore(position: Int)
+
+ /**
+ * Move the control in `position` to the position after it.
+ */
+ fun moveAfter(position: Int)
+ }
}
/**
diff --git a/packages/SystemUI/src/com/android/systemui/controls/management/FavoritesModel.kt b/packages/SystemUI/src/com/android/systemui/controls/management/FavoritesModel.kt
index 411170cb322c..524250134e9b 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/management/FavoritesModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/management/FavoritesModel.kt
@@ -17,6 +17,7 @@
package com.android.systemui.controls.management
import android.content.ComponentName
+import android.util.Log
import androidx.recyclerview.widget.ItemTouchHelper
import androidx.recyclerview.widget.RecyclerView
import com.android.systemui.controls.ControlInterface
@@ -39,9 +40,39 @@ class FavoritesModel(
private val favoritesModelCallback: FavoritesModelCallback
) : ControlsModel {
+ companion object {
+ private const val TAG = "FavoritesModel"
+ }
+
private var adapter: RecyclerView.Adapter<*>? = null
private var modified = false
+ override val moveHelper = object : ControlsModel.MoveHelper {
+ override fun canMoveBefore(position: Int): Boolean {
+ return position > 0 && position < dividerPosition
+ }
+
+ override fun canMoveAfter(position: Int): Boolean {
+ return position >= 0 && position < dividerPosition - 1
+ }
+
+ override fun moveBefore(position: Int) {
+ if (!canMoveBefore(position)) {
+ Log.w(TAG, "Cannot move position $position before")
+ } else {
+ onMoveItem(position, position - 1)
+ }
+ }
+
+ override fun moveAfter(position: Int) {
+ if (!canMoveAfter(position)) {
+ Log.w(TAG, "Cannot move position $position after")
+ } else {
+ onMoveItem(position, position + 1)
+ }
+ }
+ }
+
override fun attachAdapter(adapter: RecyclerView.Adapter<*>) {
this.adapter = adapter
}
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshot.java b/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshot.java
index 9cfb1b27cbf3..839ea69953af 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshot.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshot.java
@@ -56,6 +56,7 @@ import android.os.PowerManager;
import android.os.RemoteException;
import android.os.UserHandle;
import android.provider.DeviceConfig;
+import android.provider.Settings;
import android.util.DisplayMetrics;
import android.util.Log;
import android.util.MathUtils;
@@ -160,6 +161,9 @@ public class GlobalScreenshot implements ViewTreeObserver.OnComputeInternalInset
static final String EXTRA_CANCEL_NOTIFICATION = "android:screenshot_cancel_notification";
static final String EXTRA_DISALLOW_ENTER_PIP = "android:screenshot_disallow_enter_pip";
+ // From WizardManagerHelper.java
+ private static final String SETTINGS_SECURE_USER_SETUP_COMPLETE = "user_setup_complete";
+
private static final String TAG = "GlobalScreenshot";
private static final long SCREENSHOT_FLASH_IN_DURATION_MS = 133;
@@ -460,6 +464,13 @@ public class GlobalScreenshot implements ViewTreeObserver.OnComputeInternalInset
return;
}
+ if (!isUserSetupComplete()) {
+ // User setup isn't complete, so we don't want to show any UI beyond a toast, as editing
+ // and sharing shouldn't be exposed to the user.
+ saveScreenshotAndToast(finisher);
+ return;
+ }
+
// Optimizations
mScreenBitmap.setHasAlpha(false);
mScreenBitmap.prepareToDraw();
@@ -546,6 +557,41 @@ public class GlobalScreenshot implements ViewTreeObserver.OnComputeInternalInset
}
/**
+ * Save the bitmap but don't show the normal screenshot UI.. just a toast (or notification on
+ * failure).
+ */
+ private void saveScreenshotAndToast(Consumer<Uri> finisher) {
+ // Play the shutter sound to notify that we've taken a screenshot
+ mScreenshotHandler.post(() -> {
+ mCameraSound.play(MediaActionSound.SHUTTER_CLICK);
+ });
+
+ saveScreenshotInWorkerThread(finisher, new ActionsReadyListener() {
+ @Override
+ void onActionsReady(SavedImageData imageData) {
+ finisher.accept(imageData.uri);
+ if (imageData.uri == null) {
+ mUiEventLogger.log(ScreenshotEvent.SCREENSHOT_NOT_SAVED);
+ mNotificationsController.notifyScreenshotError(
+ R.string.screenshot_failed_to_capture_text);
+ } else {
+ mUiEventLogger.log(ScreenshotEvent.SCREENSHOT_SAVED);
+
+ mScreenshotHandler.post(() -> {
+ Toast.makeText(mContext, R.string.screenshot_saved_title,
+ Toast.LENGTH_SHORT).show();
+ });
+ }
+ }
+ });
+ }
+
+ private boolean isUserSetupComplete() {
+ return Settings.Secure.getInt(mContext.getContentResolver(),
+ SETTINGS_SECURE_USER_SETUP_COMPLETE, 0) == 1;
+ }
+
+ /**
* Clears current screenshot
*/
private void dismissScreenshot(String reason, boolean immediate) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/ActionClickLogger.kt b/packages/SystemUI/src/com/android/systemui/statusbar/ActionClickLogger.kt
new file mode 100644
index 000000000000..7f7ff9cf4881
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/ActionClickLogger.kt
@@ -0,0 +1,88 @@
+/*
+ * Copyright (C) 2020 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.statusbar
+
+import android.app.PendingIntent
+import com.android.systemui.log.LogBuffer
+import com.android.systemui.log.LogLevel
+import com.android.systemui.log.dagger.NotifInteractionLog
+import com.android.systemui.statusbar.notification.collection.NotificationEntry
+import javax.inject.Inject
+
+/**
+ * Logger class for events related to the user clicking on notification actions
+ */
+class ActionClickLogger @Inject constructor(
+ @NotifInteractionLog private val buffer: LogBuffer
+) {
+ fun logInitialClick(
+ entry: NotificationEntry?,
+ pendingIntent: PendingIntent
+ ) {
+ buffer.log(TAG, LogLevel.DEBUG, {
+ str1 = entry?.key
+ str2 = entry?.ranking?.channel?.id
+ str3 = pendingIntent.intent.toString()
+ }, {
+ "ACTION CLICK $str1 (channel=$str2) for pending intent $str3"
+ })
+ }
+
+ fun logRemoteInputWasHandled(
+ entry: NotificationEntry?
+ ) {
+ buffer.log(TAG, LogLevel.DEBUG, {
+ str1 = entry?.key
+ }, {
+ " [Action click] Triggered remote input (for $str1))"
+ })
+ }
+
+ fun logStartingIntentWithDefaultHandler(
+ entry: NotificationEntry?,
+ pendingIntent: PendingIntent
+ ) {
+ buffer.log(TAG, LogLevel.DEBUG, {
+ str1 = entry?.key
+ str2 = pendingIntent.intent.toString()
+ }, {
+ " [Action click] Launching intent $str2 via default handler (for $str1)"
+ })
+ }
+
+ fun logWaitingToCloseKeyguard(
+ pendingIntent: PendingIntent
+ ) {
+ buffer.log(TAG, LogLevel.DEBUG, {
+ str1 = pendingIntent.intent.toString()
+ }, {
+ " [Action click] Intent $str1 launches an activity, dismissing keyguard first..."
+ })
+ }
+
+ fun logKeyguardGone(
+ pendingIntent: PendingIntent
+ ) {
+ buffer.log(TAG, LogLevel.DEBUG, {
+ str1 = pendingIntent.intent.toString()
+ }, {
+ " [Action click] Keyguard dismissed, calling default handler for intent $str1"
+ })
+ }
+}
+
+private const val TAG = "ActionClickLogger" \ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationRemoteInputManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationRemoteInputManager.java
index bf28040233f5..9181c69e3722 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationRemoteInputManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationRemoteInputManager.java
@@ -54,7 +54,7 @@ import com.android.systemui.Dumpable;
import com.android.systemui.R;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
-import com.android.systemui.statusbar.dagger.StatusBarModule;
+import com.android.systemui.statusbar.dagger.StatusBarDependenciesModule;
import com.android.systemui.statusbar.notification.NotificationEntryListener;
import com.android.systemui.statusbar.notification.NotificationEntryManager;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
@@ -114,6 +114,7 @@ public class NotificationRemoteInputManager implements Dumpable {
private final SmartReplyController mSmartReplyController;
private final NotificationEntryManager mEntryManager;
private final Handler mMainHandler;
+ private final ActionClickLogger mLogger;
private final Lazy<StatusBar> mStatusBarLazy;
@@ -138,14 +139,18 @@ public class NotificationRemoteInputManager implements Dumpable {
mStatusBarLazy.get().wakeUpIfDozing(SystemClock.uptimeMillis(), view,
"NOTIFICATION_CLICK");
+ final NotificationEntry entry = getNotificationForParent(view.getParent());
+ mLogger.logInitialClick(entry, pendingIntent);
+
if (handleRemoteInput(view, pendingIntent)) {
+ mLogger.logRemoteInputWasHandled(entry);
return true;
}
if (DEBUG) {
Log.v(TAG, "Notification click handler invoked for intent: " + pendingIntent);
}
- logActionClick(view, pendingIntent);
+ logActionClick(view, entry, pendingIntent);
// The intent we are sending is for the application, which
// won't have permission to immediately start an activity after
// the user switches to home. We know it is safe to do at this
@@ -158,11 +163,15 @@ public class NotificationRemoteInputManager implements Dumpable {
Pair<Intent, ActivityOptions> options = response.getLaunchOptions(view);
options.second.setLaunchWindowingMode(
WINDOWING_MODE_FULLSCREEN_OR_SPLIT_SCREEN_SECONDARY);
+ mLogger.logStartingIntentWithDefaultHandler(entry, pendingIntent);
return RemoteViews.startPendingIntent(view, pendingIntent, options);
});
}
- private void logActionClick(View view, PendingIntent actionIntent) {
+ private void logActionClick(
+ View view,
+ NotificationEntry entry,
+ PendingIntent actionIntent) {
Integer actionIndex = (Integer)
view.getTag(com.android.internal.R.id.notification_action_index_tag);
if (actionIndex == null) {
@@ -170,7 +179,7 @@ public class NotificationRemoteInputManager implements Dumpable {
return;
}
ViewParent parent = view.getParent();
- StatusBarNotification statusBarNotification = getNotificationForParent(parent);
+ StatusBarNotification statusBarNotification = entry.getSbn();
if (statusBarNotification == null) {
Log.w(TAG, "Couldn't determine notification for click.");
return;
@@ -212,10 +221,10 @@ public class NotificationRemoteInputManager implements Dumpable {
}
}
- private StatusBarNotification getNotificationForParent(ViewParent parent) {
+ private NotificationEntry getNotificationForParent(ViewParent parent) {
while (parent != null) {
if (parent instanceof ExpandableNotificationRow) {
- return ((ExpandableNotificationRow) parent).getEntry().getSbn();
+ return ((ExpandableNotificationRow) parent).getEntry();
}
parent = parent.getParent();
}
@@ -255,7 +264,7 @@ public class NotificationRemoteInputManager implements Dumpable {
};
/**
- * Injected constructor. See {@link StatusBarModule}.
+ * Injected constructor. See {@link StatusBarDependenciesModule}.
*/
public NotificationRemoteInputManager(
Context context,
@@ -265,13 +274,15 @@ public class NotificationRemoteInputManager implements Dumpable {
Lazy<StatusBar> statusBarLazy,
StatusBarStateController statusBarStateController,
@Main Handler mainHandler,
- RemoteInputUriController remoteInputUriController) {
+ RemoteInputUriController remoteInputUriController,
+ ActionClickLogger logger) {
mContext = context;
mLockscreenUserManager = lockscreenUserManager;
mSmartReplyController = smartReplyController;
mEntryManager = notificationEntryManager;
mStatusBarLazy = statusBarLazy;
mMainHandler = mainHandler;
+ mLogger = logger;
mBarService = IStatusBarService.Stub.asInterface(
ServiceManager.getService(Context.STATUS_BAR_SERVICE));
mUserManager = (UserManager) mContext.getSystemService(Context.USER_SERVICE);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/dagger/StatusBarDependenciesModule.java b/packages/SystemUI/src/com/android/systemui/statusbar/dagger/StatusBarDependenciesModule.java
index f0fed13114ba..b08eb9fafe41 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/dagger/StatusBarDependenciesModule.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/dagger/StatusBarDependenciesModule.java
@@ -25,6 +25,7 @@ import com.android.systemui.bubbles.BubbleController;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.media.MediaDataManager;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
+import com.android.systemui.statusbar.ActionClickLogger;
import com.android.systemui.statusbar.CommandQueue;
import com.android.systemui.statusbar.MediaArtworkProcessor;
import com.android.systemui.statusbar.NotificationListener;
@@ -73,7 +74,8 @@ public interface StatusBarDependenciesModule {
Lazy<StatusBar> statusBarLazy,
StatusBarStateController statusBarStateController,
Handler mainHandler,
- RemoteInputUriController remoteInputUriController) {
+ RemoteInputUriController remoteInputUriController,
+ ActionClickLogger actionClickLogger) {
return new NotificationRemoteInputManager(
context,
lockscreenUserManager,
@@ -82,7 +84,8 @@ public interface StatusBarDependenciesModule {
statusBarLazy,
statusBarStateController,
mainHandler,
- remoteInputUriController);
+ remoteInputUriController,
+ actionClickLogger);
}
/** */
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/BubbleCoordinator.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/BubbleCoordinator.java
index 57be1a502842..92426e54ec91 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/BubbleCoordinator.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/BubbleCoordinator.java
@@ -19,8 +19,6 @@ package com.android.systemui.statusbar.notification.collection.coordinator;
import static android.service.notification.NotificationStats.DISMISSAL_OTHER;
import static android.service.notification.NotificationStats.DISMISS_SENTIMENT_NEUTRAL;
-import android.annotation.NonNull;
-
import com.android.internal.statusbar.NotificationVisibility;
import com.android.systemui.bubbles.BubbleController;
import com.android.systemui.statusbar.notification.collection.NotifCollection;
@@ -123,7 +121,7 @@ public class BubbleCoordinator implements Coordinator {
private final BubbleController.NotifCallback mNotifCallback =
new BubbleController.NotifCallback() {
@Override
- public void removeNotification(@NonNull final NotificationEntry entry, int reason) {
+ public void removeNotification(NotificationEntry entry, int reason) {
if (isInterceptingDismissal(entry)) {
mInterceptedDismissalEntries.remove(entry.getKey());
mOnEndDismissInterception.onEndDismissInterception(mDismissInterceptor, entry,
@@ -143,7 +141,7 @@ public class BubbleCoordinator implements Coordinator {
}
@Override
- public void maybeCancelSummary(@NonNull final NotificationEntry entry) {
+ public void maybeCancelSummary(NotificationEntry entry) {
// no-op
}
};
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationGroupManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationGroupManager.java
index d0a872eb112f..80785db6df3e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationGroupManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationGroupManager.java
@@ -16,7 +16,6 @@
package com.android.systemui.statusbar.phone;
-import android.annotation.NonNull;
import android.annotation.Nullable;
import android.service.notification.StatusBarNotification;
import android.util.ArraySet;
@@ -455,7 +454,7 @@ public class NotificationGroupManager implements OnHeadsUpChangedListener, State
* If there is a {@link NotificationGroup} associated with the provided entry, this method
* will update the suppression of that group.
*/
- public void updateSuppression(@NonNull final NotificationEntry entry) {
+ public void updateSuppression(NotificationEntry entry) {
NotificationGroup group = mGroupMap.get(getGroupKey(entry.getSbn()));
if (group != null) {
updateSuppression(group);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarRemoteInputCallback.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarRemoteInputCallback.java
index 428de9e9adbb..669e6a4f2138 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarRemoteInputCallback.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarRemoteInputCallback.java
@@ -37,6 +37,7 @@ import android.view.ViewParent;
import com.android.systemui.ActivityIntentHelper;
import com.android.systemui.plugins.ActivityStarter;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
+import com.android.systemui.statusbar.ActionClickLogger;
import com.android.systemui.statusbar.CommandQueue;
import com.android.systemui.statusbar.CommandQueue.Callbacks;
import com.android.systemui.statusbar.NotificationLockscreenUserManager;
@@ -73,6 +74,7 @@ public class StatusBarRemoteInputCallback implements Callback, Callbacks,
private View mPendingRemoteInputView;
private KeyguardManager mKeyguardManager;
private final CommandQueue mCommandQueue;
+ private final ActionClickLogger mActionClickLogger;
private int mDisabled2;
protected BroadcastReceiver mChallengeReceiver = new ChallengeReceiver();
private Handler mMainHandler = new Handler();
@@ -87,7 +89,8 @@ public class StatusBarRemoteInputCallback implements Callback, Callbacks,
StatusBarStateController statusBarStateController,
StatusBarKeyguardViewManager statusBarKeyguardViewManager,
ActivityStarter activityStarter, ShadeController shadeController,
- CommandQueue commandQueue) {
+ CommandQueue commandQueue,
+ ActionClickLogger clickLogger) {
mContext = context;
mStatusBarKeyguardViewManager = statusBarKeyguardViewManager;
mShadeController = shadeController;
@@ -101,6 +104,7 @@ public class StatusBarRemoteInputCallback implements Callback, Callbacks,
mKeyguardManager = context.getSystemService(KeyguardManager.class);
mCommandQueue = commandQueue;
mCommandQueue.addCallback(this);
+ mActionClickLogger = clickLogger;
mActivityIntentHelper = new ActivityIntentHelper(mContext);
mGroupManager = groupManager;
// Listen to onKeyguardShowingChanged in case a managed profile needs to be unlocked
@@ -304,9 +308,12 @@ public class StatusBarRemoteInputCallback implements Callback, Callbacks,
NotificationRemoteInputManager.ClickHandler defaultHandler) {
final boolean isActivity = pendingIntent.isActivity();
if (isActivity) {
+ mActionClickLogger.logWaitingToCloseKeyguard(pendingIntent);
final boolean afterKeyguardGone = mActivityIntentHelper.wouldLaunchResolverActivity(
pendingIntent.getIntent(), mLockscreenUserManager.getCurrentUserId());
mActivityStarter.dismissKeyguardThenExecute(() -> {
+ mActionClickLogger.logKeyguardGone(pendingIntent);
+
try {
ActivityManager.getService().resumeAppSwitches();
} catch (RemoteException e) {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleControllerTest.java
index 52923abae07c..96e868d2a618 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleControllerTest.java
@@ -317,7 +317,7 @@ public class BubbleControllerTest extends SysuiTestCase {
verify(mNotificationEntryManager).updateNotifications(any());
mBubbleController.removeBubble(
- mRow.getEntry().getKey(), BubbleController.DISMISS_USER_GESTURE);
+ mRow.getEntry(), BubbleController.DISMISS_USER_GESTURE);
assertNull(mBubbleData.getBubbleInStackWithKey(mRow.getEntry().getKey()));
verify(mNotificationEntryManager, times(2)).updateNotifications(anyString());
@@ -329,7 +329,7 @@ public class BubbleControllerTest extends SysuiTestCase {
mBubbleController.updateBubble(mRow2.getEntry());
mBubbleController.updateBubble(mRow.getEntry());
mBubbleController.removeBubble(
- mRow.getEntry().getKey(), BubbleController.DISMISS_USER_GESTURE);
+ mRow.getEntry(), BubbleController.DISMISS_USER_GESTURE);
Bubble b = mBubbleData.getOverflowBubbleWithKey(mRow.getEntry().getKey());
assertThat(mBubbleData.getOverflowBubbles()).isEqualTo(ImmutableList.of(b));
@@ -350,10 +350,9 @@ public class BubbleControllerTest extends SysuiTestCase {
mBubbleController.updateBubble(mRow.getEntry(), /* suppressFlyout */
false, /* showInShade */ true);
mBubbleController.removeBubble(
- mRow.getEntry().getKey(), BubbleController.DISMISS_USER_GESTURE);
+ mRow.getEntry(), BubbleController.DISMISS_USER_GESTURE);
- mBubbleController.removeBubble(
- mRow.getEntry().getKey(), BubbleController.DISMISS_NOTIF_CANCEL);
+ mBubbleController.removeBubble(mRow.getEntry(), BubbleController.DISMISS_NOTIF_CANCEL);
verify(mNotificationEntryManager, times(1)).performRemoveNotification(
eq(mRow.getEntry().getSbn()), anyInt());
assertThat(mBubbleData.getOverflowBubbles()).isEmpty();
@@ -366,7 +365,7 @@ public class BubbleControllerTest extends SysuiTestCase {
assertTrue(mBubbleController.hasBubbles());
mBubbleController.removeBubble(
- mRow.getEntry().getKey(), BubbleController.DISMISS_USER_CHANGED);
+ mRow.getEntry(), BubbleController.DISMISS_USER_CHANGED);
verify(mNotificationEntryManager, never()).performRemoveNotification(
eq(mRow.getEntry().getSbn()), anyInt());
assertFalse(mBubbleController.hasBubbles());
@@ -564,8 +563,7 @@ public class BubbleControllerTest extends SysuiTestCase {
// Dismiss currently expanded
mBubbleController.removeBubble(
- mBubbleData.getBubbleInStackWithKey(stackView.getExpandedBubble().getKey())
- .getEntry().getKey(),
+ mBubbleData.getBubbleInStackWithKey(stackView.getExpandedBubble().getKey()).getEntry(),
BubbleController.DISMISS_USER_GESTURE);
verify(mBubbleExpandListener).onBubbleExpandChanged(false, mRow2.getEntry().getKey());
@@ -576,8 +574,7 @@ public class BubbleControllerTest extends SysuiTestCase {
// Dismiss that one
mBubbleController.removeBubble(
- mBubbleData.getBubbleInStackWithKey(stackView.getExpandedBubble().getKey())
- .getEntry().getKey(),
+ mBubbleData.getBubbleInStackWithKey(stackView.getExpandedBubble().getKey()).getEntry(),
BubbleController.DISMISS_USER_GESTURE);
// Make sure state changes and collapse happens
@@ -703,7 +700,7 @@ public class BubbleControllerTest extends SysuiTestCase {
@Test
public void testDeleteIntent_removeBubble_aged() throws PendingIntent.CanceledException {
mBubbleController.updateBubble(mRow.getEntry());
- mBubbleController.removeBubble(mRow.getEntry().getKey(), BubbleController.DISMISS_AGED);
+ mBubbleController.removeBubble(mRow.getEntry(), BubbleController.DISMISS_AGED);
verify(mDeleteIntent, never()).send();
}
@@ -711,7 +708,7 @@ public class BubbleControllerTest extends SysuiTestCase {
public void testDeleteIntent_removeBubble_user() throws PendingIntent.CanceledException {
mBubbleController.updateBubble(mRow.getEntry());
mBubbleController.removeBubble(
- mRow.getEntry().getKey(), BubbleController.DISMISS_USER_GESTURE);
+ mRow.getEntry(), BubbleController.DISMISS_USER_GESTURE);
verify(mDeleteIntent, times(1)).send();
}
@@ -811,7 +808,7 @@ public class BubbleControllerTest extends SysuiTestCase {
// Dismiss the bubble into overflow.
mBubbleController.removeBubble(
- mRow.getEntry().getKey(), BubbleController.DISMISS_USER_GESTURE);
+ mRow.getEntry(), BubbleController.DISMISS_USER_GESTURE);
assertFalse(mBubbleController.hasBubbles());
boolean intercepted = mRemoveInterceptor.onNotificationRemoveRequested(
@@ -832,7 +829,7 @@ public class BubbleControllerTest extends SysuiTestCase {
mRow.getEntry()));
mBubbleController.removeBubble(
- mRow.getEntry().getKey(), BubbleController.DISMISS_NO_LONGER_BUBBLE);
+ mRow.getEntry(), BubbleController.DISMISS_NO_LONGER_BUBBLE);
assertFalse(mBubbleController.hasBubbles());
boolean intercepted = mRemoveInterceptor.onNotificationRemoveRequested(
@@ -854,12 +851,12 @@ public class BubbleControllerTest extends SysuiTestCase {
mBubbleData.setMaxOverflowBubbles(1);
mBubbleController.removeBubble(
- mRow.getEntry().getKey(), BubbleController.DISMISS_USER_GESTURE);
+ mRow.getEntry(), BubbleController.DISMISS_USER_GESTURE);
assertEquals(mBubbleData.getBubbles().size(), 2);
assertEquals(mBubbleData.getOverflowBubbles().size(), 1);
mBubbleController.removeBubble(
- mRow2.getEntry().getKey(), BubbleController.DISMISS_USER_GESTURE);
+ mRow2.getEntry(), BubbleController.DISMISS_USER_GESTURE);
// Overflow max of 1 is reached; mRow is oldest, so it gets removed
verify(mNotificationEntryManager, times(1)).performRemoveNotification(
mRow.getEntry().getSbn(), REASON_CANCEL);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleDataTest.java b/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleDataTest.java
index 882504bdb5e4..66f119a082a6 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleDataTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleDataTest.java
@@ -20,8 +20,11 @@ import static com.android.systemui.statusbar.NotificationEntryHelper.modifyRanki
import static com.google.common.truth.Truth.assertThat;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.reset;
+import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyZeroInteractions;
import static org.mockito.Mockito.when;
@@ -177,8 +180,7 @@ public class BubbleDataTest extends SysuiTestCase {
mBubbleData.setListener(mListener);
// Test
- mBubbleData.notificationEntryRemoved(
- mEntryA1.getKey(), BubbleController.DISMISS_USER_GESTURE);
+ mBubbleData.notificationEntryRemoved(mEntryA1, BubbleController.DISMISS_USER_GESTURE);
// Verify
verifyUpdateReceived();
@@ -295,14 +297,12 @@ public class BubbleDataTest extends SysuiTestCase {
mBubbleData.setListener(mListener);
mBubbleData.setMaxOverflowBubbles(1);
- mBubbleData.notificationEntryRemoved(
- mEntryA1.getKey(), BubbleController.DISMISS_USER_GESTURE);
+ mBubbleData.notificationEntryRemoved(mEntryA1, BubbleController.DISMISS_USER_GESTURE);
verifyUpdateReceived();
assertOverflowChangedTo(ImmutableList.of(mBubbleA1));
// Overflow max of 1 is reached; A1 is oldest, so it gets removed
- mBubbleData.notificationEntryRemoved(
- mEntryA2.getKey(), BubbleController.DISMISS_USER_GESTURE);
+ mBubbleData.notificationEntryRemoved(mEntryA2, BubbleController.DISMISS_USER_GESTURE);
verifyUpdateReceived();
assertOverflowChangedTo(ImmutableList.of(mBubbleA2));
}
@@ -449,8 +449,7 @@ public class BubbleDataTest extends SysuiTestCase {
mBubbleData.setListener(mListener);
// Test
- mBubbleData.notificationEntryRemoved(
- mEntryA2.getKey(), BubbleController.DISMISS_USER_GESTURE);
+ mBubbleData.notificationEntryRemoved(mEntryA2, BubbleController.DISMISS_USER_GESTURE);
verifyUpdateReceived();
assertOrderChangedTo(mBubbleB2, mBubbleB1, mBubbleA1);
}
@@ -470,8 +469,7 @@ public class BubbleDataTest extends SysuiTestCase {
mBubbleData.setListener(mListener);
// Test
- mBubbleData.notificationEntryRemoved(
- mEntryB1.getKey(), BubbleController.DISMISS_USER_GESTURE);
+ mBubbleData.notificationEntryRemoved(mEntryB1, BubbleController.DISMISS_USER_GESTURE);
verifyUpdateReceived();
assertOrderNotChanged();
}
@@ -491,8 +489,7 @@ public class BubbleDataTest extends SysuiTestCase {
mBubbleData.setListener(mListener);
// Test
- mBubbleData.notificationEntryRemoved(
- mEntryA1.getKey(), BubbleController.DISMISS_NOTIF_CANCEL);
+ mBubbleData.notificationEntryRemoved(mEntryA1, BubbleController.DISMISS_NOTIF_CANCEL);
verifyUpdateReceived();
assertOrderChangedTo(mBubbleB2, mBubbleB1, mBubbleA2);
}
@@ -513,14 +510,12 @@ public class BubbleDataTest extends SysuiTestCase {
mBubbleData.setListener(mListener);
// Test
- mBubbleData.notificationEntryRemoved(
- mEntryA1.getKey(), BubbleController.DISMISS_NOTIF_CANCEL);
+ mBubbleData.notificationEntryRemoved(mEntryA1, BubbleController.DISMISS_NOTIF_CANCEL);
verifyUpdateReceived();
assertOverflowChangedTo(ImmutableList.of(mBubbleA2));
// Test
- mBubbleData.notificationEntryRemoved(
- mEntryA2.getKey(), BubbleController.DISMISS_GROUP_CANCELLED);
+ mBubbleData.notificationEntryRemoved(mEntryA2, BubbleController.DISMISS_GROUP_CANCELLED);
verifyUpdateReceived();
assertOverflowChangedTo(ImmutableList.of());
}
@@ -539,8 +534,7 @@ public class BubbleDataTest extends SysuiTestCase {
mBubbleData.setListener(mListener);
// Test
- mBubbleData.notificationEntryRemoved(
- mEntryA2.getKey(), BubbleController.DISMISS_NOTIF_CANCEL);
+ mBubbleData.notificationEntryRemoved(mEntryA2, BubbleController.DISMISS_NOTIF_CANCEL);
verifyUpdateReceived();
assertSelectionChangedTo(mBubbleB2);
}
@@ -631,8 +625,7 @@ public class BubbleDataTest extends SysuiTestCase {
mBubbleData.setListener(mListener);
// Test
- mBubbleData.notificationEntryRemoved(
- mEntryA1.getKey(), BubbleController.DISMISS_USER_GESTURE);
+ mBubbleData.notificationEntryRemoved(mEntryA1, BubbleController.DISMISS_USER_GESTURE);
// Verify the selection was cleared.
verifyUpdateReceived();
@@ -786,8 +779,7 @@ public class BubbleDataTest extends SysuiTestCase {
mBubbleData.setListener(mListener);
// Test
- mBubbleData.notificationEntryRemoved(
- mEntryB2.getKey(), BubbleController.DISMISS_USER_GESTURE);
+ mBubbleData.notificationEntryRemoved(mEntryB2, BubbleController.DISMISS_USER_GESTURE);
verifyUpdateReceived();
assertOrderChangedTo(mBubbleB1, mBubbleA2, mBubbleA1);
}
@@ -812,13 +804,11 @@ public class BubbleDataTest extends SysuiTestCase {
mBubbleData.setListener(mListener);
// Test
- mBubbleData.notificationEntryRemoved(
- mEntryA2.getKey(), BubbleController.DISMISS_USER_GESTURE);
+ mBubbleData.notificationEntryRemoved(mEntryA2, BubbleController.DISMISS_USER_GESTURE);
verifyUpdateReceived();
assertSelectionChangedTo(mBubbleA1);
- mBubbleData.notificationEntryRemoved(
- mEntryA1.getKey(), BubbleController.DISMISS_USER_GESTURE);
+ mBubbleData.notificationEntryRemoved(mEntryA1, BubbleController.DISMISS_USER_GESTURE);
verifyUpdateReceived();
assertSelectionChangedTo(mBubbleB1);
}
@@ -933,8 +923,7 @@ public class BubbleDataTest extends SysuiTestCase {
mBubbleData.setListener(mListener);
// Test
- mBubbleData.notificationEntryRemoved(
- mEntryA1.getKey(), BubbleController.DISMISS_USER_GESTURE);
+ mBubbleData.notificationEntryRemoved(mEntryA1, BubbleController.DISMISS_USER_GESTURE);
verifyUpdateReceived();
assertExpandedChangedTo(false);
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/bubbles/NewNotifPipelineBubbleControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/bubbles/NewNotifPipelineBubbleControllerTest.java
index c16801cdf8ad..73b876019863 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/bubbles/NewNotifPipelineBubbleControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/bubbles/NewNotifPipelineBubbleControllerTest.java
@@ -285,8 +285,7 @@ public class NewNotifPipelineBubbleControllerTest extends SysuiTestCase {
assertTrue(mBubbleController.hasBubbles());
verify(mNotifCallback, times(1)).invalidateNotifications(anyString());
- mBubbleController.removeBubble(
- mRow.getEntry().getKey(), BubbleController.DISMISS_USER_GESTURE);
+ mBubbleController.removeBubble(mRow.getEntry(), BubbleController.DISMISS_USER_GESTURE);
assertNull(mBubbleData.getBubbleInStackWithKey(mRow.getEntry().getKey()));
verify(mNotifCallback, times(2)).invalidateNotifications(anyString());
}
@@ -303,8 +302,7 @@ public class NewNotifPipelineBubbleControllerTest extends SysuiTestCase {
mBubbleData.getBubbleInStackWithKey(mRow.getEntry().getKey()).setSuppressNotification(true);
// Now remove the bubble
- mBubbleController.removeBubble(
- mRow.getEntry().getKey(), BubbleController.DISMISS_USER_GESTURE);
+ mBubbleController.removeBubble(mRow.getEntry(), BubbleController.DISMISS_USER_GESTURE);
assertTrue(mBubbleData.hasOverflowBubbleWithKey(mRow.getEntry().getKey()));
// We don't remove the notification since the bubble is still in overflow.
@@ -324,8 +322,7 @@ public class NewNotifPipelineBubbleControllerTest extends SysuiTestCase {
mBubbleData.getBubbleInStackWithKey(mRow.getEntry().getKey()).setSuppressNotification(true);
// Now remove the bubble
- mBubbleController.removeBubble(
- mRow.getEntry().getKey(), BubbleController.DISMISS_NOTIF_CANCEL);
+ mBubbleController.removeBubble(mRow.getEntry(), BubbleController.DISMISS_NOTIF_CANCEL);
assertFalse(mBubbleData.hasOverflowBubbleWithKey(mRow.getEntry().getKey()));
// Since the notif is dismissed and not in overflow, once the bubble is removed,
@@ -505,8 +502,7 @@ public class NewNotifPipelineBubbleControllerTest extends SysuiTestCase {
// Dismiss currently expanded
mBubbleController.removeBubble(
- mBubbleData.getBubbleInStackWithKey(
- stackView.getExpandedBubble().getKey()).getEntry().getKey(),
+ mBubbleData.getBubbleInStackWithKey(stackView.getExpandedBubble().getKey()).getEntry(),
BubbleController.DISMISS_USER_GESTURE);
verify(mBubbleExpandListener).onBubbleExpandChanged(false, mRow2.getEntry().getKey());
@@ -517,8 +513,7 @@ public class NewNotifPipelineBubbleControllerTest extends SysuiTestCase {
// Dismiss that one
mBubbleController.removeBubble(
- mBubbleData.getBubbleInStackWithKey(
- stackView.getExpandedBubble().getKey()).getEntry().getKey(),
+ mBubbleData.getBubbleInStackWithKey(stackView.getExpandedBubble().getKey()).getEntry(),
BubbleController.DISMISS_USER_GESTURE);
// Make sure state changes and collapse happens
@@ -618,7 +613,7 @@ public class NewNotifPipelineBubbleControllerTest extends SysuiTestCase {
@Test
public void testDeleteIntent_removeBubble_aged() throws PendingIntent.CanceledException {
mBubbleController.updateBubble(mRow.getEntry());
- mBubbleController.removeBubble(mRow.getEntry().getKey(), BubbleController.DISMISS_AGED);
+ mBubbleController.removeBubble(mRow.getEntry(), BubbleController.DISMISS_AGED);
verify(mDeleteIntent, never()).send();
}
@@ -626,7 +621,7 @@ public class NewNotifPipelineBubbleControllerTest extends SysuiTestCase {
public void testDeleteIntent_removeBubble_user() throws PendingIntent.CanceledException {
mBubbleController.updateBubble(mRow.getEntry());
mBubbleController.removeBubble(
- mRow.getEntry().getKey(), BubbleController.DISMISS_USER_GESTURE);
+ mRow.getEntry(), BubbleController.DISMISS_USER_GESTURE);
verify(mDeleteIntent, times(1)).send();
}
@@ -691,7 +686,7 @@ public class NewNotifPipelineBubbleControllerTest extends SysuiTestCase {
// Dismiss the bubble
mBubbleController.removeBubble(
- mRow.getEntry().getKey(), BubbleController.DISMISS_USER_GESTURE);
+ mRow.getEntry(), BubbleController.DISMISS_USER_GESTURE);
assertFalse(mBubbleController.hasBubbles());
// Dismiss the notification
@@ -712,7 +707,7 @@ public class NewNotifPipelineBubbleControllerTest extends SysuiTestCase {
// Dismiss the bubble
mBubbleController.removeBubble(
- mRow.getEntry().getKey(), BubbleController.DISMISS_NOTIF_CANCEL);
+ mRow.getEntry(), BubbleController.DISMISS_NOTIF_CANCEL);
assertFalse(mBubbleController.hasBubbles());
// Dismiss the notification
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationRemoteInputManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationRemoteInputManagerTest.java
index 1117646c482b..5a7dea4de29c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationRemoteInputManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationRemoteInputManagerTest.java
@@ -82,7 +82,8 @@ public class NotificationRemoteInputManagerTest extends SysuiTestCase {
() -> mock(StatusBar.class),
mStateController,
Handler.createAsync(Looper.myLooper()),
- mRemoteInputUriController);
+ mRemoteInputUriController,
+ mock(ActionClickLogger.class));
mEntry = new NotificationEntryBuilder()
.setPkg(TEST_PACKAGE_NAME)
.setOpPkg(TEST_PACKAGE_NAME)
@@ -256,17 +257,26 @@ public class NotificationRemoteInputManagerTest extends SysuiTestCase {
private class TestableNotificationRemoteInputManager extends NotificationRemoteInputManager {
- TestableNotificationRemoteInputManager(Context context,
+ TestableNotificationRemoteInputManager(
+ Context context,
NotificationLockscreenUserManager lockscreenUserManager,
SmartReplyController smartReplyController,
NotificationEntryManager notificationEntryManager,
Lazy<StatusBar> statusBarLazy,
StatusBarStateController statusBarStateController,
Handler mainHandler,
- RemoteInputUriController remoteInputUriController) {
- super(context, lockscreenUserManager, smartReplyController, notificationEntryManager,
- statusBarLazy, statusBarStateController, mainHandler,
- remoteInputUriController);
+ RemoteInputUriController remoteInputUriController,
+ ActionClickLogger actionClickLogger) {
+ super(
+ context,
+ lockscreenUserManager,
+ smartReplyController,
+ notificationEntryManager,
+ statusBarLazy,
+ statusBarStateController,
+ mainHandler,
+ remoteInputUriController,
+ actionClickLogger);
}
public void setUpWithPresenterForTest(Callback callback,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/SmartReplyControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/SmartReplyControllerTest.java
index 22dc0803339c..79507e96ae02 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/SmartReplyControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/SmartReplyControllerTest.java
@@ -92,7 +92,8 @@ public class SmartReplyControllerTest extends SysuiTestCase {
mNotificationEntryManager, () -> mock(StatusBar.class),
mStatusBarStateController,
Handler.createAsync(Looper.myLooper()),
- mRemoteInputUriController);
+ mRemoteInputUriController,
+ mock(ActionClickLogger.class));
mRemoteInputManager.setUpWithCallback(mCallback, mDelegate);
mNotification = new Notification.Builder(mContext, "")
.setSmallIcon(R.drawable.ic_person)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarRemoteInputCallbackTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarRemoteInputCallbackTest.java
index cd2c3498ce56..bf2bd38638ff 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarRemoteInputCallbackTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarRemoteInputCallbackTest.java
@@ -31,6 +31,7 @@ import androidx.test.filters.SmallTest;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.plugins.ActivityStarter;
+import com.android.systemui.statusbar.ActionClickLogger;
import com.android.systemui.statusbar.CommandQueue;
import com.android.systemui.statusbar.NotificationLockscreenUserManager;
import com.android.systemui.statusbar.SysuiStatusBarStateController;
@@ -73,7 +74,8 @@ public class StatusBarRemoteInputCallbackTest extends SysuiTestCase {
mRemoteInputCallback = spy(new StatusBarRemoteInputCallback(mContext,
mock(NotificationGroupManager.class), mNotificationLockscreenUserManager,
mKeyguardStateController, mStatusBarStateController, mStatusBarKeyguardViewManager,
- mActivityStarter, mShadeController, new CommandQueue(mContext)));
+ mActivityStarter, mShadeController, new CommandQueue(mContext),
+ mock(ActionClickLogger.class)));
mRemoteInputCallback.mChallengeReceiver = mRemoteInputCallback.new ChallengeReceiver();
}
diff --git a/services/core/java/com/android/server/display/LocalDisplayAdapter.java b/services/core/java/com/android/server/display/LocalDisplayAdapter.java
index 4f5a02ad22fa..2a65b33461cf 100644
--- a/services/core/java/com/android/server/display/LocalDisplayAdapter.java
+++ b/services/core/java/com/android/server/display/LocalDisplayAdapter.java
@@ -201,7 +201,6 @@ final class LocalDisplayAdapter extends DisplayAdapter {
private SurfaceControl.DisplayConfig[] mDisplayConfigs;
private Spline mSystemBrightnessToNits;
private Spline mNitsToHalBrightness;
- private boolean mHalBrightnessSupport;
private DisplayDeviceConfig mDisplayDeviceConfig;
@@ -225,7 +224,6 @@ final class LocalDisplayAdapter extends DisplayAdapter {
}
mAllmSupported = SurfaceControl.getAutoLowLatencyModeSupport(displayToken);
mGameContentTypeSupported = SurfaceControl.getGameContentTypeSupport(displayToken);
- mHalBrightnessSupport = SurfaceControl.getDisplayBrightnessSupport(displayToken);
mDisplayDeviceConfig = null;
// Defer configuration file loading
BackgroundThread.getHandler().sendMessage(PooledLambda.obtainMessage(
@@ -717,11 +715,10 @@ final class LocalDisplayAdapter extends DisplayAdapter {
Trace.traceBegin(Trace.TRACE_TAG_POWER, "setDisplayBrightness("
+ "id=" + physicalDisplayId + ", brightness=" + brightness + ")");
try {
- // TODO: make it float
if (isHalBrightnessRangeSpecified()) {
brightness = displayBrightnessToHalBrightness(
- BrightnessSynchronizer.brightnessFloatToInt(getContext(),
- brightness));
+ BrightnessSynchronizer.brightnessFloatToIntRange(
+ getContext(), brightness));
}
if (mBacklight != null) {
mBacklight.setBrightness(brightness);
@@ -744,12 +741,13 @@ final class LocalDisplayAdapter extends DisplayAdapter {
* Hal brightness space if the HAL brightness space has been provided via
* a display device configuration file.
*/
- private float displayBrightnessToHalBrightness(int brightness) {
+ private float displayBrightnessToHalBrightness(float brightness) {
if (!isHalBrightnessRangeSpecified()) {
return PowerManager.BRIGHTNESS_INVALID_FLOAT;
}
- if (brightness == 0) {
+ if (BrightnessSynchronizer.floatEquals(
+ brightness, PowerManager.BRIGHTNESS_OFF)) {
return PowerManager.BRIGHTNESS_OFF_FLOAT;
}
diff --git a/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java b/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java
index 12309f407786..a859a42d8a8f 100644
--- a/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java
+++ b/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java
@@ -289,6 +289,7 @@ public class StatsPullAtomService extends SystemService {
private StatsPullAtomCallbackImpl mStatsCallbackImpl;
private int mAppOpsSamplingRate = 0;
+ private final ArraySet<Integer> mDangerousAppOpsList = new ArraySet<>();
// Baselines that stores list of NetworkStats right after initializing, with associated
// information. This is used to calculate difference when pulling
@@ -526,6 +527,25 @@ public class StatsPullAtomService extends SystemService {
} catch (RemoteException e) {
Slog.e(TAG, "failed to initialize healthHalWrapper");
}
+
+ // Initialize list of AppOps related to DangerousPermissions
+ PackageManager pm = mContext.getPackageManager();
+ for (int op = 0; op < AppOpsManager._NUM_OP; op++) {
+ String perm = AppOpsManager.opToPermission(op);
+ if (perm == null) {
+ continue;
+ } else {
+ PermissionInfo permInfo;
+ try {
+ permInfo = pm.getPermissionInfo(perm, 0);
+ if (permInfo.getProtection() == PROTECTION_DANGEROUS) {
+ mDangerousAppOpsList.add(op);
+ }
+ } catch (PackageManager.NameNotFoundException exception) {
+ continue;
+ }
+ }
+ }
}
void registerEventListeners() {
@@ -3113,22 +3133,8 @@ public class StatsPullAtomService extends SystemService {
e.writeLong(op.getBackgroundRejectCount(OP_FLAGS_PULLED));
e.writeLong(op.getForegroundAccessDuration(OP_FLAGS_PULLED));
e.writeLong(op.getBackgroundAccessDuration(OP_FLAGS_PULLED));
+ e.writeBoolean(mDangerousAppOpsList.contains(op.getOpCode()));
- String perm = AppOpsManager.opToPermission(op.getOpCode());
- if (perm == null) {
- e.writeBoolean(false);
- } else {
- PermissionInfo permInfo;
- try {
- permInfo = mContext.getPackageManager().getPermissionInfo(
- perm,
- 0);
- e.writeBoolean(
- permInfo.getProtection() == PROTECTION_DANGEROUS);
- } catch (PackageManager.NameNotFoundException exception) {
- e.writeBoolean(false);
- }
- }
if (atomTag == FrameworkStatsLog.ATTRIBUTED_APP_OPS) {
e.writeInt(mAppOpsSamplingRate);
}
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index 91c849c4110f..0598680f8c5d 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -6684,6 +6684,13 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
}
@Override
+ void getAnimationPosition(Point outPosition) {
+ // Always animate from zero because if the activity doesn't fill the task, the letterbox
+ // will fill the remaining area that should be included in the animation.
+ outPosition.set(0, 0);
+ }
+
+ @Override
public void onConfigurationChanged(Configuration newParentConfig) {
if (mCompatDisplayInsets != null) {
Configuration overrideConfig = getRequestedOverrideConfiguration();
diff --git a/services/core/java/com/android/server/wm/WindowContainer.java b/services/core/java/com/android/server/wm/WindowContainer.java
index 5d7ec127da46..7bfddd79f8a4 100644
--- a/services/core/java/com/android/server/wm/WindowContainer.java
+++ b/services/core/java/com/android/server/wm/WindowContainer.java
@@ -2120,6 +2120,11 @@ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer<
return getBounds();
}
+ /** Gets the position relative to parent for animation. */
+ void getAnimationPosition(Point outPosition) {
+ getRelativePosition(outPosition);
+ }
+
/**
* Applies the app transition animation according the given the layout properties in the
* window hierarchy.
@@ -2178,9 +2183,9 @@ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer<
// Separate position and size for use in animators.
mTmpRect.set(getAnimationBounds(appStackClipMode));
- if (sHierarchicalAnimations) {
- getRelativePosition(mTmpPoint);
- } else {
+ getAnimationPosition(mTmpPoint);
+ if (!sHierarchicalAnimations) {
+ // Non-hierarchical animation uses position in global coordinates.
mTmpPoint.set(mTmpRect.left, mTmpRect.top);
}
mTmpRect.offsetTo(0, 0);
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index 0815ee523555..18c25c142113 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -247,6 +247,7 @@ import android.stats.devicepolicy.DevicePolicyEnums;
import android.telephony.TelephonyManager;
import android.telephony.data.ApnSetting;
import android.text.TextUtils;
+import android.text.format.DateUtils;
import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.AtomicFile;
@@ -16005,31 +16006,27 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
private @PersonalAppsSuspensionReason int updatePersonalAppsSuspension(
int profileUserId, boolean unlocked) {
final boolean suspendedExplicitly;
- final int deadlineState;
- final String poPackage;
+ final boolean suspendedByTimeout;
synchronized (getLockObject()) {
final ActiveAdmin profileOwner = getProfileOwnerAdminLocked(profileUserId);
if (profileOwner != null) {
- deadlineState =
+ final int deadlineState =
updateProfileOffDeadlineLocked(profileUserId, profileOwner, unlocked);
suspendedExplicitly = profileOwner.mSuspendPersonalApps;
- poPackage = profileOwner.info.getPackageName();
+ suspendedByTimeout = deadlineState == PROFILE_OFF_DEADLINE_REACHED;
+ Slog.d(LOG_TAG, String.format(
+ "Personal apps suspended explicitly: %b, deadline state: %d",
+ suspendedExplicitly, deadlineState));
+ final int notificationState =
+ unlocked ? PROFILE_OFF_DEADLINE_DEFAULT : deadlineState;
+ updateProfileOffDeadlineNotificationLocked(
+ profileUserId, profileOwner, notificationState);
} else {
- poPackage = null;
suspendedExplicitly = false;
- deadlineState = PROFILE_OFF_DEADLINE_DEFAULT;
+ suspendedByTimeout = false;
}
}
- Slog.d(LOG_TAG, String.format("Personal apps suspended explicitly: %b, deadline state: %d",
- suspendedExplicitly, deadlineState));
-
- if (poPackage != null) {
- final int notificationState = unlocked ? PROFILE_OFF_DEADLINE_DEFAULT : deadlineState;
- updateProfileOffDeadlineNotification(profileUserId, poPackage, notificationState);
- }
-
- final boolean suspendedByTimeout = deadlineState == PROFILE_OFF_DEADLINE_REACHED;
final int parentUserId = getProfileParentId(profileUserId);
suspendPersonalAppsInternal(parentUserId, suspendedExplicitly || suspendedByTimeout);
@@ -16141,9 +16138,9 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
}
}
- private void updateProfileOffDeadlineNotification(
- int profileUserId, String profileOwnerPackage, int notificationState) {
-
+ @GuardedBy("getLockObject()")
+ private void updateProfileOffDeadlineNotificationLocked(
+ int profileUserId, ActiveAdmin profileOwner, int notificationState) {
if (notificationState == PROFILE_OFF_DEADLINE_DEFAULT) {
mInjector.getNotificationManager().cancel(SystemMessage.NOTE_PERSONAL_APPS_SUSPENDED);
return;
@@ -16161,11 +16158,23 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
final Notification.Action turnProfileOnButton =
new Notification.Action.Builder(null /* icon */, buttonText, pendingIntent).build();
- final String text = mContext.getString(
- notificationState == PROFILE_OFF_DEADLINE_WARNING
- ? R.string.personal_apps_suspension_tomorrow_text
- : R.string.personal_apps_suspension_text);
- final boolean ongoing = notificationState == PROFILE_OFF_DEADLINE_REACHED;
+ final String text;
+ final boolean ongoing;
+ if (notificationState == PROFILE_OFF_DEADLINE_WARNING) {
+ // Round to the closest integer number of days.
+ final int maxDays = (int)
+ ((profileOwner.mProfileMaximumTimeOffMillis + MS_PER_DAY / 2) / MS_PER_DAY);
+ final String date = DateUtils.formatDateTime(
+ mContext, profileOwner.mProfileOffDeadline, DateUtils.FORMAT_SHOW_DATE);
+ final String time = DateUtils.formatDateTime(
+ mContext, profileOwner.mProfileOffDeadline, DateUtils.FORMAT_SHOW_TIME);
+ text = mContext.getString(
+ R.string.personal_apps_suspension_soon_text, date, time, maxDays);
+ ongoing = false;
+ } else {
+ text = mContext.getString(R.string.personal_apps_suspension_text);
+ ongoing = true;
+ }
final int color = mContext.getColor(R.color.personal_apps_suspension_notification_color);
final Bundle extras = new Bundle();
// TODO: Create a separate string for this.
diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
index 17dd2868f4bc..724048b1b8ee 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
@@ -204,7 +204,7 @@ public class DevicePolicyManagerTest extends DpmTestBase {
// Notification title and text for setManagedProfileMaximumTimeOff tests:
private static final String PROFILE_OFF_SUSPENSION_TITLE = "suspension_title";
private static final String PROFILE_OFF_SUSPENSION_TEXT = "suspension_text";
- private static final String PROFILE_OFF_SUSPENSION_TOMORROW_TEXT = "suspension_tomorrow_text";
+ private static final String PROFILE_OFF_SUSPENSION_SOON_TEXT = "suspension_tomorrow_text";
@Override
protected void setUp() throws Exception {
@@ -6331,7 +6331,7 @@ public class DevicePolicyManagerTest extends DpmTestBase {
// Now the user should see a warning notification.
verify(getServices().notificationManager, times(1))
.notify(anyInt(), argThat(hasExtra(EXTRA_TITLE, PROFILE_OFF_SUSPENSION_TITLE,
- EXTRA_TEXT, PROFILE_OFF_SUSPENSION_TOMORROW_TEXT)));
+ EXTRA_TEXT, PROFILE_OFF_SUSPENSION_SOON_TEXT)));
// Apps shouldn't be suspended yet.
verifyZeroInteractions(getServices().ipackageManager);
clearInvocations(getServices().alarmManager);
@@ -6482,7 +6482,7 @@ public class DevicePolicyManagerTest extends DpmTestBase {
// To allow creation of Notification via Notification.Builder
mContext.applicationInfo = mRealTestContext.getApplicationInfo();
- // Setup notification titles.
+ // Setup resources to render notification titles and texts.
when(mServiceContext.resources
.getString(R.string.personal_apps_suspension_title))
.thenReturn(PROFILE_OFF_SUSPENSION_TITLE);
@@ -6490,14 +6490,19 @@ public class DevicePolicyManagerTest extends DpmTestBase {
.getString(R.string.personal_apps_suspension_text))
.thenReturn(PROFILE_OFF_SUSPENSION_TEXT);
when(mServiceContext.resources
- .getString(R.string.personal_apps_suspension_tomorrow_text))
- .thenReturn(PROFILE_OFF_SUSPENSION_TOMORROW_TEXT);
+ .getString(eq(R.string.personal_apps_suspension_soon_text),
+ anyString(), anyString(), anyInt()))
+ .thenReturn(PROFILE_OFF_SUSPENSION_SOON_TEXT);
+
+ // Make locale available for date formatting:
+ when(mServiceContext.resources.getConfiguration())
+ .thenReturn(mRealTestContext.getResources().getConfiguration());
clearInvocations(getServices().ipackageManager);
}
private static Matcher<Notification> hasExtra(String... extras) {
- assertEquals("Odd numebr of extra key-values", 0, extras.length % 2);
+ assertEquals("Odd number of extra key-values", 0, extras.length % 2);
return new BaseMatcher<Notification>() {
@Override
public boolean matches(Object item) {
diff --git a/services/tests/wmtests/src/com/android/server/wm/AppWindowTokenTests.java b/services/tests/wmtests/src/com/android/server/wm/AppWindowTokenTests.java
index 8f18f648f8f0..07050d9666d5 100644
--- a/services/tests/wmtests/src/com/android/server/wm/AppWindowTokenTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/AppWindowTokenTests.java
@@ -52,6 +52,7 @@ import static org.mockito.Mockito.reset;
import static org.mockito.Mockito.verify;
import android.content.res.Configuration;
+import android.graphics.Point;
import android.graphics.Rect;
import android.platform.test.annotations.Presubmit;
import android.view.IWindowManager;
@@ -432,20 +433,35 @@ public class AppWindowTokenTests extends WindowTestsBase {
removeGlobalMinSizeRestriction();
final Rect stackBounds = new Rect(0, 0, 1000, 600);
final Rect taskBounds = new Rect(100, 400, 600, 800);
- mStack.setBounds(stackBounds);
- mTask.setBounds(taskBounds);
+ // Set the bounds and windowing mode to window configuration directly, otherwise the
+ // testing setups may be discarded by configuration resolving.
+ mStack.getWindowConfiguration().setBounds(stackBounds);
+ mTask.getWindowConfiguration().setBounds(taskBounds);
+ mActivity.getWindowConfiguration().setBounds(taskBounds);
// Check that anim bounds for freeform window match task bounds
- mTask.setWindowingMode(WINDOWING_MODE_FREEFORM);
+ mTask.getWindowConfiguration().setWindowingMode(WINDOWING_MODE_FREEFORM);
assertEquals(mTask.getBounds(), mActivity.getAnimationBounds(STACK_CLIP_NONE));
// STACK_CLIP_AFTER_ANIM should use task bounds since they will be clipped by
// bounds animation layer.
- mTask.setWindowingMode(WINDOWING_MODE_FULLSCREEN);
+ mTask.getWindowConfiguration().setWindowingMode(WINDOWING_MODE_FULLSCREEN);
assertEquals(mTask.getBounds(), mActivity.getAnimationBounds(STACK_CLIP_AFTER_ANIM));
+ // Even the activity is smaller than task and it is not aligned to the top-left corner of
+ // task, the animation bounds the same as task and position should be zero because in real
+ // case the letterbox will fill the remaining area in task.
+ final Rect halfBounds = new Rect(taskBounds);
+ halfBounds.scale(0.5f);
+ mActivity.getWindowConfiguration().setBounds(halfBounds);
+ final Point animationPosition = new Point();
+ mActivity.getAnimationPosition(animationPosition);
+
+ assertEquals(taskBounds, mActivity.getAnimationBounds(STACK_CLIP_AFTER_ANIM));
+ assertEquals(new Point(0, 0), animationPosition);
+
// STACK_CLIP_BEFORE_ANIM should use stack bounds since it won't be clipped later.
- mTask.setWindowingMode(WINDOWING_MODE_SPLIT_SCREEN_PRIMARY);
+ mTask.getWindowConfiguration().setWindowingMode(WINDOWING_MODE_SPLIT_SCREEN_PRIMARY);
assertEquals(mStack.getBounds(), mActivity.getAnimationBounds(STACK_CLIP_BEFORE_ANIM));
}