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

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

Merge "Using persisted item storage for install queue." into ub-launcher3-master

parents 53a2814f 104c9d12
Loading
Loading
Loading
Loading
+1 −2
Original line number Diff line number Diff line
@@ -297,8 +297,7 @@ public class BgDataModel {
                        .filter(wi -> wi.itemType == Favorites.ITEM_TYPE_DEEP_SHORTCUT)
                        .map(ShortcutKey::fromItemInfo),
                    // Pending shortcuts
                    ItemInstallQueue.INSTANCE.get(context).getPendingShortcuts()
                        .stream().filter(si -> si.user.equals(user)))
                    ItemInstallQueue.INSTANCE.get(context).getPendingShortcuts(user))
                .collect(groupingBy(ShortcutKey::getPackageName,
                        mapping(ShortcutKey::getId, Collectors.toSet())));

+63 −140
Original line number Diff line number Diff line
@@ -21,7 +21,6 @@ import static android.appwidget.AppWidgetManager.EXTRA_APPWIDGET_ID;
import static com.android.launcher3.LauncherSettings.Favorites.ITEM_TYPE_APPLICATION;
import static com.android.launcher3.LauncherSettings.Favorites.ITEM_TYPE_APPWIDGET;
import static com.android.launcher3.LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT;
import static com.android.launcher3.LauncherSettings.Favorites.PROFILE_ID;
import static com.android.launcher3.model.data.AppInfo.makeLaunchIntent;
import static com.android.launcher3.util.Executors.MODEL_EXECUTOR;

@@ -30,11 +29,9 @@ import android.appwidget.AppWidgetProviderInfo;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.content.pm.LauncherActivityInfo;
import android.content.pm.LauncherApps;
import android.content.pm.ShortcutInfo;
import android.os.Process;
import android.os.UserHandle;
import android.util.Log;
import android.util.Pair;
@@ -47,27 +44,19 @@ import com.android.launcher3.Launcher;
import com.android.launcher3.LauncherAppState;
import com.android.launcher3.LauncherAppWidgetProviderInfo;
import com.android.launcher3.LauncherSettings.Favorites;
import com.android.launcher3.Utilities;
import com.android.launcher3.model.data.ItemInfo;
import com.android.launcher3.model.data.LauncherAppWidgetInfo;
import com.android.launcher3.model.data.WorkspaceItemInfo;
import com.android.launcher3.pm.UserCache;
import com.android.launcher3.shortcuts.ShortcutKey;
import com.android.launcher3.shortcuts.ShortcutRequest;
import com.android.launcher3.util.MainThreadInitializedObject;
import com.android.launcher3.util.PersistedItemArray;
import com.android.launcher3.util.Preconditions;

import org.json.JSONException;
import org.json.JSONObject;
import org.json.JSONStringer;

import java.net.URISyntaxException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;
import java.util.stream.Stream;

