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