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

Commit 4a588436 authored by Sihua Ma's avatar Sihua Ma
Browse files

Create remote collection cache to eliminate duplicate intent

Bug: 245950570
Test: Manual
Change-Id: I5c1bd369e63826d0ab264277fe735c0c051abcbf
parent 8731af28
Loading
Loading
Loading
Loading
+55 −34
Original line number Original line Diff line number Diff line
@@ -562,13 +562,6 @@ public class AppWidgetManager {
        });
        });
    }
    }


    private boolean isPostingTaskToBackground(@Nullable RemoteViews views) {
        return Looper.myLooper() == Looper.getMainLooper()
                && RemoteViews.isAdapterConversionEnabled()
                && (mHasPostedLegacyLists = mHasPostedLegacyLists
                        || (views != null && views.hasLegacyLists()));
    }

    /**
    /**
     * Set the RemoteViews to use for the specified appWidgetIds.
     * Set the RemoteViews to use for the specified appWidgetIds.
     * <p>
     * <p>
@@ -593,10 +586,18 @@ public class AppWidgetManager {
            return;
            return;
        }
        }


        if (isPostingTaskToBackground(views)) {
        final boolean isConvertingAdapter = RemoteViews.isAdapterConversionEnabled()
                && (mHasPostedLegacyLists = mHasPostedLegacyLists
                        || (views != null && views.hasLegacyLists()));

        if (isConvertingAdapter) {
            views.collectAllIntents();

            if (Looper.getMainLooper() == Looper.myLooper()) {
                RemoteViews viewsCopy = new RemoteViews(views);
                createUpdateExecutorIfNull().execute(() -> {
                createUpdateExecutorIfNull().execute(() -> {
                    try {
                    try {
                    mService.updateAppWidgetIds(mPackageName, appWidgetIds, views);
                        mService.updateAppWidgetIds(mPackageName, appWidgetIds, viewsCopy);
                    } catch (RemoteException e) {
                    } catch (RemoteException e) {
                        Log.e(TAG, "Error updating app widget views in background", e);
                        Log.e(TAG, "Error updating app widget views in background", e);
                    }
                    }
@@ -604,6 +605,7 @@ public class AppWidgetManager {


                return;
                return;
            }
            }
        }


        try {
        try {
            mService.updateAppWidgetIds(mPackageName, appWidgetIds, views);
            mService.updateAppWidgetIds(mPackageName, appWidgetIds, views);
@@ -714,10 +716,18 @@ public class AppWidgetManager {
            return;
            return;
        }
        }


        if (isPostingTaskToBackground(views)) {
        final boolean isConvertingAdapter = RemoteViews.isAdapterConversionEnabled()
                && (mHasPostedLegacyLists = mHasPostedLegacyLists
                        || (views != null && views.hasLegacyLists()));

        if (isConvertingAdapter) {
            views.collectAllIntents();

            if (Looper.getMainLooper() == Looper.myLooper()) {
                RemoteViews viewsCopy = new RemoteViews(views);
                createUpdateExecutorIfNull().execute(() -> {
                createUpdateExecutorIfNull().execute(() -> {
                    try {
                    try {
                    mService.partiallyUpdateAppWidgetIds(mPackageName, appWidgetIds, views);
                        mService.partiallyUpdateAppWidgetIds(mPackageName, appWidgetIds, viewsCopy);
                    } catch (RemoteException e) {
                    } catch (RemoteException e) {
                        Log.e(TAG, "Error partially updating app widget views in background", e);
                        Log.e(TAG, "Error partially updating app widget views in background", e);
                    }
                    }
@@ -725,6 +735,7 @@ public class AppWidgetManager {


                return;
                return;
            }
            }
        }


        try {
        try {
            mService.partiallyUpdateAppWidgetIds(mPackageName, appWidgetIds, views);
            mService.partiallyUpdateAppWidgetIds(mPackageName, appWidgetIds, views);
@@ -782,17 +793,27 @@ public class AppWidgetManager {
            return;
            return;
        }
        }


        if (isPostingTaskToBackground(views)) {
        final boolean isConvertingAdapter = RemoteViews.isAdapterConversionEnabled()
                && (mHasPostedLegacyLists = mHasPostedLegacyLists
                        || (views != null && views.hasLegacyLists()));

        if (isConvertingAdapter) {
            views.collectAllIntents();

            if (Looper.getMainLooper() == Looper.myLooper()) {
                RemoteViews viewsCopy = new RemoteViews(views);
                createUpdateExecutorIfNull().execute(() -> {
                createUpdateExecutorIfNull().execute(() -> {
                    try {
                    try {
                    mService.updateAppWidgetProvider(provider, views);
                        mService.updateAppWidgetProvider(provider, viewsCopy);
                    } catch (RemoteException e) {
                    } catch (RemoteException e) {
                    Log.e(TAG, "Error updating app widget view using provider in background", e);
                        Log.e(TAG, "Error updating app widget view using provider in background",
                                e);
                    }
                    }
                });
                });


                return;
                return;
            }
            }
        }


        try {
        try {
            mService.updateAppWidgetProvider(provider, views);
            mService.updateAppWidgetProvider(provider, views);
+195 −25
Original line number Original line Diff line number Diff line
@@ -85,6 +85,7 @@ import android.util.Log;
import android.util.LongArray;
import android.util.LongArray;
import android.util.Pair;
import android.util.Pair;
import android.util.SizeF;
import android.util.SizeF;
import android.util.SparseArray;
import android.util.SparseIntArray;
import android.util.SparseIntArray;
import android.util.TypedValue;
import android.util.TypedValue;
import android.util.TypedValue.ComplexDimensionUnit;
import android.util.TypedValue.ComplexDimensionUnit;
@@ -371,6 +372,11 @@ public class RemoteViews implements Parcelable, Filter {
    @UnsupportedAppUsage
    @UnsupportedAppUsage
    private BitmapCache mBitmapCache = new BitmapCache();
    private BitmapCache mBitmapCache = new BitmapCache();


    /**
     * Maps Intent ID to RemoteCollectionItems to avoid duplicate items
     */
    private RemoteCollectionCache mCollectionCache = new RemoteCollectionCache();

    /** Cache of ApplicationInfos used by collection items. */
    /** Cache of ApplicationInfos used by collection items. */
    private ApplicationInfoCache mApplicationInfoCache = new ApplicationInfoCache();
    private ApplicationInfoCache mApplicationInfoCache = new ApplicationInfoCache();


