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

Commit 0c280710 authored by Makoto Onuki's avatar Makoto Onuki
Browse files

requestPinShortcut for pinned shortcut still notifies launcher

Test: adb shell am instrument -e class com.android.server.pm.ShortcutManagerTest1 -w com.android.frameworks.servicestests
Test: adb shell am instrument -e class com.android.server.pm.ShortcutManagerTest2 -w com.android.frameworks.servicestests
Test: adb shell am instrument -e class com.android.server.pm.ShortcutManagerTest3 -w com.android.frameworks.servicestests
Test: adb shell am instrument -e class com.android.server.pm.ShortcutManagerTest4 -w com.android.frameworks.servicestests
Test: adb shell am instrument -e class com.android.server.pm.ShortcutManagerTest5 -w com.android.frameworks.servicestests
Test: adb shell am instrument -e class com.android.server.pm.ShortcutManagerTest6 -w com.android.frameworks.servicestests
Test: adb shell am instrument -e class com.android.server.pm.ShortcutManagerTest7 -w com.android.frameworks.servicestests
Test: adb shell am instrument -e class com.android.server.pm.ShortcutManagerTest8 -w com.android.frameworks.servicestests
Test: adb shell am instrument -e class com.android.server.pm.ShortcutManagerTest9 -w com.android.frameworks.servicestests
Test: adb shell am instrument -e class com.android.server.pm.ShortcutManagerTest10 -w com.android.frameworks.servicestests

