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

Commit 385e9324 authored by Samuel Fufa's avatar Samuel Fufa
Browse files

Improve Hybird hotseat cache support

Loads list of cached apps and shows predicted app icons with the rest of workspace items.

-> Extracted prediction caching logic into a model layer

Bug: 155029837
Test: Manual

Change-Id: I6981594a910f5fe4e8e8cf8fe39db0cb856e7acd
parent 43e789ff
Loading
Loading
Loading
Loading
+32 −35
Original line number Diff line number Diff line
@@ -43,6 +43,7 @@ import com.android.launcher3.DropTarget;
import com.android.launcher3.Hotseat;
import com.android.launcher3.InvariantDeviceProfile;
import com.android.launcher3.Launcher;
import com.android.launcher3.LauncherAppState;
import com.android.launcher3.LauncherSettings;
import com.android.launcher3.LauncherState;
import com.android.launcher3.R;
@@ -56,6 +57,7 @@ import com.android.launcher3.dragndrop.DragController;
import com.android.launcher3.dragndrop.DragOptions;
import com.android.launcher3.icons.IconCache;
import com.android.launcher3.logging.FileLog;
import com.android.launcher3.model.PredictionModel;
import com.android.launcher3.model.data.AppInfo;
import com.android.launcher3.model.data.FolderInfo;
import com.android.launcher3.model.data.ItemInfo;
@@ -69,6 +71,7 @@ import com.android.launcher3.uioverrides.PredictedAppIcon;
import com.android.launcher3.uioverrides.QuickstepLauncher;
import com.android.launcher3.userevent.nano.LauncherLogProto;
import com.android.launcher3.util.ComponentKey;
import com.android.launcher3.util.IntArray;

import java.lang.ref.WeakReference;
import java.util.ArrayList;
@@ -93,8 +96,6 @@ public class HotseatPredictionController implements DragController.DragListener,
    //TODO: replace this with AppTargetEvent.ACTION_UNPIN (b/144119543)
    private static final int APPTARGET_ACTION_UNPIN = 4;

    private static final String PREDICTED_ITEMS_CACHE_KEY = "predicted_item_keys";

    private static final String APP_LOCATION_HOTSEAT = "hotseat";
    private static final String APP_LOCATION_WORKSPACE = "workspace";

@@ -115,11 +116,13 @@ public class HotseatPredictionController implements DragController.DragListener,

    private DynamicItemCache mDynamicItemCache;

    private final PredictionModel mPredictionModel;
    private AppPredictor mAppPredictor;
    private AllAppsStore mAllAppsStore;
    private AnimatorSet mIconRemoveAnimators;
    private boolean mUIUpdatePaused = false;
    private boolean mRequiresCacheUpdate = false;
    private boolean mRequiresCacheUpdate = true;
    private boolean mIsCacheEmpty;

    private HotseatEduController mHotseatEduController;

