• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2022 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 
17 package android.text;
18 
19 import android.graphics.Paint;
20 import android.util.Pair;
21 
22 import androidx.annotation.IntRange;
23 import androidx.annotation.NonNull;
24 
25 import java.util.ArrayList;
26 import java.util.Arrays;
27 import java.util.List;
28 import java.util.Objects;
29 
30 /**
31  * A class that represents of the highlight of the text.
32  */
33 @android.ravenwood.annotation.RavenwoodKeepWholeClass
34 public class Highlights {
35     private final List<Pair<Paint, int[]>> mHighlights;
36 
Highlights(List<Pair<Paint, int[]>> highlights)37     private Highlights(List<Pair<Paint, int[]>> highlights) {
38         mHighlights = highlights;
39     }
40 
41     /**
42      * Returns the number of highlight.
43      *
44      * @return the number of highlight.
45      *
46      * @see Builder#addRange(Paint, int, int)
47      * @see Builder#addRanges(Paint, int...)
48      */
getSize()49     public @IntRange(from = 0) int getSize() {
50         return mHighlights.size();
51     }
52 
53     /**
54      * Returns a paint used for the i-th highlight.
55      *
56      * @param index an index of the highlight. Must be between 0 and {@link #getSize()}
57      * @return the paint object
58      *
59      * @see Builder#addRange(Paint, int, int)
60      * @see Builder#addRanges(Paint, int...)
61      */
getPaint(@ntRangefrom = 0) int index)62     public @NonNull Paint getPaint(@IntRange(from = 0) int index) {
63         return mHighlights.get(index).first;
64     }
65 
66     /**
67      * Returns ranges of the i-th highlight.
68      *
69      * Ranges are represented of flattened inclusive start and exclusive end integers array. The
70      * inclusive start offset of the {@code i}-th range is stored in {@code 2 * i}-th of the array.
71      * The exclusive end offset of the {@code i}-th range is stored in {@code 2* i + 1}-th of the
72      * array. For example, the two ranges: (1, 2) and (3, 4) are flattened into single int array
73      * [1, 2, 3, 4].
74      *
75      * @param index an index of the highlight. Must be between 0 and {@link #getSize()}
76      * @return the flattened ranges.
77      *
78      * @see Builder#addRange(Paint, int, int)
79      * @see Builder#addRanges(Paint, int...)
80      */
getRanges(int index)81     public @NonNull int[] getRanges(int index) {
82         return mHighlights.get(index).second;
83     }
84 
85     /**
86      * A builder for the Highlights.
87      */
88     public static final class Builder {
89         private final List<Pair<Paint, int[]>> mHighlights = new ArrayList<>();
90 
91         /**
92          * Add single range highlight.
93          *
94          * The {@link android.widget.TextView} and underlying {@link Layout} draws highlight in the
95          * order of the {@link #addRange} calls.
96          *
97          * For example, the following code draws (1, 2) with red and (2, 5) with blue.
98          * <code>
99          *     val redPaint = Paint().apply { color = Color.RED }
100          *     val bluePaint = Paint().apply { color = Color.BLUE }
101          *     val highlight = Highlights.Builder()
102          *         .addRange(redPaint, 1, 4)
103          *         .addRange(bluePaint, 2, 5)
104          *         .build()
105          * </code>
106          *
107          *
108          * @param paint a paint object used for drawing highlight path.
109          * @param start an inclusive offset of the text.
110          * @param end an exclusive offset of the text.
111          * @return this builder instance.
112          */
addRange(@onNull Paint paint, @IntRange(from = 0) int start, @IntRange(from = 0) int end)113         public @NonNull Builder addRange(@NonNull Paint paint, @IntRange(from = 0) int start,
114                 @IntRange(from = 0) int end) {
115             if (start > end) {
116                 throw new IllegalArgumentException("start must not be larger than end: "
117                         + start + ", " + end);
118             }
119             Objects.requireNonNull(paint);
120 
121             int[] range = new int[] {start, end};
122             mHighlights.add(new Pair<>(paint, range));
123             return this;
124         }
125 
126         /**
127          * Add multiple ranges highlight.
128          *
129          * For example, the following code draws (1, 2) with red and (2, 5) with blue.
130          * <code>
131          *     val redPaint = Paint().apply { color = Color.RED }
132          *     val bluePaint = Paint().apply { color = Color.BLUE }
133          *     val highlight = Highlights.Builder()
134          *         .addRange(redPaint, 1, 4)
135          *         .addRange(bluePaint, 2, 5)
136          *         .build()
137          * </code>
138          *
139          * @param paint a paint object used for drawing highlight path.
140          * @param ranges a flatten ranges. The {@code 2 * i}-th element is an inclusive start offset
141          *              of the {@code i}-th character. The {@code 2 * i + 1}-th element is an
142          *              exclusive end offset of the {@code i}-th character.
143          * @return this builder instance.
144          */
addRanges(@onNull Paint paint, @NonNull int... ranges)145         public @NonNull Builder addRanges(@NonNull Paint paint, @NonNull int... ranges) {
146             if (ranges.length % 2 == 1) {
147                 throw new IllegalArgumentException(
148                         "Flatten ranges must have even numbered elements");
149             }
150             for (int j = 0; j < ranges.length / 2; ++j) {
151                 int start = ranges[j * 2];
152                 int end = ranges[j * 2 + 1];
153                 if (start > end) {
154                     throw new IllegalArgumentException(
155                             "Reverse range found in the flatten range: " + Arrays.toString(
156                                     ranges));
157                 }
158             }
159             Objects.requireNonNull(paint);
160             mHighlights.add(new Pair<>(paint, ranges));
161             return this;
162         }
163 
164         /**
165          * Build a new Highlights instance.
166          *
167          * @return a new Highlights instance.
168          */
build()169         public @NonNull Highlights build() {
170             return new Highlights(mHighlights);
171         }
172     }
173 }
174