Skip to content

Commit 017e670

Browse files
committed
list activity skeleton added
1 parent e72edb3 commit 017e670

File tree

6 files changed

+428
-11
lines changed

6 files changed

+428
-11
lines changed

firebase-dataconnect/demo/src/main/AndroidManifest.xml

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ limitations under the License.
2222
<application
2323
android:name=".MyApplication"
2424
android:label="Data Connect Minimal Demo"
25-
android:theme="@style/Theme.AppCompat.NoActionBar"
25+
android:theme="@style/Theme.AppCompat"
2626
>
2727

2828
<activity android:name=".MainActivity" android:exported="true">
@@ -32,6 +32,8 @@ limitations under the License.
3232
</intent-filter>
3333
</activity>
3434

35+
<activity android:name=".ListItemsActivity" />
36+
3537
</application>
3638

3739
</manifest>
Lines changed: 166 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,166 @@
1+
/*
2+
* Copyright 2024 Google LLC
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.google.firebase.dataconnect.minimaldemo
17+
18+
import android.os.Bundle
19+
import android.view.Menu
20+
import android.view.MenuItem
21+
import android.widget.CompoundButton.OnCheckedChangeListener
22+
import androidx.activity.viewModels
23+
import androidx.appcompat.app.AppCompatActivity
24+
import androidx.lifecycle.Lifecycle
25+
import androidx.lifecycle.flowWithLifecycle
26+
import androidx.lifecycle.lifecycleScope
27+
import com.google.firebase.dataconnect.minimaldemo.ListItemsViewModel.OperationState
28+
import com.google.firebase.dataconnect.minimaldemo.databinding.ActivityMainBinding
29+
import kotlinx.coroutines.flow.collectLatest
30+
import kotlinx.coroutines.launch
31+
32+
class ListItemsActivity : AppCompatActivity() {
33+
34+
private lateinit var myApplication: MyApplication
35+
private lateinit var viewBinding: ActivityMainBinding
36+
private val viewModel: ListItemsViewModel by viewModels { ListItemsViewModel.Factory }
37+
38+
override fun onCreate(savedInstanceState: Bundle?) {
39+
super.onCreate(savedInstanceState)
40+
myApplication = application as MyApplication
41+
42+
viewBinding = ActivityMainBinding.inflate(layoutInflater)
43+
setContentView(viewBinding.root)
44+
45+
viewBinding.insertItemButton.setOnClickListener { viewModel.insertItem() }
46+
viewBinding.getItemButton.setOnClickListener { viewModel.getItem() }
47+
viewBinding.deleteItemButton.setOnClickListener { viewModel.deleteItem() }
48+
viewBinding.useEmulatorCheckBox.setOnCheckedChangeListener(useEmulatorOnCheckedChangeListener)
49+
viewBinding.debugLoggingCheckBox.setOnCheckedChangeListener(debugLoggingOnCheckedChangeListener)
50+
51+
lifecycleScope.launch {
52+
viewModel.stateSequenceNumber.flowWithLifecycle(lifecycle).collectLatest {
53+
onViewModelStateChange()
54+
}
55+
}
56+
}
57+
58+
override fun onCreateOptionsMenu(menu: Menu?): Boolean {
59+
menuInflater.inflate(R.menu.menu_main, menu)
60+
return true
61+
}
62+
63+
override fun onOptionsItemSelected(item: MenuItem): Boolean {
64+
return super.onOptionsItemSelected(item)
65+
}
66+
67+
override fun onResume() {
68+
super.onResume()
69+
lifecycleScope.launch {
70+
viewBinding.useEmulatorCheckBox.isChecked = myApplication.getUseDataConnectEmulator()
71+
viewBinding.debugLoggingCheckBox.isChecked = myApplication.getDataConnectDebugLoggingEnabled()
72+
}
73+
}
74+
75+
private fun onViewModelStateChange() {
76+
viewBinding.progressText.text = viewModel.progressText
77+
viewBinding.insertItemButton.isEnabled = !viewModel.isInsertOperationInProgress
78+
viewBinding.getItemButton.isEnabled =
79+
viewModel.isGetOperationRunnable && !viewModel.isGetOperationInProgress
80+
viewBinding.deleteItemButton.isEnabled =
81+
viewModel.isDeleteOperationRunnable && !viewModel.isDeleteOperationInProgress
82+
}
83+
84+
private val debugLoggingOnCheckedChangeListener = OnCheckedChangeListener { _, isChecked ->
85+
if (!lifecycle.currentState.isAtLeast(Lifecycle.State.RESUMED)) {
86+
return@OnCheckedChangeListener
87+
}
88+
myApplication.coroutineScope.launch {
89+
myApplication.setDataConnectDebugLoggingEnabled(isChecked)
90+
}
91+
}
92+
93+
private val useEmulatorOnCheckedChangeListener = OnCheckedChangeListener { _, isChecked ->
94+
if (!lifecycle.currentState.isAtLeast(Lifecycle.State.RESUMED)) {
95+
return@OnCheckedChangeListener
96+
}
97+
myApplication.coroutineScope.launch { myApplication.setUseDataConnectEmulator(isChecked) }
98+
}
99+
100+
companion object {
101+
102+
private val ListItemsViewModel.isInsertOperationInProgress: Boolean
103+
get() = insertState is OperationState.InProgress
104+
105+
private val ListItemsViewModel.isGetOperationInProgress: Boolean
106+
get() = getState is OperationState.InProgress
107+
108+
private val ListItemsViewModel.isDeleteOperationInProgress: Boolean
109+
get() = deleteState is OperationState.InProgress
110+
111+
private val ListItemsViewModel.isGetOperationRunnable: Boolean
112+
get() = lastInsertedKey !== null
113+
114+
private val ListItemsViewModel.isDeleteOperationRunnable: Boolean
115+
get() = lastInsertedKey !== null
116+
117+
private val ListItemsViewModel.progressText: String?
118+
get() {
119+
// Save properties to local variables to enable Kotlin's type narrowing in the "if" blocks
120+
// below.
121+
val insertState = insertState
122+
val getState = getState
123+
val deleteState = deleteState
124+
val state =
125+
listOfNotNull(insertState, getState, deleteState).maxByOrNull { it.sequenceNumber }
126+
127+
return if (state === null) {
128+
null
129+
} else if (state === insertState) {
130+
when (insertState) {
131+
is OperationState.InProgress ->
132+
"Inserting item: ${insertState.variables.toDisplayString()}"
133+
is OperationState.Completed ->
134+
insertState.result.fold(
135+
onSuccess = {
136+
"Inserted item with id=${it.id}:\n${insertState.variables.toDisplayString()}"
137+
},
138+
onFailure = { "Inserting item ${insertState.variables} FAILED: $it" },
139+
)
140+
}
141+
} else if (state === getState) {
142+
when (getState) {
143+
is OperationState.InProgress -> "Retrieving item with ID ${getState.variables.id}..."
144+
is OperationState.Completed ->
145+
getState.result.fold(
146+
onSuccess = {
147+
"Retrieved item with ID ${getState.variables.id}:\n${it?.toDisplayString()}"
148+
},
149+
onFailure = { "Retrieving item with ID ${getState.variables.id} FAILED: $it" },
150+
)
151+
}
152+
} else if (state === deleteState) {
153+
when (deleteState) {
154+
is OperationState.InProgress -> "Deleting item with ID ${deleteState.variables.id}..."
155+
is OperationState.Completed ->
156+
deleteState.result.fold(
157+
onSuccess = { "Deleted item with ID ${deleteState.variables.id}" },
158+
onFailure = { "Deleting item with ID ${deleteState.variables.id} FAILED: $it" },
159+
)
160+
}
161+
} else {
162+
throw RuntimeException("internal error: unknown state: $state (error code vp4rjptx6r)")
163+
}
164+
}
165+
}
166+
}
Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ import kotlinx.coroutines.flow.StateFlow
3939
import kotlinx.coroutines.flow.asStateFlow
4040
import kotlinx.coroutines.launch
4141

42-
class MainActivityViewModel(private val app: MyApplication) : ViewModel() {
42+
class ListItemsViewModel(private val app: MyApplication) : ViewModel() {
4343

4444
private val rs = RandomSource.default()
4545

@@ -218,7 +218,7 @@ class MainActivityViewModel(private val app: MyApplication) : ViewModel() {
218218
private const val TAG = "MainActivityViewModel"
219219

220220
val Factory: ViewModelProvider.Factory = viewModelFactory {
221-
initializer { MainActivityViewModel(this[APPLICATION_KEY] as MyApplication) }
221+
initializer { ListItemsViewModel(this[APPLICATION_KEY] as MyApplication) }
222222
}
223223
}
224224
}

firebase-dataconnect/demo/src/main/kotlin/com/google/firebase/dataconnect/minimaldemo/MainActivity.kt

Lines changed: 25 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -15,14 +15,17 @@
1515
*/
1616
package com.google.firebase.dataconnect.minimaldemo
1717