/**
 * Class to maintain a queue of pending items to be added to the workspace.
@@ -79,7 +68,6 @@ public class ItemInstallQueue {
    public static final int FLAG_DRAG_AND_DROP = 4;

    private static final String TAG = "InstallShortcutReceiver";
    private static final boolean DBG = false;

    // The set of shortcuts that are pending install
    private static final String APPS_PENDING_INSTALL = "apps_to_install";
@@ -90,24 +78,34 @@ public class ItemInstallQueue {
    public static MainThreadInitializedObject<ItemInstallQueue> INSTANCE =
            new MainThreadInitializedObject<>(ItemInstallQueue::new);

    private final PersistedItemArray<PendingInstallShortcutInfo> mStorage =
            new PersistedItemArray<>(APPS_PENDING_INSTALL);
    private final Context mContext;

    // Determines whether to defer installing shortcuts immediately until
    // processAllPendingInstalls() is called.
    private int mInstallQueueDisabledFlags = 0;

    // Only accessed on worker thread
    private List<PendingInstallShortcutInfo> mItems;

    private ItemInstallQueue(Context context) {
        mContext = context;
    }

    @WorkerThread
    private void ensureQueueLoaded() {
        Preconditions.assertWorkerThread();
        if (mItems == null) {
            mItems = mStorage.read(mContext, this::decode);
        }
    }

    @WorkerThread
    private void addToQueue(PendingInstallShortcutInfo info) {
        String encoded = info.encodeToString(mContext);
        SharedPreferences prefs = Utilities.getPrefs(mContext);
        Set<String> strings = prefs.getStringSet(APPS_PENDING_INSTALL, null);
        strings = (strings != null) ? new HashSet<>(strings) : new HashSet<>(1);
        strings.add(encoded);
        prefs.edit().putStringSet(APPS_PENDING_INSTALL, strings).apply();
        ensureQueueLoaded();
        mItems.add(info);
        mStorage.write(mContext, mItems);
    }

    @WorkerThread
@@ -117,28 +115,21 @@ public class ItemInstallQueue {
            // Launcher not loaded
            return;
        }

        ArrayList<Pair<ItemInfo, Object>> installQueue = new ArrayList<>();
        SharedPreferences prefs = Utilities.getPrefs(mContext);
        Set<String> strings = prefs.getStringSet(APPS_PENDING_INSTALL, null);
        if (DBG) Log.d(TAG, "Getting and clearing APPS_PENDING_INSTALL: " + strings);
        if (strings == null) {
        ensureQueueLoaded();
        if (mItems.isEmpty()) {
            return;
        }

        for (String encoded : strings) {
            PendingInstallShortcutInfo info = decode(encoded, mContext);
            if (info == null) {
                continue;
            }
        List<Pair<ItemInfo, Object>> installQueue = mItems.stream()
                .map(info -> info.getItemInfo(mContext))
                .collect(Collectors.toList());

            // Generate a shortcut info to add into the model
            installQueue.add(info.getItemInfo(mContext));
        }
        prefs.edit().remove(APPS_PENDING_INSTALL).apply();
        // Add the items and clear queue
        if (!installQueue.isEmpty()) {
            launcher.getModel().addAndBindAddedWorkspaceItems(installQueue);
        }
        mItems.clear();
        mStorage.getFile(mContext).delete();
    }

    /**
@@ -149,34 +140,12 @@ public class ItemInstallQueue {
        if (packageNames.isEmpty()) {
            return;
        }
        Preconditions.assertWorkerThread();

        SharedPreferences sp = Utilities.getPrefs(mContext);
        Set<String> strings = sp.getStringSet(APPS_PENDING_INSTALL, null);
        if (DBG) {
            Log.d(TAG, "APPS_PENDING_INSTALL: " + strings
                    + ", removing packages: " + packageNames);
        }
        if (strings == null || ((Collection) strings).isEmpty()) {
            return;
        }
        Set<String> newStrings = new HashSet<>(strings);
        Iterator<String> newStringsIter = newStrings.iterator();
        while (newStringsIter.hasNext()) {
            String encoded = newStringsIter.next();
            try {
                Decoder decoder = new Decoder(encoded, mContext);
                if (packageNames.contains(getIntentPackage(decoder.intent))
                        && user.equals(decoder.user)) {
                    newStringsIter.remove();
                }
            } catch (JSONException | URISyntaxException e) {
                Log.d(TAG, "Exception reading shortcut to add: " + e);
                newStringsIter.remove();
        ensureQueueLoaded();
        if (mItems.removeIf(item ->
                item.user.equals(user) && packageNames.contains(getIntentPackage(item.intent)))) {
            mStorage.write(mContext, mItems);
        }
    }
        sp.edit().putStringSet(APPS_PENDING_INSTALL, newStrings).apply();
    }

    /**
     * Adds an item to the install queue
@@ -200,28 +169,14 @@ public class ItemInstallQueue {
    }

    /**
     * Returns all pending shorts in the queue
     * Returns a stream of all pending shortcuts in the queue
     */
    @WorkerThread
    public HashSet<ShortcutKey> getPendingShortcuts() {
        HashSet<ShortcutKey> result = new HashSet<>();

        Set<String> strings = Utilities.getPrefs(mContext).getStringSet(APPS_PENDING_INSTALL, null);
        if (strings == null || ((Collection) strings).isEmpty()) {
            return result;
        }

        for (String encoded : strings) {
            try {
                Decoder decoder = new Decoder(encoded, mContext);
                if (decoder.optInt(Favorites.ITEM_TYPE, -1) == ITEM_TYPE_DEEP_SHORTCUT) {
                    result.add(ShortcutKey.fromIntent(decoder.intent, decoder.user));
                }
            } catch (JSONException | URISyntaxException e) {
                Log.d(TAG, "Exception reading shortcut to add: " + e);
            }
        }
        return result;
    public Stream<ShortcutKey> getPendingShortcuts(UserHandle user) {
        ensureQueueLoaded();
        return mItems.stream()
                .filter(item -> item.itemType == ITEM_TYPE_DEEP_SHORTCUT && user.equals(item.user))
                .map(item -> ShortcutKey.fromIntent(item.intent, user));
    }

    private void queuePendingShortcutInfo(PendingInstallShortcutInfo info) {
@@ -293,19 +248,9 @@ public class ItemInstallQueue {
            providerInfo = info;
        }

        public String encodeToString(Context context) {
            try {
                return new JSONStringer()
                        .object()
                        .key(Favorites.ITEM_TYPE).value(itemType)
                        .key(Favorites.INTENT).value(intent.toUri(0))
                        .key(PROFILE_ID).value(
                                UserCache.INSTANCE.get(context).getSerialNumberForUser(user))
                        .endObject().toString();
            } catch (JSONException e) {
                Log.d(TAG, "Exception when adding shortcut: " + e);
                return null;
            }
        @Override
        public Intent getIntent() {
            return intent;
        }

        public Pair<ItemInfo, Object> getItemInfo(Context context) {
@@ -365,16 +310,13 @@ public class ItemInstallQueue {
                ? intent.getPackage() : intent.getComponent().getPackageName();
    }

    private static PendingInstallShortcutInfo decode(String encoded, Context context) {
        try {
            Decoder decoder = new Decoder(encoded, context);
            switch (decoder.optInt(Favorites.ITEM_TYPE, -1)) {
    private PendingInstallShortcutInfo decode(int itemType, UserHandle user, Intent intent) {
        switch (itemType) {
            case Favorites.ITEM_TYPE_APPLICATION:
                    return new PendingInstallShortcutInfo(
                            decoder.intent.getPackage(), decoder.user);
                return new PendingInstallShortcutInfo(intent.getPackage(), user);
            case Favorites.ITEM_TYPE_DEEP_SHORTCUT: {
                    List<ShortcutInfo> si = ShortcutKey.fromIntent(decoder.intent, decoder.user)
                            .buildRequest(context)
                List<ShortcutInfo> si = ShortcutKey.fromIntent(intent, user)
                        .buildRequest(mContext)
                        .query(ShortcutRequest.ALL);
                if (si.isEmpty()) {
                    return null;
@@ -383,11 +325,11 @@ public class ItemInstallQueue {
                }
            }
            case Favorites.ITEM_TYPE_APPWIDGET: {
                    int widgetId = decoder.intent.getIntExtra(EXTRA_APPWIDGET_ID, 0);
                int widgetId = intent.getIntExtra(EXTRA_APPWIDGET_ID, 0);
                AppWidgetProviderInfo info =
                            AppWidgetManager.getInstance(context).getAppWidgetInfo(widgetId);
                    if (info == null || !info.provider.equals(decoder.intent.getComponent())
                            || !info.getProfile().equals(decoder.user)) {
                        AppWidgetManager.getInstance(mContext).getAppWidgetInfo(widgetId);
                if (info == null || !info.provider.equals(intent.getComponent())
                        || !info.getProfile().equals(user)) {
                    return null;
                }
                return new PendingInstallShortcutInfo(info, widgetId);
@@ -395,25 +337,6 @@ public class ItemInstallQueue {
            default:
                Log.e(TAG, "Unknown item type");
        }
        } catch (JSONException | URISyntaxException e) {
            Log.d(TAG, "Exception reading shortcut to add: " + e);
        }
        return null;
    }

    private static class Decoder extends JSONObject {
        public final Intent intent;
        public final UserHandle user;

        private Decoder(String encoded, Context context) throws JSONException, URISyntaxException {
            super(encoded);
            intent = Intent.parseUri(getString(Favorites.INTENT), 0);
            user = has(PROFILE_ID)
                    ? UserCache.INSTANCE.get(context).getUserForSerialNumber(getLong(PROFILE_ID))
                    : Process.myUserHandle();
            if (user == null || intent == null) {
                throw new JSONException("Invalid data");
            }
        }
    }
}
+9 −5
Original line number Diff line number Diff line
@@ -68,8 +68,7 @@ public class PersistedItemArray<T extends ItemInfo> {
     */
    @WorkerThread
    public void write(Context context, List<T> items) {
        AtomicFile file = new AtomicFile(context.getFileStreamPath(mFileName));

        AtomicFile file = getFile(context);
        FileOutputStream fos;
        try {
            fos = file.startWrite();
@@ -124,9 +123,7 @@ public class PersistedItemArray<T extends ItemInfo> {
    @WorkerThread
    public List<T> read(Context context, ItemFactory<T> factory, LongFunction<UserHandle> userFn) {
        List<T> result = new ArrayList<>();
        AtomicFile file = new AtomicFile(context.getFileStreamPath(mFileName));

        try (FileInputStream fis = file.openRead()) {
        try (FileInputStream fis = getFile(context).openRead()) {
            XmlPullParser parser = Xml.newPullParser();
            parser.setInput(new InputStreamReader(fis, StandardCharsets.UTF_8));

@@ -166,6 +163,13 @@ public class PersistedItemArray<T extends ItemInfo> {
        return result;
    }

    /**
     * Returns the underlying file used for persisting data
     */
    public AtomicFile getFile(Context context) {
        return new AtomicFile(context.getFileStreamPath(mFileName));
    }

    /**
     * Interface to create an ItemInfo during parsing
     */