Home | History | Annotate | Download | only in model
      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 package com.example.android.autofill.service.model;
     17 
     18 import android.service.autofill.Dataset;
     19 import android.support.annotation.NonNull;
     20 import android.util.Log;
     21 import android.view.View;
     22 import android.view.autofill.AutofillId;
     23 import android.view.autofill.AutofillValue;
     24 
     25 import com.example.android.autofill.service.AutofillFieldMetadata;
     26 import com.example.android.autofill.service.AutofillFieldMetadataCollection;
     27 import com.example.android.autofill.service.AutofillHints;
     28 import com.example.android.autofill.service.W3cHints;
     29 import com.google.gson.annotations.Expose;
     30 
     31 import java.util.HashMap;
     32 import java.util.List;
     33 
     34 import static com.example.android.autofill.service.CommonUtil.DEBUG;
     35 import static com.example.android.autofill.service.CommonUtil.TAG;
     36 
     37 /**
     38  * FilledAutofillFieldCollection is the model that holds all of the data on a client app's page,
     39  * plus the dataset name associated with it.
     40  */
     41 public final class FilledAutofillFieldCollection {
     42     @Expose
     43     private final HashMap<String, FilledAutofillField> mHintMap;
     44     @Expose
     45     private String mDatasetName;
     46 
     47     public FilledAutofillFieldCollection() {
     48         this(null, new HashMap<String, FilledAutofillField>());
     49     }
     50 
     51     public FilledAutofillFieldCollection(String datasetName, HashMap<String, FilledAutofillField> hintMap) {
     52         mHintMap = hintMap;
     53         mDatasetName = datasetName;
     54     }
     55 
     56     private static boolean isW3cSectionPrefix(String hint) {
     57         return hint.startsWith(W3cHints.PREFIX_SECTION);
     58     }
     59 
     60     private static boolean isW3cAddressType(String hint) {
     61         switch (hint) {
     62             case W3cHints.SHIPPING:
     63             case W3cHints.BILLING:
     64                 return true;
     65         }
     66         return false;
     67     }
     68 
     69     private static boolean isW3cTypePrefix(String hint) {
     70         switch (hint) {
     71             case W3cHints.PREFIX_WORK:
     72             case W3cHints.PREFIX_FAX:
     73             case W3cHints.PREFIX_HOME:
     74             case W3cHints.PREFIX_PAGER:
     75                 return true;
     76         }
     77         return false;
     78     }
     79 
     80     private static boolean isW3cTypeHint(String hint) {
     81         switch (hint) {
     82             case W3cHints.TEL:
     83             case W3cHints.TEL_COUNTRY_CODE:
     84             case W3cHints.TEL_NATIONAL:
     85             case W3cHints.TEL_AREA_CODE:
     86             case W3cHints.TEL_LOCAL:
     87             case W3cHints.TEL_LOCAL_PREFIX:
     88             case W3cHints.TEL_LOCAL_SUFFIX:
     89             case W3cHints.TEL_EXTENSION:
     90             case W3cHints.EMAIL:
     91             case W3cHints.IMPP:
     92                 return true;
     93         }
     94         Log.w(TAG, "Invalid W3C type hint: " + hint);
     95         return false;
     96     }
     97 
     98     /**
     99      * Returns the name of the {@link Dataset}.
    100      */
    101     public String getDatasetName() {
    102         return mDatasetName;
    103     }
    104 
    105     /**
    106      * Sets the {@link Dataset} name.
    107      */
    108     public void setDatasetName(String datasetName) {
    109         mDatasetName = datasetName;
    110     }
    111 
    112     /**
    113      * Adds a {@code FilledAutofillField} to the collection, indexed by all of its hints.
    114      */
    115     public void add(@NonNull FilledAutofillField filledAutofillField) {
    116         String[] autofillHints = filledAutofillField.getAutofillHints();
    117         String nextHint = null;
    118         for (int i = 0; i < autofillHints.length; i++) {
    119             String hint = autofillHints[i];
    120             if (i < autofillHints.length - 1) {
    121                 nextHint = autofillHints[i + 1];
    122             }
    123             // First convert the compound W3C autofill hints
    124             if (isW3cSectionPrefix(hint) && i < autofillHints.length - 1) {
    125                 hint = autofillHints[++i];
    126                 if (DEBUG) Log.d(TAG, "Hint is a W3C section prefix; using " + hint + " instead");
    127                 if (i < autofillHints.length - 1) {
    128                     nextHint = autofillHints[i + 1];
    129                 }
    130             }
    131             if (isW3cTypePrefix(hint) && nextHint != null && isW3cTypeHint(nextHint)) {
    132                 hint = nextHint;
    133                 i++;
    134                 if (DEBUG) Log.d(TAG, "Hint is a W3C type prefix; using " + hint + " instead");
    135             }
    136             if (isW3cAddressType(hint) && nextHint != null) {
    137                 hint = nextHint;
    138                 i++;
    139                 if (DEBUG) Log.d(TAG, "Hint is a W3C address prefix; using " + hint + " instead");
    140             }
    141 
    142             // Then check if the "actual" hint is supported.
    143 
    144 
    145             if (AutofillHints.isValidHint(hint)) {
    146                 mHintMap.put(hint, filledAutofillField);
    147             } else {
    148                 Log.e(TAG, "Invalid hint: " + autofillHints[i]);
    149             }
    150         }
    151     }
    152 
    153     /**
    154      * Populates a {@link Dataset.Builder} with appropriate values for each {@link AutofillId}
    155      * in a {@code AutofillFieldMetadataCollection}.
    156      *
    157      * In other words, it constructs an autofill
    158      * {@link Dataset.Builder} by applying saved values (from this {@code FilledAutofillFieldCollection})
    159      * to Views specified in a {@code AutofillFieldMetadataCollection}, which represents the current
    160      * page the user is on.
    161      */
    162     public boolean applyToFields(AutofillFieldMetadataCollection autofillFieldMetadataCollection,
    163             Dataset.Builder datasetBuilder) {
    164         boolean setValueAtLeastOnce = false;
    165         List<String> allHints = autofillFieldMetadataCollection.getAllHints();
    166         for (int hintIndex = 0; hintIndex < allHints.size(); hintIndex++) {
    167             String hint = allHints.get(hintIndex);
    168             List<AutofillFieldMetadata> fillableAutofillFields =
    169                     autofillFieldMetadataCollection.getFieldsForHint(hint);
    170             if (fillableAutofillFields == null) {
    171                 continue;
    172             }
    173             for (int autofillFieldIndex = 0; autofillFieldIndex < fillableAutofillFields.size(); autofillFieldIndex++) {
    174                 FilledAutofillField filledAutofillField = mHintMap.get(hint);
    175                 if (filledAutofillField == null) {
    176                     continue;
    177                 }
    178                 AutofillFieldMetadata autofillFieldMetadata = fillableAutofillFields.get(autofillFieldIndex);
    179                 AutofillId autofillId = autofillFieldMetadata.getId();
    180                 int autofillType = autofillFieldMetadata.getAutofillType();
    181                 switch (autofillType) {
    182                     case View.AUTOFILL_TYPE_LIST:
    183                         int listValue = autofillFieldMetadata.getAutofillOptionIndex(filledAutofillField.getTextValue());
    184                         if (listValue != -1) {
    185                             datasetBuilder.setValue(autofillId, AutofillValue.forList(listValue));
    186                             setValueAtLeastOnce = true;
    187                         }
    188                         break;
    189                     case View.AUTOFILL_TYPE_DATE:
    190                         Long dateValue = filledAutofillField.getDateValue();
    191                         if (dateValue != null) {
    192                             datasetBuilder.setValue(autofillId, AutofillValue.forDate(dateValue));
    193                             setValueAtLeastOnce = true;
    194                         }
    195                         break;
    196                     case View.AUTOFILL_TYPE_TEXT:
    197                         String textValue = filledAutofillField.getTextValue();
    198                         if (textValue != null) {
    199                             datasetBuilder.setValue(autofillId, AutofillValue.forText(textValue));
    200                             setValueAtLeastOnce = true;
    201                         }
    202                         break;
    203                     case View.AUTOFILL_TYPE_TOGGLE:
    204                         Boolean toggleValue = filledAutofillField.getToggleValue();
    205                         if (toggleValue != null) {
    206                             datasetBuilder.setValue(autofillId, AutofillValue.forToggle(toggleValue));
    207                             setValueAtLeastOnce = true;
    208                         }
    209                         break;
    210                     case View.AUTOFILL_TYPE_NONE:
    211                     default:
    212                         Log.w(TAG, "Invalid autofill type - " + autofillType);
    213                         break;
    214                 }
    215             }
    216         }
    217         return setValueAtLeastOnce;
    218     }
    219 
    220     /**
    221      * Takes in a list of autofill hints (autofillHints), usually associated with a View or set of
    222      * Views. Returns whether any of the filled fields on the page have at least 1 of these
    223      * autofillHints.
    224      */
    225     public boolean helpsWithHints(List<String> autofillHints) {
    226         for (int i = 0; i < autofillHints.size(); i++) {
    227             String autofillHint = autofillHints.get(i);
    228             if (mHintMap.containsKey(autofillHint) && !mHintMap.get(autofillHint).isNull()) {
    229                 return true;
    230             }
    231         }
    232         return false;
    233     }
    234 }
    235