Sample app for datastore. This will be linked in the DataStore readme.
There are a lot of files here that were generated by android studio when creating the activity. My changes are in settings.proto, build.gradle and MainActivity.kt
Test: tested on my pixel 3
Bug: 157174209
Change-Id: Ic71f1448a75b103b427ae1e92a855c67c4ac47e2
diff --git a/datastore/datastore-sampleapp/.gitignore b/datastore/datastore-sampleapp/.gitignore
new file mode 100644
index 0000000..42afabf
--- /dev/null
+++ b/datastore/datastore-sampleapp/.gitignore
@@ -0,0 +1 @@
+/build
\ No newline at end of file
diff --git a/datastore/datastore-sampleapp/build.gradle b/datastore/datastore-sampleapp/build.gradle
new file mode 100644
index 0000000..0072845
--- /dev/null
+++ b/datastore/datastore-sampleapp/build.gradle
@@ -0,0 +1,94 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
+import static androidx.build.dependencies.DependenciesKt.CONSTRAINT_LAYOUT
+import static androidx.build.dependencies.DependenciesKt.MATERIAL
+import static androidx.build.dependencies.DependenciesKt.PROTOBUF_COMPILER
+import static androidx.build.dependencies.DependenciesKt.PROTOBUF_LITE
+import static androidx.build.dependencies.DependenciesKt.KOTLIN_STDLIB
+
+plugins {
+ id("AndroidXPlugin")
+ id('com.android.application')
+ id('kotlin-android')
+ id("com.google.protobuf")
+}
+
+android {
+ compileSdkVersion 29
+ buildToolsVersion "29.0.3"
+
+ defaultConfig {
+ applicationId "com.example.datastoresampleapp"
+ minSdkVersion 16
+ targetSdkVersion 29
+ versionCode 1
+ versionName "1.0"
+
+ testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
+ }
+
+ compileOptions {
+ sourceCompatibility JavaVersion.VERSION_1_8
+ targetCompatibility JavaVersion.VERSION_1_8
+ }
+ kotlinOptions {
+ jvmTarget = '1.8'
+ }
+}
+
+dependencies {
+ //For DataStore with Preferences
+ implementation(project(":datastore:datastore-preferences"))
+
+ // For DataStore with protos
+ implementation(project(":datastore:datastore-core"))
+ implementation(PROTOBUF_LITE)
+
+ implementation(KOTLIN_STDLIB)
+
+ implementation(MATERIAL)
+ implementation(CONSTRAINT_LAYOUT, { transitive = true })
+ implementation('androidx.navigation:navigation-fragment-ktx:2.2.2')
+ implementation project(":annotation:annotation-sampled")
+}
+
+protobuf {
+ protoc {
+ artifact = PROTOBUF_COMPILER
+ }
+
+ // Generates the java proto-lite code for the protos in this project. See
+ // https://github.com/google/protobuf-gradle-plugin#customizing-protobuf-compilation
+ // for more information.
+ generateProtoTasks {
+ all().each { task ->
+ task.builtins {
+ java {
+ option 'lite'
+ }
+ }
+ }
+ }
+}
+
+// Allow usage of Kotlin's @OptIn.
+tasks.withType(KotlinCompile).configureEach {
+ kotlinOptions {
+ freeCompilerArgs += ["-Xopt-in=kotlin.RequiresOptIn"]
+ }
+}
\ No newline at end of file
diff --git a/datastore/datastore-sampleapp/src/main/AndroidManifest.xml b/datastore/datastore-sampleapp/src/main/AndroidManifest.xml
new file mode 100644
index 0000000..e46b51a
--- /dev/null
+++ b/datastore/datastore-sampleapp/src/main/AndroidManifest.xml
@@ -0,0 +1,38 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.example.datastoresampleapp">
+
+ <application
+ android:allowBackup="true"
+ android:supportsRtl="true"
+ android:theme="@style/Theme.null">
+ <activity android:name=".PreferencesDataStoreActivity">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ <category android:name="android.intent.category.LAUNCHER" />
+ </intent-filter>
+ </activity>
+ <activity android:name=".ProtoDataStoreActivity">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ <category android:name="android.intent.category.LAUNCHER" />
+ </intent-filter>
+ </activity>
+ </application>
+</manifest>
\ No newline at end of file
diff --git a/datastore/datastore-sampleapp/src/main/java/com/example/datastoresampleapp/PreferencesDataStoreActivity.kt b/datastore/datastore-sampleapp/src/main/java/com/example/datastoresampleapp/PreferencesDataStoreActivity.kt
new file mode 100644
index 0000000..d36e21b
--- /dev/null
+++ b/datastore/datastore-sampleapp/src/main/java/com/example/datastoresampleapp/PreferencesDataStoreActivity.kt
@@ -0,0 +1,105 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.example.datastoresampleapp
+
+import android.os.Bundle
+import android.os.StrictMode
+import android.util.Log
+import android.widget.Button
+import android.widget.TextView
+import androidx.annotation.Sampled
+import androidx.appcompat.app.AppCompatActivity
+import androidx.datastore.DataStore
+import androidx.datastore.preferences.PreferenceDataStoreFactory
+import androidx.datastore.preferences.Preferences
+import androidx.lifecycle.lifecycleScope
+import kotlinx.coroutines.flow.catch
+import kotlinx.coroutines.flow.collect
+import kotlinx.coroutines.flow.distinctUntilChanged
+import kotlinx.coroutines.flow.map
+import kotlinx.coroutines.launch
+import java.io.File
+import java.io.IOException
+
+@OptIn(kotlinx.coroutines.ExperimentalCoroutinesApi::class)
+class PreferencesDataStoreActivity : AppCompatActivity() {
+ private val TAG = "PreferencesActivity"
+
+ private val PREFERENCE_STORE_FILE_NAME = "datastore_test_app.preferences_pb"
+ private val COUNTER_KEY = "counter"
+
+ private val preferenceStore: DataStore<Preferences> by lazy {
+ PreferenceDataStoreFactory().create(
+ { File(applicationContext.filesDir, PREFERENCE_STORE_FILE_NAME) })
+ }
+
+ @Sampled
+ override fun onCreate(savedInstanceState: Bundle?) {
+ // Strict mode allows us to check that no writes or reads are blocking the UI thread.
+ StrictMode.setThreadPolicy(
+ StrictMode.ThreadPolicy.Builder()
+ .detectDiskReads()
+ .detectDiskWrites()
+ .penaltyDeath()
+ .build()
+ )
+
+ super.onCreate(savedInstanceState)
+ setContentView(R.layout.activity_main)
+
+ setUpPreferenceStoreUi()
+ }
+
+ @Sampled
+ private fun setUpPreferenceStoreUi() {
+ // Using preferenceStore:
+ findViewById<Button>(R.id.counter_dec).setOnClickListener {
+ lifecycleScope.launch {
+ preferenceStore.updateData { currentPrefs ->
+ val currentValue = currentPrefs.getInt(COUNTER_KEY, defaultValue = 0)
+ currentPrefs.toBuilder().setInt(COUNTER_KEY, currentValue - 1).build()
+ }
+ }
+ }
+
+ findViewById<Button>(R.id.counter_inc).setOnClickListener {
+ lifecycleScope.launch {
+ preferenceStore.updateData { currentPrefs ->
+ val currentValue = currentPrefs.getInt(COUNTER_KEY, defaultValue = 0)
+ currentPrefs.toBuilder().setInt(COUNTER_KEY, currentValue + 1).build()
+ }
+ }
+ }
+
+ lifecycleScope.launch {
+ preferenceStore.data
+ .catch { e ->
+ if (e is IOException) {
+ Log.e(TAG, "Error reading preferences.", e)
+ emit(Preferences.empty())
+ } else {
+ throw e
+ }
+ }
+ .map { it.getInt(COUNTER_KEY, defaultValue = 0) }
+ .distinctUntilChanged()
+ .collect { counterValue ->
+ findViewById<TextView>(R.id.counter_text_view).text = counterValue.toString()
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/datastore/datastore-sampleapp/src/main/java/com/example/datastoresampleapp/ProtoDataStoreActivity.kt b/datastore/datastore-sampleapp/src/main/java/com/example/datastoresampleapp/ProtoDataStoreActivity.kt
new file mode 100644
index 0000000..57d6411
--- /dev/null
+++ b/datastore/datastore-sampleapp/src/main/java/com/example/datastoresampleapp/ProtoDataStoreActivity.kt
@@ -0,0 +1,120 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.example.datastoresampleapp
+
+import android.os.Bundle
+import android.os.StrictMode
+import android.util.Log
+import android.widget.Button
+import android.widget.TextView
+import androidx.annotation.Sampled
+import androidx.appcompat.app.AppCompatActivity
+import androidx.datastore.CorruptionException
+import androidx.datastore.DataStore
+import androidx.datastore.DataStoreFactory
+import androidx.datastore.Serializer
+import androidx.lifecycle.lifecycleScope
+import com.google.protobuf.InvalidProtocolBufferException
+import kotlinx.coroutines.flow.catch
+import kotlinx.coroutines.flow.collect
+import kotlinx.coroutines.flow.distinctUntilChanged
+import kotlinx.coroutines.flow.map
+import kotlinx.coroutines.launch
+import java.io.File
+import java.io.IOException
+import java.io.InputStream
+import java.io.OutputStream
+
+@OptIn(kotlinx.coroutines.ExperimentalCoroutinesApi::class)
+class ProtoDataStoreActivity : AppCompatActivity() {
+ private val TAG = "ProtoActivity"
+
+ private val PROTO_STORE_FILE_NAME = "datastore_test_app.pb"
+
+ private val settingsStore: DataStore<Settings> by lazy {
+ DataStoreFactory().create(
+ { File(applicationContext.filesDir, PROTO_STORE_FILE_NAME) },
+ SettingsSerializer
+ )
+ }
+
+ @Sampled
+ override fun onCreate(savedInstanceState: Bundle?) {
+ // Strict mode allows us to check that no writes or reads are blocking the UI thread.
+ StrictMode.setThreadPolicy(
+ StrictMode.ThreadPolicy.Builder()
+ .detectDiskReads()
+ .detectDiskWrites()
+ .penaltyDeath()
+ .build()
+ )
+
+ super.onCreate(savedInstanceState)
+ setContentView(R.layout.activity_main)
+
+ setUpProtoDataStoreUi()
+ }
+
+ @Sampled
+ private fun setUpProtoDataStoreUi() {
+ findViewById<Button>(R.id.counter_dec).setOnClickListener {
+ lifecycleScope.launch {
+ settingsStore.updateData { currentSettings ->
+ currentSettings.toBuilder().setCounter(currentSettings.counter - 1).build()
+ }
+ }
+ }
+
+ findViewById<Button>(R.id.counter_inc).setOnClickListener {
+ lifecycleScope.launch {
+ settingsStore.updateData { currentSettings ->
+ currentSettings.toBuilder().setCounter(currentSettings.counter + 1).build()
+ }
+ }
+ }
+
+ lifecycleScope.launch {
+ settingsStore.data
+ .catch { e ->
+ if (e is IOException) {
+ Log.e(TAG, "Error reading preferences.", e)
+ emit(Settings.getDefaultInstance())
+ } else {
+ throw e
+ }
+ }
+ .map { it.counter }
+ .distinctUntilChanged()
+ .collect { counterValue ->
+ findViewById<TextView>(R.id.counter_text_view).text =
+ counterValue.toString()
+ }
+ }
+ }
+
+ private object SettingsSerializer : Serializer<Settings> {
+ override fun readFrom(input: InputStream): Settings {
+ try {
+ return Settings.parseFrom(input)
+ } catch (ipbe: InvalidProtocolBufferException) {
+ throw CorruptionException("Cannot read proto.", ipbe)
+ }
+ }
+
+ override fun writeTo(t: Settings, output: OutputStream) = t.writeTo(output)
+ }
+}
\ No newline at end of file
diff --git a/datastore/datastore-sampleapp/src/main/proto/settings.proto b/datastore/datastore-sampleapp/src/main/proto/settings.proto
new file mode 100644
index 0000000..38fd074
--- /dev/null
+++ b/datastore/datastore-sampleapp/src/main/proto/settings.proto
@@ -0,0 +1,8 @@
+syntax = "proto3";
+
+option java_package = "com.example.datastoresampleapp";
+option java_multiple_files = true;
+
+message Settings {
+ int32 counter = 1;
+}
diff --git a/datastore/datastore-sampleapp/src/main/res/layout/activity_main.xml b/datastore/datastore-sampleapp/src/main/res/layout/activity_main.xml
new file mode 100644
index 0000000..0775572
--- /dev/null
+++ b/datastore/datastore-sampleapp/src/main/res/layout/activity_main.xml
@@ -0,0 +1,59 @@
+<?xml version="1.0" encoding="utf-8"?><!--
+ Copyright 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:app="http://schemas.android.com/apk/res-auto"
+ xmlns:tools="http://schemas.android.com/tools"
+ android:id="@+id/rootLayout"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ tools:context=".PreferencesDataStoreActivity">
+
+ <Button
+ android:id="@+id/counter_inc"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginEnd="16dp"
+ android:layout_marginRight="16dp"
+ android:layout_marginTop="16dp"
+ android:text="Counter++"
+ app:layout_constraintEnd_toEndOf="parent"
+ app:layout_constraintTop_toTopOf="parent" />
+
+ <Button
+ android:id="@+id/counter_dec"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginLeft="16dp"
+ android:layout_marginStart="16dp"
+ android:layout_marginTop="16dp"
+ android:text="Counter--"
+ app:layout_constraintStart_toStartOf="parent"
+ app:layout_constraintTop_toTopOf="parent" />
+
+ <TextView
+ android:id="@+id/counter_text_view"
+ android:layout_width="101dp"
+ android:layout_height="54dp"
+ android:layout_marginTop="16dp"
+ android:gravity="center"
+ android:text="Loading Counter"
+ android:textAppearance="@style/TextAppearance.AppCompat.Large"
+ app:layout_constraintEnd_toStartOf="@+id/counter_inc"
+ app:layout_constraintStart_toEndOf="@+id/counter_dec"
+ app:layout_constraintTop_toTopOf="parent" />
+
+</androidx.constraintlayout.widget.ConstraintLayout>
\ No newline at end of file
diff --git a/datastore/datastore-sampleapp/src/main/res/values/themes.xml b/datastore/datastore-sampleapp/src/main/res/values/themes.xml
new file mode 100644
index 0000000..826f8ba
--- /dev/null
+++ b/datastore/datastore-sampleapp/src/main/res/values/themes.xml
@@ -0,0 +1,20 @@
+<!--
+ Copyright 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources>
+ <style name="Theme.null" parent="Theme.AppCompat.DayNight.DarkActionBar">
+ </style>
+</resources>
\ No newline at end of file
diff --git a/settings.gradle b/settings.gradle
index a51cfa4..de7c1e69 100644
--- a/settings.gradle
+++ b/settings.gradle
@@ -138,6 +138,7 @@
includeProject(":datastore:datastore-core", "datastore/datastore-core")
includeProject(":datastore:datastore-preferences", "datastore/datastore-preferences")
includeProject(":datastore:datastore-proto", "datastore/datastore-proto")
+includeProject(":datastore:datastore-sampleapp", "datastore/datastore-sampleapp")
includeProject(":documentfile:documentfile", "documentfile/documentfile")
includeProject(":drawerlayout:drawerlayout", "drawerlayout/drawerlayout")
includeProject(":dynamicanimation:dynamicanimation", "dynamic-animation/dynamic-animation")