/* * Copyright (C) 2018 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package android.service.autofill; import static android.view.autofill.Helper.sDebug; import static android.view.autofill.Helper.sVerbose; import android.annotation.IdRes; import android.annotation.NonNull; import android.os.Parcel; import android.os.Parcelable; import android.util.Slog; import android.util.SparseIntArray; import android.view.View; import android.view.View.Visibility; import android.view.ViewGroup; import android.widget.RemoteViews; import com.android.internal.util.Preconditions; /** * Action used to change the visibility of other child view in a {@link CustomDescription} * {@link RemoteViews presentation template}. * *

See {@link CustomDescription.Builder#addOnClickAction(int, OnClickAction)} for more details. */ public final class VisibilitySetterAction extends InternalOnClickAction implements OnClickAction, Parcelable { private static final String TAG = "VisibilitySetterAction"; @NonNull private final SparseIntArray mVisibilities; private VisibilitySetterAction(@NonNull Builder builder) { mVisibilities = builder.mVisibilities; } /** @hide */ @Override public void onClick(@NonNull ViewGroup rootView) { for (int i = 0; i < mVisibilities.size(); i++) { final int id = mVisibilities.keyAt(i); final View child = rootView.findViewById(id); if (child == null) { Slog.w(TAG, "Skipping view id " + id + " because it's not found on " + rootView); continue; } final int visibility = mVisibilities.valueAt(i); if (sVerbose) { Slog.v(TAG, "Changing visibility of view " + child + " from " + child.getVisibility() + " to " + visibility); } child.setVisibility(visibility); } } /** * Builder for {@link VisibilitySetterAction} objects. */ public static final class Builder { private final SparseIntArray mVisibilities = new SparseIntArray(); private boolean mDestroyed; /** * Creates a new builder for an action that change the visibility of one child view. * * @param id view resource id of the children view. * @param visibility one of {@link View#VISIBLE}, {@link View#INVISIBLE}, or * {@link View#GONE}. * @throws IllegalArgumentException if visibility is not one of {@link View#VISIBLE}, * {@link View#INVISIBLE}, or {@link View#GONE}. */ public Builder(@IdRes int id, @Visibility int visibility) { setVisibility(id, visibility); } /** * Sets the action to changes the visibility of a child view. * * @param id view resource id of the children view. * @param visibility one of {@link View#VISIBLE}, {@link View#INVISIBLE}, or * {@link View#GONE}. * @throws IllegalArgumentException if visibility is not one of {@link View#VISIBLE}, * {@link View#INVISIBLE}, or {@link View#GONE}. */ @NonNull public Builder setVisibility(@IdRes int id, @Visibility int visibility) { throwIfDestroyed(); switch (visibility) { case View.VISIBLE: case View.INVISIBLE: case View.GONE: mVisibilities.put(id, visibility); return this; } throw new IllegalArgumentException("Invalid visibility: " + visibility); } /** * Creates a new {@link VisibilitySetterAction} instance. */ @NonNull public VisibilitySetterAction build() { throwIfDestroyed(); mDestroyed = true; return new VisibilitySetterAction(this); } private void throwIfDestroyed() { Preconditions.checkState(!mDestroyed, "Already called build()"); } } ///////////////////////////////////// // Object "contract" methods. // ///////////////////////////////////// @Override public String toString() { if (!sDebug) return super.toString(); return "VisibilitySetterAction: [" + mVisibilities + "]"; } ///////////////////////////////////// // Parcelable "contract" methods. // ///////////////////////////////////// @Override public int describeContents() { return 0; } @Override public void writeToParcel(Parcel parcel, int flags) { parcel.writeSparseIntArray(mVisibilities); } public static final @android.annotation.NonNull Parcelable.Creator CREATOR = new Parcelable.Creator() { @NonNull @Override public VisibilitySetterAction createFromParcel(Parcel parcel) { // Always go through the builder to ensure the data ingested by // the system obeys the contract of the builder to avoid attacks final SparseIntArray visibilities = parcel.readSparseIntArray(); Builder builder = null; for (int i = 0; i < visibilities.size(); i++) { final int id = visibilities.keyAt(i); final int visibility = visibilities.valueAt(i); if (builder == null) { builder = new Builder(id, visibility); } else { builder.setVisibility(id, visibility); } } return builder == null ? null : builder.build(); } @NonNull @Override public VisibilitySetterAction[] newArray(int size) { return new VisibilitySetterAction[size]; } }; }