Home | History | Annotate | Download | only in service
      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;
     17 
     18 import android.support.annotation.NonNull;
     19 
     20 import com.example.android.autofill.service.model.FakeData;
     21 import com.example.android.autofill.service.model.FieldType;
     22 import com.example.android.autofill.service.model.FieldTypeWithHeuristics;
     23 import com.example.android.autofill.service.model.FilledAutofillField;
     24 
     25 import java.util.ArrayList;
     26 import java.util.Calendar;
     27 import java.util.HashMap;
     28 import java.util.List;
     29 import java.util.Objects;
     30 
     31 import static com.example.android.autofill.service.util.Util.logd;
     32 import static com.example.android.autofill.service.util.Util.logw;
     33 import static java.util.stream.Collectors.toList;
     34 
     35 public final class AutofillHints {
     36     public static final int PARTITION_ALL = -1;
     37     public static final int PARTITION_OTHER = 0;
     38     public static final int PARTITION_ADDRESS = 1;
     39     public static final int PARTITION_EMAIL = 2;
     40     public static final int PARTITION_CREDIT_CARD = 3;
     41     public static final int[] PARTITIONS = {
     42             PARTITION_OTHER, PARTITION_ADDRESS, PARTITION_EMAIL, PARTITION_CREDIT_CARD
     43     };
     44 
     45     private AutofillHints() {
     46     }
     47 
     48     public static FilledAutofillField generateFakeField(
     49             FieldTypeWithHeuristics fieldTypeWithHeuristics, String packageName, int seed,
     50             String datasetId) {
     51         FakeData fakeData = fieldTypeWithHeuristics.fieldType.getFakeData();
     52         String fieldTypeName = fieldTypeWithHeuristics.fieldType.getTypeName();
     53         String text = null;
     54         Long date = null;
     55         Boolean toggle = null;
     56         if (fakeData.strictExampleSet != null && fakeData.strictExampleSet.strings != null &&
     57                 fakeData.strictExampleSet.strings.size() > 0 &&
     58                 !fakeData.strictExampleSet.strings.get(0).isEmpty()) {
     59             List<String> choices = fakeData.strictExampleSet.strings;
     60             text = choices.get(seed % choices.size());
     61         } else if (fakeData.textTemplate != null) {
     62             text = fakeData.textTemplate.replace("seed", "" + seed)
     63                     .replace("curr_time", "" + Calendar.getInstance().getTimeInMillis());
     64         } else if (fakeData.dateTemplate != null) {
     65             if (fakeData.dateTemplate.contains("curr_time")) {
     66                 date = Calendar.getInstance().getTimeInMillis();
     67             }
     68         }
     69         return new FilledAutofillField(datasetId, fieldTypeName, text, date, toggle);
     70     }
     71 
     72     public static String getFieldTypeNameFromAutofillHints(
     73             HashMap<String, FieldTypeWithHeuristics> fieldTypesByAutofillHint,
     74             @NonNull List<String> hints) {
     75         return getFieldTypeNameFromAutofillHints(fieldTypesByAutofillHint, hints, PARTITION_ALL);
     76     }
     77 
     78     public static String getFieldTypeNameFromAutofillHints(
     79             HashMap<String, FieldTypeWithHeuristics> fieldTypesByAutofillHint,
     80             @NonNull List<String> hints, int partition) {
     81         List<String> fieldTypeNames = removePrefixes(hints)
     82                 .stream()
     83                 .filter(fieldTypesByAutofillHint::containsKey)
     84                 .map(fieldTypesByAutofillHint::get)
     85                 .filter(Objects::nonNull)
     86                 .filter((fieldTypeWithHints) ->
     87                         matchesPartition(fieldTypeWithHints.fieldType.getPartition(), partition))
     88                 .map(FieldTypeWithHeuristics::getFieldType).map(FieldType::getTypeName)
     89                 .collect(toList());
     90         if (fieldTypeNames != null && fieldTypeNames.size() > 0) {
     91             return fieldTypeNames.get(0);
     92         } else {
     93             return null;
     94         }
     95     }
     96 
     97     public static boolean matchesPartition(int partition, int otherPartition) {
     98         return partition == PARTITION_ALL || otherPartition == PARTITION_ALL ||
     99                 partition == otherPartition;
    100     }
    101 
    102     private static List<String> removePrefixes(@NonNull List<String> hints) {
    103         List<String> hintsWithoutPrefixes = new ArrayList<>();
    104         String nextHint = null;
    105         for (int i = 0; i < hints.size(); i++) {
    106             String hint = hints.get(i);
    107             if (i < hints.size() - 1) {
    108                 nextHint = hints.get(i + 1);
    109             }
    110             // First convert the compound W3C autofill hints
    111             if (isW3cSectionPrefix(hint) && i < hints.size() - 1) {
    112                 i++;
    113                 hint = hints.get(i);
    114                 logd("Hint is a W3C section prefix; using %s instead", hint);
    115                 if (i < hints.size() - 1) {
    116                     nextHint = hints.get(i + 1);
    117                 }
    118             }
    119             if (isW3cTypePrefix(hint) && nextHint != null && isW3cTypeHint(nextHint)) {
    120                 hint = nextHint;
    121                 i++;
    122                 logd("Hint is a W3C type prefix; using %s instead", hint);
    123             }
    124             if (isW3cAddressType(hint) && nextHint != null) {
    125                 hint = nextHint;
    126                 i++;
    127                 logd("Hint is a W3C address prefix; using %s instead", hint);
    128             }
    129             hintsWithoutPrefixes.add(hint);
    130         }
    131         return hintsWithoutPrefixes;
    132     }
    133 
    134     private static boolean isW3cSectionPrefix(@NonNull String hint) {
    135         return hint.startsWith(W3cHints.PREFIX_SECTION);
    136     }
    137 
    138     private static boolean isW3cAddressType(@NonNull String hint) {
    139         switch (hint) {
    140             case W3cHints.SHIPPING:
    141             case W3cHints.BILLING:
    142                 return true;
    143         }
    144         return false;
    145     }
    146 
    147     private static boolean isW3cTypePrefix(@NonNull String hint) {
    148         switch (hint) {
    149             case W3cHints.PREFIX_WORK:
    150             case W3cHints.PREFIX_FAX:
    151             case W3cHints.PREFIX_HOME:
    152             case W3cHints.PREFIX_PAGER:
    153                 return true;
    154         }
    155         return false;
    156     }
    157 
    158     private static boolean isW3cTypeHint(@NonNull String hint) {
    159         switch (hint) {
    160             case W3cHints.TEL:
    161             case W3cHints.TEL_COUNTRY_CODE:
    162             case W3cHints.TEL_NATIONAL:
    163             case W3cHints.TEL_AREA_CODE:
    164             case W3cHints.TEL_LOCAL:
    165             case W3cHints.TEL_LOCAL_PREFIX:
    166             case W3cHints.TEL_LOCAL_SUFFIX:
    167             case W3cHints.TEL_EXTENSION:
    168             case W3cHints.EMAIL:
    169             case W3cHints.IMPP:
    170                 return true;
    171         }
    172         logw("Invalid W3C type hint: %s", hint);
    173         return false;
    174     }
    175 }
    176