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

Commit 1af9a589 authored by TreeHugger Robot's avatar TreeHugger Robot Committed by Android (Google) Code Review
Browse files

Merge "Shortcut: Only "main" activities can have shortcuts." into nyc-mr1-dev

parents 34010567 b08790c3
Loading
Loading
Loading
Loading
+19 −10
Original line number Diff line number Diff line
@@ -709,7 +709,7 @@ public final class ShortcutInfo implements Parcelable {
        @NonNull
        @Deprecated
        public Builder setId(@NonNull String id) {
            mId = Preconditions.checkStringNotEmpty(id, "id");
            mId = Preconditions.checkStringNotEmpty(id, "id cannot be empty");
            return this;
        }

@@ -721,14 +721,17 @@ public final class ShortcutInfo implements Parcelable {
         */
        public Builder(Context context, String id) {
            mContext = context;
            mId = Preconditions.checkStringNotEmpty(id, "id");
            mId = Preconditions.checkStringNotEmpty(id, "id cannot be empty");
        }

        /**
         * Sets the target activity. A shortcut will be shown with this activity on the launcher.
         *
         * <p>This is a mandatory field, unless it's passed to
         * {@link ShortcutManager#updateShortcuts(List)}.
         * <p>Only "main" activities -- i.e. ones with an intent filter for
         * {@link Intent#ACTION_MAIN} and {@link Intent#CATEGORY_LAUNCHER} can be target activities.
         *
         * <p>By default, the first main activity defined in the application manifest will be
         * the target.
         *
         * <p>The package name of the target activity must match the package name of the shortcut
         * publisher.
@@ -738,7 +741,7 @@ public final class ShortcutInfo implements Parcelable {
         */
        @NonNull
        public Builder setActivity(@NonNull ComponentName activity) {
            mActivity = Preconditions.checkNotNull(activity, "activity");
            mActivity = Preconditions.checkNotNull(activity, "activity cannot be null");
            return this;
        }

@@ -785,7 +788,7 @@ public final class ShortcutInfo implements Parcelable {
        @NonNull
        public Builder setShortLabel(@NonNull CharSequence shortLabel) {
            Preconditions.checkState(mTitleResId == 0, "shortLabelResId already set");
            mTitle = Preconditions.checkStringNotEmpty(shortLabel, "shortLabel");
            mTitle = Preconditions.checkStringNotEmpty(shortLabel, "shortLabel cannot be empty");
            return this;
        }

@@ -810,7 +813,7 @@ public final class ShortcutInfo implements Parcelable {
        @NonNull
        public Builder setLongLabel(@NonNull CharSequence longLabel) {
            Preconditions.checkState(mTextResId == 0, "longLabelResId already set");
            mText = Preconditions.checkStringNotEmpty(longLabel, "longLabel");
            mText = Preconditions.checkStringNotEmpty(longLabel, "longLabel cannot be empty");
            return this;
        }

@@ -854,7 +857,8 @@ public final class ShortcutInfo implements Parcelable {
            Preconditions.checkState(
                    mDisabledMessageResId == 0, "disabledMessageResId already set");
            mDisabledMessage =
                    Preconditions.checkStringNotEmpty(disabledMessage, "disabledMessage");
                    Preconditions.checkStringNotEmpty(disabledMessage,
                            "disabledMessage cannot be empty");
            return this;
        }

@@ -876,8 +880,8 @@ public final class ShortcutInfo implements Parcelable {
         */
        @NonNull
        public Builder setIntent(@NonNull Intent intent) {
            mIntent = Preconditions.checkNotNull(intent, "intent");
            Preconditions.checkNotNull(mIntent.getAction(), "Intent action must be set");
            mIntent = Preconditions.checkNotNull(intent, "intent cannot be null");
            Preconditions.checkNotNull(mIntent.getAction(), "intent's action must be set");
            return this;
        }

@@ -944,6 +948,11 @@ public final class ShortcutInfo implements Parcelable {
        return mActivity;
    }

    /** @hide */
    public void setActivity(ComponentName activity) {
        mActivity = activity;
    }

    /**
     * Icon.
     *
+38 −9
Original line number Diff line number Diff line
@@ -54,8 +54,6 @@ import java.util.function.Predicate;
 * User information used by {@link ShortcutService}.
 *
 * All methods should be guarded by {@code #mShortcutUser.mService.mLock}.
 *
 * TODO Max dynamic shortcuts cap should be per activity.
 */
class ShortcutPackage extends ShortcutPackageItem {
    private static final String TAG = ShortcutService.TAG;
@@ -321,9 +319,27 @@ class ShortcutPackage extends ShortcutPackageItem {
    /**
     * Remove a dynamic shortcut by ID.  It'll be removed from the dynamic set, but if the shortcut
     * is pinned, it'll remain as a pinned shortcut, and is still enabled.
     *
     * @return true if it's actually removed because it wasn't pinned, or false if it's still
     * pinned.
     */
    public boolean deleteDynamicWithId(@NonNull String shortcutId) {
        final ShortcutInfo removed = deleteOrDisableWithId(
                shortcutId, /* disable =*/ false, /* overrideImmutable=*/ false);
        return removed == null;
    }

    /**
     * Disable a dynamic shortcut by ID.  It'll be removed from the dynamic set, but if the shortcut
     * is pinned, it'll remain as a pinned shortcut, but will be disabled.
     *
     * @return true if it's actually removed because it wasn't pinned, or false if it's still
     * pinned.
     */
    public void deleteDynamicWithId(@NonNull String shortcutId) {
        deleteOrDisableWithId(shortcutId, /* disable =*/ false, /* overrideImmutable=*/ false);
    private boolean disableDynamicWithId(@NonNull String shortcutId) {
        final ShortcutInfo disabled = deleteOrDisableWithId(
                shortcutId, /* disable =*/ true, /* overrideImmutable=*/ false);
        return disabled == null;
    }

    /**
@@ -599,14 +615,14 @@ class ShortcutPackage extends ShortcutPackageItem {
     *
     * @return TRUE if any shortcuts have been changed.
     */
    public boolean handlePackageAddedOrUpdated(boolean isNewApp) {
    public boolean handlePackageAddedOrUpdated(boolean isNewApp, boolean forceRescan) {
        final PackageInfo pi = mShortcutUser.mService.getPackageInfo(
                getPackageName(), getPackageUserId());
        if (pi == null) {
            return false; // Shouldn't happen.
        }

        if (!isNewApp) {
        if (!isNewApp && !forceRescan) {
            // Make sure the version code or last update time has changed.
            // Otherwise, nothing to do.
            if (getPackageInfo().getVersionCode() >= pi.versionCode
@@ -649,12 +665,26 @@ class ShortcutPackage extends ShortcutPackageItem {
        boolean changed = false;

        // For existing shortcuts, update timestamps if they have any resources.
        // Also check if shortcuts' activities are still main activities.  Otherwise, disable them.
        if (!isNewApp) {
            Resources publisherRes = null;

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

                if (si.isDynamic()) {
                    if (!s.injectIsMainActivity(si.getActivity(), getPackageUserId())) {
                        Slog.w(TAG, String.format(
                                "%s is no longer main activity. Disabling shorcut %s.",
                                getPackageName(), si.getId()));
                        if (disableDynamicWithId(si.getId())) {
                            continue; // Actually removed.
                        }
                        // Still pinned, so fall-through and possibly update the resources.
                    }
                    changed = true;
                }

                if (si.hasAnyResources()) {
                    if (!si.isOriginallyFromManifest()) {
                        if (publisherRes == null) {
@@ -912,9 +942,8 @@ class ShortcutPackage extends ShortcutPackageItem {
            final ComponentName newActivity = newShortcut.getActivity();
            if (newActivity == null) {
                if (operation != ShortcutService.OPERATION_UPDATE) {
                    // This method may be called before validating shortcuts, so this may happen,
                    // and is a caller side error.
                    throw new NullPointerException("Activity must be provided");
                    service.wtf("Activity must not be null at this point");
                    continue; // Just ignore this invalid case.
                }
                continue; // Activity can be null for update.
            }
+30 −6
Original line number Diff line number Diff line
@@ -21,6 +21,7 @@ import android.content.ComponentName;
import android.content.Intent;
import android.content.pm.ActivityInfo;
import android.content.pm.PackageInfo;
import android.content.pm.ResolveInfo;
import android.content.pm.ShortcutInfo;
import android.content.res.TypedArray;
import android.content.res.XmlResourceParser;
@@ -58,18 +59,36 @@ public class ShortcutParser {
    @Nullable
    public static List<ShortcutInfo> parseShortcuts(ShortcutService service,
            String packageName, @UserIdInt int userId) throws IOException, XmlPullParserException {
        final PackageInfo pi = service.injectGetActivitiesWithMetadata(packageName, userId);
        if (ShortcutService.DEBUG) {
            Slog.d(TAG, String.format("Scanning package %s for manifest shortcuts on user %d",
                    packageName, userId));
        }
        final List<ResolveInfo> activities = service.injectGetMainActivities(packageName, userId);
        if (activities == null || activities.size() == 0) {
            return null;
        }

        List<ShortcutInfo> result = null;

        try {
            if (pi != null && pi.activities != null) {
                for (ActivityInfo activityInfo : pi.activities) {
                    result = parseShortcutsOneFile(service, activityInfo, packageName, userId, result);
            final int size = activities.size();
            for (int i = 0; i < size; i++) {
                final ActivityInfo activityInfoNoMetadata = activities.get(i).activityInfo;
                if (activityInfoNoMetadata == null) {
                    continue;
                }

                final ActivityInfo activityInfoWithMetadata =
                        service.injectGetActivityInfoWithMetadata(
                        activityInfoNoMetadata.getComponentName(), userId);
                if (activityInfoWithMetadata != null) {
                    result = parseShortcutsOneFile(
                            service, activityInfoWithMetadata, packageName, userId, result);
                }
            }
        } catch (RuntimeException e) {
            // Resource ID mismatch may cause various runtime exceptions when parsing XMLs.
            // Resource ID mismatch may cause various runtime exceptions when parsing XMLs,
            // But we don't crash the device, so just swallow them.
            service.wtf(
                    "Exception caught while parsing shortcut XML for package=" + packageName, e);
            return null;
@@ -81,6 +100,11 @@ public class ShortcutParser {
            ShortcutService service,
            ActivityInfo activityInfo, String packageName, @UserIdInt int userId,
            List<ShortcutInfo> result) throws IOException, XmlPullParserException {
        if (ShortcutService.DEBUG) {
            Slog.d(TAG, String.format(
                    "Checking main activity %s", activityInfo.getComponentName()));
        }

        XmlResourceParser parser = null;
        try {
            parser = service.injectXmlMetaData(activityInfo, METADATA_KEY);
@@ -223,7 +247,7 @@ public class ShortcutParser {
                    continue;
                }

                Log.w(TAG, "Unknown tag " + tag + " at depth " + depth);
                ShortcutService.warnForInvalidTag(depth, tag);
            }
        } finally {
            if (parser != null) {
+230 −51

File changed.

Preview size limit exceeded, changes collapsed.

+6 −6
Original line number Diff line number Diff line
@@ -88,7 +88,7 @@ class ShortcutUser {

        @Override
        public String toString() {
            return String.format("{Package: %d, %s}", userId, packageName);
            return String.format("[Package: %d, %s]", userId, packageName);
        }
    }

@@ -99,8 +99,6 @@ class ShortcutUser {

    private final ArrayMap<String, ShortcutPackage> mPackages = new ArrayMap<>();

    private final SparseArray<ShortcutPackage> mPackagesFromUid = new SparseArray<>();

    private final ArrayMap<PackageWithUser, ShortcutLauncher> mLaunchers = new ArrayMap<>();

    /** Default launcher that can access the launcher apps APIs. */
@@ -244,12 +242,12 @@ class ShortcutUser {
        }
    }

    public void handlePackageAddedOrUpdated(@NonNull String packageName) {
    public void handlePackageAddedOrUpdated(@NonNull String packageName, boolean forceRescan) {
        final boolean isNewApp = !mPackages.containsKey(packageName);

        final ShortcutPackage shortcutPackage = getPackageShortcuts(packageName);

        if (!shortcutPackage.handlePackageAddedOrUpdated(isNewApp)) {
        if (!shortcutPackage.handlePackageAddedOrUpdated(isNewApp, forceRescan)) {
            if (isNewApp) {
                mPackages.remove(packageName);
            }
@@ -381,8 +379,10 @@ class ShortcutUser {
        pw.print(mUserId);
        pw.print("  Known locale seq#: ");
        pw.print(mKnownLocaleChangeSequenceNumber);
        pw.print("  Last app scan: ");
        pw.print("  Last app scan: [");
        pw.print(mLastAppScanTime);
        pw.print("] ");
        pw.print(ShortcutService.formatTime(mLastAppScanTime));
        pw.println();

        prefix += prefix + "  ";
Loading