├── .circleci
└── config.yml
├── .gitignore
├── LICENSE
├── README.md
├── app
├── .gitignore
├── build.gradle
├── proguard-rules.pro
└── src
│ ├── main
│ ├── AndroidManifest.xml
│ ├── java
│ │ └── io
│ │ │ └── flatcircle
│ │ │ └── coroutinehelperexample
│ │ │ └── MainActivity.kt
│ └── res
│ │ ├── drawable-v24
│ │ └── ic_launcher_foreground.xml
│ │ ├── drawable
│ │ └── ic_launcher_background.xml
│ │ ├── layout
│ │ ├── activity_main.xml
│ │ └── content_main.xml
│ │ ├── menu
│ │ └── menu_main.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
│ │ ├── dimens.xml
│ │ ├── strings.xml
│ │ └── styles.xml
│ └── test
│ └── java
│ └── io
│ └── flatcircle
│ └── coroutinehelperexample
│ └── ExampleUnitTest.kt
├── build.gradle
├── coroutinehelper
├── .gitignore
├── build.gradle
├── proguard-rules.pro
└── src
│ ├── main
│ ├── AndroidManifest.xml
│ ├── java
│ │ └── io
│ │ │ └── flatcircle
│ │ │ └── coroutinehelper
│ │ │ ├── ApiResult.kt
│ │ │ ├── CallbackFlattening.kt
│ │ │ └── CoroutineScopeDelegates.kt
│ └── res
│ │ └── values
│ │ └── strings.xml
│ └── test
│ └── java
│ └── io
│ └── flatcircle
│ └── coroutinehelper
│ └── ExampleUnitTest.java
├── gradle
└── wrapper
│ ├── gradle-wrapper.jar
│ └── gradle-wrapper.properties
├── gradlew
├── gradlew.bat
├── ktlint.gradle
└── settings.gradle
/.circleci/config.yml:
--------------------------------------------------------------------------------
1 | version: 2
2 | jobs:
3 | prepare:
4 | working_directory: ~/code
5 | docker:
6 | - image: circleci/android:api-28-alpha
7 | environment:
8 | JVM_OPTS: -Xmx3g
9 | steps:
10 | - checkout
11 | - restore_cache:
12 | key: gradle-{{ checksum "build.gradle" }}
13 | - run:
14 | name: Download dependencies
15 | command: ./gradlew androidDependencies
16 | - save_cache:
17 | paths:
18 | - ~/.gradle
19 | key: gradle-{{ checksum "build.gradle" }}
20 | destination: reports
21 |
22 | ktlint:
23 | working_directory: ~/code
24 | docker:
25 | - image: circleci/android:api-28-alpha
26 | environment:
27 | JVM_OPTS: -Xmx3g
28 | steps:
29 | - checkout
30 | - restore_cache:
31 | key: jars-{{ checksum "build.gradle" }}-{{ checksum "app/build.gradle" }}
32 | - run:
33 | name: Run ktlint
34 | command: ./gradlew ktlint
35 |
36 | tests:
37 | working_directory: ~/code
38 | docker:
39 | - image: circleci/android:api-28-alpha
40 | environment:
41 | JVM_OPTS: -Xmx3g
42 | steps:
43 | - checkout
44 | - restore_cache:
45 | key: jars-{{ checksum "build.gradle" }}-{{ checksum "app/build.gradle" }}
46 | - run:
47 | name: Run Tests
48 | command: ./gradlew testDebugUnitTest -x lint
49 | - store_artifacts:
50 | path: app/build/reports
51 | destination: reports
52 | - store_test_results:
53 | path: app/build/test-results
54 |
55 | deploy:
56 | working_directory: ~/code
57 | docker:
58 | - image: circleci/android:api-28-alpha
59 | environment:
60 | JVM_OPTS: -Xmx3g
61 | steps:
62 | - checkout
63 | - restore_cache:
64 | key: jars-{{ checksum "build.gradle" }}-{{ checksum "app/build.gradle" }}
65 | - run:
66 | name: Publish to Bintray
67 | command: ./gradlew build bintrayUpload -PdryRun=false
68 |
69 | workflows:
70 | version: 2
71 | build-and-deploy:
72 | jobs:
73 | - prepare
74 | - ktlint:
75 | requires:
76 | - prepare
77 | - tests:
78 | requires:
79 | - prepare
80 | - hold:
81 | type: approval
82 | requires:
83 | - ktlint
84 | - tests
85 | filters:
86 | branches:
87 | only:
88 | - master
89 | - /release\/.*/ # all branches starting with release/
90 | - deploy:
91 | requires:
92 | - hold
93 | filters:
94 | branches:
95 | only:
96 | - master
97 | - /release\/.*/ # all branches starting with release/
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | *.iml
2 | .gradle
3 | /local.properties
4 | /.idea/navEditor.xml
5 | /.idea/assetWizardSettings.xml
6 | .DS_Store
7 | /build
8 | /captures
9 | .externalNativeBuild
10 | /.idea/
11 | /gradle.properties
12 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2019 flatcircle
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # CoroutineHelper
2 | Extension functions, utility functions and delegates for Coroutines
3 |
4 | [](https://circleci.com/gh/flatcircle/CoroutineHelper) [  ](https://bintray.com/flatcircle/CoroutineHelper/coroutinehelper/_latestVersion)
5 |
6 | Installation
7 | --------
8 |
9 | ```groovy
10 | implementation 'io.flatcircle:coroutinehelper:{version}'
11 | ```
12 |
13 | Delegates
14 | -----
15 |
16 | The library comes with a few delegate functions to make declaring your CoroutineScope easier
17 |
18 | | Delegate | Description | Example |
19 | | ------------- | ------------- | ------------- |
20 | | : CoroutineScope by MainCoroutineScope | Implements a coroutine scope in the class on the Main/UI Dispatcher | [Example](https://github.com/flatcircle/LiveDataHelper/blob/master/app/src/main/java/io/flatcircle/livedatahelperexample/MainActivity.kt#L34) |
21 | | : CoroutineScope by DefaultCoroutineScope | Implements a coroutine scope in the class on the Default Dispatcher | [Example](https://github.com/flatcircle/LiveDataHelper/blob/master/app/src/main/java/io/flatcircle/livedatahelperexample/MainActivity.kt#L34) |
22 | | : CoroutineScope by IoCoroutineScope | Implements a coroutine scope in the class on the IO Dispatcher | [Example](https://github.com/flatcircle/LiveDataHelper/blob/master/app/src/main/java/io/flatcircle/livedatahelperexample/MainActivity.kt#L34) |
23 |
24 | Railway Oriented Programming
25 | -----
26 |
27 | This library provides an ApiResult<> class with infix functions, to be used in place of the Kotlin Result<> class, much as described in the Kotlin [Railway Oriented Programming Post on ProAndroidDev](https://proandroiddev.com/railway-oriented-programming-in-kotlin-f1bceed399e5).
28 |
29 |
30 | ```kotlin
31 | // longRunningOperation is a function which returns an ApiResult<>
32 | longRunningOperation(100) then { apiResult ->
33 | if (apiResult.value > 80) {
34 | Failure(IllegalStateException("`then` can throw an exception and pass it down the line"))
35 | } else {
36 | apiResult
37 | }
38 | } onSuccess { value ->
39 | textView.text = "Result = $value"
40 | } onFail { throwable ->
41 | textView.text = "A failure happened, with details $throwable"
42 | }
43 | ```
44 |
45 | Callback Reduction
46 | -----
47 |
48 | The library also has some convenience functions for reducing callback hell.
49 |
50 | ```kotlin
51 | // The original callback function. This could be anything
52 | functionWithCallback1("hello") { result ->
53 | println(result)
54 | }
55 |
56 | // Using suspendAsync function
57 | val result = suspendAsync(::functionWithCallback1, "hello")
58 | println(result)
59 |
60 | // You can also call a function of an instance of a class with instance::method reference, like so
61 | val userInstance = User.getInstance()
62 | val result = suspendAsync(userInstance::functionWithCallback1, "hello")
63 | println(result)
64 |
65 | // Using infix notation
66 | val result = ::functionWithCallback1 suspendAndInvokeWith "hello"
67 | println(result)
68 |
69 | // You can have up to three inputs, but you have to pass in a Pair()/Triple when using infix
70 | val result = ::functionWithCallback2 suspendAndInvokeWith Pair("hello", 2)
71 | val result = ::functionWithCallback3 suspendAndInvokeWith Triple("Hello", 2, "goodbye")
72 | println(result)
73 |
74 | // You can have up to three outputs using suspendAndInvokeWith2/3, but you have to receive it as a pair/triple
75 | val (result1, result2) = ::functionWithCallback22 suspendAndInvokeWith2 Pair("hello", 2)
76 | val (result1, result2, result3) = ::functionWithCallback33 suspendAndInvokeWith3 Triple("hello", 2, "goodbye")
77 | println(result1 + result2 + result3)
78 |
79 | ```
80 |
81 | You can find some additional reading on the callback hell functions [in this blog post](https://jacquessmuts.github.io/post/callback_hell/)
82 |
--------------------------------------------------------------------------------
/app/.gitignore:
--------------------------------------------------------------------------------
1 | /build
2 |
--------------------------------------------------------------------------------
/app/build.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: 'com.android.application'
2 | apply plugin: 'kotlin-android'
3 | apply plugin: 'kotlin-android-extensions'
4 |
5 | android {
6 | compileSdkVersion 28
7 | defaultConfig {
8 | applicationId "io.flatcircle.coroutinehelperexample"
9 | minSdkVersion 19
10 | targetSdkVersion 28
11 | versionCode 1
12 | versionName "1.0"
13 | testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
14 | }
15 | buildTypes {
16 | release {
17 | minifyEnabled false
18 | proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
19 | }
20 | }
21 | }
22 |
23 | dependencies {
24 | implementation fileTree(dir: 'libs', include: ['*.jar'])
25 |
26 | implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:1.2.1"
27 | implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:1.2.1"
28 |
29 | implementation project(':coroutinehelper')
30 |
31 | implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
32 | implementation 'androidx.appcompat:appcompat:1.0.2'
33 | implementation 'androidx.core:core-ktx:1.0.2'
34 | implementation 'androidx.constraintlayout:constraintlayout:1.1.3'
35 | implementation 'com.google.android.material:material:1.0.0'
36 | testImplementation 'junit:junit:4.12'
37 | androidTestImplementation 'androidx.test:runner:1.1.1'
38 | androidTestImplementation 'androidx.test.espresso:espresso-core:3.1.1'
39 | }
40 |
--------------------------------------------------------------------------------
/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
22 |
--------------------------------------------------------------------------------
/app/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
12 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
--------------------------------------------------------------------------------
/app/src/main/java/io/flatcircle/coroutinehelperexample/MainActivity.kt:
--------------------------------------------------------------------------------
1 | package io.flatcircle.coroutinehelperexample
2 |
3 | import android.os.Bundle
4 | import android.view.Menu
5 | import android.view.MenuItem
6 | import androidx.appcompat.app.AppCompatActivity
7 | import io.flatcircle.coroutinehelper.ApiResult
8 | import io.flatcircle.coroutinehelper.Failure
9 | import io.flatcircle.coroutinehelper.Success
10 | import io.flatcircle.coroutinehelper.onFail
11 | import io.flatcircle.coroutinehelper.onSuccess
12 | import io.flatcircle.coroutinehelper.then
13 | import kotlinx.android.synthetic.main.activity_main.*
14 | import kotlinx.android.synthetic.main.content_main.*
15 | import kotlinx.coroutines.Dispatchers
16 | import kotlinx.coroutines.GlobalScope
17 | import kotlinx.coroutines.delay
18 | import kotlinx.coroutines.launch
19 | import java.net.SocketTimeoutException
20 | import kotlin.random.Random
21 |
22 | class MainActivity : AppCompatActivity() {
23 |
24 | override fun onCreate(savedInstanceState: Bundle?) {
25 | super.onCreate(savedInstanceState)
26 | setContentView(R.layout.activity_main)
27 | setSupportActionBar(toolbar)
28 |
29 | fab.setOnClickListener { view ->
30 | launchLongRunningOperation()
31 | }
32 |
33 | launchLongRunningOperation()
34 | }
35 |
36 | fun launchLongRunningOperation() {
37 | // Not the right way to launch a coroutine, but useful for demonstrative purposes
38 | GlobalScope.launch(Dispatchers.Main) {
39 |
40 | longRunningOperation(100) then { apiResult ->
41 | if (apiResult.value > 80) {
42 | Failure(IllegalStateException("`then` can throw an exception and pass it down the line"))
43 | } else {
44 | apiResult
45 | }
46 | } onSuccess { value ->
47 | textView.text = "Result = $value"
48 | } onFail { throwable ->
49 | textView.text = "A failure happened, with details $throwable"
50 | }
51 | }
52 | }
53 |
54 | override fun onCreateOptionsMenu(menu: Menu): Boolean {
55 | // Inflate the menu; this adds items to the action bar if it is present.
56 | menuInflater.inflate(R.menu.menu_main, menu)
57 | return true
58 | }
59 |
60 | override fun onOptionsItemSelected(item: MenuItem): Boolean {
61 | // Handle action bar item clicks here. The action bar will
62 | // automatically handle clicks on the Home/Up button, so long
63 | // as you specify a parent activity in AndroidManifest.xml.
64 | return when (item.itemId) {
65 | R.id.action_settings -> true
66 | else -> super.onOptionsItemSelected(item)
67 | }
68 | }
69 |
70 | suspend fun longRunningOperation(input: Int): ApiResult {
71 | delay(5000)
72 | val value = Random.nextInt(0, input*2)
73 |
74 | return if (value > input) {
75 | Success(value)
76 | } else {
77 | Failure(SocketTimeoutException("This could be any exception. Faking an api timeout here."))
78 | }
79 | }
80 | }
81 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable-v24/ic_launcher_foreground.xml:
--------------------------------------------------------------------------------
1 |
7 |
12 |
13 |
19 |
22 |
25 |
26 |
27 |
28 |
34 |
35 |
--------------------------------------------------------------------------------
/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_main.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
9 |
13 |
14 |
20 |
21 |
22 |
23 |
24 |
25 |
32 |
33 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/content_main.xml:
--------------------------------------------------------------------------------
1 |
2 |
10 |
11 |
20 |
21 |
--------------------------------------------------------------------------------
/app/src/main/res/menu/menu_main.xml:
--------------------------------------------------------------------------------
1 |
11 |
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-hdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/flatcircle/CoroutineHelper/b2fb007ba3d26efb689ec33f313312fad7581855/app/src/main/res/mipmap-hdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-hdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/flatcircle/CoroutineHelper/b2fb007ba3d26efb689ec33f313312fad7581855/app/src/main/res/mipmap-hdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-mdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/flatcircle/CoroutineHelper/b2fb007ba3d26efb689ec33f313312fad7581855/app/src/main/res/mipmap-mdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-mdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/flatcircle/CoroutineHelper/b2fb007ba3d26efb689ec33f313312fad7581855/app/src/main/res/mipmap-mdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/flatcircle/CoroutineHelper/b2fb007ba3d26efb689ec33f313312fad7581855/app/src/main/res/mipmap-xhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/flatcircle/CoroutineHelper/b2fb007ba3d26efb689ec33f313312fad7581855/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/flatcircle/CoroutineHelper/b2fb007ba3d26efb689ec33f313312fad7581855/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/flatcircle/CoroutineHelper/b2fb007ba3d26efb689ec33f313312fad7581855/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/flatcircle/CoroutineHelper/b2fb007ba3d26efb689ec33f313312fad7581855/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/flatcircle/CoroutineHelper/b2fb007ba3d26efb689ec33f313312fad7581855/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/values/colors.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | #008577
4 | #00574B
5 | #D81B60
6 |
7 |
--------------------------------------------------------------------------------
/app/src/main/res/values/dimens.xml:
--------------------------------------------------------------------------------
1 |
2 | 16dp
3 |
4 |
--------------------------------------------------------------------------------
/app/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | CoroutineHelperExample
3 | Settings
4 |
5 |
--------------------------------------------------------------------------------
/app/src/main/res/values/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
10 |
11 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
--------------------------------------------------------------------------------
/app/src/test/java/io/flatcircle/coroutinehelperexample/ExampleUnitTest.kt:
--------------------------------------------------------------------------------
1 | package io.flatcircle.coroutinehelperexample
2 |
3 | import org.junit.Assert.assertEquals
4 | import org.junit.Test
5 |
6 | /**
7 | * Example local unit test, which will execute on the development machine (host).
8 | *
9 | * See [testing documentation](http://d.android.com/tools/testing).
10 | */
11 | class ExampleUnitTest {
12 | @Test
13 | fun addition_isCorrect() {
14 | assertEquals(4, 2 + 2)
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/build.gradle:
--------------------------------------------------------------------------------
1 | // Top-level build file where you can add configuration options common to all sub-projects/modules.
2 |
3 | buildscript {
4 | ext.kotlin_version = '1.3.31'
5 | repositories {
6 | google()
7 | jcenter()
8 |
9 | }
10 | dependencies {
11 | classpath 'com.android.tools.build:gradle:3.4.1'
12 | classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
13 | classpath 'com.novoda:bintray-release:0.9'
14 | // NOTE: Do not place your application dependencies here; they belong
15 | // in the individual module build.gradle files
16 | }
17 | }
18 |
19 | allprojects {
20 | repositories {
21 | google()
22 | jcenter()
23 | }
24 | apply from: "$rootDir/ktlint.gradle"
25 | }
26 |
27 | task clean(type: Delete) {
28 | delete rootProject.buildDir
29 | }
30 |
--------------------------------------------------------------------------------
/coroutinehelper/.gitignore:
--------------------------------------------------------------------------------
1 | /build
2 |
--------------------------------------------------------------------------------
/coroutinehelper/build.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: 'com.android.library'
2 | apply plugin: 'kotlin-android'
3 | apply plugin: 'com.novoda.bintray-release'
4 |
5 | android {
6 | compileSdkVersion 28
7 |
8 | defaultConfig {
9 | minSdkVersion 19
10 | targetSdkVersion 28
11 | versionCode 1
12 | versionName "1.0"
13 |
14 | testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
15 |
16 | }
17 |
18 | buildTypes {
19 | release {
20 | minifyEnabled false
21 | proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
22 | }
23 | }
24 | }
25 |
26 | publish {
27 | userOrg = 'flatcircle'
28 | groupId = 'io.flatcircle'
29 | repoName = 'CoroutineHelper'
30 | artifactId = 'coroutinehelper'
31 | publishVersion = '0.0.3'
32 | bintrayUser = 'chesterflatcircle'
33 | bintrayKey = System.getenv("BINTRAY_KEY")
34 | desc = 'A list of small kotlin extension functions, utility functions and delegates for Coroutines'
35 | website = 'https://github.com/flatcircle/coroutinehelper'
36 | }
37 |
38 | dependencies {
39 | implementation fileTree(dir: 'libs', include: ['*.jar'])
40 | implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
41 | implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:1.2.1"
42 | implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:1.2.1"
43 | testImplementation 'junit:junit:4.12'
44 | }
45 |
--------------------------------------------------------------------------------
/coroutinehelper/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
22 |
--------------------------------------------------------------------------------
/coroutinehelper/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
3 |
--------------------------------------------------------------------------------
/coroutinehelper/src/main/java/io/flatcircle/coroutinehelper/ApiResult.kt:
--------------------------------------------------------------------------------
1 | package io.flatcircle.coroutinehelper
2 |
3 | /**
4 | * Created by jacquessmuts on 2019-05-28
5 | * This class represent a result of an api call. It's a wrapper similar to Result<> in
6 | * Kotlin-experimental, or Swift. It's got the added benefit of doing a (context free) message
7 | * based on the exception it is provided.
8 | *
9 | * To handle the results in a railway manner, read more [here](https://proandroiddev.com/railway-oriented-programming-in-kotlin-f1bceed399e5)
10 | */
11 |
12 | sealed class ApiResult {
13 |
14 | companion object {
15 |
16 | /**
17 | * This maps a given ApiResult into the newly required ApiResult. You just have to pass it the (nullable)
18 | * successvalue.
19 | *
20 | * Please note that if you pass Failure result, this will return a Failure regardless of successValue
21 | */
22 | fun fromResult(originalResult: ApiResult<*>, successValue: T?): ApiResult {
23 | return if (originalResult is Success && successValue != null) {
24 | Success(successValue)
25 | } else if (originalResult is Failure) {
26 | Failure(originalResult.error)
27 | } else {
28 | Failure(IllegalArgumentException("Cannot convert $originalResult without successvalue, $successValue"))
29 | }
30 | }
31 |
32 | fun purposefullyLeftEmpty(): ApiResult {
33 | return Failure(PurposeFullyLeftEmptyException("This API call is not implemented on this specific client."))
34 | }
35 |
36 | fun notImplemented(): ApiResult {
37 | return Failure(NotImplementedError())
38 | }
39 |
40 | fun success(value: T): ApiResult {
41 | return Success(value)
42 | }
43 |
44 | fun fail(error: Throwable): ApiResult {
45 | return Failure(error)
46 | }
47 | }
48 |
49 | /**
50 | * Returns `true` if this instance represents successful outcome.
51 | * In this case [isFailure] returns `false`.
52 | */
53 | val isSuccess: Boolean get() = this !is Failure
54 |
55 | /**
56 | * Returns `true` if this instance represents failed outcome.
57 | * In this case [isSuccess] returns `false`.
58 | */
59 | val isFailure: Boolean get() = this is Failure
60 |
61 | // value & exception retrieval
62 |
63 | /**
64 | * Returns the encapsulated value if this instance represents [success][Result.isSuccess] or `null`
65 | * if it is [failure][Result.isFailure].
66 | */
67 | fun getOrNull(): T? =
68 | when (this) {
69 | is Failure -> null
70 | is Success -> value
71 | }
72 |
73 | /**
74 | * Returns the encapsulated exception if this instance represents [failure][isFailure] or `null`
75 | * if it is [success][isSuccess].
76 | *
77 | * This function is shorthand for `fold(onSuccess = { null }, onFailure = { it })` (see [fold]).
78 | */
79 | fun exceptionOrNull(): Throwable? =
80 | when (this) {
81 | is Failure -> error
82 | else -> null
83 | }
84 | }
85 | data class Success(val value: T) : ApiResult()
86 | data class Failure(val error: Throwable) : ApiResult()
87 |
88 | // -- extensions ---
89 |
90 | /**
91 | * Run the function to return the result, or pass on the failure
92 | */
93 | suspend infix fun ApiResult.then(function: suspend (Success) -> ApiResult): ApiResult =
94 | when (this) {
95 | is Success -> function(this)
96 | is Failure -> this
97 | }
98 |
99 | /**
100 | * Run the function to return the result, or pass on the failure
101 | */
102 | infix fun ApiResult.thenBlocking(function: (Success) -> ApiResult): ApiResult =
103 | when (this) {
104 | is Success -> function(this)
105 | is Failure -> this
106 | }
107 |
108 | /**
109 | * Run the function, and pass the value on regardless
110 | */
111 | suspend infix fun ApiResult.onSuccess(function: suspend (T) -> Unit): ApiResult =
112 | when (this) {
113 | is Success -> {
114 | function(this.value)
115 | this
116 | }
117 | is Failure -> this
118 | }
119 |
120 | /**
121 | * Run the function, and pass the value on regardless
122 | */
123 | infix fun ApiResult.onSuccessBlocking(function: (T) -> Unit): ApiResult =
124 | when (this) {
125 | is Success -> {
126 | function(this.value)
127 | this
128 | }
129 | is Failure -> this
130 | }
131 |
132 | /**
133 | * Pass on the success, or handle the failure
134 | */
135 | suspend infix fun ApiResult.onFail(function: suspend (Throwable) -> Unit): ApiResult =
136 | when (this) {
137 | is Success -> this
138 | is Failure -> {
139 | function(this.error)
140 | this
141 | }
142 | }
143 |
144 | /**
145 | * Run the function, and pass the value on
146 | */
147 | infix fun ApiResult.onFailBlocking(function: (Throwable) -> Unit): ApiResult =
148 | when (this) {
149 | is Success -> this
150 | is Failure -> {
151 | function(this.error)
152 | this
153 | }
154 | }
155 |
156 | /**
157 | * do something regardless of success or failure
158 | */
159 | suspend infix fun ApiResult.finally(function: suspend (ApiResult) -> Unit): ApiResult {
160 | function(this)
161 | return this
162 | }
163 |
164 | /**
165 | * do something regardless of success or failure
166 | */
167 | infix fun ApiResult.finallyBlocking(function: (ApiResult) -> Unit): ApiResult {
168 | function(this)
169 | return this
170 | }
171 |
172 | // infix fun T.to(function: (T) -> ApiResult) = Success(this).then(function)
173 |
174 | class PurposeFullyLeftEmptyException(override val message: String?) : Exception()
175 |
--------------------------------------------------------------------------------
/coroutinehelper/src/main/java/io/flatcircle/coroutinehelper/CallbackFlattening.kt:
--------------------------------------------------------------------------------
1 | package io.flatcircle.coroutinehelper
2 |
3 | import kotlin.coroutines.resume
4 | import kotlin.coroutines.suspendCoroutine
5 |
6 | /**
7 | * Created by jacquessmuts on 2019-05-21
8 | * Functions that assist with turning callback functions into suspendCoroutines
9 | */
10 |
11 | // Region One Output
12 | suspend fun suspendAsync(
13 | function: (Input, (Output) -> Unit) -> Unit,
14 | input: Input
15 | ) = suspendCoroutine