@@ -801,9 +807,12 @@ public class RemoteViews implements Parcelable, Filter {
                if (action instanceof SetRemoteCollectionItemListAdapterAction itemsAction
                if (action instanceof SetRemoteCollectionItemListAdapterAction itemsAction
                        && itemsAction.mViewId == viewId
                        && itemsAction.mViewId == viewId
                        && itemsAction.mServiceIntent != null) {
                        && itemsAction.mServiceIntent != null) {
                    mActions.set(i,
                    SetRemoteCollectionItemListAdapterAction newCollectionAction =
                            new SetRemoteCollectionItemListAdapterAction(itemsAction.mViewId,
                            new SetRemoteCollectionItemListAdapterAction(
                                    itemsAction.mServiceIntent));
                                    itemsAction.mViewId, itemsAction.mServiceIntent);
                    newCollectionAction.mIntentId = itemsAction.mIntentId;
                    newCollectionAction.mIsReplacedIntoAction = true;
                    mActions.set(i, newCollectionAction);
                    isActionReplaced = true;
                    isActionReplaced = true;
                } else if (action instanceof SetRemoteViewsAdapterIntent intentAction
                } else if (action instanceof SetRemoteViewsAdapterIntent intentAction
                        && intentAction.mViewId == viewId) {
                        && intentAction.mViewId == viewId) {
@@ -1145,6 +1154,8 @@ public class RemoteViews implements Parcelable, Filter {
        @NonNull
        @NonNull
        private CompletableFuture<RemoteCollectionItems> mItemsFuture;
        private CompletableFuture<RemoteCollectionItems> mItemsFuture;
        final Intent mServiceIntent;
        final Intent mServiceIntent;
        int mIntentId = -1;
        boolean mIsReplacedIntoAction = false;


        SetRemoteCollectionItemListAdapterAction(@IdRes int id,
        SetRemoteCollectionItemListAdapterAction(@IdRes int id,
                @NonNull RemoteCollectionItems items) {
                @NonNull RemoteCollectionItems items) {
@@ -1205,38 +1216,36 @@ public class RemoteViews implements Parcelable, Filter {


        SetRemoteCollectionItemListAdapterAction(Parcel parcel) {
        SetRemoteCollectionItemListAdapterAction(Parcel parcel) {
            mViewId = parcel.readInt();
            mViewId = parcel.readInt();
            mItemsFuture = CompletableFuture.completedFuture(
            mIntentId = parcel.readInt();
                    new RemoteCollectionItems(parcel, getHierarchyRootData()));
            mItemsFuture = CompletableFuture.completedFuture(mIntentId != -1
                    ? null
                    : new RemoteCollectionItems(parcel, getHierarchyRootData()));
            mServiceIntent = parcel.readTypedObject(Intent.CREATOR);
            mServiceIntent = parcel.readTypedObject(Intent.CREATOR);
        }
        }


        @Override
        @Override
        public void setHierarchyRootData(HierarchyRootData rootData) {
        public void setHierarchyRootData(HierarchyRootData rootData) {
            if (mIntentId == -1) {
                mItemsFuture = mItemsFuture
                mItemsFuture = mItemsFuture
                        .thenApply(rc -> {
                        .thenApply(rc -> {
                            rc.setHierarchyRootData(rootData);
                            rc.setHierarchyRootData(rootData);
                            return rc;
                            return rc;
                        });
                        });
                return;
            }
            }


        private static RemoteCollectionItems getCollectionItemsFromFuture(
            // Set the root data for items in the cache instead
                CompletableFuture<RemoteCollectionItems> itemsFuture) {
            mCollectionCache.setHierarchyDataForId(mIntentId, rootData);
            RemoteCollectionItems items;
            try {
                items = itemsFuture.get();
            } catch (Exception e) {
                Log.e(LOG_TAG, "Error getting collection items from future", e);
                items = new RemoteCollectionItems.Builder().build();
            }

            return items;
        }
        }


        @Override
        @Override
        public void writeToParcel(Parcel dest, int flags) {
        public void writeToParcel(Parcel dest, int flags) {
            dest.writeInt(mViewId);
            dest.writeInt(mViewId);
            dest.writeInt(mIntentId);
            if (mIntentId == -1) {
                RemoteCollectionItems items = getCollectionItemsFromFuture(mItemsFuture);
                RemoteCollectionItems items = getCollectionItemsFromFuture(mItemsFuture);
                items.writeToParcel(dest, flags, /* attached= */ true);
                items.writeToParcel(dest, flags, /* attached= */ true);
            }
            dest.writeTypedObject(mServiceIntent, flags);
            dest.writeTypedObject(mServiceIntent, flags);
        }
        }


@@ -1246,7 +1255,9 @@ public class RemoteViews implements Parcelable, Filter {
            View target = root.findViewById(mViewId);
            View target = root.findViewById(mViewId);
            if (target == null) return;
            if (target == null) return;


            RemoteCollectionItems items = getCollectionItemsFromFuture(mItemsFuture);
            RemoteCollectionItems items = mIntentId == -1
                    ? getCollectionItemsFromFuture(mItemsFuture)
                    : mCollectionCache.getItemsForId(mIntentId);


            // Ensure that we are applying to an AppWidget root
            // Ensure that we are applying to an AppWidget root
            if (!(rootParent instanceof AppWidgetHostView)) {
            if (!(rootParent instanceof AppWidgetHostView)) {
@@ -1307,6 +1318,153 @@ public class RemoteViews implements Parcelable, Filter {
        }
        }
    }
    }


    private static RemoteCollectionItems getCollectionItemsFromFuture(
            CompletableFuture<RemoteCollectionItems> itemsFuture) {
        RemoteCollectionItems items;
        try {
            items = itemsFuture.get();
        } catch (Exception e) {
            Log.e(LOG_TAG, "Error getting collection items from future", e);
            items = new RemoteCollectionItems.Builder().build();
        }

        return items;
    }

    /**
     * @hide
     */
    public void collectAllIntents() {
        mCollectionCache.collectAllIntentsNoComplete(this);
    }

    private class RemoteCollectionCache {
        private SparseArray<String> mIdToUriMapping = new SparseArray<>();
        private HashMap<String, RemoteCollectionItems> mUriToCollectionMapping = new HashMap<>();

        // We don't put this into the parcel
        private HashMap<String, CompletableFuture<RemoteCollectionItems>> mTempUriToFutureMapping =
                new HashMap<>();

        RemoteCollectionCache() { }

        RemoteCollectionCache(RemoteCollectionCache src) {
            boolean isWaitingCache = src.mTempUriToFutureMapping.size() != 0;
            for (int i = 0; i < src.mIdToUriMapping.size(); i++) {
                String uri = src.mIdToUriMapping.valueAt(i);
                mIdToUriMapping.put(src.mIdToUriMapping.keyAt(i), uri);
                if (isWaitingCache) {
                    mTempUriToFutureMapping.put(uri, src.mTempUriToFutureMapping.get(uri));
                } else {
                    mUriToCollectionMapping.put(uri, src.mUriToCollectionMapping.get(uri));
                }
            }
        }

        RemoteCollectionCache(Parcel in) {
            int cacheSize = in.readInt();
            HierarchyRootData currentRootData = new HierarchyRootData(mBitmapCache,
                    this,
                    mApplicationInfoCache,
                    mClassCookies);
            for (int i = 0; i < cacheSize; i++) {
                int intentId = in.readInt();
                String intentUri = in.readString8();
                RemoteCollectionItems items = new RemoteCollectionItems(in, currentRootData);
                mIdToUriMapping.put(intentId, intentUri);
                mUriToCollectionMapping.put(intentUri, items);
            }
        }

        void setHierarchyDataForId(int intentId, HierarchyRootData data) {
            String uri = mIdToUriMapping.get(intentId);
            if (mTempUriToFutureMapping.get(uri) != null) {
                CompletableFuture<RemoteCollectionItems> itemsFuture =
                        mTempUriToFutureMapping.get(uri);
                mTempUriToFutureMapping.put(uri, itemsFuture.thenApply(rc -> {
                    rc.setHierarchyRootData(data);
                    return rc;
                }));

                return;
            }

            RemoteCollectionItems items = mUriToCollectionMapping.get(uri);
            items.setHierarchyRootData(data);
        }

        RemoteCollectionItems getItemsForId(int intentId) {
            String uri = mIdToUriMapping.get(intentId);
            return mUriToCollectionMapping.get(uri);
        }

        void collectAllIntentsNoComplete(@NonNull RemoteViews inViews) {
            if (inViews.hasSizedRemoteViews()) {
                for (RemoteViews remoteViews : inViews.mSizedRemoteViews) {
                    remoteViews.collectAllIntents();
                }
            } else if (inViews.hasLandscapeAndPortraitLayouts()) {
                inViews.mLandscape.collectAllIntents();
                inViews.mPortrait.collectAllIntents();
            } else if (inViews.mActions != null) {
                for (Action action : inViews.mActions) {
                    if (action instanceof SetRemoteCollectionItemListAdapterAction rca) {
                        // Deal with the case where the intent is replaced into the action list
                        if (rca.mIntentId != -1 && !rca.mIsReplacedIntoAction) {
                            continue;
                        }

                        if (rca.mIntentId != -1 && rca.mIsReplacedIntoAction) {
                            String uri = mIdToUriMapping.get(rca.mIntentId);
                            mTempUriToFutureMapping.put(uri, rca.mItemsFuture);
                            rca.mItemsFuture = CompletableFuture.completedFuture(null);
                            continue;
                        }

                        // Differentiate between the normal collection actions and the ones with
                        // intents.
                        if (rca.mServiceIntent != null) {
                            String uri = rca.mServiceIntent.toUri(0);
                            int index = mIdToUriMapping.indexOfValue(uri);
                            if (index == -1) {
                                int newIntentId = mIdToUriMapping.size();
                                rca.mIntentId = newIntentId;
                                mIdToUriMapping.put(newIntentId, uri);
                                // mUriToIntentMapping.put(uri, mServiceIntent);
                                mTempUriToFutureMapping.put(uri, rca.mItemsFuture);
                            } else {
                                rca.mIntentId = mIdToUriMapping.keyAt(index);
                            }
                            rca.mItemsFuture = CompletableFuture.completedFuture(null);
                        } else {
                            RemoteCollectionItems items = getCollectionItemsFromFuture(
                                    rca.mItemsFuture);
                            for (RemoteViews views : items.mViews) {
                                views.collectAllIntents();
                            }
                        }
                    } else if (action instanceof ViewGroupActionAdd vgaa
                            && vgaa.mNestedViews != null) {
                        vgaa.mNestedViews.collectAllIntents();
                    }
                }
            }
        }

        public void writeToParcel(Parcel out, int flags) {
            out.writeInt(mIdToUriMapping.size());
            for (int i = 0; i < mIdToUriMapping.size(); i++) {
                out.writeInt(mIdToUriMapping.keyAt(i));
                String intentUri = mIdToUriMapping.valueAt(i);
                out.writeString8(intentUri);
                RemoteCollectionItems items = mTempUriToFutureMapping.get(intentUri) != null
                        ? getCollectionItemsFromFuture(mTempUriToFutureMapping.get(intentUri))
                        : mUriToCollectionMapping.get(intentUri);
                items.writeToParcel(out, flags, true);
            }
        }
    }

    private class SetRemoteViewsAdapterIntent extends Action {
    private class SetRemoteViewsAdapterIntent extends Action {
        Intent mIntent;
        Intent mIntent;
        boolean mIsAsync = false;
        boolean mIsAsync = false;
@@ -3992,9 +4150,12 @@ public class RemoteViews implements Parcelable, Filter {
    private void initializeFrom(@NonNull RemoteViews src, @Nullable RemoteViews hierarchyRoot) {
    private void initializeFrom(@NonNull RemoteViews src, @Nullable RemoteViews hierarchyRoot) {
        if (hierarchyRoot == null) {
        if (hierarchyRoot == null) {
            mBitmapCache = src.mBitmapCache;
            mBitmapCache = src.mBitmapCache;
            // We need to create a new instance because we don't reconstruct collection cache
            mCollectionCache = new RemoteCollectionCache(src.mCollectionCache);
            mApplicationInfoCache = src.mApplicationInfoCache;
            mApplicationInfoCache = src.mApplicationInfoCache;
        } else {
        } else {
            mBitmapCache = hierarchyRoot.mBitmapCache;
            mBitmapCache = hierarchyRoot.mBitmapCache;
            mCollectionCache = hierarchyRoot.mCollectionCache;
            mApplicationInfoCache = hierarchyRoot.mApplicationInfoCache;
            mApplicationInfoCache = hierarchyRoot.mApplicationInfoCache;
        }
        }
        if (hierarchyRoot == null || src.mIsRoot) {
        if (hierarchyRoot == null || src.mIsRoot) {
@@ -4068,6 +4229,7 @@ public class RemoteViews implements Parcelable, Filter {
            mBitmapCache = new BitmapCache(parcel);
            mBitmapCache = new BitmapCache(parcel);
            // Store the class cookies such that they are available when we clone this RemoteView.
            // Store the class cookies such that they are available when we clone this RemoteView.
            mClassCookies = parcel.copyClassCookies();
            mClassCookies = parcel.copyClassCookies();
            mCollectionCache = new RemoteCollectionCache(parcel);
        } else {
        } else {
            configureAsChild(rootData);
            configureAsChild(rootData);
        }
        }
@@ -4233,6 +4395,7 @@ public class RemoteViews implements Parcelable, Filter {
    private void configureAsChild(@NonNull HierarchyRootData rootData) {
    private void configureAsChild(@NonNull HierarchyRootData rootData) {
        mIsRoot = false;
        mIsRoot = false;
        mBitmapCache = rootData.mBitmapCache;
        mBitmapCache = rootData.mBitmapCache;
        mCollectionCache = rootData.mRemoteCollectionCache;
        mApplicationInfoCache = rootData.mApplicationInfoCache;
        mApplicationInfoCache = rootData.mApplicationInfoCache;
        mClassCookies = rootData.mClassCookies;
        mClassCookies = rootData.mClassCookies;
        configureDescendantsAsChildren();
        configureDescendantsAsChildren();
@@ -6499,6 +6662,7 @@ public class RemoteViews implements Parcelable, Filter {
            // is shared by all children.
            // is shared by all children.
            if (mIsRoot) {
            if (mIsRoot) {
                mBitmapCache.writeBitmapsToParcel(dest, flags);
                mBitmapCache.writeBitmapsToParcel(dest, flags);
                mCollectionCache.writeToParcel(dest, flags);
            }
            }
            mApplication.writeToParcel(dest, flags);
            mApplication.writeToParcel(dest, flags);
            if (mIsRoot || mIdealSize == null) {
            if (mIsRoot || mIdealSize == null) {
@@ -6515,6 +6679,7 @@ public class RemoteViews implements Parcelable, Filter {
            dest.writeInt(MODE_HAS_SIZED_REMOTEVIEWS);
            dest.writeInt(MODE_HAS_SIZED_REMOTEVIEWS);
            if (mIsRoot) {
            if (mIsRoot) {
                mBitmapCache.writeBitmapsToParcel(dest, flags);
                mBitmapCache.writeBitmapsToParcel(dest, flags);
                mCollectionCache.writeToParcel(dest, flags);
            }
            }
            dest.writeInt(mSizedRemoteViews.size());
            dest.writeInt(mSizedRemoteViews.size());
            for (RemoteViews view : mSizedRemoteViews) {
            for (RemoteViews view : mSizedRemoteViews) {
@@ -6526,6 +6691,7 @@ public class RemoteViews implements Parcelable, Filter {
            // is shared by all children.
            // is shared by all children.
            if (mIsRoot) {
            if (mIsRoot) {
                mBitmapCache.writeBitmapsToParcel(dest, flags);
                mBitmapCache.writeBitmapsToParcel(dest, flags);
                mCollectionCache.writeToParcel(dest, flags);
            }
            }
            mLandscape.writeToParcel(dest, flags);
            mLandscape.writeToParcel(dest, flags);
            // Both RemoteViews already share the same package and user
            // Both RemoteViews already share the same package and user
@@ -7404,19 +7570,23 @@ public class RemoteViews implements Parcelable, Filter {
    }
    }


    private HierarchyRootData getHierarchyRootData() {
    private HierarchyRootData getHierarchyRootData() {
        return new HierarchyRootData(mBitmapCache, mApplicationInfoCache, mClassCookies);
        return new HierarchyRootData(mBitmapCache, mCollectionCache,
                mApplicationInfoCache, mClassCookies);
    }
    }


    private static final class HierarchyRootData {
    private static final class HierarchyRootData {
        final BitmapCache mBitmapCache;
        final BitmapCache mBitmapCache;
        final RemoteCollectionCache mRemoteCollectionCache;
        final ApplicationInfoCache mApplicationInfoCache;
        final ApplicationInfoCache mApplicationInfoCache;
        final Map<Class, Object> mClassCookies;
        final Map<Class, Object> mClassCookies;


        HierarchyRootData(
        HierarchyRootData(
                BitmapCache bitmapCache,
                BitmapCache bitmapCache,
                RemoteCollectionCache remoteCollectionCache,
                ApplicationInfoCache applicationInfoCache,
                ApplicationInfoCache applicationInfoCache,
                Map<Class, Object> classCookies) {
                Map<Class, Object> classCookies) {
            mBitmapCache = bitmapCache;
            mBitmapCache = bitmapCache;
            mRemoteCollectionCache = remoteCollectionCache;
            mApplicationInfoCache = applicationInfoCache;
            mApplicationInfoCache = applicationInfoCache;
            mClassCookies = classCookies;
            mClassCookies = classCookies;
        }
        }