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

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

Merge "ShortcutManager: Floating shortcuts shouldn't have target activities."

parents ad726872 106ff7a0
Loading
Loading
Loading
Loading
+10 −3
Original line number Diff line number Diff line
@@ -314,9 +314,11 @@ public final class ShortcutInfo implements Parcelable {
     *
     * @hide
     */
    public void enforceMandatoryFields() {
    public void enforceMandatoryFields(boolean forPinned) {
        Preconditions.checkStringNotEmpty(mId, "Shortcut ID must be provided");
        if (!forPinned) {
            Preconditions.checkNotNull(mActivity, "Activity must be provided");
        }
        if (mTitle == null && mTitleResId == 0) {
            throw new IllegalArgumentException("Short label must be provided");
        }
@@ -1055,6 +1057,11 @@ public final class ShortcutInfo implements Parcelable {
     * Launcher apps should show the launcher icon for the returned activity alongside
     * this shortcut.
     *
     * <p>When a shortcut is dynamic or manifest
     * (i.e. either {@link #isDynamic()} or {@link #isDeclaredInManifest()} returns {@code TRUE}),
     * then it should always have a non-null target activity.
     * Otherwise it will return null.
     *
     * @see Builder#setActivity
     */
    @Nullable
@@ -1361,7 +1368,7 @@ public final class ShortcutInfo implements Parcelable {
    }

    /**
     * @return true if pinned but neither static nor dynamic.
     * Return {@code TRUE} if a shortcut is pinned but neither manifest nor dynamic.
     * @hide
     */
    public boolean isFloating() {
+24 −5
Original line number Diff line number Diff line
@@ -259,6 +259,11 @@ class ShortcutPackage extends ShortcutPackageItem {
        for (int i = mShortcuts.size() - 1; i >= 0; i--) {
            final ShortcutInfo si = mShortcuts.valueAt(i);

            if (si.isFloating()) {
                si.setRank(0);
                si.setActivity(null);
            }

            if (si.isAlive()) continue;

            if (removeList == null) {
@@ -288,6 +293,7 @@ class ShortcutPackage extends ShortcutPackageItem {
                si.setTimestamp(now);
                si.clearFlags(ShortcutInfo.FLAG_DYNAMIC);
                si.setRank(0); // It may still be pinned, so clear the rank.
                si.setActivity(null);
            }
        }
        if (changed) {
@@ -355,6 +361,7 @@ class ShortcutPackage extends ShortcutPackageItem {
        if (oldShortcut.isPinned()) {

            oldShortcut.setRank(0);
            oldShortcut.setActivity(null);
            oldShortcut.clearFlags(ShortcutInfo.FLAG_DYNAMIC | ShortcutInfo.FLAG_MANIFEST);
            if (disable) {
                oldShortcut.addFlags(ShortcutInfo.FLAG_DISABLED);
@@ -595,6 +602,10 @@ class ShortcutPackage extends ShortcutPackageItem {

        for (int i = mShortcuts.size() - 1; i >= 0; i--) {
            final ShortcutInfo si = mShortcuts.valueAt(i);

            if (si.isFloating()) {
                continue; // Ignore floating shortcuts, which are not tied to any activities.
            }
            final ComponentName activity = si.getActivity();

            if (checked.contains(activity)) {
@@ -1356,6 +1367,10 @@ class ShortcutPackage extends ShortcutPackageItem {
                    case TAG_SHORTCUT:
                        final ShortcutInfo si = parseShortcut(parser, packageName,
                                shortcutUser.getUserId());
                        // Floating shortcut used to have target activities, but not anymore.
                        if (si.isFloating()) { // Not really needed by just in case.
                            si.setActivity(null);
                        }

                        // Don't use addShortcut(), we don't need to save the icon.
                        ret.mShortcuts.put(si.getId(), si);
@@ -1462,7 +1477,6 @@ class ShortcutPackage extends ShortcutPackageItem {
            intents.clear();
            intents.add(intentLegacy);
        }

        return new ShortcutInfo(
                userId, id, packageName, activityComponent, /* icon =*/ null,
                title, titleResId, titleResName, text, textResId, textResName,
@@ -1553,12 +1567,17 @@ class ShortcutPackage extends ShortcutPackageItem {
                Log.e(TAG_VERIFY, "Package " + getPackageName() + ": shortcut " + si.getId()
                        + " is both dynamic and manifest at the same time.");
            }
            if (si.getActivity() == null) {
            if (!si.isFloating() && si.getActivity() == null) {
                failed = true;
                Log.e(TAG_VERIFY, "Package " + getPackageName() + ": shortcut " + si.getId()
                        + " is not floating, but has null activity.");
            }
            if (si.isFloating() && si.getActivity() != null) {
                failed = true;
                Log.e(TAG_VERIFY, "Package " + getPackageName() + ": shortcut " + si.getId()
                        + " has null activity.");
                        + " is floating, but has non-null activity.");
            }
            if ((si.isDynamic() || si.isManifestShortcut()) && !si.isEnabled()) {
            if (!si.isFloating() && !si.isEnabled()) {
                failed = true;
                Log.e(TAG_VERIFY, "Package " + getPackageName() + ": shortcut " + si.getId()
                        + " is not floating, but is disabled.");
@@ -1581,7 +1600,7 @@ class ShortcutPackage extends ShortcutPackageItem {
        }

        if (failed) {
            throw new IllegalStateException("See logcat for errors");
            mShortcutUser.mService.verifyError();
        }
    }

+13 −3
Original line number Diff line number Diff line
@@ -124,6 +124,7 @@ import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Objects;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.function.Consumer;
import java.util.function.Predicate;
@@ -401,6 +402,9 @@ public class ShortcutService extends IShortcutService.Stub {

    @VisibleForTesting
    ShortcutService(Context context, Looper looper, boolean onlyForPackageManagerApis) {
        if (DEBUG) {
            Binder.LOG_RUNTIME_EXCEPTION = true;
        }
        mContext = Preconditions.checkNotNull(context);
        LocalServices.addService(ShortcutServiceInternal.class, new LocalService());
        mHandler = new Handler(looper);
@@ -1604,7 +1608,7 @@ public class ShortcutService extends IShortcutService.Stub {
        }

        if (!forUpdate) {
            shortcut.enforceMandatoryFields();
            shortcut.enforceMandatoryFields(/* forPinned= */ false);
            Preconditions.checkArgument(
                    injectIsMainActivity(shortcut.getActivity(), shortcut.getUserId()),
                    "Cannot publish shortcut: " + shortcut.getActivity() + " is not main activity");
@@ -1752,6 +1756,9 @@ public class ShortcutService extends IShortcutService.Stub {

                // Note copyNonNullFieldsFrom() does the "updatable with?" check too.
                target.copyNonNullFieldsFrom(source);
                if (target.isFloating()) {
                    target.setActivity(null);
                }
                target.setTimestamp(injectCurrentTimeMillis());

                if (replacingIcon) {
@@ -2320,8 +2327,7 @@ public class ShortcutService extends IShortcutService.Stub {
                            return false;
                        }
                        if (componentName != null) {
                            if (si.getActivity() != null
                                    && !si.getActivity().equals(componentName)) {
                            if (!Objects.equals(componentName, si.getActivity())) {
                                return false;
                            }
                        }
@@ -3771,4 +3777,8 @@ public class ShortcutService extends IShortcutService.Stub {
            forEachLoadedUserLocked(u -> u.forAllPackageItems(ShortcutPackageItem::verifyStates));
        }
    }

    void verifyError() {
        Slog.e(TAG, "See logcat for errors");
    }
}
+6 −0
Original line number Diff line number Diff line
@@ -376,6 +376,7 @@ public abstract class BaseShortcutManagerTest extends InstrumentationTestCase {

        @Override
        boolean injectIsActivityEnabledAndExported(ComponentName activity, @UserIdInt int userId) {
            assertNotNull(activity);
            return mEnabledActivityChecker.test(activity, userId);
        }

@@ -416,6 +417,11 @@ public abstract class BaseShortcutManagerTest extends InstrumentationTestCase {
            // During tests, WTF is fatal.
            fail(message + "  exception: " + th + "\n" + Log.getStackTraceString(th));
        }

        @Override
        void verifyError() {
            fail("Verify error");
        }
    }

    /** ShortcutManager with injection override methods. */
+37 −1
Original line number Diff line number Diff line
@@ -1292,7 +1292,43 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest {
                    /* activity =*/ null, /* flags */ 0), getCallingUser());
                });

        // TODO More tests: pinned but dynamic.
        // Make sure floating shortcuts don't match with an activity.
        // At this point, s1 is dynamic and pinned, so it still has a target activity.
        runWithCaller(LAUNCHER_1, USER_0, () -> {
            assertWith(mLauncherApps.getShortcuts(buildQuery(
                    /* time =*/ 0,
                    CALLING_PACKAGE_2,
                    /* activity =*/ new ComponentName(CALLING_PACKAGE_2,
                            ShortcutActivity2.class.getName()),
                    ShortcutQuery.FLAG_GET_PINNED),
                    getCallingUser()))
                    .haveIds("s3")
                    .areAllPinned()
                    .areAllDynamic()
                    .areAllWithActivity(new ComponentName(CALLING_PACKAGE_2,
                            ShortcutActivity2.class.getName()));
        });

        // Now remove as a dynamic, making it floating.
        runWithCaller(CALLING_PACKAGE_2, USER_0, () -> {
            mManager.removeDynamicShortcuts(list("s3"));
            assertWith(mManager.getPinnedShortcuts())
                    .selectFloating()
                    .areAllWithNoActivity();
        });

        runWithCaller(LAUNCHER_1, USER_0, () -> {
            // This shouldn't match now.
            assertWith(mLauncherApps.getShortcuts(buildQuery(
                    /* time =*/ 0,
                    CALLING_PACKAGE_2,
                    /* activity =*/ new ComponentName(CALLING_PACKAGE_2,
                            ShortcutActivity2.class.getName()),
                    ShortcutQuery.FLAG_GET_PINNED),
                    getCallingUser()))
                    .isEmpty();
        });

    }

    public void testGetShortcuts_shortcutKinds() throws Exception {
Loading