Bug: 34391829
Change-Id: Iac652790f2aa02dab6afbe52f2e40be99543f325
parent 0ae1a7d9
Loading
Loading
Loading
Loading
+7 −0
Original line number Diff line number Diff line
@@ -1185,6 +1185,13 @@ public class LauncherApps {
     * Represents a "pin shortcut" request made by an app, which is sent with
     * an {@link #ACTION_CONFIRM_PIN_ITEM} intent to the default launcher app.
     *
     * <p>Note the launcher may receive a request to pin a shortcut that is already pinned, because
     * the user may actually want to have multiple icons of the same shortcut on the launcher.
     * The launcher can tell this case by calling {@link ShortcutInfo#isPinned()} on the shortcut
     * returned by {@link #getShortcutInfo()}.  In this case, calling {@link #accept()} is optional;
     * even if the launcher does not call it, the shortcut is already pinned.  Also in this case,
     * the {@code options} argument to {@link #accept(Bundle)} will be ignored.
     *
     * @see #EXTRA_PIN_ITEM_REQUEST
     * @see #getPinItemRequest(Intent)
     */
+27 −28
Original line number Diff line number Diff line
@@ -203,19 +203,13 @@ class ShortcutRequestPinProcessor {
        // Next, validate the incoming shortcut, etc.
        final PinItemRequest request;
        if (inShortcut != null) {
            request = requestPinShortcutLocked(inShortcut, resultIntent, confirmActivity,
                    true /* ignoreIfAlreadyPinned */);
            request = requestPinShortcutLocked(inShortcut, resultIntent, confirmActivity);
        } else {
            int launcherUid = mService.injectGetPackageUid(
                    confirmActivity.first.getPackageName(), launcherUserId);
            request = new PinItemRequest(inAppWidget,
                    new PinItemRequestInner(this, resultIntent, launcherUid));
        }

        if (request == null) {
            sendResultIntent(resultIntent, null);
            return true;
        }
        return startRequestConfirmActivity(confirmActivity.first, launcherUserId, request);
    }

@@ -238,23 +232,17 @@ class ShortcutRequestPinProcessor {
        mService.throwIfUserLockedL(launcherUserId);

        // Next, validate the incoming shortcut, etc.
        PinItemRequest request = requestPinShortcutLocked(inShortcut, null,
                Pair.create(defaultLauncher, launcherUserId), false /* ignoreIfAlreadyPinned */);
        if (request == null) {
            return null;
        }
        final PinItemRequest request = requestPinShortcutLocked(inShortcut, null,
                Pair.create(defaultLauncher, launcherUserId));
        return new Intent().putExtra(LauncherApps.EXTRA_PIN_ITEM_REQUEST, request);
    }

    /**
     * Handle {@link android.content.pm.ShortcutManager#requestPinShortcut)}.
     *
     * @param ignoreIfAlreadyPinned if true and the {@param inShortcut} is already pinned for
     *                              {@param confirmActivity}, null is returned instead.
     */
    @NonNull
    private PinItemRequest requestPinShortcutLocked(ShortcutInfo inShortcut,
            IntentSender resultIntent, Pair<ComponentName, Integer> confirmActivity,
            boolean ignoreIfAlreadyPinned) {
            IntentSender resultIntentOriginal, Pair<ComponentName, Integer> confirmActivity) {
        final ShortcutPackage ps = mService.getPackageShortcutsForPublisherLocked(
                inShortcut.getPackage(), inShortcut.getUserId());

@@ -272,16 +260,20 @@ class ShortcutRequestPinProcessor {
        final String launcherPackage = confirmActivity.first.getPackageName();
        final int launcherUserId = confirmActivity.second;

        IntentSender resultIntentToSend = resultIntentOriginal;

        if (existsAlready) {
            validateExistingShortcut(existing);

            final boolean isAlreadyPinned = mService.getLauncherShortcutsLocked(
                    launcherPackage, existing.getUserId(), launcherUserId).hasPinned(existing);
            // See if it's already pinned.
            if (ignoreIfAlreadyPinned && isAlreadyPinned) {
                Log.i(TAG, "Launcher's already pinning shortcut " + existing.getId()
                        + " for package " + existing.getPackage());
                return null;
            if (isAlreadyPinned) {
                // When the shortcut is already pinned by this launcher, the request will always
                // succeed, so just send the result at this point.
                sendResultIntent(resultIntentOriginal, null);

                // So, do not send the intent again.
                resultIntentToSend = null;
            }

            // Pass a clone, not the original.
@@ -289,7 +281,7 @@ class ShortcutRequestPinProcessor {
            shortcutForLauncher = existing.clone(ShortcutInfo.CLONE_REMOVE_FOR_LAUNCHER);

            if (!isAlreadyPinned) {
                // FLAG_PINNED is still set, if it's pinned by other launchers.
                // FLAG_PINNED may still be set, if it's pinned by other launchers.
                shortcutForLauncher.clearFlags(ShortcutInfo.FLAG_PINNED);
            }
        } else {
@@ -320,8 +312,8 @@ class ShortcutRequestPinProcessor {

        // Create a request object.
        final PinShortcutRequestInner inner =
                new PinShortcutRequestInner(this, inShortcut, shortcutForLauncher, resultIntent,
                        launcherPackage, launcherUserId,
                new PinShortcutRequestInner(this, inShortcut, shortcutForLauncher,
                        resultIntentToSend, launcherPackage, launcherUserId,
                        mService.injectGetPackageUid(launcherPackage, launcherUserId),
                        existsAlready);

@@ -410,6 +402,16 @@ class ShortcutRequestPinProcessor {
                return false;
            }

            final ShortcutLauncher launcher = mService.getLauncherShortcutsLocked(
                    launcherPackage, appUserId, launcherUserId);
            launcher.attemptToRestoreIfNeededAndSave();
            if (launcher.hasPinned(original)) {
                if (DEBUG) {
                    Slog.d(TAG, "Shortcut " + original + " already pinned.");
                }
                return true;
            }

            final ShortcutPackage ps = mService.getPackageShortcutsForPublisherLocked(
                    appPackageName, appUserId);
            final ShortcutInfo current = ps.findShortcutById(shortcutId);
@@ -447,9 +449,6 @@ class ShortcutRequestPinProcessor {
                Slog.d(TAG, "Pinning " + shortcutId);
            }

            final ShortcutLauncher launcher = mService.getLauncherShortcutsLocked(
                    launcherPackage, appUserId, launcherUserId);
            launcher.attemptToRestoreIfNeededAndSave();
            launcher.addPinnedShortcut(appPackageName, appUserId, shortcutId);

            if (current == null) {
+132 −15
Original line number Diff line number Diff line
@@ -22,6 +22,9 @@ import static com.android.server.pm.shortcutmanagertest.ShortcutManagerTestUtils

import static org.mockito.Matchers.any;
import static org.mockito.Matchers.eq;
import static org.mockito.Matchers.isNull;
import static org.mockito.Matchers.notNull;
import static org.mockito.Mockito.reset;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;

@@ -238,7 +241,7 @@ public class ShortcutManagerTest8 extends BaseShortcutManagerTest {
     * - Launcher supports the feature.
     * - Shortcut doesn't pre-exist.
     */
    private void checkRequestPinShortcut(@Nullable PendingIntent resultIntent) {
    private void checkRequestPinShortcut(@Nullable IntentSender resultIntent) {
        setDefaultLauncher(USER_0, mMainActivityFetcher.apply(LAUNCHER_1, USER_0));
        setDefaultLauncher(USER_10, mMainActivityFetcher.apply(LAUNCHER_2, USER_10));

@@ -254,8 +257,7 @@ public class ShortcutManagerTest8 extends BaseShortcutManagerTest {

            assertNull(s.getActivity());

            assertTrue(mManager.requestPinShortcut(s,
                    resultIntent == null ? null : resultIntent.getIntentSender()));
            assertTrue(mManager.requestPinShortcut(s, resultIntent));

            verify(mServiceContext, times(0)).sendIntentSender(any(IntentSender.class));

@@ -294,9 +296,9 @@ public class ShortcutManagerTest8 extends BaseShortcutManagerTest {

        // This method is always called, even with PI == null.
        if (resultIntent == null) {
            verify(mServiceContext, times(1)).sendIntentSender(eq(null));
            verify(mServiceContext, times(1)).sendIntentSender(isNull(IntentSender.class));
        } else {
            verify(mServiceContext, times(1)).sendIntentSender(any(IntentSender.class));
            verify(mServiceContext, times(1)).sendIntentSender(notNull(IntentSender.class));
        }

        runWithCaller(CALLING_PACKAGE_1, USER_P0, () -> {
@@ -314,11 +316,12 @@ public class ShortcutManagerTest8 extends BaseShortcutManagerTest {
        checkRequestPinShortcut(/* resultIntent=*/ null);
    }

    public void testRequestPinShortcut_withCallback() {
        final PendingIntent resultIntent =
                PendingIntent.getActivity(getTestContext(), 0, new Intent(), 0);
    private IntentSender makeResultIntent() {
        return PendingIntent.getActivity(getTestContext(), 0, new Intent(), 0).getIntentSender();
    }

        checkRequestPinShortcut(resultIntent);
    public void testRequestPinShortcut_withCallback() {
        checkRequestPinShortcut(makeResultIntent());
    }

    public void testRequestPinShortcut_explicitTargetActivity() {
@@ -578,8 +581,15 @@ public class ShortcutManagerTest8 extends BaseShortcutManagerTest {
    public void testRequestPinShortcut_dynamicExists_alreadyPinned() {
        setDefaultLauncher(USER_0, mMainActivityFetcher.apply(LAUNCHER_1, USER_0));

        final Icon res32x32 = Icon.createWithResource(getTestContext(), R.drawable.black_32x32);

        runWithCaller(CALLING_PACKAGE_1, USER_P0, () -> {
            assertTrue(mManager.setDynamicShortcuts(list(makeShortcut("s1"))));
            final ShortcutInfo.Builder  b = new ShortcutInfo.Builder(mClientContext, "s1")
                    .setShortLabel("Title-" + "s1")
                    .setIcon(res32x32)
                    .setIntent(makeIntent(Intent.ACTION_VIEW, ShortcutActivity.class));
            final ShortcutInfo s = b.build();
            assertTrue(mManager.setDynamicShortcuts(list(s)));
        });

        runWithCaller(LAUNCHER_1, USER_0, () -> {
@@ -590,14 +600,66 @@ public class ShortcutManagerTest8 extends BaseShortcutManagerTest {
            assertWith(getCallerShortcuts())
                    .haveIds("s1")
                    .areAllDynamic()
                    .areAllWithActivity(new ComponentName(CALLING_PACKAGE_1, "main"))
                    .areAllWithActivity(new ComponentName(CALLING_PACKAGE_1, "MainActivity"))
                    .areAllPinned();

            assertTrue(mManager.requestPinShortcut(makeShortcutIdOnly("s1"),
                    /* resultIntent=*/ null));
                    makeResultIntent()));

            // The intent should be sent right away.
            verify(mServiceContext, times(1)).sendIntentSender(any(IntentSender.class));
            verify(mServiceContext, times(1)).sendIntentSender(notNull(IntentSender.class));
        });

        // Already pinned.
        runWithCaller(CALLING_PACKAGE_1, USER_P0, () -> {
            assertWith(getCallerShortcuts())
                    .haveIds("s1")
                    .areAllDynamic()
                    .areAllEnabled()
                    .areAllWithActivity(new ComponentName(CALLING_PACKAGE_1, "MainActivity"))
                    .areAllPinned();
        });

        // ... But the launcher will still receive the request.
        runWithCaller(LAUNCHER_1, USER_0, () -> {
            // Check the intent passed to startActivityAsUser().
            final ArgumentCaptor<Intent> intent = ArgumentCaptor.forClass(Intent.class);

            verify(mServiceContext).startActivityAsUser(intent.capture(), eq(HANDLE_USER_0));

            assertPinItemRequestIntent(intent.getValue(), mInjectedClientPackage);

            // Check the request object.
            final PinItemRequest request = mLauncherApps.getPinItemRequest(intent.getValue());

            assertPinItemRequest(request);

            assertWith(request.getShortcutInfo())
                    .haveIds("s1")
                    .areAllDynamic()
                    .areAllPinned() // Note it's pinned already.
                    .areAllWithActivity(new ComponentName(CALLING_PACKAGE_1, "MainActivity"))
                    .areAllWithNoIntent();

            assertAllHaveIcon(list(request.getShortcutInfo()));

            reset(mServiceContext);

            // Accept the request.
            assertTrue(request.accept());

            // The intent is only sent once, so times(1).
            verify(mServiceContext, times(1)).sendIntentSender(isNull(IntentSender.class));
        });

        // Still pinned.
        runWithCaller(CALLING_PACKAGE_1, USER_P0, () -> {
            assertWith(getCallerShortcuts())
                    .haveIds("s1")
                    .areAllDynamic()
                    .areAllEnabled()
                    .areAllWithActivity(new ComponentName(CALLING_PACKAGE_1, "MainActivity"))
                    .areAllPinned();
        });
    }

@@ -621,10 +683,65 @@ public class ShortcutManagerTest8 extends BaseShortcutManagerTest {
                    .areAllPinned();

            assertTrue(mManager.requestPinShortcut(makeShortcutIdOnly("ms1"),
                    /* resultIntent=*/ null));
                    makeResultIntent()));

            // The intent should be sent right away.
            verify(mServiceContext, times(1)).sendIntentSender(any(IntentSender.class));
            verify(mServiceContext, times(1)).sendIntentSender(notNull(IntentSender.class));
        });

        // Already pinned.
        runWithCaller(CALLING_PACKAGE_1, USER_P0, () -> {
            assertWith(getCallerShortcuts())
                    .haveIds("ms1")
                    .areAllManifest()
                    .areAllEnabled()
                    .areAllWithActivity(new ComponentName(CALLING_PACKAGE_1,
                            ShortcutActivity.class.getName()))
                    .areAllPinned();
        });

        // ... But the launcher will still receive the request.
        runWithCaller(LAUNCHER_1, USER_0, () -> {
            // Check the intent passed to startActivityAsUser().
            final ArgumentCaptor<Intent> intent = ArgumentCaptor.forClass(Intent.class);

            verify(mServiceContext).startActivityAsUser(intent.capture(), eq(HANDLE_USER_0));

            assertPinItemRequestIntent(intent.getValue(), mInjectedClientPackage);

            // Check the request object.
            final PinItemRequest request = mLauncherApps.getPinItemRequest(intent.getValue());

            assertPinItemRequest(request);

            assertWith(request.getShortcutInfo())
                    .haveIds("ms1")
                    .areAllManifest()
                    .areAllPinned() // Note it's pinned already.
                    .areAllWithActivity(new ComponentName(CALLING_PACKAGE_1,
                            ShortcutActivity.class.getName()))
                    .areAllWithNoIntent();

            assertAllHaveIcon(list(request.getShortcutInfo()));

            reset(mServiceContext);

            // Accept the request.
            assertTrue(request.accept());

            // The intent is only sent once, so times(1).
            verify(mServiceContext, times(1)).sendIntentSender(isNull(IntentSender.class));
        });

        // Still pinned.
        runWithCaller(CALLING_PACKAGE_1, USER_P0, () -> {
            assertWith(getCallerShortcuts())
                    .haveIds("ms1")
                    .areAllManifest()
                    .areAllEnabled()
                    .areAllWithActivity(new ComponentName(CALLING_PACKAGE_1,
                            ShortcutActivity.class.getName()))
                    .areAllPinned();
        });
    }