├── .gitignore ├── .idea ├── compiler.xml ├── copyright │ └── profiles_settings.xml ├── encodings.xml ├── gradle.xml ├── misc.xml ├── modules.xml ├── runConfigurations.xml └── vcs.xml ├── README.md ├── TODO.md ├── app ├── .gitignore ├── build.gradle ├── proguard-rules.pro └── src │ ├── androidTest │ └── java │ │ └── com │ │ └── pddstudio │ │ └── encryptedpreferences │ │ └── ApplicationTest.java │ ├── main │ ├── AndroidManifest.xml │ ├── java │ │ └── com │ │ │ └── pddstudio │ │ │ └── encryptedpreferences │ │ │ ├── MainActivity.java │ │ │ └── MockUtils.java │ └── res │ │ ├── layout │ │ └── activity_main.xml │ │ ├── mipmap-hdpi │ │ └── ic_launcher.png │ │ ├── mipmap-mdpi │ │ └── ic_launcher.png │ │ ├── mipmap-xhdpi │ │ └── ic_launcher.png │ │ ├── mipmap-xxhdpi │ │ └── ic_launcher.png │ │ ├── mipmap-xxxhdpi │ │ └── ic_launcher.png │ │ ├── values-w820dp │ │ └── dimens.xml │ │ └── values │ │ ├── colors.xml │ │ ├── dimens.xml │ │ ├── strings.xml │ │ └── styles.xml │ └── test │ └── java │ └── com │ └── pddstudio │ └── encryptedpreferences │ └── ExampleUnitTest.java ├── build.gradle ├── encrypted-preferences ├── .gitignore ├── build.gradle ├── gradle.properties ├── maven-push.gradle ├── proguard-rules.pro └── src │ ├── androidTest │ └── java │ │ └── com │ │ └── pddstudio │ │ └── preferences │ │ └── encrypted │ │ ├── ApplicationTest.java │ │ ├── EncryptedEditorTest.java │ │ ├── EncryptedPreferencesTest.java │ │ ├── SingletonCreationTest.java │ │ └── UtilsTest.java │ ├── main │ ├── AndroidManifest.xml │ ├── java │ │ └── com │ │ │ └── pddstudio │ │ │ └── preferences │ │ │ └── encrypted │ │ │ └── EncryptedPreferences.java │ └── res │ │ └── values │ │ ├── about_strings.xml │ │ └── config.xml │ └── test │ └── java │ └── com │ └── pddstudio │ └── preferences │ └── encrypted │ └── ExampleUnitTest.java ├── gradle.properties ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat └── settings.gradle /.gitignore: -------------------------------------------------------------------------------- 1 | # Base .gitignore taken from "https://raw.githubusercontent.com/github/gitignore/master/Android.gitignore" 2 | # Built application files 3 | *.apk 4 | *.ap_ 5 | 6 | # Files for the ART/Dalvik VM 7 | *.dex 8 | 9 | # Java class files 10 | *.class 11 | 12 | # Generated files 13 | bin/ 14 | gen/ 15 | out/ 16 | 17 | # Gradle files 18 | .gradle/ 19 | build/ 20 | 21 | # Local configuration file (sdk path, etc) 22 | local.properties 23 | 24 | # Proguard folder generated by Eclipse 25 | proguard/ 26 | 27 | # Log Files 28 | *.log 29 | 30 | # Android Studio Navigation editor temp files 31 | .navigation/ 32 | 33 | # Android Studio captures folder 34 | captures/ 35 | 36 | # Intellij 37 | *.iml 38 | .idea/workspace.xml 39 | .idea/tasks.xml 40 | .idea/libraries 41 | .idea/ 42 | 43 | # Keystore files 44 | *.jks 45 | 46 | # External native build folder generated in Android Studio 2.2 and later 47 | .externalNativeBuild 48 | .idea/vcs.xml 49 | .idea/misc.xml 50 | .idea/instapk.xml 51 | instapk.log* -------------------------------------------------------------------------------- /.idea/compiler.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /.idea/copyright/profiles_settings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /.idea/encodings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /.idea/gradle.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 18 | 19 | -------------------------------------------------------------------------------- /.idea/misc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 19 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 46 | -------------------------------------------------------------------------------- /.idea/modules.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /.idea/runConfigurations.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 11 | 12 | -------------------------------------------------------------------------------- /.idea/vcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## EncryptedPreferences 2 | An Android Library to securely read and write encrypted values to your SharedPreferences. 3 | 4 | [![Maven Central](https://maven-badges.herokuapp.com/maven-central/com.pddstudio/encrypted-preferences/badge.svg)](https://maven-badges.herokuapp.com/maven-central/com.pddstudio/encrypted-preferences) 5 | [![Android Arsenal](https://img.shields.io/badge/Android%20Arsenal-EncryptedPreferences-green.svg?style=true)](https://android-arsenal.com/details/1/4280) 6 | 7 | 8 | ## Reason & Explanation 9 | 10 | When developing an Android application you often save primitive values to your application's internal storage using [SharedPreferences](https://developer.android.com/reference/android/content/SharedPreferences.html). 11 | 12 | All values written to your SharedPreferences are stored unencrypted in a simple and plain `.xml file` inside your application's internal directory. In case you save sensitive or important data here, it can be easily read (and modified) by users with a rooted phone. To avoid this, I created EncryptedPreferences. 13 | 14 | EncryptedPreferences is a simple wrapper around the official SharedPreferences API, which saves all data (containing both, keys and values) encrypted using [Advanced Encryption Standard](https://en.wikipedia.org/wiki/Advanced_Encryption_Standard) (AES/256 bit key). The library takes care about all heavy lifting, so all you have to do is to read and write your preferences as usual. 15 | 16 | EncryptedPreferences aims to have the same API as the official SharedPreferences, to make it as easy as possible to integrate it into your application. 17 | 18 | ## Getting Started 19 | 20 | ### Add the library as dependency 21 | Add the library as dependency to your app's `build.gradle` file. 22 | 23 | ``` 24 | dependencies { 25 | compile 'com.pddstudio:encrypted-preferences:1.3.0' 26 | } 27 | ``` 28 | Make sure you're always using the latest version, which can be found on [Maven Central](http://search.maven.org/#artifactdetails%7Ccom.pddstudio%7Cencrypted-preferences). 29 | 30 | ### Start using EncryptedPreferences 31 | To start using EncryptedPreferences you have to create a new instance using the `EncryptedPreferences.Builder`. 32 | 33 | **Example:** 34 | 35 | ```java 36 | EncryptedPreferences encryptedPreferences = new EncryptedPreferences.Builder(this).withEncryptionPassword("password").build(); 37 | ``` 38 | 39 | **Note:** Starting with version 1.2.0 the support for default (password/key) configuration is deprecated due to security reasons. Therefore it's now required to specify your own password using the Builder's `.withEncryptionPassword("password")` method. If no password is set a `RuntimeException` will be thrown. 40 | 41 | Once you created your `EncryptedPreferences` instance, you can read and write values. 42 | 43 | **Saving Values:** 44 | 45 | ```java 46 | encryptedPreferences.edit() 47 | .putString(TEST_KEY_VALUE_STRING, "testString") 48 | .putFloat(TEST_KEY_VALUE_FLOAT, 1.5f) 49 | .putLong(TEST_KEY_VALUE_LONG, 10L) 50 | .putBoolean(TEST_KEY_VALUE_BOOLEAN, false) 51 | .apply(); 52 | ``` 53 | 54 | *Note:* 55 | As with the official SharedPreferences API, make sure to call `apply()` in order to save your changes! 56 | 57 | **Reading Values:** 58 | 59 | ```java 60 | private void printValues() { 61 | Log.d("MainActivity", TEST_KEY_VALUE_STRING + " => " + encryptedPreferences.getString(TEST_KEY_VALUE_STRING, TEST_KEY_VALUE_STRING)); 62 | Log.d("MainActivity", TEST_KEY_VALUE_FLOAT + " => " + encryptedPreferences.getFloat(TEST_KEY_VALUE_FLOAT, 0)); 63 | Log.d("MainActivity", TEST_KEY_VALUE_LONG + " => " + encryptedPreferences.getLong(TEST_KEY_VALUE_LONG, 0)); 64 | Log.d("MainActivity", TEST_KEY_VALUE_BOOLEAN + " => " + encryptedPreferences.getBoolean(TEST_KEY_VALUE_BOOLEAN, true)); 65 | } 66 | ``` 67 | 68 | **Listening for Changes:** 69 | 70 | Beginning with version 1.2.0 it is possible to get notified when a value changed (added/updated/removed). 71 | 72 | You can receive change events by implementing the `EncryptedPreferences.OnSharedPreferenceChangeListener` interface into your Activity/Fragment (or custom component). Make sure to respect your component's lifecycle and register/unregister the listener(s) to avoid unwanted behaviours. 73 | 74 | ```java 75 | @Override 76 | protected void onCreate() { 77 | super.onCreate(); 78 | //some other stuff here... 79 | 80 | //register the listener 81 | encryptedPreferences.registerOnSharedPreferenceChangeListener(this); 82 | } 83 | 84 | @Override 85 | protected void onDestroy() { 86 | //unregister the listener before destroying the Activity/Fragment 87 | encryptedPreferences.unregisterOnSharedPreferenceChangeListener(this); 88 | super.onDestroy(); 89 | } 90 | 91 | @Override 92 | public void onSharedPreferenceChanged(EncryptedPreferences encryptedPreferences, String key) { 93 | //do your stuff with the changed data here 94 | Log.d("MainActivity", "onSharedPreferenceChanged() => key: " + key); 95 | } 96 | ``` 97 | 98 | For more information about how to read and write data to SharedPreferences, head over to the [official Android Developer Guide](https://developer.android.com/training/basics/data-storage/shared-preferences.html). 99 | 100 | **Utilities:** 101 | 102 | Since version 1.0.1 EncryptedPreferences has a utility class, which might come in handy for several usecases beside persisting data. The `EncryptedPreferences.Utils` class is bound to your `EncryptedPreferences` instance and uses the same configuration. 103 | 104 | You can retrieve the `EncryptedPreferences.Utils` instance by calling `getUtils()` on your `EncryptedPreferences` object. 105 | 106 | *Example:* 107 | 108 | ```java 109 | private void utilsExample() { 110 | //get the encrypted value for an api key while debugging, so we don't have to save the original api key as plain text in production. 111 | String encryptedApiKey = encryptedPreferences.getUtils().encryptStringValue("SOME_API_KEY_HERE"); 112 | Log.d("MainActivity", "encryptedApiKey => " + encryptedApiKey); 113 | //in production we simply use the utility method with the encrypted value which we got from debugging. 114 | String decryptedApiKey = encryptedPreferences.getUtils().decryptStringValue(encryptedApiKey); 115 | Log.d("MainActivity", "decryptedApiKey => " + decryptedApiKey); 116 | } 117 | ``` 118 | 119 | ## Third-Party Libraries 120 | This library is using [AESCrypt-Android](https://github.com/scottyab/AESCrypt-Android) by [scottyab](https://github.com/scottyab). 121 | 122 | ## About & Contact 123 | - In case you've a question feel free to hit me up via E-Mail (patrick.pddstudio[at]googlemail.com) 124 | - or [Google+](http://plus.google.com/+PatrickJung42) / Hangouts 125 | 126 | ## License 127 | ``` 128 | Copyright 2016 Patrick J 129 | 130 | Licensed under the Apache License, Version 2.0 (the "License"); 131 | you may not use this file except in compliance with the License. 132 | You may obtain a copy of the License at 133 | 134 | http://www.apache.org/licenses/LICENSE-2.0 135 | 136 | Unless required by applicable law or agreed to in writing, software 137 | distributed under the License is distributed on an "AS IS" BASIS, 138 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 139 | See the License for the specific language governing permissions and 140 | limitations under the License. 141 | ``` 142 | -------------------------------------------------------------------------------- /TODO.md: -------------------------------------------------------------------------------- 1 | ##To-Do List for everything regarding EncryptedPreferences 2 | 3 | ###ToDo Library Module 4 | 5 | - Allow [password change during runtime](https://github.com/PDDStudio/EncryptedPreferences/issues/5) (with automated migration of previous saved data) 6 | 7 | -------------------------------------------------------------------------------- /app/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /app/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.application' 2 | 3 | android { 4 | compileSdkVersion 25 5 | buildToolsVersion '25.0.2' 6 | 7 | defaultConfig { 8 | applicationId "com.pddstudio.encryptedpreferences" 9 | minSdkVersion 16 10 | targetSdkVersion 25 11 | versionCode 1 12 | versionName "1.0" 13 | } 14 | buildTypes { 15 | release { 16 | minifyEnabled false 17 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 18 | } 19 | } 20 | } 21 | 22 | dependencies { 23 | compile fileTree(include: ['*.jar'], dir: 'libs') 24 | testCompile 'junit:junit:4.12' 25 | compile 'com.android.support:appcompat-v7:24.2.0' 26 | compile project(':encrypted-preferences') 27 | } 28 | -------------------------------------------------------------------------------- /app/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # By default, the flags in this file are appended to flags specified 3 | # in /Users/pddstudio/Library/Android/sdk/tools/proguard/proguard-android.txt 4 | # You can edit the include path and order by changing the proguardFiles 5 | # directive in build.gradle. 6 | # 7 | # For more details, see 8 | # http://developer.android.com/guide/developing/tools/proguard.html 9 | 10 | # Add any project specific keep options here: 11 | 12 | # If your project uses WebView with JS, uncomment the following 13 | # and specify the fully qualified class name to the JavaScript interface 14 | # class: 15 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 16 | # public *; 17 | #} 18 | -------------------------------------------------------------------------------- /app/src/androidTest/java/com/pddstudio/encryptedpreferences/ApplicationTest.java: -------------------------------------------------------------------------------- 1 | package com.pddstudio.encryptedpreferences; 2 | 3 | import android.app.Application; 4 | import android.test.ApplicationTestCase; 5 | 6 | /** 7 | * Testing Fundamentals 8 | */ 9 | public class ApplicationTest extends ApplicationTestCase { 10 | public ApplicationTest() { 11 | super(Application.class); 12 | } 13 | } -------------------------------------------------------------------------------- /app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /app/src/main/java/com/pddstudio/encryptedpreferences/MainActivity.java: -------------------------------------------------------------------------------- 1 | package com.pddstudio.encryptedpreferences; 2 | 3 | import android.os.Bundle; 4 | import android.support.v7.app.AppCompatActivity; 5 | import android.util.Log; 6 | import android.view.View; 7 | import android.widget.Button; 8 | 9 | import com.pddstudio.preferences.encrypted.EncryptedPreferences; 10 | 11 | public class MainActivity extends AppCompatActivity implements View.OnClickListener, EncryptedPreferences.OnSharedPreferenceChangeListener { 12 | 13 | private static final String TEST_KEY_VALUE_STRING = "testValueString"; 14 | private static final String TEST_KEY_VALUE_FLOAT = "testValueFloat"; 15 | private static final String TEST_KEY_VALUE_LONG = "testValueLong"; 16 | private static final String TEST_KEY_VALUE_BOOLEAN = "testValueBoolean"; 17 | 18 | private EncryptedPreferences encryptedPreferences; 19 | 20 | private Button addRandomValueButton; 21 | private Button registerListenerButton; 22 | private Button unregisterListenerButton; 23 | private Button importMockPreferencesButton; 24 | 25 | @Override 26 | protected void onCreate(Bundle savedInstanceState) { 27 | super.onCreate(savedInstanceState); 28 | setContentView(R.layout.activity_main); 29 | 30 | //register demo buttons 31 | addRandomValueButton = (Button) findViewById(R.id.add_random_value_button); 32 | addRandomValueButton.setOnClickListener(this); 33 | registerListenerButton = (Button) findViewById(R.id.register_listener_button); 34 | registerListenerButton.setOnClickListener(this); 35 | unregisterListenerButton = (Button) findViewById(R.id.unregister_listener_button); 36 | unregisterListenerButton.setOnClickListener(this); 37 | importMockPreferencesButton = (Button) findViewById(R.id.import_preferences_button); 38 | importMockPreferencesButton.setOnClickListener(this); 39 | 40 | //create EncryptedPreferences instance 41 | encryptedPreferences = new EncryptedPreferences.Builder(this).withEncryptionPassword("example").withOnSharedPreferenceChangeListener(this).build(); 42 | 43 | //save the preferences 44 | saveValues(); 45 | printSeparatorLine(); 46 | //validate the preferences 47 | validateValues(); 48 | printSeparatorLine(); 49 | //print the values 50 | printValues(); 51 | printSeparatorLine(); 52 | utilsExample(); 53 | printSeparatorLine(); 54 | } 55 | 56 | @Override 57 | protected void onDestroy() { 58 | encryptedPreferences.unregisterOnSharedPreferenceChangeListener(this); 59 | super.onDestroy(); 60 | } 61 | 62 | private void printSeparatorLine() { 63 | Log.d("MainActivity", "= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = ="); 64 | } 65 | 66 | private void saveValues() { 67 | encryptedPreferences.edit() 68 | .putString(TEST_KEY_VALUE_STRING, "testString") 69 | .putFloat(TEST_KEY_VALUE_FLOAT, 1.5f) 70 | .putLong(TEST_KEY_VALUE_LONG, 10L) 71 | .putBoolean(TEST_KEY_VALUE_BOOLEAN, false) 72 | .apply(); 73 | Log.d("MainActivity", "done saving preferences!"); 74 | } 75 | 76 | private void validateValues() { 77 | Log.d("MainActivity", TEST_KEY_VALUE_STRING + " : " + encryptedPreferences.contains(TEST_KEY_VALUE_STRING)); 78 | Log.d("MainActivity", TEST_KEY_VALUE_FLOAT + " : " + encryptedPreferences.contains(TEST_KEY_VALUE_FLOAT)); 79 | Log.d("MainActivity", TEST_KEY_VALUE_LONG + " : " + encryptedPreferences.contains(TEST_KEY_VALUE_LONG)); 80 | Log.d("MainActivity", TEST_KEY_VALUE_BOOLEAN + " : " + encryptedPreferences.contains(TEST_KEY_VALUE_BOOLEAN)); 81 | } 82 | 83 | private void printValues() { 84 | Log.d("MainActivity", TEST_KEY_VALUE_STRING + " => " + encryptedPreferences.getString(TEST_KEY_VALUE_STRING, TEST_KEY_VALUE_STRING)); 85 | Log.d("MainActivity", TEST_KEY_VALUE_FLOAT + " => " + encryptedPreferences.getFloat(TEST_KEY_VALUE_FLOAT, 0)); 86 | Log.d("MainActivity", TEST_KEY_VALUE_LONG + " => " + encryptedPreferences.getLong(TEST_KEY_VALUE_LONG, 0)); 87 | Log.d("MainActivity", TEST_KEY_VALUE_BOOLEAN + " => " + encryptedPreferences.getBoolean(TEST_KEY_VALUE_BOOLEAN, true)); 88 | } 89 | 90 | private void utilsExample() { 91 | //get the encrypted value for an api key while debugging, so we don't have to save the original api key as plain text in production. 92 | String encryptedApiKey = encryptedPreferences.getUtils().encryptStringValue("SOME_API_KEY_HERE"); 93 | Log.d("MainActivity", "encryptedApiKey => " + encryptedApiKey); 94 | //in production we simply use the utility method with the encrypted value which we got from debugging. 95 | String decryptedApiKey = encryptedPreferences.getUtils().decryptStringValue(encryptedApiKey); 96 | Log.d("MainActivity", "decryptedApiKey => " + decryptedApiKey); 97 | } 98 | 99 | @Override 100 | public void onClick(View v) { 101 | switch (v.getId()) { 102 | case R.id.add_random_value_button: 103 | MockUtils.addRandomValue(encryptedPreferences); 104 | break; 105 | case R.id.register_listener_button: 106 | encryptedPreferences.registerOnSharedPreferenceChangeListener(this); 107 | Log.d("MainActivity", "registered listener!"); 108 | break; 109 | case R.id.unregister_listener_button: 110 | encryptedPreferences.unregisterOnSharedPreferenceChangeListener(this); 111 | Log.d("MainActivity", "unregistered listener!"); 112 | break; 113 | case R.id.import_preferences_button: 114 | encryptedPreferences.importSharedPreferences(MockUtils.createMockSharedPreferences(this), true, true); 115 | Log.d("MainActivity", "created and imported mocked preferences!"); 116 | break; 117 | } 118 | } 119 | 120 | @Override 121 | public void onSharedPreferenceChanged(EncryptedPreferences encryptedPreferences, String key) { 122 | Log.d("MainActivity", "onSharedPreferenceChanged() => key: " + key); 123 | } 124 | 125 | } 126 | -------------------------------------------------------------------------------- /app/src/main/java/com/pddstudio/encryptedpreferences/MockUtils.java: -------------------------------------------------------------------------------- 1 | package com.pddstudio.encryptedpreferences; 2 | 3 | import android.content.Context; 4 | import android.content.SharedPreferences; 5 | import android.util.Log; 6 | 7 | import com.pddstudio.preferences.encrypted.EncryptedPreferences; 8 | 9 | import java.util.Random; 10 | 11 | /** 12 | * Created by pddstudio on 11/09/16. 13 | */ 14 | public class MockUtils { 15 | 16 | private static Random random = new Random(); 17 | 18 | public static void addRandomValue(EncryptedPreferences encryptedPreferences) { 19 | int identifier = random.nextInt(5); 20 | String key = getRandomKey(identifier); 21 | EncryptedPreferences.EncryptedEditor editor = encryptedPreferences.edit(); 22 | switch (identifier) { 23 | case 0: 24 | editor.putBoolean(key, random.nextBoolean()); 25 | break; 26 | case 1: 27 | editor.putFloat(key, random.nextFloat()); 28 | break; 29 | case 2: 30 | editor.putInt(key, random.nextInt()); 31 | break; 32 | case 3: 33 | editor.putLong(key, random.nextLong()); 34 | break; 35 | case 4: 36 | editor.putString(key, key); 37 | break; 38 | } 39 | editor.apply(); 40 | } 41 | 42 | public static void addRandomValue(SharedPreferences preferences) { 43 | int identifier = random.nextInt(5); 44 | String key = getRandomKey(identifier); 45 | SharedPreferences.Editor editor = preferences.edit(); 46 | switch (identifier) { 47 | case 0: 48 | editor.putBoolean(key, random.nextBoolean()); 49 | break; 50 | case 1: 51 | editor.putFloat(key, random.nextFloat()); 52 | break; 53 | case 2: 54 | editor.putInt(key, random.nextInt()); 55 | break; 56 | case 3: 57 | editor.putLong(key, random.nextLong()); 58 | break; 59 | case 4: 60 | editor.putString(key, key); 61 | break; 62 | } 63 | editor.apply(); 64 | } 65 | 66 | public static SharedPreferences createMockSharedPreferences(Context context) { 67 | //create a dummy preference 68 | String prefName = getRandomPreferenceName(context); 69 | SharedPreferences preferences = context.getSharedPreferences(prefName, 0); 70 | //fill it with some random values 71 | for(int i = 0; i < 20; i++) { 72 | addRandomValue(preferences); 73 | } 74 | Log.d("MainActivity", "Created mock preferences! Name: " + prefName + " Item count: " + preferences.getAll().size()); 75 | return preferences; 76 | } 77 | 78 | public static String getRandomPreferenceName(Context context) { 79 | return context.getPackageName() + "_random_preference_" + System.currentTimeMillis(); 80 | } 81 | 82 | public static String getRandomKey(int identifier) { 83 | String base = "RANDOM_STRING_BASE_"; 84 | return base + identifier + "_" + System.currentTimeMillis(); 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_main.xml: -------------------------------------------------------------------------------- 1 | 2 | 11 | 12 | 17 | 18 |