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

Commit 444d6474 authored by Makoto Onuki's avatar Makoto Onuki Committed by Android (Google) Code Review
Browse files

Merge "requestPinShortcut for pinned shortcut still notifies launcher"

parents 27be0189 0c280710
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();
        });
    }