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 17 package android.service.autofill; 18 19 import static android.view.autofill.Helper.sDebug; 20 21 import android.annotation.NonNull; 22 import android.os.Parcel; 23 import android.view.autofill.Helper; 24 25 import com.android.internal.util.Preconditions; 26 27 import java.util.ArrayList; 28 import java.util.Collections; 29 import java.util.Comparator; 30 import java.util.List; 31 32 /** 33 * Represents the <a href="AutofillService.html#FieldClassification">field classification</a> 34 * results for a given field. 35 */ 36 public final class FieldClassification { 37 38 private final ArrayList<Match> mMatches; 39 40 /** @hide */ FieldClassification(@onNull ArrayList<Match> matches)41 public FieldClassification(@NonNull ArrayList<Match> matches) { 42 mMatches = Preconditions.checkNotNull(matches); 43 Collections.sort(mMatches, new Comparator<Match>() { 44 @Override 45 public int compare(Match o1, Match o2) { 46 if (o1.mScore > o2.mScore) return -1; 47 if (o1.mScore < o2.mScore) return 1; 48 return 0; 49 }} 50 ); 51 } 52 53 /** 54 * Gets the {@link Match matches} with the highest {@link Match#getScore() scores} (sorted in 55 * descending order). 56 * 57 * <p><b>Note:</b> There's no guarantee of how many matches will be returned. In fact, 58 * the Android System might return just the top match to minimize the impact of field 59 * classification in the device's health. 60 */ 61 @NonNull getMatches()62 public List<Match> getMatches() { 63 return mMatches; 64 } 65 66 @Override toString()67 public String toString() { 68 if (!sDebug) return super.toString(); 69 70 return "FieldClassification: " + mMatches; 71 } 72 writeToParcel(Parcel parcel)73 private void writeToParcel(Parcel parcel) { 74 parcel.writeInt(mMatches.size()); 75 for (int i = 0; i < mMatches.size(); i++) { 76 mMatches.get(i).writeToParcel(parcel); 77 } 78 } 79 readFromParcel(Parcel parcel)80 private static FieldClassification readFromParcel(Parcel parcel) { 81 final int size = parcel.readInt(); 82 final ArrayList<Match> matches = new ArrayList<>(); 83 for (int i = 0; i < size; i++) { 84 matches.add(i, Match.readFromParcel(parcel)); 85 } 86 87 return new FieldClassification(matches); 88 } 89 readArrayFromParcel(Parcel parcel)90 static FieldClassification[] readArrayFromParcel(Parcel parcel) { 91 final int length = parcel.readInt(); 92 final FieldClassification[] fcs = new FieldClassification[length]; 93 for (int i = 0; i < length; i++) { 94 fcs[i] = readFromParcel(parcel); 95 } 96 return fcs; 97 } 98 writeArrayToParcel(@onNull Parcel parcel, @NonNull FieldClassification[] fcs)99 static void writeArrayToParcel(@NonNull Parcel parcel, @NonNull FieldClassification[] fcs) { 100 parcel.writeInt(fcs.length); 101 for (int i = 0; i < fcs.length; i++) { 102 fcs[i].writeToParcel(parcel); 103 } 104 } 105 106 /** 107 * Represents the score of a {@link UserData} entry for the field. 108 */ 109 public static final class Match { 110 111 private final String mCategoryId; 112 private final float mScore; 113 114 /** @hide */ Match(String categoryId, float score)115 public Match(String categoryId, float score) { 116 mCategoryId = Preconditions.checkNotNull(categoryId); 117 mScore = score; 118 } 119 120 /** 121 * Gets the category id of the {@link UserData} entry. 122 */ 123 @NonNull getCategoryId()124 public String getCategoryId() { 125 return mCategoryId; 126 } 127 128 /** 129 * Gets a classification score for the value of this field compared to the value of the 130 * {@link UserData} entry. 131 * 132 * <p>The score is based in a comparison of the field value and the user data entry, and it 133 * ranges from {@code 0.0F} to {@code 1.0F}: 134 * <ul> 135 * <li>{@code 1.0F} represents a full match ({@code 100%}). 136 * <li>{@code 0.0F} represents a full mismatch ({@code 0%}). 137 * <li>Any other value is a partial match. 138 * </ul> 139 * 140 * <p>How the score is calculated depends on the 141 * {@link UserData.Builder#setFieldClassificationAlgorithm(String, android.os.Bundle) 142 * algorithm} used. 143 */ getScore()144 public float getScore() { 145 return mScore; 146 } 147 148 @Override toString()149 public String toString() { 150 if (!sDebug) return super.toString(); 151 152 final StringBuilder string = new StringBuilder("Match: categoryId="); 153 Helper.appendRedacted(string, mCategoryId); 154 return string.append(", score=").append(mScore).toString(); 155 } 156 writeToParcel(@onNull Parcel parcel)157 private void writeToParcel(@NonNull Parcel parcel) { 158 parcel.writeString(mCategoryId); 159 parcel.writeFloat(mScore); 160 } 161 readFromParcel(@onNull Parcel parcel)162 private static Match readFromParcel(@NonNull Parcel parcel) { 163 return new Match(parcel.readString(), parcel.readFloat()); 164 } 165 } 166 } 167