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

Commit 3e54a58f authored by Sihua Ma's avatar Sihua Ma Committed by Android (Google) Code Review
Browse files

Merge "Create remote collection cache to eliminate duplicate intent" into main

parents 336dee11 4a588436
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;
@@ -367,6 +368,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();


@@ -784,9 +790,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) {
@@ -1048,6 +1057,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) {
@@ -1108,38 +1119,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);
        }
        }


@@ -1149,7 +1158,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)) {
@@ -1210,6 +1221,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;
@@ -3850,9 +4008,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) {
@@ -3926,6 +4087,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);
        }
        }
@@ -4087,6 +4249,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();
@@ -6357,6 +6520,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) {
@@ -6373,6 +6537,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) {
@@ -6384,6 +6549,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
@@ -7262,19 +7428,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;
        }
        }