-
Notifications
You must be signed in to change notification settings - Fork 14
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
coroutine-flow-support: Add example to show how to safely collect flo…
…w. (EO)
- Loading branch information
emmano
committed
Sep 13, 2021
1 parent
fe71822
commit 3261255
Showing
35 changed files
with
510 additions
and
5 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
/build |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,55 @@ | ||
apply plugin: 'com.android.application' | ||
apply plugin: 'kotlin-android' | ||
apply plugin: "kotlin-parcelize" | ||
|
||
android { | ||
compileSdkVersion constants.compileSdk | ||
defaultConfig { | ||
applicationId "com.instacart.formula.stopwatch.coroutines" | ||
minSdkVersion constants.minSdk | ||
targetSdkVersion constants.targetSdk | ||
versionCode 1 | ||
versionName "1.0" | ||
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" | ||
} | ||
|
||
compileOptions { | ||
sourceCompatibility JavaVersion.VERSION_1_8 | ||
targetCompatibility JavaVersion.VERSION_1_8 | ||
} | ||
|
||
buildTypes { | ||
release { | ||
minifyEnabled false | ||
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' | ||
} | ||
} | ||
|
||
testOptions { | ||
unitTests { | ||
includeAndroidResources = true | ||
} | ||
} | ||
} | ||
|
||
tasks.withType(org.jetbrains.kotlin.gradle.tasks.KotlinCompile).all { | ||
kotlinOptions { | ||
jvmTarget = "1.8" | ||
} | ||
} | ||
|
||
dependencies { | ||
implementation project(":formula-coroutines") | ||
|
||
implementation libraries.kotlin | ||
implementation libraries.androidx.appcompat | ||
implementation libraries.androidx.lifecycle.runtimektx | ||
implementation libraries.androidx.activity.ktx | ||
|
||
testImplementation libraries.junit | ||
testImplementation "com.google.truth:truth:$truthVersion" | ||
|
||
testImplementation libraries.androidx.test.rules | ||
testImplementation libraries.androidx.test.runner | ||
testImplementation libraries.cororutinesTest | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
# Add project specific ProGuard rules here. | ||
# You can control the set of applied configuration files using the | ||
# proguardFiles setting in build.gradle. | ||
# | ||
# For more details, see | ||
# http://developer.android.com/guide/developing/tools/proguard.html | ||
|
||
# If your project uses WebView with JS, uncomment the following | ||
# and specify the fully qualified class name to the JavaScript interface | ||
# class: | ||
#-keepclassmembers class fqcn.of.javascript.interface.for.webview { | ||
# public *; | ||
#} | ||
|
||
# Uncomment this to preserve the line number information for | ||
# debugging stack traces. | ||
#-keepattributes SourceFile,LineNumberTable | ||
|
||
# If you keep the line number information, uncomment this to | ||
# hide the original source file name. | ||
#-renamesourcefileattribute SourceFile |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
<manifest xmlns:android="http://schemas.android.com/apk/res/android" | ||
package="com.instacart.formula.stopwatch"> | ||
|
||
<application android:allowBackup="true" | ||
android:label="@string/app_name" | ||
android:icon="@mipmap/ic_launcher" | ||
android:roundIcon="@mipmap/ic_launcher_round" | ||
android:supportsRtl="true" | ||
android:theme="@style/AppTheme"> | ||
|
||
<activity android:name=".StopwatchActivity"> | ||
<intent-filter> | ||
<action android:name="android.intent.action.MAIN" /> | ||
<category android:name="android.intent.category.LAUNCHER" /> | ||
</intent-filter> | ||
</activity> | ||
</application> | ||
</manifest> |
16 changes: 16 additions & 0 deletions
16
.../stopwatch-coroutines/src/main/java/com/instacart/formula/stopwatch/StopWatchViewModel.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
package com.instacart.formula.stopwatch | ||
|
||
import androidx.lifecycle.ViewModel | ||
import androidx.lifecycle.viewModelScope | ||
import com.instacart.formula.coroutines.toFlow | ||
import kotlinx.coroutines.flow.* | ||
|
||
class StopWatchViewModel(private val formula: StopwatchFormula = StopwatchFormula()) : ViewModel() { | ||
|
||
val rendererStream by lazy { | ||
formula.toFlow() //Let's imagine this flow uses a really expensive resource (i.e. Connectivity monitoring) | ||
//Cannot use .stateIn() since we do not have initial StopwatchRenderModel | ||
.shareIn(viewModelScope, SharingStarted.WhileSubscribed(), 0) | ||
.distinctUntilChanged() | ||
} | ||
} |
36 changes: 36 additions & 0 deletions
36
...s/stopwatch-coroutines/src/main/java/com/instacart/formula/stopwatch/StopwatchActivity.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,36 @@ | ||
package com.instacart.formula.stopwatch | ||
|
||
import android.os.Bundle | ||
import androidx.activity.viewModels | ||
import androidx.fragment.app.FragmentActivity | ||
import androidx.lifecycle.Lifecycle | ||
import androidx.lifecycle.lifecycleScope | ||
import androidx.lifecycle.repeatOnLifecycle | ||
import kotlinx.coroutines.flow.Flow | ||
import kotlinx.coroutines.flow.collect | ||
import kotlinx.coroutines.launch | ||
|
||
class StopwatchActivity : FragmentActivity() { | ||
|
||
private val counterViewModel by viewModels<StopWatchViewModel>() | ||
|
||
override fun onCreate(savedInstanceState: Bundle?) { | ||
super.onCreate(savedInstanceState) | ||
setContentView(R.layout.stopwatch_activity) | ||
|
||
val renderView = StopwatchRenderView(findViewById(R.id.activity_content)) | ||
|
||
val renderModels = counterViewModel.rendererStream | ||
|
||
renderModels.safeCollect { renderView.render(it) } | ||
} | ||
|
||
fun <T> Flow<T>.safeCollect(block: (T) -> Unit) = lifecycleScope.launch { | ||
repeatOnLifecycle(Lifecycle.State.STARTED) { | ||
this@safeCollect.collect { | ||
block(it) | ||
} | ||
} | ||
} | ||
|
||
} |
5 changes: 5 additions & 0 deletions
5
.../stopwatch-coroutines/src/main/java/com/instacart/formula/stopwatch/StopwatchAnalytics.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
package com.instacart.formula.stopwatch | ||
|
||
class StopwatchAnalytics { | ||
fun trackClick() = Unit | ||
} |
82 changes: 82 additions & 0 deletions
82
...es/stopwatch-coroutines/src/main/java/com/instacart/formula/stopwatch/StopwatchFormula.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,82 @@ | ||
package com.instacart.formula.stopwatch | ||
|
||
import com.instacart.formula.Evaluation | ||
import com.instacart.formula.Formula | ||
import com.instacart.formula.FormulaContext | ||
import com.instacart.formula.coroutines.FlowStream | ||
import kotlinx.coroutines.CoroutineScope | ||
import kotlinx.coroutines.GlobalScope | ||
import kotlinx.coroutines.MainScope | ||
import kotlinx.coroutines.delay | ||
import kotlinx.coroutines.flow.flow | ||
import kotlinx.coroutines.flow.flowOf | ||
import kotlinx.coroutines.flow.onEach | ||
import java.util.concurrent.TimeUnit | ||
|
||
class StopwatchFormula(private val scope: CoroutineScope = MainScope()) : Formula<Unit, StopwatchFormula.State, StopwatchRenderModel> { | ||
|
||
data class State( | ||
val timePassedInMillis: Long, | ||
val isRunning: Boolean | ||
) | ||
|
||
private val analytics = StopwatchAnalytics() | ||
|
||
override fun initialState(input: Unit): State = State( | ||
timePassedInMillis = 0, | ||
isRunning = true | ||
) | ||
|
||
override fun evaluate( | ||
input: Unit, | ||
state: State, | ||
context: FormulaContext<State> | ||
): Evaluation<StopwatchRenderModel> { | ||
return Evaluation( | ||
output = StopwatchRenderModel( | ||
timePassed = formatTimePassed(state.timePassedInMillis), | ||
), | ||
updates = context.updates { | ||
if (state.isRunning) { | ||
val incrementTimePassed = FlowStream.fromFlow(scope) { | ||
|
||
flow { | ||
while(true) { | ||
emit(Unit) | ||
delay(1) | ||
} | ||
} | ||
} | ||
|
||
incrementTimePassed.onEvent { | ||
transition(state.copy(timePassedInMillis = state.timePassedInMillis + 1)) | ||
} | ||
} | ||
} | ||
) | ||
} | ||
|
||
private fun formatTimePassed(timePassedInMillis: Long): String { | ||
return buildString { | ||
val minutesPassed = TimeUnit.MILLISECONDS.toMinutes(timePassedInMillis) | ||
if (minutesPassed > 0) { | ||
append(minutesPassed) | ||
append('m') | ||
append(' ') | ||
} | ||
|
||
val secondsPassed = TimeUnit.MILLISECONDS.toSeconds(timePassedInMillis) % 60 | ||
append(secondsPassed) | ||
append('s') | ||
append(' ') | ||
|
||
// Always show millis as two digits | ||
val millisPassed = (timePassedInMillis % 1000) / 10 | ||
if (millisPassed < 10) { | ||
append('0') | ||
} | ||
append(millisPassed) | ||
} | ||
} | ||
|
||
} |
6 changes: 6 additions & 0 deletions
6
...opwatch-coroutines/src/main/java/com/instacart/formula/stopwatch/StopwatchRenderModels.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
package com.instacart.formula.stopwatch | ||
|
||
data class StopwatchRenderModel( | ||
val timePassed: String | ||
) | ||
|
16 changes: 16 additions & 0 deletions
16
...stopwatch-coroutines/src/main/java/com/instacart/formula/stopwatch/StopwatchRenderView.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
package com.instacart.formula.stopwatch | ||
|
||
import android.view.ViewGroup | ||
import android.widget.Button | ||
import android.widget.TextView | ||
import com.instacart.formula.Renderer | ||
import com.instacart.formula.RenderView | ||
|
||
class StopwatchRenderView(root: ViewGroup) : RenderView<StopwatchRenderModel> { | ||
private val timePassed: TextView = root.findViewById(R.id.time_passed_text_view) | ||
|
||
override val render: Renderer<StopwatchRenderModel> = Renderer { model -> | ||
|
||
timePassed.text = model.timePassed | ||
} | ||
} |
34 changes: 34 additions & 0 deletions
34
samples/stopwatch-coroutines/src/main/res/drawable-v24/ic_launcher_foreground.xml
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,34 @@ | ||
<vector xmlns:android="http://schemas.android.com/apk/res/android" | ||
xmlns:aapt="http://schemas.android.com/aapt" | ||
android:width="108dp" | ||
android:height="108dp" | ||
android:viewportHeight="108" | ||
android:viewportWidth="108"> | ||
<path | ||
android:fillType="evenOdd" | ||
android:pathData="M32,64C32,64 38.39,52.99 44.13,50.95C51.37,48.37 70.14,49.57 70.14,49.57L108.26,87.69L108,109.01L75.97,107.97L32,64Z" | ||
android:strokeColor="#00000000" | ||
android:strokeWidth="1"> | ||
<aapt:attr name="android:fillColor"> | ||
<gradient | ||
android:endX="78.5885" | ||
android:endY="90.9159" | ||
android:startX="48.7653" | ||
android:startY="61.0927" | ||
android:type="linear"> | ||
<item | ||
android:color="#44000000" | ||
android:offset="0.0" /> | ||
<item | ||
android:color="#00000000" | ||
android:offset="1.0" /> | ||
</gradient> | ||
</aapt:attr> | ||
</path> | ||
<path | ||
android:fillColor="#FFFFFF" | ||
android:fillType="nonZero" | ||
android:pathData="M66.94,46.02L66.94,46.02C72.44,50.07 76,56.61 76,64L32,64C32,56.61 35.56,50.11 40.98,46.06L36.18,41.19C35.45,40.45 35.45,39.3 36.18,38.56C36.91,37.81 38.05,37.81 38.78,38.56L44.25,44.05C47.18,42.57 50.48,41.71 54,41.71C57.48,41.71 60.78,42.57 63.68,44.05L69.11,38.56C69.84,37.81 70.98,37.81 71.71,38.56C72.44,39.3 72.44,40.45 71.71,41.19L66.94,46.02ZM62.94,56.92C64.08,56.92 65,56.01 65,54.88C65,53.76 64.08,52.85 62.94,52.85C61.8,52.85 60.88,53.76 60.88,54.88C60.88,56.01 61.8,56.92 62.94,56.92ZM45.06,56.92C46.2,56.92 47.13,56.01 47.13,54.88C47.13,53.76 46.2,52.85 45.06,52.85C43.92,52.85 43,53.76 43,54.88C43,56.01 43.92,56.92 45.06,56.92Z" | ||
android:strokeColor="#00000000" | ||
android:strokeWidth="1" /> | ||
</vector> |
Oops, something went wrong.