1 /* 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 17 package com.android.server.autofill; 18 19 import static android.service.autofill.FillRequest.FLAG_MANUAL_REQUEST; 20 21 import static com.android.server.autofill.Helper.sDebug; 22 23 import android.annotation.NonNull; 24 import android.annotation.Nullable; 25 import android.graphics.Rect; 26 import android.service.autofill.FillResponse; 27 import android.util.DebugUtils; 28 import android.util.Slog; 29 import android.view.autofill.AutofillId; 30 import android.view.autofill.AutofillValue; 31 32 import java.io.PrintWriter; 33 34 /** 35 * State for a given view with a AutofillId. 36 * 37 * <p>This class holds state about a view and calls its listener when the fill UI is ready to 38 * be displayed for the view. 39 */ 40 final class ViewState { 41 interface Listener { 42 /** 43 * Called when the fill UI is ready to be shown for this view. 44 */ onFillReady(@onNull FillResponse fillResponse, @NonNull AutofillId focusedId, @Nullable AutofillValue value, int flags)45 void onFillReady(@NonNull FillResponse fillResponse, @NonNull AutofillId focusedId, 46 @Nullable AutofillValue value, int flags); 47 } 48 49 private static final String TAG = "ViewState"; 50 51 /** Initial state. */ 52 public static final int STATE_INITIAL = 0x001; 53 /** View id is present in a dataset returned by the service. */ 54 public static final int STATE_FILLABLE = 0x002; 55 /** View was autofilled after user selected a dataset. */ 56 public static final int STATE_AUTOFILLED = 0x004; 57 /** View value was changed, but not by the service. */ 58 public static final int STATE_CHANGED = 0x008; 59 /** Set only in the View that started a session. */ 60 public static final int STATE_STARTED_SESSION = 0x010; 61 /** View that started a new partition when focused on. */ 62 public static final int STATE_STARTED_PARTITION = 0x020; 63 /** User select a dataset in this view, but service must authenticate first. */ 64 public static final int STATE_WAITING_DATASET_AUTH = 0x040; 65 /** Service does not care about this view. */ 66 public static final int STATE_IGNORED = 0x080; 67 /** User manually request autofill in this view, after it was already autofilled. */ 68 public static final int STATE_RESTARTED_SESSION = 0x100; 69 /** View is the URL bar of a package on compat mode. */ 70 public static final int STATE_URL_BAR = 0x200; 71 /** View was asked to autofill but failed to do so. */ 72 public static final int STATE_AUTOFILL_FAILED = 0x400; 73 /** View has been autofilled at least once. */ 74 public static final int STATE_AUTOFILLED_ONCE = 0x800; 75 /** View triggered the latest augmented autofill request. */ 76 public static final int STATE_TRIGGERED_AUGMENTED_AUTOFILL = 0x1000; 77 /** Inline suggestions were shown for this View. */ 78 public static final int STATE_INLINE_SHOWN = 0x2000; 79 /** A character was removed from the View value (not by the service). */ 80 public static final int STATE_CHAR_REMOVED = 0x4000; 81 /** Showing inline suggestions is not allowed for this View. */ 82 public static final int STATE_INLINE_DISABLED = 0x8000; 83 /** The View is waiting for an inline suggestions request from IME.*/ 84 public static final int STATE_PENDING_CREATE_INLINE_REQUEST = 0x10000; 85 /** Fill dialog were shown for this View. */ 86 public static final int STATE_FILL_DIALOG_SHOWN = 0x20000; 87 88 public final AutofillId id; 89 90 private final Listener mListener; 91 92 private FillResponse mResponse; 93 private AutofillValue mCurrentValue; 94 private AutofillValue mAutofilledValue; 95 private AutofillValue mSanitizedValue; 96 private Rect mVirtualBounds; 97 private int mState; 98 private String mDatasetId; 99 ViewState(AutofillId id, Listener listener, int state)100 ViewState(AutofillId id, Listener listener, int state) { 101 this.id = id; 102 mListener = listener; 103 mState = state; 104 } 105 106 /** 107 * Gets the boundaries of the virtual view, or {@code null} if the the view is not virtual. 108 */ 109 @Nullable getVirtualBounds()110 Rect getVirtualBounds() { 111 return mVirtualBounds; 112 } 113 114 /** 115 * Gets the current value of the view. 116 */ 117 @Nullable getCurrentValue()118 AutofillValue getCurrentValue() { 119 return mCurrentValue; 120 } 121 setCurrentValue(AutofillValue value)122 void setCurrentValue(AutofillValue value) { 123 mCurrentValue = value; 124 } 125 126 @Nullable getAutofilledValue()127 AutofillValue getAutofilledValue() { 128 return mAutofilledValue; 129 } 130 setAutofilledValue(@ullable AutofillValue value)131 void setAutofilledValue(@Nullable AutofillValue value) { 132 mAutofilledValue = value; 133 } 134 135 @Nullable getSanitizedValue()136 AutofillValue getSanitizedValue() { 137 return mSanitizedValue; 138 } 139 setSanitizedValue(@ullable AutofillValue value)140 void setSanitizedValue(@Nullable AutofillValue value) { 141 mSanitizedValue = value; 142 } 143 144 @Nullable getResponse()145 FillResponse getResponse() { 146 return mResponse; 147 } 148 setResponse(FillResponse response)149 void setResponse(FillResponse response) { 150 mResponse = response; 151 } 152 getState()153 int getState() { 154 return mState; 155 } 156 getStateAsString()157 String getStateAsString() { 158 return getStateAsString(mState); 159 } 160 getStateAsString(int state)161 static String getStateAsString(int state) { 162 return DebugUtils.flagsToString(ViewState.class, "STATE_", state); 163 } 164 setState(int state)165 void setState(int state) { 166 if (mState == STATE_INITIAL) { 167 mState = state; 168 } else { 169 mState |= state; 170 } 171 if (state == STATE_AUTOFILLED) { 172 mState |= STATE_AUTOFILLED_ONCE; 173 } 174 } 175 resetState(int state)176 void resetState(int state) { 177 mState &= ~state; 178 } 179 180 @Nullable getDatasetId()181 String getDatasetId() { 182 return mDatasetId; 183 } 184 setDatasetId(String datasetId)185 void setDatasetId(String datasetId) { 186 mDatasetId = datasetId; 187 } 188 189 // TODO: refactor / rename / document this method (and maybeCallOnFillReady) to make it clear 190 // that it can change the value and update the UI; similarly, should replace code that 191 // directly sets mAutofillValue to use encapsulation. update(@ullable AutofillValue autofillValue, @Nullable Rect virtualBounds, int flags)192 void update(@Nullable AutofillValue autofillValue, @Nullable Rect virtualBounds, int flags) { 193 if (autofillValue != null) { 194 mCurrentValue = autofillValue; 195 } 196 if (virtualBounds != null) { 197 mVirtualBounds = virtualBounds; 198 } 199 200 maybeCallOnFillReady(flags); 201 } 202 203 /** 204 * Calls {@link 205 * Listener#onFillReady(FillResponse, AutofillId, AutofillValue, int)} if the 206 * fill UI is ready to be displayed (i.e. when response and bounds are set). 207 */ maybeCallOnFillReady(int flags)208 void maybeCallOnFillReady(int flags) { 209 if ((mState & STATE_AUTOFILLED) != 0 && (flags & FLAG_MANUAL_REQUEST) == 0) { 210 if (sDebug) Slog.d(TAG, "Ignoring UI for " + id + " on " + getStateAsString()); 211 return; 212 } 213 // First try the current response associated with this View. 214 if (mResponse != null) { 215 if (mResponse.getDatasets() != null || mResponse.getAuthentication() != null) { 216 mListener.onFillReady(mResponse, this.id, mCurrentValue, flags); 217 } 218 } 219 } 220 221 @Override toString()222 public String toString() { 223 final StringBuilder builder = new StringBuilder("ViewState: [id=").append(id); 224 if (mDatasetId != null) { 225 builder.append(", datasetId:" ).append(mDatasetId); 226 } 227 builder.append(", state:").append(getStateAsString()); 228 if (mCurrentValue != null) { 229 builder.append(", currentValue:" ).append(mCurrentValue); 230 } 231 if (mAutofilledValue != null) { 232 builder.append(", autofilledValue:" ).append(mAutofilledValue); 233 } 234 if (mSanitizedValue != null) { 235 builder.append(", sanitizedValue:" ).append(mSanitizedValue); 236 } 237 if (mVirtualBounds != null) { 238 builder.append(", virtualBounds:" ).append(mVirtualBounds); 239 } 240 builder.append("]"); 241 return builder.toString(); 242 } 243 dump(String prefix, PrintWriter pw)244 void dump(String prefix, PrintWriter pw) { 245 pw.print(prefix); pw.print("id:" ); pw.println(id); 246 if (mDatasetId != null) { 247 pw.print(prefix); pw.print("datasetId:" ); pw.println(mDatasetId); 248 } 249 pw.print(prefix); pw.print("state:" ); pw.println(getStateAsString()); 250 if (mResponse != null) { 251 pw.print(prefix); pw.print("response id:");pw.println(mResponse.getRequestId()); 252 } 253 if (mCurrentValue != null) { 254 pw.print(prefix); pw.print("currentValue:" ); pw.println(mCurrentValue); 255 } 256 if (mAutofilledValue != null) { 257 pw.print(prefix); pw.print("autofilledValue:" ); pw.println(mAutofilledValue); 258 } 259 if (mSanitizedValue != null) { 260 pw.print(prefix); pw.print("sanitizedValue:" ); pw.println(mSanitizedValue); 261 } 262 if (mVirtualBounds != null) { 263 pw.print(prefix); pw.print("virtualBounds:" ); pw.println(mVirtualBounds); 264 } 265 } 266 }