├── .gitignore ├── .idea ├── codeStyles │ ├── Project.xml │ └── codeStyleConfig.xml ├── gradle.xml ├── jarRepositories.xml ├── misc.xml ├── runConfigurations.xml └── vcs.xml ├── README.md ├── app ├── .gitignore ├── build.gradle ├── proguard-rules.pro └── src │ ├── androidTest │ └── java │ │ └── com │ │ └── shalu │ │ └── android │ │ └── datastore │ │ └── ExampleInstrumentedTest.kt │ ├── main │ ├── AndroidManifest.xml │ ├── java │ │ └── com │ │ │ └── shalu │ │ │ └── android │ │ │ └── datastore │ │ │ ├── MainActivity.kt │ │ │ ├── UserPreferences.kt │ │ │ ├── manager │ │ │ ├── MigrationManager.kt │ │ │ ├── PrefSettingsManager.kt │ │ │ └── ProtoSettingsManager.kt │ │ │ ├── pref │ │ │ ├── MigrateToPrefDataStoreActivity.kt │ │ │ ├── PrefDataStoreActivity.kt │ │ │ └── UserDetails.kt │ │ │ └── proto │ │ │ ├── ProtoDataStoreActivity.kt │ │ │ └── ProtoSettingsSerializer.kt │ ├── proto │ │ └── user_info.proto │ └── res │ │ ├── drawable-v24 │ │ └── ic_launcher_foreground.xml │ │ ├── drawable │ │ └── ic_launcher_background.xml │ │ ├── layout │ │ ├── activity_datastore.xml │ │ ├── activity_main.xml │ │ └── activity_migrate_datastore.xml │ │ ├── mipmap-anydpi-v26 │ │ ├── ic_launcher.xml │ │ └── ic_launcher_round.xml │ │ ├── mipmap-hdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ │ ├── mipmap-mdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ │ ├── mipmap-xhdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ │ ├── mipmap-xxhdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ │ ├── mipmap-xxxhdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ │ └── values │ │ ├── colors.xml │ │ ├── strings.xml │ │ └── styles.xml │ └── test │ └── java │ └── com │ └── shalu │ └── android │ └── datastore │ └── ExampleUnitTest.kt ├── build.gradle ├── gradle.properties ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat └── settings.gradle /.gitignore: -------------------------------------------------------------------------------- 1 | *.iml 2 | .gradle 3 | /local.properties 4 | /.idea/caches 5 | /.idea/libraries 6 | /.idea/modules.xml 7 | /.idea/workspace.xml 8 | /.idea/navEditor.xml 9 | /.idea/assetWizardSettings.xml 10 | .DS_Store 11 | /build 12 | /captures 13 | .externalNativeBuild 14 | .cxx 15 | -------------------------------------------------------------------------------- /.idea/codeStyles/Project.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 11 | 13 | 14 | 15 | 17 | 18 | 19 |
20 | 21 | 22 | 23 | xmlns:android 24 | 25 | ^$ 26 | 27 | 28 | 29 |
30 |
31 | 32 | 33 | 34 | xmlns:.* 35 | 36 | ^$ 37 | 38 | 39 | BY_NAME 40 | 41 |
42 |
43 | 44 | 45 | 46 | .*:id 47 | 48 | http://schemas.android.com/apk/res/android 49 | 50 | 51 | 52 |
53 |
54 | 55 | 56 | 57 | .*:name 58 | 59 | http://schemas.android.com/apk/res/android 60 | 61 | 62 | 63 |
64 |
65 | 66 | 67 | 68 | name 69 | 70 | ^$ 71 | 72 | 73 | 74 |
75 |
76 | 77 | 78 | 79 | style 80 | 81 | ^$ 82 | 83 | 84 | 85 |
86 |
87 | 88 | 89 | 90 | .* 91 | 92 | ^$ 93 | 94 | 95 | BY_NAME 96 | 97 |
98 |
99 | 100 | 101 | 102 | .* 103 | 104 | http://schemas.android.com/apk/res/android 105 | 106 | 107 | ANDROID_ATTRIBUTE_ORDER 108 | 109 |
110 |
111 | 112 | 113 | 114 | .* 115 | 116 | .* 117 | 118 | 119 | BY_NAME 120 | 121 |
122 |
123 |
124 |
125 | 126 | 128 |
129 |
-------------------------------------------------------------------------------- /.idea/codeStyles/codeStyleConfig.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | -------------------------------------------------------------------------------- /.idea/gradle.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 20 | 21 | -------------------------------------------------------------------------------- /.idea/jarRepositories.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 9 | 10 | 14 | 15 | 19 | 20 | 24 | 25 | -------------------------------------------------------------------------------- /.idea/misc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 14 | -------------------------------------------------------------------------------- /.idea/runConfigurations.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 11 | 12 | -------------------------------------------------------------------------------- /.idea/vcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # DataStore 2 | Jetpack DataStore is a data storage solution that allows you to store key-value pairs or typed objects with protocol buffers. 3 | DataStore uses Kotlin's coroutines and Flow to store data asynchronously, consistently, and transactionally. It will replace SharedPreferences. 4 | ## Two Types of DataStore: 5 | 6 | 1. Preferences DataStore stores and accesses data using keys. This implementation does not require a predefined schema, and it does not provide type safety. 7 | 2. Proto DataStore stores data as instances of a custom data type. This implementation requires you to define a schema using protocol buffers, but it provides 8 | type safety. 9 | 10 | Note : You can go through the [medium link](https://medium.com/@shalutd007/welcome-datastore-good-bye-sharedpreferences-4bf68e70efdb) to get detail explanation about DataStore 11 | -------------------------------------------------------------------------------- /app/.gitignore: -------------------------------------------------------------------------------- 1 | /build -------------------------------------------------------------------------------- /app/build.gradle: -------------------------------------------------------------------------------- 1 | plugins { 2 | id 'com.android.application' 3 | id 'kotlin-android' 4 | id "com.google.protobuf" version "0.8.12" 5 | } 6 | 7 | android { 8 | compileSdkVersion 30 9 | 10 | defaultConfig { 11 | applicationId "com.codelab.android.datastore" 12 | minSdkVersion 16 13 | targetSdkVersion 30 14 | versionCode 1 15 | versionName "1.0" 16 | 17 | testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" 18 | } 19 | 20 | buildTypes { 21 | release { 22 | minifyEnabled false 23 | proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' 24 | } 25 | } 26 | compileOptions { 27 | sourceCompatibility JavaVersion.VERSION_1_8 28 | targetCompatibility JavaVersion.VERSION_1_8 29 | } 30 | kotlinOptions { 31 | jvmTarget = '1.8' 32 | freeCompilerArgs += ["-Xopt-in=kotlinx.coroutines.ExperimentalCoroutinesApi"] 33 | } 34 | 35 | android.buildFeatures.viewBinding = true 36 | } 37 | 38 | dependencies { 39 | 40 | implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version" 41 | implementation "androidx.appcompat:appcompat:$supportLibVersion" 42 | implementation "androidx.constraintlayout:constraintlayout:$constraintLayoutVersion" 43 | 44 | // kotlin 45 | implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" 46 | implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:$coroutinesVersion" 47 | implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:$coroutinesVersion" 48 | 49 | // DataStore 50 | implementation "androidx.datastore:datastore-core:$dataStoreVersion" 51 | implementation "androidx.datastore:datastore-preferences:$dataStoreVersion" 52 | 53 | // Protobuf 54 | implementation "com.google.protobuf:protobuf-javalite:$protobufVersion" 55 | 56 | testImplementation "junit:junit:$junitVersion" 57 | androidTestImplementation "androidx.test:runner:$runnerVersion" 58 | androidTestImplementation "androidx.test.espresso:espresso-core:$espressoVersion" 59 | } 60 | 61 | protobuf { 62 | protoc { 63 | artifact = "com.google.protobuf:protoc:3.10.0" 64 | } 65 | 66 | // Generates the java Protobuf-lite code for the Protobufs in this project. See 67 | // https://github.com/google/protobuf-gradle-plugin#customizing-protobuf-compilation 68 | // for more information. 69 | generateProtoTasks { 70 | all().each { task -> 71 | task.builtins { 72 | java { 73 | option 'lite' 74 | } 75 | } 76 | } 77 | } 78 | } -------------------------------------------------------------------------------- /app/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # You can control the set of applied configuration files using the 3 | # proguardFiles setting in build.gradle. 4 | # 5 | # For more details, see 6 | # http://developer.android.com/guide/developing/tools/proguard.html 7 | 8 | # If your project uses WebView with JS, uncomment the following 9 | # and specify the fully qualified class name to the JavaScript interface 10 | # class: 11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 12 | # public *; 13 | #} 14 | 15 | # Uncomment this to preserve the line number information for 16 | # debugging stack traces. 17 | #-keepattributes SourceFile,LineNumberTable 18 | 19 | # If you keep the line number information, uncomment this to 20 | # hide the original source file name. 21 | #-renamesourcefileattribute SourceFile -------------------------------------------------------------------------------- /app/src/androidTest/java/com/shalu/android/datastore/ExampleInstrumentedTest.kt: -------------------------------------------------------------------------------- 1 | package com.shalu.android.datastore 2 | 3 | import androidx.test.platform.app.InstrumentationRegistry 4 | import androidx.test.ext.junit.runners.AndroidJUnit4 5 | 6 | import org.junit.Test 7 | import org.junit.runner.RunWith 8 | 9 | import org.junit.Assert.* 10 | 11 | /** 12 | * Instrumented test, which will execute on an Android device. 13 | * 14 | * See [testing documentation](http://d.android.com/tools/testing). 15 | */ 16 | @RunWith(AndroidJUnit4::class) 17 | class ExampleInstrumentedTest { 18 | @Test 19 | fun useAppContext() { 20 | // Context of the app under test. 21 | val appContext = InstrumentationRegistry.getInstrumentation().targetContext 22 | assertEquals("com.shalu.android.datastore", appContext.packageName) 23 | } 24 | } -------------------------------------------------------------------------------- /app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /app/src/main/java/com/shalu/android/datastore/MainActivity.kt: -------------------------------------------------------------------------------- 1 | package com.shalu.android.datastore 2 | 3 | import android.content.Intent 4 | import androidx.appcompat.app.AppCompatActivity 5 | import android.os.Bundle 6 | import android.view.View 7 | import com.shalu.android.datastore.pref.MigrateToPrefDataStoreActivity 8 | import com.shalu.android.datastore.pref.PrefDataStoreActivity 9 | import com.shalu.android.datastore.proto.ProtoDataStoreActivity 10 | 11 | class MainActivity : AppCompatActivity() { 12 | override fun onCreate(savedInstanceState: Bundle?) { 13 | super.onCreate(savedInstanceState) 14 | setContentView(R.layout.activity_main) 15 | 16 | findViewById(R.id.btnPref).setOnClickListener { 17 | startActivity(Intent(this, PrefDataStoreActivity::class.java)) 18 | } 19 | 20 | findViewById(R.id.btnProto).setOnClickListener { 21 | startActivity(Intent(this, ProtoDataStoreActivity::class.java)) 22 | } 23 | 24 | findViewById(R.id.btnMigratePref).setOnClickListener { 25 | startActivity(Intent(this, MigrateToPrefDataStoreActivity::class.java)) 26 | } 27 | } 28 | } -------------------------------------------------------------------------------- /app/src/main/java/com/shalu/android/datastore/UserPreferences.kt: -------------------------------------------------------------------------------- 1 | package com.shalu.android.datastore 2 | 3 | 4 | data class UserPreferences(val color: Int) -------------------------------------------------------------------------------- /app/src/main/java/com/shalu/android/datastore/manager/MigrationManager.kt: -------------------------------------------------------------------------------- 1 | package com.shalu.android.datastore.manager 2 | 3 | import android.content.Context 4 | import androidx.datastore.preferences.SharedPreferencesMigration 5 | import androidx.datastore.preferences.createDataStore 6 | import androidx.datastore.preferences.emptyPreferences 7 | import androidx.datastore.preferences.preferencesKey 8 | import com.shalu.android.datastore.pref.UserDetails 9 | import kotlinx.coroutines.flow.Flow 10 | import kotlinx.coroutines.flow.catch 11 | import kotlinx.coroutines.flow.map 12 | import java.io.IOException 13 | 14 | class MigrationManager(context: Context) { 15 | val dataStore = context.createDataStore( 16 | name = USER_PREFERENCES_NAME, 17 | migrations = listOf(SharedPreferencesMigration(context, 18 | USER_PREFERENCES_NAME 19 | )) 20 | ) 21 | 22 | val userDetailsFlow: Flow = dataStore.data 23 | .catch { exception -> 24 | if (exception is IOException) { 25 | emit(emptyPreferences()) 26 | } else { 27 | throw exception 28 | } 29 | }.map { 30 | val firstName = it[PREF_FIRST_NAME] ?: "testFirstName" 31 | val lastName = it[PREF_LAST_NAME] ?: "testLastName" 32 | UserDetails(firstName, lastName) 33 | } 34 | 35 | companion object { 36 | const val USER_PREFERENCES_NAME = "my_preference" 37 | const val USER_FIRST_NAME = "my_firstname" 38 | const val USER_LAST_NAME = "my_lastname" 39 | 40 | val PREF_FIRST_NAME = preferencesKey(USER_FIRST_NAME) 41 | val PREF_LAST_NAME = preferencesKey(USER_LAST_NAME) 42 | } 43 | } -------------------------------------------------------------------------------- /app/src/main/java/com/shalu/android/datastore/manager/PrefSettingsManager.kt: -------------------------------------------------------------------------------- 1 | package com.shalu.android.datastore.manager 2 | 3 | import android.content.Context 4 | import androidx.datastore.preferences.* 5 | import com.shalu.android.datastore.UserPreferences 6 | import kotlinx.coroutines.flow.Flow 7 | import kotlinx.coroutines.flow.catch 8 | import kotlinx.coroutines.flow.map 9 | import java.io.IOException 10 | 11 | class PrefSettingsManager(val context: Context) { 12 | private val dataStore = context.createDataStore(SETTINGS_PREF) 13 | 14 | val userPreferencesFlow: Flow = dataStore.data 15 | .catch { exception -> 16 | if (exception is IOException) { 17 | emit(emptyPreferences()) 18 | } else { 19 | throw exception 20 | } 21 | }.map { preferences -> 22 | val color = preferences[BG_COLOR]?: android.R.color.white 23 | UserPreferences(color) 24 | } 25 | 26 | suspend fun updateColor(color: Int) { 27 | dataStore.edit { preferences -> 28 | preferences[BG_COLOR] = color 29 | } 30 | } 31 | 32 | companion object { 33 | val BG_COLOR = preferencesKey("background_color") 34 | const val SETTINGS_PREF = "settings_pref" 35 | } 36 | } -------------------------------------------------------------------------------- /app/src/main/java/com/shalu/android/datastore/manager/ProtoSettingsManager.kt: -------------------------------------------------------------------------------- 1 | package com.shalu.android.datastore.manager 2 | 3 | import android.content.Context 4 | import androidx.datastore.DataStore 5 | import androidx.datastore.createDataStore 6 | import com.shalu.android.datastore.UserSettings 7 | import com.shalu.android.datastore.proto.ProtoSettingsSerializer 8 | import kotlinx.coroutines.flow.Flow 9 | import kotlinx.coroutines.flow.catch 10 | import java.io.IOException 11 | 12 | class ProtoSettingsManager(val context: Context) { 13 | private val dataStore: DataStore = 14 | context.createDataStore(fileName = "user_settings.pb", serializer = ProtoSettingsSerializer) 15 | 16 | val userSettingsFlow: Flow = dataStore.data.catch { ex -> 17 | if (ex is IOException) { 18 | emit(UserSettings.getDefaultInstance()) 19 | } else { 20 | throw ex 21 | } 22 | } 23 | suspend fun updateColor(bgColor: UserSettings.BgColor) { 24 | dataStore.updateData { userSetting -> 25 | userSetting.toBuilder().setBgColor(bgColor).build() 26 | } 27 | } 28 | } -------------------------------------------------------------------------------- /app/src/main/java/com/shalu/android/datastore/pref/MigrateToPrefDataStoreActivity.kt: -------------------------------------------------------------------------------- 1 | package com.shalu.android.datastore.pref 2 | 3 | import android.annotation.SuppressLint 4 | import android.content.Context 5 | import android.content.SharedPreferences 6 | import android.os.Bundle 7 | import android.view.View 8 | import android.widget.TextView 9 | import androidx.appcompat.app.AppCompatActivity 10 | import com.shalu.android.datastore.R 11 | import com.shalu.android.datastore.manager.MigrationManager 12 | import kotlinx.coroutines.Dispatchers 13 | import kotlinx.coroutines.GlobalScope 14 | import kotlinx.coroutines.flow.collect 15 | import kotlinx.coroutines.launch 16 | import kotlinx.coroutines.withContext 17 | 18 | class MigrateToPrefDataStoreActivity : AppCompatActivity() { 19 | 20 | private lateinit var migrationManager: MigrationManager 21 | private lateinit var sharedpreferences: SharedPreferences 22 | private lateinit var tvPrefFirstName: TextView 23 | private lateinit var tvPrefLastName: TextView 24 | private lateinit var tvDatastoreFirstName: TextView 25 | private lateinit var tvDatastoreLastName: TextView 26 | 27 | override fun onCreate(savedInstanceState: Bundle?) { 28 | super.onCreate(savedInstanceState) 29 | setContentView(R.layout.activity_migrate_datastore) 30 | title = getString(R.string.migrate_preferences_datastore) 31 | 32 | tvPrefFirstName = findViewById(R.id.tvPref_first_name) 33 | tvPrefLastName = findViewById(R.id.tvPref_last_name) 34 | tvDatastoreFirstName = findViewById(R.id.tv_datastore_first_name) 35 | tvDatastoreLastName = findViewById(R.id.tv_datastore_last_name) 36 | 37 | setSharedPreferenceData("Roberto", "Bagio") 38 | 39 | findViewById(R.id.btnMigrate).setOnClickListener { 40 | migrateToPreferencesDataStore() 41 | } 42 | } 43 | 44 | private fun setSharedPreferenceData(firstName: String, lastName: String) { 45 | sharedpreferences = getSharedPreferences( 46 | MigrationManager.USER_PREFERENCES_NAME, 47 | Context.MODE_PRIVATE 48 | ) 49 | sharedpreferences.edit().putString(MigrationManager.USER_FIRST_NAME, firstName).putString( 50 | MigrationManager.USER_LAST_NAME, lastName 51 | ).apply() 52 | 53 | displaySharedPreferenceData() 54 | } 55 | 56 | @SuppressLint("SetTextI18n") 57 | private fun displaySharedPreferenceData() { 58 | // verify data exists in shared preferences 59 | tvPrefFirstName.text = 60 | "FirstName : ${sharedpreferences.getString(MigrationManager.USER_FIRST_NAME, "")}" 61 | tvPrefLastName.text = 62 | "LstName : ${sharedpreferences.getString(MigrationManager.USER_LAST_NAME, "")}" 63 | } 64 | 65 | @SuppressLint("SetTextI18n") 66 | private fun migrateToPreferencesDataStore() { 67 | migrationManager = MigrationManager(this) 68 | 69 | GlobalScope.launch { 70 | migrationManager.userDetailsFlow.collect { 71 | withContext(Dispatchers.Main) { 72 | //Update UI 73 | tvDatastoreFirstName.text = "FirstName : ${it.firstName}" 74 | tvDatastoreLastName.text = "LastName : ${it.lastName}" 75 | displaySharedPreferenceData(); 76 | } 77 | } 78 | } 79 | } 80 | } -------------------------------------------------------------------------------- /app/src/main/java/com/shalu/android/datastore/pref/PrefDataStoreActivity.kt: -------------------------------------------------------------------------------- 1 | package com.shalu.android.datastore.pref 2 | 3 | import android.os.Bundle 4 | import android.view.View 5 | import androidx.appcompat.app.AppCompatActivity 6 | import androidx.core.content.ContextCompat 7 | import com.shalu.android.datastore.R 8 | import com.shalu.android.datastore.manager.PrefSettingsManager 9 | import kotlinx.coroutines.GlobalScope 10 | import kotlinx.coroutines.flow.collect 11 | import kotlinx.coroutines.launch 12 | 13 | class PrefDataStoreActivity : AppCompatActivity() { 14 | 15 | private lateinit var prefSettingsManager: PrefSettingsManager 16 | private lateinit var outerView: View 17 | 18 | override fun onCreate(savedInstanceState: Bundle?) { 19 | super.onCreate(savedInstanceState) 20 | setContentView(R.layout.activity_datastore) 21 | title = getString(R.string.preferences_datastore) 22 | outerView = findViewById(R.id.outerView) 23 | 24 | prefSettingsManager = 25 | PrefSettingsManager(this) 26 | 27 | readSettings() 28 | 29 | findViewById(R.id.btnWhite).setOnClickListener { 30 | updateSettings(android.R.color.white) 31 | } 32 | 33 | findViewById(R.id.btnBlack).setOnClickListener { 34 | updateSettings(android.R.color.black) 35 | } 36 | } 37 | 38 | fun updateSettings(color: Int) { 39 | GlobalScope.launch { 40 | prefSettingsManager.updateColor(color) 41 | } 42 | } 43 | 44 | fun readSettings() { 45 | GlobalScope.launch { 46 | prefSettingsManager.userPreferencesFlow.collect { 47 | outerView.setBackgroundColor( 48 | ContextCompat.getColor( 49 | this@PrefDataStoreActivity, 50 | it.color 51 | ) 52 | ) 53 | } 54 | } 55 | } 56 | } -------------------------------------------------------------------------------- /app/src/main/java/com/shalu/android/datastore/pref/UserDetails.kt: -------------------------------------------------------------------------------- 1 | package com.shalu.android.datastore.pref 2 | 3 | data class UserDetails( 4 | val firstName: String, 5 | val lastName: String 6 | ) -------------------------------------------------------------------------------- /app/src/main/java/com/shalu/android/datastore/proto/ProtoDataStoreActivity.kt: -------------------------------------------------------------------------------- 1 | package com.shalu.android.datastore.proto 2 | 3 | import android.os.Bundle 4 | import android.view.View 5 | import androidx.appcompat.app.AppCompatActivity 6 | import androidx.core.content.ContextCompat 7 | import com.shalu.android.datastore.R 8 | import com.shalu.android.datastore.UserSettings 9 | import com.shalu.android.datastore.manager.ProtoSettingsManager 10 | import kotlinx.coroutines.GlobalScope 11 | import kotlinx.coroutines.flow.collect 12 | import kotlinx.coroutines.launch 13 | 14 | class ProtoDataStoreActivity : AppCompatActivity() { 15 | 16 | private lateinit var protoSettingsManager: ProtoSettingsManager 17 | private lateinit var outerView: View 18 | 19 | override fun onCreate(savedInstanceState: Bundle?) { 20 | super.onCreate(savedInstanceState) 21 | setContentView(R.layout.activity_datastore) 22 | title = getString(R.string.proto_datastore) 23 | outerView = findViewById(R.id.outerView) 24 | 25 | protoSettingsManager = 26 | ProtoSettingsManager(this) 27 | 28 | readSettings() 29 | 30 | findViewById(R.id.btnWhite).setOnClickListener { 31 | updateSettings(UserSettings.BgColor.COLOR_WHITE) 32 | } 33 | 34 | findViewById(R.id.btnBlack).setOnClickListener { 35 | updateSettings(UserSettings.BgColor.COLOR_BLACK) 36 | } 37 | } 38 | 39 | fun updateSettings(bgColor: UserSettings.BgColor) { 40 | GlobalScope.launch { 41 | protoSettingsManager.updateColor(bgColor) 42 | } 43 | } 44 | 45 | fun readSettings() { 46 | GlobalScope.launch { 47 | protoSettingsManager.userSettingsFlow.collect { 48 | when(it.bgColor) { 49 | UserSettings.BgColor.COLOR_WHITE -> outerView.setBackgroundColor( 50 | ContextCompat.getColor( 51 | this@ProtoDataStoreActivity, 52 | android.R.color.white 53 | ) 54 | ) 55 | UserSettings.BgColor.COLOR_BLACK -> outerView.setBackgroundColor( 56 | ContextCompat.getColor( 57 | this@ProtoDataStoreActivity, 58 | android.R.color.black 59 | ) 60 | ) 61 | else -> { 62 | // do nothing 63 | } 64 | } 65 | } 66 | } 67 | } 68 | } -------------------------------------------------------------------------------- /app/src/main/java/com/shalu/android/datastore/proto/ProtoSettingsSerializer.kt: -------------------------------------------------------------------------------- 1 | package com.shalu.android.datastore.proto 2 | 3 | import androidx.datastore.CorruptionException 4 | import androidx.datastore.Serializer 5 | import com.google.protobuf.InvalidProtocolBufferException 6 | import com.shalu.android.datastore.UserSettings 7 | import java.io.InputStream 8 | import java.io.OutputStream 9 | 10 | 11 | object ProtoSettingsSerializer : Serializer { 12 | 13 | override fun readFrom(input: InputStream): UserSettings { 14 | try { 15 | return UserSettings.parseFrom(input) 16 | } catch (ex: InvalidProtocolBufferException) { 17 | throw CorruptionException("Cannot read proto.", ex) 18 | } 19 | } 20 | 21 | override fun writeTo(t: UserSettings, output: OutputStream) { 22 | t.writeTo(output) 23 | } 24 | 25 | } -------------------------------------------------------------------------------- /app/src/main/proto/user_info.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | 3 | option java_package = "com.shalu.android.datastore"; 4 | option java_multiple_files = true; 5 | 6 | message UserSettings { 7 | 8 | enum BgColor { 9 | COLOR_WHITE = 0; 10 | COLOR_BLACK = 1; 11 | } 12 | BgColor bgColor = 1; 13 | } -------------------------------------------------------------------------------- /app/src/main/res/drawable-v24/ic_launcher_foreground.xml: -------------------------------------------------------------------------------- 1 | 7 | 8 | 9 | 15 | 18 | 21 | 22 | 23 | 24 | 30 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_launcher_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 10 | 15 | 20 | 25 | 30 | 35 | 40 | 45 | 50 | 55 | 60 | 65 | 70 | 75 | 80 | 85 | 90 | 95 | 100 | 105 | 110 | 115 | 120 | 125 | 130 | 135 | 140 | 145 | 150 | 155 | 160 | 165 | 170 | 171 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_datastore.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 |