Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit dcc609a1 authored by TreeHugger Robot's avatar TreeHugger Robot Committed by Android (Google) Code Review
Browse files

Merge changes I95898fa6,I21e4b17e into rvc-dev

* changes:
  Prevent duplicates from persisted bubbles.
  Persists bubbles to disk (part 4)
parents 68348d03 6a8fab05
Loading
Loading
Loading
Loading
+86 −10
Original line number Diff line number Diff line
@@ -15,7 +15,7 @@
 */
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;

@@ -27,6 +27,7 @@ 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;
@@ -55,6 +56,11 @@ 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;

@@ -96,11 +102,18 @@ class Bubble implements BubbleViewProvider {
    private Bitmap mBadgedImage;
    private int mDotColor;
    private Path mDotPath;
    private int mFlags;

    // TODO: Decouple Bubble from NotificationEntry and transform ShortcutInfo into Bubble
    Bubble(ShortcutInfo shortcutInfo) {
    /**
     * 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.
     */
    Bubble(@NonNull final String key, @NonNull final ShortcutInfo shortcutInfo) {
        Objects.requireNonNull(key);
        Objects.requireNonNull(shortcutInfo);
        mShortcutInfo = shortcutInfo;
        mKey = shortcutInfo.getId();
        mKey = key;
        mFlags = 0;
    }

    /** Used in tests when no UI is required. */
@@ -111,6 +124,7 @@ class Bubble implements BubbleViewProvider {
        mKey = e.getKey();
        mLastUpdated = e.getSbn().getPostTime();
        mSuppressionListener = listener;
        mFlags = e.getSbn().getNotification().flags;
    }

    @Override
@@ -118,12 +132,22 @@ 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 getPackageName() {
        return mEntry.getSbn().getPackageName();
        return mEntry == null
                ? mShortcutInfo == null ? null : mShortcutInfo.getPackage()
                : mEntry.getSbn().getPackageName();
    }

    @Override
@@ -167,6 +191,18 @@ class Bubble implements BubbleViewProvider {
        return mExpandedView;
    }

    @Nullable
    public String getTitle() {
        final CharSequence titleCharSeq;
        if (mEntry == null) {
            titleCharSeq = null;
        } else {
            titleCharSeq = mEntry.getSbn().getNotification().extras.getCharSequence(
                    Notification.EXTRA_TITLE);
        }
        return titleCharSeq != null ? titleCharSeq.toString() : null;
    }

    /**
     * Call when the views should be removed, ensure this is called to clean up ActivityView
     * content.
@@ -207,7 +243,8 @@ class Bubble implements BubbleViewProvider {
    void inflate(BubbleViewInfoTask.Callback callback,
            Context context,
            BubbleStackView stackView,
            BubbleIconFactory iconFactory) {
            BubbleIconFactory iconFactory,
            boolean skipInflation) {
        if (isBubbleLoading()) {
            mInflationTask.cancel(true /* mayInterruptIfRunning */);
        }
