blob: 9f977d79c184eb4da6bae7a430dfb4b81635e965 [file] [log] [blame]
Felipe Leme37f83722018-08-16 13:12:03 -07001/*
2 * Copyright (C) 2018 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;
20import static android.view.autofill.Helper.sVerbose;
21
22import android.annotation.IdRes;
23import android.annotation.NonNull;
24import android.os.Parcel;
25import android.os.Parcelable;
26import android.util.Slog;
27import android.util.SparseIntArray;
28import android.view.View;
29import android.view.View.Visibility;
30import android.view.ViewGroup;
31import android.widget.RemoteViews;
32
33import com.android.internal.util.Preconditions;
34
35/**
36 * Action used to change the visibility of other child view in a {@link CustomDescription}
37 * {@link RemoteViews presentation template}.
38 *
39 * <p>See {@link CustomDescription.Builder#addOnClickAction(int, OnClickAction)} for more details.
40 */
41public final class VisibilitySetterAction extends InternalOnClickAction implements
42 OnClickAction, Parcelable {
43 private static final String TAG = "VisibilitySetterAction";
44
45 @NonNull private final SparseIntArray mVisibilities;
46
47 private VisibilitySetterAction(@NonNull Builder builder) {
48 mVisibilities = builder.mVisibilities;
49 }
50
51 /** @hide */
52 @Override
53 public void onClick(@NonNull ViewGroup rootView) {
54 for (int i = 0; i < mVisibilities.size(); i++) {
55 final int id = mVisibilities.keyAt(i);
56 final View child = rootView.findViewById(id);
57 if (child == null) {
58 Slog.w(TAG, "Skipping view id " + id + " because it's not found on " + rootView);
59 continue;
60 }
61 final int visibility = mVisibilities.valueAt(i);
62 if (sVerbose) {
63 Slog.v(TAG, "Changing visibility of view " + child + " from "
64 + child.getVisibility() + " to " + visibility);
65 }
66 child.setVisibility(visibility);
67 }
68 }
69
70 /**
71 * Builder for {@link VisibilitySetterAction} objects.
72 */
73 public static class Builder {
74 private final SparseIntArray mVisibilities = new SparseIntArray();
75 private boolean mDestroyed;
76
77 /**
78 * Creates a new builder for an action that change the visibility of one child view.
79 *
80 * @param id view resource id of the children view.
81 * @param visibility one of {@link View#VISIBLE}, {@link View#INVISIBLE}, or
82 * {@link View#GONE}.
Felipe Leme5d62f822018-08-21 11:34:13 -070083 * @throws IllegalArgumentException if visibility is not one of {@link View#VISIBLE},
Felipe Leme37f83722018-08-16 13:12:03 -070084 * {@link View#INVISIBLE}, or {@link View#GONE}.
85 */
86 public Builder(@IdRes int id, @Visibility int visibility) {
87 setVisibility(id, visibility);
88 }
89
90 /**
91 * Sets the action to changes the visibility of a child view.
92 *
93 * @param id view resource id of the children view.
94 * @param visibility one of {@link View#VISIBLE}, {@link View#INVISIBLE}, or
95 * {@link View#GONE}.
Felipe Leme5d62f822018-08-21 11:34:13 -070096 * @throws IllegalArgumentException if visibility is not one of {@link View#VISIBLE},
Felipe Leme37f83722018-08-16 13:12:03 -070097 * {@link View#INVISIBLE}, or {@link View#GONE}.
98 */
99 public Builder setVisibility(@IdRes int id, @Visibility int visibility) {
100 throwIfDestroyed();
101 switch (visibility) {
102 case View.VISIBLE:
103 case View.INVISIBLE:
104 case View.GONE:
105 mVisibilities.put(id, visibility);
106 return this;
107 }
108 throw new IllegalArgumentException("Invalid visibility: " + visibility);
109 }
110
111 /**
112 * Creates a new {@link VisibilitySetterAction} instance.
113 */
114 public VisibilitySetterAction build() {
115 throwIfDestroyed();
116 mDestroyed = true;
117 return new VisibilitySetterAction(this);
118 }
119
120 private void throwIfDestroyed() {
121 Preconditions.checkState(!mDestroyed, "Already called build()");
122 }
123 }
124
125 /////////////////////////////////////
126 // Object "contract" methods. //
127 /////////////////////////////////////
128 @Override
129 public String toString() {
130 if (!sDebug) return super.toString();
131
132 return "VisibilitySetterAction: [" + mVisibilities + "]";
133 }
134
135 /////////////////////////////////////
136 // Parcelable "contract" methods. //
137 /////////////////////////////////////
138 @Override
139 public int describeContents() {
140 return 0;
141 }
142
143 @Override
144 public void writeToParcel(Parcel parcel, int flags) {
145 parcel.writeSparseIntArray(mVisibilities);
146 }
147
148 public static final Parcelable.Creator<VisibilitySetterAction> CREATOR =
149 new Parcelable.Creator<VisibilitySetterAction>() {
150 @Override
151 public VisibilitySetterAction createFromParcel(Parcel parcel) {
152 // Always go through the builder to ensure the data ingested by
153 // the system obeys the contract of the builder to avoid attacks
154 final SparseIntArray visibilities = parcel.readSparseIntArray();
155 Builder builder = null;
156 for (int i = 0; i < visibilities.size(); i++) {
157 final int id = visibilities.keyAt(i);
158 final int visibility = visibilities.valueAt(i);
159 if (builder == null) {
160 builder = new Builder(id, visibility);
161 } else {
162 builder.setVisibility(id, visibility);
163 }
164 }
165 return builder == null ? null : builder.build();
166 }
167
168 @Override
169 public VisibilitySetterAction[] newArray(int size) {
170 return new VisibilitySetterAction[size];
171 }
172 };
173}