• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2024 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 package android.widget;
17 
18 import static com.android.text.flags.Flags.FLAG_NO_BREAK_NO_HYPHENATION_SPAN;
19 import static com.android.text.flags.Flags.noBreakNoHyphenationSpan;
20 
21 import static java.nio.charset.StandardCharsets.UTF_8;
22 
23 import android.annotation.FlaggedApi;
24 import android.content.res.ColorStateList;
25 import android.content.res.Resources;
26 import android.graphics.Bitmap;
27 import android.graphics.BitmapFactory;
28 import android.graphics.BlendMode;
29 import android.graphics.drawable.Icon;
30 import android.graphics.text.LineBreakConfig;
31 import android.os.LocaleList;
32 import android.os.PersistableBundle;
33 import android.text.Annotation;
34 import android.text.Layout;
35 import android.text.SpannableStringBuilder;
36 import android.text.Spanned;
37 import android.text.style.AbsoluteSizeSpan;
38 import android.text.style.AccessibilityClickableSpan;
39 import android.text.style.AccessibilityReplacementSpan;
40 import android.text.style.AccessibilityURLSpan;
41 import android.text.style.AlignmentSpan;
42 import android.text.style.BackgroundColorSpan;
43 import android.text.style.BulletSpan;
44 import android.text.style.CharacterStyle;
45 import android.text.style.EasyEditSpan;
46 import android.text.style.ForegroundColorSpan;
47 import android.text.style.LeadingMarginSpan;
48 import android.text.style.LineBackgroundSpan;
49 import android.text.style.LineBreakConfigSpan;
50 import android.text.style.LineHeightSpan;
51 import android.text.style.LocaleSpan;
52 import android.text.style.QuoteSpan;
53 import android.text.style.RelativeSizeSpan;
54 import android.text.style.ScaleXSpan;
55 import android.text.style.SpellCheckSpan;
56 import android.text.style.StrikethroughSpan;
57 import android.text.style.StyleSpan;
58 import android.text.style.SubscriptSpan;
59 import android.text.style.SuggestionRangeSpan;
60 import android.text.style.SuggestionSpan;
61 import android.text.style.SuperscriptSpan;
62 import android.text.style.TextAppearanceSpan;
63 import android.text.style.TtsSpan;
64 import android.text.style.TypefaceSpan;
65 import android.text.style.URLSpan;
66 import android.text.style.UnderlineSpan;
67 import android.util.Log;
68 import android.util.LongSparseArray;
69 import android.util.proto.ProtoInputStream;
70 import android.util.proto.ProtoOutputStream;
71 import android.util.proto.ProtoUtils;
72 
73 import androidx.annotation.NonNull;
74 
75 import java.io.ByteArrayInputStream;
76 import java.io.ByteArrayOutputStream;
77 import java.io.IOException;
78 import java.util.ArrayList;
79 import java.util.List;
80 import java.util.function.Function;
81 
82 /**
83  * This class provides serialization for certain types used within RemoteViews.
84  *
85  * @hide
86  */
87 public class RemoteViewsSerializers {
88     private static final String TAG = "RemoteViews";
89 
90     /**
91      * Write Icon to proto.
92      */
writeIconToProto(@onNull ProtoOutputStream out, @NonNull Resources appResources, @NonNull Icon icon)93     public static void writeIconToProto(@NonNull ProtoOutputStream out,
94             @NonNull Resources appResources, @NonNull Icon icon) {
95         if (icon.getTintList() != null) {
96             final long token = out.start(RemoteViewsProto.Icon.TINT_LIST);
97             icon.getTintList().writeToProto(out);
98             out.end(token);
99         }
100         out.write(RemoteViewsProto.Icon.BLEND_MODE, BlendMode.toValue(icon.getTintBlendMode()));
101         switch (icon.getType()) {
102             case Icon.TYPE_BITMAP:
103                 final ByteArrayOutputStream bitmapBytes = new ByteArrayOutputStream();
104                 icon.getBitmap().compress(Bitmap.CompressFormat.WEBP_LOSSLESS, 100, bitmapBytes);
105                 out.write(RemoteViewsProto.Icon.BITMAP, bitmapBytes.toByteArray());
106                 break;
107             case Icon.TYPE_ADAPTIVE_BITMAP:
108                 final ByteArrayOutputStream adaptiveBitmapBytes = new ByteArrayOutputStream();
109                 icon.getBitmap()
110                         .compress(Bitmap.CompressFormat.WEBP_LOSSLESS, 100, adaptiveBitmapBytes);
111                 out.write(RemoteViewsProto.Icon.ADAPTIVE_BITMAP, adaptiveBitmapBytes.toByteArray());
112                 break;
113             case Icon.TYPE_RESOURCE:
114                 out.write(
115                         RemoteViewsProto.Icon.RESOURCE,
116                         appResources.getResourceName(icon.getResId()));
117                 break;
118             case Icon.TYPE_DATA:
119                 out.write(RemoteViewsProto.Icon.DATA, icon.getDataBytes());
120                 break;
121             case Icon.TYPE_URI:
122                 out.write(RemoteViewsProto.Icon.URI, icon.getUriString());
123                 break;
124             case Icon.TYPE_URI_ADAPTIVE_BITMAP:
125                 out.write(RemoteViewsProto.Icon.URI_ADAPTIVE_BITMAP, icon.getUriString());
126                 break;
127             default:
128                 Log.e(TAG, "Tried to serialize unknown Icon type " + icon.getType());
129         }
130     }
131 
132     /**
133      * Create Icon from proto.
134      */
135     @NonNull
createIconFromProto(@onNull ProtoInputStream in)136     public static Function<Resources, Icon> createIconFromProto(@NonNull ProtoInputStream in)
137             throws Exception {
138         final LongSparseArray<Object> values = new LongSparseArray<>();
139         while (in.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
140             switch (in.getFieldNumber()) {
141                 case (int) RemoteViewsProto.Icon.BLEND_MODE:
142                     values.put(
143                             RemoteViewsProto.Icon.BLEND_MODE,
144                             in.readInt(RemoteViewsProto.Icon.BLEND_MODE));
145                     break;
146                 case (int) RemoteViewsProto.Icon.TINT_LIST:
147                     final long tintListToken = in.start(RemoteViewsProto.Icon.TINT_LIST);
148                     values.put(RemoteViewsProto.Icon.TINT_LIST, ColorStateList.createFromProto(in));
149                     in.end(tintListToken);
150                     break;
151                 case (int) RemoteViewsProto.Icon.BITMAP:
152                     byte[] bitmapData = in.readBytes(RemoteViewsProto.Icon.BITMAP);
153                     values.put(
154                             RemoteViewsProto.Icon.BITMAP,
155                             BitmapFactory.decodeByteArray(bitmapData, 0, bitmapData.length));
156                     break;
157                 case (int) RemoteViewsProto.Icon.ADAPTIVE_BITMAP:
158                     final byte[] bitmapAdaptiveData = in.readBytes(
159                             RemoteViewsProto.Icon.ADAPTIVE_BITMAP);
160                     values.put(RemoteViewsProto.Icon.ADAPTIVE_BITMAP,
161                             BitmapFactory.decodeByteArray(bitmapAdaptiveData, 0,
162                                     bitmapAdaptiveData.length));
163                     break;
164                 case (int) RemoteViewsProto.Icon.RESOURCE:
165                     values.put(
166                             RemoteViewsProto.Icon.RESOURCE,
167                             in.readString(RemoteViewsProto.Icon.RESOURCE));
168                     break;
169                 case (int) RemoteViewsProto.Icon.DATA:
170                     values.put(
171                             RemoteViewsProto.Icon.DATA, in.readBytes(RemoteViewsProto.Icon.DATA));
172                     break;
173                 case (int) RemoteViewsProto.Icon.URI:
174                     values.put(RemoteViewsProto.Icon.URI, in.readString(RemoteViewsProto.Icon.URI));
175                     break;
176                 case (int) RemoteViewsProto.Icon.URI_ADAPTIVE_BITMAP:
177                     values.put(
178                             RemoteViewsProto.Icon.URI_ADAPTIVE_BITMAP,
179                             in.readString(RemoteViewsProto.Icon.URI_ADAPTIVE_BITMAP));
180                     break;
181                 default:
182                     Log.w(
183                             TAG,
184                             "Unhandled field while reading Icon proto!\n"
185                                     + ProtoUtils.currentFieldToString(in));
186             }
187         }
188 
189         return (resources) -> {
190             final int blendMode = (int) values.get(RemoteViewsProto.Icon.BLEND_MODE, -1);
191             final ColorStateList tintList = (ColorStateList) values.get(
192                     RemoteViewsProto.Icon.TINT_LIST);
193             final Bitmap bitmap = (Bitmap) values.get(RemoteViewsProto.Icon.BITMAP);
194             final Bitmap bitmapAdaptive = (Bitmap) values.get(
195                     RemoteViewsProto.Icon.ADAPTIVE_BITMAP);
196             final String resName = (String) values.get(RemoteViewsProto.Icon.RESOURCE);
197             final int resource = resName != null ? resources.getIdentifier(resName, /* defType= */
198                     null,
199                     /* defPackage= */ null) : -1;
200             final byte[] data = (byte[]) values.get(RemoteViewsProto.Icon.DATA);
201             final String uri = (String) values.get(RemoteViewsProto.Icon.URI);
202             final String uriAdaptive = (String) values.get(
203                     RemoteViewsProto.Icon.URI_ADAPTIVE_BITMAP);
204             Icon icon;
205             if (bitmap != null) {
206                 icon = Icon.createWithBitmap(bitmap);
207             } else if (bitmapAdaptive != null) {
208                 icon = Icon.createWithAdaptiveBitmap(bitmapAdaptive);
209             } else if (resource != -1) {
210                 icon = Icon.createWithResource(resources, resource);
211             } else if (data != null) {
212                 icon = Icon.createWithData(data, 0, data.length);
213             } else if (uri != null) {
214                 icon = Icon.createWithContentUri(uri);
215             } else if (uriAdaptive != null) {
216                 icon = Icon.createWithAdaptiveBitmapContentUri(uriAdaptive);
217             } else {
218                 // Either this Icon has no data or is of an unknown type.
219                 return null;
220             }
221 
222             if (tintList != null) {
223                 icon.setTintList(tintList);
224             }
225             if (blendMode != -1) {
226                 icon.setTintBlendMode(BlendMode.fromValue(blendMode));
227             }
228             return icon;
229         };
230     }
231 
writeCharSequenceToProto(@onNull ProtoOutputStream out, @NonNull CharSequence cs)232     public static void writeCharSequenceToProto(@NonNull ProtoOutputStream out,
233             @NonNull CharSequence cs) {
234         out.write(RemoteViewsProto.CharSequence.TEXT, cs.toString());
235         if (!(cs instanceof Spanned sp)) return;
236 
237         Object[] os = sp.getSpans(0, cs.length(), Object.class);
238         for (Object original : os) {
239             Object prop = original;
240             if (prop instanceof CharacterStyle) {
241                 prop = ((CharacterStyle) prop).getUnderlying();
242             }
243 
244             final long spansToken = out.start(RemoteViewsProto.CharSequence.SPANS);
245             out.write(RemoteViewsProto.CharSequence.Span.START, sp.getSpanStart(original));
246             out.write(RemoteViewsProto.CharSequence.Span.END, sp.getSpanEnd(original));
247             out.write(RemoteViewsProto.CharSequence.Span.FLAGS, sp.getSpanFlags(original));
248 
249             if (prop instanceof AbsoluteSizeSpan span) {
250                 final long spanToken = out.start(RemoteViewsProto.CharSequence.Span.ABSOLUTE_SIZE);
251                 writeAbsoluteSizeSpanToProto(out, span);
252                 out.end(spanToken);
253             } else if (prop instanceof AccessibilityClickableSpan span) {
254                 final long spanToken = out.start(
255                         RemoteViewsProto.CharSequence.Span.ACCESSIBILITY_CLICKABLE);
256                 writeAccessibilityClickableSpanToProto(out, span);
257                 out.end(spanToken);
258             } else if (prop instanceof AccessibilityReplacementSpan span) {
259                 final long spanToken = out.start(
260                         RemoteViewsProto.CharSequence.Span.ACCESSIBILITY_REPLACEMENT);
261                 writeAccessibilityReplacementSpanToProto(out, span);
262                 out.end(spanToken);
263             } else if (prop instanceof AccessibilityURLSpan span) {
264                 final long spanToken = out.start(
265                         RemoteViewsProto.CharSequence.Span.ACCESSIBILITY_URL);
266                 writeAccessibilityURLSpanToProto(out, span);
267                 out.end(spanToken);
268             } else if (prop instanceof Annotation span) {
269                 final long spanToken = out.start(RemoteViewsProto.CharSequence.Span.ANNOTATION);
270                 writeAnnotationToProto(out, span);
271                 out.end(spanToken);
272             } else if (prop instanceof BackgroundColorSpan span) {
273                 final long spanToken = out.start(
274                         RemoteViewsProto.CharSequence.Span.BACKGROUND_COLOR);
275                 writeBackgroundColorSpanToProto(out, span);
276                 out.end(spanToken);
277             } else if (prop instanceof BulletSpan span) {
278                 final long spanToken = out.start(RemoteViewsProto.CharSequence.Span.BULLET);
279                 writeBulletSpanToProto(out, span);
280                 out.end(spanToken);
281             } else if (prop instanceof EasyEditSpan span) {
282                 final long spanToken = out.start(RemoteViewsProto.CharSequence.Span.EASY_EDIT);
283                 writeEasyEditSpanToProto(out, span);
284                 out.end(spanToken);
285             } else if (prop instanceof ForegroundColorSpan span) {
286                 final long spanToken = out.start(
287                         RemoteViewsProto.CharSequence.Span.FOREGROUND_COLOR);
288                 writeForegroundColorSpanToProto(out, span);
289                 out.end(spanToken);
290             } else if (noBreakNoHyphenationSpan() && prop instanceof LineBreakConfigSpan span) {
291                 final long spanToken = out.start(RemoteViewsProto.CharSequence.Span.LINE_BREAK);
292                 writeLineBreakConfigSpanToProto(out, span);
293                 out.end(spanToken);
294             } else if (prop instanceof LocaleSpan span) {
295                 final long spanToken = out.start(RemoteViewsProto.CharSequence.Span.LOCALE);
296                 writeLocaleSpanToProto(out, span);
297                 out.end(spanToken);
298             } else if (prop instanceof QuoteSpan span) {
299                 final long spanToken = out.start(RemoteViewsProto.CharSequence.Span.QUOTE);
300                 writeQuoteSpanToProto(out, span);
301                 out.end(spanToken);
302             } else if (prop instanceof RelativeSizeSpan span) {
303                 final long spanToken = out.start(RemoteViewsProto.CharSequence.Span.RELATIVE_SIZE);
304                 writeRelativeSizeSpanToProto(out, span);
305                 out.end(spanToken);
306             } else if (prop instanceof ScaleXSpan span) {
307                 final long spanToken = out.start(RemoteViewsProto.CharSequence.Span.SCALE_X);
308                 writeScaleXSpanToProto(out, span);
309                 out.end(spanToken);
310             } else if (prop instanceof SpellCheckSpan span) {
311                 final long spanToken = out.start(RemoteViewsProto.CharSequence.Span.SPELL_CHECK);
312                 writeSpellCheckSpanToProto(out, span);
313                 out.end(spanToken);
314             } else if (prop instanceof LineBackgroundSpan.Standard span) {
315                 final long spanToken = out.start(
316                         RemoteViewsProto.CharSequence.Span.LINE_BACKGROUND);
317                 writeLineBackgroundSpanStandardToProto(out, span);
318                 out.end(spanToken);
319             } else if (prop instanceof LineHeightSpan.Standard span) {
320                 final long spanToken = out.start(RemoteViewsProto.CharSequence.Span.LINE_HEIGHT);
321                 writeLineHeightSpanStandardToProto(out, span);
322                 out.end(spanToken);
323             } else if (prop instanceof LeadingMarginSpan.Standard span) {
324                 final long spanToken = out.start(RemoteViewsProto.CharSequence.Span.LEADING_MARGIN);
325                 writeLeadingMarginSpanStandardToProto(out, span);
326                 out.end(spanToken);
327             } else if (prop instanceof AlignmentSpan.Standard span) {
328                 final long spanToken = out.start(RemoteViewsProto.CharSequence.Span.ALIGNMENT);
329                 writeAlignmentSpanStandardToProto(out, span);
330                 out.end(spanToken);
331             } else if (prop instanceof StrikethroughSpan span) {
332                 final long spanToken = out.start(RemoteViewsProto.CharSequence.Span.STRIKETHROUGH);
333                 writeStrikethroughSpanToProto(out, span);
334                 out.end(spanToken);
335             } else if (prop instanceof StyleSpan span) {
336                 final long spanToken = out.start(RemoteViewsProto.CharSequence.Span.STYLE);
337                 writeStyleSpanToProto(out, span);
338                 out.end(spanToken);
339             } else if (prop instanceof SubscriptSpan span) {
340                 final long spanToken = out.start(RemoteViewsProto.CharSequence.Span.SUBSCRIPT);
341                 writeSubscriptSpanToProto(out, span);
342                 out.end(spanToken);
343             } else if (prop instanceof SuggestionRangeSpan span) {
344                 final long spanToken = out.start(
345                         RemoteViewsProto.CharSequence.Span.SUGGESTION_RANGE);
346                 writeSuggestionRangeSpanToProto(out, span);
347                 out.end(spanToken);
348             } else if (prop instanceof SuggestionSpan span) {
349                 final long spanToken = out.start(RemoteViewsProto.CharSequence.Span.SUGGESTION);
350                 writeSuggestionSpanToProto(out, span);
351                 out.end(spanToken);
352             } else if (prop instanceof SuperscriptSpan span) {
353                 final long spanToken = out.start(RemoteViewsProto.CharSequence.Span.SUPERSCRIPT);
354                 writeSuperscriptSpanToProto(out, span);
355                 out.end(spanToken);
356             } else if (prop instanceof TextAppearanceSpan span) {
357                 final long spanToken = out.start(
358                         RemoteViewsProto.CharSequence.Span.TEXT_APPEARANCE);
359                 writeTextAppearanceSpanToProto(out, span);
360                 out.end(spanToken);
361             } else if (prop instanceof TtsSpan span) {
362                 final long spanToken = out.start(RemoteViewsProto.CharSequence.Span.TTS);
363                 writeTtsSpanToProto(out, span);
364                 out.end(spanToken);
365             } else if (prop instanceof TypefaceSpan span) {
366                 final long spanToken = out.start(RemoteViewsProto.CharSequence.Span.TYPEFACE);
367                 writeTypefaceSpanToProto(out, span);
368                 out.end(spanToken);
369             } else if (prop instanceof URLSpan span) {
370                 final long spanToken = out.start(RemoteViewsProto.CharSequence.Span.URL);
371                 writeURLSpanToProto(out, span);
372                 out.end(spanToken);
373             } else if (prop instanceof UnderlineSpan span) {
374                 final long spanToken = out.start(RemoteViewsProto.CharSequence.Span.UNDERLINE);
375                 writeUnderlineSpanToProto(out, span);
376                 out.end(spanToken);
377             }
378             out.end(spansToken);
379         }
380     }
381 
createCharSequenceFromProto(ProtoInputStream in)382     public static CharSequence createCharSequenceFromProto(ProtoInputStream in) throws Exception {
383         SpannableStringBuilder builder = new SpannableStringBuilder();
384         boolean hasSpans = false;
385         while (in.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
386             switch (in.getFieldNumber()) {
387                 case (int) RemoteViewsProto.CharSequence.TEXT:
388                     String text = in.readString(RemoteViewsProto.CharSequence.TEXT);
389                     builder.append(text);
390                     break;
391                 case (int) RemoteViewsProto.CharSequence.SPANS:
392                     hasSpans = true;
393                     final long spansToken = in.start(RemoteViewsProto.CharSequence.SPANS);
394                     createSpanFromProto(in, builder);
395                     in.end(spansToken);
396                     break;
397                 default:
398                     Log.w(TAG, "Unhandled field while reading CharSequence proto!\n"
399                             + ProtoUtils.currentFieldToString(in));
400             }
401         }
402         return hasSpans ? builder : builder.toString();
403     }
404 
createSpanFromProto(ProtoInputStream in, SpannableStringBuilder builder)405     private static void createSpanFromProto(ProtoInputStream in, SpannableStringBuilder builder)
406             throws Exception {
407         int start = 0;
408         int end = 0;
409         int flags = 0;
410         Object what = null;
411         while (in.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
412             switch (in.getFieldNumber()) {
413                 case (int) RemoteViewsProto.CharSequence.Span.START:
414                     start = in.readInt(RemoteViewsProto.CharSequence.Span.START);
415                     break;
416                 case (int) RemoteViewsProto.CharSequence.Span.END:
417                     end = in.readInt(RemoteViewsProto.CharSequence.Span.END);
418                     break;
419                 case (int) RemoteViewsProto.CharSequence.Span.FLAGS:
420                     flags = in.readInt(RemoteViewsProto.CharSequence.Span.FLAGS);
421                     break;
422                 case (int) RemoteViewsProto.CharSequence.Span.ABSOLUTE_SIZE:
423                     final long asToken = in.start(RemoteViewsProto.CharSequence.Span.ABSOLUTE_SIZE);
424                     what = createAbsoluteSizeSpanFromProto(in);
425                     in.end(asToken);
426                     break;
427                 case (int) RemoteViewsProto.CharSequence.Span.ACCESSIBILITY_CLICKABLE:
428                     final long acToken = in.start(
429                             RemoteViewsProto.CharSequence.Span.ACCESSIBILITY_CLICKABLE);
430                     what = createAccessibilityClickableSpanFromProto(in);
431                     in.end(acToken);
432                     break;
433                 case (int) RemoteViewsProto.CharSequence.Span.ACCESSIBILITY_REPLACEMENT:
434                     final long arToken = in.start(
435                             RemoteViewsProto.CharSequence.Span.ACCESSIBILITY_REPLACEMENT);
436                     what = createAccessibilityReplacementSpanFromProto(in);
437                     in.end(arToken);
438                     break;
439                 case (int) RemoteViewsProto.CharSequence.Span.ACCESSIBILITY_URL:
440                     final long auToken = in.start(
441                             RemoteViewsProto.CharSequence.Span.ACCESSIBILITY_URL);
442                     what = createAccessibilityURLSpanFromProto(in);
443                     in.end(auToken);
444                     break;
445                 case (int) RemoteViewsProto.CharSequence.Span.ALIGNMENT:
446                     final long aToken = in.start(RemoteViewsProto.CharSequence.Span.ALIGNMENT);
447                     what = createAlignmentSpanStandardFromProto(in);
448                     in.end(aToken);
449                     break;
450                 case (int) RemoteViewsProto.CharSequence.Span.ANNOTATION:
451                     final long annToken = in.start(RemoteViewsProto.CharSequence.Span.ANNOTATION);
452                     what = createAnnotationFromProto(in);
453                     in.end(annToken);
454                     break;
455                 case (int) RemoteViewsProto.CharSequence.Span.BACKGROUND_COLOR:
456                     final long bcToken = in.start(
457                             RemoteViewsProto.CharSequence.Span.BACKGROUND_COLOR);
458                     what = createBackgroundColorSpanFromProto(in);
459                     in.end(bcToken);
460                     break;
461                 case (int) RemoteViewsProto.CharSequence.Span.BULLET:
462                     final long bToken = in.start(RemoteViewsProto.CharSequence.Span.BULLET);
463                     what = createBulletSpanFromProto(in);
464                     in.end(bToken);
465                     break;
466                 case (int) RemoteViewsProto.CharSequence.Span.EASY_EDIT:
467                     final long eeToken = in.start(RemoteViewsProto.CharSequence.Span.EASY_EDIT);
468                     what = createEasyEditSpanFromProto(in);
469                     in.end(eeToken);
470                     break;
471                 case (int) RemoteViewsProto.CharSequence.Span.FOREGROUND_COLOR:
472                     final long fcToken = in.start(
473                             RemoteViewsProto.CharSequence.Span.FOREGROUND_COLOR);
474                     what = createForegroundColorSpanFromProto(in);
475                     in.end(fcToken);
476                     break;
477                 case (int) RemoteViewsProto.CharSequence.Span.LEADING_MARGIN:
478                     final long lmToken = in.start(
479                             RemoteViewsProto.CharSequence.Span.LEADING_MARGIN);
480                     what = createLeadingMarginSpanStandardFromProto(in);
481                     in.end(lmToken);
482                     break;
483                 case (int) RemoteViewsProto.CharSequence.Span.LINE_BACKGROUND:
484                     final long lbToken = in.start(
485                             RemoteViewsProto.CharSequence.Span.LINE_BACKGROUND);
486                     what = createLineBackgroundSpanStandardFromProto(in);
487                     in.end(lbToken);
488                     break;
489                 case (int) RemoteViewsProto.CharSequence.Span.LINE_BREAK:
490                     if (!noBreakNoHyphenationSpan()) {
491                         continue;
492                     }
493                     final long lbrToken = in.start(RemoteViewsProto.CharSequence.Span.LINE_BREAK);
494                     what = createLineBreakConfigSpanFromProto(in);
495                     in.end(lbrToken);
496                     break;
497                 case (int) RemoteViewsProto.CharSequence.Span.LINE_HEIGHT:
498                     final long lhToken = in.start(RemoteViewsProto.CharSequence.Span.LINE_HEIGHT);
499                     what = createLineHeightSpanStandardFromProto(in);
500                     in.end(lhToken);
501                     break;
502                 case (int) RemoteViewsProto.CharSequence.Span.LOCALE:
503                     final long lToken = in.start(RemoteViewsProto.CharSequence.Span.LOCALE);
504                     what = createLocaleSpanFromProto(in);
505                     in.end(lToken);
506                     break;
507                 case (int) RemoteViewsProto.CharSequence.Span.QUOTE:
508                     final long qToken = in.start(RemoteViewsProto.CharSequence.Span.QUOTE);
509                     what = createQuoteSpanFromProto(in);
510                     in.end(qToken);
511                     break;
512                 case (int) RemoteViewsProto.CharSequence.Span.RELATIVE_SIZE:
513                     final long rsToken = in.start(RemoteViewsProto.CharSequence.Span.RELATIVE_SIZE);
514                     what = createRelativeSizeSpanFromProto(in);
515                     in.end(rsToken);
516                     break;
517                 case (int) RemoteViewsProto.CharSequence.Span.SCALE_X:
518                     final long sxToken = in.start(RemoteViewsProto.CharSequence.Span.SCALE_X);
519                     what = createScaleXSpanFromProto(in);
520                     in.end(sxToken);
521                     break;
522                 case (int) RemoteViewsProto.CharSequence.Span.SPELL_CHECK:
523                     final long scToken = in.start(RemoteViewsProto.CharSequence.Span.SPELL_CHECK);
524                     what = createSpellCheckSpanFromProto(in);
525                     in.end(scToken);
526                     break;
527                 case (int) RemoteViewsProto.CharSequence.Span.STRIKETHROUGH:
528                     final long stToken = in.start(RemoteViewsProto.CharSequence.Span.STRIKETHROUGH);
529                     what = createStrikethroughSpanFromProto(in);
530                     in.end(stToken);
531                     break;
532                 case (int) RemoteViewsProto.CharSequence.Span.STYLE:
533                     final long sToken = in.start(RemoteViewsProto.CharSequence.Span.STYLE);
534                     what = createStyleSpanFromProto(in);
535                     in.end(sToken);
536                     break;
537                 case (int) RemoteViewsProto.CharSequence.Span.SUBSCRIPT:
538                     final long suToken = in.start(RemoteViewsProto.CharSequence.Span.SUBSCRIPT);
539                     what = createSubscriptSpanFromProto(in);
540                     in.end(suToken);
541                     break;
542                 case (int) RemoteViewsProto.CharSequence.Span.SUGGESTION_RANGE:
543                     final long srToken = in.start(
544                             RemoteViewsProto.CharSequence.Span.SUGGESTION_RANGE);
545                     what = createSuggestionRangeSpanFromProto(in);
546                     in.end(srToken);
547                     break;
548                 case (int) RemoteViewsProto.CharSequence.Span.SUGGESTION:
549                     final long sugToken = in.start(RemoteViewsProto.CharSequence.Span.SUGGESTION);
550                     what = createSuggestionSpanFromProto(in);
551                     in.end(sugToken);
552                     break;
553                 case (int) RemoteViewsProto.CharSequence.Span.SUPERSCRIPT:
554                     final long supToken = in.start(RemoteViewsProto.CharSequence.Span.SUPERSCRIPT);
555                     what = createSuperscriptSpanFromProto(in);
556                     in.end(supToken);
557                     break;
558                 case (int) RemoteViewsProto.CharSequence.Span.TEXT_APPEARANCE:
559                     final long taToken = in.start(
560                             RemoteViewsProto.CharSequence.Span.TEXT_APPEARANCE);
561                     what = createTextAppearanceSpanFromProto(in);
562                     in.end(taToken);
563                     break;
564                 case (int) RemoteViewsProto.CharSequence.Span.TTS:
565                     final long ttsToken = in.start(RemoteViewsProto.CharSequence.Span.TTS);
566                     what = createTtsSpanFromProto(in);
567                     in.end(ttsToken);
568                     break;
569                 case (int) RemoteViewsProto.CharSequence.Span.TYPEFACE:
570                     final long tfToken = in.start(RemoteViewsProto.CharSequence.Span.TYPEFACE);
571                     what = createTypefaceSpanFromProto(in);
572                     in.end(tfToken);
573                     break;
574                 case (int) RemoteViewsProto.CharSequence.Span.UNDERLINE:
575                     final long unToken = in.start(RemoteViewsProto.CharSequence.Span.UNDERLINE);
576                     what = createUnderlineSpanFromProto(in);
577                     in.end(unToken);
578                     break;
579                 case (int) RemoteViewsProto.CharSequence.Span.URL:
580                     final long urlToken = in.start(RemoteViewsProto.CharSequence.Span.URL);
581                     what = createURLSpanFromProto(in);
582                     in.end(urlToken);
583                     break;
584                 default:
585                     Log.w(TAG, "Unhandled field while reading CharSequence proto!\n"
586                             + ProtoUtils.currentFieldToString(in));
587             }
588         }
589         if (what == null) {
590             return;
591         }
592         builder.setSpan(what, start, end, flags);
593     }
594 
createAbsoluteSizeSpanFromProto(@onNull ProtoInputStream in)595     public static AbsoluteSizeSpan createAbsoluteSizeSpanFromProto(@NonNull ProtoInputStream in)
596             throws Exception {
597         int size = 0;
598         boolean dip = false;
599         while (in.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
600             switch (in.getFieldNumber()) {
601                 case (int) RemoteViewsProto.CharSequence.Span.AbsoluteSize.SIZE:
602                     size = in.readInt(RemoteViewsProto.CharSequence.Span.AbsoluteSize.SIZE);
603                     break;
604                 case (int) RemoteViewsProto.CharSequence.Span.AbsoluteSize.DIP:
605                     dip = in.readBoolean(RemoteViewsProto.CharSequence.Span.AbsoluteSize.DIP);
606                     break;
607                 default:
608                     Log.w("AbsoluteSizeSpan",
609                             "Unhandled field while reading AbsoluteSizeSpan " + "proto!\n"
610                                     + ProtoUtils.currentFieldToString(in));
611             }
612         }
613         return new AbsoluteSizeSpan(size, dip);
614     }
615 
writeAbsoluteSizeSpanToProto(@onNull ProtoOutputStream out, AbsoluteSizeSpan span)616     public static void writeAbsoluteSizeSpanToProto(@NonNull ProtoOutputStream out,
617             AbsoluteSizeSpan span) {
618         out.write(RemoteViewsProto.CharSequence.Span.AbsoluteSize.SIZE, span.getSize());
619         out.write(RemoteViewsProto.CharSequence.Span.AbsoluteSize.DIP, span.getDip());
620     }
621 
createAccessibilityClickableSpanFromProto( @onNull ProtoInputStream in)622     public static AccessibilityClickableSpan createAccessibilityClickableSpanFromProto(
623             @NonNull ProtoInputStream in) throws Exception {
624         int originalClickableSpanId = 0;
625         while (in.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
626             switch (in.getFieldNumber()) {
627                 case (int) RemoteViewsProto.CharSequence.Span
628                         .AccessibilityClickable.ORIGINAL_CLICKABLE_SPAN_ID:
629                     originalClickableSpanId = in.readInt(
630                             RemoteViewsProto.CharSequence.Span
631                                     .AccessibilityClickable.ORIGINAL_CLICKABLE_SPAN_ID);
632                     break;
633                 default:
634                     Log.w("AccessibilityClickable",
635                             "Unhandled field while reading" + " AccessibilityClickableSpan proto!\n"
636                                     + ProtoUtils.currentFieldToString(in));
637             }
638         }
639         return new AccessibilityClickableSpan(originalClickableSpanId);
640     }
641 
writeAccessibilityClickableSpanToProto(@onNull ProtoOutputStream out, AccessibilityClickableSpan span)642     public static void writeAccessibilityClickableSpanToProto(@NonNull ProtoOutputStream out,
643             AccessibilityClickableSpan span) {
644         out.write(
645                 RemoteViewsProto.CharSequence.Span
646                         .AccessibilityClickable.ORIGINAL_CLICKABLE_SPAN_ID,
647                 span.getOriginalClickableSpanId());
648     }
649 
createAccessibilityReplacementSpanFromProto( @onNull ProtoInputStream in)650     public static AccessibilityReplacementSpan createAccessibilityReplacementSpanFromProto(
651             @NonNull ProtoInputStream in) throws Exception {
652         CharSequence contentDescription = null;
653         while (in.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
654             switch (in.getFieldNumber()) {
655                 case (int) RemoteViewsProto.CharSequence.Span
656                         .AccessibilityReplacement.CONTENT_DESCRIPTION:
657                     final long token = in.start(
658                             RemoteViewsProto.CharSequence.Span
659                                     .AccessibilityReplacement.CONTENT_DESCRIPTION);
660                     contentDescription = createCharSequenceFromProto(in);
661                     in.end(token);
662                     break;
663                 default:
664                     Log.w("AccessibilityReplacemen", "Unhandled field while reading"
665                             + " AccessibilityReplacementSpan proto!\n"
666                             + ProtoUtils.currentFieldToString(in));
667             }
668         }
669         return new AccessibilityReplacementSpan(contentDescription);
670     }
671 
writeAccessibilityReplacementSpanToProto(@onNull ProtoOutputStream out, AccessibilityReplacementSpan span)672     public static void writeAccessibilityReplacementSpanToProto(@NonNull ProtoOutputStream out,
673             AccessibilityReplacementSpan span) {
674         final long token = out.start(
675                 RemoteViewsProto.CharSequence.Span.AccessibilityReplacement.CONTENT_DESCRIPTION);
676         CharSequence description = span.getContentDescription();
677         if (description != null) {
678             writeCharSequenceToProto(out, description);
679         }
680         out.end(token);
681     }
682 
createAccessibilityURLSpanFromProto(ProtoInputStream in)683     public static AccessibilityURLSpan createAccessibilityURLSpanFromProto(ProtoInputStream in)
684             throws Exception {
685         String url = null;
686         while (in.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
687             switch (in.getFieldNumber()) {
688                 case (int) RemoteViewsProto.CharSequence.Span.AccessibilityUrl.URL:
689                     url = in.readString(RemoteViewsProto.CharSequence.Span.AccessibilityUrl.URL);
690                     break;
691                 default:
692                     Log.w("AccessibilityURLSpan",
693                             "Unhandled field while reading AccessibilityURLSpan " + "proto!\n"
694                                     + ProtoUtils.currentFieldToString(in));
695             }
696         }
697         return new AccessibilityURLSpan(new URLSpan(url));
698     }
699 
writeAccessibilityURLSpanToProto(@onNull ProtoOutputStream out, AccessibilityURLSpan span)700     public static void writeAccessibilityURLSpanToProto(@NonNull ProtoOutputStream out,
701             AccessibilityURLSpan span) {
702         out.write(RemoteViewsProto.CharSequence.Span.AccessibilityUrl.URL, span.getURL());
703     }
704 
createAlignmentSpanStandardFromProto( @onNull ProtoInputStream in)705     public static AlignmentSpan.Standard createAlignmentSpanStandardFromProto(
706             @NonNull ProtoInputStream in) throws Exception {
707         String alignment = null;
708         while (in.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
709             switch (in.getFieldNumber()) {
710                 case (int) RemoteViewsProto.CharSequence.Span.Alignment.ALIGNMENT:
711                     alignment = in.readString(
712                             RemoteViewsProto.CharSequence.Span.Alignment.ALIGNMENT);
713                     break;
714                 default:
715                     Log.w("AlignmentSpan",
716                             "Unhandled field while reading AlignmentSpan " + "proto!\n"
717                                     + ProtoUtils.currentFieldToString(in));
718             }
719         }
720         return new AlignmentSpan.Standard(Layout.Alignment.valueOf(alignment));
721     }
722 
writeAlignmentSpanStandardToProto(@onNull ProtoOutputStream out, AlignmentSpan.Standard span)723     public static void writeAlignmentSpanStandardToProto(@NonNull ProtoOutputStream out,
724             AlignmentSpan.Standard span) {
725         out.write(RemoteViewsProto.CharSequence.Span.Alignment.ALIGNMENT,
726                 span.getAlignment().name());
727     }
728 
createAnnotationFromProto(@onNull ProtoInputStream in)729     public static Annotation createAnnotationFromProto(@NonNull ProtoInputStream in)
730             throws Exception {
731         String key = null;
732         String value = null;
733         while (in.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
734             switch (in.getFieldNumber()) {
735                 case (int) RemoteViewsProto.CharSequence.Span.Annotation.KEY:
736                     key = in.readString(RemoteViewsProto.CharSequence.Span.Annotation.KEY);
737                     break;
738                 case (int) RemoteViewsProto.CharSequence.Span.Annotation.VALUE:
739                     value = in.readString(RemoteViewsProto.CharSequence.Span.Annotation.VALUE);
740                     break;
741                 default:
742                     Log.w("Annotation", "Unhandled field while reading" + " Annotation proto!\n"
743                             + ProtoUtils.currentFieldToString(in));
744             }
745         }
746         return new Annotation(key, value);
747     }
748 
writeAnnotationToProto(@onNull ProtoOutputStream out, Annotation span)749     public static void writeAnnotationToProto(@NonNull ProtoOutputStream out, Annotation span) {
750         out.write(RemoteViewsProto.CharSequence.Span.Annotation.KEY, span.getKey());
751         out.write(RemoteViewsProto.CharSequence.Span.Annotation.VALUE, span.getValue());
752     }
753 
createBackgroundColorSpanFromProto( @onNull ProtoInputStream in)754     public static BackgroundColorSpan createBackgroundColorSpanFromProto(
755             @NonNull ProtoInputStream in) throws Exception {
756         int color = 0;
757         while (in.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
758             switch (in.getFieldNumber()) {
759                 case (int) RemoteViewsProto.CharSequence.Span.BackgroundColor.COLOR:
760                     color = in.readInt(RemoteViewsProto.CharSequence.Span.BackgroundColor.COLOR);
761                     break;
762                 default:
763                     Log.w("BackgroundColorSpan",
764                             "Unhandled field while reading" + " BackgroundColorSpan proto!\n"
765                                     + ProtoUtils.currentFieldToString(in));
766             }
767         }
768         return new BackgroundColorSpan(color);
769     }
770 
writeBackgroundColorSpanToProto(@onNull ProtoOutputStream out, BackgroundColorSpan span)771     public static void writeBackgroundColorSpanToProto(@NonNull ProtoOutputStream out,
772             BackgroundColorSpan span) {
773         out.write(RemoteViewsProto.CharSequence.Span.BackgroundColor.COLOR,
774                 span.getBackgroundColor());
775     }
776 
createBulletSpanFromProto(@onNull ProtoInputStream in)777     public static BulletSpan createBulletSpanFromProto(@NonNull ProtoInputStream in)
778             throws Exception {
779         int bulletRadius = 0;
780         int color = 0;
781         int gapWidth = 0;
782         boolean wantColor = false;
783         while (in.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
784             switch (in.getFieldNumber()) {
785                 case (int) RemoteViewsProto.CharSequence.Span.Bullet.BULLET_RADIUS:
786                     bulletRadius = in.readInt(
787                             RemoteViewsProto.CharSequence.Span.Bullet.BULLET_RADIUS);
788                     break;
789                 case (int) RemoteViewsProto.CharSequence.Span.Bullet.COLOR:
790                     color = in.readInt(RemoteViewsProto.CharSequence.Span.Bullet.COLOR);
791                     break;
792                 case (int) RemoteViewsProto.CharSequence.Span.Bullet.GAP_WIDTH:
793                     gapWidth = in.readInt(RemoteViewsProto.CharSequence.Span.Bullet.GAP_WIDTH);
794                     break;
795                 case (int) RemoteViewsProto.CharSequence.Span.Bullet.WANT_COLOR:
796                     wantColor = in.readBoolean(
797                             RemoteViewsProto.CharSequence.Span.Bullet.WANT_COLOR);
798                     break;
799                 default:
800                     Log.w("BulletSpan", "Unhandled field while reading BulletSpan " + "proto!\n"
801                             + ProtoUtils.currentFieldToString(in));
802             }
803         }
804         return new BulletSpan(gapWidth, color, wantColor, bulletRadius);
805     }
806 
writeBulletSpanToProto(@onNull ProtoOutputStream out, BulletSpan span)807     public static void writeBulletSpanToProto(@NonNull ProtoOutputStream out, BulletSpan span) {
808         out.write(RemoteViewsProto.CharSequence.Span.Bullet.BULLET_RADIUS, span.getBulletRadius());
809         out.write(RemoteViewsProto.CharSequence.Span.Bullet.COLOR, span.getColor());
810         out.write(RemoteViewsProto.CharSequence.Span.Bullet.GAP_WIDTH, span.getGapWidth());
811         out.write(RemoteViewsProto.CharSequence.Span.Bullet.WANT_COLOR, span.getWantColor());
812     }
813 
createEasyEditSpanFromProto(@onNull ProtoInputStream in)814     public static EasyEditSpan createEasyEditSpanFromProto(@NonNull ProtoInputStream in)
815             throws Exception {
816         return new EasyEditSpan();
817     }
818 
writeEasyEditSpanToProto(@onNull ProtoOutputStream out, EasyEditSpan span)819     public static void writeEasyEditSpanToProto(@NonNull ProtoOutputStream out, EasyEditSpan span) {
820     }
821 
createForegroundColorSpanFromProto( @onNull ProtoInputStream in)822     public static ForegroundColorSpan createForegroundColorSpanFromProto(
823             @NonNull ProtoInputStream in) throws Exception {
824         int color = 0;
825         while (in.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
826             switch (in.getFieldNumber()) {
827                 case (int) RemoteViewsProto.CharSequence.Span.BackgroundColor.COLOR:
828                     color = in.readInt(RemoteViewsProto.CharSequence.Span.BackgroundColor.COLOR);
829                     break;
830                 default:
831                     Log.w("ForegroundColorSpan",
832                             "Unhandled field while reading" + " ForegroundColorSpan proto!\n"
833                                     + ProtoUtils.currentFieldToString(in));
834             }
835         }
836         return new ForegroundColorSpan(color);
837     }
838 
createLeadingMarginSpanStandardFromProto( @onNull ProtoInputStream in)839     public static LeadingMarginSpan.Standard createLeadingMarginSpanStandardFromProto(
840             @NonNull ProtoInputStream in) throws Exception {
841         int first = 0;
842         int rest = 0;
843         while (in.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
844             switch (in.getFieldNumber()) {
845                 case (int) RemoteViewsProto.CharSequence.Span.LeadingMargin.FIRST:
846                     first = in.readInt(RemoteViewsProto.CharSequence.Span.LeadingMargin.FIRST);
847                     break;
848                 case (int) RemoteViewsProto.CharSequence.Span.LeadingMargin.REST:
849                     rest = in.readInt(RemoteViewsProto.CharSequence.Span.LeadingMargin.REST);
850                     break;
851                 default:
852                     Log.w("LeadingMarginSpan",
853                             "Unhandled field while reading LeadingMarginSpan" + "proto!\n"
854                                     + ProtoUtils.currentFieldToString(in));
855             }
856         }
857         return new LeadingMarginSpan.Standard(first, rest);
858     }
859 
writeLeadingMarginSpanStandardToProto(@onNull ProtoOutputStream out, LeadingMarginSpan.Standard span)860     public static void writeLeadingMarginSpanStandardToProto(@NonNull ProtoOutputStream out,
861             LeadingMarginSpan.Standard span) {
862         out.write(RemoteViewsProto.CharSequence.Span.LeadingMargin.FIRST,
863                 span.getLeadingMargin(/* first= */ true));
864         out.write(RemoteViewsProto.CharSequence.Span.LeadingMargin.REST,
865                 span.getLeadingMargin(/* first= */ false));
866     }
867 
writeForegroundColorSpanToProto(@onNull ProtoOutputStream out, ForegroundColorSpan span)868     public static void writeForegroundColorSpanToProto(@NonNull ProtoOutputStream out,
869             ForegroundColorSpan span) {
870         out.write(RemoteViewsProto.CharSequence.Span.ForegroundColor.COLOR,
871                 span.getForegroundColor());
872     }
873 
createLineBackgroundSpanStandardFromProto( @onNull ProtoInputStream in)874     public static LineBackgroundSpan.Standard createLineBackgroundSpanStandardFromProto(
875             @NonNull ProtoInputStream in) throws Exception {
876         int color = 0;
877         while (in.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
878             switch (in.getFieldNumber()) {
879                 case (int) RemoteViewsProto.CharSequence.Span.LineBackground.COLOR:
880                     color = in.readInt(RemoteViewsProto.CharSequence.Span.LineBackground.COLOR);
881                     break;
882                 default:
883                     Log.w("LineBackgroundSpan",
884                             "Unhandled field while reading" + " LineBackgroundSpan proto!\n"
885                                     + ProtoUtils.currentFieldToString(in));
886             }
887         }
888         return new LineBackgroundSpan.Standard(color);
889     }
890 
writeLineBackgroundSpanStandardToProto(@onNull ProtoOutputStream out, LineBackgroundSpan.Standard span)891     public static void writeLineBackgroundSpanStandardToProto(@NonNull ProtoOutputStream out,
892             LineBackgroundSpan.Standard span) {
893         out.write(RemoteViewsProto.CharSequence.Span.LineBackground.COLOR, span.getColor());
894     }
895 
896     @FlaggedApi(FLAG_NO_BREAK_NO_HYPHENATION_SPAN)
createLineBreakConfigSpanFromProto( @onNull ProtoInputStream in)897     public static LineBreakConfigSpan createLineBreakConfigSpanFromProto(
898             @NonNull ProtoInputStream in) throws Exception {
899         int lineBreakStyle = 0;
900         int lineBreakWordStyle = 0;
901         int hyphenation = 0;
902         while (in.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
903             switch (in.getFieldNumber()) {
904                 case (int) RemoteViewsProto.CharSequence.Span.LineBreak.LINE_BREAK_STYLE:
905                     lineBreakStyle = in.readInt(
906                             RemoteViewsProto.CharSequence.Span.LineBreak.LINE_BREAK_STYLE);
907                     break;
908                 case (int) RemoteViewsProto.CharSequence.Span.LineBreak.LINE_BREAK_WORD_STYLE:
909                     lineBreakWordStyle = in.readInt(
910                             RemoteViewsProto.CharSequence.Span.LineBreak.LINE_BREAK_WORD_STYLE);
911                     break;
912                 case (int) RemoteViewsProto.CharSequence.Span.LineBreak.HYPHENATION:
913                     hyphenation = in.readInt(
914                             RemoteViewsProto.CharSequence.Span.LineBreak.HYPHENATION);
915                     break;
916                 default:
917                     Log.w("LineBreakConfigSpan",
918                             "Unhandled field while reading " + "LineBreakConfigSpan proto!\n"
919                                     + ProtoUtils.currentFieldToString(in));
920             }
921         }
922         LineBreakConfig lbc = new LineBreakConfig.Builder().setLineBreakStyle(
923                 lineBreakStyle).setLineBreakWordStyle(lineBreakWordStyle).setHyphenation(
924                 hyphenation).build();
925         return new LineBreakConfigSpan(lbc);
926     }
927 
928     @FlaggedApi(FLAG_NO_BREAK_NO_HYPHENATION_SPAN)
writeLineBreakConfigSpanToProto(@onNull ProtoOutputStream out, LineBreakConfigSpan span)929     public static void writeLineBreakConfigSpanToProto(@NonNull ProtoOutputStream out,
930             LineBreakConfigSpan span) {
931         out.write(RemoteViewsProto.CharSequence.Span.LineBreak.LINE_BREAK_STYLE,
932                 span.getLineBreakConfig().getLineBreakStyle());
933         out.write(RemoteViewsProto.CharSequence.Span.LineBreak.LINE_BREAK_WORD_STYLE,
934                 span.getLineBreakConfig().getLineBreakWordStyle());
935         out.write(RemoteViewsProto.CharSequence.Span.LineBreak.HYPHENATION,
936                 span.getLineBreakConfig().getHyphenation());
937     }
938 
createLineHeightSpanStandardFromProto( @onNull ProtoInputStream in)939     public static LineHeightSpan.Standard createLineHeightSpanStandardFromProto(
940             @NonNull ProtoInputStream in) throws Exception {
941         int height = 0;
942         while (in.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
943             switch (in.getFieldNumber()) {
944                 case (int) RemoteViewsProto.CharSequence.Span.LineHeight.HEIGHT:
945                     height = in.readInt(RemoteViewsProto.CharSequence.Span.LineHeight.HEIGHT);
946                     break;
947                 default:
948                     Log.w("LineHeightSpan.Standard",
949                             "Unhandled field while reading" + " LineHeightSpan.Standard proto!\n"
950                                     + ProtoUtils.currentFieldToString(in));
951             }
952         }
953         return new LineHeightSpan.Standard(height);
954     }
955 
writeLineHeightSpanStandardToProto(@onNull ProtoOutputStream out, LineHeightSpan.Standard span)956     public static void writeLineHeightSpanStandardToProto(@NonNull ProtoOutputStream out,
957             LineHeightSpan.Standard span) {
958         out.write(RemoteViewsProto.CharSequence.Span.LineHeight.HEIGHT, span.getHeight());
959     }
960 
createLocaleSpanFromProto(@onNull ProtoInputStream in)961     public static LocaleSpan createLocaleSpanFromProto(@NonNull ProtoInputStream in)
962             throws Exception {
963         String languageTags = null;
964         while (in.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
965             switch (in.getFieldNumber()) {
966                 case (int) RemoteViewsProto.CharSequence.Span.Locale.LANGUAGE_TAGS:
967                     languageTags = in.readString(
968                             RemoteViewsProto.CharSequence.Span.Locale.LANGUAGE_TAGS);
969                     break;
970                 default:
971                     Log.w("LocaleSpan", "Unhandled field while reading" + " LocaleSpan proto!\n"
972                             + ProtoUtils.currentFieldToString(in));
973             }
974         }
975         return new LocaleSpan(LocaleList.forLanguageTags(languageTags));
976     }
977 
writeLocaleSpanToProto(@onNull ProtoOutputStream out, LocaleSpan span)978     public static void writeLocaleSpanToProto(@NonNull ProtoOutputStream out, LocaleSpan span) {
979         out.write(RemoteViewsProto.CharSequence.Span.Locale.LANGUAGE_TAGS,
980                 span.getLocales().toLanguageTags());
981     }
982 
createQuoteSpanFromProto(@onNull ProtoInputStream in)983     public static QuoteSpan createQuoteSpanFromProto(@NonNull ProtoInputStream in)
984             throws Exception {
985         int color = 0;
986         int stripeWidth = 0;
987         int gapWidth = 0;
988         while (in.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
989             switch (in.getFieldNumber()) {
990                 case (int) RemoteViewsProto.CharSequence.Span.Quote.COLOR:
991                     color = in.readInt(RemoteViewsProto.CharSequence.Span.Quote.COLOR);
992                     break;
993                 case (int) RemoteViewsProto.CharSequence.Span.Quote.STRIPE_WIDTH:
994                     stripeWidth = in.readInt(RemoteViewsProto.CharSequence.Span.Quote.STRIPE_WIDTH);
995                     break;
996                 case (int) RemoteViewsProto.CharSequence.Span.Quote.GAP_WIDTH:
997                     gapWidth = in.readInt(RemoteViewsProto.CharSequence.Span.Quote.GAP_WIDTH);
998                     break;
999                 default:
1000                     Log.w("QuoteSpan", "Unhandled field while reading QuoteSpan " + "proto!\n"
1001                             + ProtoUtils.currentFieldToString(in));
1002             }
1003         }
1004         return new QuoteSpan(color, stripeWidth, gapWidth);
1005     }
1006 
writeQuoteSpanToProto(@onNull ProtoOutputStream out, QuoteSpan span)1007     public static void writeQuoteSpanToProto(@NonNull ProtoOutputStream out, QuoteSpan span) {
1008         out.write(RemoteViewsProto.CharSequence.Span.Quote.COLOR, span.getColor());
1009         out.write(RemoteViewsProto.CharSequence.Span.Quote.STRIPE_WIDTH, span.getStripeWidth());
1010         out.write(RemoteViewsProto.CharSequence.Span.Quote.GAP_WIDTH, span.getGapWidth());
1011     }
1012 
createRelativeSizeSpanFromProto(@onNull ProtoInputStream in)1013     public static RelativeSizeSpan createRelativeSizeSpanFromProto(@NonNull ProtoInputStream in)
1014             throws Exception {
1015         float proportion = 0;
1016         while (in.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
1017             switch (in.getFieldNumber()) {
1018                 case (int) RemoteViewsProto.CharSequence.Span.RelativeSize.PROPORTION:
1019                     proportion = in.readFloat(
1020                             RemoteViewsProto.CharSequence.Span.RelativeSize.PROPORTION);
1021                     break;
1022                 default:
1023                     Log.w("RelativeSizeSpan",
1024                             "Unhandled field while reading" + " RelativeSizeSpan proto!\n"
1025                                     + ProtoUtils.currentFieldToString(in));
1026             }
1027         }
1028         return new RelativeSizeSpan(proportion);
1029     }
1030 
writeRelativeSizeSpanToProto(@onNull ProtoOutputStream out, RelativeSizeSpan span)1031     public static void writeRelativeSizeSpanToProto(@NonNull ProtoOutputStream out,
1032             RelativeSizeSpan span) {
1033         out.write(RemoteViewsProto.CharSequence.Span.RelativeSize.PROPORTION, span.getSizeChange());
1034     }
1035 
createScaleXSpanFromProto(@onNull ProtoInputStream in)1036     public static ScaleXSpan createScaleXSpanFromProto(@NonNull ProtoInputStream in)
1037             throws Exception {
1038         float proportion = 0f;
1039         while (in.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
1040             switch (in.getFieldNumber()) {
1041                 case (int) RemoteViewsProto.CharSequence.Span.ScaleX.PROPORTION:
1042                     proportion = in.readFloat(RemoteViewsProto.CharSequence.Span.ScaleX.PROPORTION);
1043                     break;
1044                 default:
1045                     Log.w("ScaleXSpan", "Unhandled field while reading" + " ScaleXSpan proto!\n"
1046                             + ProtoUtils.currentFieldToString(in));
1047             }
1048         }
1049         return new ScaleXSpan(proportion);
1050     }
1051 
writeScaleXSpanToProto(@onNull ProtoOutputStream out, ScaleXSpan span)1052     public static void writeScaleXSpanToProto(@NonNull ProtoOutputStream out, ScaleXSpan span) {
1053         out.write(RemoteViewsProto.CharSequence.Span.ScaleX.PROPORTION, span.getScaleX());
1054     }
1055 
createSpellCheckSpanFromProto(@onNull ProtoInputStream in)1056     public static SpellCheckSpan createSpellCheckSpanFromProto(@NonNull ProtoInputStream in) {
1057         return new SpellCheckSpan();
1058     }
1059 
writeSpellCheckSpanToProto(@onNull ProtoOutputStream out, SpellCheckSpan span)1060     public static void writeSpellCheckSpanToProto(@NonNull ProtoOutputStream out,
1061             SpellCheckSpan span) {
1062     }
1063 
createStrikethroughSpanFromProto(@onNull ProtoInputStream in)1064     public static StrikethroughSpan createStrikethroughSpanFromProto(@NonNull ProtoInputStream in) {
1065         return new StrikethroughSpan();
1066     }
1067 
writeStrikethroughSpanToProto(@onNull ProtoOutputStream out, StrikethroughSpan span)1068     public static void writeStrikethroughSpanToProto(@NonNull ProtoOutputStream out,
1069             StrikethroughSpan span) {
1070     }
1071 
createStyleSpanFromProto(@onNull ProtoInputStream in)1072     public static StyleSpan createStyleSpanFromProto(@NonNull ProtoInputStream in)
1073             throws Exception {
1074         int style = 0;
1075         int fontWeightAdjustment = 0;
1076         while (in.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
1077             switch (in.getFieldNumber()) {
1078                 case (int) RemoteViewsProto.CharSequence.Span.Style.STYLE:
1079                     style = in.readInt(RemoteViewsProto.CharSequence.Span.Style.STYLE);
1080                     break;
1081                 case (int) RemoteViewsProto.CharSequence.Span.Style.FONT_WEIGHT_ADJUSTMENT:
1082                     fontWeightAdjustment = in.readInt(
1083                             RemoteViewsProto.CharSequence.Span.Style.FONT_WEIGHT_ADJUSTMENT);
1084                     break;
1085                 default:
1086                     Log.w("StyleSpan", "Unhandled field while reading StyleSpan " + "proto!\n"
1087                             + ProtoUtils.currentFieldToString(in));
1088             }
1089         }
1090         return new StyleSpan(style, fontWeightAdjustment);
1091     }
1092 
writeStyleSpanToProto(@onNull ProtoOutputStream out, StyleSpan span)1093     public static void writeStyleSpanToProto(@NonNull ProtoOutputStream out, StyleSpan span) {
1094         out.write(RemoteViewsProto.CharSequence.Span.Style.STYLE, span.getStyle());
1095         out.write(RemoteViewsProto.CharSequence.Span.Style.FONT_WEIGHT_ADJUSTMENT,
1096                 span.getFontWeightAdjustment());
1097     }
1098 
createSubscriptSpanFromProto(@onNull ProtoInputStream in)1099     public static SubscriptSpan createSubscriptSpanFromProto(@NonNull ProtoInputStream in) {
1100         return new SubscriptSpan();
1101     }
1102 
writeSubscriptSpanToProto(@onNull ProtoOutputStream out, SubscriptSpan span)1103     public static void writeSubscriptSpanToProto(@NonNull ProtoOutputStream out,
1104             SubscriptSpan span) {
1105     }
1106 
createSuggestionRangeSpanFromProto( @onNull ProtoInputStream in)1107     public static SuggestionRangeSpan createSuggestionRangeSpanFromProto(
1108             @NonNull ProtoInputStream in) throws Exception {
1109         int backgroundColor = 0;
1110         while (in.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
1111             switch (in.getFieldNumber()) {
1112                 case (int) RemoteViewsProto.CharSequence.Span.SuggestionRange.BACKGROUND_COLOR:
1113                     backgroundColor = in.readInt(
1114                             RemoteViewsProto.CharSequence.Span.SuggestionRange.BACKGROUND_COLOR);
1115                     break;
1116                 default:
1117                     Log.w("SuggestionRangeSpan",
1118                             "Unhandled field while reading" + " SuggestionRangeSpan proto!\n"
1119                                     + ProtoUtils.currentFieldToString(in));
1120             }
1121         }
1122         SuggestionRangeSpan span = new SuggestionRangeSpan();
1123         span.setBackgroundColor(backgroundColor);
1124         return span;
1125     }
1126 
writeSuggestionRangeSpanToProto(@onNull ProtoOutputStream out, SuggestionRangeSpan span)1127     public static void writeSuggestionRangeSpanToProto(@NonNull ProtoOutputStream out,
1128             SuggestionRangeSpan span) {
1129         out.write(RemoteViewsProto.CharSequence.Span.SuggestionRange.BACKGROUND_COLOR,
1130                 span.getBackgroundColor());
1131     }
1132 
createSuggestionSpanFromProto(@onNull ProtoInputStream in)1133     public static SuggestionSpan createSuggestionSpanFromProto(@NonNull ProtoInputStream in)
1134             throws Exception {
1135         List<String> suggestions = new ArrayList<>();
1136         int flags = 0;
1137         String localeStringForCompatibility = null;
1138         String languageTag = null;
1139         int hashCode = 0;
1140         int easyCorrectUnderlineColor = 0;
1141         float easyCorrectUnderlineThickness = 0;
1142         int misspelledUnderlineColor = 0;
1143         float misspelledUnderlineThickness = 0;
1144         int autoCorrectionUnderlineColor = 0;
1145         float autoCorrectionUnderlineThickness = 0;
1146         int grammarErrorUnderlineColor = 0;
1147         float grammarErrorUnderlineThickness = 0;
1148         while (in.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
1149             switch (in.getFieldNumber()) {
1150                 case (int) RemoteViewsProto.CharSequence.Span.Suggestion.SUGGESTIONS:
1151                     suggestions.add(in.readString(
1152                             RemoteViewsProto.CharSequence.Span.Suggestion.SUGGESTIONS));
1153                     break;
1154                 case (int) RemoteViewsProto.CharSequence.Span.Suggestion.FLAGS:
1155                     flags = in.readInt(RemoteViewsProto.CharSequence.Span.Suggestion.FLAGS);
1156                     break;
1157                 case (int) RemoteViewsProto.CharSequence.Span
1158                         .Suggestion.LOCALE_STRING_FOR_COMPATIBILITY:
1159                     localeStringForCompatibility = in.readString(
1160                             RemoteViewsProto.CharSequence.Span
1161                                     .Suggestion.LOCALE_STRING_FOR_COMPATIBILITY);
1162                     break;
1163                 case (int) RemoteViewsProto.CharSequence.Span.Suggestion.LANGUAGE_TAG:
1164                     languageTag = in.readString(
1165                             RemoteViewsProto.CharSequence.Span.Suggestion.LANGUAGE_TAG);
1166                     break;
1167                 case (int) RemoteViewsProto.CharSequence.Span.Suggestion.HASH_CODE:
1168                     hashCode = in.readInt(RemoteViewsProto.CharSequence.Span.Suggestion.HASH_CODE);
1169                     break;
1170                 case (int) RemoteViewsProto.CharSequence.Span
1171                         .Suggestion.EASY_CORRECT_UNDERLINE_COLOR:
1172                     easyCorrectUnderlineColor = in.readInt(
1173                             RemoteViewsProto.CharSequence.Span
1174                                     .Suggestion.EASY_CORRECT_UNDERLINE_COLOR);
1175                     break;
1176                 case (int) RemoteViewsProto.CharSequence.Span
1177                         .Suggestion.EASY_CORRECT_UNDERLINE_THICKNESS:
1178                     easyCorrectUnderlineThickness = in.readFloat(
1179                             RemoteViewsProto.CharSequence.Span
1180                                     .Suggestion.EASY_CORRECT_UNDERLINE_THICKNESS);
1181                     break;
1182                 case (int) RemoteViewsProto.CharSequence.Span
1183                         .Suggestion.MISSPELLED_UNDERLINE_COLOR:
1184                     misspelledUnderlineColor = in.readInt(
1185                             RemoteViewsProto.CharSequence.Span
1186                                     .Suggestion.MISSPELLED_UNDERLINE_COLOR);
1187                     break;
1188                 case (int) RemoteViewsProto.CharSequence.Span
1189                         .Suggestion.MISSPELLED_UNDERLINE_THICKNESS:
1190                     misspelledUnderlineThickness = in.readFloat(
1191                             RemoteViewsProto.CharSequence.Span
1192                                     .Suggestion.MISSPELLED_UNDERLINE_THICKNESS);
1193                     break;
1194                 case (int) RemoteViewsProto.CharSequence.Span
1195                         .Suggestion.AUTO_CORRECTION_UNDERLINE_COLOR:
1196                     autoCorrectionUnderlineColor = in.readInt(
1197                             RemoteViewsProto.CharSequence.Span
1198                                     .Suggestion.AUTO_CORRECTION_UNDERLINE_COLOR);
1199                     break;
1200                 case (int) RemoteViewsProto.CharSequence.Span
1201                         .Suggestion.AUTO_CORRECTION_UNDERLINE_THICKNESS:
1202                     autoCorrectionUnderlineThickness = in.readFloat(
1203                             RemoteViewsProto.CharSequence.Span
1204                                     .Suggestion.AUTO_CORRECTION_UNDERLINE_THICKNESS);
1205                     break;
1206                 case (int) RemoteViewsProto.CharSequence.Span
1207                         .Suggestion.GRAMMAR_ERROR_UNDERLINE_COLOR:
1208                     grammarErrorUnderlineColor = in.readInt(
1209                             RemoteViewsProto.CharSequence.Span
1210                                     .Suggestion.GRAMMAR_ERROR_UNDERLINE_COLOR);
1211                     break;
1212                 case (int) RemoteViewsProto.CharSequence.Span
1213                         .Suggestion.GRAMMAR_ERROR_UNDERLINE_THICKNESS:
1214                     grammarErrorUnderlineThickness = in.readFloat(
1215                             RemoteViewsProto.CharSequence.Span
1216                                     .Suggestion.GRAMMAR_ERROR_UNDERLINE_THICKNESS);
1217                     break;
1218                 default:
1219                     Log.w("SuggestionSpan",
1220                             "Unhandled field while reading SuggestionSpan " + "proto!\n"
1221                                     + ProtoUtils.currentFieldToString(in));
1222             }
1223         }
1224         String[] suggestionsArray = new String[suggestions.size()];
1225         suggestions.toArray(suggestionsArray);
1226         return new SuggestionSpan(suggestionsArray, flags, localeStringForCompatibility,
1227                 languageTag, hashCode, easyCorrectUnderlineColor, easyCorrectUnderlineThickness,
1228                 misspelledUnderlineColor, misspelledUnderlineThickness,
1229                 autoCorrectionUnderlineColor, autoCorrectionUnderlineThickness,
1230                 grammarErrorUnderlineColor, grammarErrorUnderlineThickness);
1231     }
1232 
writeSuggestionSpanToProto(@onNull ProtoOutputStream out, SuggestionSpan span)1233     public static void writeSuggestionSpanToProto(@NonNull ProtoOutputStream out,
1234             SuggestionSpan span) {
1235         for (String suggestion : span.getSuggestions()) {
1236             out.write(RemoteViewsProto.CharSequence.Span.Suggestion.SUGGESTIONS, suggestion);
1237         }
1238         out.write(RemoteViewsProto.CharSequence.Span.Suggestion.FLAGS, span.getFlags());
1239         out.write(RemoteViewsProto.CharSequence.Span.Suggestion.LOCALE_STRING_FOR_COMPATIBILITY,
1240                 span.getLocale());
1241         if (span.getLocaleObject() != null) {
1242             out.write(RemoteViewsProto.CharSequence.Span.Suggestion.LANGUAGE_TAG,
1243                     span.getLocaleObject().toLanguageTag());
1244         }
1245         out.write(RemoteViewsProto.CharSequence.Span.Suggestion.HASH_CODE, span.hashCode());
1246         out.write(RemoteViewsProto.CharSequence.Span.Suggestion.EASY_CORRECT_UNDERLINE_COLOR,
1247                 span.getEasyCorrectUnderlineColor());
1248         out.write(RemoteViewsProto.CharSequence.Span.Suggestion.EASY_CORRECT_UNDERLINE_THICKNESS,
1249                 span.getEasyCorrectUnderlineThickness());
1250         out.write(RemoteViewsProto.CharSequence.Span.Suggestion.MISSPELLED_UNDERLINE_COLOR,
1251                 span.getMisspelledUnderlineColor());
1252         out.write(RemoteViewsProto.CharSequence.Span.Suggestion.MISSPELLED_UNDERLINE_THICKNESS,
1253                 span.getMisspelledUnderlineThickness());
1254         out.write(RemoteViewsProto.CharSequence.Span.Suggestion.AUTO_CORRECTION_UNDERLINE_COLOR,
1255                 span.getAutoCorrectionUnderlineColor());
1256         out.write(RemoteViewsProto.CharSequence.Span.Suggestion.AUTO_CORRECTION_UNDERLINE_THICKNESS,
1257                 span.getAutoCorrectionUnderlineThickness());
1258         out.write(RemoteViewsProto.CharSequence.Span.Suggestion.GRAMMAR_ERROR_UNDERLINE_COLOR,
1259                 span.getGrammarErrorUnderlineColor());
1260         out.write(RemoteViewsProto.CharSequence.Span.Suggestion.GRAMMAR_ERROR_UNDERLINE_THICKNESS,
1261                 span.getGrammarErrorUnderlineThickness());
1262     }
1263 
createSuperscriptSpanFromProto(@onNull ProtoInputStream in)1264     public static SuperscriptSpan createSuperscriptSpanFromProto(@NonNull ProtoInputStream in) {
1265         return new SuperscriptSpan();
1266     }
1267 
writeSuperscriptSpanToProto(@onNull ProtoOutputStream out, SuperscriptSpan span)1268     public static void writeSuperscriptSpanToProto(@NonNull ProtoOutputStream out,
1269             SuperscriptSpan span) {
1270     }
1271 
createTextAppearanceSpanFromProto(@onNull ProtoInputStream in)1272     public static TextAppearanceSpan createTextAppearanceSpanFromProto(@NonNull ProtoInputStream in)
1273             throws Exception {
1274         String familyName = null;
1275         int style = 0;
1276         int textSize = 0;
1277         ColorStateList textColor = null;
1278         ColorStateList textColorLink = null;
1279         int textFontWeight = 0;
1280         LocaleList textLocales = null;
1281         float shadowRadius = 0F;
1282         float shadowDx = 0F;
1283         float shadowDy = 0F;
1284         int shadowColor = 0;
1285         boolean hasElegantTextHeight = false;
1286         boolean elegantTextHeight = false;
1287         boolean hasLetterSpacing = false;
1288         float letterSpacing = 0F;
1289         String fontFeatureSettings = null;
1290         String fontVariationSettings = null;
1291         while (in.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
1292             switch (in.getFieldNumber()) {
1293                 case (int) RemoteViewsProto.CharSequence.Span.TextAppearance.FAMILY_NAME:
1294                     familyName = in.readString(
1295                             RemoteViewsProto.CharSequence.Span.TextAppearance.FAMILY_NAME);
1296                     break;
1297                 case (int) RemoteViewsProto.CharSequence.Span.TextAppearance.STYLE:
1298                     style = in.readInt(RemoteViewsProto.CharSequence.Span.TextAppearance.STYLE);
1299                     break;
1300                 case (int) RemoteViewsProto.CharSequence.Span.TextAppearance.TEXT_SIZE:
1301                     textSize = in.readInt(
1302                             RemoteViewsProto.CharSequence.Span.TextAppearance.TEXT_SIZE);
1303                     break;
1304                 case (int) RemoteViewsProto.CharSequence.Span.TextAppearance.TEXT_COLOR:
1305                     final long textColorToken = in.start(
1306                             RemoteViewsProto.CharSequence.Span.TextAppearance.TEXT_COLOR);
1307                     textColor = ColorStateList.createFromProto(in);
1308                     in.end(textColorToken);
1309                     break;
1310                 case (int) RemoteViewsProto.CharSequence.Span.TextAppearance.TEXT_COLOR_LINK:
1311                     final long textColorLinkToken = in.start(
1312                             RemoteViewsProto.CharSequence.Span.TextAppearance.TEXT_COLOR_LINK);
1313                     textColorLink = ColorStateList.createFromProto(in);
1314                     in.end(textColorLinkToken);
1315                     break;
1316                 case (int) RemoteViewsProto.CharSequence.Span.TextAppearance.TEXT_FONT_WEIGHT:
1317                     textFontWeight = in.readInt(
1318                             RemoteViewsProto.CharSequence.Span.TextAppearance.TEXT_FONT_WEIGHT);
1319                     break;
1320                 case (int) RemoteViewsProto.CharSequence.Span.TextAppearance.TEXT_LOCALE:
1321                     textLocales = LocaleList.forLanguageTags(in.readString(
1322                             RemoteViewsProto.CharSequence.Span.TextAppearance.TEXT_LOCALE));
1323                     break;
1324                 case (int) RemoteViewsProto.CharSequence.Span.TextAppearance.SHADOW_RADIUS:
1325                     shadowRadius = in.readFloat(
1326                             RemoteViewsProto.CharSequence.Span.TextAppearance.SHADOW_RADIUS);
1327                     break;
1328                 case (int) RemoteViewsProto.CharSequence.Span.TextAppearance.SHADOW_DX:
1329                     shadowDx = in.readFloat(
1330                             RemoteViewsProto.CharSequence.Span.TextAppearance.SHADOW_DX);
1331                     break;
1332                 case (int) RemoteViewsProto.CharSequence.Span.TextAppearance.SHADOW_DY:
1333                     shadowDy = in.readFloat(
1334                             RemoteViewsProto.CharSequence.Span.TextAppearance.SHADOW_DY);
1335                     break;
1336                 case (int) RemoteViewsProto.CharSequence.Span.TextAppearance.SHADOW_COLOR:
1337                     shadowColor = in.readInt(
1338                             RemoteViewsProto.CharSequence.Span.TextAppearance.SHADOW_COLOR);
1339                     break;
1340                 case (int) RemoteViewsProto.CharSequence.Span
1341                         .TextAppearance.HAS_ELEGANT_TEXT_HEIGHT_FIELD:
1342                     hasElegantTextHeight = in.readBoolean(
1343                             RemoteViewsProto.CharSequence.Span
1344                                     .TextAppearance.HAS_ELEGANT_TEXT_HEIGHT_FIELD);
1345                     break;
1346                 case (int) RemoteViewsProto.CharSequence.Span.TextAppearance.ELEGANT_TEXT_HEIGHT:
1347                     elegantTextHeight = in.readBoolean(
1348                             RemoteViewsProto.CharSequence.Span.TextAppearance.ELEGANT_TEXT_HEIGHT);
1349                     break;
1350                 case (int) RemoteViewsProto.CharSequence.Span
1351                         .TextAppearance.HAS_LETTER_SPACING_FIELD:
1352                     hasLetterSpacing = in.readBoolean(
1353                             RemoteViewsProto.CharSequence.Span
1354                                     .TextAppearance.HAS_LETTER_SPACING_FIELD);
1355                     break;
1356                 case (int) RemoteViewsProto.CharSequence.Span.TextAppearance.LETTER_SPACING:
1357                     letterSpacing = in.readFloat(
1358                             RemoteViewsProto.CharSequence.Span.TextAppearance.LETTER_SPACING);
1359                     break;
1360                 case (int) RemoteViewsProto.CharSequence.Span.TextAppearance.FONT_FEATURE_SETTINGS:
1361                     fontFeatureSettings = in.readString(
1362                             RemoteViewsProto.CharSequence.Span
1363                                     .TextAppearance.FONT_FEATURE_SETTINGS);
1364                     break;
1365                 case (int) RemoteViewsProto.CharSequence.Span
1366                         .TextAppearance.FONT_VARIATION_SETTINGS:
1367                     fontVariationSettings = in.readString(
1368                             RemoteViewsProto.CharSequence.Span
1369                                     .TextAppearance.FONT_VARIATION_SETTINGS);
1370                     break;
1371                 default:
1372                     Log.w("TextAppearanceSpan",
1373                             "Unhandled field while reading TextAppearanceSpan " + "proto!\n"
1374                                     + ProtoUtils.currentFieldToString(in));
1375             }
1376         }
1377         return new TextAppearanceSpan(familyName, style, textSize, textColor, textColorLink,
1378                 /* typeface= */ null, textFontWeight, textLocales, shadowRadius, shadowDx, shadowDy,
1379                 shadowColor, hasElegantTextHeight, elegantTextHeight, hasLetterSpacing,
1380                 letterSpacing, fontFeatureSettings, fontVariationSettings);
1381     }
1382 
writeTextAppearanceSpanToProto(@onNull ProtoOutputStream out, TextAppearanceSpan span)1383     public static void writeTextAppearanceSpanToProto(@NonNull ProtoOutputStream out,
1384             TextAppearanceSpan span) {
1385         out.write(RemoteViewsProto.CharSequence.Span.TextAppearance.FAMILY_NAME, span.getFamily());
1386         out.write(RemoteViewsProto.CharSequence.Span.TextAppearance.STYLE, span.getTextStyle());
1387         out.write(RemoteViewsProto.CharSequence.Span.TextAppearance.TEXT_SIZE, span.getTextSize());
1388         out.write(RemoteViewsProto.CharSequence.Span.TextAppearance.TEXT_FONT_WEIGHT,
1389                 span.getTextFontWeight());
1390         if (span.getTextLocales() != null) {
1391             out.write(RemoteViewsProto.CharSequence.Span.TextAppearance.TEXT_LOCALE,
1392                     span.getTextLocales().toLanguageTags());
1393         }
1394         out.write(RemoteViewsProto.CharSequence.Span.TextAppearance.SHADOW_RADIUS,
1395                 span.getShadowRadius());
1396         out.write(RemoteViewsProto.CharSequence.Span.TextAppearance.SHADOW_DX, span.getShadowDx());
1397         out.write(RemoteViewsProto.CharSequence.Span.TextAppearance.SHADOW_DY, span.getShadowDy());
1398         out.write(RemoteViewsProto.CharSequence.Span.TextAppearance.SHADOW_COLOR,
1399                 span.getShadowColor());
1400         out.write(RemoteViewsProto.CharSequence.Span.TextAppearance.HAS_ELEGANT_TEXT_HEIGHT_FIELD,
1401                 span.hasElegantTextHeight());
1402         out.write(RemoteViewsProto.CharSequence.Span.TextAppearance.ELEGANT_TEXT_HEIGHT,
1403                 span.isElegantTextHeight());
1404         out.write(RemoteViewsProto.CharSequence.Span.TextAppearance.HAS_LETTER_SPACING_FIELD,
1405                 span.hasLetterSpacing());
1406         out.write(RemoteViewsProto.CharSequence.Span.TextAppearance.LETTER_SPACING,
1407                 span.getLetterSpacing());
1408         out.write(RemoteViewsProto.CharSequence.Span.TextAppearance.FONT_FEATURE_SETTINGS,
1409                 span.getFontFeatureSettings());
1410         out.write(RemoteViewsProto.CharSequence.Span.TextAppearance.FONT_VARIATION_SETTINGS,
1411                 span.getFontVariationSettings());
1412         if (span.getTextColor() != null) {
1413             final long textColorToken = out.start(
1414                     RemoteViewsProto.CharSequence.Span.TextAppearance.TEXT_COLOR);
1415             span.getTextColor().writeToProto(out);
1416             out.end(textColorToken);
1417         }
1418         if (span.getLinkTextColor() != null) {
1419             final long textColorLinkToken = out.start(
1420                     RemoteViewsProto.CharSequence.Span.TextAppearance.TEXT_COLOR_LINK);
1421             span.getLinkTextColor().writeToProto(out);
1422             out.end(textColorLinkToken);
1423         }
1424     }
1425 
createTtsSpanFromProto(@onNull ProtoInputStream in)1426     public static TtsSpan createTtsSpanFromProto(@NonNull ProtoInputStream in) throws Exception {
1427         String type = null;
1428         PersistableBundle args = null;
1429         while (in.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
1430             switch (in.getFieldNumber()) {
1431                 case (int) RemoteViewsProto.CharSequence.Span.Tts.TYPE:
1432                     type = in.readString(RemoteViewsProto.CharSequence.Span.Tts.TYPE);
1433                     break;
1434                 case (int) RemoteViewsProto.CharSequence.Span.Tts.ARGS:
1435                     final byte[] data = in.readString(
1436                             RemoteViewsProto.CharSequence.Span.Tts.ARGS).getBytes();
1437                     args = PersistableBundle.readFromStream(new ByteArrayInputStream(data));
1438                     break;
1439                 default:
1440                     Log.w("TtsSpan", "Unhandled field while reading TtsSpan " + "proto!\n"
1441                             + ProtoUtils.currentFieldToString(in));
1442             }
1443         }
1444         return new TtsSpan(type, args);
1445     }
1446 
writeTtsSpanToProto(@onNull ProtoOutputStream out, TtsSpan span)1447     public static void writeTtsSpanToProto(@NonNull ProtoOutputStream out, TtsSpan span) {
1448         out.write(RemoteViewsProto.CharSequence.Span.Tts.TYPE, span.getType());
1449         if (span.getArgs() != null) {
1450             ByteArrayOutputStream buf = new ByteArrayOutputStream();
1451             try {
1452                 span.getArgs().writeToStream(buf);
1453             } catch (IOException e) {
1454                 throw new RuntimeException(e);
1455             }
1456             out.write(RemoteViewsProto.CharSequence.Span.Tts.ARGS, buf.toString(UTF_8));
1457         }
1458     }
1459 
createTypefaceSpanFromProto(@onNull ProtoInputStream in)1460     public static TypefaceSpan createTypefaceSpanFromProto(@NonNull ProtoInputStream in)
1461             throws Exception {
1462         String family = null;
1463         while (in.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
1464             switch (in.getFieldNumber()) {
1465                 case (int) RemoteViewsProto.CharSequence.Span.Typeface.FAMILY:
1466                     family = in.readString(RemoteViewsProto.CharSequence.Span.Typeface.FAMILY);
1467                     break;
1468                 default:
1469                     Log.w("TypefaceSpan", "Unhandled field while reading" + " TypefaceSpan proto!\n"
1470                             + ProtoUtils.currentFieldToString(in));
1471             }
1472         }
1473         return new TypefaceSpan(family);
1474     }
1475 
writeTypefaceSpanToProto(@onNull ProtoOutputStream out, TypefaceSpan span)1476     public static void writeTypefaceSpanToProto(@NonNull ProtoOutputStream out, TypefaceSpan span) {
1477         out.write(RemoteViewsProto.CharSequence.Span.Typeface.FAMILY, span.getFamily());
1478     }
1479 
createURLSpanFromProto(@onNull ProtoInputStream in)1480     public static URLSpan createURLSpanFromProto(@NonNull ProtoInputStream in) throws Exception {
1481         String url = null;
1482         while (in.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
1483             switch (in.getFieldNumber()) {
1484                 case (int) RemoteViewsProto.CharSequence.Span.Url.URL:
1485                     url = in.readString(RemoteViewsProto.CharSequence.Span.Url.URL);
1486                     break;
1487                 default:
1488                     Log.w("URLSpan", "Unhandled field while reading" + " URLSpan proto!\n"
1489                             + ProtoUtils.currentFieldToString(in));
1490             }
1491         }
1492         return new URLSpan(url);
1493     }
1494 
writeURLSpanToProto(@onNull ProtoOutputStream out, URLSpan span)1495     public static void writeURLSpanToProto(@NonNull ProtoOutputStream out, URLSpan span) {
1496         out.write(RemoteViewsProto.CharSequence.Span.Url.URL, span.getURL());
1497     }
1498 
createUnderlineSpanFromProto(@onNull ProtoInputStream in)1499     public static UnderlineSpan createUnderlineSpanFromProto(@NonNull ProtoInputStream in) {
1500         return new UnderlineSpan();
1501     }
1502 
writeUnderlineSpanToProto(@onNull ProtoOutputStream out, UnderlineSpan span)1503     public static void writeUnderlineSpanToProto(@NonNull ProtoOutputStream out,
1504             UnderlineSpan span) {
1505     }
1506 }
1507