1 /* 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 17 package android.service.autofill; 18 19 import static android.view.autofill.Helper.sDebug; 20 import static android.view.autofill.Helper.sVerbose; 21 22 import android.annotation.IdRes; 23 import android.annotation.NonNull; 24 import android.os.Parcel; 25 import android.os.Parcelable; 26 import android.util.Slog; 27 import android.util.SparseIntArray; 28 import android.view.View; 29 import android.view.View.Visibility; 30 import android.view.ViewGroup; 31 import android.widget.RemoteViews; 32 33 import 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 */ 41 public final class VisibilitySetterAction extends InternalOnClickAction implements 42 OnClickAction, Parcelable { 43 private static final String TAG = "VisibilitySetterAction"; 44 45 @NonNull private final SparseIntArray mVisibilities; 46 VisibilitySetterAction(@onNull Builder builder)47 private VisibilitySetterAction(@NonNull Builder builder) { 48 mVisibilities = builder.mVisibilities; 49 } 50 51 /** @hide */ 52 @Override onClick(@onNull ViewGroup rootView)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 final 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}. 83 * @throws IllegalArgumentException if visibility is not one of {@link View#VISIBLE}, 84 * {@link View#INVISIBLE}, or {@link View#GONE}. 85 */ Builder(@dRes int id, @Visibility int visibility)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}. 96 * @throws IllegalArgumentException if visibility is not one of {@link View#VISIBLE}, 97 * {@link View#INVISIBLE}, or {@link View#GONE}. 98 */ 99 @NonNull setVisibility(@dRes int id, @Visibility int visibility)100 public Builder setVisibility(@IdRes int id, @Visibility int visibility) { 101 throwIfDestroyed(); 102 switch (visibility) { 103 case View.VISIBLE: 104 case View.INVISIBLE: 105 case View.GONE: 106 mVisibilities.put(id, visibility); 107 return this; 108 } 109 throw new IllegalArgumentException("Invalid visibility: " + visibility); 110 } 111 112 /** 113 * Creates a new {@link VisibilitySetterAction} instance. 114 */ 115 @NonNull build()116 public VisibilitySetterAction build() { 117 throwIfDestroyed(); 118 mDestroyed = true; 119 return new VisibilitySetterAction(this); 120 } 121 throwIfDestroyed()122 private void throwIfDestroyed() { 123 Preconditions.checkState(!mDestroyed, "Already called build()"); 124 } 125 } 126 127 ///////////////////////////////////// 128 // Object "contract" methods. // 129 ///////////////////////////////////// 130 @Override toString()131 public String toString() { 132 if (!sDebug) return super.toString(); 133 134 return "VisibilitySetterAction: [" + mVisibilities + "]"; 135 } 136 137 ///////////////////////////////////// 138 // Parcelable "contract" methods. // 139 ///////////////////////////////////// 140 @Override describeContents()141 public int describeContents() { 142 return 0; 143 } 144 145 @Override writeToParcel(Parcel parcel, int flags)146 public void writeToParcel(Parcel parcel, int flags) { 147 parcel.writeSparseIntArray(mVisibilities); 148 } 149 150 public static final @android.annotation.NonNull Parcelable.Creator<VisibilitySetterAction> CREATOR = 151 new Parcelable.Creator<VisibilitySetterAction>() { 152 153 @NonNull 154 @Override 155 public VisibilitySetterAction createFromParcel(Parcel parcel) { 156 // Always go through the builder to ensure the data ingested by 157 // the system obeys the contract of the builder to avoid attacks 158 final SparseIntArray visibilities = parcel.readSparseIntArray(); 159 Builder builder = null; 160 for (int i = 0; i < visibilities.size(); i++) { 161 final int id = visibilities.keyAt(i); 162 final int visibility = visibilities.valueAt(i); 163 if (builder == null) { 164 builder = new Builder(id, visibility); 165 } else { 166 builder.setVisibility(id, visibility); 167 } 168 } 169 return builder == null ? null : builder.build(); 170 } 171 172 @NonNull 173 @Override 174 public VisibilitySetterAction[] newArray(int size) { 175 return new VisibilitySetterAction[size]; 176 } 177 }; 178 } 179