1bc055b0ef1c11337b8ec5f681097e7b51e84b9c4Felipe Leme/* 2bc055b0ef1c11337b8ec5f681097e7b51e84b9c4Felipe Leme * Copyright (C) 2018 The Android Open Source Project 3bc055b0ef1c11337b8ec5f681097e7b51e84b9c4Felipe Leme * 4bc055b0ef1c11337b8ec5f681097e7b51e84b9c4Felipe Leme * Licensed under the Apache License, Version 2.0 (the "License"); 5bc055b0ef1c11337b8ec5f681097e7b51e84b9c4Felipe Leme * you may not use this file except in compliance with the License. 6bc055b0ef1c11337b8ec5f681097e7b51e84b9c4Felipe Leme * You may obtain a copy of the License at 7bc055b0ef1c11337b8ec5f681097e7b51e84b9c4Felipe Leme * 8bc055b0ef1c11337b8ec5f681097e7b51e84b9c4Felipe Leme * http://www.apache.org/licenses/LICENSE-2.0 9bc055b0ef1c11337b8ec5f681097e7b51e84b9c4Felipe Leme * 10bc055b0ef1c11337b8ec5f681097e7b51e84b9c4Felipe Leme * Unless required by applicable law or agreed to in writing, software 11bc055b0ef1c11337b8ec5f681097e7b51e84b9c4Felipe Leme * distributed under the License is distributed on an "AS IS" BASIS, 12bc055b0ef1c11337b8ec5f681097e7b51e84b9c4Felipe Leme * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13bc055b0ef1c11337b8ec5f681097e7b51e84b9c4Felipe Leme * See the License for the specific language governing permissions and 14bc055b0ef1c11337b8ec5f681097e7b51e84b9c4Felipe Leme * limitations under the License. 15bc055b0ef1c11337b8ec5f681097e7b51e84b9c4Felipe Leme */ 16bc055b0ef1c11337b8ec5f681097e7b51e84b9c4Felipe Lemepackage android.service.autofill; 17bc055b0ef1c11337b8ec5f681097e7b51e84b9c4Felipe Leme 189f1921f9ffc20544f22e76dd412523b53cb68a14Eugene Suslaimport static com.android.internal.util.function.pooled.PooledLambda.obtainMessage; 199f1921f9ffc20544f22e76dd412523b53cb68a14Eugene Susla 20bc055b0ef1c11337b8ec5f681097e7b51e84b9c4Felipe Lemeimport android.annotation.NonNull; 21bc055b0ef1c11337b8ec5f681097e7b51e84b9c4Felipe Lemeimport android.annotation.Nullable; 22bc055b0ef1c11337b8ec5f681097e7b51e84b9c4Felipe Lemeimport android.annotation.SystemApi; 23bc055b0ef1c11337b8ec5f681097e7b51e84b9c4Felipe Lemeimport android.app.Service; 24bc055b0ef1c11337b8ec5f681097e7b51e84b9c4Felipe Lemeimport android.content.Intent; 25bc055b0ef1c11337b8ec5f681097e7b51e84b9c4Felipe Lemeimport android.os.Bundle; 269f1921f9ffc20544f22e76dd412523b53cb68a14Eugene Suslaimport android.os.Handler; 27bc055b0ef1c11337b8ec5f681097e7b51e84b9c4Felipe Lemeimport android.os.IBinder; 28bc055b0ef1c11337b8ec5f681097e7b51e84b9c4Felipe Lemeimport android.os.Looper; 29bc055b0ef1c11337b8ec5f681097e7b51e84b9c4Felipe Lemeimport android.os.Parcel; 30bc055b0ef1c11337b8ec5f681097e7b51e84b9c4Felipe Lemeimport android.os.Parcelable; 31bc055b0ef1c11337b8ec5f681097e7b51e84b9c4Felipe Lemeimport android.os.RemoteCallback; 32bc055b0ef1c11337b8ec5f681097e7b51e84b9c4Felipe Lemeimport android.os.RemoteException; 33bc055b0ef1c11337b8ec5f681097e7b51e84b9c4Felipe Lemeimport android.util.Log; 34bc055b0ef1c11337b8ec5f681097e7b51e84b9c4Felipe Lemeimport android.view.autofill.AutofillValue; 35bc055b0ef1c11337b8ec5f681097e7b51e84b9c4Felipe Leme 36bc055b0ef1c11337b8ec5f681097e7b51e84b9c4Felipe Lemeimport java.util.Arrays; 37bc055b0ef1c11337b8ec5f681097e7b51e84b9c4Felipe Lemeimport java.util.List; 38bc055b0ef1c11337b8ec5f681097e7b51e84b9c4Felipe Leme 39bc055b0ef1c11337b8ec5f681097e7b51e84b9c4Felipe Leme/** 40bc055b0ef1c11337b8ec5f681097e7b51e84b9c4Felipe Leme * A service that calculates field classification scores. 41bc055b0ef1c11337b8ec5f681097e7b51e84b9c4Felipe Leme * 42bc055b0ef1c11337b8ec5f681097e7b51e84b9c4Felipe Leme * <p>A field classification score is a {@code float} representing how well an 43bc055b0ef1c11337b8ec5f681097e7b51e84b9c4Felipe Leme * {@link AutofillValue} filled matches a expected value predicted by an autofill service 448ce43a5e701de4ddb2f1e281b946d5ea96711d06Felipe Leme * —a full match is {@code 1.0} (representing 100%), while a full mismatch is {@code 0.0}. 45bc055b0ef1c11337b8ec5f681097e7b51e84b9c4Felipe Leme * 468ce43a5e701de4ddb2f1e281b946d5ea96711d06Felipe Leme * <p>The exact score depends on the algorithm used to calculate it—the service must provide 47bc055b0ef1c11337b8ec5f681097e7b51e84b9c4Felipe Leme * at least one default algorithm (which is used when the algorithm is not specified or is invalid), 488ce43a5e701de4ddb2f1e281b946d5ea96711d06Felipe Leme * but it could provide more (in which case the algorithm name should be specified by the caller 49bc055b0ef1c11337b8ec5f681097e7b51e84b9c4Felipe Leme * when calculating the scores). 50bc055b0ef1c11337b8ec5f681097e7b51e84b9c4Felipe Leme * 51bc055b0ef1c11337b8ec5f681097e7b51e84b9c4Felipe Leme * {@hide} 52bc055b0ef1c11337b8ec5f681097e7b51e84b9c4Felipe Leme */ 53bc055b0ef1c11337b8ec5f681097e7b51e84b9c4Felipe Leme@SystemApi 54bc055b0ef1c11337b8ec5f681097e7b51e84b9c4Felipe Lemepublic abstract class AutofillFieldClassificationService extends Service { 55bc055b0ef1c11337b8ec5f681097e7b51e84b9c4Felipe Leme 56bc055b0ef1c11337b8ec5f681097e7b51e84b9c4Felipe Leme private static final String TAG = "AutofillFieldClassificationService"; 57bc055b0ef1c11337b8ec5f681097e7b51e84b9c4Felipe Leme 58bc055b0ef1c11337b8ec5f681097e7b51e84b9c4Felipe Leme /** 59bc055b0ef1c11337b8ec5f681097e7b51e84b9c4Felipe Leme * The {@link Intent} action that must be declared as handled by a service 60bc055b0ef1c11337b8ec5f681097e7b51e84b9c4Felipe Leme * in its manifest for the system to recognize it as a quota providing service. 61bc055b0ef1c11337b8ec5f681097e7b51e84b9c4Felipe Leme */ 62bc055b0ef1c11337b8ec5f681097e7b51e84b9c4Felipe Leme public static final String SERVICE_INTERFACE = 63bc055b0ef1c11337b8ec5f681097e7b51e84b9c4Felipe Leme "android.service.autofill.AutofillFieldClassificationService"; 64bc055b0ef1c11337b8ec5f681097e7b51e84b9c4Felipe Leme 65d11a66220c424c030542aca5c47de059d4a308ccFelipe Leme /** 66d11a66220c424c030542aca5c47de059d4a308ccFelipe Leme * Manifest metadata key for the resource string containing the name of the default field 67d11a66220c424c030542aca5c47de059d4a308ccFelipe Leme * classification algorithm. 68d11a66220c424c030542aca5c47de059d4a308ccFelipe Leme */ 69d11a66220c424c030542aca5c47de059d4a308ccFelipe Leme public static final String SERVICE_META_DATA_KEY_DEFAULT_ALGORITHM = 70d11a66220c424c030542aca5c47de059d4a308ccFelipe Leme "android.autofill.field_classification.default_algorithm"; 71d11a66220c424c030542aca5c47de059d4a308ccFelipe Leme /** 72d11a66220c424c030542aca5c47de059d4a308ccFelipe Leme * Manifest metadata key for the resource string array containing the names of all field 73d11a66220c424c030542aca5c47de059d4a308ccFelipe Leme * classification algorithms provided by the service. 74d11a66220c424c030542aca5c47de059d4a308ccFelipe Leme */ 75d11a66220c424c030542aca5c47de059d4a308ccFelipe Leme public static final String SERVICE_META_DATA_KEY_AVAILABLE_ALGORITHMS = 76d11a66220c424c030542aca5c47de059d4a308ccFelipe Leme "android.autofill.field_classification.available_algorithms"; 77d11a66220c424c030542aca5c47de059d4a308ccFelipe Leme 78d11a66220c424c030542aca5c47de059d4a308ccFelipe Leme 79bc055b0ef1c11337b8ec5f681097e7b51e84b9c4Felipe Leme /** {@hide} **/ 80bc055b0ef1c11337b8ec5f681097e7b51e84b9c4Felipe Leme public static final String EXTRA_SCORES = "scores"; 81bc055b0ef1c11337b8ec5f681097e7b51e84b9c4Felipe Leme 82bc055b0ef1c11337b8ec5f681097e7b51e84b9c4Felipe Leme private AutofillFieldClassificationServiceWrapper mWrapper; 83bc055b0ef1c11337b8ec5f681097e7b51e84b9c4Felipe Leme 849f1921f9ffc20544f22e76dd412523b53cb68a14Eugene Susla private void getScores(RemoteCallback callback, String algorithmName, Bundle algorithmArgs, 859f1921f9ffc20544f22e76dd412523b53cb68a14Eugene Susla List<AutofillValue> actualValues, String[] userDataValues) { 86bc055b0ef1c11337b8ec5f681097e7b51e84b9c4Felipe Leme final Bundle data = new Bundle(); 879f1921f9ffc20544f22e76dd412523b53cb68a14Eugene Susla final float[][] scores = onGetScores(algorithmName, algorithmArgs, actualValues, 889f1921f9ffc20544f22e76dd412523b53cb68a14Eugene Susla Arrays.asList(userDataValues)); 899f1921f9ffc20544f22e76dd412523b53cb68a14Eugene Susla if (scores != null) { 909f1921f9ffc20544f22e76dd412523b53cb68a14Eugene Susla data.putParcelable(EXTRA_SCORES, new Scores(scores)); 91bc055b0ef1c11337b8ec5f681097e7b51e84b9c4Felipe Leme } 92bc055b0ef1c11337b8ec5f681097e7b51e84b9c4Felipe Leme callback.sendResult(data); 939f1921f9ffc20544f22e76dd412523b53cb68a14Eugene Susla } 94bc055b0ef1c11337b8ec5f681097e7b51e84b9c4Felipe Leme 959f1921f9ffc20544f22e76dd412523b53cb68a14Eugene Susla private final Handler mHandler = new Handler(Looper.getMainLooper(), null, true); 96bc055b0ef1c11337b8ec5f681097e7b51e84b9c4Felipe Leme 97bc055b0ef1c11337b8ec5f681097e7b51e84b9c4Felipe Leme /** @hide */ 98bc055b0ef1c11337b8ec5f681097e7b51e84b9c4Felipe Leme public AutofillFieldClassificationService() { 99bc055b0ef1c11337b8ec5f681097e7b51e84b9c4Felipe Leme 100bc055b0ef1c11337b8ec5f681097e7b51e84b9c4Felipe Leme } 101bc055b0ef1c11337b8ec5f681097e7b51e84b9c4Felipe Leme 102bc055b0ef1c11337b8ec5f681097e7b51e84b9c4Felipe Leme @Override 103bc055b0ef1c11337b8ec5f681097e7b51e84b9c4Felipe Leme public void onCreate() { 104bc055b0ef1c11337b8ec5f681097e7b51e84b9c4Felipe Leme super.onCreate(); 105bc055b0ef1c11337b8ec5f681097e7b51e84b9c4Felipe Leme mWrapper = new AutofillFieldClassificationServiceWrapper(); 106bc055b0ef1c11337b8ec5f681097e7b51e84b9c4Felipe Leme } 107bc055b0ef1c11337b8ec5f681097e7b51e84b9c4Felipe Leme 108bc055b0ef1c11337b8ec5f681097e7b51e84b9c4Felipe Leme @Override 109bc055b0ef1c11337b8ec5f681097e7b51e84b9c4Felipe Leme public IBinder onBind(Intent intent) { 110bc055b0ef1c11337b8ec5f681097e7b51e84b9c4Felipe Leme return mWrapper; 111bc055b0ef1c11337b8ec5f681097e7b51e84b9c4Felipe Leme } 112bc055b0ef1c11337b8ec5f681097e7b51e84b9c4Felipe Leme 113bc055b0ef1c11337b8ec5f681097e7b51e84b9c4Felipe Leme /** 114bc055b0ef1c11337b8ec5f681097e7b51e84b9c4Felipe Leme * Calculates field classification scores in a batch. 115bc055b0ef1c11337b8ec5f681097e7b51e84b9c4Felipe Leme * 11620d30e522654d26a30b2afc3a03410e9bc0219ceFelipe Leme * <p>A field classification score is a {@code float} representing how well an 11720d30e522654d26a30b2afc3a03410e9bc0219ceFelipe Leme * {@link AutofillValue} filled matches a expected value predicted by an autofill service 1188ce43a5e701de4ddb2f1e281b946d5ea96711d06Felipe Leme * —a full match is {@code 1.0} (representing 100%), while a full mismatch is {@code 0.0}. 119bc055b0ef1c11337b8ec5f681097e7b51e84b9c4Felipe Leme * 1208ce43a5e701de4ddb2f1e281b946d5ea96711d06Felipe Leme * <p>The exact score depends on the algorithm used to calculate it—the service must 12120d30e522654d26a30b2afc3a03410e9bc0219ceFelipe Leme * provide at least one default algorithm (which is used when the algorithm is not specified 12220d30e522654d26a30b2afc3a03410e9bc0219ceFelipe Leme * or is invalid), but it could provide more (in which case the algorithm name should be 1238ce43a5e701de4ddb2f1e281b946d5ea96711d06Felipe Leme * specified by the caller when calculating the scores). 12420d30e522654d26a30b2afc3a03410e9bc0219ceFelipe Leme * 12520d30e522654d26a30b2afc3a03410e9bc0219ceFelipe Leme * <p>For example, if the service provides an algorithm named {@code EXACT_MATCH} that 1268ce43a5e701de4ddb2f1e281b946d5ea96711d06Felipe Leme * returns {@code 1.0} if all characters match or {@code 0.0} otherwise, a call to: 12720d30e522654d26a30b2afc3a03410e9bc0219ceFelipe Leme * 12820d30e522654d26a30b2afc3a03410e9bc0219ceFelipe Leme * <pre> 12920d30e522654d26a30b2afc3a03410e9bc0219ceFelipe Leme * service.onGetScores("EXACT_MATCH", null, 13020d30e522654d26a30b2afc3a03410e9bc0219ceFelipe Leme * Arrays.asList(AutofillValue.forText("email1"), AutofillValue.forText("PHONE1")), 13120d30e522654d26a30b2afc3a03410e9bc0219ceFelipe Leme * Arrays.asList("email1", "phone1")); 13220d30e522654d26a30b2afc3a03410e9bc0219ceFelipe Leme * </pre> 13320d30e522654d26a30b2afc3a03410e9bc0219ceFelipe Leme * 13420d30e522654d26a30b2afc3a03410e9bc0219ceFelipe Leme * <p>Returns: 13520d30e522654d26a30b2afc3a03410e9bc0219ceFelipe Leme * 13620d30e522654d26a30b2afc3a03410e9bc0219ceFelipe Leme * <pre> 13720d30e522654d26a30b2afc3a03410e9bc0219ceFelipe Leme * [ 13820d30e522654d26a30b2afc3a03410e9bc0219ceFelipe Leme * [1.0, 0.0], // "email1" compared against ["email1", "phone1"] 13920d30e522654d26a30b2afc3a03410e9bc0219ceFelipe Leme * [0.0, 0.0] // "PHONE1" compared against ["email1", "phone1"] 14020d30e522654d26a30b2afc3a03410e9bc0219ceFelipe Leme * ]; 14120d30e522654d26a30b2afc3a03410e9bc0219ceFelipe Leme * </pre> 14220d30e522654d26a30b2afc3a03410e9bc0219ceFelipe Leme * 14320d30e522654d26a30b2afc3a03410e9bc0219ceFelipe Leme * <p>If the same algorithm allows the caller to specify whether the comparisons should be 14420d30e522654d26a30b2afc3a03410e9bc0219ceFelipe Leme * case sensitive by passing a boolean option named {@code "case_sensitive"}, then a call to: 14520d30e522654d26a30b2afc3a03410e9bc0219ceFelipe Leme * 14620d30e522654d26a30b2afc3a03410e9bc0219ceFelipe Leme * <pre> 14720d30e522654d26a30b2afc3a03410e9bc0219ceFelipe Leme * Bundle algorithmOptions = new Bundle(); 14820d30e522654d26a30b2afc3a03410e9bc0219ceFelipe Leme * algorithmOptions.putBoolean("case_sensitive", false); 14920d30e522654d26a30b2afc3a03410e9bc0219ceFelipe Leme * 15020d30e522654d26a30b2afc3a03410e9bc0219ceFelipe Leme * service.onGetScores("EXACT_MATCH", algorithmOptions, 15120d30e522654d26a30b2afc3a03410e9bc0219ceFelipe Leme * Arrays.asList(AutofillValue.forText("email1"), AutofillValue.forText("PHONE1")), 15220d30e522654d26a30b2afc3a03410e9bc0219ceFelipe Leme * Arrays.asList("email1", "phone1")); 15320d30e522654d26a30b2afc3a03410e9bc0219ceFelipe Leme * </pre> 15420d30e522654d26a30b2afc3a03410e9bc0219ceFelipe Leme * 15520d30e522654d26a30b2afc3a03410e9bc0219ceFelipe Leme * <p>Returns: 15620d30e522654d26a30b2afc3a03410e9bc0219ceFelipe Leme * 15720d30e522654d26a30b2afc3a03410e9bc0219ceFelipe Leme * <pre> 15820d30e522654d26a30b2afc3a03410e9bc0219ceFelipe Leme * [ 15920d30e522654d26a30b2afc3a03410e9bc0219ceFelipe Leme * [1.0, 0.0], // "email1" compared against ["email1", "phone1"] 16020d30e522654d26a30b2afc3a03410e9bc0219ceFelipe Leme * [0.0, 1.0] // "PHONE1" compared against ["email1", "phone1"] 16120d30e522654d26a30b2afc3a03410e9bc0219ceFelipe Leme * ]; 16220d30e522654d26a30b2afc3a03410e9bc0219ceFelipe Leme * </pre> 16320d30e522654d26a30b2afc3a03410e9bc0219ceFelipe Leme * 16420d30e522654d26a30b2afc3a03410e9bc0219ceFelipe Leme * @param algorithm name of the algorithm to be used to calculate the scores. If invalid or 16520d30e522654d26a30b2afc3a03410e9bc0219ceFelipe Leme * {@code null}, the default algorithm is used instead. 16620d30e522654d26a30b2afc3a03410e9bc0219ceFelipe Leme * @param algorithmOptions optional arguments to be passed to the algorithm. 167bc055b0ef1c11337b8ec5f681097e7b51e84b9c4Felipe Leme * @param actualValues values entered by the user. 168bc055b0ef1c11337b8ec5f681097e7b51e84b9c4Felipe Leme * @param userDataValues values predicted from the user data. 16920d30e522654d26a30b2afc3a03410e9bc0219ceFelipe Leme * @return the calculated scores of {@code actualValues} x {@code userDataValues}. 170bc055b0ef1c11337b8ec5f681097e7b51e84b9c4Felipe Leme * 171bc055b0ef1c11337b8ec5f681097e7b51e84b9c4Felipe Leme * {@hide} 172bc055b0ef1c11337b8ec5f681097e7b51e84b9c4Felipe Leme */ 173bc055b0ef1c11337b8ec5f681097e7b51e84b9c4Felipe Leme @Nullable 174bc055b0ef1c11337b8ec5f681097e7b51e84b9c4Felipe Leme @SystemApi 175d11a66220c424c030542aca5c47de059d4a308ccFelipe Leme public float[][] onGetScores(@Nullable String algorithm, 17620d30e522654d26a30b2afc3a03410e9bc0219ceFelipe Leme @Nullable Bundle algorithmOptions, @NonNull List<AutofillValue> actualValues, 177bc055b0ef1c11337b8ec5f681097e7b51e84b9c4Felipe Leme @NonNull List<String> userDataValues) { 178029f10100cced4a0dd6b07b26b0ffd670881aa47Felipe Leme Log.e(TAG, "service implementation (" + getClass() + " does not implement onGetScore()"); 179029f10100cced4a0dd6b07b26b0ffd670881aa47Felipe Leme return null; 180bc055b0ef1c11337b8ec5f681097e7b51e84b9c4Felipe Leme } 181bc055b0ef1c11337b8ec5f681097e7b51e84b9c4Felipe Leme 182bc055b0ef1c11337b8ec5f681097e7b51e84b9c4Felipe Leme private final class AutofillFieldClassificationServiceWrapper 183bc055b0ef1c11337b8ec5f681097e7b51e84b9c4Felipe Leme extends IAutofillFieldClassificationService.Stub { 184bc055b0ef1c11337b8ec5f681097e7b51e84b9c4Felipe Leme @Override 185bc055b0ef1c11337b8ec5f681097e7b51e84b9c4Felipe Leme public void getScores(RemoteCallback callback, String algorithmName, Bundle algorithmArgs, 186bc055b0ef1c11337b8ec5f681097e7b51e84b9c4Felipe Leme List<AutofillValue> actualValues, String[] userDataValues) 187bc055b0ef1c11337b8ec5f681097e7b51e84b9c4Felipe Leme throws RemoteException { 1889f1921f9ffc20544f22e76dd412523b53cb68a14Eugene Susla mHandler.sendMessage(obtainMessage( 1899f1921f9ffc20544f22e76dd412523b53cb68a14Eugene Susla AutofillFieldClassificationService::getScores, 1909f1921f9ffc20544f22e76dd412523b53cb68a14Eugene Susla AutofillFieldClassificationService.this, 1919f1921f9ffc20544f22e76dd412523b53cb68a14Eugene Susla callback, algorithmName, algorithmArgs, actualValues, userDataValues)); 192bc055b0ef1c11337b8ec5f681097e7b51e84b9c4Felipe Leme } 193bc055b0ef1c11337b8ec5f681097e7b51e84b9c4Felipe Leme } 194bc055b0ef1c11337b8ec5f681097e7b51e84b9c4Felipe Leme 195bc055b0ef1c11337b8ec5f681097e7b51e84b9c4Felipe Leme /** 196d11a66220c424c030542aca5c47de059d4a308ccFelipe Leme * Helper class used to encapsulate a float[][] in a Parcelable. 197bc055b0ef1c11337b8ec5f681097e7b51e84b9c4Felipe Leme * 198bc055b0ef1c11337b8ec5f681097e7b51e84b9c4Felipe Leme * {@hide} 199bc055b0ef1c11337b8ec5f681097e7b51e84b9c4Felipe Leme */ 200bc055b0ef1c11337b8ec5f681097e7b51e84b9c4Felipe Leme public static final class Scores implements Parcelable { 201fe05a529885281ebfb103d1a3ad6bfb862281268Felipe Leme @NonNull 202d11a66220c424c030542aca5c47de059d4a308ccFelipe Leme public final float[][] scores; 203bc055b0ef1c11337b8ec5f681097e7b51e84b9c4Felipe Leme 204d11a66220c424c030542aca5c47de059d4a308ccFelipe Leme private Scores(Parcel parcel) { 205bc055b0ef1c11337b8ec5f681097e7b51e84b9c4Felipe Leme final int size1 = parcel.readInt(); 206bc055b0ef1c11337b8ec5f681097e7b51e84b9c4Felipe Leme final int size2 = parcel.readInt(); 207d11a66220c424c030542aca5c47de059d4a308ccFelipe Leme scores = new float[size1][size2]; 208bc055b0ef1c11337b8ec5f681097e7b51e84b9c4Felipe Leme for (int i = 0; i < size1; i++) { 209bc055b0ef1c11337b8ec5f681097e7b51e84b9c4Felipe Leme for (int j = 0; j < size2; j++) { 210d11a66220c424c030542aca5c47de059d4a308ccFelipe Leme scores[i][j] = parcel.readFloat(); 211bc055b0ef1c11337b8ec5f681097e7b51e84b9c4Felipe Leme } 212bc055b0ef1c11337b8ec5f681097e7b51e84b9c4Felipe Leme } 213bc055b0ef1c11337b8ec5f681097e7b51e84b9c4Felipe Leme } 214bc055b0ef1c11337b8ec5f681097e7b51e84b9c4Felipe Leme 215fe05a529885281ebfb103d1a3ad6bfb862281268Felipe Leme private Scores(@NonNull float[][] scores) { 216d11a66220c424c030542aca5c47de059d4a308ccFelipe Leme this.scores = scores; 217bc055b0ef1c11337b8ec5f681097e7b51e84b9c4Felipe Leme } 218bc055b0ef1c11337b8ec5f681097e7b51e84b9c4Felipe Leme 219bc055b0ef1c11337b8ec5f681097e7b51e84b9c4Felipe Leme @Override 220fe05a529885281ebfb103d1a3ad6bfb862281268Felipe Leme public String toString() { 221fe05a529885281ebfb103d1a3ad6bfb862281268Felipe Leme final int size1 = scores.length; 222fe05a529885281ebfb103d1a3ad6bfb862281268Felipe Leme final int size2 = size1 > 0 ? scores[0].length : 0; 223fe05a529885281ebfb103d1a3ad6bfb862281268Felipe Leme final StringBuilder builder = new StringBuilder("Scores [") 224fe05a529885281ebfb103d1a3ad6bfb862281268Felipe Leme .append(size1).append("x").append(size2).append("] "); 225fe05a529885281ebfb103d1a3ad6bfb862281268Felipe Leme for (int i = 0; i < size1; i++) { 226fe05a529885281ebfb103d1a3ad6bfb862281268Felipe Leme builder.append(i).append(": ").append(Arrays.toString(scores[i])).append(' '); 227fe05a529885281ebfb103d1a3ad6bfb862281268Felipe Leme } 228fe05a529885281ebfb103d1a3ad6bfb862281268Felipe Leme return builder.toString(); 229fe05a529885281ebfb103d1a3ad6bfb862281268Felipe Leme } 230fe05a529885281ebfb103d1a3ad6bfb862281268Felipe Leme 231fe05a529885281ebfb103d1a3ad6bfb862281268Felipe Leme @Override 232bc055b0ef1c11337b8ec5f681097e7b51e84b9c4Felipe Leme public int describeContents() { 233bc055b0ef1c11337b8ec5f681097e7b51e84b9c4Felipe Leme return 0; 234bc055b0ef1c11337b8ec5f681097e7b51e84b9c4Felipe Leme } 235bc055b0ef1c11337b8ec5f681097e7b51e84b9c4Felipe Leme 236bc055b0ef1c11337b8ec5f681097e7b51e84b9c4Felipe Leme @Override 237bc055b0ef1c11337b8ec5f681097e7b51e84b9c4Felipe Leme public void writeToParcel(Parcel parcel, int flags) { 238d11a66220c424c030542aca5c47de059d4a308ccFelipe Leme int size1 = scores.length; 239d11a66220c424c030542aca5c47de059d4a308ccFelipe Leme int size2 = scores[0].length; 240bc055b0ef1c11337b8ec5f681097e7b51e84b9c4Felipe Leme parcel.writeInt(size1); 241bc055b0ef1c11337b8ec5f681097e7b51e84b9c4Felipe Leme parcel.writeInt(size2); 242bc055b0ef1c11337b8ec5f681097e7b51e84b9c4Felipe Leme for (int i = 0; i < size1; i++) { 243bc055b0ef1c11337b8ec5f681097e7b51e84b9c4Felipe Leme for (int j = 0; j < size2; j++) { 244d11a66220c424c030542aca5c47de059d4a308ccFelipe Leme parcel.writeFloat(scores[i][j]); 245bc055b0ef1c11337b8ec5f681097e7b51e84b9c4Felipe Leme } 246bc055b0ef1c11337b8ec5f681097e7b51e84b9c4Felipe Leme } 247bc055b0ef1c11337b8ec5f681097e7b51e84b9c4Felipe Leme } 248bc055b0ef1c11337b8ec5f681097e7b51e84b9c4Felipe Leme 249bc055b0ef1c11337b8ec5f681097e7b51e84b9c4Felipe Leme public static final Creator<Scores> CREATOR = new Creator<Scores>() { 250bc055b0ef1c11337b8ec5f681097e7b51e84b9c4Felipe Leme @Override 251bc055b0ef1c11337b8ec5f681097e7b51e84b9c4Felipe Leme public Scores createFromParcel(Parcel parcel) { 252bc055b0ef1c11337b8ec5f681097e7b51e84b9c4Felipe Leme return new Scores(parcel); 253bc055b0ef1c11337b8ec5f681097e7b51e84b9c4Felipe Leme } 254bc055b0ef1c11337b8ec5f681097e7b51e84b9c4Felipe Leme 255bc055b0ef1c11337b8ec5f681097e7b51e84b9c4Felipe Leme @Override 256bc055b0ef1c11337b8ec5f681097e7b51e84b9c4Felipe Leme public Scores[] newArray(int size) { 257bc055b0ef1c11337b8ec5f681097e7b51e84b9c4Felipe Leme return new Scores[size]; 258bc055b0ef1c11337b8ec5f681097e7b51e84b9c4Felipe Leme } 259bc055b0ef1c11337b8ec5f681097e7b51e84b9c4Felipe Leme }; 260bc055b0ef1c11337b8ec5f681097e7b51e84b9c4Felipe Leme } 261bc055b0ef1c11337b8ec5f681097e7b51e84b9c4Felipe Leme} 262