18+
import android.content.Intent
1819
import android.os.Bundle
20+
import android.view.Menu
21+
import android.view.MenuItem
1922
import android.widget.CompoundButton.OnCheckedChangeListener
2023
import androidx.activity.viewModels
2124
import androidx.appcompat.app.AppCompatActivity
2225
import androidx.lifecycle.Lifecycle
2326
import androidx.lifecycle.flowWithLifecycle
2427
import androidx.lifecycle.lifecycleScope
25-
import com.google.firebase.dataconnect.minimaldemo.MainActivityViewModel.OperationState
28+
import com.google.firebase.dataconnect.minimaldemo.MainViewModel.OperationState
2629
import com.google.firebase.dataconnect.minimaldemo.databinding.ActivityMainBinding
2730
import kotlinx.coroutines.flow.collectLatest
2831
import kotlinx.coroutines.launch
@@ -31,7 +34,7 @@ class MainActivity : AppCompatActivity() {
3134

3235
private lateinit var myApplication: MyApplication
3336
private lateinit var viewBinding: ActivityMainBinding
34-
private val viewModel: MainActivityViewModel by viewModels { MainActivityViewModel.Factory }
37+
private val viewModel: MainViewModel by viewModels { MainViewModel.Factory }
3538

3639
override fun onCreate(savedInstanceState: Bundle?) {
3740
super.onCreate(savedInstanceState)
@@ -53,6 +56,20 @@ class MainActivity : AppCompatActivity() {
5356
}
5457
}
5558

59+
override fun onCreateOptionsMenu(menu: Menu?): Boolean {
60+
menuInflater.inflate(R.menu.menu_main, menu)
61+
return true
62+
}
63+
64+
override fun onOptionsItemSelected(item: MenuItem): Boolean =
65+
when (item.itemId) {
66+
R.id.action_list -> {
67+
startActivity(Intent(this, ListItemsActivity::class.java))
68+
true
69+
}
70+
else -> super.onOptionsItemSelected(item)
71+
}
72+
5673
override fun onResume() {
5774
super.onResume()
5875
lifecycleScope.launch {
@@ -88,22 +105,22 @@ class MainActivity : AppCompatActivity() {
88105

89106
companion object {
90107

91-
private val MainActivityViewModel.isInsertOperationInProgress: Boolean
108+
private val MainViewModel.isInsertOperationInProgress: Boolean
92109
get() = insertState is OperationState.InProgress
93110

94-
private val MainActivityViewModel.isGetOperationInProgress: Boolean
111+
private val MainViewModel.isGetOperationInProgress: Boolean
95112
get() = getState is OperationState.InProgress
96113

97-
private val MainActivityViewModel.isDeleteOperationInProgress: Boolean
114+
private val MainViewModel.isDeleteOperationInProgress: Boolean
98115
get() = deleteState is OperationState.InProgress
99116

100-
private val MainActivityViewModel.isGetOperationRunnable: Boolean
117+
private val MainViewModel.isGetOperationRunnable: Boolean
101118
get() = lastInsertedKey !== null
102119

103-
private val MainActivityViewModel.isDeleteOperationRunnable: Boolean
120+
private val MainViewModel.isDeleteOperationRunnable: Boolean
104121
get() = lastInsertedKey !== null
105122

106-
private val MainActivityViewModel.progressText: String?
123+
private val MainViewModel.progressText: String?
107124
get() {
108125
// Save properties to local variables to enable Kotlin's type narrowing in the "if" blocks
109126
// below.

0 commit comments

Comments
 (0)