@@ -138,15 +141,16 @@ public class HotseatPredictionController implements DragController.DragListener,
        mLauncher = launcher;
        mHotseat = launcher.getHotseat();
        mAllAppsStore = mLauncher.getAppsView().getAppsStore();
        mPredictionModel = LauncherAppState.INSTANCE.get(launcher).getPredictionModel();
        mAllAppsStore.addUpdateListener(this);
        mDynamicItemCache = new DynamicItemCache(mLauncher, this::fillGapsWithPrediction);
        mHotSeatItemsCount = mLauncher.getDeviceProfile().inv.numHotseatIcons;
        launcher.getDeviceProfile().inv.addOnChangeListener(this);
        mHotseat.addOnAttachStateChangeListener(this);
        mIsCacheEmpty = mPredictionModel.getPredictionComponentKeys().isEmpty();
        if (mHotseat.isAttachedToWindow()) {
            onViewAttachedToWindow(mHotseat);
        }
        showCachedItems();
    }

    /**
@@ -185,6 +189,11 @@ public class HotseatPredictionController implements DragController.DragListener,
            return;
        }
        List<WorkspaceItemInfo> predictedApps = mapToWorkspaceItemInfo(mComponentKeyMappers);
        if (mComponentKeyMappers.isEmpty() != predictedApps.isEmpty()) {
            // Safely ignore update as AppsList is not ready yet. This will called again once
            // apps are ready (HotseatPredictionController#onAppsUpdated)
            return;
        }
        int predictionIndex = 0;
        ArrayList<WorkspaceItemInfo> newItems = new ArrayList<>();
        // make sure predicted icon removal and filling predictions don't step on each other
@@ -305,14 +314,23 @@ public class HotseatPredictionController implements DragController.DragListener,
        mAppPredictor.requestPredictionUpdate();
    }

    private void showCachedItems() {
        ArrayList<ComponentKey> componentKeys = getCachedComponentKeys();
    /**
     * Create WorkspaceItemInfo objects and binds PredictedAppIcon views for cached predicted items.
     */
    public void showCachedItems(List<AppInfo> apps, IntArray ranks) {
        int count = Math.min(ranks.size(), apps.size());
        List<WorkspaceItemInfo> items = new ArrayList<>(count);
        for (int i = 0; i < count; i++) {
            WorkspaceItemInfo item = new WorkspaceItemInfo(apps.get(i));
            preparePredictionInfo(item, ranks.get(i));
            items.add(item);
        }
        mComponentKeyMappers.clear();
        for (ComponentKey key : componentKeys) {
        for (ComponentKey key : mPredictionModel.getPredictionComponentKeys()) {
            mComponentKeyMappers.add(new ComponentKeyMapper(key, mDynamicItemCache));
        }
        updateDependencies();
        fillGapsWithPrediction();
        bindItems(items, false, null);
    }

    private Bundle getAppPredictionContextExtra() {
@@ -394,37 +412,16 @@ public class HotseatPredictionController implements DragController.DragListener,
        if (!isEduSeen() && mHotseatEduController != null) {
            mHotseatEduController.setPredictedApps(mapToWorkspaceItemInfo(mComponentKeyMappers));
        }
        // should invalidate cache if AiAi sends empty list of AppTargets
        if (appTargets.isEmpty()) {
            mRequiresCacheUpdate = true;
        }
        cachePredictionComponentKeys(componentKeys);
        cachePredictionComponentKeysIfNecessary(componentKeys);
    }

    private void cachePredictionComponentKeys(ArrayList<ComponentKey> componentKeys) {
        if (!mRequiresCacheUpdate) return;
        StringBuilder builder = new StringBuilder();
        for (ComponentKey componentKey : componentKeys) {
            builder.append(componentKey);
            builder.append("\n");
        }
        mLauncher.getDevicePrefs().edit().putString(PREDICTED_ITEMS_CACHE_KEY,
                builder.toString()).apply();
    private void cachePredictionComponentKeysIfNecessary(ArrayList<ComponentKey> componentKeys) {
        if (!mRequiresCacheUpdate && componentKeys.isEmpty() == mIsCacheEmpty) return;
        mPredictionModel.cachePredictionComponentKeys(componentKeys);
        mIsCacheEmpty = componentKeys.isEmpty();
        mRequiresCacheUpdate = false;
    }

    private ArrayList<ComponentKey> getCachedComponentKeys() {
        String cachedBlob = mLauncher.getDevicePrefs().getString(PREDICTED_ITEMS_CACHE_KEY, "");
        ArrayList<ComponentKey> results = new ArrayList<>();
        for (String line : cachedBlob.split("\n")) {
            ComponentKey key = ComponentKey.fromString(line);
            if (key != null) {
                results.add(key);
            }
        }
        return results;
    }

    private void updateDependencies() {
        mDynamicItemCache.updateDependencies(mComponentKeyMappers, mAllAppsStore, this,
                mHotSeatItemsCount);
+11 −0
Original line number Diff line number Diff line
@@ -44,6 +44,7 @@ import com.android.launcher3.anim.AnimatorPlaybackController;
import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.folder.Folder;
import com.android.launcher3.hybridhotseat.HotseatPredictionController;
import com.android.launcher3.model.data.AppInfo;
import com.android.launcher3.model.data.ItemInfo;
import com.android.launcher3.model.data.WorkspaceItemInfo;
import com.android.launcher3.popup.SystemShortcut;
@@ -58,6 +59,7 @@ import com.android.launcher3.uioverrides.touchcontrollers.QuickSwitchTouchContro
import com.android.launcher3.uioverrides.touchcontrollers.StatusBarTouchController;
import com.android.launcher3.uioverrides.touchcontrollers.TaskViewTouchController;
import com.android.launcher3.uioverrides.touchcontrollers.TransposedQuickSwitchTouchController;
import com.android.launcher3.util.IntArray;
import com.android.launcher3.util.TouchController;
import com.android.launcher3.util.UiThreadHelper;
import com.android.launcher3.util.UiThreadHelper.AsyncCommand;
@@ -70,6 +72,7 @@ import com.android.quickstep.views.TaskView;
import java.io.FileDescriptor;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Stream;

public class QuickstepLauncher extends BaseQuickstepLauncher {
@@ -177,6 +180,14 @@ public class QuickstepLauncher extends BaseQuickstepLauncher {
        }
    }

    @Override
    public void bindPredictedItems(List<AppInfo> appInfos, IntArray ranks) {
        super.bindPredictedItems(appInfos, ranks);
        if (mHotseatPredictionController != null) {
            mHotseatPredictionController.showCachedItems(appInfos, ranks);
        }
    }

    @Override
    public void onDestroy() {
        super.onDestroy();
+3 −0
Original line number Diff line number Diff line
@@ -2225,6 +2225,9 @@ public class Launcher extends BaseDraggingActivity implements LauncherExterns,
        workspace.requestLayout();
    }

    @Override
    public void bindPredictedItems(List<AppInfo> appInfos, IntArray ranks) { }

    /**
     * Add the views for a widget to the workspace.
     */
+7 −0
Original line number Diff line number Diff line
@@ -33,6 +33,7 @@ import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.icons.IconCache;
import com.android.launcher3.icons.IconProvider;
import com.android.launcher3.icons.LauncherIcons;
import com.android.launcher3.model.PredictionModel;
import com.android.launcher3.notification.NotificationListener;
import com.android.launcher3.pm.InstallSessionHelper;
import com.android.launcher3.pm.InstallSessionTracker;
@@ -57,6 +58,7 @@ public class LauncherAppState {
    private final IconCache mIconCache;
    private final WidgetPreviewLoader mWidgetCache;
    private final InvariantDeviceProfile mInvariantDeviceProfile;
    private final PredictionModel mPredictionModel;

    private SecureSettingsObserver mNotificationDotsObserver;
    private InstallSessionTracker mInstallSessionTracker;
@@ -127,6 +129,7 @@ public class LauncherAppState {
        mIconCache = new IconCache(mContext, mInvariantDeviceProfile, iconCacheFileName);
        mWidgetCache = new WidgetPreviewLoader(mContext, mIconCache);
        mModel = new LauncherModel(this, mIconCache, AppFilter.newInstance(mContext));
        mPredictionModel = new PredictionModel(mContext);
    }

    protected void onNotificationSettingsChanged(boolean areNotificationDotsEnabled) {
@@ -182,6 +185,10 @@ public class LauncherAppState {
        return mModel;
    }

    public PredictionModel getPredictionModel() {
        return mPredictionModel;
    }

    public WidgetPreviewLoader getWidgetCache() {
        return mWidgetCache;
    }
+10 −0
Original line number Diff line number Diff line
@@ -17,6 +17,7 @@
package com.android.launcher3.model;

import static com.android.launcher3.model.ModelUtils.filterCurrentWorkspaceItems;
import static com.android.launcher3.model.ModelUtils.getMissingHotseatRanks;
import static com.android.launcher3.model.ModelUtils.sortWorkspaceItemsSpatially;

import android.util.Log;
@@ -196,6 +197,10 @@ public abstract class BaseLoaderResults {
            // Load items on the current page.
            bindWorkspaceItems(currentWorkspaceItems, mainExecutor);
            bindAppWidgets(currentAppWidgets, mainExecutor);

            // Locate available spots for prediction using currentWorkspaceItems
            IntArray gaps = getMissingHotseatRanks(currentWorkspaceItems, idp.numHotseatIcons);
            bindPredictedItems(gaps, mainExecutor);
            // In case of validFirstPage, only bind the first screen, and defer binding the
            // remaining screens after first onDraw (and an optional the fade animation whichever
            // happens later).
@@ -247,6 +252,11 @@ public abstract class BaseLoaderResults {
            }
        }

        private void bindPredictedItems(IntArray ranks, final Executor executor) {
            executeCallbacksTask(
                    c -> c.bindPredictedItems(mBgDataModel.cachedPredictedItems, ranks), executor);
        }

        protected void executeCallbacksTask(CallbackTask task, Executor executor) {
            executor.execute(() -> {
                if (mMyBindingId != mBgDataModel.lastBindId) {
Loading