1 /* <lambda>null2 * 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.multidatasetservice.datasource 17 18 import android.content.Context 19 import android.content.SharedPreferences 20 import android.util.ArraySet 21 import com.example.android.autofillframework.CommonUtil 22 import com.example.android.autofillframework.multidatasetservice.model.FilledAutofillFieldCollection 23 import com.google.gson.GsonBuilder 24 import com.google.gson.reflect.TypeToken 25 26 27 /** 28 * Singleton autofill data repository that stores autofill fields to SharedPreferences. 29 * Disclaimer: you should not store sensitive fields like user data unencrypted. This is done 30 * here only for simplicity and learning purposes. 31 */ 32 object SharedPrefsAutofillRepository : AutofillRepository { 33 private val SHARED_PREF_KEY = "com.example.android.autofillframework.service" 34 private val CLIENT_FORM_DATA_KEY = "loginCredentialDatasets" 35 private val DATASET_NUMBER_KEY = "datasetNumber" 36 37 private fun getPrefs(context: Context): SharedPreferences { 38 return context.applicationContext.getSharedPreferences(SHARED_PREF_KEY, Context.MODE_PRIVATE) 39 } 40 41 override fun getFilledAutofillFieldCollection(context: Context, focusedAutofillHints: List<String>, 42 allAutofillHints: List<String>): HashMap<String, FilledAutofillFieldCollection>? { 43 var hasDataForFocusedAutofillHints = false 44 val clientFormDataMap = HashMap<String, FilledAutofillFieldCollection>() 45 val clientFormDataStringSet = getAllAutofillDataStringSet(context) 46 val gson = GsonBuilder().excludeFieldsWithoutExposeAnnotation().setPrettyPrinting().create() 47 val type = object : TypeToken<FilledAutofillFieldCollection>() {}.type 48 for (clientFormDataString in clientFormDataStringSet) { 49 gson.fromJson<FilledAutofillFieldCollection>(clientFormDataString, type)?.let { 50 if (it.helpsWithHints(focusedAutofillHints)) { 51 // Saved data has data relevant to at least 1 of the hints associated with the 52 // View in focus. 53 hasDataForFocusedAutofillHints = true 54 it.datasetName?.let { datasetName -> 55 if (it.helpsWithHints(allAutofillHints)) { 56 // Saved data has data relevant to at least 1 of these hints associated with any 57 // of the Views in the hierarchy. 58 clientFormDataMap.put(datasetName, it) 59 } 60 } 61 } 62 } 63 } 64 if (hasDataForFocusedAutofillHints) { 65 return clientFormDataMap 66 } else { 67 return null 68 } 69 } 70 71 override fun saveFilledAutofillFieldCollection(context: Context, filledAutofillFieldCollection: FilledAutofillFieldCollection) { 72 val datasetName = "dataset-" + getDatasetNumber(context) 73 filledAutofillFieldCollection.datasetName = datasetName 74 val allAutofillData = getAllAutofillDataStringSet(context) 75 val gson = CommonUtil.createGson() 76 allAutofillData.add(gson.toJson(filledAutofillFieldCollection).toString()) 77 saveAllAutofillDataStringSet(context, allAutofillData) 78 incrementDatasetNumber(context) 79 } 80 81 override fun clear(context: Context) { 82 getPrefs(context).edit().remove(CLIENT_FORM_DATA_KEY).remove(DATASET_NUMBER_KEY).apply() 83 } 84 85 private fun getAllAutofillDataStringSet(context: Context): MutableSet<String> { 86 return getPrefs(context).getStringSet(CLIENT_FORM_DATA_KEY, ArraySet<String>()) 87 } 88 89 private fun saveAllAutofillDataStringSet(context: Context, 90 allAutofillDataStringSet: Set<String>) { 91 getPrefs(context).edit().putStringSet(CLIENT_FORM_DATA_KEY, allAutofillDataStringSet).apply() 92 } 93 94 /** 95 * For simplicity, datasets will be named in the form "dataset-X" where X means 96 * this was the Xth dataset saved. 97 */ 98 private fun getDatasetNumber(context: Context): Int { 99 return getPrefs(context).getInt(DATASET_NUMBER_KEY, 0) 100 } 101 102 /** 103 * Every time a dataset is saved, this should be called to increment the dataset number. 104 * (only important for this service's dataset naming scheme). 105 */ 106 private fun incrementDatasetNumber(context: Context) { 107 getPrefs(context).edit().putInt(DATASET_NUMBER_KEY, getDatasetNumber(context) + 1).apply() 108 } 109 }