summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--packages/SystemUI/src/com/android/systemui/bubbles/BadgedImageView.java105
-rw-r--r--packages/SystemUI/src/com/android/systemui/bubbles/Bubble.java163
-rw-r--r--packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java52
-rw-r--r--packages/SystemUI/src/com/android/systemui/bubbles/BubbleData.java37
-rw-r--r--packages/SystemUI/src/com/android/systemui/bubbles/BubbleExpandedView.java80
-rw-r--r--packages/SystemUI/src/com/android/systemui/bubbles/BubbleIconFactory.java33
-rw-r--r--packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java14
-rw-r--r--packages/SystemUI/src/com/android/systemui/bubbles/BubbleViewInfoTask.java182
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleControllerTest.java15
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleDataTest.java41
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleTest.java2
11 files changed, 417 insertions, 307 deletions
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BadgedImageView.java b/packages/SystemUI/src/com/android/systemui/bubbles/BadgedImageView.java
index a6a3ce06324f..dc2499602125 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BadgedImageView.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BadgedImageView.java
@@ -16,22 +16,15 @@
package com.android.systemui.bubbles;
import android.annotation.Nullable;
-import android.app.Notification;
import android.content.Context;
-import android.content.pm.LauncherApps;
+import android.graphics.Bitmap;
import android.graphics.Canvas;
-import android.graphics.Color;
-import android.graphics.Matrix;
import android.graphics.Path;
import android.graphics.Rect;
-import android.graphics.drawable.Drawable;
-import android.graphics.drawable.Icon;
import android.util.AttributeSet;
import android.util.PathParser;
import android.widget.ImageView;
-import com.android.internal.graphics.ColorUtils;
-import com.android.launcher3.icons.BitmapInfo;
import com.android.launcher3.icons.DotRenderer;
import com.android.systemui.Interpolators;
import com.android.systemui.R;
@@ -46,9 +39,9 @@ import com.android.systemui.R;
public class BadgedImageView extends ImageView {
/** Same value as Launcher3 dot code */
- private static final float WHITE_SCRIM_ALPHA = 0.54f;
+ public static final float WHITE_SCRIM_ALPHA = 0.54f;
/** Same as value in Launcher3 IconShape */
- private static final int DEFAULT_PATH_SIZE = 100;
+ public static final int DEFAULT_PATH_SIZE = 100;
static final int DOT_STATE_DEFAULT = 0;
static final int DOT_STATE_SUPPRESSED_FOR_FLYOUT = 1;
@@ -58,7 +51,6 @@ public class BadgedImageView extends ImageView {
private int mCurrentDotState = DOT_STATE_SUPPRESSED_FOR_FLYOUT;
private Bubble mBubble;
- private BubbleIconFactory mBubbleIconFactory;
private int mIconBitmapSize;
private DotRenderer mDotRenderer;
@@ -94,6 +86,18 @@ public class BadgedImageView extends ImageView {
mDotRenderer = new DotRenderer(mIconBitmapSize, iconPath, DEFAULT_PATH_SIZE);
}
+ /**
+ * Updates the view with provided info.
+ */
+ public void update(Bubble bubble, Bitmap bubbleImage, int dotColor, Path dotPath) {
+ mBubble = bubble;
+ setImageBitmap(bubbleImage);
+ setDotState(DOT_STATE_SUPPRESSED_FOR_FLYOUT);
+ mDotColor = dotColor;
+ drawDot(dotPath);
+ animateDot();
+ }
+
@Override
public void onDraw(Canvas canvas) {
super.onDraw(canvas);
@@ -140,14 +144,6 @@ public class BadgedImageView extends ImageView {
}
/**
- * The colour to use for the dot.
- */
- void setDotColor(int color) {
- mDotColor = ColorUtils.setAlphaComponent(color, 255 /* alpha */);
- invalidate();
- }
-
- /**
* @param iconPath The new icon path to use when calculating dot position.
*/
void drawDot(Path iconPath) {
@@ -187,25 +183,6 @@ public class BadgedImageView extends ImageView {
}
/**
- * Populates this view with a bubble.
- * <p>
- * This should only be called when a new bubble is being set on the view, updates to the
- * current bubble should use {@link #update(Bubble)}.
- *
- * @param bubble the bubble to display in this view.
- */
- public void setBubble(Bubble bubble) {
- mBubble = bubble;
- }
-
- /**
- * @param factory Factory for creating normalized bubble icons.
- */
- public void setBubbleIconFactory(BubbleIconFactory factory) {
- mBubbleIconFactory = factory;
- }
-
- /**
* The key for the {@link Bubble} associated with this view, if one exists.
*/
@Nullable
@@ -213,15 +190,6 @@ public class BadgedImageView extends ImageView {
return (mBubble != null) ? mBubble.getKey() : null;
}
- /**
- * Updates the UI based on the bubble, updates badge and animates messages as needed.
- */
- public void update(Bubble bubble) {
- mBubble = bubble;
- setDotState(DOT_STATE_SUPPRESSED_FOR_FLYOUT);
- updateViews();
- }
-
int getDotColor() {
return mDotColor;
}
@@ -277,47 +245,4 @@ public class BadgedImageView extends ImageView {
}
}).start();
}
-
- void updateViews() {
- if (mBubble == null || mBubbleIconFactory == null) {
- return;
- }
-
- Drawable bubbleDrawable = getBubbleDrawable(mContext);
- BitmapInfo badgeBitmapInfo = mBubbleIconFactory.getBadgedBitmap(mBubble);
- BitmapInfo bubbleBitmapInfo = mBubbleIconFactory.getBubbleBitmap(bubbleDrawable,
- badgeBitmapInfo);
- setImageBitmap(bubbleBitmapInfo.icon);
-
- // Update badge.
- mDotColor = ColorUtils.blendARGB(badgeBitmapInfo.color, Color.WHITE, WHITE_SCRIM_ALPHA);
- setDotColor(mDotColor);
-
- // Update dot.
- Path iconPath = PathParser.createPathFromPathData(
- getResources().getString(com.android.internal.R.string.config_icon_mask));
- Matrix matrix = new Matrix();
- float scale = mBubbleIconFactory.getNormalizer().getScale(bubbleDrawable,
- null /* outBounds */, null /* path */, null /* outMaskShape */);
- float radius = BadgedImageView.DEFAULT_PATH_SIZE / 2f;
- matrix.setScale(scale /* x scale */, scale /* y scale */, radius /* pivot x */,
- radius /* pivot y */);
- iconPath.transform(matrix);
- drawDot(iconPath);
-
- animateDot();
- }
-
- Drawable getBubbleDrawable(Context context) {
- if (mBubble.getShortcutInfo() != null && mBubble.usingShortcutInfo()) {
- LauncherApps launcherApps =
- (LauncherApps) getContext().getSystemService(Context.LAUNCHER_APPS_SERVICE);
- int density = getContext().getResources().getConfiguration().densityDpi;
- return launcherApps.getShortcutIconDrawable(mBubble.getShortcutInfo(), density);
- } else {
- Notification.BubbleMetadata metadata = mBubble.getEntry().getBubbleMetadata();
- Icon ic = metadata.getIcon();
- return ic.loadDrawable(context);
- }
- }
}
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/Bubble.java b/packages/SystemUI/src/com/android/systemui/bubbles/Bubble.java
index 7934e10c8605..77c8e0b07158 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/Bubble.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/Bubble.java
@@ -16,6 +16,7 @@
package com.android.systemui.bubbles;
+import static android.os.AsyncTask.Status.FINISHED;
import static android.view.Display.INVALID_DISPLAY;
import static com.android.internal.annotations.VisibleForTesting.Visibility.PRIVATE;
@@ -26,20 +27,17 @@ 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;
import android.content.res.Resources;
import android.graphics.Rect;
-import android.graphics.drawable.Drawable;
import android.os.Bundle;
import android.os.Parcelable;
import android.os.UserHandle;
import android.provider.Settings;
import android.text.TextUtils;
import android.util.Log;
-import android.view.LayoutInflater;
import com.android.internal.annotations.VisibleForTesting;
import com.android.systemui.R;
@@ -59,19 +57,19 @@ class Bubble {
private NotificationEntry mEntry;
private final String mKey;
private final String mGroupId;
+
+ private long mLastUpdated;
+ private long mLastAccessed;
+
+ // Items that are typically loaded later
private String mAppName;
- private Drawable mUserBadgedAppIcon;
private ShortcutInfo mShortcutInfo;
-
- private boolean mInflated;
private BadgedImageView mIconView;
private BubbleExpandedView mExpandedView;
- private BubbleIconFactory mBubbleIconFactory;
-
- private long mLastUpdated;
- private long mLastAccessed;
- private boolean mIsUserCreated;
+ private boolean mInflated;
+ private BubbleViewInfoTask mInflationTask;
+ private boolean mInflateSynchronously;
/**
* Whether this notification should be shown in the shade when it is also displayed as a bubble.
@@ -94,37 +92,11 @@ class Bubble {
/** Used in tests when no UI is required. */
@VisibleForTesting(visibility = PRIVATE)
- Bubble(Context context, NotificationEntry e) {
+ Bubble(NotificationEntry e) {
mEntry = e;
mKey = e.getKey();
mLastUpdated = e.getSbn().getPostTime();
mGroupId = groupId(e);
-
- String shortcutId = e.getSbn().getNotification().getShortcutId();
- if (BubbleExperimentConfig.useShortcutInfoToBubble(context)
- && shortcutId != null) {
- mShortcutInfo = BubbleExperimentConfig.getShortcutInfo(context,
- e.getSbn().getPackageName(),
- e.getSbn().getUser(), shortcutId);
- }
-
- PackageManager pm = context.getPackageManager();
- ApplicationInfo info;
- try {
- info = pm.getApplicationInfo(
- mEntry.getSbn().getPackageName(),
- PackageManager.MATCH_UNINSTALLED_PACKAGES
- | PackageManager.MATCH_DISABLED_COMPONENTS
- | PackageManager.MATCH_DIRECT_BOOT_UNAWARE
- | PackageManager.MATCH_DIRECT_BOOT_AWARE);
- if (info != null) {
- mAppName = String.valueOf(pm.getApplicationLabel(info));
- }
- Drawable appIcon = pm.getApplicationIcon(mEntry.getSbn().getPackageName());
- mUserBadgedAppIcon = pm.getUserBadgedIcon(appIcon, mEntry.getSbn().getUser());
- } catch (PackageManager.NameNotFoundException unused) {
- mAppName = mEntry.getSbn().getPackageName();
- }
}
public String getKey() {
@@ -143,41 +115,22 @@ class Bubble {
return mEntry.getSbn().getPackageName();
}
+ @Nullable
public String getAppName() {
return mAppName;
}
- Drawable getUserBadgedAppIcon() {
- return mUserBadgedAppIcon;
- }
-
@Nullable
public ShortcutInfo getShortcutInfo() {
return mShortcutInfo;
}
- /**
- * Whether shortcut information should be used to populate the bubble.
- * <p>
- * To populate the activity use {@link LauncherApps#startShortcut(ShortcutInfo, Rect, Bundle)}.
- * To populate the icon use {@link LauncherApps#getShortcutIconDrawable(ShortcutInfo, int)}.
- */
- public boolean usingShortcutInfo() {
- return BubbleExperimentConfig.isShortcutIntent(getBubbleIntent());
- }
-
- void setBubbleIconFactory(BubbleIconFactory factory) {
- mBubbleIconFactory = factory;
- }
-
- boolean isInflated() {
- return mInflated;
- }
-
+ @Nullable
BadgedImageView getIconView() {
return mIconView;
}
+ @Nullable
BubbleExpandedView getExpandedView() {
return mExpandedView;
}
@@ -188,20 +141,62 @@ class Bubble {
}
}
- void inflate(LayoutInflater inflater, BubbleStackView stackView) {
- if (mInflated) {
- return;
+ /**
+ * Sets whether to perform inflation on the same thread as the caller. This method should only
+ * be used in tests, not in production.
+ */
+ @VisibleForTesting
+ void setInflateSynchronously(boolean inflateSynchronously) {
+ mInflateSynchronously = inflateSynchronously;
+ }
+
+ /**
+ * Starts a task to inflate & load any necessary information to display a bubble.
+ *
+ * @param callback the callback to notify one the bubble is ready to be displayed.
+ * @param context the context for the bubble.
+ * @param stackView the stackView the bubble is eventually added to.
+ * @param iconFactory the iconfactory use to create badged images for the bubble.
+ */
+ void inflate(BubbleViewInfoTask.Callback callback,
+ Context context,
+ BubbleStackView stackView,
+ BubbleIconFactory iconFactory) {
+ if (isBubbleLoading()) {
+ mInflationTask.cancel(true /* mayInterruptIfRunning */);
+ }
+ mInflationTask = new BubbleViewInfoTask(this,
+ context,
+ stackView,
+ iconFactory,
+ callback);
+ if (mInflateSynchronously) {
+ mInflationTask.onPostExecute(mInflationTask.doInBackground());
+ } else {
+ mInflationTask.execute();
}
- mIconView = (BadgedImageView) inflater.inflate(
- R.layout.bubble_view, stackView, false /* attachToRoot */);
- mIconView.setBubbleIconFactory(mBubbleIconFactory);
- mIconView.setBubble(this);
+ }
- mExpandedView = (BubbleExpandedView) inflater.inflate(
- R.layout.bubble_expanded_view, stackView, false /* attachToRoot */);
- mExpandedView.setBubble(this, stackView);
+ private boolean isBubbleLoading() {
+ return mInflationTask != null && mInflationTask.getStatus() != FINISHED;
+ }
- mInflated = true;
+ boolean isInflated() {
+ return mInflated;
+ }
+
+ void setViewInfo(BubbleViewInfoTask.BubbleViewInfo info) {
+ if (!isInflated()) {
+ mIconView = info.imageView;
+ mExpandedView = info.expandedView;
+ mInflated = true;
+ }
+
+ mShortcutInfo = info.shortcutInfo;
+ mAppName = info.appName;
+
+ mExpandedView.update(this);
+ mIconView.update(this, info.badgedBubbleImage, info.dotColor, info.dotPath);
}
/**
@@ -218,13 +213,12 @@ class Bubble {
}
}
- void updateEntry(NotificationEntry entry) {
+ /**
+ * Sets the entry associated with this bubble.
+ */
+ void setEntry(NotificationEntry entry) {
mEntry = entry;
mLastUpdated = entry.getSbn().getPostTime();
- if (mInflated) {
- mIconView.update(this);
- mExpandedView.update(this);
- }
}
/**
@@ -242,13 +236,6 @@ class Bubble {
}
/**
- * @return the timestamp in milliseconds when this bubble was last displayed in expanded state
- */
- long getLastAccessTime() {
- return mLastAccessed;
- }
-
- /**
* @return the display id of the virtual display on which bubble contents is drawn.
*/
int getDisplayId() {
@@ -352,6 +339,16 @@ class Bubble {
}
}
+ /**
+ * Whether shortcut information should be used to populate the bubble.
+ * <p>
+ * To populate the activity use {@link LauncherApps#startShortcut(ShortcutInfo, Rect, Bundle)}.
+ * To populate the icon use {@link LauncherApps#getShortcutIconDrawable(ShortcutInfo, int)}.
+ */
+ boolean usingShortcutInfo() {
+ return BubbleExperimentConfig.isShortcutIntent(getBubbleIntent());
+ }
+
@Nullable
PendingIntent getBubbleIntent() {
Notification.BubbleMetadata data = mEntry.getBubbleMetadata();
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java
index c82bc30338a7..7cd29ea48199 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java
@@ -150,6 +150,7 @@ public class BubbleController implements ConfigurationController.ConfigurationLi
private BubbleData mBubbleData;
@Nullable private BubbleStackView mStackView;
+ private BubbleIconFactory mBubbleIconFactory;
// Tracks the id of the current (foreground) user.
private int mCurrentUserId;
@@ -183,6 +184,8 @@ public class BubbleController implements ConfigurationController.ConfigurationLi
/** Last known orientation, used to detect orientation changes in {@link #onConfigChanged}. */
private int mOrientation = Configuration.ORIENTATION_UNDEFINED;
+ private boolean mInflateSynchronously;
+
/**
* Listener to be notified when some states of the bubbles change.
*/
@@ -352,6 +355,16 @@ public class BubbleController implements ConfigurationController.ConfigurationLi
mUserBlockedBubbles = new HashSet<>();
mScreenshotHelper = new ScreenshotHelper(context);
+ mBubbleIconFactory = new BubbleIconFactory(context);
+ }
+
+ /**
+ * Sets whether to perform inflation on the same thread as the caller. This method should only
+ * be used in tests, not in production.
+ */
+ @VisibleForTesting
+ void setInflateSynchronously(boolean inflateSynchronously) {
+ mInflateSynchronously = inflateSynchronously;
}
/**
@@ -415,16 +428,23 @@ public class BubbleController implements ConfigurationController.ConfigurationLi
@Override
public void onUiModeChanged() {
- if (mStackView != null) {
- mStackView.onThemeChanged();
- }
+ updateForThemeChanges();
}
@Override
public void onOverlayChanged() {
+ updateForThemeChanges();
+ }
+
+ private void updateForThemeChanges() {
if (mStackView != null) {
mStackView.onThemeChanged();
}
+ mBubbleIconFactory = new BubbleIconFactory(mContext);
+ for (Bubble b: mBubbleData.getBubbles()) {
+ // Reload each bubble
+ b.inflate(null /* callback */, mContext, mStackView, mBubbleIconFactory);
+ }
}
@Override
@@ -508,14 +528,10 @@ public class BubbleController implements ConfigurationController.ConfigurationLi
return (isSummary && isSuppressedSummary) || isBubbleAndSuppressed;
}
- void selectBubble(Bubble bubble) {
- mBubbleData.setSelectedBubble(bubble);
- }
-
@VisibleForTesting
void selectBubble(String key) {
Bubble bubble = mBubbleData.getBubbleWithKey(key);
- selectBubble(bubble);
+ mBubbleData.setSelectedBubble(bubble);
}
/**
@@ -562,11 +578,19 @@ public class BubbleController implements ConfigurationController.ConfigurationLi
}
void updateBubble(NotificationEntry notif, boolean suppressFlyout, boolean showInShade) {
+ if (mStackView == null) {
+ // Lazy init stack view when a bubble is created
+ ensureStackViewCreated();
+ }
// If this is an interruptive notif, mark that it's interrupted
if (notif.getImportance() >= NotificationManager.IMPORTANCE_HIGH) {
notif.setInterruption();
}
- mBubbleData.notificationEntryUpdated(notif, suppressFlyout, showInShade);
+ Bubble bubble = mBubbleData.getOrCreateBubble(notif);
+ bubble.setInflateSynchronously(mInflateSynchronously);
+ bubble.inflate(
+ b -> mBubbleData.notificationEntryUpdated(b, suppressFlyout, showInShade),
+ mContext, mStackView, mBubbleIconFactory);
}
/**
@@ -783,16 +807,6 @@ public class BubbleController implements ConfigurationController.ConfigurationLi
@Override
public void applyUpdate(BubbleData.Update update) {
- if (mStackView == null && update.addedBubble != null) {
- // Lazy init stack view when the first bubble is added.
- ensureStackViewCreated();
- }
-
- // If not yet initialized, ignore all other changes.
- if (mStackView == null) {
- return;
- }
-
if (update.addedBubble != null) {
mStackView.addBubble(update.addedBubble);
}
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleData.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleData.java
index b7df5baa4cf1..97224f1234dd 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleData.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleData.java
@@ -33,6 +33,7 @@ import android.util.Pair;
import androidx.annotation.Nullable;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.systemui.R;
import com.android.systemui.bubbles.BubbleController.DismissReason;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
@@ -48,7 +49,6 @@ import java.util.Objects;
import javax.inject.Inject;
import javax.inject.Singleton;
-import com.android.systemui.R;
/**
* Keeps track of active bubbles.
@@ -180,28 +180,44 @@ public class BubbleData {
dispatchPendingChanges();
}
- void notificationEntryUpdated(NotificationEntry entry, boolean suppressFlyout,
- boolean showInShade) {
+ /**
+ * Constructs a new bubble or returns an existing one. Does not add new bubbles to
+ * bubble data, must go through {@link #notificationEntryUpdated(Bubble, boolean, boolean)}
+ * for that.
+ */
+ Bubble getOrCreateBubble(NotificationEntry entry) {
+ Bubble bubble = getBubbleWithKey(entry.getKey());
+ if (bubble == null) {
+ bubble = new Bubble(entry);
+ } else {
+ bubble.setEntry(entry);
+ }
+ return bubble;
+ }
+
+ /**
+ * When this method is called it is expected that all info in the bubble has completed loading.
+ * @see Bubble#inflate(BubbleViewInfoTask.Callback, Context,
+ * BubbleStackView, BubbleIconFactory).
+ */
+ void notificationEntryUpdated(Bubble bubble, boolean suppressFlyout, boolean showInShade) {
if (DEBUG_BUBBLE_DATA) {
- Log.d(TAG, "notificationEntryUpdated: " + entry);
+ Log.d(TAG, "notificationEntryUpdated: " + bubble);
}
- Bubble bubble = getBubbleWithKey(entry.getKey());
- suppressFlyout |= !shouldShowFlyout(entry);
+ Bubble prevBubble = getBubbleWithKey(bubble.getKey());
+ suppressFlyout |= !shouldShowFlyout(bubble.getEntry());
- if (bubble == null) {
+ if (prevBubble == null) {
// Create a new bubble
- bubble = new Bubble(mContext, entry);
bubble.setSuppressFlyout(suppressFlyout);
doAdd(bubble);
trim();
} else {
// Updates an existing bubble
- bubble.updateEntry(entry);
bubble.setSuppressFlyout(suppressFlyout);
doUpdate(bubble);
}
-
if (bubble.shouldAutoExpand()) {
setSelectedBubbleInternal(bubble);
if (!mExpanded) {
@@ -214,6 +230,7 @@ public class BubbleData {
bubble.setShowInShade(!isBubbleExpandedAndSelected && showInShade);
bubble.setShowDot(!isBubbleExpandedAndSelected /* show */, true /* animate */);
dispatchPendingChanges();
+
}
public void notificationEntryRemoved(NotificationEntry entry, @DismissReason int reason) {
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExpandedView.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExpandedView.java
index 63d036d6362d..c1705dbb4e4b 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExpandedView.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExpandedView.java
@@ -99,7 +99,6 @@ public class BubbleExpandedView extends LinearLayout implements View.OnClickList
private int mExpandedViewTouchSlop;
private Bubble mBubble;
- private String mAppName;
private BubbleController mBubbleController = Dependency.get(BubbleController.class);
private WindowManager mWindowManager;
@@ -339,68 +338,51 @@ public class BubbleExpandedView extends LinearLayout implements View.OnClickList
}
}
- /**
- * Sets the bubble used to populate this view.
- */
- public void setBubble(Bubble bubble, BubbleStackView stackView) {
- if (DEBUG_BUBBLE_EXPANDED_VIEW) {
- Log.d(TAG, "setBubble: bubble=" + (bubble != null ? bubble.getKey() : "null"));
- }
+ void setStackView(BubbleStackView stackView) {
mStackView = stackView;
- mBubble = bubble;
- mAppName = bubble.getAppName();
-
- applyThemeAttrs();
- showSettingsIcon();
- updateExpandedView();
- }
-
- /**
- * Lets activity view know it should be shown / populated.
- */
- public void populateExpandedView() {
- if (DEBUG_BUBBLE_EXPANDED_VIEW) {
- Log.d(TAG, "populateExpandedView: "
- + "bubble=" + getBubbleKey());
- }
-
- if (usingActivityView()) {
- mActivityView.setCallback(mStateCallback);
- } else {
- Log.e(TAG, "Cannot populate expanded view.");
- }
}
/**
- * Updates the bubble backing this view. This will not re-populate ActivityView, it will
- * only update the deep-links in the title, and the height of the view.
+ * Sets the bubble used to populate this view.
*/
- public void update(Bubble bubble) {
+ void update(Bubble bubble) {
if (DEBUG_BUBBLE_EXPANDED_VIEW) {
Log.d(TAG, "update: bubble=" + (bubble != null ? bubble.getKey() : "null"));
}
- if (bubble.getKey().equals(mBubble.getKey())) {
+ boolean isNew = mBubble == null;
+ if (isNew || bubble.getKey().equals(mBubble.getKey())) {
mBubble = bubble;
- updateSettingsContentDescription();
- updateHeight();
+ mSettingsIcon.setContentDescription(getResources().getString(
+ R.string.bubbles_settings_button_description, bubble.getAppName()));
+
+ if (isNew) {
+ mBubbleIntent = mBubble.getBubbleIntent();
+ if (mBubbleIntent != null) {
+ setContentVisibility(false);
+ mActivityView.setVisibility(VISIBLE);
+ }
+ }
+ applyThemeAttrs();
} else {
Log.w(TAG, "Trying to update entry with different key, new bubble: "
+ bubble.getKey() + " old bubble: " + bubble.getKey());
}
}
- private void updateExpandedView() {
+ /**
+ * Lets activity view know it should be shown / populated with activity content.
+ */
+ void populateExpandedView() {
if (DEBUG_BUBBLE_EXPANDED_VIEW) {
- Log.d(TAG, "updateExpandedView: bubble="
- + getBubbleKey());
+ Log.d(TAG, "populateExpandedView: "
+ + "bubble=" + getBubbleKey());
}
- mBubbleIntent = mBubble.getBubbleIntent();
- if (mBubbleIntent != null) {
- setContentVisibility(false);
- mActivityView.setVisibility(VISIBLE);
+ if (usingActivityView()) {
+ mActivityView.setCallback(mStateCallback);
+ } else {
+ Log.e(TAG, "Cannot populate expanded view.");
}
- updateView();
}
boolean performBackPressIfNeeded() {
@@ -490,16 +472,6 @@ public class BubbleExpandedView extends LinearLayout implements View.OnClickList
}
}
- private void updateSettingsContentDescription() {
- mSettingsIcon.setContentDescription(getResources().getString(
- R.string.bubbles_settings_button_description, mAppName));
- }
-
- void showSettingsIcon() {
- updateSettingsContentDescription();
- mSettingsIcon.setVisibility(VISIBLE);
- }
-
/**
* Update appearance of the expanded view being displayed.
*/
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleIconFactory.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleIconFactory.java
index 9ff033cb3411..b32dbb724258 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleIconFactory.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleIconFactory.java
@@ -15,11 +15,14 @@
*/
package com.android.systemui.bubbles;
+import android.app.Notification;
import android.content.Context;
+import android.content.pm.LauncherApps;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
+import android.graphics.drawable.Icon;
import com.android.launcher3.icons.BaseIconFactory;
import com.android.launcher3.icons.BitmapInfo;
@@ -42,19 +45,40 @@ public class BubbleIconFactory extends BaseIconFactory {
return mContext.getResources().getDimensionPixelSize(
com.android.launcher3.icons.R.dimen.profile_badge_size);
}
+ /**
+ * Returns the drawable that the developer has provided to display in the bubble.
+ */
+ Drawable getBubbleDrawable(Bubble b, Context context) {
+ if (b.getShortcutInfo() != null && b.usingShortcutInfo()) {
+ LauncherApps launcherApps =
+ (LauncherApps) context.getSystemService(Context.LAUNCHER_APPS_SERVICE);
+ int density = context.getResources().getConfiguration().densityDpi;
+ return launcherApps.getShortcutIconDrawable(b.getShortcutInfo(), density);
+ } else {
+ Notification.BubbleMetadata metadata = b.getEntry().getBubbleMetadata();
+ Icon ic = metadata.getIcon();
+ return ic.loadDrawable(context);
+ }
+ }
- BitmapInfo getBadgedBitmap(Bubble b) {
+ /**
+ * Returns a {@link BitmapInfo} for the app-badge that is shown on top of each bubble. This
+ * will include the workprofile indicator on the badge if appropriate.
+ */
+ BitmapInfo getBadgeBitmap(Drawable userBadgedAppIcon) {
Bitmap userBadgedBitmap = createIconBitmap(
- b.getUserBadgedAppIcon(), 1f, getBadgeSize());
+ userBadgedAppIcon, 1f, getBadgeSize());
Canvas c = new Canvas();
ShadowGenerator shadowGenerator = new ShadowGenerator(getBadgeSize());
c.setBitmap(userBadgedBitmap);
shadowGenerator.recreateIcon(Bitmap.createBitmap(userBadgedBitmap), c);
- BitmapInfo bitmapInfo = createIconBitmap(userBadgedBitmap);
- return bitmapInfo;
+ return createIconBitmap(userBadgedBitmap);
}
+ /**
+ * Returns a {@link BitmapInfo} for the entire bubble icon including the badge.
+ */
BitmapInfo getBubbleBitmap(Drawable bubble, BitmapInfo badge) {
BitmapInfo bubbleIconInfo = createBadgedIconBitmap(bubble,
null /* user */,
@@ -64,5 +88,4 @@ public class BubbleIconFactory extends BaseIconFactory {
new BitmapDrawable(mContext.getResources(), badge.icon));
return bubbleIconInfo;
}
-
}
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java
index 245b2320f99d..898768311031 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java
@@ -516,14 +516,7 @@ public class BubbleStackView extends FrameLayout {
* Handle theme changes.
*/
public void onThemeChanged() {
- // Recreate icon factory to update default adaptive icon scale.
- mBubbleIconFactory = new BubbleIconFactory(mContext);
setUpFlyout();
- for (Bubble b: mBubbleData.getBubbles()) {
- b.getIconView().setBubbleIconFactory(mBubbleIconFactory);
- b.getIconView().updateViews();
- b.getExpandedView().applyThemeAttrs();
- }
}
/** Respond to the phone being rotated by repositioning the stack and hiding any flyouts. */
@@ -749,10 +742,6 @@ public class BubbleStackView extends FrameLayout {
mStackOnLeftOrWillBe = mStackAnimationController.isStackOnLeftSide();
}
- bubble.setBubbleIconFactory(mBubbleIconFactory);
- bubble.inflate(mInflater, this);
- bubble.getIconView().updateViews();
-
// Set the dot position to the opposite of the side the stack is resting on, since the stack
// resting slightly off-screen would result in the dot also being off-screen.
bubble.getIconView().setDotPosition(
@@ -1567,9 +1556,6 @@ public class BubbleStackView extends FrameLayout {
mExpandedViewContainer.setVisibility(mIsExpanded ? VISIBLE : GONE);
if (mIsExpanded) {
- // First update the view so that it calculates a new height (ensuring the y position
- // calculation is correct)
- mExpandedBubble.getExpandedView().updateView();
final float y = getExpandedViewY();
if (!mExpandedViewYAnim.isRunning()) {
// We're not animating so set the value
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleViewInfoTask.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleViewInfoTask.java
new file mode 100644
index 000000000000..41f5028ed5c7
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleViewInfoTask.java
@@ -0,0 +1,182 @@
+/*
+ * Copyright (C) 2019 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.bubbles;
+
+import static com.android.systemui.bubbles.BadgedImageView.DEFAULT_PATH_SIZE;
+import static com.android.systemui.bubbles.BadgedImageView.WHITE_SCRIM_ALPHA;
+import static com.android.systemui.bubbles.BubbleDebugConfig.TAG_BUBBLES;
+import static com.android.systemui.bubbles.BubbleDebugConfig.TAG_WITH_CLASS_NAME;
+
+import android.content.Context;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.ShortcutInfo;
+import android.graphics.Bitmap;
+import android.graphics.Color;
+import android.graphics.Matrix;
+import android.graphics.Path;
+import android.graphics.drawable.Drawable;
+import android.os.AsyncTask;
+import android.service.notification.StatusBarNotification;
+import android.util.Log;
+import android.util.PathParser;
+import android.view.LayoutInflater;
+
+import androidx.annotation.Nullable;
+
+import com.android.internal.graphics.ColorUtils;
+import com.android.launcher3.icons.BitmapInfo;
+import com.android.systemui.R;
+
+import java.lang.ref.WeakReference;
+
+/**
+ * Simple task to inflate views & load necessary info to display a bubble.
+ */
+public class BubbleViewInfoTask extends AsyncTask<Void, Void, BubbleViewInfoTask.BubbleViewInfo> {
+ private static final String TAG = TAG_WITH_CLASS_NAME ? "BubbleViewInfoTask" : TAG_BUBBLES;
+
+
+ /**
+ * Callback to find out when the bubble has been inflated & necessary data loaded.
+ */
+ public interface Callback {
+ /**
+ * Called when data has been loaded for the bubble.
+ */
+ void onBubbleViewsReady(Bubble bubble);
+ }
+
+ private Bubble mBubble;
+ private WeakReference<Context> mContext;
+ private WeakReference<BubbleStackView> mStackView;
+ private BubbleIconFactory mIconFactory;
+ private Callback mCallback;
+
+ /**
+ * Creates a task to load information for the provided {@link Bubble}. Once all info
+ * is loaded, {@link Callback} is notified.
+ */
+ BubbleViewInfoTask(Bubble b,
+ Context context,
+ BubbleStackView stackView,
+ BubbleIconFactory factory,
+ Callback c) {
+ mBubble = b;
+ mContext = new WeakReference<>(context);
+ mStackView = new WeakReference<>(stackView);
+ mIconFactory = factory;
+ mCallback = c;
+ }
+
+ @Override
+ protected BubbleViewInfo doInBackground(Void... voids) {
+ return BubbleViewInfo.populate(mContext.get(), mStackView.get(), mIconFactory, mBubble);
+ }
+
+ @Override
+ protected void onPostExecute(BubbleViewInfo viewInfo) {
+ if (viewInfo != null) {
+ mBubble.setViewInfo(viewInfo);
+ if (mCallback != null && !isCancelled()) {
+ mCallback.onBubbleViewsReady(mBubble);
+ }
+ }
+ }
+
+ static class BubbleViewInfo {
+ BadgedImageView imageView;
+ BubbleExpandedView expandedView;
+ ShortcutInfo shortcutInfo;
+ String appName;
+ Bitmap badgedBubbleImage;
+ int dotColor;
+ Path dotPath;
+
+ @Nullable
+ static BubbleViewInfo populate(Context c, BubbleStackView stackView,
+ BubbleIconFactory iconFactory, Bubble b) {
+ BubbleViewInfo info = new BubbleViewInfo();
+
+ // View inflation: only should do this once per bubble
+ if (!b.isInflated()) {
+ LayoutInflater inflater = LayoutInflater.from(c);
+ info.imageView = (BadgedImageView) inflater.inflate(
+ R.layout.bubble_view, stackView, false /* attachToRoot */);
+
+ info.expandedView = (BubbleExpandedView) inflater.inflate(
+ R.layout.bubble_expanded_view, stackView, false /* attachToRoot */);
+ info.expandedView.setStackView(stackView);
+ }
+
+ StatusBarNotification sbn = b.getEntry().getSbn();
+ String packageName = sbn.getPackageName();
+
+ // Shortcut info for this bubble
+ String shortcutId = sbn.getNotification().getShortcutId();
+ if (BubbleExperimentConfig.useShortcutInfoToBubble(c)
+ && shortcutId != null) {
+ info.shortcutInfo = BubbleExperimentConfig.getShortcutInfo(c,
+ packageName,
+ sbn.getUser(), shortcutId);
+ }
+
+ // App name & app icon
+ PackageManager pm = c.getPackageManager();
+ ApplicationInfo appInfo;
+ Drawable badgedIcon;
+ try {
+ appInfo = pm.getApplicationInfo(
+ packageName,
+ PackageManager.MATCH_UNINSTALLED_PACKAGES
+ | PackageManager.MATCH_DISABLED_COMPONENTS
+ | PackageManager.MATCH_DIRECT_BOOT_UNAWARE
+ | PackageManager.MATCH_DIRECT_BOOT_AWARE);
+ if (appInfo != null) {
+ info.appName = String.valueOf(pm.getApplicationLabel(appInfo));
+ }
+ Drawable appIcon = pm.getApplicationIcon(packageName);
+ 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);
+ return null;
+ }
+
+ // Badged bubble image
+ Drawable bubbleDrawable = iconFactory.getBubbleDrawable(b, c);
+ BitmapInfo badgeBitmapInfo = iconFactory.getBadgeBitmap(badgedIcon);
+ info.badgedBubbleImage = iconFactory.getBubbleBitmap(bubbleDrawable,
+ badgeBitmapInfo).icon;
+
+ // Dot color & placement
+ Path iconPath = PathParser.createPathFromPathData(
+ c.getResources().getString(com.android.internal.R.string.config_icon_mask));
+ Matrix matrix = new Matrix();
+ float scale = iconFactory.getNormalizer().getScale(bubbleDrawable,
+ null /* outBounds */, null /* path */, null /* outMaskShape */);
+ float radius = DEFAULT_PATH_SIZE / 2f;
+ matrix.setScale(scale /* x scale */, scale /* y scale */, radius /* pivot x */,
+ radius /* pivot y */);
+ iconPath.transform(matrix);
+ info.dotPath = iconPath;
+ info.dotColor = ColorUtils.blendARGB(badgeBitmapInfo.color,
+ Color.WHITE, WHITE_SCRIM_ALPHA);
+ return info;
+ }
+ }
+}
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 2bf855a27cc4..e0b4b81c368d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleControllerTest.java
@@ -45,9 +45,7 @@ import android.app.IActivityManager;
import android.app.Notification;
import android.app.PendingIntent;
import android.content.Context;
-import android.content.Intent;
import android.content.res.Resources;
-import android.graphics.drawable.Icon;
import android.hardware.face.FaceManager;
import android.service.notification.ZenModeConfig;
import android.testing.AndroidTestingRunner;
@@ -57,7 +55,6 @@ import android.view.WindowManager;
import androidx.test.filters.SmallTest;
import com.android.internal.colorextraction.ColorExtractor;
-import com.android.systemui.R;
import com.android.systemui.SystemUIFactory;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.colorextraction.SysuiColorExtractor;
@@ -731,6 +728,7 @@ public class BubbleControllerTest extends SysuiTestCase {
data, Runnable::run, configurationController, interruptionStateProvider,
zenModeController, lockscreenUserManager, groupManager, entryManager,
remoteInputUriController);
+ setInflateSynchronously(true);
}
}
@@ -746,17 +744,6 @@ public class BubbleControllerTest extends SysuiTestCase {
}
/**
- * @return basic {@link android.app.Notification.BubbleMetadata.Builder}
- */
- private Notification.BubbleMetadata.Builder getBuilder() {
- Intent target = new Intent(mContext, BubblesTestActivity.class);
- PendingIntent bubbleIntent = PendingIntent.getActivity(mContext, 0, target, 0);
- return new Notification.BubbleMetadata.Builder()
- .setIntent(bubbleIntent)
- .setIcon(Icon.createWithResource(mContext, R.drawable.android));
- }
-
- /**
* Sets the bubble metadata flags for this entry. These flags are normally set by
* NotificationManagerService when the notification is sent, however, these tests do not
* go through that path so we set them explicitly when testing.
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 1131be943ee5..c4ae4093b680 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleDataTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleDataTest.java
@@ -85,6 +85,8 @@ public class BubbleDataTest extends SysuiTestCase {
private Bubble mBubbleB2;
private Bubble mBubbleB3;
private Bubble mBubbleC1;
+ private Bubble mBubbleInterruptive;
+ private Bubble mBubbleDismissed;
private BubbleData mBubbleData;
@@ -119,18 +121,20 @@ public class BubbleDataTest extends SysuiTestCase {
modifyRanking(mEntryInterruptive)
.setVisuallyInterruptive(true)
.build();
+ mBubbleInterruptive = new Bubble(mEntryInterruptive);
ExpandableNotificationRow row = mNotificationTestHelper.createBubble();
mEntryDismissed = createBubbleEntry(1, "dismissed", "package.d");
mEntryDismissed.setRow(row);
+ mBubbleDismissed = new Bubble(mEntryDismissed);
- mBubbleA1 = new Bubble(mContext, mEntryA1);
- mBubbleA2 = new Bubble(mContext, mEntryA2);
- mBubbleA3 = new Bubble(mContext, mEntryA3);
- mBubbleB1 = new Bubble(mContext, mEntryB1);
- mBubbleB2 = new Bubble(mContext, mEntryB2);
- mBubbleB3 = new Bubble(mContext, mEntryB3);
- mBubbleC1 = new Bubble(mContext, mEntryC1);
+ mBubbleA1 = new Bubble(mEntryA1);
+ mBubbleA2 = new Bubble(mEntryA2);
+ mBubbleA3 = new Bubble(mEntryA3);
+ mBubbleB1 = new Bubble(mEntryB1);
+ mBubbleB2 = new Bubble(mEntryB2);
+ mBubbleB3 = new Bubble(mEntryB3);
+ mBubbleC1 = new Bubble(mEntryC1);
mBubbleData = new BubbleData(getContext());
@@ -180,7 +184,7 @@ public class BubbleDataTest extends SysuiTestCase {
mBubbleData.setListener(mListener);
// Test
- mBubbleData.notificationEntryUpdated(mEntryC1, /* suppressFlyout */ true, /* showInShade */
+ mBubbleData.notificationEntryUpdated(mBubbleC1, /* suppressFlyout */ true, /* showInShade */
true);
// Verify
@@ -195,7 +199,7 @@ public class BubbleDataTest extends SysuiTestCase {
mBubbleData.setListener(mListener);
// Test
- mBubbleData.notificationEntryUpdated(mEntryInterruptive,
+ mBubbleData.notificationEntryUpdated(mBubbleInterruptive,
false /* suppressFlyout */, true /* showInShade */);
// Verify
@@ -210,11 +214,11 @@ public class BubbleDataTest extends SysuiTestCase {
mBubbleData.setListener(mListener);
// Test
- mBubbleData.notificationEntryUpdated(mEntryC1, false /* suppressFlyout */,
+ mBubbleData.notificationEntryUpdated(mBubbleC1, false /* suppressFlyout */,
true /* showInShade */);
verifyUpdateReceived();
- mBubbleData.notificationEntryUpdated(mEntryC1, false /* suppressFlyout */,
+ mBubbleData.notificationEntryUpdated(mBubbleC1, false /* suppressFlyout */,
true /* showInShade */);
verifyUpdateReceived();
@@ -229,16 +233,16 @@ public class BubbleDataTest extends SysuiTestCase {
mBubbleData.setListener(mListener);
// Test
- mBubbleData.notificationEntryUpdated(mEntryDismissed, false /* suppressFlyout */,
- false /* showInShade */);
+ mBubbleData.notificationEntryUpdated(mBubbleDismissed, false /* suppressFlyout */,
+ true /* showInShade */);
verifyUpdateReceived();
// Make it look like user swiped away row
mEntryDismissed.getRow().dismiss(false /* refocusOnDismiss */);
- assertThat(mBubbleData.getBubbleWithKey(mEntryDismissed.getKey()).showInShade()).isFalse();
+ assertThat(mBubbleData.getBubbleWithKey(mBubbleDismissed.getKey()).showInShade()).isFalse();
- mBubbleData.notificationEntryUpdated(mEntryDismissed, false /* suppressFlyout */,
- false /* showInShade */);
+ mBubbleData.notificationEntryUpdated(mBubbleDismissed, false /* suppressFlyout */,
+ true /* showInShade */);
verifyUpdateReceived();
// Verify
@@ -974,7 +978,10 @@ public class BubbleDataTest extends SysuiTestCase {
private void sendUpdatedEntryAtTime(NotificationEntry entry, long postTime) {
setPostTime(entry, postTime);
- mBubbleData.notificationEntryUpdated(entry, false /* suppressFlyout*/,
+ // BubbleController calls this:
+ Bubble b = mBubbleData.getOrCreateBubble(entry);
+ // And then this
+ mBubbleData.notificationEntryUpdated(b, false /* suppressFlyout*/,
true /* showInShade */);
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleTest.java b/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleTest.java
index 643a5d2d82d5..3c42fd1cb48d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleTest.java
@@ -58,7 +58,7 @@ public class BubbleTest extends SysuiTestCase {
mEntry = new NotificationEntryBuilder()
.setNotification(mNotif)
.build();
- mBubble = new Bubble(mContext, mEntry);
+ mBubble = new Bubble(mEntry);
}
@Test