@@ -215,6 +252,7 @@ class Bubble implements BubbleViewProvider {
                context,
                stackView,
                iconFactory,
                skipInflation,
                callback);
        if (mInflateSynchronously) {
            mInflationTask.onPostExecute(mInflationTask.doInBackground());
@@ -327,6 +365,7 @@ class Bubble implements BubbleViewProvider {
     * Whether this notification should be shown in the shade.
     */
    boolean showInShade() {
        if (mEntry == null) return false;
        return !shouldSuppressNotification() || !mEntry.isClearable();
    }

@@ -334,8 +373,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) {
@@ -366,6 +405,7 @@ class Bubble implements BubbleViewProvider {
     */
    @Override
    public boolean showDot() {
        if (mEntry == null) return false;
        return mShowBubbleUpdateDot
                && !mEntry.shouldSuppressNotificationDot()
                && !shouldSuppressNotification();
@@ -375,6 +415,7 @@ 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();
@@ -394,6 +435,7 @@ class Bubble implements BubbleViewProvider {
    }

    float getDesiredHeight(Context context) {
        if (mEntry == null) return 0;
        Notification.BubbleMetadata data = mEntry.getBubbleMetadata();
        boolean useRes = data.getDesiredHeightResId() != 0;
        if (useRes) {
@@ -407,6 +449,7 @@ 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) {
@@ -423,11 +466,13 @@ class Bubble implements BubbleViewProvider {
     * To populate the icon use {@link LauncherApps#getShortcutIconDrawable(ShortcutInfo, int)}.
     */
    boolean usingShortcutInfo() {
        return mEntry.getBubbleMetadata().getShortcutId() != null;
        return mEntry != null && mEntry.getBubbleMetadata().getShortcutId() != null
                || mShortcutInfo != null;
    }

    @Nullable
    PendingIntent getBubbleIntent() {
        if (mEntry == null) return null;
        Notification.BubbleMetadata data = mEntry.getBubbleMetadata();
        if (data != null) {
            return data.getIntent();
@@ -435,16 +480,32 @@ class Bubble implements BubbleViewProvider {
        return null;
    }

    Intent getSettingsIntent() {
    Intent getSettingsIntent(final Context context) {
        final Intent intent = new Intent(Settings.ACTION_APP_NOTIFICATION_BUBBLE_SETTINGS);
        intent.putExtra(Settings.EXTRA_APP_PACKAGE, getPackageName());
        intent.putExtra(Settings.EXTRA_APP_UID, mEntry.getSbn().getUid());
        final int uid = getUid(context);
        if (uid != -1) {
            intent.putExtra(Settings.EXTRA_APP_UID, uid);
        }
        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;
@@ -466,11 +527,13 @@ 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()) ||  mShouldAutoExpand;
    }
@@ -479,6 +542,19 @@ class Bubble implements BubbleViewProvider {
        mShouldAutoExpand = shouldAutoExpand;
    }

    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 + '}';
+82 −29
Original line number Diff line number Diff line
@@ -42,6 +42,7 @@ 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;
@@ -113,6 +114,7 @@ import java.lang.annotation.Retention;
import java.lang.annotation.Target;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;

/**
 * Bubbles are a special type of content that can "float" on top of other apps or System UI.
@@ -243,13 +245,13 @@ 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(NotificationEntry entry, int reason);
        void removeNotification(@NonNull NotificationEntry entry, int reason);

        /**
         * Called when a bubbled notification has changed whether it should be
         * filtered from the shade.
         */
        void invalidateNotifications(String reason);
        void invalidateNotifications(@NonNull String reason);

        /**
         * Called on a bubbled entry that has been removed when there are no longer
@@ -259,7 +261,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(NotificationEntry entry);
        void maybeCancelSummary(@NonNull NotificationEntry entry);
    }

    /**
@@ -755,10 +757,12 @@ 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);
            b.inflate(null /* callback */, mContext, mStackView, mBubbleIconFactory,
                    false /* skipInflation */);
        }
        for (Bubble b: mBubbleData.getOverflowBubbles()) {
            b.inflate(null /* callback */, mContext, mStackView, mBubbleIconFactory);
            b.inflate(null /* callback */, mContext, mStackView, mBubbleIconFactory,
                    false /* skipInflation */);
        }
    }

@@ -845,7 +849,7 @@ public class BubbleController implements ConfigurationController.ConfigurationLi

    void promoteBubbleFromOverflow(Bubble bubble) {
        bubble.setInflateSynchronously(mInflateSynchronously);
        setIsBubble(bubble.getEntry(), /* isBubble */ true);
        setIsBubble(bubble, /* isBubble */ true);
        mBubbleData.promoteBubbleFromOverflow(bubble, mStackView, mBubbleIconFactory);
    }

@@ -895,10 +899,30 @@ public class BubbleController implements ConfigurationController.ConfigurationLi
        updateBubble(notif, false /* 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) {
        // 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();
@@ -918,11 +942,11 @@ public class BubbleController implements ConfigurationController.ConfigurationLi
                            return;
                        }
                        mHandler.post(
                                () -> removeBubble(bubble.getEntry(),
                                () -> removeBubble(bubble.getKey(),
                                        BubbleController.DISMISS_INVALID_INTENT));
                    });
                },
                mContext, mStackView, mBubbleIconFactory);
                mContext, mStackView, mBubbleIconFactory, false /* skipInflation */);
    }

    /**
@@ -934,7 +958,10 @@ 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(NotificationEntry entry, boolean shouldBubble) {
    public void onUserChangedBubble(@Nullable final NotificationEntry entry, boolean shouldBubble) {
        if (entry == null) {
            return;
        }
        NotificationChannel channel = entry.getChannel();
        final String appPkg = entry.getSbn().getPackageName();
        final int appUid = entry.getSbn().getUid();
@@ -973,14 +1000,14 @@ public class BubbleController implements ConfigurationController.ConfigurationLi
    }

    /**
     * Removes the bubble with the given NotificationEntry.
     * Removes the bubble with the given key.
     * <p>
     * Must be called from the main thread.
     */
    @MainThread
    void removeBubble(NotificationEntry entry, int reason) {
        if (mBubbleData.hasAnyBubbleWithKey(entry.getKey())) {
            mBubbleData.notificationEntryRemoved(entry, reason);
    void removeBubble(String key, int reason) {
        if (mBubbleData.hasAnyBubbleWithKey(key)) {
            mBubbleData.notificationEntryRemoved(key, reason);
        }
    }

@@ -998,7 +1025,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, DISMISS_NO_LONGER_BUBBLE);
            removeBubble(entry.getKey(), DISMISS_NO_LONGER_BUBBLE);
        } else if (shouldBubble && entry.isBubble()) {
            updateBubble(entry);
        }
@@ -1012,10 +1039,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).getEntry(), DISMISS_GROUP_CANCELLED);
                removeBubble(bubbleChildren.get(i).getKey(), DISMISS_GROUP_CANCELLED);
            }
        } else {
            removeBubble(entry, DISMISS_NOTIF_CANCEL);
            removeBubble(entry.getKey(), DISMISS_NOTIF_CANCEL);
        }
    }

@@ -1037,7 +1064,8 @@ public class BubbleController implements ConfigurationController.ConfigurationLi
            rankingMap.getRanking(key, mTmpRanking);
            boolean isActiveBubble = mBubbleData.hasAnyBubbleWithKey(key);
            if (isActiveBubble && !mTmpRanking.canBubble()) {
                mBubbleData.notificationEntryRemoved(entry, BubbleController.DISMISS_BLOCKED);
                mBubbleData.notificationEntryRemoved(entry.getKey(),
                        BubbleController.DISMISS_BLOCKED);
            } else if (entry != null && mTmpRanking.isBubble() && !isActiveBubble) {
                entry.setFlagBubble(true);
                onEntryUpdated(entry);
@@ -1045,7 +1073,8 @@ public class BubbleController implements ConfigurationController.ConfigurationLi
        }
    }

    private void setIsBubble(NotificationEntry entry, boolean isBubble) {
    private void setIsBubble(@NonNull final NotificationEntry entry, final boolean isBubble) {
        Objects.requireNonNull(entry);
        if (isBubble) {
            entry.getSbn().getNotification().flags |= FLAG_BUBBLE;
        } else {
@@ -1058,11 +1087,31 @@ public class BubbleController implements ConfigurationController.ConfigurationLi
        }
    }

    private void setIsBubble(@NonNull final Bubble b, final boolean isBubble) {
        Objects.requireNonNull(b);
        if (isBubble) {
            b.enable(FLAG_BUBBLE);
        } else {
            b.disable(FLAG_BUBBLE);
        }
        if (b.getEntry() != null) {
            setIsBubble(b.getEntry(), isBubble);
        } else {
            try {
                mBarService.onNotificationBubbleChanged(b.getKey(), isBubble, 0);
            } catch (RemoteException e) {
                // Bad things have happened
            }
        }
    }

    @SuppressWarnings("FieldCanBeLocal")
    private final BubbleData.Listener mBubbleDataListener = new BubbleData.Listener() {

        @Override
        public void applyUpdate(BubbleData.Update update) {
            // Lazy load overflow bubbles from disk
            loadOverflowBubblesFromDisk();
            // Update bubbles in overflow.
            if (mOverflowCallback != null) {
                mOverflowCallback.run();
@@ -1097,18 +1146,21 @@ 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);
                            }
                        }
                    } else {
                        if (bubble.getEntry().isBubble() && bubble.showInShade()) {
                            setIsBubble(bubble.getEntry(), false /* isBubble */);
                        if (bubble.isBubble() && bubble.showInShade()) {
                            setIsBubble(bubble, false /* isBubble */);
                        }
                        if (bubble.getEntry().getRow() != null) {
                        if (bubble.getEntry() != null && 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
@@ -1117,6 +1169,7 @@ public class BubbleController implements ConfigurationController.ConfigurationLi
                        }
                    }
                }
            }
            mDataRepository.removeBubbles(mCurrentUserId, bubblesToBeRemovedFromRepository);

            if (update.addedBubble != null) {
@@ -1138,7 +1191,7 @@ public class BubbleController implements ConfigurationController.ConfigurationLi

            if (update.selectionChanged) {
                mStackView.setSelectedBubble(update.selectedBubble);
                if (update.selectedBubble != null) {
                if (update.selectedBubble != null && update.selectedBubble.getEntry() != null) {
                    mNotificationGroupManager.updateSuppression(
                            update.selectedBubble.getEntry());
                }
+16 −8
Original line number Diff line number Diff line
@@ -21,6 +21,7 @@ import static com.android.systemui.bubbles.BubbleDebugConfig.DEBUG_BUBBLE_DATA;
import static com.android.systemui.bubbles.BubbleDebugConfig.TAG_BUBBLES;
import static com.android.systemui.bubbles.BubbleDebugConfig.TAG_WITH_CLASS_NAME;

import android.annotation.NonNull;
import android.app.Notification;
import android.app.PendingIntent;
import android.content.Context;
@@ -214,7 +215,7 @@ public class BubbleData {
                    notificationEntryUpdated(bubble, false /* suppressFlyout */,
                            true /* showInShade */);
                },
                mContext, stack, factory);
                mContext, stack, factory, false /* skipInflation */);
    }

    void setShowingOverflow(boolean showingOverflow) {
@@ -268,7 +269,8 @@ public class BubbleData {
        }
        mPendingBubbles.remove(bubble); // No longer pending once we're here
        Bubble prevBubble = getBubbleInStackWithKey(bubble.getKey());
        suppressFlyout |= !bubble.getEntry().getRanking().visuallyInterruptive();
        suppressFlyout |= bubble.getEntry() == null
                || !bubble.getEntry().getRanking().visuallyInterruptive();

        if (prevBubble == null) {
            // Create a new bubble
@@ -297,11 +299,14 @@ public class BubbleData {
        dispatchPendingChanges();
    }

    public void notificationEntryRemoved(NotificationEntry entry, @DismissReason int reason) {
    /**
     * Called when a notification associated with a bubble is removed.
     */
    public void notificationEntryRemoved(String key, @DismissReason int reason) {
        if (DEBUG_BUBBLE_DATA) {
            Log.d(TAG, "notificationEntryRemoved: entry=" + entry + " reason=" + reason);
            Log.d(TAG, "notificationEntryRemoved: key=" + key + " reason=" + reason);
        }
        doRemove(entry.getKey(), reason);
        doRemove(key, reason);
        dispatchPendingChanges();
    }

@@ -349,7 +354,7 @@ public class BubbleData {
            return bubbleChildren;
        }
        for (Bubble b : mBubbles) {
            if (groupKey.equals(b.getEntry().getSbn().getGroupKey())) {
            if (b.getEntry() != null && groupKey.equals(b.getEntry().getSbn().getGroupKey())) {
                bubbleChildren.add(b);
            }
        }
@@ -447,8 +452,10 @@ public class BubbleData {
            Bubble newSelected = mBubbles.get(newIndex);
            setSelectedBubbleInternal(newSelected);
        }
        if (bubbleToRemove.getEntry() != null) {
            maybeSendDeleteIntent(reason, bubbleToRemove.getEntry());
        }
    }

    void overflowBubble(@DismissReason int reason, Bubble bubble) {
        if (bubble.getPendingIntentCanceled()
@@ -615,7 +622,8 @@ public class BubbleData {
        return true;
    }

    private void maybeSendDeleteIntent(@DismissReason int reason, NotificationEntry entry) {
    private void maybeSendDeleteIntent(@DismissReason int reason,
            @NonNull final NotificationEntry entry) {
        if (reason == BubbleController.DISMISS_USER_GESTURE) {
            Notification.BubbleMetadata bubbleMetadata = entry.getBubbleMetadata();
            PendingIntent deleteIntent = bubbleMetadata != null
+17 −12
Original line number Diff line number Diff line
@@ -74,8 +74,10 @@ internal class BubbleDataRepository @Inject constructor(

    private fun transform(userId: Int, bubbles: List<Bubble>): List<BubbleEntity> {
        return bubbles.mapNotNull { b ->
            val shortcutId = b.shortcutInfo?.id ?: return@mapNotNull null
            BubbleEntity(userId, b.packageName, shortcutId)
            var shortcutId = b.shortcutInfo?.id
            if (shortcutId == null) shortcutId = b.entry?.bubbleMetadata?.shortcutId
            if (shortcutId == null) return@mapNotNull null
            BubbleEntity(userId, b.packageName, shortcutId, b.key)
        }
    }

@@ -108,7 +110,6 @@ 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 {
        /**
@@ -132,17 +133,17 @@ internal class BubbleDataRepository @Inject constructor(
        val shortcutKeys = entities.map { ShortcutKey(it.userId, it.packageName) }.toSet()
        /**
         * Retrieve shortcuts with given userId/packageName combination, then construct a mapping
         * between BubbleEntity and ShortcutInfo.
         * from the userId/packageName pair to a list of associated ShortcutInfo.
         * e.g.
         * {
         *     BubbleEntity(0, "com.example.messenger", "id-0") ->
         *     ShortcutKey(0, "com.example.messenger") -> [
         *         ShortcutInfo(userId=0, pkg="com.example.messenger", id="id-0"),
         *     BubbleEntity(0, "com.example.messenger", "id-2") ->
         *         ShortcutInfo(userId=0, pkg="com.example.messenger", id="id-2"),
         *     BubbleEntity(10, "com.example.chat", "id-1") ->
         *         ShortcutInfo(userId=0, pkg="com.example.messenger", id="id-2")
         *     ]
         *     ShortcutKey(10, "com.example.chat") -> [
         *         ShortcutInfo(userId=10, pkg="com.example.chat", id="id-1"),
         *     BubbleEntity(10, "com.example.chat", "id-3") ->
         *         ShortcutInfo(userId=10, pkg="com.example.chat", id="id-3")
         *     ]
         * }
         */
        val shortcutMap = shortcutKeys.flatMap { key ->
@@ -150,11 +151,15 @@ internal class BubbleDataRepository @Inject constructor(
                    LauncherApps.ShortcutQuery()
                            .setPackage(key.pkg)
                            .setQueryFlags(SHORTCUT_QUERY_FLAG), UserHandle.of(key.userId))
                    ?.map { BubbleEntity(key.userId, key.pkg, it.id) to it } ?: emptyList()
        }.toMap()
                    ?: emptyList()
        }.groupBy { ShortcutKey(it.userId, it.`package`) }
        // For each entity loaded from xml, find the corresponding ShortcutInfo then convert them
        // into Bubble.
        val bubbles = entities.mapNotNull { entity -> shortcutMap[entity]?.let { Bubble(it) } }
        val bubbles = entities.mapNotNull { entity ->
            shortcutMap[ShortcutKey(entity.userId, entity.packageName)]
                    ?.first { shortcutInfo -> entity.shortcutId == shortcutInfo.id }
                    ?.let { shortcutInfo -> Bubble(entity.key, shortcutInfo) }
        }
        uiScope.launch { cb(bubbles) }
    }

+2 −7

File changed.

Preview size limit exceeded, changes collapsed.

Loading