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")