Merge "Optimize parcelization protocol in RemoteViews and RemoteCollectionItems" into sc-v2-dev
This commit is contained in:
committed by
Android (Google) Code Review
commit
742187b89d
@@ -338,7 +338,10 @@ public class RemoteViews implements Parcelable, Filter {
|
|||||||
* Maps bitmaps to unique indicies to avoid Bitmap duplication.
|
* Maps bitmaps to unique indicies to avoid Bitmap duplication.
|
||||||
*/
|
*/
|
||||||
@UnsupportedAppUsage
|
@UnsupportedAppUsage
|
||||||
private BitmapCache mBitmapCache;
|
private BitmapCache mBitmapCache = new BitmapCache();
|
||||||
|
|
||||||
|
/** Cache of ApplicationInfos used by collection items. */
|
||||||
|
private ApplicationInfoCache mApplicationInfoCache = new ApplicationInfoCache();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Indicates whether or not this RemoteViews object is contained as a child of any other
|
* Indicates whether or not this RemoteViews object is contained as a child of any other
|
||||||
@@ -576,7 +579,7 @@ public class RemoteViews implements Parcelable, Filter {
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setBitmapCache(BitmapCache bitmapCache) {
|
public void setHierarchyRootData(HierarchyRootData root) {
|
||||||
// Do nothing
|
// Do nothing
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -605,14 +608,6 @@ public class RemoteViews implements Parcelable, Filter {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Overridden by subclasses which have (or inherit) an ApplicationInfo instance
|
|
||||||
* as member variable
|
|
||||||
*/
|
|
||||||
public boolean hasSameAppInfo(ApplicationInfo parentInfo) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void visitUris(@NonNull Consumer<Uri> visitor) {
|
public void visitUris(@NonNull Consumer<Uri> visitor) {
|
||||||
// Nothing to visit by default
|
// Nothing to visit by default
|
||||||
}
|
}
|
||||||
@@ -690,9 +685,8 @@ public class RemoteViews implements Parcelable, Filter {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Because pruning can remove the need for bitmaps, we reconstruct the bitmap cache
|
// Because pruning can remove the need for bitmaps, we reconstruct the caches.
|
||||||
mBitmapCache = new BitmapCache();
|
reconstructCaches();
|
||||||
setBitmapCache(mBitmapCache);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -938,23 +932,70 @@ public class RemoteViews implements Parcelable, Filter {
|
|||||||
ArrayList<RemoteViews> list;
|
ArrayList<RemoteViews> list;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static class SetRemoteCollectionItemListAdapterAction extends Action {
|
/**
|
||||||
|
* Cache of {@link ApplicationInfo}s that can be used to ensure that the same
|
||||||
|
* {@link ApplicationInfo} instance is used throughout the RemoteViews.
|
||||||
|
*/
|
||||||
|
private static class ApplicationInfoCache {
|
||||||
|
private final Map<Pair<String, Integer>, ApplicationInfo> mPackageUserToApplicationInfo;
|
||||||
|
|
||||||
|
ApplicationInfoCache() {
|
||||||
|
mPackageUserToApplicationInfo = new ArrayMap<>();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds the {@link ApplicationInfo} to the cache if it's not present. Returns either the
|
||||||
|
* provided {@code applicationInfo} or a previously added value with the same package name
|
||||||
|
* and uid.
|
||||||
|
*/
|
||||||
|
@Nullable
|
||||||
|
ApplicationInfo getOrPut(@Nullable ApplicationInfo applicationInfo) {
|
||||||
|
Pair<String, Integer> key = getPackageUserKey(applicationInfo);
|
||||||
|
if (key == null) return null;
|
||||||
|
return mPackageUserToApplicationInfo.computeIfAbsent(key, ignored -> applicationInfo);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Puts the {@link ApplicationInfo} in the cache, replacing any previously stored value. */
|
||||||
|
void put(@Nullable ApplicationInfo applicationInfo) {
|
||||||
|
Pair<String, Integer> key = getPackageUserKey(applicationInfo);
|
||||||
|
if (key == null) return;
|
||||||
|
mPackageUserToApplicationInfo.put(key, applicationInfo);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the currently stored {@link ApplicationInfo} from the cache matching
|
||||||
|
* {@code applicationInfo}, or null if there wasn't any.
|
||||||
|
*/
|
||||||
|
@Nullable ApplicationInfo get(@Nullable ApplicationInfo applicationInfo) {
|
||||||
|
Pair<String, Integer> key = getPackageUserKey(applicationInfo);
|
||||||
|
if (key == null) return null;
|
||||||
|
return mPackageUserToApplicationInfo.get(key);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private class SetRemoteCollectionItemListAdapterAction extends Action {
|
||||||
private final RemoteCollectionItems mItems;
|
private final RemoteCollectionItems mItems;
|
||||||
|
|
||||||
SetRemoteCollectionItemListAdapterAction(@IdRes int id, RemoteCollectionItems items) {
|
SetRemoteCollectionItemListAdapterAction(@IdRes int id, RemoteCollectionItems items) {
|
||||||
viewId = id;
|
viewId = id;
|
||||||
mItems = items;
|
mItems = items;
|
||||||
|
mItems.setHierarchyRootData(getHierarchyRootData());
|
||||||
}
|
}
|
||||||
|
|
||||||
SetRemoteCollectionItemListAdapterAction(Parcel parcel) {
|
SetRemoteCollectionItemListAdapterAction(Parcel parcel) {
|
||||||
viewId = parcel.readInt();
|
viewId = parcel.readInt();
|
||||||
mItems = parcel.readTypedObject(RemoteCollectionItems.CREATOR);
|
mItems = new RemoteCollectionItems(parcel, getHierarchyRootData());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setHierarchyRootData(HierarchyRootData rootData) {
|
||||||
|
mItems.setHierarchyRootData(rootData);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void writeToParcel(Parcel dest, int flags) {
|
public void writeToParcel(Parcel dest, int flags) {
|
||||||
dest.writeInt(viewId);
|
dest.writeInt(viewId);
|
||||||
dest.writeTypedObject(mItems, flags);
|
mItems.writeToParcel(dest, flags, /* attached= */ true);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -1602,8 +1643,8 @@ public class RemoteViews implements Parcelable, Filter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void setBitmapCache(BitmapCache bitmapCache) {
|
public void setHierarchyRootData(HierarchyRootData rootData) {
|
||||||
bitmapId = bitmapCache.getBitmapId(bitmap);
|
bitmapId = rootData.mBitmapCache.getBitmapId(bitmap);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -2220,15 +2261,6 @@ public class RemoteViews implements Parcelable, Filter {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void configureRemoteViewsAsChild(RemoteViews rv) {
|
|
||||||
rv.setBitmapCache(mBitmapCache);
|
|
||||||
rv.setNotRoot();
|
|
||||||
}
|
|
||||||
|
|
||||||
void setNotRoot() {
|
|
||||||
mIsRoot = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static boolean hasStableId(View view) {
|
private static boolean hasStableId(View view) {
|
||||||
Object tag = view.getTag(com.android.internal.R.id.remote_views_stable_id);
|
Object tag = view.getTag(com.android.internal.R.id.remote_views_stable_id);
|
||||||
return tag != null;
|
return tag != null;
|
||||||
@@ -2302,17 +2334,14 @@ public class RemoteViews implements Parcelable, Filter {
|
|||||||
mNestedViews = nestedViews;
|
mNestedViews = nestedViews;
|
||||||
mIndex = index;
|
mIndex = index;
|
||||||
mStableId = stableId;
|
mStableId = stableId;
|
||||||
if (nestedViews != null) {
|
nestedViews.configureAsChild(getHierarchyRootData());
|
||||||
configureRemoteViewsAsChild(nestedViews);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ViewGroupActionAdd(Parcel parcel, BitmapCache bitmapCache, ApplicationInfo info,
|
ViewGroupActionAdd(Parcel parcel, ApplicationInfo info, int depth) {
|
||||||
int depth, Map<Class, Object> classCookies) {
|
|
||||||
viewId = parcel.readInt();
|
viewId = parcel.readInt();
|
||||||
mIndex = parcel.readInt();
|
mIndex = parcel.readInt();
|
||||||
mStableId = parcel.readInt();
|
mStableId = parcel.readInt();
|
||||||
mNestedViews = new RemoteViews(parcel, bitmapCache, info, depth, classCookies);
|
mNestedViews = new RemoteViews(parcel, getHierarchyRootData(), info, depth);
|
||||||
mNestedViews.addFlags(mApplyFlags);
|
mNestedViews.addFlags(mApplyFlags);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2324,8 +2353,8 @@ public class RemoteViews implements Parcelable, Filter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean hasSameAppInfo(ApplicationInfo parentInfo) {
|
public void setHierarchyRootData(HierarchyRootData root) {
|
||||||
return mNestedViews.hasSameAppInfo(parentInfo);
|
mNestedViews.configureAsChild(root);
|
||||||
}
|
}
|
||||||
|
|
||||||
private int findViewIndexToRecycle(ViewGroup target, RemoteViews newContent) {
|
private int findViewIndexToRecycle(ViewGroup target, RemoteViews newContent) {
|
||||||
@@ -2493,11 +2522,6 @@ public class RemoteViews implements Parcelable, Filter {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public void setBitmapCache(BitmapCache bitmapCache) {
|
|
||||||
mNestedViews.setBitmapCache(bitmapCache);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int mergeBehavior() {
|
public int mergeBehavior() {
|
||||||
return MERGE_APPEND;
|
return MERGE_APPEND;
|
||||||
@@ -3505,8 +3529,7 @@ public class RemoteViews implements Parcelable, Filter {
|
|||||||
protected RemoteViews(ApplicationInfo application, @LayoutRes int layoutId) {
|
protected RemoteViews(ApplicationInfo application, @LayoutRes int layoutId) {
|
||||||
mApplication = application;
|
mApplication = application;
|
||||||
mLayoutId = layoutId;
|
mLayoutId = layoutId;
|
||||||
mBitmapCache = new BitmapCache();
|
mApplicationInfoCache.put(application);
|
||||||
mClassCookies = null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean hasMultipleLayouts() {
|
private boolean hasMultipleLayouts() {
|
||||||
@@ -3562,12 +3585,10 @@ public class RemoteViews implements Parcelable, Filter {
|
|||||||
mLandscape = landscape;
|
mLandscape = landscape;
|
||||||
mPortrait = portrait;
|
mPortrait = portrait;
|
||||||
|
|
||||||
mBitmapCache = new BitmapCache();
|
|
||||||
configureRemoteViewsAsChild(landscape);
|
|
||||||
configureRemoteViewsAsChild(portrait);
|
|
||||||
|
|
||||||
mClassCookies = (portrait.mClassCookies != null)
|
mClassCookies = (portrait.mClassCookies != null)
|
||||||
? portrait.mClassCookies : landscape.mClassCookies;
|
? portrait.mClassCookies : landscape.mClassCookies;
|
||||||
|
|
||||||
|
configureDescendantsAsChildren();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -3593,10 +3614,12 @@ public class RemoteViews implements Parcelable, Filter {
|
|||||||
throw new IllegalArgumentException("Too many RemoteViews in constructor");
|
throw new IllegalArgumentException("Too many RemoteViews in constructor");
|
||||||
}
|
}
|
||||||
if (remoteViews.size() == 1) {
|
if (remoteViews.size() == 1) {
|
||||||
initializeFrom(remoteViews.values().iterator().next());
|
// If the map only contains a single mapping, treat this as if that RemoteViews was
|
||||||
|
// passed as the top-level RemoteViews.
|
||||||
|
RemoteViews single = remoteViews.values().iterator().next();
|
||||||
|
initializeFrom(single, /* hierarchyRoot= */ single);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
mBitmapCache = new BitmapCache();
|
|
||||||
mClassCookies = initializeSizedRemoteViews(
|
mClassCookies = initializeSizedRemoteViews(
|
||||||
remoteViews.entrySet().stream().map(
|
remoteViews.entrySet().stream().map(
|
||||||
entry -> {
|
entry -> {
|
||||||
@@ -3611,6 +3634,8 @@ public class RemoteViews implements Parcelable, Filter {
|
|||||||
mLayoutId = smallestView.mLayoutId;
|
mLayoutId = smallestView.mLayoutId;
|
||||||
mViewId = smallestView.mViewId;
|
mViewId = smallestView.mViewId;
|
||||||
mLightBackgroundLayoutId = smallestView.mLightBackgroundLayoutId;
|
mLightBackgroundLayoutId = smallestView.mLightBackgroundLayoutId;
|
||||||
|
|
||||||
|
configureDescendantsAsChildren();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Initialize mSizedRemoteViews and return the class cookies.
|
// Initialize mSizedRemoteViews and return the class cookies.
|
||||||
@@ -3639,7 +3664,6 @@ public class RemoteViews implements Parcelable, Filter {
|
|||||||
} else {
|
} else {
|
||||||
sizedRemoteViews.add(view);
|
sizedRemoteViews.add(view);
|
||||||
}
|
}
|
||||||
configureRemoteViewsAsChild(view);
|
|
||||||
view.setIdealSize(size);
|
view.setIdealSize(size);
|
||||||
if (classCookies == null) {
|
if (classCookies == null) {
|
||||||
classCookies = view.mClassCookies;
|
classCookies = view.mClassCookies;
|
||||||
@@ -3654,13 +3678,38 @@ public class RemoteViews implements Parcelable, Filter {
|
|||||||
* Creates a copy of another RemoteViews.
|
* Creates a copy of another RemoteViews.
|
||||||
*/
|
*/
|
||||||
public RemoteViews(RemoteViews src) {
|
public RemoteViews(RemoteViews src) {
|
||||||
initializeFrom(src);
|
initializeFrom(src, /* hierarchyRoot= */ null);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void initializeFrom(RemoteViews src) {
|
/**
|
||||||
mBitmapCache = src.mBitmapCache;
|
* No-arg constructor for use with {@link #initializeFrom(RemoteViews, RemoteViews)}. A
|
||||||
|
* constructor taking two RemoteViews parameters would clash with the landscape/portrait
|
||||||
|
* constructor.
|
||||||
|
*/
|
||||||
|
private RemoteViews() {}
|
||||||
|
|
||||||
|
private static RemoteViews createInitializedFrom(@NonNull RemoteViews src,
|
||||||
|
@Nullable RemoteViews hierarchyRoot) {
|
||||||
|
RemoteViews child = new RemoteViews();
|
||||||
|
child.initializeFrom(src, hierarchyRoot);
|
||||||
|
return child;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void initializeFrom(@NonNull RemoteViews src, @Nullable RemoteViews hierarchyRoot) {
|
||||||
|
if (hierarchyRoot == null || src.mIsRoot) {
|
||||||
|
// If there's no provided root, or if src was itself a root, then this RemoteViews is
|
||||||
|
// the root of the new hierarchy.
|
||||||
|
mIsRoot = true;
|
||||||
|
mBitmapCache = new BitmapCache();
|
||||||
|
mApplicationInfoCache = new ApplicationInfoCache();
|
||||||
|
hierarchyRoot = this;
|
||||||
|
} else {
|
||||||
|
// Otherwise, we're a descendant in the hierarchy.
|
||||||
|
mIsRoot = false;
|
||||||
|
mBitmapCache = hierarchyRoot.mBitmapCache;
|
||||||
|
mApplicationInfoCache = hierarchyRoot.mApplicationInfoCache;
|
||||||
|
}
|
||||||
mApplication = src.mApplication;
|
mApplication = src.mApplication;
|
||||||
mIsRoot = src.mIsRoot;
|
|
||||||
mLayoutId = src.mLayoutId;
|
mLayoutId = src.mLayoutId;
|
||||||
mLightBackgroundLayoutId = src.mLightBackgroundLayoutId;
|
mLightBackgroundLayoutId = src.mLightBackgroundLayoutId;
|
||||||
mApplyFlags = src.mApplyFlags;
|
mApplyFlags = src.mApplyFlags;
|
||||||
@@ -3669,21 +3718,21 @@ public class RemoteViews implements Parcelable, Filter {
|
|||||||
mProviderInstanceId = src.mProviderInstanceId;
|
mProviderInstanceId = src.mProviderInstanceId;
|
||||||
|
|
||||||
if (src.hasLandscapeAndPortraitLayouts()) {
|
if (src.hasLandscapeAndPortraitLayouts()) {
|
||||||
mLandscape = new RemoteViews(src.mLandscape);
|
mLandscape = createInitializedFrom(src.mLandscape, hierarchyRoot);
|
||||||
mPortrait = new RemoteViews(src.mPortrait);
|
mPortrait = createInitializedFrom(src.mPortrait, hierarchyRoot);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (src.hasSizedRemoteViews()) {
|
if (src.hasSizedRemoteViews()) {
|
||||||
mSizedRemoteViews = new ArrayList<>(src.mSizedRemoteViews.size());
|
mSizedRemoteViews = new ArrayList<>(src.mSizedRemoteViews.size());
|
||||||
for (RemoteViews srcView : src.mSizedRemoteViews) {
|
for (RemoteViews srcView : src.mSizedRemoteViews) {
|
||||||
mSizedRemoteViews.add(new RemoteViews(srcView));
|
mSizedRemoteViews.add(createInitializedFrom(srcView, hierarchyRoot));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (src.mActions != null) {
|
if (src.mActions != null) {
|
||||||
Parcel p = Parcel.obtain();
|
Parcel p = Parcel.obtain();
|
||||||
p.putClassCookies(mClassCookies);
|
p.putClassCookies(mClassCookies);
|
||||||
src.writeActionsToParcel(p);
|
src.writeActionsToParcel(p, /* flags= */ 0);
|
||||||
p.setDataPosition(0);
|
p.setDataPosition(0);
|
||||||
// Since src is already in memory, we do not care about stack overflow as it has
|
// Since src is already in memory, we do not care about stack overflow as it has
|
||||||
// already been read once.
|
// already been read once.
|
||||||
@@ -3691,9 +3740,11 @@ public class RemoteViews implements Parcelable, Filter {
|
|||||||
p.recycle();
|
p.recycle();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Now that everything is initialized and duplicated, setting a new BitmapCache will
|
// Now that everything is initialized and duplicated, create new caches for this
|
||||||
// re-initialize the cache.
|
// RemoteViews and recursively set up all descendants.
|
||||||
setBitmapCache(new BitmapCache());
|
if (mIsRoot) {
|
||||||
|
reconstructCaches();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -3702,11 +3753,11 @@ public class RemoteViews implements Parcelable, Filter {
|
|||||||
* @param parcel
|
* @param parcel
|
||||||
*/
|
*/
|
||||||
public RemoteViews(Parcel parcel) {
|
public RemoteViews(Parcel parcel) {
|
||||||
this(parcel, null, null, 0, null);
|
this(parcel, /* rootParent= */ null, /* info= */ null, /* depth= */ 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
private RemoteViews(Parcel parcel, BitmapCache bitmapCache, ApplicationInfo info, int depth,
|
private RemoteViews(@NonNull Parcel parcel, @Nullable HierarchyRootData rootData,
|
||||||
Map<Class, Object> classCookies) {
|
@Nullable ApplicationInfo info, int depth) {
|
||||||
if (depth > MAX_NESTED_VIEWS
|
if (depth > MAX_NESTED_VIEWS
|
||||||
&& (UserHandle.getAppId(Binder.getCallingUid()) != Process.SYSTEM_UID)) {
|
&& (UserHandle.getAppId(Binder.getCallingUid()) != Process.SYSTEM_UID)) {
|
||||||
throw new IllegalArgumentException("Too many nested views.");
|
throw new IllegalArgumentException("Too many nested views.");
|
||||||
@@ -3715,20 +3766,17 @@ public class RemoteViews implements Parcelable, Filter {
|
|||||||
|
|
||||||
int mode = parcel.readInt();
|
int mode = parcel.readInt();
|
||||||
|
|
||||||
// We only store a bitmap cache in the root of the RemoteViews.
|
if (rootData == null) {
|
||||||
if (bitmapCache == null) {
|
// We only store a bitmap cache in the root of the RemoteViews.
|
||||||
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();
|
||||||
} else {
|
} else {
|
||||||
setBitmapCache(bitmapCache);
|
configureAsChild(rootData);
|
||||||
mClassCookies = classCookies;
|
|
||||||
setNotRoot();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (mode == MODE_NORMAL) {
|
if (mode == MODE_NORMAL) {
|
||||||
mApplication = parcel.readInt() == 0 ? info :
|
mApplication = ApplicationInfo.CREATOR.createFromParcel(parcel);
|
||||||
ApplicationInfo.CREATOR.createFromParcel(parcel);
|
|
||||||
mIdealSize = parcel.readInt() == 0 ? null : SizeF.CREATOR.createFromParcel(parcel);
|
mIdealSize = parcel.readInt() == 0 ? null : SizeF.CREATOR.createFromParcel(parcel);
|
||||||
mLayoutId = parcel.readInt();
|
mLayoutId = parcel.readInt();
|
||||||
mViewId = parcel.readInt();
|
mViewId = parcel.readInt();
|
||||||
@@ -3743,8 +3791,7 @@ public class RemoteViews implements Parcelable, Filter {
|
|||||||
}
|
}
|
||||||
List<RemoteViews> remoteViews = new ArrayList<>(numViews);
|
List<RemoteViews> remoteViews = new ArrayList<>(numViews);
|
||||||
for (int i = 0; i < numViews; i++) {
|
for (int i = 0; i < numViews; i++) {
|
||||||
RemoteViews view = new RemoteViews(parcel, mBitmapCache, info, depth,
|
RemoteViews view = new RemoteViews(parcel, getHierarchyRootData(), info, depth);
|
||||||
mClassCookies);
|
|
||||||
info = view.mApplication;
|
info = view.mApplication;
|
||||||
remoteViews.add(view);
|
remoteViews.add(view);
|
||||||
}
|
}
|
||||||
@@ -3756,9 +3803,9 @@ public class RemoteViews implements Parcelable, Filter {
|
|||||||
mLightBackgroundLayoutId = smallestView.mLightBackgroundLayoutId;
|
mLightBackgroundLayoutId = smallestView.mLightBackgroundLayoutId;
|
||||||
} else {
|
} else {
|
||||||
// MODE_HAS_LANDSCAPE_AND_PORTRAIT
|
// MODE_HAS_LANDSCAPE_AND_PORTRAIT
|
||||||
mLandscape = new RemoteViews(parcel, mBitmapCache, info, depth, mClassCookies);
|
mLandscape = new RemoteViews(parcel, getHierarchyRootData(), info, depth);
|
||||||
mPortrait = new RemoteViews(parcel, mBitmapCache, mLandscape.mApplication, depth,
|
mPortrait =
|
||||||
mClassCookies);
|
new RemoteViews(parcel, getHierarchyRootData(), mLandscape.mApplication, depth);
|
||||||
mApplication = mPortrait.mApplication;
|
mApplication = mPortrait.mApplication;
|
||||||
mLayoutId = mPortrait.mLayoutId;
|
mLayoutId = mPortrait.mLayoutId;
|
||||||
mViewId = mPortrait.mViewId;
|
mViewId = mPortrait.mViewId;
|
||||||
@@ -3766,6 +3813,11 @@ public class RemoteViews implements Parcelable, Filter {
|
|||||||
}
|
}
|
||||||
mApplyFlags = parcel.readInt();
|
mApplyFlags = parcel.readInt();
|
||||||
mProviderInstanceId = parcel.readLong();
|
mProviderInstanceId = parcel.readLong();
|
||||||
|
|
||||||
|
// Ensure that all descendants have their caches set up recursively.
|
||||||
|
if (mIsRoot) {
|
||||||
|
configureDescendantsAsChildren();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void readActionsFromParcel(Parcel parcel, int depth) {
|
private void readActionsFromParcel(Parcel parcel, int depth) {
|
||||||
@@ -3788,8 +3840,7 @@ public class RemoteViews implements Parcelable, Filter {
|
|||||||
case REFLECTION_ACTION_TAG:
|
case REFLECTION_ACTION_TAG:
|
||||||
return new ReflectionAction(parcel);
|
return new ReflectionAction(parcel);
|
||||||
case VIEW_GROUP_ACTION_ADD_TAG:
|
case VIEW_GROUP_ACTION_ADD_TAG:
|
||||||
return new ViewGroupActionAdd(parcel, mBitmapCache, mApplication, depth,
|
return new ViewGroupActionAdd(parcel, mApplication, depth);
|
||||||
mClassCookies);
|
|
||||||
case VIEW_GROUP_ACTION_REMOVE_TAG:
|
case VIEW_GROUP_ACTION_REMOVE_TAG:
|
||||||
return new ViewGroupActionRemove(parcel);
|
return new ViewGroupActionRemove(parcel);
|
||||||
case VIEW_CONTENT_NAVIGATION_TAG:
|
case VIEW_CONTENT_NAVIGATION_TAG:
|
||||||
@@ -3879,27 +3930,55 @@ public class RemoteViews implements Parcelable, Filter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Recursively sets BitmapCache in the hierarchy and update the bitmap ids.
|
* Sets the root of the hierarchy and then recursively traverses the tree to update the root
|
||||||
|
* and populate caches for all descendants.
|
||||||
*/
|
*/
|
||||||
private void setBitmapCache(BitmapCache bitmapCache) {
|
private void configureAsChild(@NonNull HierarchyRootData rootData) {
|
||||||
mBitmapCache = bitmapCache;
|
mIsRoot = false;
|
||||||
|
mBitmapCache = rootData.mBitmapCache;
|
||||||
|
mApplicationInfoCache = rootData.mApplicationInfoCache;
|
||||||
|
mClassCookies = rootData.mClassCookies;
|
||||||
|
configureDescendantsAsChildren();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Recursively traverses the tree to update the root and populate caches for all descendants.
|
||||||
|
*/
|
||||||
|
private void configureDescendantsAsChildren() {
|
||||||
|
// Before propagating down the tree, replace our application from the root application info
|
||||||
|
// cache, to ensure the same instance is present throughout the hierarchy to allow for
|
||||||
|
// squashing.
|
||||||
|
mApplication = mApplicationInfoCache.getOrPut(mApplication);
|
||||||
|
|
||||||
|
HierarchyRootData rootData = getHierarchyRootData();
|
||||||
if (hasSizedRemoteViews()) {
|
if (hasSizedRemoteViews()) {
|
||||||
for (RemoteViews remoteView : mSizedRemoteViews) {
|
for (RemoteViews remoteView : mSizedRemoteViews) {
|
||||||
remoteView.setBitmapCache(bitmapCache);
|
remoteView.configureAsChild(rootData);
|
||||||
}
|
}
|
||||||
} else if (hasLandscapeAndPortraitLayouts()) {
|
} else if (hasLandscapeAndPortraitLayouts()) {
|
||||||
mLandscape.setBitmapCache(bitmapCache);
|
mLandscape.configureAsChild(rootData);
|
||||||
mPortrait.setBitmapCache(bitmapCache);
|
mPortrait.configureAsChild(rootData);
|
||||||
} else {
|
} else {
|
||||||
if (mActions != null) {
|
if (mActions != null) {
|
||||||
final int count = mActions.size();
|
for (Action action : mActions) {
|
||||||
for (int i = 0; i < count; ++i) {
|
action.setHierarchyRootData(rootData);
|
||||||
mActions.get(i).setBitmapCache(bitmapCache);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Recreates caches at the root level of the hierarchy, then recursively populates the caches
|
||||||
|
* down the hierarchy.
|
||||||
|
*/
|
||||||
|
private void reconstructCaches() {
|
||||||
|
if (!mIsRoot) return;
|
||||||
|
mBitmapCache = new BitmapCache();
|
||||||
|
mApplicationInfoCache = new ApplicationInfoCache();
|
||||||
|
mApplication = mApplicationInfoCache.getOrPut(mApplication);
|
||||||
|
configureDescendantsAsChildren();
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns an estimate of the bitmap heap memory usage for this RemoteViews.
|
* Returns an estimate of the bitmap heap memory usage for this RemoteViews.
|
||||||
*/
|
*/
|
||||||
@@ -5848,21 +5927,18 @@ public class RemoteViews implements Parcelable, Filter {
|
|||||||
|
|
||||||
/** @hide */
|
/** @hide */
|
||||||
public void updateAppInfo(@NonNull ApplicationInfo info) {
|
public void updateAppInfo(@NonNull ApplicationInfo info) {
|
||||||
if (mApplication != null && mApplication.sourceDir.equals(info.sourceDir)) {
|
ApplicationInfo existing = mApplicationInfoCache.get(info);
|
||||||
|
if (existing != null && !existing.sourceDir.equals(info.sourceDir)) {
|
||||||
// Overlay paths are generated against a particular version of an application.
|
// Overlay paths are generated against a particular version of an application.
|
||||||
// The overlays paths of a newly upgraded application are incompatible with the
|
// The overlays paths of a newly upgraded application are incompatible with the
|
||||||
// old version of the application.
|
// old version of the application.
|
||||||
mApplication = info;
|
return;
|
||||||
}
|
|
||||||
if (hasSizedRemoteViews()) {
|
|
||||||
for (RemoteViews layout : mSizedRemoteViews) {
|
|
||||||
layout.updateAppInfo(info);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (hasLandscapeAndPortraitLayouts()) {
|
|
||||||
mLandscape.updateAppInfo(info);
|
|
||||||
mPortrait.updateAppInfo(info);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// If we can update to the new AppInfo, put it in the cache and propagate the change
|
||||||
|
// throughout the hierarchy.
|
||||||
|
mApplicationInfoCache.put(info);
|
||||||
|
configureDescendantsAsChildren();
|
||||||
}
|
}
|
||||||
|
|
||||||
private Context getContextForResourcesEnsuringCorrectCachedApkPaths(Context context) {
|
private Context getContextForResourcesEnsuringCorrectCachedApkPaths(Context context) {
|
||||||
@@ -6028,6 +6104,8 @@ public class RemoteViews implements Parcelable, Filter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void writeToParcel(Parcel dest, int flags) {
|
public void writeToParcel(Parcel dest, int flags) {
|
||||||
|
boolean prevSquashingAllowed = dest.allowSquashing();
|
||||||
|
|
||||||
if (!hasMultipleLayouts()) {
|
if (!hasMultipleLayouts()) {
|
||||||
dest.writeInt(MODE_NORMAL);
|
dest.writeInt(MODE_NORMAL);
|
||||||
// We only write the bitmap cache if we are the root RemoteViews, as this cache
|
// We only write the bitmap cache if we are the root RemoteViews, as this cache
|
||||||
@@ -6035,12 +6113,7 @@ public class RemoteViews implements Parcelable, Filter {
|
|||||||
if (mIsRoot) {
|
if (mIsRoot) {
|
||||||
mBitmapCache.writeBitmapsToParcel(dest, flags);
|
mBitmapCache.writeBitmapsToParcel(dest, flags);
|
||||||
}
|
}
|
||||||
if (!mIsRoot && (flags & PARCELABLE_ELIDE_DUPLICATES) != 0) {
|
mApplication.writeToParcel(dest, flags);
|
||||||
dest.writeInt(0);
|
|
||||||
} else {
|
|
||||||
dest.writeInt(1);
|
|
||||||
mApplication.writeToParcel(dest, flags);
|
|
||||||
}
|
|
||||||
if (mIsRoot || mIdealSize == null) {
|
if (mIsRoot || mIdealSize == null) {
|
||||||
dest.writeInt(0);
|
dest.writeInt(0);
|
||||||
} else {
|
} else {
|
||||||
@@ -6050,17 +6123,15 @@ public class RemoteViews implements Parcelable, Filter {
|
|||||||
dest.writeInt(mLayoutId);
|
dest.writeInt(mLayoutId);
|
||||||
dest.writeInt(mViewId);
|
dest.writeInt(mViewId);
|
||||||
dest.writeInt(mLightBackgroundLayoutId);
|
dest.writeInt(mLightBackgroundLayoutId);
|
||||||
writeActionsToParcel(dest);
|
writeActionsToParcel(dest, flags);
|
||||||
} else if (hasSizedRemoteViews()) {
|
} else if (hasSizedRemoteViews()) {
|
||||||
dest.writeInt(MODE_HAS_SIZED_REMOTEVIEWS);
|
dest.writeInt(MODE_HAS_SIZED_REMOTEVIEWS);
|
||||||
if (mIsRoot) {
|
if (mIsRoot) {
|
||||||
mBitmapCache.writeBitmapsToParcel(dest, flags);
|
mBitmapCache.writeBitmapsToParcel(dest, flags);
|
||||||
}
|
}
|
||||||
int childFlags = flags;
|
|
||||||
dest.writeInt(mSizedRemoteViews.size());
|
dest.writeInt(mSizedRemoteViews.size());
|
||||||
for (RemoteViews view : mSizedRemoteViews) {
|
for (RemoteViews view : mSizedRemoteViews) {
|
||||||
view.writeToParcel(dest, childFlags);
|
view.writeToParcel(dest, flags);
|
||||||
childFlags |= PARCELABLE_ELIDE_DUPLICATES;
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
dest.writeInt(MODE_HAS_LANDSCAPE_AND_PORTRAIT);
|
dest.writeInt(MODE_HAS_LANDSCAPE_AND_PORTRAIT);
|
||||||
@@ -6071,13 +6142,15 @@ public class RemoteViews implements Parcelable, Filter {
|
|||||||
}
|
}
|
||||||
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
|
||||||
mPortrait.writeToParcel(dest, flags | PARCELABLE_ELIDE_DUPLICATES);
|
mPortrait.writeToParcel(dest, flags);
|
||||||
}
|
}
|
||||||
dest.writeInt(mApplyFlags);
|
dest.writeInt(mApplyFlags);
|
||||||
dest.writeLong(mProviderInstanceId);
|
dest.writeLong(mProviderInstanceId);
|
||||||
|
|
||||||
|
dest.restoreAllowSquashing(prevSquashingAllowed);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void writeActionsToParcel(Parcel parcel) {
|
private void writeActionsToParcel(Parcel parcel, int flags) {
|
||||||
int count;
|
int count;
|
||||||
if (mActions != null) {
|
if (mActions != null) {
|
||||||
count = mActions.size();
|
count = mActions.size();
|
||||||
@@ -6088,8 +6161,7 @@ public class RemoteViews implements Parcelable, Filter {
|
|||||||
for (int i = 0; i < count; i++) {
|
for (int i = 0; i < count; i++) {
|
||||||
Action a = mActions.get(i);
|
Action a = mActions.get(i);
|
||||||
parcel.writeInt(a.getActionTag());
|
parcel.writeInt(a.getActionTag());
|
||||||
a.writeToParcel(parcel, a.hasSameAppInfo(mApplication)
|
a.writeToParcel(parcel, flags);
|
||||||
? PARCELABLE_ELIDE_DUPLICATES : 0);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -6568,6 +6640,8 @@ public class RemoteViews implements Parcelable, Filter {
|
|||||||
private final boolean mHasStableIds;
|
private final boolean mHasStableIds;
|
||||||
private final int mViewTypeCount;
|
private final int mViewTypeCount;
|
||||||
|
|
||||||
|
private HierarchyRootData mHierarchyRootData;
|
||||||
|
|
||||||
RemoteCollectionItems(
|
RemoteCollectionItems(
|
||||||
long[] ids, RemoteViews[] views, boolean hasStableIds, int viewTypeCount) {
|
long[] ids, RemoteViews[] views, boolean hasStableIds, int viewTypeCount) {
|
||||||
mIds = ids;
|
mIds = ids;
|
||||||
@@ -6590,16 +6664,53 @@ public class RemoteViews implements Parcelable, Filter {
|
|||||||
"View type count is set to " + viewTypeCount + ", but the collection "
|
"View type count is set to " + viewTypeCount + ", but the collection "
|
||||||
+ "contains " + layoutIdCount + " different layout ids");
|
+ "contains " + layoutIdCount + " different layout ids");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Until the collection items are attached to a parent, we configure the first item
|
||||||
|
// to be the root of the others to share caches and save space during serialization.
|
||||||
|
if (views.length > 0) {
|
||||||
|
setHierarchyRootData(views[0].getHierarchyRootData());
|
||||||
|
views[0].mIsRoot = true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
RemoteCollectionItems(Parcel in) {
|
RemoteCollectionItems(@NonNull Parcel in, @Nullable HierarchyRootData hierarchyRootData) {
|
||||||
|
mHasStableIds = in.readBoolean();
|
||||||
|
mViewTypeCount = in.readInt();
|
||||||
int length = in.readInt();
|
int length = in.readInt();
|
||||||
mIds = new long[length];
|
mIds = new long[length];
|
||||||
in.readLongArray(mIds);
|
in.readLongArray(mIds);
|
||||||
|
|
||||||
|
boolean attached = in.readBoolean();
|
||||||
mViews = new RemoteViews[length];
|
mViews = new RemoteViews[length];
|
||||||
in.readTypedArray(mViews, RemoteViews.CREATOR);
|
int firstChildIndex;
|
||||||
mHasStableIds = in.readBoolean();
|
if (attached) {
|
||||||
mViewTypeCount = in.readInt();
|
if (hierarchyRootData == null) {
|
||||||
|
throw new IllegalStateException("Cannot unparcel a RemoteCollectionItems that "
|
||||||
|
+ "was parceled as attached without providing data for a root "
|
||||||
|
+ "RemoteViews");
|
||||||
|
}
|
||||||
|
mHierarchyRootData = hierarchyRootData;
|
||||||
|
firstChildIndex = 0;
|
||||||
|
} else {
|
||||||
|
mViews[0] = new RemoteViews(in);
|
||||||
|
mHierarchyRootData = mViews[0].getHierarchyRootData();
|
||||||
|
firstChildIndex = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = firstChildIndex; i < length; i++) {
|
||||||
|
mViews[i] = new RemoteViews(
|
||||||
|
in,
|
||||||
|
mHierarchyRootData,
|
||||||
|
/* info= */ null,
|
||||||
|
/* depth= */ 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void setHierarchyRootData(@NonNull HierarchyRootData rootData) {
|
||||||
|
mHierarchyRootData = rootData;
|
||||||
|
for (RemoteViews view : mViews) {
|
||||||
|
view.configureAsChild(rootData);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -6609,11 +6720,39 @@ public class RemoteViews implements Parcelable, Filter {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void writeToParcel(@NonNull Parcel dest, int flags) {
|
public void writeToParcel(@NonNull Parcel dest, int flags) {
|
||||||
dest.writeInt(mIds.length);
|
writeToParcel(dest, flags, /* attached= */ false);
|
||||||
dest.writeLongArray(mIds);
|
}
|
||||||
dest.writeTypedArray(mViews, flags);
|
|
||||||
|
private void writeToParcel(@NonNull Parcel dest, int flags, boolean attached) {
|
||||||
|
boolean prevAllowSquashing = dest.allowSquashing();
|
||||||
|
|
||||||
dest.writeBoolean(mHasStableIds);
|
dest.writeBoolean(mHasStableIds);
|
||||||
dest.writeInt(mViewTypeCount);
|
dest.writeInt(mViewTypeCount);
|
||||||
|
dest.writeInt(mIds.length);
|
||||||
|
dest.writeLongArray(mIds);
|
||||||
|
|
||||||
|
if (attached && mHierarchyRootData == null) {
|
||||||
|
throw new IllegalStateException("Cannot call writeToParcelAttached for a "
|
||||||
|
+ "RemoteCollectionItems without first calling setHierarchyRootData()");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write whether we parceled as attached or not. This allows cleaner validation and
|
||||||
|
// proper error messaging when unparceling later.
|
||||||
|
dest.writeBoolean(attached);
|
||||||
|
boolean restoreRoot = false;
|
||||||
|
if (!attached && mViews.length > 0 && !mViews[0].mIsRoot) {
|
||||||
|
// If we're writing unattached, temporarily set the first item as the root so that
|
||||||
|
// the bitmap cache is written to the parcel.
|
||||||
|
restoreRoot = true;
|
||||||
|
mViews[0].mIsRoot = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (RemoteViews view : mViews) {
|
||||||
|
view.writeToParcel(dest, flags);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (restoreRoot) mViews[0].mIsRoot = false;
|
||||||
|
dest.restoreAllowSquashing(prevAllowSquashing);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -6671,7 +6810,7 @@ public class RemoteViews implements Parcelable, Filter {
|
|||||||
@NonNull
|
@NonNull
|
||||||
@Override
|
@Override
|
||||||
public RemoteCollectionItems createFromParcel(@NonNull Parcel source) {
|
public RemoteCollectionItems createFromParcel(@NonNull Parcel source) {
|
||||||
return new RemoteCollectionItems(source);
|
return new RemoteCollectionItems(source, /* hierarchyRoot= */ null);
|
||||||
}
|
}
|
||||||
|
|
||||||
@NonNull
|
@NonNull
|
||||||
@@ -6846,4 +6985,29 @@ public class RemoteViews implements Parcelable, Filter {
|
|||||||
viewId |= childId;
|
viewId |= childId;
|
||||||
return viewId;
|
return viewId;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
private static Pair<String, Integer> getPackageUserKey(@Nullable ApplicationInfo info) {
|
||||||
|
if (info == null || info.packageName == null) return null;
|
||||||
|
return Pair.create(info.packageName, info.uid);
|
||||||
|
}
|
||||||
|
|
||||||
|
private HierarchyRootData getHierarchyRootData() {
|
||||||
|
return new HierarchyRootData(mBitmapCache, mApplicationInfoCache, mClassCookies);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final class HierarchyRootData {
|
||||||
|
final BitmapCache mBitmapCache;
|
||||||
|
final ApplicationInfoCache mApplicationInfoCache;
|
||||||
|
final Map<Class, Object> mClassCookies;
|
||||||
|
|
||||||
|
HierarchyRootData(
|
||||||
|
BitmapCache bitmapCache,
|
||||||
|
ApplicationInfoCache applicationInfoCache,
|
||||||
|
Map<Class, Object> classCookies) {
|
||||||
|
mBitmapCache = bitmapCache;
|
||||||
|
mApplicationInfoCache = applicationInfoCache;
|
||||||
|
mClassCookies = classCookies;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user