ViewState.java revision 8697a31d3c32f212cee48b4d0576e8783c6c9d5f
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
17package com.android.server.autofill;
18
19import static android.service.autofill.FillRequest.FLAG_MANUAL_REQUEST;
20import static com.android.server.autofill.Helper.sDebug;
21import static com.android.server.autofill.Helper.sVerbose;
22
23import android.annotation.Nullable;
24import android.graphics.Rect;
25import android.service.autofill.FillResponse;
26import android.util.DebugUtils;
27import android.util.Slog;
28import android.view.autofill.AutofillId;
29import android.view.autofill.AutofillValue;
30
31import java.io.PrintWriter;
32
33/**
34 * State for a given view with a AutofillId.
35 *
36 * <p>This class holds state about a view and calls its listener when the fill UI is ready to
37 * be displayed for the view.
38 */
39final class ViewState {
40    interface Listener {
41        /**
42         * Called when the fill UI is ready to be shown for this view.
43         */
44        void onFillReady(FillResponse fillResponse, AutofillId focusedId,
45                @Nullable AutofillValue value);
46    }
47
48    private static final String TAG = "ViewState";
49
50    // NOTE: state constants must be public because of flagstoString().
51    public static final int STATE_UNKNOWN = 0x000;
52    /** Initial state. */
53    public static final int STATE_INITIAL = 0x001;
54    /** View id is present in a dataset returned by the service. */
55    public static final int STATE_FILLABLE = 0x002;
56    /** View was autofilled after user selected a dataset. */
57    public static final int STATE_AUTOFILLED = 0x004;
58    /** View value was changed, but not by the service. */
59    public static final int STATE_CHANGED = 0x008;
60    /** Set only in the View that started a session. */
61    public static final int STATE_STARTED_SESSION = 0x010;
62    /** View that started a new partition when focused on. */
63    public static final int STATE_STARTED_PARTITION = 0x020;
64    /** User select a dataset in this view, but service must authenticate first. */
65    public static final int STATE_WAITING_DATASET_AUTH = 0x040;
66    /** Service does not care about this view. */
67    public static final int STATE_IGNORED = 0x080;
68    /** User manually request autofill in this view, after it was already autofilled. */
69    public static final int STATE_RESTARTED_SESSION = 0x100;
70
71    public final AutofillId id;
72
73    private final Listener mListener;
74    private final Session mSession;
75
76    private FillResponse mResponse;
77    private AutofillValue mInitialValue;
78    private AutofillValue mCurrentValue;
79    private AutofillValue mAutofilledValue;
80    private Rect mVirtualBounds;
81    private int mState;
82
83    ViewState(Session session, AutofillId id, AutofillValue value, Listener listener, int state) {
84        mSession = session;
85        this.id = id;
86        mInitialValue = value;
87        mListener = listener;
88        mState = state;
89    }
90
91    /**
92     * Gets the boundaries of the virtual view, or {@code null} if the the view is not virtual.
93     */
94    @Nullable
95    Rect getVirtualBounds() {
96        return mVirtualBounds;
97    }
98
99    /**
100     * Gets the current value of the view.
101     */
102    @Nullable
103    AutofillValue getCurrentValue() {
104        return mCurrentValue;
105    }
106
107    void setCurrentValue(AutofillValue value) {
108        mCurrentValue = value;
109    }
110
111    @Nullable
112    AutofillValue getAutofilledValue() {
113        return mAutofilledValue;
114    }
115
116    void setAutofilledValue(@Nullable AutofillValue value) {
117        mAutofilledValue = value;
118    }
119
120    @Nullable
121    AutofillValue getInitialValue() {
122        return mInitialValue;
123    }
124
125    @Nullable
126    FillResponse getResponse() {
127        return mResponse;
128    }
129
130    void setResponse(FillResponse response) {
131        mResponse = response;
132    }
133
134    CharSequence getServiceName() {
135        return mSession.getServiceName();
136    }
137
138    int getState() {
139        return mState;
140    }
141
142    String getStateAsString() {
143        return DebugUtils.flagsToString(ViewState.class, "STATE_", mState);
144    }
145
146    void setState(int state) {
147        if (mState == STATE_INITIAL) {
148            mState = state;
149        } else {
150            mState |= state;
151        }
152    }
153
154    void resetState(int state) {
155        mState &= ~state;
156    }
157
158    // TODO: refactor / rename / document this method (and maybeCallOnFillReady) to make it clear
159    // that it can change the value and update the UI; similarly, should replace code that
160    // directly sets mAutofillValue to use encapsulation.
161    void update(@Nullable AutofillValue autofillValue, @Nullable Rect virtualBounds, int flags) {
162        if (autofillValue != null) {
163            mCurrentValue = autofillValue;
164        }
165        if (virtualBounds != null) {
166            mVirtualBounds = virtualBounds;
167        }
168
169        maybeCallOnFillReady(flags);
170    }
171
172    /**
173     * Calls {@link
174     * Listener#onFillReady(FillResponse, AutofillId, AutofillValue)} if the
175     * fill UI is ready to be displayed (i.e. when response and bounds are set).
176     */
177    void maybeCallOnFillReady(int flags) {
178        if ((mState & STATE_AUTOFILLED) != 0 && (flags & FLAG_MANUAL_REQUEST) == 0) {
179            if (sDebug) Slog.d(TAG, "Ignoring UI for " + id + " on " + getStateAsString());
180            return;
181        }
182        // First try the current response associated with this View.
183        if (mResponse != null) {
184            if (mResponse.getDatasets() != null || mResponse.getAuthentication() != null) {
185                mListener.onFillReady(mResponse, this.id, mCurrentValue);
186            }
187        }
188    }
189
190    @Override
191    public String toString() {
192        return "ViewState: [id=" + id + ", initialValue=" + mInitialValue
193                + ", currentValue=" + mCurrentValue + ", autofilledValue=" + mAutofilledValue
194                + ", bounds=" + mVirtualBounds + ", state=" + getStateAsString() + "]";
195    }
196
197    void dump(String prefix, PrintWriter pw) {
198        pw.print(prefix); pw.print("id:" ); pw.println(this.id);
199        pw.print(prefix); pw.print("state:" ); pw.println(getStateAsString());
200        pw.print(prefix); pw.print("response:");
201        if (mResponse == null) {
202            pw.println("N/A");
203        } else {
204            if (sVerbose) {
205                pw.println(mResponse);
206            } else {
207                pw.println(mResponse.getRequestId());
208            }
209        }
210        pw.print(prefix); pw.print("initialValue:" ); pw.println(mInitialValue);
211        pw.print(prefix); pw.print("currentValue:" ); pw.println(mCurrentValue);
212        pw.print(prefix); pw.print("autofilledValue:" ); pw.println(mAutofilledValue);
213        pw.print(prefix); pw.print("virtualBounds:" ); pw.println(mVirtualBounds);
214    }
215}