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

Commit 00276585 authored by Pinyao Ting's avatar Pinyao Ting Committed by Android (Google) Code Review
Browse files

Merge "Include new api to exlude a shortcut from launcher"

parents d17933a7 338430ce
Loading
Loading
Loading
Loading
+3 −0
Original line number Diff line number Diff line
@@ -13385,6 +13385,7 @@ package android.content.pm {
    method public boolean isDynamic();
    method public boolean isEnabled();
    method public boolean isImmutable();
    method public boolean isIncludedIn(int);
    method public boolean isPinned();
    method public void writeToParcel(android.os.Parcel, int);
    field @NonNull public static final android.os.Parcelable.Creator<android.content.pm.ShortcutInfo> CREATOR;
@@ -13397,6 +13398,7 @@ package android.content.pm {
    field public static final int DISABLED_REASON_UNKNOWN = 3; // 0x3
    field public static final int DISABLED_REASON_VERSION_LOWER = 100; // 0x64
    field public static final String SHORTCUT_CATEGORY_CONVERSATION = "android.shortcut.conversation";
    field public static final int SURFACE_LAUNCHER = 1; // 0x1
  }
  public static class ShortcutInfo.Builder {
@@ -13405,6 +13407,7 @@ package android.content.pm {
    method @NonNull public android.content.pm.ShortcutInfo.Builder setActivity(@NonNull android.content.ComponentName);
    method @NonNull public android.content.pm.ShortcutInfo.Builder setCategories(java.util.Set<java.lang.String>);
    method @NonNull public android.content.pm.ShortcutInfo.Builder setDisabledMessage(@NonNull CharSequence);
    method @NonNull public android.content.pm.ShortcutInfo.Builder setExcludedFromSurfaces(int);
    method @NonNull public android.content.pm.ShortcutInfo.Builder setExtras(@NonNull android.os.PersistableBundle);
    method @NonNull public android.content.pm.ShortcutInfo.Builder setIcon(android.graphics.drawable.Icon);
    method @NonNull public android.content.pm.ShortcutInfo.Builder setIntent(@NonNull android.content.Intent);
+44 −0
Original line number Diff line number Diff line
@@ -352,6 +352,16 @@ public final class ShortcutInfo implements Parcelable {
        return disabledReason >= DISABLED_REASON_RESTORE_ISSUE_START;
    }

    /** @hide */
    @IntDef(flag = true, value = {SURFACE_LAUNCHER})
    @Retention(RetentionPolicy.SOURCE)
    public @interface Surface {}

    /**
     * Indicates system surfaces managed by a launcher app. e.g. Long-Press Menu.
     */
    public static final int SURFACE_LAUNCHER = 1 << 0;

    /**
     * Shortcut category for messaging related actions, such as chat.
     */
@@ -451,6 +461,8 @@ public final class ShortcutInfo implements Parcelable {

    @Nullable private String mStartingThemeResName;

    private int mExcludedSurfaces;

    private ShortcutInfo(Builder b) {
        mUserId = b.mContext.getUserId();

@@ -474,6 +486,7 @@ public final class ShortcutInfo implements Parcelable {
        if (b.mIsLongLived) {
            setLongLived();
        }
        mExcludedSurfaces = b.mExcludedSurfaces;
        mRank = b.mRank;
        mExtras = b.mExtras;
        mLocusId = b.mLocusId;
@@ -587,6 +600,7 @@ public final class ShortcutInfo implements Parcelable {
        mLastChangedTimestamp = source.mLastChangedTimestamp;
        mDisabledReason = source.mDisabledReason;
        mLocusId = source.mLocusId;
        mExcludedSurfaces = source.mExcludedSurfaces;

        // Just always keep it since it's cheep.
        mIconResId = source.mIconResId;
@@ -1025,6 +1039,8 @@ public final class ShortcutInfo implements Parcelable {

        private int mStartingThemeResId;

        private int mExcludedSurfaces;

        /**
         * Old style constructor.
         * @hide
@@ -1384,6 +1400,22 @@ public final class ShortcutInfo implements Parcelable {
            return this;
        }

        /**
         * Sets which surfaces a shortcut will be excluded from.
         *
         * If the shortcut is set to be excluded from {@link #SURFACE_LAUNCHER}, shortcuts will be
         * excluded from the search result of {@link android.content.pm.LauncherApps#getShortcuts(
         * android.content.pm.LauncherApps.ShortcutQuery, UserHandle)} nor
         * {@link android.content.pm.ShortcutManager#getShortcuts(int)}. This generally means the
         * shortcut would not be displayed by a launcher app (e.g. in Long-Press menu), while
         * remain visible in other surfaces such as assistant or on-device-intelligence.
         */
        @NonNull
        public Builder setExcludedFromSurfaces(final int surfaces) {
            mExcludedSurfaces = surfaces;
            return this;
        }

        /**
         * Creates a {@link ShortcutInfo} instance.
         */
@@ -2137,6 +2169,13 @@ public final class ShortcutInfo implements Parcelable {
        mCategories = cloneCategories(categories);
    }

    /**
     * Return true if the shortcut is included in specified surface.
     */
    public boolean isIncludedIn(@Surface int surface) {
        return (mExcludedSurfaces & surface) == 0;
    }

    private ShortcutInfo(Parcel source) {
        final ClassLoader cl = getClass().getClassLoader();

@@ -2185,6 +2224,7 @@ public final class ShortcutInfo implements Parcelable {
        mLocusId = source.readParcelable(cl);
        mIconUri = source.readString8();
        mStartingThemeResName = source.readString8();
        mExcludedSurfaces = source.readInt();
    }

    @Override
@@ -2237,6 +2277,7 @@ public final class ShortcutInfo implements Parcelable {
        dest.writeParcelable(mLocusId, flags);
        dest.writeString8(mIconUri);
        dest.writeString8(mStartingThemeResName);
        dest.writeInt(mExcludedSurfaces);
    }

    public static final @NonNull Creator<ShortcutInfo> CREATOR =
@@ -2346,6 +2387,9 @@ public final class ShortcutInfo implements Parcelable {
        if (isLongLived()) {
            sb.append("Liv");
        }
        if (!isIncludedIn(SURFACE_LAUNCHER)) {
            sb.append("Hid-L");
        }
        sb.append("]");

        addIndentOrComma(sb, indent);
+49 −28
Original line number Diff line number Diff line
@@ -96,8 +96,6 @@ import java.util.stream.Collectors;
/**
 * Package information used by {@link ShortcutService}.
 * User information used by {@link ShortcutService}.
 *
 * All methods should be guarded by {@code #mShortcutUser.mService.mLock}.
 */
class ShortcutPackage extends ShortcutPackageItem {
    private static final String TAG = ShortcutService.TAG;
@@ -162,10 +160,18 @@ class ShortcutPackage extends ShortcutPackageItem {
    private final Executor mExecutor;

    /**
     * An temp in-memory copy of shortcuts for this package that was loaded from xml, keyed on IDs.
     * An in-memory copy of shortcuts for this package that was loaded from xml, keyed on IDs.
     */
    @GuardedBy("mLock")
    final ArrayMap<String, ShortcutInfo> mShortcuts = new ArrayMap<>();

    /**
     * A temporary copy of shortcuts that are to be cleared once persisted into AppSearch, keyed on
     * IDs.
     */
    @GuardedBy("mLock")
    private ArrayMap<String, ShortcutInfo> mTransientShortcuts = new ArrayMap<>(0);

    /**
     * All the share targets from the package
     */
@@ -330,6 +336,15 @@ class ShortcutPackage extends ShortcutPackageItem {
        }
    }

    public void ensureAllShortcutsVisibleToLauncher(@NonNull List<ShortcutInfo> shortcuts) {
        for (ShortcutInfo shortcut : shortcuts) {
            if (!shortcut.isIncludedIn(ShortcutInfo.SURFACE_LAUNCHER)) {
                throw new IllegalArgumentException("Shortcut ID=" + shortcut.getId()
                        + " is hidden from launcher and may not be manipulated via APIs");
            }
        }
    }

    /**
     * Delete a shortcut by ID. This will *always* remove it even if it's immutable or invisible.
     */
@@ -384,7 +399,15 @@ class ShortcutPackage extends ShortcutPackageItem {
                    & (ShortcutInfo.FLAG_PINNED | ShortcutInfo.FLAG_CACHED_ALL));
        }

        if (!newShortcut.isIncludedIn(ShortcutInfo.SURFACE_LAUNCHER)) {
            if (isAppSearchEnabled()) {
                synchronized (mLock) {
                    mTransientShortcuts.put(newShortcut.getId(), newShortcut);
                }
            }
        } else {
            forceReplaceShortcutInner(newShortcut);
        }
        return oldShortcut != null;
    }

@@ -444,7 +467,15 @@ class ShortcutPackage extends ShortcutPackageItem {
                    & (ShortcutInfo.FLAG_PINNED | ShortcutInfo.FLAG_CACHED_ALL));
        }

        if (!newShortcut.isIncludedIn(ShortcutInfo.SURFACE_LAUNCHER)) {
            if (isAppSearchEnabled()) {
                synchronized (mLock) {
                    mTransientShortcuts.put(newShortcut.getId(), newShortcut);
                }
            }
        } else {
            forceReplaceShortcutInner(newShortcut);
        }
        if (isAppSearchEnabled()) {
            runAsSystem(() -> fromAppSearch().thenAccept(session ->
                    session.reportUsage(new ReportUsageRequest.Builder(
@@ -669,7 +700,6 @@ class ShortcutPackage extends ShortcutPackageItem {
        forEachShortcutMutate(si -> {
            if (!pinnedShortcuts.contains(si.getId()) && si.isPinned()) {
                si.clearFlags(ShortcutInfo.FLAG_PINNED);
                return;
            }
        });

@@ -1704,8 +1734,15 @@ class ShortcutPackage extends ShortcutPackageItem {
            for (int j = 0; j < shareTargetSize; j++) {
                mShareTargets.get(j).saveToXml(out);
            }
            saveShortcutsAsync(mShortcuts.values().stream().filter(ShortcutInfo::usesQuota)
                    .collect(Collectors.toList()));
            synchronized (mLock) {
                final Map<String, ShortcutInfo> copy = mShortcuts;
                if (!mTransientShortcuts.isEmpty()) {
                    copy.putAll(mTransientShortcuts);
                    mTransientShortcuts.clear();
                }
                saveShortcutsAsync(copy.values().stream().filter(ShortcutInfo::usesQuota).collect(
                        Collectors.toList()));
            }
        }

        out.endTag(null, TAG_ROOT);
@@ -2233,26 +2270,6 @@ class ShortcutPackage extends ShortcutPackageItem {
        }
    }

    void updateVisibility(String packageName, byte[] certificate, boolean visible) {
        if (!isAppSearchEnabled()) {
            return;
        }
        if (visible) {
            mPackageIdentifiers.put(packageName, new PackageIdentifier(packageName, certificate));
        } else {
            mPackageIdentifiers.remove(packageName);
        }
        synchronized (mLock) {
            mIsAppSearchSchemaUpToDate = false;
        }
        final long callingIdentity = Binder.clearCallingIdentity();
        try {
            fromAppSearch();
        } finally {
            Binder.restoreCallingIdentity(callingIdentity);
        }
    }

    void mutateShortcut(@NonNull final String id, @Nullable final ShortcutInfo shortcut,
            @NonNull final Consumer<ShortcutInfo> transform) {
        Objects.requireNonNull(id);
@@ -2358,6 +2375,7 @@ class ShortcutPackage extends ShortcutPackageItem {
                .addFilterSchemas(AppSearchShortcutInfo.SCHEMA_TYPE)
                .addFilterNamespaces(getPackageName())
                .setTermMatch(SearchSpec.TERM_MATCH_EXACT_ONLY)
                .setResultCountPerPage(mShortcutUser.mService.getMaxActivityShortcuts())
                .build();
    }

@@ -2451,6 +2469,9 @@ class ShortcutPackage extends ShortcutPackageItem {

    @VisibleForTesting
    void getTopShortcutsFromPersistence(AndroidFuture<List<ShortcutInfo>> cb) {
        if (!isAppSearchEnabled()) {
            cb.complete(null);
        }
        runAsSystem(() -> fromAppSearch().thenAccept(session -> {
            SearchResults res = session.search("", getSearchSpec());
            res.getNextPage(mShortcutUser.mExecutor, results -> {
+4 −0
Original line number Diff line number Diff line
@@ -2014,6 +2014,7 @@ public class ShortcutService extends IShortcutService.Stub {

            ps.ensureImmutableShortcutsNotIncluded(newShortcuts, /*ignoreInvisible=*/ true);
            ps.ensureNoBitmapIconIfShortcutIsLongLived(newShortcuts);
            ps.ensureAllShortcutsVisibleToLauncher(newShortcuts);

            // For update, don't fill in the default activity.  Having null activity means
            // "don't update the activity" here.
@@ -2214,6 +2215,9 @@ public class ShortcutService extends IShortcutService.Stub {
            IntentSender resultIntent, int userId, AndroidFuture<String> ret) {
        Objects.requireNonNull(shortcut);
        Preconditions.checkArgument(shortcut.isEnabled(), "Shortcut must be enabled");
        Preconditions.checkArgument(
                shortcut.isIncludedIn(ShortcutInfo.SURFACE_LAUNCHER),
                "Shortcut excluded from launcher cannot be pinned");
        ret.complete(String.valueOf(requestPinItem(
                packageName, userId, shortcut, null, null, resultIntent)));
    }
+26 −0
Original line number Diff line number Diff line
@@ -1500,6 +1500,20 @@ public abstract class BaseShortcutManagerTest extends InstrumentationTestCase {
                makeIntent(Intent.ACTION_VIEW, ShortcutActivity.class), /* rank =*/ 0);
    }

    /**
     * Make a hidden shortcut with an ID.
     */
    protected ShortcutInfo makeShortcutExcludedFromLauncher(String id) {
        final ShortcutInfo.Builder  b = new ShortcutInfo.Builder(mClientContext, id)
                .setActivity(new ComponentName(mClientContext.getPackageName(), "main"))
                .setShortLabel("Title-" + id)
                .setIntent(makeIntent(Intent.ACTION_VIEW, ShortcutActivity.class))
                .setExcludedFromSurfaces(ShortcutInfo.SURFACE_LAUNCHER);
        final ShortcutInfo s = b.build();
        s.setTimestamp(mInjectedCurrentTimeMillis);
        return s;
    }

    @Deprecated // Title was renamed to short label.
    protected ShortcutInfo makeShortcutWithTitle(String id, String title) {
        return makeShortcut(
@@ -1889,6 +1903,18 @@ public abstract class BaseShortcutManagerTest extends InstrumentationTestCase {
        assertEquals("Exception type different", expectedException, thrown.getClass());
    }

    protected void assertThrown(@NonNull final Class<?> expectedException,
            @NonNull final Runnable fn) {
        Exception thrown = null;
        try {
            fn.run();
        } catch (Exception e) {
            thrown = e;
        }
        assertNotNull("Exception was not thrown", thrown);
        assertEquals("Exception type different", expectedException, thrown.getClass());
    }

    protected void assertBitmapDirectories(int userId, String... expectedDirectories) {
        final Set<String> expected = hashSet(set(expectedDirectories));

Loading