Skip to content

Commit 118ee99

Browse files
SUPERCILEXqwwdfsad
authored andcommitted
Integration module for Play Services Tasks API.
Fixes #62
1 parent 7f11d0e commit 118ee99

File tree

10 files changed

+430
-12
lines changed

10 files changed

+430
-12
lines changed

binary-compatibility-validator/build.gradle

+1
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ dependencies {
2121
testArtifacts project(':kotlinx-coroutines-guava')
2222
testArtifacts project(':kotlinx-coroutines-jdk8')
2323
testArtifacts project(':kotlinx-coroutines-slf4j')
24+
testArtifacts project(path: ':kotlinx-coroutines-play-services', configuration: 'default')
2425

2526
testArtifacts project(':kotlinx-coroutines-android')
2627
testArtifacts project(':kotlinx-coroutines-javafx')
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
public final class kotlinx/coroutines/experimental/tasks/TasksKt {
2+
public static final fun asDeferred (Lcom/google/android/gms/tasks/Task;)Lkotlinx/coroutines/experimental/Deferred;
3+
public static final fun asTask (Lkotlinx/coroutines/experimental/Deferred;)Lcom/google/android/gms/tasks/Task;
4+
public static final fun await (Lcom/google/android/gms/tasks/Task;Lkotlin/coroutines/experimental/Continuation;)Ljava/lang/Object;
5+
}
6+

integration/README.md

+1
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ Module name below corresponds to the artifact name in Maven/Gradle.
88
* [kotlinx-coroutines-jdk8](kotlinx-coroutines-jdk8/README.md) -- integration with JDK8 `CompletableFuture` (Android API level 24).
99
* [kotlinx-coroutines-guava](kotlinx-coroutines-guava/README.md) -- integration with Guava [ListenableFuture](https://github.com/google/guava/wiki/ListenableFutureExplained).
1010
* [kotlinx-coroutines-slf4j](kotlinx-coroutines-slf4j/README.md) -- integration with SLF4J [MDC](https://logback.qos.ch/manual/mdc.html).
11+
* [kotlinx-coroutines-play-services](kotlinx-coroutines-play-services) -- integration with Google Play Services [Tasks API](https://developers.google.com/android/guides/tasks). |
1112

1213
## Contributing
1314

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
# Module kotlinx-coroutines-play-services
2+
3+
Integration with Google Play Services [Tasks API](https://developers.google.com/android/guides/tasks).
4+
5+
Extension functions:
6+
7+
| **Name** | **Description**
8+
| -------- | ---------------
9+
| [Task.await][await] | Awaits for completion of the Task (cancellable)
10+
| [Deferred.asTask][asTask] | Converts a deferred value to a Task
11+
12+
## Example
13+
14+
Using Firebase APIs becomes simple:
15+
16+
```kotlin
17+
FirebaseAuth.getInstance().signInAnonymously().await()
18+
val snapshot = try {
19+
FirebaseFirestore.getInstance().document("users/$id").get().await() // Cancellable await
20+
} catch (e: FirebaseFirestoreException) {
21+
// Handle exception
22+
return@async
23+
}
24+
25+
// Do stuff
26+
```
27+
28+
[await]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-play-services/kotlinx.coroutines.experimental.tasks/com.google.android.gms.tasks.-task/await.html
29+
[asTask]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-play-services/kotlinx.coroutines.experimental.tasks/kotlinx.coroutines.experimental.-deferred/as-task.html
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
import java.nio.file.Files
2+
import java.nio.file.NoSuchFileException
3+
import java.util.zip.ZipEntry
4+
import java.util.zip.ZipFile
5+
6+
/*
7+
* Copyright 2016-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
8+
*/
9+
10+
ext.tasks_version = '15.0.1'
11+
12+
repositories {
13+
google()
14+
}
15+
16+
def attr = Attribute.of("artifactType", String.class)
17+
configurations {
18+
aar {
19+
attributes { attribute(attr, ArtifactTypeDefinition.JAR_TYPE) }
20+
sourceSets.main.compileClasspath += it
21+
sourceSets.test.compileClasspath += it
22+
sourceSets.test.runtimeClasspath += it
23+
}
24+
}
25+
26+
dependencies {
27+
registerTransform {
28+
from.attribute(attr, "aar")
29+
to.attribute(attr, "jar")
30+
artifactTransform(ExtractJars.class)
31+
}
32+
33+
aar("com.google.android.gms:play-services-tasks:$tasks_version") {
34+
exclude group: 'com.android.support'
35+
}
36+
}
37+
38+
tasks.withType(dokka.getClass()) {
39+
externalDocumentationLink {
40+
url = new URL("https://developers.google.com/android/reference/")
41+
}
42+
}
43+
44+
class ExtractJars extends ArtifactTransform {
45+
@Override
46+
List<File> transform(File input) {
47+
unzip(input)
48+
49+
List<File> jars = new ArrayList<>()
50+
outputDirectory.traverse(nameFilter: ~/.*\.jar/) { jars += it }
51+
52+
return jars
53+
}
54+
55+
private void unzip(File zipFile) {
56+
ZipFile zip
57+
try {
58+
zip = new ZipFile(zipFile)
59+
for (entry in zip.entries()) {
60+
unzipEntryTo(zip, entry)
61+
}
62+
} finally {
63+
if (zip != null) zip.close()
64+
}
65+
}
66+
67+
private void unzipEntryTo(ZipFile zip, ZipEntry entry) {
68+
File output = new File(outputDirectory, entry.name)
69+
if (entry.isDirectory()) {
70+
output.mkdirs()
71+
} else {
72+
InputStream stream
73+
try {
74+
stream = zip.getInputStream(entry)
75+
Files.copy(stream, output.toPath())
76+
} catch (NoSuchFileException ignored) {
77+
} finally {
78+
if (stream != null) stream.close()
79+
}
80+
}
81+
}
82+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
/*
2+
* Copyright 2016-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
3+
*/
4+
5+
@file:Suppress("RedundantVisibilityModifier")
6+
7+
package kotlinx.coroutines.experimental.tasks
8+
9+
import com.google.android.gms.tasks.CancellationTokenSource
10+
import com.google.android.gms.tasks.RuntimeExecutionException
11+
import com.google.android.gms.tasks.Task
12+
import com.google.android.gms.tasks.TaskCompletionSource
13+
import kotlinx.coroutines.experimental.CancellationException
14+
import kotlinx.coroutines.experimental.CompletableDeferred
15+
import kotlinx.coroutines.experimental.Deferred
16+
import kotlinx.coroutines.experimental.Job
17+
import kotlinx.coroutines.experimental.suspendCancellableCoroutine
18+
19+
/**
20+
* Converts this deferred to the instance of [Task].
21+
* If deferred is cancelled then resulting task will be cancelled as well.
22+
*/
23+
public fun <T> Deferred<T>.asTask(): Task<T> {
24+
val cancellation = CancellationTokenSource()
25+
val source = TaskCompletionSource<T>(cancellation.token)
26+
27+
invokeOnCompletion callback@{
28+
if (isCancelled) {
29+
cancellation.cancel()
30+
return@callback
31+
}
32+
33+
val t = getCompletionExceptionOrNull()
34+
if (t == null) {
35+
source.setResult(getCompleted())
36+
} else {
37+
source.setException(t as? Exception ?: RuntimeExecutionException(t))
38+
}
39+
}
40+
41+
return source.task
42+
}
43+
44+
/**
45+
* Converts this task to an instance of [Deferred].
46+
* If task is cancelled then resulting deferred will be cancelled as well.
47+
*/
48+
public fun <T> Task<T>.asDeferred(): Deferred<T> {
49+
if (isComplete) {
50+
val e = exception
51+
return if (e == null) {
52+
CompletableDeferred<T>().apply { if (isCanceled) cancel() else complete(result) }
53+
} else {
54+
CompletableDeferred<T>().apply { completeExceptionally(e) }
55+
}
56+
}
57+
58+
val result = CompletableDeferred<T>()
59+
addOnCompleteListener {
60+
val e = it.exception
61+
if (e == null) {
62+
if (isCanceled) result.cancel() else result.complete(it.result)
63+
} else {
64+
result.completeExceptionally(e)
65+
}
66+
}
67+
return result
68+
}
69+
70+
/**
71+
* Awaits for completion of the task without blocking a thread.
72+
*
73+
* This suspending function is cancellable.
74+
* If the [Job] of the current coroutine is cancelled or completed while this suspending function is waiting, this function
75+
* stops waiting for the completion stage and immediately resumes with [CancellationException].
76+
*/
77+
public suspend fun <T> Task<T>.await(): T {
78+
// fast path
79+
if (isComplete) {
80+
val e = exception
81+
return if (e == null) {
82+
if (isCanceled) {
83+
throw CancellationException("Task $this was cancelled normally.")
84+
} else {
85+
result
86+
}
87+
} else {
88+
throw e
89+
}
90+
}
91+
92+
return suspendCancellableCoroutine { cont ->
93+
addOnCompleteListener {
94+
val e = exception
95+
if (e == null) {
96+
if (isCanceled) cont.cancel() else cont.resume(result)
97+
} else {
98+
cont.resumeWithException(e)
99+
}
100+
}
101+
}
102+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
package android.os
2+
3+
import kotlinx.coroutines.experimental.GlobalScope
4+
import kotlinx.coroutines.experimental.launch
5+
6+
class Handler(val looper: Looper) {
7+
fun post(r: Runnable): Boolean {
8+
GlobalScope.launch { r.run() }
9+
return true
10+
}
11+
}
12+
13+
class Looper {
14+
companion object {
15+
@JvmStatic
16+
fun getMainLooper() = Looper()
17+
}
18+
}

0 commit comments

Comments
 (0)