blob: e0b1c2f91ed2a8e321a79c6973981d888b7159f3 [file] [log] [blame]
Felipe Leme63c601a2017-09-27 17:40:30 -07001/*
2 * Copyright (C) 2017 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
17package android.service.autofill;
18
19import static android.view.autofill.Helper.sDebug;
20
21import android.annotation.NonNull;
22import android.annotation.Nullable;
23import android.os.Parcel;
24import android.os.Parcelable;
25import android.util.Pair;
26import android.widget.RemoteViews;
27
28import com.android.internal.util.Preconditions;
29
30import java.util.ArrayList;
31
32/**
33 * Defines actions to be applied to a {@link RemoteViews template presentation}.
34 *
35 *
36 * <p>It supports 2 types of actions:
37 *
38 * <ol>
39 * <li>{@link RemoteViews Actions} to be applied to the template.
40 * <li>{@link Transformation Transformations} to be applied on child views.
41 * </ol>
42 *
43 * <p>Typically used on {@link CustomDescription custom descriptions} to conditionally display
44 * differents views based on user input - see
45 * {@link CustomDescription.Builder#batchUpdate(Validator, BatchUpdates)} for more information.
46 */
47public final class BatchUpdates implements Parcelable {
48
49 private final ArrayList<Pair<Integer, InternalTransformation>> mTransformations;
50 private final RemoteViews mUpdates;
51
52 private BatchUpdates(Builder builder) {
53 mTransformations = builder.mTransformations;
54 mUpdates = builder.mUpdates;
55 }
56
57 /** @hide */
58 @Nullable
59 public ArrayList<Pair<Integer, InternalTransformation>> getTransformations() {
60 return mTransformations;
61 }
62
63 /** @hide */
64 @Nullable
65 public RemoteViews getUpdates() {
66 return mUpdates;
67 }
68
69 /**
70 * Builder for {@link BatchUpdates} objects.
71 */
72 public static class Builder {
73 private RemoteViews mUpdates;
74
75 private boolean mDestroyed;
76 private ArrayList<Pair<Integer, InternalTransformation>> mTransformations;
77
78 /**
79 * Applies the {@code updates} in the underlying presentation template.
80 *
81 * <p><b>Note:</b> The updates are applied before the
82 * {@link #transformChild(int, Transformation) transformations} are applied to the children
83 * views.
84 *
Dake Gu36b86c22018-04-16 12:49:30 -070085 * <p>Theme does not work with RemoteViews layout. Avoid hardcoded text color
86 * or background color: Autofill on different platforms may have different themes.
87 *
Felipe Leme63c601a2017-09-27 17:40:30 -070088 * @param updates a {@link RemoteViews} with the updated actions to be applied in the
89 * underlying presentation template.
90 *
91 * @return this builder
92 * @throws IllegalArgumentException if {@code condition} is not a class provided
93 * by the Android System.
94 */
95 public Builder updateTemplate(@NonNull RemoteViews updates) {
96 throwIfDestroyed();
97 mUpdates = Preconditions.checkNotNull(updates);
98 return this;
99 }
100
101 /**
102 * Adds a transformation to replace the value of a child view with the fields in the
103 * screen.
104 *
105 * <p>When multiple transformations are added for the same child view, they are applied
106 * in the same order as added.
107 *
108 * <p><b>Note:</b> The transformations are applied after the
109 * {@link #updateTemplate(RemoteViews) updates} are applied to the presentation template.
110 *
111 * @param id view id of the children view.
112 * @param transformation an implementation provided by the Android System.
113 * @return this builder.
114 * @throws IllegalArgumentException if {@code transformation} is not a class provided
115 * by the Android System.
116 */
117 public Builder transformChild(int id, @NonNull Transformation transformation) {
118 throwIfDestroyed();
119 Preconditions.checkArgument((transformation instanceof InternalTransformation),
120 "not provided by Android System: " + transformation);
121 if (mTransformations == null) {
122 mTransformations = new ArrayList<>();
123 }
124 mTransformations.add(new Pair<>(id, (InternalTransformation) transformation));
125 return this;
126 }
127
128 /**
129 * Creates a new {@link BatchUpdates} instance.
130 *
131 * @throws IllegalStateException if {@link #build()} was already called before or no call
132 * to {@link #updateTemplate(RemoteViews)} or {@link #transformChild(int, Transformation)}
133 * has been made.
134 */
135 public BatchUpdates build() {
136 throwIfDestroyed();
137 Preconditions.checkState(mUpdates != null || mTransformations != null,
138 "must call either updateTemplate() or transformChild() at least once");
139 mDestroyed = true;
140 return new BatchUpdates(this);
141 }
142
143 private void throwIfDestroyed() {
144 if (mDestroyed) {
145 throw new IllegalStateException("Already called #build()");
146 }
147 }
148 }
149
150 /////////////////////////////////////
151 // Object "contract" methods. //
152 /////////////////////////////////////
153 @Override
154 public String toString() {
155 if (!sDebug) return super.toString();
156
157 return new StringBuilder("BatchUpdates: [")
158 .append(", transformations=")
159 .append(mTransformations == null ? "N/A" : mTransformations.size())
160 .append(", updates=").append(mUpdates)
161 .append("]").toString();
162 }
163
164 /////////////////////////////////////
165 // Parcelable "contract" methods. //
166 /////////////////////////////////////
167 @Override
168 public int describeContents() {
169 return 0;
170 }
171
172 @Override
173 public void writeToParcel(Parcel dest, int flags) {
174 if (mTransformations == null) {
175 dest.writeIntArray(null);
176 } else {
177 final int size = mTransformations.size();
178 final int[] ids = new int[size];
179 final InternalTransformation[] values = new InternalTransformation[size];
180 for (int i = 0; i < size; i++) {
181 final Pair<Integer, InternalTransformation> pair = mTransformations.get(i);
182 ids[i] = pair.first;
183 values[i] = pair.second;
184 }
185 dest.writeIntArray(ids);
186 dest.writeParcelableArray(values, flags);
187 }
188 dest.writeParcelable(mUpdates, flags);
189 }
Jeff Sharkey9e8f83d2019-02-28 12:06:45 -0700190 public static final @android.annotation.NonNull Parcelable.Creator<BatchUpdates> CREATOR =
Felipe Leme63c601a2017-09-27 17:40:30 -0700191 new Parcelable.Creator<BatchUpdates>() {
192 @Override
193 public BatchUpdates createFromParcel(Parcel parcel) {
194 // Always go through the builder to ensure the data ingested by
195 // the system obeys the contract of the builder to avoid attacks
196 // using specially crafted parcels.
197 final Builder builder = new Builder();
198 final int[] ids = parcel.createIntArray();
199 if (ids != null) {
200 final InternalTransformation[] values =
201 parcel.readParcelableArray(null, InternalTransformation.class);
202 final int size = ids.length;
203 for (int i = 0; i < size; i++) {
204 builder.transformChild(ids[i], values[i]);
205 }
206 }
207 final RemoteViews updates = parcel.readParcelable(null);
208 if (updates != null) {
209 builder.updateTemplate(updates);
210 }
211 return builder.build();
212 }
213
214 @Override
215 public BatchUpdates[] newArray(int size) {
216 return new BatchUpdates[size];
217 }
218 };
219}