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.autofillframework.service.datasource; 17 18 import android.content.Context; 19 import android.content.SharedPreferences; 20 import android.util.ArraySet; 21 22 import com.example.android.autofillframework.service.model.ClientFormData; 23 24 import org.json.JSONException; 25 import org.json.JSONObject; 26 27 import java.util.HashMap; 28 import java.util.List; 29 import java.util.Set; 30 31 /** 32 * Singleton autofill data repository, that stores autofill fields to SharedPreferences. 33 * DISCLAIMER, you should not store sensitive fields like user data unencrypted. This is only done 34 * here for simplicity and learning purposes. 35 */ 36 public class LocalAutofillRepository implements AutofillRepository { 37 private static final String SHARED_PREF_KEY = "com.example.android.autofillframework.service"; 38 private static final String CLIENT_FORM_DATA_KEY = "loginCredentialDatasets"; 39 private static final String DATASET_NUMBER_KEY = "datasetNumber"; 40 41 private static LocalAutofillRepository sInstance; 42 43 private final SharedPreferences mPrefs; 44 45 // TODO prepend with autofill data set in Settings. 46 private LocalAutofillRepository(Context context) { 47 mPrefs = context.getApplicationContext() 48 .getSharedPreferences(SHARED_PREF_KEY, Context.MODE_PRIVATE); 49 } 50 51 public static LocalAutofillRepository getInstance(Context context) { 52 if (sInstance == null) { 53 sInstance = new LocalAutofillRepository(context); 54 } 55 return sInstance; 56 } 57 58 @Override 59 public HashMap<String, ClientFormData> getClientFormData(List<String> focusedAutofillHints, 60 List<String> allAutofillHints) { 61 try { 62 // TODO use sqlite instead. 63 boolean hasDataForFocusedAutofillHints = false; 64 HashMap<String, ClientFormData> clientFormDataMap = new HashMap<>(); 65 Set<String> clientFormDataStringSet = getAllAutofillDataStringSet(); 66 for (String clientFormDataString : clientFormDataStringSet) { 67 ClientFormData clientFormData = ClientFormData 68 .fromJson(new JSONObject(clientFormDataString)); 69 if (clientFormData != null) { 70 if (clientFormData.helpsWithHints(focusedAutofillHints)) { 71 hasDataForFocusedAutofillHints = true; 72 } 73 if (clientFormData.helpsWithHints(allAutofillHints)) { 74 clientFormDataMap.put(clientFormData.getDatasetName(), clientFormData); 75 } 76 } 77 } 78 if (hasDataForFocusedAutofillHints) { 79 return clientFormDataMap; 80 } else { 81 return null; 82 } 83 } catch (JSONException e) { 84 return null; 85 } 86 } 87 88 @Override 89 public void saveClientFormData(ClientFormData clientFormData) { 90 //TODO use sqlite instead. 91 String datasetName = "dataset-" + getDatasetNumber(); 92 clientFormData.setDatasetName(datasetName); 93 Set<String> allAutofillData = getAllAutofillDataStringSet(); 94 allAutofillData.add(clientFormData.toJson().toString()); 95 saveAllAutofillDataStringSet(allAutofillData); 96 incrementDatasetNumber(); 97 } 98 99 @Override 100 public void clear() { 101 mPrefs.edit().remove(CLIENT_FORM_DATA_KEY).apply(); 102 } 103 104 private Set<String> getAllAutofillDataStringSet() { 105 return mPrefs.getStringSet(CLIENT_FORM_DATA_KEY, new ArraySet<String>()); 106 } 107 108 private void saveAllAutofillDataStringSet(Set<String> allAutofillDataStringSet) { 109 mPrefs.edit().putStringSet(CLIENT_FORM_DATA_KEY, allAutofillDataStringSet).apply(); 110 } 111 112 /** 113 * For simplicity, datasets will be named in the form "dataset-X" where X means 114 * this was the Xth dataset saved. 115 */ 116 private int getDatasetNumber() { 117 return mPrefs.getInt(DATASET_NUMBER_KEY, 0); 118 } 119 120 /** 121 * Every time a dataset is saved, this should be called to increment the dataset number. 122 * (only important for this service's dataset naming scheme). 123 */ 124 private void incrementDatasetNumber() { 125 mPrefs.edit().putInt(DATASET_NUMBER_KEY, getDatasetNumber() + 1).apply(); 126 } 127 }