Felipe Leme | 6d55387 | 2016-12-08 17:13:25 -0800 | [diff] [blame] | 1 | /* |
| 2 | * Copyright (C) 2016 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 | */ |
Felipe Leme | b3c3537 | 2017-04-03 10:20:34 -0700 | [diff] [blame] | 16 | |
Svet Ganov | 782043c | 2017-02-11 00:52:02 +0000 | [diff] [blame] | 17 | package android.service.autofill; |
Felipe Leme | 6d55387 | 2016-12-08 17:13:25 -0800 | [diff] [blame] | 18 | |
Felipe Leme | 2596089 | 2018-03-21 12:56:05 -0700 | [diff] [blame] | 19 | import static android.service.autofill.AutofillServiceHelper.assertValid; |
Philip P. Moltmann | c761963 | 2017-04-25 11:57:37 -0700 | [diff] [blame] | 20 | import static android.service.autofill.FillRequest.INVALID_REQUEST_ID; |
Felipe Leme | 9f9ee25 | 2017-04-27 13:56:22 -0700 | [diff] [blame] | 21 | import static android.view.autofill.Helper.sDebug; |
Felipe Leme | d633f07 | 2017-02-14 10:17:17 -0800 | [diff] [blame] | 22 | |
Felipe Leme | 59c4464 | 2017-10-13 11:05:05 -0700 | [diff] [blame] | 23 | import android.annotation.IntDef; |
Svet Ganov | 0f4928f | 2017-02-02 20:02:51 -0800 | [diff] [blame] | 24 | import android.annotation.NonNull; |
Felipe Leme | 436ab6a | 2016-12-15 11:56:15 -0800 | [diff] [blame] | 25 | import android.annotation.Nullable; |
Felipe Leme | 24d7173 | 2017-10-20 10:32:57 -0700 | [diff] [blame] | 26 | import android.annotation.TestApi; |
Felipe Leme | 2ef19c1 | 2017-06-05 11:32:32 -0700 | [diff] [blame] | 27 | import android.app.Activity; |
Svet Ganov | 0f4928f | 2017-02-02 20:02:51 -0800 | [diff] [blame] | 28 | import android.content.IntentSender; |
Felipe Leme | cb96f39 | 2017-07-25 15:19:30 -0700 | [diff] [blame] | 29 | import android.content.pm.ParceledListSlice; |
Felipe Leme | 6d55387 | 2016-12-08 17:13:25 -0800 | [diff] [blame] | 30 | import android.os.Bundle; |
| 31 | import android.os.Parcel; |
| 32 | import android.os.Parcelable; |
Felipe Leme | b3c3537 | 2017-04-03 10:20:34 -0700 | [diff] [blame] | 33 | import android.view.autofill.AutofillId; |
Svet Ganov | 00c771dc | 2017-02-19 00:06:22 -0800 | [diff] [blame] | 34 | import android.widget.RemoteViews; |
Felipe Leme | 6d55387 | 2016-12-08 17:13:25 -0800 | [diff] [blame] | 35 | |
Felipe Leme | 17292d1 | 2017-10-24 14:03:10 -0700 | [diff] [blame] | 36 | import com.android.internal.util.Preconditions; |
| 37 | |
Felipe Leme | 59c4464 | 2017-10-13 11:05:05 -0700 | [diff] [blame] | 38 | import java.lang.annotation.Retention; |
| 39 | import java.lang.annotation.RetentionPolicy; |
Felipe Leme | fe35e69 | 2017-02-21 13:44:50 -0800 | [diff] [blame] | 40 | import java.util.ArrayList; |
Felipe Leme | c2430f3c4d6 | 2017-05-01 09:35:33 -0700 | [diff] [blame] | 41 | import java.util.Arrays; |
Felipe Leme | cb96f39 | 2017-07-25 15:19:30 -0700 | [diff] [blame] | 42 | import java.util.List; |
Felipe Leme | fe35e69 | 2017-02-21 13:44:50 -0800 | [diff] [blame] | 43 | |
Felipe Leme | 6d55387 | 2016-12-08 17:13:25 -0800 | [diff] [blame] | 44 | /** |
Felipe Leme | 78172e7 | 2017-12-08 17:01:15 -0800 | [diff] [blame] | 45 | * Response for an {@link |
Felipe Leme | e5f9c30 | 2017-04-18 17:48:49 -0700 | [diff] [blame] | 46 | * AutofillService#onFillRequest(FillRequest, android.os.CancellationSignal, FillCallback)}. |
Felipe Leme | 6d55387 | 2016-12-08 17:13:25 -0800 | [diff] [blame] | 47 | * |
Felipe Leme | 2ef19c1 | 2017-06-05 11:32:32 -0700 | [diff] [blame] | 48 | * <p>See the main {@link AutofillService} documentation for more details and examples. |
Felipe Leme | 6d55387 | 2016-12-08 17:13:25 -0800 | [diff] [blame] | 49 | */ |
| 50 | public final class FillResponse implements Parcelable { |
Svet Ganov | 782043c | 2017-02-11 00:52:02 +0000 | [diff] [blame] | 51 | |
Felipe Leme | 59c4464 | 2017-10-13 11:05:05 -0700 | [diff] [blame] | 52 | /** |
Felipe Leme | d37f53e | 2017-12-07 10:41:10 -0800 | [diff] [blame] | 53 | * Flag used to generate {@link FillEventHistory.Event events} of type |
| 54 | * {@link FillEventHistory.Event#TYPE_CONTEXT_COMMITTED}—if this flag is not passed to |
| 55 | * {@link Builder#setFlags(int)}, these events are not generated. |
Felipe Leme | 59c4464 | 2017-10-13 11:05:05 -0700 | [diff] [blame] | 56 | */ |
| 57 | public static final int FLAG_TRACK_CONTEXT_COMMITED = 0x1; |
| 58 | |
Felipe Leme | 17292d1 | 2017-10-24 14:03:10 -0700 | [diff] [blame] | 59 | /** |
Felipe Leme | d37f53e | 2017-12-07 10:41:10 -0800 | [diff] [blame] | 60 | * Flag used to change the behavior of {@link FillResponse.Builder#disableAutofill(long)}— |
| 61 | * when this flag is passed to {@link Builder#setFlags(int)}, autofill is disabled only for the |
| 62 | * activiy that generated the {@link FillRequest}, not the whole app. |
Felipe Leme | 17292d1 | 2017-10-24 14:03:10 -0700 | [diff] [blame] | 63 | */ |
| 64 | public static final int FLAG_DISABLE_ACTIVITY_ONLY = 0x2; |
| 65 | |
Felipe Leme | 59c4464 | 2017-10-13 11:05:05 -0700 | [diff] [blame] | 66 | /** @hide */ |
Jeff Sharkey | ce8db99 | 2017-12-13 20:05:05 -0700 | [diff] [blame] | 67 | @IntDef(flag = true, prefix = { "FLAG_" }, value = { |
Felipe Leme | 17292d1 | 2017-10-24 14:03:10 -0700 | [diff] [blame] | 68 | FLAG_TRACK_CONTEXT_COMMITED, |
| 69 | FLAG_DISABLE_ACTIVITY_ONLY |
Felipe Leme | 59c4464 | 2017-10-13 11:05:05 -0700 | [diff] [blame] | 70 | }) |
| 71 | @Retention(RetentionPolicy.SOURCE) |
| 72 | @interface FillResponseFlags {} |
| 73 | |
Felipe Leme | cb96f39 | 2017-07-25 15:19:30 -0700 | [diff] [blame] | 74 | private final @Nullable ParceledListSlice<Dataset> mDatasets; |
Svet Ganov | 013efe1 | 2017-04-13 21:56:16 -0700 | [diff] [blame] | 75 | private final @Nullable SaveInfo mSaveInfo; |
| 76 | private final @Nullable Bundle mClientState; |
| 77 | private final @Nullable RemoteViews mPresentation; |
Felipe Leme | e932478 | 2017-11-21 18:00:56 -0800 | [diff] [blame] | 78 | private final @Nullable RemoteViews mHeader; |
| 79 | private final @Nullable RemoteViews mFooter; |
Svet Ganov | 013efe1 | 2017-04-13 21:56:16 -0700 | [diff] [blame] | 80 | private final @Nullable IntentSender mAuthentication; |
| 81 | private final @Nullable AutofillId[] mAuthenticationIds; |
Felipe Leme | 01ded10 | 2017-04-20 15:58:30 -0700 | [diff] [blame] | 82 | private final @Nullable AutofillId[] mIgnoredIds; |
Felipe Leme | 17292d1 | 2017-10-24 14:03:10 -0700 | [diff] [blame] | 83 | private final long mDisableDuration; |
Felipe Leme | 452886a | 2017-11-27 13:09:13 -0800 | [diff] [blame] | 84 | private final @Nullable AutofillId[] mFieldClassificationIds; |
Felipe Leme | 59c4464 | 2017-10-13 11:05:05 -0700 | [diff] [blame] | 85 | private final int mFlags; |
Philip P. Moltmann | c761963 | 2017-04-25 11:57:37 -0700 | [diff] [blame] | 86 | private int mRequestId; |
Adam He | 2cc3146 | 2018-11-12 16:26:14 -0800 | [diff] [blame] | 87 | private final @Nullable UserData mUserData; |
Felipe Leme | 6d55387 | 2016-12-08 17:13:25 -0800 | [diff] [blame] | 88 | |
Svet Ganov | 0f4928f | 2017-02-02 20:02:51 -0800 | [diff] [blame] | 89 | private FillResponse(@NonNull Builder builder) { |
Felipe Leme | cb96f39 | 2017-07-25 15:19:30 -0700 | [diff] [blame] | 90 | mDatasets = (builder.mDatasets != null) ? new ParceledListSlice<>(builder.mDatasets) : null; |
Felipe Leme | 6ee1ed4 | 2017-03-07 19:07:21 -0800 | [diff] [blame] | 91 | mSaveInfo = builder.mSaveInfo; |
Felipe Leme | 6f12e67 | 2017-10-20 13:04:19 -0700 | [diff] [blame] | 92 | mClientState = builder.mClientState; |
Svet Ganov | 00c771dc | 2017-02-19 00:06:22 -0800 | [diff] [blame] | 93 | mPresentation = builder.mPresentation; |
Felipe Leme | e932478 | 2017-11-21 18:00:56 -0800 | [diff] [blame] | 94 | mHeader = builder.mHeader; |
| 95 | mFooter = builder.mFooter; |
Svet Ganov | 0f4928f | 2017-02-02 20:02:51 -0800 | [diff] [blame] | 96 | mAuthentication = builder.mAuthentication; |
Felipe Leme | b3c3537 | 2017-04-03 10:20:34 -0700 | [diff] [blame] | 97 | mAuthenticationIds = builder.mAuthenticationIds; |
Felipe Leme | 01ded10 | 2017-04-20 15:58:30 -0700 | [diff] [blame] | 98 | mIgnoredIds = builder.mIgnoredIds; |
Felipe Leme | 17292d1 | 2017-10-24 14:03:10 -0700 | [diff] [blame] | 99 | mDisableDuration = builder.mDisableDuration; |
Felipe Leme | 452886a | 2017-11-27 13:09:13 -0800 | [diff] [blame] | 100 | mFieldClassificationIds = builder.mFieldClassificationIds; |
Felipe Leme | 59c4464 | 2017-10-13 11:05:05 -0700 | [diff] [blame] | 101 | mFlags = builder.mFlags; |
Philip P. Moltmann | c761963 | 2017-04-25 11:57:37 -0700 | [diff] [blame] | 102 | mRequestId = INVALID_REQUEST_ID; |
Adam He | 2cc3146 | 2018-11-12 16:26:14 -0800 | [diff] [blame] | 103 | mUserData = builder.mUserData; |
Felipe Leme | 6d55387 | 2016-12-08 17:13:25 -0800 | [diff] [blame] | 104 | } |
| 105 | |
| 106 | /** @hide */ |
Svet Ganov | 013efe1 | 2017-04-13 21:56:16 -0700 | [diff] [blame] | 107 | public @Nullable Bundle getClientState() { |
| 108 | return mClientState; |
Felipe Leme | 6d55387 | 2016-12-08 17:13:25 -0800 | [diff] [blame] | 109 | } |
| 110 | |
Felipe Leme | 436ab6a | 2016-12-15 11:56:15 -0800 | [diff] [blame] | 111 | /** @hide */ |
Felipe Leme | cb96f39 | 2017-07-25 15:19:30 -0700 | [diff] [blame] | 112 | public @Nullable List<Dataset> getDatasets() { |
| 113 | return (mDatasets != null) ? mDatasets.getList() : null; |
Felipe Leme | 436ab6a | 2016-12-15 11:56:15 -0800 | [diff] [blame] | 114 | } |
| 115 | |
| 116 | /** @hide */ |
Felipe Leme | f69761f | 2017-02-23 17:52:01 -0800 | [diff] [blame] | 117 | public @Nullable SaveInfo getSaveInfo() { |
| 118 | return mSaveInfo; |
Felipe Leme | 436ab6a | 2016-12-15 11:56:15 -0800 | [diff] [blame] | 119 | } |
| 120 | |
| 121 | /** @hide */ |
Svet Ganov | 00c771dc | 2017-02-19 00:06:22 -0800 | [diff] [blame] | 122 | public @Nullable RemoteViews getPresentation() { |
| 123 | return mPresentation; |
| 124 | } |
| 125 | |
| 126 | /** @hide */ |
Felipe Leme | e932478 | 2017-11-21 18:00:56 -0800 | [diff] [blame] | 127 | public @Nullable RemoteViews getHeader() { |
| 128 | return mHeader; |
| 129 | } |
| 130 | |
| 131 | /** @hide */ |
| 132 | public @Nullable RemoteViews getFooter() { |
| 133 | return mFooter; |
| 134 | } |
| 135 | |
| 136 | /** @hide */ |
Svet Ganov | 0f4928f | 2017-02-02 20:02:51 -0800 | [diff] [blame] | 137 | public @Nullable IntentSender getAuthentication() { |
| 138 | return mAuthentication; |
Felipe Leme | 436ab6a | 2016-12-15 11:56:15 -0800 | [diff] [blame] | 139 | } |
| 140 | |
Felipe Leme | b3c3537 | 2017-04-03 10:20:34 -0700 | [diff] [blame] | 141 | /** @hide */ |
| 142 | public @Nullable AutofillId[] getAuthenticationIds() { |
| 143 | return mAuthenticationIds; |
| 144 | } |
| 145 | |
Felipe Leme | 01ded10 | 2017-04-20 15:58:30 -0700 | [diff] [blame] | 146 | /** @hide */ |
| 147 | public @Nullable AutofillId[] getIgnoredIds() { |
| 148 | return mIgnoredIds; |
| 149 | } |
| 150 | |
Felipe Leme | 59c4464 | 2017-10-13 11:05:05 -0700 | [diff] [blame] | 151 | /** @hide */ |
Felipe Leme | 17292d1 | 2017-10-24 14:03:10 -0700 | [diff] [blame] | 152 | public long getDisableDuration() { |
| 153 | return mDisableDuration; |
| 154 | } |
| 155 | |
| 156 | /** @hide */ |
Felipe Leme | 452886a | 2017-11-27 13:09:13 -0800 | [diff] [blame] | 157 | public @Nullable AutofillId[] getFieldClassificationIds() { |
| 158 | return mFieldClassificationIds; |
Felipe Leme | 24d7173 | 2017-10-20 10:32:57 -0700 | [diff] [blame] | 159 | } |
| 160 | |
| 161 | /** @hide */ |
Adam He | 2cc3146 | 2018-11-12 16:26:14 -0800 | [diff] [blame] | 162 | public @Nullable UserData getUserData() { |
| 163 | return mUserData; |
| 164 | } |
| 165 | |
| 166 | /** @hide */ |
Felipe Leme | 78172e7 | 2017-12-08 17:01:15 -0800 | [diff] [blame] | 167 | @TestApi |
Felipe Leme | 59c4464 | 2017-10-13 11:05:05 -0700 | [diff] [blame] | 168 | public int getFlags() { |
| 169 | return mFlags; |
| 170 | } |
| 171 | |
Felipe Leme | 6d55387 | 2016-12-08 17:13:25 -0800 | [diff] [blame] | 172 | /** |
Philip P. Moltmann | c761963 | 2017-04-25 11:57:37 -0700 | [diff] [blame] | 173 | * Associates a {@link FillResponse} to a request. |
| 174 | * |
| 175 | * <p>Set inside of the {@link FillCallback} code, not the {@link AutofillService}. |
| 176 | * |
| 177 | * @param requestId The id of the request to associate the response to. |
| 178 | * |
| 179 | * @hide |
| 180 | */ |
| 181 | public void setRequestId(int requestId) { |
| 182 | mRequestId = requestId; |
| 183 | } |
| 184 | |
| 185 | /** @hide */ |
| 186 | public int getRequestId() { |
| 187 | return mRequestId; |
| 188 | } |
| 189 | |
| 190 | /** |
Svet Ganov | 0f4928f | 2017-02-02 20:02:51 -0800 | [diff] [blame] | 191 | * Builder for {@link FillResponse} objects. You must to provide at least |
Svet Ganov | 00c771dc | 2017-02-19 00:06:22 -0800 | [diff] [blame] | 192 | * one dataset or set an authentication intent with a presentation view. |
Felipe Leme | 6d55387 | 2016-12-08 17:13:25 -0800 | [diff] [blame] | 193 | */ |
| 194 | public static final class Builder { |
Felipe Leme | fe35e69 | 2017-02-21 13:44:50 -0800 | [diff] [blame] | 195 | private ArrayList<Dataset> mDatasets; |
Felipe Leme | f69761f | 2017-02-23 17:52:01 -0800 | [diff] [blame] | 196 | private SaveInfo mSaveInfo; |
Felipe Leme | 6f12e67 | 2017-10-20 13:04:19 -0700 | [diff] [blame] | 197 | private Bundle mClientState; |
Svet Ganov | 00c771dc | 2017-02-19 00:06:22 -0800 | [diff] [blame] | 198 | private RemoteViews mPresentation; |
Felipe Leme | e932478 | 2017-11-21 18:00:56 -0800 | [diff] [blame] | 199 | private RemoteViews mHeader; |
| 200 | private RemoteViews mFooter; |
Svet Ganov | 0f4928f | 2017-02-02 20:02:51 -0800 | [diff] [blame] | 201 | private IntentSender mAuthentication; |
Felipe Leme | b3c3537 | 2017-04-03 10:20:34 -0700 | [diff] [blame] | 202 | private AutofillId[] mAuthenticationIds; |
Felipe Leme | 01ded10 | 2017-04-20 15:58:30 -0700 | [diff] [blame] | 203 | private AutofillId[] mIgnoredIds; |
Felipe Leme | 17292d1 | 2017-10-24 14:03:10 -0700 | [diff] [blame] | 204 | private long mDisableDuration; |
Felipe Leme | 452886a | 2017-11-27 13:09:13 -0800 | [diff] [blame] | 205 | private AutofillId[] mFieldClassificationIds; |
Felipe Leme | 59c4464 | 2017-10-13 11:05:05 -0700 | [diff] [blame] | 206 | private int mFlags; |
Svet Ganov | 0f4928f | 2017-02-02 20:02:51 -0800 | [diff] [blame] | 207 | private boolean mDestroyed; |
Adam He | 2cc3146 | 2018-11-12 16:26:14 -0800 | [diff] [blame] | 208 | private UserData mUserData; |
Felipe Leme | 436ab6a | 2016-12-15 11:56:15 -0800 | [diff] [blame] | 209 | |
Felipe Leme | 436ab6a | 2016-12-15 11:56:15 -0800 | [diff] [blame] | 210 | /** |
Felipe Leme | 601d220 | 2017-11-17 08:21:23 -0800 | [diff] [blame] | 211 | * Triggers a custom UI before before autofilling the screen with any data set in this |
| 212 | * response. |
| 213 | * |
| 214 | * <p><b>Note:</b> Although the name of this method suggests that it should be used just for |
| 215 | * authentication flow, it can be used for other advanced flows; see {@link AutofillService} |
| 216 | * for examples. |
Svet Ganov | 0f4928f | 2017-02-02 20:02:51 -0800 | [diff] [blame] | 217 | * |
Svet Ganov | 782043c | 2017-02-11 00:52:02 +0000 | [diff] [blame] | 218 | * <p>This is typically useful when a user interaction is required to unlock their |
| 219 | * data vault if you encrypt the data set labels and data set data. It is recommended |
| 220 | * to encrypt only the sensitive data and not the data set labels which would allow |
| 221 | * auth on the data set level leading to a better user experience. Note that if you |
| 222 | * use sensitive data as a label, for example an email address, then it should also |
| 223 | * be encrypted. The provided {@link android.app.PendingIntent intent} must be an |
Felipe Leme | 2ef19c1 | 2017-06-05 11:32:32 -0700 | [diff] [blame] | 224 | * {@link Activity} which implements your authentication flow. Also if you provide an auth |
Svet Ganov | 00c771dc | 2017-02-19 00:06:22 -0800 | [diff] [blame] | 225 | * intent you also need to specify the presentation view to be shown in the fill UI |
Felipe Leme | b3c3537 | 2017-04-03 10:20:34 -0700 | [diff] [blame] | 226 | * for the user to trigger your authentication flow. |
Svet Ganov | 0f4928f | 2017-02-02 20:02:51 -0800 | [diff] [blame] | 227 | * |
Felipe Leme | 640f30a | 2017-03-06 15:44:06 -0800 | [diff] [blame] | 228 | * <p>When a user triggers autofill, the system launches the provided intent |
Felipe Leme | cb96f39 | 2017-07-25 15:19:30 -0700 | [diff] [blame] | 229 | * whose extras will have the |
| 230 | * {@link android.view.autofill.AutofillManager#EXTRA_ASSIST_STRUCTURE screen |
Svetoslav Ganov | a9379d0 | 2017-05-09 17:40:24 -0700 | [diff] [blame] | 231 | * content} and your {@link android.view.autofill.AutofillManager#EXTRA_CLIENT_STATE |
Felipe Leme | 2ef19c1 | 2017-06-05 11:32:32 -0700 | [diff] [blame] | 232 | * client state}. Once you complete your authentication flow you should set the |
Felipe Leme | b0a4057 | 2017-09-05 13:11:37 -0700 | [diff] [blame] | 233 | * {@link Activity} result to {@link android.app.Activity#RESULT_OK} and set the |
| 234 | * {@link android.view.autofill.AutofillManager#EXTRA_AUTHENTICATION_RESULT} extra |
| 235 | * with the fully populated {@link FillResponse response} (or {@code null} if the screen |
| 236 | * cannot be autofilled). |
| 237 | * |
| 238 | * <p>For example, if you provided an empty {@link FillResponse response} because the |
Svet Ganov | 782043c | 2017-02-11 00:52:02 +0000 | [diff] [blame] | 239 | * user's data was locked and marked that the response needs an authentication then |
| 240 | * in the response returned if authentication succeeds you need to provide all |
| 241 | * available data sets some of which may need to be further authenticated, for |
Felipe Leme | b3c3537 | 2017-04-03 10:20:34 -0700 | [diff] [blame] | 242 | * example a credit card whose CVV needs to be entered. |
Svet Ganov | 0f4928f | 2017-02-02 20:02:51 -0800 | [diff] [blame] | 243 | * |
Svet Ganov | eb49515 | 2017-02-21 17:47:07 -0800 | [diff] [blame] | 244 | * <p>If you provide an authentication intent you must also provide a presentation |
| 245 | * which is used to visualize visualize the response for triggering the authentication |
Felipe Leme | b3c3537 | 2017-04-03 10:20:34 -0700 | [diff] [blame] | 246 | * flow. |
Svet Ganov | eb49515 | 2017-02-21 17:47:07 -0800 | [diff] [blame] | 247 | * |
Felipe Leme | 17292d1 | 2017-10-24 14:03:10 -0700 | [diff] [blame] | 248 | * <p><b>Note:</b> Do not make the provided pending intent |
Svet Ganov | 0f4928f | 2017-02-02 20:02:51 -0800 | [diff] [blame] | 249 | * immutable by using {@link android.app.PendingIntent#FLAG_IMMUTABLE} as the |
Felipe Leme | b3c3537 | 2017-04-03 10:20:34 -0700 | [diff] [blame] | 250 | * platform needs to fill in the authentication arguments. |
Svet Ganov | 0f4928f | 2017-02-02 20:02:51 -0800 | [diff] [blame] | 251 | * |
Dake Gu | 36b86c2 | 2018-04-16 12:49:30 -0700 | [diff] [blame] | 252 | * <p>Theme does not work with RemoteViews layout. Avoid hardcoded text color |
| 253 | * or background color: Autofill on different platforms may have different themes. |
| 254 | * |
Svet Ganov | 782043c | 2017-02-11 00:52:02 +0000 | [diff] [blame] | 255 | * @param authentication Intent to an activity with your authentication flow. |
Svet Ganov | eb49515 | 2017-02-21 17:47:07 -0800 | [diff] [blame] | 256 | * @param presentation The presentation to visualize the response. |
Felipe Leme | 2c88842 | 2017-10-26 12:46:35 -0700 | [diff] [blame] | 257 | * @param ids id of Views that when focused will display the authentication UI. |
Svet Ganov | 0f4928f | 2017-02-02 20:02:51 -0800 | [diff] [blame] | 258 | * |
Felipe Leme | b3c3537 | 2017-04-03 10:20:34 -0700 | [diff] [blame] | 259 | * @return This builder. |
Felipe Leme | 2596089 | 2018-03-21 12:56:05 -0700 | [diff] [blame] | 260 | * |
| 261 | * @throws IllegalArgumentException if any of the following occurs: |
| 262 | * <ul> |
| 263 | * <li>{@code ids} is {@code null}</li> |
| 264 | * <li>{@code ids} is empty</li> |
| 265 | * <li>{@code ids} contains a {@code null} element</li> |
| 266 | * <li>both {@code authentication} and {@code presentation} are {@code null}</li> |
| 267 | * <li>both {@code authentication} and {@code presentation} are non-{@code null}</li> |
| 268 | * </ul> |
Felipe Leme | c2430f3c4d6 | 2017-05-01 09:35:33 -0700 | [diff] [blame] | 269 | * |
Felipe Leme | e932478 | 2017-11-21 18:00:56 -0800 | [diff] [blame] | 270 | * @throws IllegalStateException if a {@link #setHeader(RemoteViews) header} or a |
| 271 | * {@link #setFooter(RemoteViews) footer} are already set for this builder. |
| 272 | * |
Svet Ganov | 0f4928f | 2017-02-02 20:02:51 -0800 | [diff] [blame] | 273 | * @see android.app.PendingIntent#getIntentSender() |
| 274 | */ |
Felipe Leme | 8e156a6 | 2019-03-01 17:16:40 -0800 | [diff] [blame] | 275 | @NonNull |
| 276 | public Builder setAuthentication(@NonNull AutofillId[] ids, |
Felipe Leme | b3c3537 | 2017-04-03 10:20:34 -0700 | [diff] [blame] | 277 | @Nullable IntentSender authentication, @Nullable RemoteViews presentation) { |
Svet Ganov | 0f4928f | 2017-02-02 20:02:51 -0800 | [diff] [blame] | 278 | throwIfDestroyed(); |
Felipe Leme | 17292d1 | 2017-10-24 14:03:10 -0700 | [diff] [blame] | 279 | throwIfDisableAutofillCalled(); |
Felipe Leme | e932478 | 2017-11-21 18:00:56 -0800 | [diff] [blame] | 280 | if (mHeader != null || mFooter != null) { |
| 281 | throw new IllegalStateException("Already called #setHeader() or #setFooter()"); |
| 282 | } |
| 283 | |
Svet Ganov | eb49515 | 2017-02-21 17:47:07 -0800 | [diff] [blame] | 284 | if (authentication == null ^ presentation == null) { |
| 285 | throw new IllegalArgumentException("authentication and presentation" |
| 286 | + " must be both non-null or null"); |
| 287 | } |
Svet Ganov | 0f4928f | 2017-02-02 20:02:51 -0800 | [diff] [blame] | 288 | mAuthentication = authentication; |
Svet Ganov | eb49515 | 2017-02-21 17:47:07 -0800 | [diff] [blame] | 289 | mPresentation = presentation; |
Felipe Leme | 2596089 | 2018-03-21 12:56:05 -0700 | [diff] [blame] | 290 | mAuthenticationIds = assertValid(ids); |
Felipe Leme | 436ab6a | 2016-12-15 11:56:15 -0800 | [diff] [blame] | 291 | return this; |
| 292 | } |
Felipe Leme | 6d55387 | 2016-12-08 17:13:25 -0800 | [diff] [blame] | 293 | |
| 294 | /** |
Felipe Leme | e5f9c30 | 2017-04-18 17:48:49 -0700 | [diff] [blame] | 295 | * Specifies views that should not trigger new |
| 296 | * {@link AutofillService#onFillRequest(FillRequest, android.os.CancellationSignal, |
| 297 | * FillCallback)} requests. |
| 298 | * |
Felipe Leme | 2ef19c1 | 2017-06-05 11:32:32 -0700 | [diff] [blame] | 299 | * <p>This is typically used when the service cannot autofill the view; for example, a |
| 300 | * text field representing the result of a Captcha challenge. |
Felipe Leme | e5f9c30 | 2017-04-18 17:48:49 -0700 | [diff] [blame] | 301 | */ |
Felipe Leme | 8e156a6 | 2019-03-01 17:16:40 -0800 | [diff] [blame] | 302 | @NonNull |
Felipe Leme | e5f9c30 | 2017-04-18 17:48:49 -0700 | [diff] [blame] | 303 | public Builder setIgnoredIds(AutofillId...ids) { |
Felipe Leme | 17292d1 | 2017-10-24 14:03:10 -0700 | [diff] [blame] | 304 | throwIfDestroyed(); |
Felipe Leme | 01ded10 | 2017-04-20 15:58:30 -0700 | [diff] [blame] | 305 | mIgnoredIds = ids; |
Felipe Leme | e5f9c30 | 2017-04-18 17:48:49 -0700 | [diff] [blame] | 306 | return this; |
| 307 | } |
| 308 | |
| 309 | /** |
Svet Ganov | 00c771dc | 2017-02-19 00:06:22 -0800 | [diff] [blame] | 310 | * Adds a new {@link Dataset} to this response. |
Felipe Leme | 6d55387 | 2016-12-08 17:13:25 -0800 | [diff] [blame] | 311 | * |
Felipe Leme | cb96f39 | 2017-07-25 15:19:30 -0700 | [diff] [blame] | 312 | * <p><b>Note: </b> on Android {@link android.os.Build.VERSION_CODES#O}, the total number of |
| 313 | * datasets is limited by the Binder transaction size, so it's recommended to keep it |
| 314 | * small (in the range of 10-20 at most) and use pagination by adding a fake |
Felipe Leme | c0c6ab4 | 2017-07-28 09:32:42 -0700 | [diff] [blame] | 315 | * {@link Dataset.Builder#setAuthentication(IntentSender) authenticated dataset} at the end |
| 316 | * with a presentation string like "Next 10" that would return a new {@link FillResponse} |
| 317 | * with the next 10 datasets, and so on. This limitation was lifted on |
Felipe Leme | cb96f39 | 2017-07-25 15:19:30 -0700 | [diff] [blame] | 318 | * Android {@link android.os.Build.VERSION_CODES#O_MR1}, although the Binder transaction |
| 319 | * size can still be reached if each dataset itself is too big. |
| 320 | * |
Svet Ganov | 00c771dc | 2017-02-19 00:06:22 -0800 | [diff] [blame] | 321 | * @return This builder. |
Felipe Leme | 6d55387 | 2016-12-08 17:13:25 -0800 | [diff] [blame] | 322 | */ |
Felipe Leme | 8e156a6 | 2019-03-01 17:16:40 -0800 | [diff] [blame] | 323 | @NonNull |
| 324 | public Builder addDataset(@Nullable Dataset dataset) { |
Svet Ganov | 0f4928f | 2017-02-02 20:02:51 -0800 | [diff] [blame] | 325 | throwIfDestroyed(); |
Felipe Leme | 17292d1 | 2017-10-24 14:03:10 -0700 | [diff] [blame] | 326 | throwIfDisableAutofillCalled(); |
Svet Ganov | 0f4928f | 2017-02-02 20:02:51 -0800 | [diff] [blame] | 327 | if (dataset == null) { |
| 328 | return this; |
| 329 | } |
| 330 | if (mDatasets == null) { |
Felipe Leme | fe35e69 | 2017-02-21 13:44:50 -0800 | [diff] [blame] | 331 | mDatasets = new ArrayList<>(); |
Svet Ganov | 0f4928f | 2017-02-02 20:02:51 -0800 | [diff] [blame] | 332 | } |
Svet Ganov | 0f4928f | 2017-02-02 20:02:51 -0800 | [diff] [blame] | 333 | if (!mDatasets.add(dataset)) { |
| 334 | return this; |
| 335 | } |
Felipe Leme | f69761f | 2017-02-23 17:52:01 -0800 | [diff] [blame] | 336 | return this; |
| 337 | } |
| 338 | |
Felipe Leme | 6d55387 | 2016-12-08 17:13:25 -0800 | [diff] [blame] | 339 | /** |
Felipe Leme | f69761f | 2017-02-23 17:52:01 -0800 | [diff] [blame] | 340 | * Sets the {@link SaveInfo} associated with this response. |
Felipe Leme | 6d55387 | 2016-12-08 17:13:25 -0800 | [diff] [blame] | 341 | * |
Svet Ganov | 00c771dc | 2017-02-19 00:06:22 -0800 | [diff] [blame] | 342 | * @return This builder. |
Felipe Leme | 6d55387 | 2016-12-08 17:13:25 -0800 | [diff] [blame] | 343 | */ |
Felipe Leme | f69761f | 2017-02-23 17:52:01 -0800 | [diff] [blame] | 344 | public @NonNull Builder setSaveInfo(@NonNull SaveInfo saveInfo) { |
Svet Ganov | 0f4928f | 2017-02-02 20:02:51 -0800 | [diff] [blame] | 345 | throwIfDestroyed(); |
Felipe Leme | 17292d1 | 2017-10-24 14:03:10 -0700 | [diff] [blame] | 346 | throwIfDisableAutofillCalled(); |
Felipe Leme | f69761f | 2017-02-23 17:52:01 -0800 | [diff] [blame] | 347 | mSaveInfo = saveInfo; |
Felipe Leme | 6d55387 | 2016-12-08 17:13:25 -0800 | [diff] [blame] | 348 | return this; |
| 349 | } |
| 350 | |
Felipe Leme | e5f9c30 | 2017-04-18 17:48:49 -0700 | [diff] [blame] | 351 | /** |
Felipe Leme | d37f53e | 2017-12-07 10:41:10 -0800 | [diff] [blame] | 352 | * Sets a bundle with state that is passed to subsequent APIs that manipulate this response. |
| 353 | * |
| 354 | * <p>You can use this bundle to store intermediate state that is passed to subsequent calls |
| 355 | * to {@link AutofillService#onFillRequest(FillRequest, android.os.CancellationSignal, |
| 356 | * FillCallback)} and {@link AutofillService#onSaveRequest(SaveRequest, SaveCallback)}, and |
| 357 | * you can also retrieve it by calling {@link FillEventHistory.Event#getClientState()}. |
Svet Ganov | 00c771dc | 2017-02-19 00:06:22 -0800 | [diff] [blame] | 358 | * |
Felipe Leme | 52d5d3d | 2017-04-04 13:10:58 -0700 | [diff] [blame] | 359 | * <p>If this method is called on multiple {@link FillResponse} objects for the same |
Felipe Leme | 2ef19c1 | 2017-06-05 11:32:32 -0700 | [diff] [blame] | 360 | * screen, just the latest bundle is passed back to the service. |
Felipe Leme | 52d5d3d | 2017-04-04 13:10:58 -0700 | [diff] [blame] | 361 | * |
Svet Ganov | 013efe1 | 2017-04-13 21:56:16 -0700 | [diff] [blame] | 362 | * @param clientState The custom client state. |
Svet Ganov | 00c771dc | 2017-02-19 00:06:22 -0800 | [diff] [blame] | 363 | * @return This builder. |
Felipe Leme | 6d55387 | 2016-12-08 17:13:25 -0800 | [diff] [blame] | 364 | */ |
Felipe Leme | 8e156a6 | 2019-03-01 17:16:40 -0800 | [diff] [blame] | 365 | @NonNull |
Svet Ganov | 013efe1 | 2017-04-13 21:56:16 -0700 | [diff] [blame] | 366 | public Builder setClientState(@Nullable Bundle clientState) { |
Svet Ganov | 0f4928f | 2017-02-02 20:02:51 -0800 | [diff] [blame] | 367 | throwIfDestroyed(); |
Felipe Leme | b57080e | 2017-11-20 17:10:41 -0800 | [diff] [blame] | 368 | throwIfDisableAutofillCalled(); |
Felipe Leme | 6f12e67 | 2017-10-20 13:04:19 -0700 | [diff] [blame] | 369 | mClientState = clientState; |
Felipe Leme | 6d55387 | 2016-12-08 17:13:25 -0800 | [diff] [blame] | 370 | return this; |
| 371 | } |
| 372 | |
| 373 | /** |
Felipe Leme | 78172e7 | 2017-12-08 17:01:15 -0800 | [diff] [blame] | 374 | * Sets which fields are used for |
| 375 | * <a href="AutofillService.html#FieldClassification">field classification</a> |
Felipe Leme | 452886a | 2017-11-27 13:09:13 -0800 | [diff] [blame] | 376 | * |
Felipe Leme | 78172e7 | 2017-12-08 17:01:15 -0800 | [diff] [blame] | 377 | * <p><b>Note:</b> This method automatically adds the |
| 378 | * {@link FillResponse#FLAG_TRACK_CONTEXT_COMMITED} to the {@link #setFlags(int) flags}. |
| 379 | |
Felipe Leme | 452886a | 2017-11-27 13:09:13 -0800 | [diff] [blame] | 380 | * @throws IllegalArgumentException is length of {@code ids} args is more than |
| 381 | * {@link UserData#getMaxFieldClassificationIdsSize()}. |
| 382 | * @throws IllegalStateException if {@link #build()} or {@link #disableAutofill(long)} was |
| 383 | * already called. |
| 384 | * @throws NullPointerException if {@code ids} or any element on it is {@code null}. |
Felipe Leme | 24d7173 | 2017-10-20 10:32:57 -0700 | [diff] [blame] | 385 | */ |
Felipe Leme | 8e156a6 | 2019-03-01 17:16:40 -0800 | [diff] [blame] | 386 | @NonNull |
Felipe Leme | 452886a | 2017-11-27 13:09:13 -0800 | [diff] [blame] | 387 | public Builder setFieldClassificationIds(@NonNull AutofillId... ids) { |
Felipe Leme | 24d7173 | 2017-10-20 10:32:57 -0700 | [diff] [blame] | 388 | throwIfDestroyed(); |
| 389 | throwIfDisableAutofillCalled(); |
Felipe Leme | 452886a | 2017-11-27 13:09:13 -0800 | [diff] [blame] | 390 | Preconditions.checkArrayElementsNotNull(ids, "ids"); |
| 391 | Preconditions.checkArgumentInRange(ids.length, 1, |
| 392 | UserData.getMaxFieldClassificationIdsSize(), "ids length"); |
| 393 | mFieldClassificationIds = ids; |
Felipe Leme | 78172e7 | 2017-12-08 17:01:15 -0800 | [diff] [blame] | 394 | mFlags |= FLAG_TRACK_CONTEXT_COMMITED; |
Felipe Leme | 24d7173 | 2017-10-20 10:32:57 -0700 | [diff] [blame] | 395 | return this; |
| 396 | } |
| 397 | |
| 398 | /** |
Felipe Leme | 59c4464 | 2017-10-13 11:05:05 -0700 | [diff] [blame] | 399 | * Sets flags changing the response behavior. |
| 400 | * |
Felipe Leme | 17292d1 | 2017-10-24 14:03:10 -0700 | [diff] [blame] | 401 | * @param flags a combination of {@link #FLAG_TRACK_CONTEXT_COMMITED} and |
| 402 | * {@link #FLAG_DISABLE_ACTIVITY_ONLY}, or {@code 0}. |
Felipe Leme | 59c4464 | 2017-10-13 11:05:05 -0700 | [diff] [blame] | 403 | * |
| 404 | * @return This builder. |
| 405 | */ |
Felipe Leme | 8e156a6 | 2019-03-01 17:16:40 -0800 | [diff] [blame] | 406 | @NonNull |
Felipe Leme | 59c4464 | 2017-10-13 11:05:05 -0700 | [diff] [blame] | 407 | public Builder setFlags(@FillResponseFlags int flags) { |
| 408 | throwIfDestroyed(); |
Felipe Leme | 17292d1 | 2017-10-24 14:03:10 -0700 | [diff] [blame] | 409 | mFlags = Preconditions.checkFlagsArgument(flags, |
| 410 | FLAG_TRACK_CONTEXT_COMMITED | FLAG_DISABLE_ACTIVITY_ONLY); |
| 411 | return this; |
| 412 | } |
| 413 | |
| 414 | /** |
| 415 | * Disables autofill for the app or activity. |
| 416 | * |
| 417 | * <p>This method is useful to optimize performance in cases where the service knows it |
| 418 | * can not autofill an app—for example, when the service has a list of "blacklisted" |
| 419 | * apps such as office suites. |
| 420 | * |
| 421 | * <p>By default, it disables autofill for all activities in the app, unless the response is |
| 422 | * {@link #setFlags(int) flagged} with {@link #FLAG_DISABLE_ACTIVITY_ONLY}. |
| 423 | * |
| 424 | * <p>Autofill for the app or activity is automatically re-enabled after any of the |
| 425 | * following conditions: |
| 426 | * |
| 427 | * <ol> |
| 428 | * <li>{@code duration} milliseconds have passed. |
| 429 | * <li>The autofill service for the user has changed. |
| 430 | * <li>The device has rebooted. |
| 431 | * </ol> |
| 432 | * |
| 433 | * <p><b>Note:</b> Activities that are running when autofill is re-enabled remain |
| 434 | * disabled for autofill until they finish and restart. |
| 435 | * |
| 436 | * @param duration duration to disable autofill, in milliseconds. |
| 437 | * |
| 438 | * @return this builder |
| 439 | * |
| 440 | * @throws IllegalArgumentException if {@code duration} is not a positive number. |
| 441 | * @throws IllegalStateException if either {@link #addDataset(Dataset)}, |
Felipe Leme | b57080e | 2017-11-20 17:10:41 -0800 | [diff] [blame] | 442 | * {@link #setAuthentication(AutofillId[], IntentSender, RemoteViews)}, |
Felipe Leme | 452886a | 2017-11-27 13:09:13 -0800 | [diff] [blame] | 443 | * {@link #setSaveInfo(SaveInfo)}, {@link #setClientState(Bundle)}, or |
Felipe Leme | 78172e7 | 2017-12-08 17:01:15 -0800 | [diff] [blame] | 444 | * {@link #setFieldClassificationIds(AutofillId...)} was already called. |
Felipe Leme | 17292d1 | 2017-10-24 14:03:10 -0700 | [diff] [blame] | 445 | */ |
Felipe Leme | 8e156a6 | 2019-03-01 17:16:40 -0800 | [diff] [blame] | 446 | @NonNull |
Felipe Leme | 17292d1 | 2017-10-24 14:03:10 -0700 | [diff] [blame] | 447 | public Builder disableAutofill(long duration) { |
| 448 | throwIfDestroyed(); |
| 449 | if (duration <= 0) { |
| 450 | throw new IllegalArgumentException("duration must be greater than 0"); |
| 451 | } |
Felipe Leme | 24d7173 | 2017-10-20 10:32:57 -0700 | [diff] [blame] | 452 | if (mAuthentication != null || mDatasets != null || mSaveInfo != null |
Felipe Leme | 452886a | 2017-11-27 13:09:13 -0800 | [diff] [blame] | 453 | || mFieldClassificationIds != null || mClientState != null) { |
Felipe Leme | 17292d1 | 2017-10-24 14:03:10 -0700 | [diff] [blame] | 454 | throw new IllegalStateException("disableAutofill() must be the only method called"); |
| 455 | } |
| 456 | |
| 457 | mDisableDuration = duration; |
Felipe Leme | 59c4464 | 2017-10-13 11:05:05 -0700 | [diff] [blame] | 458 | return this; |
| 459 | } |
| 460 | |
| 461 | /** |
Felipe Leme | e932478 | 2017-11-21 18:00:56 -0800 | [diff] [blame] | 462 | * Sets a header to be shown as the first element in the list of datasets. |
| 463 | * |
| 464 | * <p>When this method is called, you must also {@link #addDataset(Dataset) add a dataset}, |
| 465 | * otherwise {@link #build()} throws an {@link IllegalStateException}. Similarly, this |
| 466 | * method should only be used on {@link FillResponse FillResponses} that do not require |
| 467 | * authentication (as the header could have been set directly in the main presentation in |
| 468 | * these cases). |
| 469 | * |
Dake Gu | 36b86c2 | 2018-04-16 12:49:30 -0700 | [diff] [blame] | 470 | * <p>Theme does not work with RemoteViews layout. Avoid hardcoded text color |
| 471 | * or background color: Autofill on different platforms may have different themes. |
| 472 | * |
Felipe Leme | e932478 | 2017-11-21 18:00:56 -0800 | [diff] [blame] | 473 | * @param header a presentation to represent the header. This presentation is not clickable |
| 474 | * —calling |
| 475 | * {@link RemoteViews#setOnClickPendingIntent(int, android.app.PendingIntent)} on it would |
| 476 | * have no effect. |
| 477 | * |
| 478 | * @return this builder |
| 479 | * |
| 480 | * @throws IllegalStateException if an |
| 481 | * {@link #setAuthentication(AutofillId[], IntentSender, RemoteViews) authentication} was |
| 482 | * already set for this builder. |
| 483 | */ |
| 484 | // TODO(b/69796626): make it sticky / update javadoc |
Felipe Leme | 8e156a6 | 2019-03-01 17:16:40 -0800 | [diff] [blame] | 485 | @NonNull |
Felipe Leme | e932478 | 2017-11-21 18:00:56 -0800 | [diff] [blame] | 486 | public Builder setHeader(@NonNull RemoteViews header) { |
| 487 | throwIfDestroyed(); |
| 488 | throwIfAuthenticationCalled(); |
| 489 | mHeader = Preconditions.checkNotNull(header); |
| 490 | return this; |
| 491 | } |
| 492 | |
| 493 | /** |
| 494 | * Sets a footer to be shown as the last element in the list of datasets. |
| 495 | * |
| 496 | * <p>When this method is called, you must also {@link #addDataset(Dataset) add a dataset}, |
| 497 | * otherwise {@link #build()} throws an {@link IllegalStateException}. Similarly, this |
| 498 | * method should only be used on {@link FillResponse FillResponses} that do not require |
| 499 | * authentication (as the footer could have been set directly in the main presentation in |
| 500 | * these cases). |
| 501 | * |
Dake Gu | 36b86c2 | 2018-04-16 12:49:30 -0700 | [diff] [blame] | 502 | * <p>Theme does not work with RemoteViews layout. Avoid hardcoded text color |
| 503 | * or background color: Autofill on different platforms may have different themes. |
| 504 | * |
Felipe Leme | e932478 | 2017-11-21 18:00:56 -0800 | [diff] [blame] | 505 | * @param footer a presentation to represent the footer. This presentation is not clickable |
| 506 | * —calling |
| 507 | * {@link RemoteViews#setOnClickPendingIntent(int, android.app.PendingIntent)} on it would |
| 508 | * have no effect. |
| 509 | * |
| 510 | * @return this builder |
| 511 | * |
| 512 | * @throws IllegalStateException if the FillResponse |
| 513 | * {@link #setAuthentication(AutofillId[], IntentSender, RemoteViews) |
| 514 | * requires authentication}. |
| 515 | */ |
| 516 | // TODO(b/69796626): make it sticky / update javadoc |
Felipe Leme | 8e156a6 | 2019-03-01 17:16:40 -0800 | [diff] [blame] | 517 | @NonNull |
Felipe Leme | e932478 | 2017-11-21 18:00:56 -0800 | [diff] [blame] | 518 | public Builder setFooter(@NonNull RemoteViews footer) { |
| 519 | throwIfDestroyed(); |
| 520 | throwIfAuthenticationCalled(); |
| 521 | mFooter = Preconditions.checkNotNull(footer); |
| 522 | return this; |
| 523 | } |
| 524 | |
| 525 | /** |
Adam He | 2cc3146 | 2018-11-12 16:26:14 -0800 | [diff] [blame] | 526 | * Sets a specific {@link UserData} for field classification for this request only. |
| 527 | * |
Adam He | 1cb6f80 | 2018-12-10 15:15:49 -0800 | [diff] [blame] | 528 | * <p>Any fields in this UserData will override corresponding fields in the generic |
| 529 | * UserData object |
| 530 | * |
Adam He | 2cc3146 | 2018-11-12 16:26:14 -0800 | [diff] [blame] | 531 | * @return this builder |
| 532 | * @throws IllegalStateException if the FillResponse |
| 533 | * {@link #setAuthentication(AutofillId[], IntentSender, RemoteViews) |
| 534 | * requires authentication}. |
| 535 | */ |
Felipe Leme | 8e156a6 | 2019-03-01 17:16:40 -0800 | [diff] [blame] | 536 | @NonNull |
Adam He | 2cc3146 | 2018-11-12 16:26:14 -0800 | [diff] [blame] | 537 | public Builder setUserData(@NonNull UserData userData) { |
| 538 | throwIfDestroyed(); |
| 539 | throwIfAuthenticationCalled(); |
| 540 | mUserData = Preconditions.checkNotNull(userData); |
| 541 | return this; |
| 542 | } |
| 543 | |
| 544 | /** |
Felipe Leme | 2ef19c1 | 2017-06-05 11:32:32 -0700 | [diff] [blame] | 545 | * Builds a new {@link FillResponse} instance. |
| 546 | * |
Felipe Leme | 17292d1 | 2017-10-24 14:03:10 -0700 | [diff] [blame] | 547 | * @throws IllegalStateException if any of the following conditions occur: |
| 548 | * <ol> |
| 549 | * <li>{@link #build()} was already called. |
| 550 | * <li>No call was made to {@link #addDataset(Dataset)}, |
| 551 | * {@link #setAuthentication(AutofillId[], IntentSender, RemoteViews)}, |
Felipe Leme | b57080e | 2017-11-20 17:10:41 -0800 | [diff] [blame] | 552 | * {@link #setSaveInfo(SaveInfo)}, {@link #disableAutofill(long)}, |
Felipe Leme | 452886a | 2017-11-27 13:09:13 -0800 | [diff] [blame] | 553 | * {@link #setClientState(Bundle)}, |
Felipe Leme | 78172e7 | 2017-12-08 17:01:15 -0800 | [diff] [blame] | 554 | * or {@link #setFieldClassificationIds(AutofillId...)}. |
Felipe Leme | e932478 | 2017-11-21 18:00:56 -0800 | [diff] [blame] | 555 | * <li>{@link #setHeader(RemoteViews)} or {@link #setFooter(RemoteViews)} is called |
| 556 | * without any previous calls to {@link #addDataset(Dataset)}. |
Felipe Leme | 17292d1 | 2017-10-24 14:03:10 -0700 | [diff] [blame] | 557 | * </ol> |
Svet Ganov | 00c771dc | 2017-02-19 00:06:22 -0800 | [diff] [blame] | 558 | * |
| 559 | * @return A built response. |
Felipe Leme | 6d55387 | 2016-12-08 17:13:25 -0800 | [diff] [blame] | 560 | */ |
Felipe Leme | 8e156a6 | 2019-03-01 17:16:40 -0800 | [diff] [blame] | 561 | @NonNull |
Felipe Leme | 6d55387 | 2016-12-08 17:13:25 -0800 | [diff] [blame] | 562 | public FillResponse build() { |
Svet Ganov | 0f4928f | 2017-02-02 20:02:51 -0800 | [diff] [blame] | 563 | throwIfDestroyed(); |
Felipe Leme | 17292d1 | 2017-10-24 14:03:10 -0700 | [diff] [blame] | 564 | if (mAuthentication == null && mDatasets == null && mSaveInfo == null |
Felipe Leme | 452886a | 2017-11-27 13:09:13 -0800 | [diff] [blame] | 565 | && mDisableDuration == 0 && mFieldClassificationIds == null |
| 566 | && mClientState == null) { |
Felipe Leme | 24d7173 | 2017-10-20 10:32:57 -0700 | [diff] [blame] | 567 | throw new IllegalStateException("need to provide: at least one DataSet, or a " |
| 568 | + "SaveInfo, or an authentication with a presentation, " |
Felipe Leme | b57080e | 2017-11-20 17:10:41 -0800 | [diff] [blame] | 569 | + "or a FieldsDetection, or a client state, or disable autofill"); |
Svet Ganov | 00c771dc | 2017-02-19 00:06:22 -0800 | [diff] [blame] | 570 | } |
Felipe Leme | e932478 | 2017-11-21 18:00:56 -0800 | [diff] [blame] | 571 | if (mDatasets == null && (mHeader != null || mFooter != null)) { |
| 572 | throw new IllegalStateException( |
| 573 | "must add at least 1 dataset when using header or footer"); |
| 574 | } |
Svet Ganov | 0f4928f | 2017-02-02 20:02:51 -0800 | [diff] [blame] | 575 | mDestroyed = true; |
Felipe Leme | 6d55387 | 2016-12-08 17:13:25 -0800 | [diff] [blame] | 576 | return new FillResponse(this); |
| 577 | } |
Svet Ganov | 0f4928f | 2017-02-02 20:02:51 -0800 | [diff] [blame] | 578 | |
| 579 | private void throwIfDestroyed() { |
| 580 | if (mDestroyed) { |
| 581 | throw new IllegalStateException("Already called #build()"); |
| 582 | } |
| 583 | } |
Felipe Leme | 17292d1 | 2017-10-24 14:03:10 -0700 | [diff] [blame] | 584 | |
| 585 | private void throwIfDisableAutofillCalled() { |
| 586 | if (mDisableDuration > 0) { |
| 587 | throw new IllegalStateException("Already called #disableAutofill()"); |
| 588 | } |
| 589 | } |
Felipe Leme | e932478 | 2017-11-21 18:00:56 -0800 | [diff] [blame] | 590 | |
| 591 | private void throwIfAuthenticationCalled() { |
| 592 | if (mAuthentication != null) { |
| 593 | throw new IllegalStateException("Already called #setAuthentication()"); |
| 594 | } |
| 595 | } |
Felipe Leme | 6d55387 | 2016-12-08 17:13:25 -0800 | [diff] [blame] | 596 | } |
| 597 | |
| 598 | ///////////////////////////////////// |
Felipe Leme | b3c3537 | 2017-04-03 10:20:34 -0700 | [diff] [blame] | 599 | // Object "contract" methods. // |
Felipe Leme | 6d55387 | 2016-12-08 17:13:25 -0800 | [diff] [blame] | 600 | ///////////////////////////////////// |
| 601 | @Override |
| 602 | public String toString() { |
Felipe Leme | 9f9ee25 | 2017-04-27 13:56:22 -0700 | [diff] [blame] | 603 | if (!sDebug) return super.toString(); |
Felipe Leme | f69761f | 2017-02-23 17:52:01 -0800 | [diff] [blame] | 604 | |
Felipe Leme | c2430f3c4d6 | 2017-05-01 09:35:33 -0700 | [diff] [blame] | 605 | // TODO: create a dump() method instead |
Felipe Leme | da9ea34 | 2018-03-22 15:19:51 -0700 | [diff] [blame] | 606 | final StringBuilder builder = new StringBuilder( |
| 607 | "FillResponse : [mRequestId=" + mRequestId); |
| 608 | if (mDatasets != null) { |
| 609 | builder.append(", datasets=").append(mDatasets.getList()); |
| 610 | } |
| 611 | if (mSaveInfo != null) { |
| 612 | builder.append(", saveInfo=").append(mSaveInfo); |
| 613 | } |
| 614 | if (mClientState != null) { |
| 615 | builder.append(", hasClientState"); |
| 616 | } |
| 617 | if (mPresentation != null) { |
| 618 | builder.append(", hasPresentation"); |
| 619 | } |
| 620 | if (mHeader != null) { |
| 621 | builder.append(", hasHeader"); |
| 622 | } |
| 623 | if (mFooter != null) { |
| 624 | builder.append(", hasFooter"); |
| 625 | } |
| 626 | if (mAuthentication != null) { |
| 627 | builder.append(", hasAuthentication"); |
| 628 | } |
| 629 | if (mAuthenticationIds != null) { |
| 630 | builder.append(", authenticationIds=").append(Arrays.toString(mAuthenticationIds)); |
| 631 | } |
| 632 | builder.append(", disableDuration=").append(mDisableDuration); |
| 633 | if (mFlags != 0) { |
| 634 | builder.append(", flags=").append(mFlags); |
| 635 | } |
| 636 | if (mFieldClassificationIds != null) { |
| 637 | builder.append(Arrays.toString(mFieldClassificationIds)); |
| 638 | } |
Adam He | 2cc3146 | 2018-11-12 16:26:14 -0800 | [diff] [blame] | 639 | if (mUserData != null) { |
| 640 | builder.append(", userData=").append(mUserData); |
| 641 | } |
Felipe Leme | da9ea34 | 2018-03-22 15:19:51 -0700 | [diff] [blame] | 642 | return builder.append("]").toString(); |
Felipe Leme | 6d55387 | 2016-12-08 17:13:25 -0800 | [diff] [blame] | 643 | } |
| 644 | |
| 645 | ///////////////////////////////////// |
Felipe Leme | b3c3537 | 2017-04-03 10:20:34 -0700 | [diff] [blame] | 646 | // Parcelable "contract" methods. // |
Felipe Leme | 6d55387 | 2016-12-08 17:13:25 -0800 | [diff] [blame] | 647 | ///////////////////////////////////// |
| 648 | |
| 649 | @Override |
| 650 | public int describeContents() { |
| 651 | return 0; |
| 652 | } |
| 653 | |
| 654 | @Override |
| 655 | public void writeToParcel(Parcel parcel, int flags) { |
Felipe Leme | cb96f39 | 2017-07-25 15:19:30 -0700 | [diff] [blame] | 656 | parcel.writeParcelable(mDatasets, flags); |
Felipe Leme | f69761f | 2017-02-23 17:52:01 -0800 | [diff] [blame] | 657 | parcel.writeParcelable(mSaveInfo, flags); |
Svet Ganov | 013efe1 | 2017-04-13 21:56:16 -0700 | [diff] [blame] | 658 | parcel.writeParcelable(mClientState, flags); |
Felipe Leme | b3c3537 | 2017-04-03 10:20:34 -0700 | [diff] [blame] | 659 | parcel.writeParcelableArray(mAuthenticationIds, flags); |
Svet Ganov | 00c771dc | 2017-02-19 00:06:22 -0800 | [diff] [blame] | 660 | parcel.writeParcelable(mAuthentication, flags); |
Svet Ganov | eb49515 | 2017-02-21 17:47:07 -0800 | [diff] [blame] | 661 | parcel.writeParcelable(mPresentation, flags); |
Felipe Leme | e932478 | 2017-11-21 18:00:56 -0800 | [diff] [blame] | 662 | parcel.writeParcelable(mHeader, flags); |
| 663 | parcel.writeParcelable(mFooter, flags); |
Adam He | 2cc3146 | 2018-11-12 16:26:14 -0800 | [diff] [blame] | 664 | parcel.writeParcelable(mUserData, flags); |
Felipe Leme | 01ded10 | 2017-04-20 15:58:30 -0700 | [diff] [blame] | 665 | parcel.writeParcelableArray(mIgnoredIds, flags); |
Felipe Leme | 17292d1 | 2017-10-24 14:03:10 -0700 | [diff] [blame] | 666 | parcel.writeLong(mDisableDuration); |
Felipe Leme | 452886a | 2017-11-27 13:09:13 -0800 | [diff] [blame] | 667 | parcel.writeParcelableArray(mFieldClassificationIds, flags); |
Felipe Leme | 59c4464 | 2017-10-13 11:05:05 -0700 | [diff] [blame] | 668 | parcel.writeInt(mFlags); |
Philip P. Moltmann | c761963 | 2017-04-25 11:57:37 -0700 | [diff] [blame] | 669 | parcel.writeInt(mRequestId); |
Felipe Leme | 6d55387 | 2016-12-08 17:13:25 -0800 | [diff] [blame] | 670 | } |
| 671 | |
Jeff Sharkey | 9e8f83d | 2019-02-28 12:06:45 -0700 | [diff] [blame] | 672 | public static final @android.annotation.NonNull Parcelable.Creator<FillResponse> CREATOR = |
Felipe Leme | 6d55387 | 2016-12-08 17:13:25 -0800 | [diff] [blame] | 673 | new Parcelable.Creator<FillResponse>() { |
| 674 | @Override |
Svet Ganov | 0f4928f | 2017-02-02 20:02:51 -0800 | [diff] [blame] | 675 | public FillResponse createFromParcel(Parcel parcel) { |
| 676 | // Always go through the builder to ensure the data ingested by |
| 677 | // the system obeys the contract of the builder to avoid attacks |
| 678 | // using specially crafted parcels. |
Svet Ganov | 782043c | 2017-02-11 00:52:02 +0000 | [diff] [blame] | 679 | final Builder builder = new Builder(); |
Felipe Leme | cb96f39 | 2017-07-25 15:19:30 -0700 | [diff] [blame] | 680 | final ParceledListSlice<Dataset> datasetSlice = parcel.readParcelable(null); |
| 681 | final List<Dataset> datasets = (datasetSlice != null) ? datasetSlice.getList() : null; |
Svet Ganov | 0f4928f | 2017-02-02 20:02:51 -0800 | [diff] [blame] | 682 | final int datasetCount = (datasets != null) ? datasets.size() : 0; |
| 683 | for (int i = 0; i < datasetCount; i++) { |
Felipe Leme | fe35e69 | 2017-02-21 13:44:50 -0800 | [diff] [blame] | 684 | builder.addDataset(datasets.get(i)); |
Svet Ganov | 0f4928f | 2017-02-02 20:02:51 -0800 | [diff] [blame] | 685 | } |
Felipe Leme | f69761f | 2017-02-23 17:52:01 -0800 | [diff] [blame] | 686 | builder.setSaveInfo(parcel.readParcelable(null)); |
Felipe Leme | 01ded10 | 2017-04-20 15:58:30 -0700 | [diff] [blame] | 687 | builder.setClientState(parcel.readParcelable(null)); |
Felipe Leme | c2430f3c4d6 | 2017-05-01 09:35:33 -0700 | [diff] [blame] | 688 | |
| 689 | // Sets authentication state. |
| 690 | final AutofillId[] authenticationIds = parcel.readParcelableArray(null, |
| 691 | AutofillId.class); |
| 692 | final IntentSender authentication = parcel.readParcelable(null); |
| 693 | final RemoteViews presentation = parcel.readParcelable(null); |
| 694 | if (authenticationIds != null) { |
| 695 | builder.setAuthentication(authenticationIds, authentication, presentation); |
| 696 | } |
Felipe Leme | e932478 | 2017-11-21 18:00:56 -0800 | [diff] [blame] | 697 | final RemoteViews header = parcel.readParcelable(null); |
| 698 | if (header != null) { |
| 699 | builder.setHeader(header); |
| 700 | } |
| 701 | final RemoteViews footer = parcel.readParcelable(null); |
| 702 | if (footer != null) { |
| 703 | builder.setFooter(footer); |
| 704 | } |
Adam He | 2cc3146 | 2018-11-12 16:26:14 -0800 | [diff] [blame] | 705 | final UserData userData = parcel.readParcelable(null); |
| 706 | if (userData != null) { |
| 707 | builder.setUserData(userData); |
| 708 | } |
Felipe Leme | c2430f3c4d6 | 2017-05-01 09:35:33 -0700 | [diff] [blame] | 709 | |
Felipe Leme | 01ded10 | 2017-04-20 15:58:30 -0700 | [diff] [blame] | 710 | builder.setIgnoredIds(parcel.readParcelableArray(null, AutofillId.class)); |
Felipe Leme | 17292d1 | 2017-10-24 14:03:10 -0700 | [diff] [blame] | 711 | final long disableDuration = parcel.readLong(); |
| 712 | if (disableDuration > 0) { |
| 713 | builder.disableAutofill(disableDuration); |
| 714 | } |
Felipe Leme | 452886a | 2017-11-27 13:09:13 -0800 | [diff] [blame] | 715 | final AutofillId[] fieldClassifactionIds = |
| 716 | parcel.readParcelableArray(null, AutofillId.class); |
| 717 | if (fieldClassifactionIds != null) { |
| 718 | builder.setFieldClassificationIds(fieldClassifactionIds); |
Felipe Leme | 24d7173 | 2017-10-20 10:32:57 -0700 | [diff] [blame] | 719 | } |
Felipe Leme | 59c4464 | 2017-10-13 11:05:05 -0700 | [diff] [blame] | 720 | builder.setFlags(parcel.readInt()); |
Philip P. Moltmann | c761963 | 2017-04-25 11:57:37 -0700 | [diff] [blame] | 721 | |
Felipe Leme | 59c4464 | 2017-10-13 11:05:05 -0700 | [diff] [blame] | 722 | final FillResponse response = builder.build(); |
Philip P. Moltmann | c761963 | 2017-04-25 11:57:37 -0700 | [diff] [blame] | 723 | response.setRequestId(parcel.readInt()); |
| 724 | |
| 725 | return response; |
Felipe Leme | 6d55387 | 2016-12-08 17:13:25 -0800 | [diff] [blame] | 726 | } |
| 727 | |
| 728 | @Override |
| 729 | public FillResponse[] newArray(int size) { |
| 730 | return new FillResponse[size]; |
| 731 | } |
| 732 | }; |
| 733 | } |