summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BadgedImageView.java4
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubble.java43
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java52
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleData.java70
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleExpandedView.java6
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleTaskViewHelper.java5
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubbles.java9
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/IBubbles.aidl5
8 files changed, 175 insertions, 19 deletions
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BadgedImageView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BadgedImageView.java
index f9a1d940c734..dc511be59764 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BadgedImageView.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BadgedImageView.java
@@ -357,7 +357,9 @@ public class BadgedImageView extends ConstraintLayout {
void showBadge() {
Bitmap appBadgeBitmap = mBubble.getAppBadge();
- if (appBadgeBitmap == null) {
+ final boolean isAppLaunchIntent = (mBubble instanceof Bubble)
+ && ((Bubble) mBubble).isAppLaunchIntent();
+ if (appBadgeBitmap == null || isAppLaunchIntent) {
mAppIcon.setVisibility(GONE);
return;
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubble.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubble.java
index 7dbbb04e4406..5cd2cb7d51d5 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubble.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubble.java
@@ -50,6 +50,7 @@ import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.logging.InstanceId;
import com.android.internal.protolog.ProtoLog;
import com.android.launcher3.icons.BubbleIconFactory;
+import com.android.wm.shell.Flags;
import com.android.wm.shell.bubbles.bar.BubbleBarExpandedView;
import com.android.wm.shell.bubbles.bar.BubbleBarLayerView;
import com.android.wm.shell.common.bubbles.BubbleInfo;
@@ -246,7 +247,23 @@ public class Bubble implements BubbleViewProvider {
mAppIntent = intent;
mDesiredHeight = Integer.MAX_VALUE;
mPackageName = intent.getPackage();
+ }
+ private Bubble(ShortcutInfo info, Executor mainExecutor) {
+ mGroupKey = null;
+ mLocusId = null;
+ mFlags = 0;
+ mUser = info.getUserHandle();
+ mIcon = info.getIcon();
+ mIsAppBubble = false;
+ mKey = getBubbleKeyForShortcut(info);
+ mShowBubbleUpdateDot = false;
+ mMainExecutor = mainExecutor;
+ mTaskId = INVALID_TASK_ID;
+ mAppIntent = null;
+ mDesiredHeight = Integer.MAX_VALUE;
+ mPackageName = info.getPackage();
+ mShortcutInfo = info;
}
/** Creates an app bubble. */
@@ -263,6 +280,13 @@ public class Bubble implements BubbleViewProvider {
mainExecutor);
}
+ /** Creates a shortcut bubble. */
+ public static Bubble createShortcutBubble(
+ ShortcutInfo info,
+ Executor mainExecutor) {
+ return new Bubble(info, mainExecutor);
+ }
+
/**
* Returns the key for an app bubble from an app with package name, {@code packageName} on an
* Android user, {@code user}.
@@ -273,6 +297,14 @@ public class Bubble implements BubbleViewProvider {
return KEY_APP_BUBBLE + ":" + user.getIdentifier() + ":" + packageName;
}
+ /**
+ * Returns the key for a shortcut bubble using {@code packageName}, {@code user}, and the
+ * {@code shortcutInfo} id.
+ */
+ public static String getBubbleKeyForShortcut(ShortcutInfo info) {
+ return info.getPackage() + ":" + info.getUserId() + ":" + info.getId();
+ }
+
@VisibleForTesting(visibility = PRIVATE)
public Bubble(@NonNull final BubbleEntry entry,
final Bubbles.BubbleMetadataFlagListener listener,
@@ -888,6 +920,17 @@ public class Bubble implements BubbleViewProvider {
return mIntent;
}
+ /**
+ * Whether this bubble represents the full app, i.e. the intent used is the launch
+ * intent for an app. In this case we don't show a badge on the icon.
+ */
+ public boolean isAppLaunchIntent() {
+ if (Flags.enableBubbleAnything() && mAppIntent != null) {
+ return mAppIntent.hasCategory("android.intent.category.LAUNCHER");
+ }
+ return false;
+ }
+
@Nullable
PendingIntent getDeleteIntent() {
return mDeleteIntent;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java
index 949a7236434a..29520efd70b0 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java
@@ -1335,6 +1335,40 @@ public class BubbleController implements ConfigurationChangeListener,
}
/**
+ * Expands and selects a bubble created or found via the provided shortcut info.
+ *
+ * @param info the shortcut info for the bubble.
+ */
+ public void expandStackAndSelectBubble(ShortcutInfo info) {
+ if (!Flags.enableBubbleAnything()) return;
+ Bubble b = mBubbleData.getOrCreateBubble(info); // Removes from overflow
+ ProtoLog.v(WM_SHELL_BUBBLES, "expandStackAndSelectBubble - shortcut=%s", info);
+ if (b.isInflated()) {
+ mBubbleData.setSelectedBubbleAndExpandStack(b);
+ } else {
+ b.enable(Notification.BubbleMetadata.FLAG_AUTO_EXPAND_BUBBLE);
+ inflateAndAdd(b, /* suppressFlyout= */ true, /* showInShade= */ false);
+ }
+ }
+
+ /**
+ * Expands and selects a bubble created or found for this app.
+ *
+ * @param intent the intent for the bubble.
+ */
+ public void expandStackAndSelectBubble(Intent intent) {
+ if (!Flags.enableBubbleAnything()) return;
+ Bubble b = mBubbleData.getOrCreateBubble(intent); // Removes from overflow
+ ProtoLog.v(WM_SHELL_BUBBLES, "expandStackAndSelectBubble - intent=%s", intent);
+ if (b.isInflated()) {
+ mBubbleData.setSelectedBubbleAndExpandStack(b);
+ } else {
+ b.enable(Notification.BubbleMetadata.FLAG_AUTO_EXPAND_BUBBLE);
+ inflateAndAdd(b, /* suppressFlyout= */ true, /* showInShade= */ false);
+ }
+ }
+
+ /**
* Expands and selects a bubble based on the provided {@link BubbleEntry}. If no bubble
* exists for this entry, and it is able to bubble, a new bubble will be created.
*
@@ -2323,6 +2357,7 @@ public class BubbleController implements ConfigurationChangeListener,
* @param entry the entry to bubble.
*/
static boolean canLaunchInTaskView(Context context, BubbleEntry entry) {
+ if (Flags.enableBubbleAnything()) return true;
PendingIntent intent = entry.getBubbleMetadata() != null
? entry.getBubbleMetadata().getIntent()
: null;
@@ -2439,6 +2474,16 @@ public class BubbleController implements ConfigurationChangeListener,
}
@Override
+ public void showShortcutBubble(ShortcutInfo info) {
+ mMainExecutor.execute(() -> mController.expandStackAndSelectBubble(info));
+ }
+
+ @Override
+ public void showAppBubble(Intent intent) {
+ mMainExecutor.execute(() -> mController.expandStackAndSelectBubble(intent));
+ }
+
+ @Override
public void showBubble(String key, int topOnScreen) {
mMainExecutor.execute(
() -> mController.expandStackAndSelectBubbleFromLauncher(key, topOnScreen));
@@ -2634,6 +2679,13 @@ public class BubbleController implements ConfigurationChangeListener,
}
@Override
+ public void expandStackAndSelectBubble(ShortcutInfo info) {
+ mMainExecutor.execute(() -> {
+ BubbleController.this.expandStackAndSelectBubble(info);
+ });
+ }
+
+ @Override
public void expandStackAndSelectBubble(Bubble bubble) {
mMainExecutor.execute(() -> {
BubbleController.this.expandStackAndSelectBubble(bubble);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleData.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleData.java
index b6da761b0f9c..3c6c6fa0d8d5 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleData.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleData.java
@@ -23,8 +23,10 @@ import static com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_BUBBLES;
import android.annotation.NonNull;
import android.app.PendingIntent;
import android.content.Context;
+import android.content.Intent;
import android.content.LocusId;
import android.content.pm.ShortcutInfo;
+import android.os.UserHandle;
import android.text.TextUtils;
import android.util.ArrayMap;
import android.util.ArraySet;
@@ -421,23 +423,19 @@ public class BubbleData {
Bubble bubbleToReturn = getBubbleInStackWithKey(key);
if (bubbleToReturn == null) {
- bubbleToReturn = getOverflowBubbleWithKey(key);
- if (bubbleToReturn != null) {
- // Promoting from overflow
- mOverflowBubbles.remove(bubbleToReturn);
- if (mOverflowBubbles.isEmpty()) {
- mStateChange.showOverflowChanged = true;
+ // Check if it's in the overflow
+ bubbleToReturn = findAndRemoveBubbleFromOverflow(key);
+ if (bubbleToReturn == null) {
+ if (entry != null) {
+ // Not in the overflow, have an entry, so it's a new bubble
+ bubbleToReturn = new Bubble(entry,
+ mBubbleMetadataFlagListener,
+ mCancelledListener,
+ mMainExecutor);
+ } else {
+ // If there's no entry it must be a persisted bubble
+ bubbleToReturn = persistedBubble;
}
- } else if (mPendingBubbles.containsKey(key)) {
- // Update while it was pending
- bubbleToReturn = mPendingBubbles.get(key);
- } else if (entry != null) {
- // New bubble
- bubbleToReturn = new Bubble(entry, mBubbleMetadataFlagListener, mCancelledListener,
- mMainExecutor);
- } else {
- // Persisted bubble being promoted
- bubbleToReturn = persistedBubble;
}
}
@@ -448,6 +446,46 @@ public class BubbleData {
return bubbleToReturn;
}
+ Bubble getOrCreateBubble(ShortcutInfo info) {
+ String bubbleKey = Bubble.getBubbleKeyForShortcut(info);
+ Bubble bubbleToReturn = findAndRemoveBubbleFromOverflow(bubbleKey);
+ if (bubbleToReturn == null) {
+ bubbleToReturn = Bubble.createShortcutBubble(info, mMainExecutor);
+ }
+ return bubbleToReturn;
+ }
+
+ Bubble getOrCreateBubble(Intent intent) {
+ UserHandle user = UserHandle.of(mCurrentUserId);
+ String bubbleKey = Bubble.getAppBubbleKeyForApp(intent.getPackage(),
+ user);
+ Bubble bubbleToReturn = findAndRemoveBubbleFromOverflow(bubbleKey);
+ if (bubbleToReturn == null) {
+ bubbleToReturn = Bubble.createAppBubble(intent, user, null, mMainExecutor);
+ }
+ return bubbleToReturn;
+ }
+
+ @Nullable
+ private Bubble findAndRemoveBubbleFromOverflow(String key) {
+ Bubble bubbleToReturn = getBubbleInStackWithKey(key);
+ if (bubbleToReturn != null) {
+ return bubbleToReturn;
+ }
+ bubbleToReturn = getOverflowBubbleWithKey(key);
+ if (bubbleToReturn != null) {
+ mOverflowBubbles.remove(bubbleToReturn);
+ // Promoting from overflow
+ mOverflowBubbles.remove(bubbleToReturn);
+ if (mOverflowBubbles.isEmpty()) {
+ mStateChange.showOverflowChanged = true;
+ }
+ } else if (mPendingBubbles.containsKey(key)) {
+ bubbleToReturn = mPendingBubbles.get(key);
+ }
+ return bubbleToReturn;
+ }
+
/**
* When this method is called it is expected that all info in the bubble has completed loading.
* @see Bubble#inflate(BubbleViewInfoTask.Callback, Context, BubbleExpandedViewManager,
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleExpandedView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleExpandedView.java
index fdb45239fa63..a0c0a25d97a2 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleExpandedView.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleExpandedView.java
@@ -232,6 +232,9 @@ public class BubbleExpandedView extends LinearLayout {
fillInIntent.addFlags(FLAG_ACTIVITY_NEW_DOCUMENT);
fillInIntent.addFlags(FLAG_ACTIVITY_MULTIPLE_TASK);
+ final boolean isShortcutBubble = (mBubble.hasMetadataShortcutId()
+ || (mBubble.getShortcutInfo() != null && Flags.enableBubbleAnything()));
+
if (mBubble.isAppBubble()) {
Context context =
mContext.createContextAsUser(
@@ -246,7 +249,8 @@ public class BubbleExpandedView extends LinearLayout {
/* options= */ null);
mTaskView.startActivity(pi, /* fillInIntent= */ null, options,
launchBounds);
- } else if (!mIsOverflow && mBubble.hasMetadataShortcutId()) {
+ } else if (!mIsOverflow && isShortcutBubble) {
+ ProtoLog.v(WM_SHELL_BUBBLES, "startingShortcutBubble=%s", getBubbleKey());
options.setApplyActivityFlagsForBubbles(true);
mTaskView.startShortcutActivity(mBubble.getShortcutInfo(),
options, launchBounds);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleTaskViewHelper.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleTaskViewHelper.java
index 5e2141aa639e..5f8f0fd0c54c 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleTaskViewHelper.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleTaskViewHelper.java
@@ -36,6 +36,7 @@ import android.view.ViewGroup;
import androidx.annotation.Nullable;
import com.android.internal.protolog.ProtoLog;
+import com.android.wm.shell.Flags;
import com.android.wm.shell.taskview.TaskView;
/**
@@ -110,6 +111,8 @@ public class BubbleTaskViewHelper {
fillInIntent.addFlags(FLAG_ACTIVITY_NEW_DOCUMENT);
fillInIntent.addFlags(FLAG_ACTIVITY_MULTIPLE_TASK);
+ final boolean isShortcutBubble = (mBubble.hasMetadataShortcutId()
+ || (mBubble.getShortcutInfo() != null && Flags.enableBubbleAnything()));
if (mBubble.isAppBubble()) {
Context context =
mContext.createContextAsUser(
@@ -124,7 +127,7 @@ public class BubbleTaskViewHelper {
/* options= */ null);
mTaskView.startActivity(pi, /* fillInIntent= */ null, options,
launchBounds);
- } else if (mBubble.hasMetadataShortcutId()) {
+ } else if (isShortcutBubble) {
options.setApplyActivityFlagsForBubbles(true);
mTaskView.startShortcutActivity(mBubble.getShortcutInfo(),
options, launchBounds);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubbles.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubbles.java
index 589dfd24624e..9a27fb65ac2c 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubbles.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubbles.java
@@ -23,6 +23,7 @@ import static java.lang.annotation.RetentionPolicy.SOURCE;
import android.app.NotificationChannel;
import android.content.Intent;
+import android.content.pm.ShortcutInfo;
import android.content.pm.UserInfo;
import android.graphics.drawable.Icon;
import android.hardware.HardwareBuffer;
@@ -118,6 +119,14 @@ public interface Bubbles {
/**
* Request the stack expand if needed, then select the specified Bubble as current.
+ * If no bubble exists for this entry, one is created.
+ *
+ * @param info the shortcut info to use to create the bubble.
+ */
+ void expandStackAndSelectBubble(ShortcutInfo info);
+
+ /**
+ * Request the stack expand if needed, then select the specified Bubble as current.
*
* @param bubble the bubble to be selected
*/
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/IBubbles.aidl b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/IBubbles.aidl
index 0907ddd1de83..5c789749412c 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/IBubbles.aidl
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/IBubbles.aidl
@@ -18,6 +18,7 @@ package com.android.wm.shell.bubbles;
import android.content.Intent;
import android.graphics.Rect;
+import android.content.pm.ShortcutInfo;
import com.android.wm.shell.bubbles.IBubblesListener;
import com.android.wm.shell.common.bubbles.BubbleBarLocation;
@@ -48,4 +49,8 @@ interface IBubbles {
oneway void updateBubbleBarTopOnScreen(in int topOnScreen) = 10;
oneway void stopBubbleDrag(in BubbleBarLocation location, in int topOnScreen) = 11;
+
+ oneway void showShortcutBubble(in ShortcutInfo info) = 12;
+
+ oneway void showAppBubble(in Intent intent) = 13;
} \ No newline at end of file