Loading core/java/android/content/res/ColorStateList.java +62 −0 Original line number Diff line number Diff line Loading @@ -32,6 +32,9 @@ import android.util.MathUtils; import android.util.SparseArray; import android.util.StateSet; import android.util.Xml; import android.util.proto.ProtoInputStream; import android.util.proto.ProtoOutputStream; import android.util.proto.ProtoUtils; import com.android.internal.R; import com.android.internal.graphics.ColorUtils; Loading @@ -44,7 +47,9 @@ import org.xmlpull.v1.XmlPullParserException; import java.io.IOException; import java.lang.ref.WeakReference; import java.util.ArrayList; import java.util.Arrays; import java.util.List; /** * Loading Loading @@ -793,4 +798,61 @@ public class ColorStateList extends ComplexColor implements Parcelable { return new ColorStateList(stateSpecs, colors); } }; /** @hide */ public void writeToProto(ProtoOutputStream out) { for (int[] states : mStateSpecs) { long specToken = out.start(ColorStateListProto.STATE_SPECS); for (int state : states) { out.write(ColorStateListProto.StateSpec.STATE, state); } out.end(specToken); } for (int color : mColors) { out.write(ColorStateListProto.COLORS, color); } } /** @hide */ public static ColorStateList createFromProto(ProtoInputStream in) throws Exception { List<int[]> stateSpecs = new ArrayList<>(); List<Integer> colors = new ArrayList<>(); while (in.nextField() != ProtoInputStream.NO_MORE_FIELDS) { switch (in.getFieldNumber()) { case (int) ColorStateListProto.COLORS: colors.add(in.readInt(ColorStateListProto.COLORS)); break; case (int) ColorStateListProto.STATE_SPECS: final long stateToken = in.start(ColorStateListProto.STATE_SPECS); List<Integer> states = new ArrayList<>(); while (in.nextField() != ProtoInputStream.NO_MORE_FIELDS) { switch (in.getFieldNumber()) { case (int) ColorStateListProto.StateSpec.STATE: states.add(in.readInt(ColorStateListProto.StateSpec.STATE)); break; default: Log.w(TAG, "Unhandled field while reading Icon proto!\n" + ProtoUtils.currentFieldToString(in)); } } int[] statesArray = new int[states.size()]; Arrays.setAll(statesArray, states::get); stateSpecs.add(statesArray); in.end(stateToken); break; default: Log.w(TAG, "Unhandled field while reading Icon proto!\n" + ProtoUtils.currentFieldToString(in)); } } int[][] stateSpecsArray = new int[stateSpecs.size()][]; Arrays.setAll(stateSpecsArray, stateSpecs::get); int[] colorsArray = new int[colors.size()]; Arrays.setAll(colorsArray, colors::get); return new ColorStateList(stateSpecsArray, colorsArray); } } core/java/android/widget/RemoteViews.java +251 −2 Original line number Diff line number Diff line Loading @@ -62,6 +62,7 @@ import android.content.res.TypedArray; import android.content.res.loader.ResourcesLoader; import android.content.res.loader.ResourcesProvider; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.graphics.BlendMode; import android.graphics.Outline; import android.graphics.PorterDuff; Loading Loading @@ -90,6 +91,7 @@ import android.util.DisplayMetrics; import android.util.IntArray; import android.util.Log; import android.util.LongArray; import android.util.LongSparseArray; import android.util.Pair; import android.util.SizeF; import android.util.SparseArray; Loading @@ -98,6 +100,7 @@ import android.util.TypedValue; import android.util.TypedValue.ComplexDimensionUnit; import android.util.proto.ProtoInputStream; import android.util.proto.ProtoOutputStream; import android.util.proto.ProtoStream; import android.util.proto.ProtoUtils; import android.view.ContextThemeWrapper; import android.view.LayoutInflater; Loading Loading @@ -1266,10 +1269,15 @@ public class RemoteViews implements Parcelable, Filter { int intentId = in.readInt(); String intentUri = in.readString8(); RemoteCollectionItems items = new RemoteCollectionItems(in, currentRootData); addMapping(intentId, intentUri, items); } } void addMapping(int intentId, String intentUri, RemoteCollectionItems items) { mIdToUriMapping.put(intentId, intentUri); mUriToCollectionMapping.put(intentUri, items); } } void setHierarchyDataForId(int intentId, HierarchyRootData data) { String uri = mIdToUriMapping.get(intentId); Loading Loading @@ -1465,6 +1473,87 @@ public class RemoteViews implements Parcelable, Filter { mUriToCollectionMapping.get(intentUri).writeToParcel(out, flags, true); } } public void writeToProto(Context context, ProtoOutputStream out) { final long token = out.start(RemoteViewsProto.REMOTE_COLLECTION_CACHE); for (int i = 0; i < mIdToUriMapping.size(); i++) { final long entryToken = out.start(RemoteViewsProto.RemoteCollectionCache.ENTRIES); out.write(RemoteViewsProto.RemoteCollectionCache.Entry.ID, mIdToUriMapping.keyAt(i)); String intentUri = mIdToUriMapping.valueAt(i); out.write(RemoteViewsProto.RemoteCollectionCache.Entry.URI, intentUri); final long itemsToken = out.start( RemoteViewsProto.RemoteCollectionCache.Entry.ITEMS); mUriToCollectionMapping.get(intentUri).writeToProto(context, out, /* attached= */ true); out.end(itemsToken); out.end(entryToken); } out.end(token); } } private PendingResources<RemoteCollectionCache> populateRemoteCollectionCacheFromProto( ProtoInputStream in) throws Exception { final ArrayList<LongSparseArray<Object>> entries = new ArrayList<>(); final long token = in.start(RemoteViewsProto.REMOTE_COLLECTION_CACHE); while (in.nextField() != NO_MORE_FIELDS) { switch (in.getFieldNumber()) { case (int) RemoteViewsProto.RemoteCollectionCache.ENTRIES: final LongSparseArray<Object> entry = new LongSparseArray<>(); final long entryToken = in.start( RemoteViewsProto.RemoteCollectionCache.ENTRIES); while (in.nextField() != NO_MORE_FIELDS) { switch (in.getFieldNumber()) { case (int) RemoteViewsProto.RemoteCollectionCache.Entry.ID: entry.put(RemoteViewsProto.RemoteCollectionCache.Entry.ID, in.readInt( RemoteViewsProto.RemoteCollectionCache.Entry.ID)); break; case (int) RemoteViewsProto.RemoteCollectionCache.Entry.URI: entry.put(RemoteViewsProto.RemoteCollectionCache.Entry.URI, in.readString( RemoteViewsProto.RemoteCollectionCache.Entry.URI)); break; case (int) RemoteViewsProto.RemoteCollectionCache.Entry.ITEMS: final long itemsToken = in.start( RemoteViewsProto.RemoteCollectionCache.Entry.ITEMS); entry.put(RemoteViewsProto.RemoteCollectionCache.Entry.ITEMS, RemoteCollectionItems.createFromProto(in)); in.end(itemsToken); break; default: Log.w(LOG_TAG, "Unhandled field while reading RemoteViews proto!\n" + ProtoUtils.currentFieldToString(in)); } } in.end(entryToken); checkContainsKeys(entry, new long[]{RemoteViewsProto.RemoteCollectionCache.Entry.ID, RemoteViewsProto.RemoteCollectionCache.Entry.URI, RemoteViewsProto.RemoteCollectionCache.Entry.ITEMS}); entries.add(entry); break; default: Log.w(LOG_TAG, "Unhandled field while reading RemoteViews proto!\n" + ProtoUtils.currentFieldToString(in)); } } in.end(token); return (context, resources, rootData, depth) -> { for (LongSparseArray<Object> entry : entries) { int id = (int) entry.get(RemoteViewsProto.RemoteCollectionCache.Entry.ID); String uri = (String) entry.get(RemoteViewsProto.RemoteCollectionCache.Entry.URI); // Depth resets to 0 for RemoteCollectionItems RemoteCollectionItems items = ((PendingResources<RemoteCollectionItems>) entry.get( RemoteViewsProto.RemoteCollectionCache.Entry.ITEMS)).create(context, resources, rootData, depth); rootData.mRemoteCollectionCache.addMapping(id, uri, items); } // Redundant return, but type signature requires we return something. return rootData.mRemoteCollectionCache; }; } private class SetRemoteViewsAdapterIntent extends Action { Loading Loading @@ -2080,6 +2169,15 @@ public class RemoteViews implements Parcelable, Filter { dest.writeTypedList(mBitmaps, flags); } public void writeBitmapsToProto(ProtoOutputStream out) { for (int i = 0; i < mBitmaps.size(); i++) { final Bitmap bitmap = mBitmaps.get(i); final ByteArrayOutputStream bytes = new ByteArrayOutputStream(); bitmap.compress(Bitmap.CompressFormat.WEBP_LOSSLESS, 100, bytes); out.write(RemoteViewsProto.BITMAP_CACHE, bytes.toByteArray()); } } public int getBitmapMemory() { if (mBitmapMemory < 0) { mBitmapMemory = 0; Loading Loading @@ -7522,6 +7620,127 @@ public class RemoteViews implements Parcelable, Filter { dest.restoreAllowSquashing(prevAllowSquashing); } /** @hide */ public void writeToProto(Context context, ProtoOutputStream out) { writeToProto(context, out, /* attached= */ false); } private void writeToProto(Context context, ProtoOutputStream out, boolean attached) { for (long id : mIds) { out.write(RemoteViewsProto.RemoteCollectionItems.IDS, id); } boolean restoreRoot = false; out.write(RemoteViewsProto.RemoteCollectionItems.ATTACHED, attached); if (!attached && mViews.length > 0 && !mViews[0].mIsRoot) { restoreRoot = true; mViews[0].mIsRoot = true; } for (RemoteViews view : mViews) { final long viewsToken = out.start(RemoteViewsProto.RemoteCollectionItems.VIEWS); view.writePreviewToProto(context, out); out.end(viewsToken); } if (restoreRoot) mViews[0].mIsRoot = false; out.write(RemoteViewsProto.RemoteCollectionItems.HAS_STABLE_IDS, mHasStableIds); out.write(RemoteViewsProto.RemoteCollectionItems.VIEW_TYPE_COUNT, mViewTypeCount); } /** * Overload used for testing unattached RemoteCollectionItems serialization. * * @hide */ public static RemoteCollectionItems createFromProto(Context context, ProtoInputStream in) throws Exception { return createFromProto(in).create(context, context.getResources(), /* rootData= */ null, 0); } /** @hide */ public static PendingResources<RemoteCollectionItems> createFromProto(ProtoInputStream in) throws Exception { final LongSparseArray<Object> values = new LongSparseArray<>(); values.put(RemoteViewsProto.RemoteCollectionItems.IDS, new ArrayList<Long>()); values.put(RemoteViewsProto.RemoteCollectionItems.VIEWS, new ArrayList<PendingResources<RemoteViews>>()); while (in.nextField() != NO_MORE_FIELDS) { switch (in.getFieldNumber()) { case (int) RemoteViewsProto.RemoteCollectionItems.IDS: ((ArrayList<Long>) values.get( RemoteViewsProto.RemoteCollectionItems.IDS)).add( in.readLong(RemoteViewsProto.RemoteCollectionItems.IDS)); break; case (int) RemoteViewsProto.RemoteCollectionItems.VIEWS: final long viewsToken = in.start( RemoteViewsProto.RemoteCollectionItems.VIEWS); ((ArrayList<PendingResources<RemoteViews>>) values.get( RemoteViewsProto.RemoteCollectionItems.VIEWS)).add( RemoteViews.createFromProto(in)); in.end(viewsToken); break; case (int) RemoteViewsProto.RemoteCollectionItems.HAS_STABLE_IDS: values.put(RemoteViewsProto.RemoteCollectionItems.HAS_STABLE_IDS, in.readBoolean( RemoteViewsProto.RemoteCollectionItems.HAS_STABLE_IDS)); break; case (int) RemoteViewsProto.RemoteCollectionItems.VIEW_TYPE_COUNT: values.put(RemoteViewsProto.RemoteCollectionItems.VIEW_TYPE_COUNT, in.readInt(RemoteViewsProto.RemoteCollectionItems.VIEW_TYPE_COUNT)); break; case (int) RemoteViewsProto.RemoteCollectionItems.ATTACHED: values.put(RemoteViewsProto.RemoteCollectionItems.ATTACHED, in.readBoolean(RemoteViewsProto.RemoteCollectionItems.ATTACHED)); break; default: Log.w(LOG_TAG, "Unhandled field while reading RemoteViews proto!\n" + ProtoUtils.currentFieldToString(in)); } } checkContainsKeys(values, new long[]{RemoteViewsProto.RemoteCollectionItems.VIEW_TYPE_COUNT}); return (context, resources, rootData, depth) -> { List<Long> idList = (List<Long>) values.get( RemoteViewsProto.RemoteCollectionItems.IDS); long[] ids = new long[idList.size()]; for (int i = 0; i < idList.size(); i++) { ids[i] = idList.get(i); } boolean attached = (boolean) values.get( RemoteViewsProto.RemoteCollectionItems.ATTACHED, false); List<PendingResources<RemoteViews>> pendingViews = (List<PendingResources<RemoteViews>>) values.get( RemoteViewsProto.RemoteCollectionItems.VIEWS); RemoteViews[] views = new RemoteViews[pendingViews.size()]; if (attached && rootData == null) { throw new IllegalStateException("Cannot create a RemoteCollectionItems from " + "proto that was attached without providing HierarchyRootData"); } int firstChildIndex = 0; if (!attached && pendingViews.size() > 0) { // If written as unattached, get HierarchyRootData from first view views[0] = pendingViews.get(0).create(context, resources, /* rootData= */ null, /* depth= */ 0); rootData = views[0].getHierarchyRootData(); firstChildIndex = 1; } for (int i = firstChildIndex; i < views.length; i++) { // Depth is reset to 0 for RemoteCollectionItems item views, see Parcel // constructor. views[i] = pendingViews.get(i).create(context, resources, rootData, /* depth= */ 0); } return new RemoteCollectionItems(ids, views, (boolean) values.get(RemoteViewsProto.RemoteCollectionItems.HAS_STABLE_IDS, false), (int) values.get(RemoteViewsProto.RemoteCollectionItems.VIEW_TYPE_COUNT, 0)); }; } /** * Returns the id for {@code position}. See {@link #hasStableIds()} for whether this id * should be considered meaningful across collection updates. Loading Loading @@ -7907,6 +8126,10 @@ public class RemoteViews implements Parcelable, Filter { if (mViewId != 0 && mViewId != -1) { out.write(RemoteViewsProto.VIEW_ID, appResources.getResourceName(mViewId)); } if (mIsRoot) { mBitmapCache.writeBitmapsToProto(out); mCollectionCache.writeToProto(context, out); } out.write(RemoteViewsProto.IS_ROOT, mIsRoot); out.write(RemoteViewsProto.APPLY_FLAGS, mApplyFlags); out.write(RemoteViewsProto.HAS_DRAW_INSTRUCTIONS, mHasDrawInstructions); Loading Loading @@ -7968,6 +8191,7 @@ public class RemoteViews implements Parcelable, Filter { final List<PendingResources<RemoteViews>> mSizedRemoteViews = new ArrayList<>(); PendingResources<RemoteViews> mLandscapeViews = null; PendingResources<RemoteViews> mPortraitViews = null; PendingResources<RemoteCollectionCache> mPopulateRemoteCollectionCache = null; boolean mIsRoot = false; boolean mHasDrawInstructions = false; }; Loading Loading @@ -8018,6 +8242,18 @@ public class RemoteViews implements Parcelable, Filter { ref.mPortraitViews = createFromProto(in); in.end(portraitToken); break; case (int) RemoteViewsProto.BITMAP_CACHE: byte[] src = in.readBytes(RemoteViewsProto.BITMAP_CACHE); Bitmap bitmap = BitmapFactory.decodeByteArray(src, 0, src.length); ref.mRv.mBitmapCache.getBitmapId(bitmap); break; case (int) RemoteViewsProto.REMOTE_COLLECTION_CACHE: final long collectionToken = in.start( RemoteViewsProto.REMOTE_COLLECTION_CACHE); ref.mPopulateRemoteCollectionCache = ref.mRv.populateRemoteCollectionCacheFromProto(in); in.end(collectionToken); break; case (int) RemoteViewsProto.IS_ROOT: ref.mIsRoot = in.readBoolean(RemoteViewsProto.IS_ROOT); break; Loading Loading @@ -8087,6 +8323,9 @@ public class RemoteViews implements Parcelable, Filter { rv.setLightBackgroundLayoutId(lightBackgroundLayoutId); } } if (ref.mPopulateRemoteCollectionCache != null) { ref.mPopulateRemoteCollectionCache.create(context, resources, rootData, depth); } if (ref.mProviderInstanceId != -1) { rv.mProviderInstanceId = ref.mProviderInstanceId; } Loading Loading @@ -8139,6 +8378,16 @@ public class RemoteViews implements Parcelable, Filter { } } private static void checkContainsKeys(LongSparseArray<?> array, long[] requiredFields) { for (long requiredField : requiredFields) { if (array.indexOfKey(requiredField) < 0) { throw new IllegalArgumentException( "RemoteViews proto missing field: " + ProtoStream.getFieldIdString( requiredField)); } } } private static SizeF createSizeFFromProto(ProtoInputStream in) throws Exception { float width = 0; float height = 0; Loading core/proto/android/content/res/color_state_list.proto 0 → 100644 +36 −0 Original line number Diff line number Diff line /* * Copyright (C) 2024 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless optional by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ syntax = "proto2"; option java_multiple_files = true; package android.content.res; import "frameworks/base/core/proto/android/privacy.proto"; /** * An android.content.res.ColorStateList object. */ message ColorStateListProto { option (android.msg_privacy).dest = DEST_AUTOMATIC; repeated StateSpec state_specs = 1; repeated int32 colors = 2 [packed = true]; message StateSpec { repeated int32 state = 1 [packed = true]; } } core/proto/android/widget/remoteviews.proto +20 −0 Original line number Diff line number Diff line Loading @@ -51,6 +51,26 @@ message RemoteViewsProto { optional RemoteViewsProto landscape_remoteviews = 11; optional bool is_root = 12; optional bool has_draw_instructions = 13; repeated bytes bitmap_cache = 14; optional RemoteCollectionCache remote_collection_cache = 15; message RemoteCollectionCache { message Entry { optional int64 id = 1; optional string uri = 2; optional RemoteCollectionItems items = 3; } repeated Entry entries = 1; } message RemoteCollectionItems { repeated int64 ids = 1 [packed = true]; repeated RemoteViewsProto views = 2; optional bool has_stable_ids = 3; optional int32 view_type_count = 4; optional bool attached = 5; } } Loading core/tests/coretests/src/android/graphics/ColorStateListTest.java +27 −0 Original line number Diff line number Diff line Loading @@ -19,6 +19,8 @@ package android.graphics; import android.content.res.ColorStateList; import android.content.res.Resources; import android.test.AndroidTestCase; import android.util.proto.ProtoInputStream; import android.util.proto.ProtoOutputStream; import androidx.test.filters.SmallTest; Loading Loading @@ -48,6 +50,15 @@ public class ColorStateListTest extends AndroidTestCase { assertEquals(mResources.getColor(R.color.testcolor1), focusColor); } @SmallTest public void testStateIsInList_proto() throws Exception { ColorStateList colorStateList = recreateFromProto( mResources.getColorStateList(R.color.color1)); int[] focusedState = {android.R.attr.state_focused}; int focusColor = colorStateList.getColorForState(focusedState, R.color.failColor); assertEquals(mResources.getColor(R.color.testcolor1), focusColor); } @SmallTest public void testEmptyState() throws Exception { ColorStateList colorStateList = mResources.getColorStateList(R.color.color1); Loading @@ -56,6 +67,15 @@ public class ColorStateListTest extends AndroidTestCase { assertEquals(mResources.getColor(R.color.testcolor2), defaultColor); } @SmallTest public void testEmptyState_proto() throws Exception { ColorStateList colorStateList = recreateFromProto( mResources.getColorStateList(R.color.color1)); int[] emptyState = {}; int defaultColor = colorStateList.getColorForState(emptyState, mFailureColor); assertEquals(mResources.getColor(R.color.testcolor2), defaultColor); } @SmallTest public void testGetColor() throws Exception { int defaultColor = mResources.getColor(R.color.color1); Loading @@ -73,4 +93,11 @@ public class ColorStateListTest extends AndroidTestCase { int defaultColor = mResources.getColor(R.color.color_with_lstar); assertEquals(mResources.getColor(R.color.testcolor3), defaultColor); } private ColorStateList recreateFromProto(ColorStateList colorStateList) throws Exception { ProtoOutputStream out = new ProtoOutputStream(); colorStateList.writeToProto(out); ProtoInputStream in = new ProtoInputStream(out.getBytes()); return ColorStateList.createFromProto(in); } } Loading
core/java/android/content/res/ColorStateList.java +62 −0 Original line number Diff line number Diff line Loading @@ -32,6 +32,9 @@ import android.util.MathUtils; import android.util.SparseArray; import android.util.StateSet; import android.util.Xml; import android.util.proto.ProtoInputStream; import android.util.proto.ProtoOutputStream; import android.util.proto.ProtoUtils; import com.android.internal.R; import com.android.internal.graphics.ColorUtils; Loading @@ -44,7 +47,9 @@ import org.xmlpull.v1.XmlPullParserException; import java.io.IOException; import java.lang.ref.WeakReference; import java.util.ArrayList; import java.util.Arrays; import java.util.List; /** * Loading Loading @@ -793,4 +798,61 @@ public class ColorStateList extends ComplexColor implements Parcelable { return new ColorStateList(stateSpecs, colors); } }; /** @hide */ public void writeToProto(ProtoOutputStream out) { for (int[] states : mStateSpecs) { long specToken = out.start(ColorStateListProto.STATE_SPECS); for (int state : states) { out.write(ColorStateListProto.StateSpec.STATE, state); } out.end(specToken); } for (int color : mColors) { out.write(ColorStateListProto.COLORS, color); } } /** @hide */ public static ColorStateList createFromProto(ProtoInputStream in) throws Exception { List<int[]> stateSpecs = new ArrayList<>(); List<Integer> colors = new ArrayList<>(); while (in.nextField() != ProtoInputStream.NO_MORE_FIELDS) { switch (in.getFieldNumber()) { case (int) ColorStateListProto.COLORS: colors.add(in.readInt(ColorStateListProto.COLORS)); break; case (int) ColorStateListProto.STATE_SPECS: final long stateToken = in.start(ColorStateListProto.STATE_SPECS); List<Integer> states = new ArrayList<>(); while (in.nextField() != ProtoInputStream.NO_MORE_FIELDS) { switch (in.getFieldNumber()) { case (int) ColorStateListProto.StateSpec.STATE: states.add(in.readInt(ColorStateListProto.StateSpec.STATE)); break; default: Log.w(TAG, "Unhandled field while reading Icon proto!\n" + ProtoUtils.currentFieldToString(in)); } } int[] statesArray = new int[states.size()]; Arrays.setAll(statesArray, states::get); stateSpecs.add(statesArray); in.end(stateToken); break; default: Log.w(TAG, "Unhandled field while reading Icon proto!\n" + ProtoUtils.currentFieldToString(in)); } } int[][] stateSpecsArray = new int[stateSpecs.size()][]; Arrays.setAll(stateSpecsArray, stateSpecs::get); int[] colorsArray = new int[colors.size()]; Arrays.setAll(colorsArray, colors::get); return new ColorStateList(stateSpecsArray, colorsArray); } }
core/java/android/widget/RemoteViews.java +251 −2 Original line number Diff line number Diff line Loading @@ -62,6 +62,7 @@ import android.content.res.TypedArray; import android.content.res.loader.ResourcesLoader; import android.content.res.loader.ResourcesProvider; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.graphics.BlendMode; import android.graphics.Outline; import android.graphics.PorterDuff; Loading Loading @@ -90,6 +91,7 @@ import android.util.DisplayMetrics; import android.util.IntArray; import android.util.Log; import android.util.LongArray; import android.util.LongSparseArray; import android.util.Pair; import android.util.SizeF; import android.util.SparseArray; Loading @@ -98,6 +100,7 @@ import android.util.TypedValue; import android.util.TypedValue.ComplexDimensionUnit; import android.util.proto.ProtoInputStream; import android.util.proto.ProtoOutputStream; import android.util.proto.ProtoStream; import android.util.proto.ProtoUtils; import android.view.ContextThemeWrapper; import android.view.LayoutInflater; Loading Loading @@ -1266,10 +1269,15 @@ public class RemoteViews implements Parcelable, Filter { int intentId = in.readInt(); String intentUri = in.readString8(); RemoteCollectionItems items = new RemoteCollectionItems(in, currentRootData); addMapping(intentId, intentUri, items); } } void addMapping(int intentId, String intentUri, RemoteCollectionItems items) { mIdToUriMapping.put(intentId, intentUri); mUriToCollectionMapping.put(intentUri, items); } } void setHierarchyDataForId(int intentId, HierarchyRootData data) { String uri = mIdToUriMapping.get(intentId); Loading Loading @@ -1465,6 +1473,87 @@ public class RemoteViews implements Parcelable, Filter { mUriToCollectionMapping.get(intentUri).writeToParcel(out, flags, true); } } public void writeToProto(Context context, ProtoOutputStream out) { final long token = out.start(RemoteViewsProto.REMOTE_COLLECTION_CACHE); for (int i = 0; i < mIdToUriMapping.size(); i++) { final long entryToken = out.start(RemoteViewsProto.RemoteCollectionCache.ENTRIES); out.write(RemoteViewsProto.RemoteCollectionCache.Entry.ID, mIdToUriMapping.keyAt(i)); String intentUri = mIdToUriMapping.valueAt(i); out.write(RemoteViewsProto.RemoteCollectionCache.Entry.URI, intentUri); final long itemsToken = out.start( RemoteViewsProto.RemoteCollectionCache.Entry.ITEMS); mUriToCollectionMapping.get(intentUri).writeToProto(context, out, /* attached= */ true); out.end(itemsToken); out.end(entryToken); } out.end(token); } } private PendingResources<RemoteCollectionCache> populateRemoteCollectionCacheFromProto( ProtoInputStream in) throws Exception { final ArrayList<LongSparseArray<Object>> entries = new ArrayList<>(); final long token = in.start(RemoteViewsProto.REMOTE_COLLECTION_CACHE); while (in.nextField() != NO_MORE_FIELDS) { switch (in.getFieldNumber()) { case (int) RemoteViewsProto.RemoteCollectionCache.ENTRIES: final LongSparseArray<Object> entry = new LongSparseArray<>(); final long entryToken = in.start( RemoteViewsProto.RemoteCollectionCache.ENTRIES); while (in.nextField() != NO_MORE_FIELDS) { switch (in.getFieldNumber()) { case (int) RemoteViewsProto.RemoteCollectionCache.Entry.ID: entry.put(RemoteViewsProto.RemoteCollectionCache.Entry.ID, in.readInt( RemoteViewsProto.RemoteCollectionCache.Entry.ID)); break; case (int) RemoteViewsProto.RemoteCollectionCache.Entry.URI: entry.put(RemoteViewsProto.RemoteCollectionCache.Entry.URI, in.readString( RemoteViewsProto.RemoteCollectionCache.Entry.URI)); break; case (int) RemoteViewsProto.RemoteCollectionCache.Entry.ITEMS: final long itemsToken = in.start( RemoteViewsProto.RemoteCollectionCache.Entry.ITEMS); entry.put(RemoteViewsProto.RemoteCollectionCache.Entry.ITEMS, RemoteCollectionItems.createFromProto(in)); in.end(itemsToken); break; default: Log.w(LOG_TAG, "Unhandled field while reading RemoteViews proto!\n" + ProtoUtils.currentFieldToString(in)); } } in.end(entryToken); checkContainsKeys(entry, new long[]{RemoteViewsProto.RemoteCollectionCache.Entry.ID, RemoteViewsProto.RemoteCollectionCache.Entry.URI, RemoteViewsProto.RemoteCollectionCache.Entry.ITEMS}); entries.add(entry); break; default: Log.w(LOG_TAG, "Unhandled field while reading RemoteViews proto!\n" + ProtoUtils.currentFieldToString(in)); } } in.end(token); return (context, resources, rootData, depth) -> { for (LongSparseArray<Object> entry : entries) { int id = (int) entry.get(RemoteViewsProto.RemoteCollectionCache.Entry.ID); String uri = (String) entry.get(RemoteViewsProto.RemoteCollectionCache.Entry.URI); // Depth resets to 0 for RemoteCollectionItems RemoteCollectionItems items = ((PendingResources<RemoteCollectionItems>) entry.get( RemoteViewsProto.RemoteCollectionCache.Entry.ITEMS)).create(context, resources, rootData, depth); rootData.mRemoteCollectionCache.addMapping(id, uri, items); } // Redundant return, but type signature requires we return something. return rootData.mRemoteCollectionCache; }; } private class SetRemoteViewsAdapterIntent extends Action { Loading Loading @@ -2080,6 +2169,15 @@ public class RemoteViews implements Parcelable, Filter { dest.writeTypedList(mBitmaps, flags); } public void writeBitmapsToProto(ProtoOutputStream out) { for (int i = 0; i < mBitmaps.size(); i++) { final Bitmap bitmap = mBitmaps.get(i); final ByteArrayOutputStream bytes = new ByteArrayOutputStream(); bitmap.compress(Bitmap.CompressFormat.WEBP_LOSSLESS, 100, bytes); out.write(RemoteViewsProto.BITMAP_CACHE, bytes.toByteArray()); } } public int getBitmapMemory() { if (mBitmapMemory < 0) { mBitmapMemory = 0; Loading Loading @@ -7522,6 +7620,127 @@ public class RemoteViews implements Parcelable, Filter { dest.restoreAllowSquashing(prevAllowSquashing); } /** @hide */ public void writeToProto(Context context, ProtoOutputStream out) { writeToProto(context, out, /* attached= */ false); } private void writeToProto(Context context, ProtoOutputStream out, boolean attached) { for (long id : mIds) { out.write(RemoteViewsProto.RemoteCollectionItems.IDS, id); } boolean restoreRoot = false; out.write(RemoteViewsProto.RemoteCollectionItems.ATTACHED, attached); if (!attached && mViews.length > 0 && !mViews[0].mIsRoot) { restoreRoot = true; mViews[0].mIsRoot = true; } for (RemoteViews view : mViews) { final long viewsToken = out.start(RemoteViewsProto.RemoteCollectionItems.VIEWS); view.writePreviewToProto(context, out); out.end(viewsToken); } if (restoreRoot) mViews[0].mIsRoot = false; out.write(RemoteViewsProto.RemoteCollectionItems.HAS_STABLE_IDS, mHasStableIds); out.write(RemoteViewsProto.RemoteCollectionItems.VIEW_TYPE_COUNT, mViewTypeCount); } /** * Overload used for testing unattached RemoteCollectionItems serialization. * * @hide */ public static RemoteCollectionItems createFromProto(Context context, ProtoInputStream in) throws Exception { return createFromProto(in).create(context, context.getResources(), /* rootData= */ null, 0); } /** @hide */ public static PendingResources<RemoteCollectionItems> createFromProto(ProtoInputStream in) throws Exception { final LongSparseArray<Object> values = new LongSparseArray<>(); values.put(RemoteViewsProto.RemoteCollectionItems.IDS, new ArrayList<Long>()); values.put(RemoteViewsProto.RemoteCollectionItems.VIEWS, new ArrayList<PendingResources<RemoteViews>>()); while (in.nextField() != NO_MORE_FIELDS) { switch (in.getFieldNumber()) { case (int) RemoteViewsProto.RemoteCollectionItems.IDS: ((ArrayList<Long>) values.get( RemoteViewsProto.RemoteCollectionItems.IDS)).add( in.readLong(RemoteViewsProto.RemoteCollectionItems.IDS)); break; case (int) RemoteViewsProto.RemoteCollectionItems.VIEWS: final long viewsToken = in.start( RemoteViewsProto.RemoteCollectionItems.VIEWS); ((ArrayList<PendingResources<RemoteViews>>) values.get( RemoteViewsProto.RemoteCollectionItems.VIEWS)).add( RemoteViews.createFromProto(in)); in.end(viewsToken); break; case (int) RemoteViewsProto.RemoteCollectionItems.HAS_STABLE_IDS: values.put(RemoteViewsProto.RemoteCollectionItems.HAS_STABLE_IDS, in.readBoolean( RemoteViewsProto.RemoteCollectionItems.HAS_STABLE_IDS)); break; case (int) RemoteViewsProto.RemoteCollectionItems.VIEW_TYPE_COUNT: values.put(RemoteViewsProto.RemoteCollectionItems.VIEW_TYPE_COUNT, in.readInt(RemoteViewsProto.RemoteCollectionItems.VIEW_TYPE_COUNT)); break; case (int) RemoteViewsProto.RemoteCollectionItems.ATTACHED: values.put(RemoteViewsProto.RemoteCollectionItems.ATTACHED, in.readBoolean(RemoteViewsProto.RemoteCollectionItems.ATTACHED)); break; default: Log.w(LOG_TAG, "Unhandled field while reading RemoteViews proto!\n" + ProtoUtils.currentFieldToString(in)); } } checkContainsKeys(values, new long[]{RemoteViewsProto.RemoteCollectionItems.VIEW_TYPE_COUNT}); return (context, resources, rootData, depth) -> { List<Long> idList = (List<Long>) values.get( RemoteViewsProto.RemoteCollectionItems.IDS); long[] ids = new long[idList.size()]; for (int i = 0; i < idList.size(); i++) { ids[i] = idList.get(i); } boolean attached = (boolean) values.get( RemoteViewsProto.RemoteCollectionItems.ATTACHED, false); List<PendingResources<RemoteViews>> pendingViews = (List<PendingResources<RemoteViews>>) values.get( RemoteViewsProto.RemoteCollectionItems.VIEWS); RemoteViews[] views = new RemoteViews[pendingViews.size()]; if (attached && rootData == null) { throw new IllegalStateException("Cannot create a RemoteCollectionItems from " + "proto that was attached without providing HierarchyRootData"); } int firstChildIndex = 0; if (!attached && pendingViews.size() > 0) { // If written as unattached, get HierarchyRootData from first view views[0] = pendingViews.get(0).create(context, resources, /* rootData= */ null, /* depth= */ 0); rootData = views[0].getHierarchyRootData(); firstChildIndex = 1; } for (int i = firstChildIndex; i < views.length; i++) { // Depth is reset to 0 for RemoteCollectionItems item views, see Parcel // constructor. views[i] = pendingViews.get(i).create(context, resources, rootData, /* depth= */ 0); } return new RemoteCollectionItems(ids, views, (boolean) values.get(RemoteViewsProto.RemoteCollectionItems.HAS_STABLE_IDS, false), (int) values.get(RemoteViewsProto.RemoteCollectionItems.VIEW_TYPE_COUNT, 0)); }; } /** * Returns the id for {@code position}. See {@link #hasStableIds()} for whether this id * should be considered meaningful across collection updates. Loading Loading @@ -7907,6 +8126,10 @@ public class RemoteViews implements Parcelable, Filter { if (mViewId != 0 && mViewId != -1) { out.write(RemoteViewsProto.VIEW_ID, appResources.getResourceName(mViewId)); } if (mIsRoot) { mBitmapCache.writeBitmapsToProto(out); mCollectionCache.writeToProto(context, out); } out.write(RemoteViewsProto.IS_ROOT, mIsRoot); out.write(RemoteViewsProto.APPLY_FLAGS, mApplyFlags); out.write(RemoteViewsProto.HAS_DRAW_INSTRUCTIONS, mHasDrawInstructions); Loading Loading @@ -7968,6 +8191,7 @@ public class RemoteViews implements Parcelable, Filter { final List<PendingResources<RemoteViews>> mSizedRemoteViews = new ArrayList<>(); PendingResources<RemoteViews> mLandscapeViews = null; PendingResources<RemoteViews> mPortraitViews = null; PendingResources<RemoteCollectionCache> mPopulateRemoteCollectionCache = null; boolean mIsRoot = false; boolean mHasDrawInstructions = false; }; Loading Loading @@ -8018,6 +8242,18 @@ public class RemoteViews implements Parcelable, Filter { ref.mPortraitViews = createFromProto(in); in.end(portraitToken); break; case (int) RemoteViewsProto.BITMAP_CACHE: byte[] src = in.readBytes(RemoteViewsProto.BITMAP_CACHE); Bitmap bitmap = BitmapFactory.decodeByteArray(src, 0, src.length); ref.mRv.mBitmapCache.getBitmapId(bitmap); break; case (int) RemoteViewsProto.REMOTE_COLLECTION_CACHE: final long collectionToken = in.start( RemoteViewsProto.REMOTE_COLLECTION_CACHE); ref.mPopulateRemoteCollectionCache = ref.mRv.populateRemoteCollectionCacheFromProto(in); in.end(collectionToken); break; case (int) RemoteViewsProto.IS_ROOT: ref.mIsRoot = in.readBoolean(RemoteViewsProto.IS_ROOT); break; Loading Loading @@ -8087,6 +8323,9 @@ public class RemoteViews implements Parcelable, Filter { rv.setLightBackgroundLayoutId(lightBackgroundLayoutId); } } if (ref.mPopulateRemoteCollectionCache != null) { ref.mPopulateRemoteCollectionCache.create(context, resources, rootData, depth); } if (ref.mProviderInstanceId != -1) { rv.mProviderInstanceId = ref.mProviderInstanceId; } Loading Loading @@ -8139,6 +8378,16 @@ public class RemoteViews implements Parcelable, Filter { } } private static void checkContainsKeys(LongSparseArray<?> array, long[] requiredFields) { for (long requiredField : requiredFields) { if (array.indexOfKey(requiredField) < 0) { throw new IllegalArgumentException( "RemoteViews proto missing field: " + ProtoStream.getFieldIdString( requiredField)); } } } private static SizeF createSizeFFromProto(ProtoInputStream in) throws Exception { float width = 0; float height = 0; Loading
core/proto/android/content/res/color_state_list.proto 0 → 100644 +36 −0 Original line number Diff line number Diff line /* * Copyright (C) 2024 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless optional by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ syntax = "proto2"; option java_multiple_files = true; package android.content.res; import "frameworks/base/core/proto/android/privacy.proto"; /** * An android.content.res.ColorStateList object. */ message ColorStateListProto { option (android.msg_privacy).dest = DEST_AUTOMATIC; repeated StateSpec state_specs = 1; repeated int32 colors = 2 [packed = true]; message StateSpec { repeated int32 state = 1 [packed = true]; } }
core/proto/android/widget/remoteviews.proto +20 −0 Original line number Diff line number Diff line Loading @@ -51,6 +51,26 @@ message RemoteViewsProto { optional RemoteViewsProto landscape_remoteviews = 11; optional bool is_root = 12; optional bool has_draw_instructions = 13; repeated bytes bitmap_cache = 14; optional RemoteCollectionCache remote_collection_cache = 15; message RemoteCollectionCache { message Entry { optional int64 id = 1; optional string uri = 2; optional RemoteCollectionItems items = 3; } repeated Entry entries = 1; } message RemoteCollectionItems { repeated int64 ids = 1 [packed = true]; repeated RemoteViewsProto views = 2; optional bool has_stable_ids = 3; optional int32 view_type_count = 4; optional bool attached = 5; } } Loading
core/tests/coretests/src/android/graphics/ColorStateListTest.java +27 −0 Original line number Diff line number Diff line Loading @@ -19,6 +19,8 @@ package android.graphics; import android.content.res.ColorStateList; import android.content.res.Resources; import android.test.AndroidTestCase; import android.util.proto.ProtoInputStream; import android.util.proto.ProtoOutputStream; import androidx.test.filters.SmallTest; Loading Loading @@ -48,6 +50,15 @@ public class ColorStateListTest extends AndroidTestCase { assertEquals(mResources.getColor(R.color.testcolor1), focusColor); } @SmallTest public void testStateIsInList_proto() throws Exception { ColorStateList colorStateList = recreateFromProto( mResources.getColorStateList(R.color.color1)); int[] focusedState = {android.R.attr.state_focused}; int focusColor = colorStateList.getColorForState(focusedState, R.color.failColor); assertEquals(mResources.getColor(R.color.testcolor1), focusColor); } @SmallTest public void testEmptyState() throws Exception { ColorStateList colorStateList = mResources.getColorStateList(R.color.color1); Loading @@ -56,6 +67,15 @@ public class ColorStateListTest extends AndroidTestCase { assertEquals(mResources.getColor(R.color.testcolor2), defaultColor); } @SmallTest public void testEmptyState_proto() throws Exception { ColorStateList colorStateList = recreateFromProto( mResources.getColorStateList(R.color.color1)); int[] emptyState = {}; int defaultColor = colorStateList.getColorForState(emptyState, mFailureColor); assertEquals(mResources.getColor(R.color.testcolor2), defaultColor); } @SmallTest public void testGetColor() throws Exception { int defaultColor = mResources.getColor(R.color.color1); Loading @@ -73,4 +93,11 @@ public class ColorStateListTest extends AndroidTestCase { int defaultColor = mResources.getColor(R.color.color_with_lstar); assertEquals(mResources.getColor(R.color.testcolor3), defaultColor); } private ColorStateList recreateFromProto(ColorStateList colorStateList) throws Exception { ProtoOutputStream out = new ProtoOutputStream(); colorStateList.writeToProto(out); ProtoInputStream in = new ProtoInputStream(out.getBytes()); return ColorStateList.createFromProto(in); } }