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

Commit 6d980605 authored by Mady Mellor's avatar Mady Mellor Committed by Android (Google) Code Review
Browse files

Merge changes I610545fd,I3c0d2677 into main

* changes:
  Update NoMan's ShortcutHelper to use ShortcutChangeCallback
  Add way to remove shortcut changed callbacks to shortcut service
parents 78deee1a 34326c14
Loading
Loading
Loading
Loading
+3 −0
Original line number Diff line number Diff line
@@ -91,6 +91,9 @@ public abstract class ShortcutServiceInternal {
    public abstract void addShortcutChangeCallback(
            @NonNull LauncherApps.ShortcutChangeCallback callback);

    public abstract void removeShortcutChangeCallback(
            @NonNull LauncherApps.ShortcutChangeCallback callback);

    public abstract int getShortcutIconResId(int launcherUserId, @NonNull String callingPackage,
            @NonNull String packageName, @NonNull String shortcutId, int userId);

+2 −4
Original line number Diff line number Diff line
@@ -8658,8 +8658,7 @@ public class NotificationManagerService extends SystemService {
                    mAttentionHelper.updateLightsLocked();
                    if (mShortcutHelper != null) {
                        mShortcutHelper.maybeListenForShortcutChangesForBubbles(r,
                                true /* isRemoved */,
                                mHandler);
                                true /* isRemoved */);
                    }
                } else {
                    if (notificationForceGrouping()) {
@@ -9116,8 +9115,7 @@ public class NotificationManagerService extends SystemService {
                    if (mShortcutHelper != null) {
                        mShortcutHelper.maybeListenForShortcutChangesForBubbles(r,
                                false /* isRemoved */,
                                mHandler);
                                false /* isRemoved */);
                    }
                    maybeRecordInterruptionLocked(r);
+74 −88
Original line number Diff line number Diff line
@@ -27,7 +27,6 @@ import android.content.pm.LauncherApps;
import android.content.pm.ShortcutInfo;
import android.content.pm.ShortcutServiceInternal;
import android.os.Binder;
import android.os.Handler;
import android.os.UserHandle;
import android.os.UserManager;
import android.text.TextUtils;
@@ -65,82 +64,31 @@ public class ShortcutHelper {
        void onShortcutRemoved(String key);
    }

    private final ShortcutListener mShortcutListener;
    private LauncherApps mLauncherAppsService;
    private ShortcutListener mShortcutListener;
    private ShortcutServiceInternal mShortcutServiceInternal;
    private UserManager mUserManager;

    // Key: packageName Value: <shortcutId, notifId>
    private HashMap<String, HashMap<String, String>> mActiveShortcutBubbles = new HashMap<>();
    private boolean mLauncherAppsCallbackRegistered;
    // Key: packageName|userId Value: <shortcutId, notifId>
    private final HashMap<String, HashMap<String, String>> mActiveShortcutBubbles = new HashMap<>();
    private boolean mShortcutChangedCallbackRegistered;

    // Bubbles can be created based on a shortcut, we need to listen for changes to
    // that shortcut so that we may update the bubble appropriately.
    private final LauncherApps.Callback mLauncherAppsCallback = new LauncherApps.Callback() {
        @Override
        public void onPackageRemoved(String packageName, UserHandle user) {
        }

        @Override
        public void onPackageAdded(String packageName, UserHandle user) {
        }

        @Override
        public void onPackageChanged(String packageName, UserHandle user) {
        }
    private final LauncherApps.ShortcutChangeCallback mShortcutChangeCallback =
            new LauncherApps.ShortcutChangeCallback() {

                @Override
        public void onPackagesAvailable(String[] packageNames, UserHandle user,
                boolean replacing) {
        }

        @Override
        public void onPackagesUnavailable(String[] packageNames, UserHandle user,
                boolean replacing) {
        }

        @Override
        public void onShortcutsChanged(@NonNull String packageName,
                public void onShortcutsAddedOrUpdated(@NonNull String packageName,
                        @NonNull List<ShortcutInfo> shortcuts, @NonNull UserHandle user) {
            HashMap<String, String> shortcutBubbles = mActiveShortcutBubbles.get(packageName);
            ArrayList<String> bubbleKeysToRemove = new ArrayList<>();
            if (shortcutBubbles != null) {
                // Copy to avoid a concurrent modification exception when we remove bubbles from
                // shortcutBubbles.
                final Set<String> shortcutIds = new HashSet<>(shortcutBubbles.keySet());

                // If we can't find one of our bubbles in the shortcut list, that bubble needs
                // to be removed.
                for (String shortcutId : shortcutIds) {
                    boolean foundShortcut = false;
                    for (int i = 0; i < shortcuts.size(); i++) {
                        if (shortcuts.get(i).getId().equals(shortcutId)) {
                            foundShortcut = true;
                            break;
                        }
                    }
                    if (!foundShortcut) {
                        bubbleKeysToRemove.add(shortcutBubbles.get(shortcutId));
                        shortcutBubbles.remove(shortcutId);
                        if (shortcutBubbles.isEmpty()) {
                            mActiveShortcutBubbles.remove(packageName);
                            if (mLauncherAppsCallbackRegistered
                                    && mActiveShortcutBubbles.isEmpty()) {
                                mLauncherAppsService.unregisterCallback(mLauncherAppsCallback);
                                mLauncherAppsCallbackRegistered = false;
                            }
                        }
                    }
                }
                }

            // Let NoMan know about the updates
            for (int i = 0; i < bubbleKeysToRemove.size(); i++) {
                // update flag bubble
                String bubbleKey = bubbleKeysToRemove.get(i);
                if (mShortcutListener != null) {
                    mShortcutListener.onShortcutRemoved(bubbleKey);
                }
                public void onShortcutsRemoved(@NonNull String packageName,
                        @NonNull List<ShortcutInfo> removedShortcuts, @NonNull UserHandle user) {
                    final String packageUserKey = getPackageUserKey(packageName, user);
                    if (mActiveShortcutBubbles.get(packageUserKey) == null) return;
                    for (ShortcutInfo info : removedShortcuts) {
                        onShortcutRemoved(packageUserKey, info.getId());
                    }
                }
            };
@@ -172,14 +120,14 @@ public class ShortcutHelper {
     * Returns whether the given shortcut info is a conversation shortcut.
     */
    public static boolean isConversationShortcut(
            ShortcutInfo shortcutInfo, ShortcutServiceInternal mShortcutServiceInternal,
            ShortcutInfo shortcutInfo, ShortcutServiceInternal shortcutServiceInternal,
            int callingUserId) {
        if (shortcutInfo == null || !shortcutInfo.isLongLived() || !shortcutInfo.isEnabled()) {
            return false;
        }
        // TODO (b/155016294) uncomment when sharing shortcuts are required
        /*
        mShortcutServiceInternal.isSharingShortcut(callingUserId, "android",
        shortcutServiceInternal.isSharingShortcut(callingUserId, "android",
                shortcutInfo.getPackage(), shortcutInfo.getId(), shortcutInfo.getUserId(),
                SHARING_FILTER);
         */
@@ -233,34 +181,30 @@ public class ShortcutHelper {
     *
     * @param r the notification record to check
     * @param removedNotification true if this notification is being removed
     * @param handler handler to register the callback with
     */
    void maybeListenForShortcutChangesForBubbles(NotificationRecord r,
            boolean removedNotification,
            Handler handler) {
            boolean removedNotification) {
        final String shortcutId = r.getNotification().getBubbleMetadata() != null
                ? r.getNotification().getBubbleMetadata().getShortcutId()
                : null;
        final String packageUserKey = getPackageUserKey(r.getSbn().getPackageName(), r.getUser());
        if (!removedNotification
                && !TextUtils.isEmpty(shortcutId)
                && r.getShortcutInfo() != null
                && r.getShortcutInfo().getId().equals(shortcutId)) {
            // Must track shortcut based bubbles in case the shortcut is removed
            HashMap<String, String> packageBubbles = mActiveShortcutBubbles.get(
                    r.getSbn().getPackageName());
                    packageUserKey);
            if (packageBubbles == null) {
                packageBubbles = new HashMap<>();
            }
            packageBubbles.put(shortcutId, r.getKey());
            mActiveShortcutBubbles.put(r.getSbn().getPackageName(), packageBubbles);
            if (!mLauncherAppsCallbackRegistered) {
                mLauncherAppsService.registerCallback(mLauncherAppsCallback, handler);
                mLauncherAppsCallbackRegistered = true;
            }
            mActiveShortcutBubbles.put(packageUserKey, packageBubbles);
            registerCallbackIfNeeded();
        } else {
            // No longer track shortcut
            HashMap<String, String> packageBubbles = mActiveShortcutBubbles.get(
                    r.getSbn().getPackageName());
                    packageUserKey);
            if (packageBubbles != null) {
                if (!TextUtils.isEmpty(shortcutId)) {
                    packageBubbles.remove(shortcutId);
@@ -278,20 +222,62 @@ public class ShortcutHelper {
                    }
                }
                if (packageBubbles.isEmpty()) {
                    mActiveShortcutBubbles.remove(r.getSbn().getPackageName());
                    mActiveShortcutBubbles.remove(packageUserKey);
                }
            }
            unregisterCallbackIfNeeded();
        }
    }

    private String getPackageUserKey(String packageName, UserHandle user) {
        return packageName + "|" + user.getIdentifier();
    }

    private void onShortcutRemoved(String packageUserKey, String shortcutId) {
        HashMap<String, String> shortcutBubbles = mActiveShortcutBubbles.get(packageUserKey);
        ArrayList<String> bubbleKeysToRemove = new ArrayList<>();
        if (shortcutBubbles != null) {
            if (shortcutBubbles.containsKey(shortcutId)) {
                bubbleKeysToRemove.add(shortcutBubbles.get(shortcutId));
                shortcutBubbles.remove(shortcutId);
                if (shortcutBubbles.isEmpty()) {
                    mActiveShortcutBubbles.remove(packageUserKey);
                    unregisterCallbackIfNeeded();
                }
            }
            if (mLauncherAppsCallbackRegistered && mActiveShortcutBubbles.isEmpty()) {
                mLauncherAppsService.unregisterCallback(mLauncherAppsCallback);
                mLauncherAppsCallbackRegistered = false;
            notifyNoMan(bubbleKeysToRemove);
        }
    }

    private void registerCallbackIfNeeded() {
        if (!mShortcutChangedCallbackRegistered) {
            mShortcutChangedCallbackRegistered = true;
            mShortcutServiceInternal.addShortcutChangeCallback(mShortcutChangeCallback);
        }
    }

    private void unregisterCallbackIfNeeded() {
        if (mShortcutChangedCallbackRegistered && mActiveShortcutBubbles.isEmpty()) {
            mShortcutServiceInternal.removeShortcutChangeCallback(mShortcutChangeCallback);
            mShortcutChangedCallbackRegistered = false;
        }
    }

    void destroy() {
        if (mLauncherAppsCallbackRegistered) {
            mLauncherAppsService.unregisterCallback(mLauncherAppsCallback);
            mLauncherAppsCallbackRegistered = false;
        if (mShortcutChangedCallbackRegistered) {
            mShortcutServiceInternal.removeShortcutChangeCallback(mShortcutChangeCallback);
            mShortcutChangedCallbackRegistered = false;
        }
    }

    private void notifyNoMan(List<String> bubbleKeysToRemove) {
        // Let NoMan know about the updates
        for (int i = 0; i < bubbleKeysToRemove.size(); i++) {
            // update flag bubble
            String bubbleKey = bubbleKeysToRemove.get(i);
            if (mShortcutListener != null) {
                mShortcutListener.onShortcutRemoved(bubbleKey);
            }
        }
    }
}
+8 −0
Original line number Diff line number Diff line
@@ -3442,6 +3442,14 @@ public class ShortcutService extends IShortcutService.Stub {
            }
        }

        @Override
        public void removeShortcutChangeCallback(
                @NonNull LauncherApps.ShortcutChangeCallback callback) {
            synchronized (mServiceLock) {
                mShortcutChangeCallbacks.remove(Objects.requireNonNull(callback));
            }
        }

        @Override
        public int getShortcutIconResId(int launcherUserId, @NonNull String callingPackage,
                @NonNull String packageName, @NonNull String shortcutId, int userId) {
+26 −17
Original line number Diff line number Diff line
@@ -89,7 +89,6 @@ import static android.os.UserManager.USER_TYPE_FULL_SECONDARY;
import static android.os.UserManager.USER_TYPE_PROFILE_CLONE;
import static android.os.UserManager.USER_TYPE_PROFILE_MANAGED;
import static android.os.UserManager.USER_TYPE_PROFILE_PRIVATE;
import static android.platform.test.flag.junit.SetFlagsRule.DefaultInitValueType.DEVICE_DEFAULT;
import static android.provider.Settings.Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS;
import static android.service.notification.Adjustment.KEY_CONTEXTUAL_ACTIONS;
import static android.service.notification.Adjustment.KEY_IMPORTANCE;
@@ -838,13 +837,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
        // Pretend the shortcut exists
        List<ShortcutInfo> shortcutInfos = new ArrayList<>();
        ShortcutInfo info = mock(ShortcutInfo.class);
        when(info.getPackage()).thenReturn(mPkg);
        when(info.getId()).thenReturn(VALID_CONVO_SHORTCUT_ID);
        when(info.getUserId()).thenReturn(USER_SYSTEM);
        when(info.isLongLived()).thenReturn(true);
        when(info.isEnabled()).thenReturn(true);
        shortcutInfos.add(info);
        shortcutInfos.add(createMockConvoShortcut());
        when(mLauncherApps.getShortcuts(any(), any())).thenReturn(shortcutInfos);
        when(mShortcutServiceInternal.isSharingShortcut(anyInt(), anyString(), anyString(),
                anyString(), anyInt(), any())).thenReturn(true);
@@ -11109,8 +11102,8 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
                BUBBLE_PREFERENCE_ALL /* app */,
                true /* channel */);
        ArgumentCaptor<LauncherApps.Callback> launcherAppsCallback =
                ArgumentCaptor.forClass(LauncherApps.Callback.class);
        ArgumentCaptor<LauncherApps.ShortcutChangeCallback> shortcutChangeCallback =
                ArgumentCaptor.forClass(LauncherApps.ShortcutChangeCallback.class);
        // Messaging notification with shortcut info
        Notification.BubbleMetadata metadata =
@@ -11131,7 +11124,8 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
        // Verify:
        // Make sure we register the callback for shortcut changes
        verify(mLauncherApps, times(1)).registerCallback(launcherAppsCallback.capture(), any());
        verify(mShortcutServiceInternal, times(1)).addShortcutChangeCallback(
                shortcutChangeCallback.capture());
        // yes allowed, yes messaging w/shortcut, yes bubble
        Notification notif = mService.getNotificationRecord(nr.getSbn().getKey()).getNotification();
@@ -11144,14 +11138,17 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
        // Test: Remove the shortcut
        when(mLauncherApps.getShortcuts(any(), any())).thenReturn(null);
        launcherAppsCallback.getValue().onShortcutsChanged(mPkg, emptyList(),
        ArrayList<ShortcutInfo> removedShortcuts = new ArrayList<>();
        removedShortcuts.add(createMockConvoShortcut());
        shortcutChangeCallback.getValue().onShortcutsRemoved(mPkg, removedShortcuts,
                UserHandle.getUserHandleForUid(mUid));
        waitForIdle();
        // Verify:
        // Make sure callback is unregistered
        verify(mLauncherApps, times(1)).unregisterCallback(launcherAppsCallback.getValue());
        verify(mShortcutServiceInternal, times(1)).removeShortcutChangeCallback(
                shortcutChangeCallback.getValue());
        // We're no longer a bubble
        NotificationRecord notif2 = mService.getNotificationRecord(
@@ -11169,8 +11166,8 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
                BUBBLE_PREFERENCE_ALL /* app */,
                true /* channel */);
        ArgumentCaptor<LauncherApps.Callback> launcherAppsCallback =
                ArgumentCaptor.forClass(LauncherApps.Callback.class);
        ArgumentCaptor<LauncherApps.ShortcutChangeCallback> shortcutChangeCallback =
                ArgumentCaptor.forClass(LauncherApps.ShortcutChangeCallback.class);
        // Messaging notification with shortcut info
        Notification.BubbleMetadata metadata = new Notification.BubbleMetadata.Builder(
@@ -11204,7 +11201,8 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
        // Verify:
        // Make sure we register the callback for shortcut changes
        verify(mLauncherApps, times(1)).registerCallback(launcherAppsCallback.capture(), any());
        verify(mShortcutServiceInternal, times(1)).addShortcutChangeCallback(
                shortcutChangeCallback.capture());
        // yes allowed, yes messaging w/shortcut, yes bubble
        Notification notif = mService.getNotificationRecord(nr.getSbn().getKey()).getNotification();
@@ -11223,7 +11221,8 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
        // Verify:
        // Make sure callback is unregistered
        verify(mLauncherApps, times(1)).unregisterCallback(launcherAppsCallback.getValue());
        verify(mShortcutServiceInternal, times(1)).removeShortcutChangeCallback(
                shortcutChangeCallback.getValue());
    }
    @Test
@@ -16263,4 +16262,14 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
        assertThat(r.getChannel().getId()).isEqualTo(NEWS_ID);
    }
    private ShortcutInfo createMockConvoShortcut() {
        ShortcutInfo info = mock(ShortcutInfo.class);
        when(info.getPackage()).thenReturn(mPkg);
        when(info.getId()).thenReturn(VALID_CONVO_SHORTCUT_ID);
        when(info.getUserId()).thenReturn(USER_SYSTEM);
        when(info.isLongLived()).thenReturn(true);
        when(info.isEnabled()).thenReturn(true);
        return info;
    }
}
Loading