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 }