Repo Created
This commit is contained in:
parent
eb305e2886
commit
a8c22c65db
4784 changed files with 329907 additions and 2 deletions
35
play-services-tasks/build.gradle
Normal file
35
play-services-tasks/build.gradle
Normal file
|
|
@ -0,0 +1,35 @@
|
|||
/*
|
||||
* SPDX-FileCopyrightText: 2020 microG Project Team
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
apply plugin: 'com.android.library'
|
||||
apply plugin: 'maven-publish'
|
||||
apply plugin: 'signing'
|
||||
|
||||
android {
|
||||
namespace "com.google.android.gms.tasks"
|
||||
|
||||
compileSdkVersion androidCompileSdk
|
||||
buildToolsVersion "$androidBuildVersionTools"
|
||||
|
||||
defaultConfig {
|
||||
versionName version
|
||||
minSdkVersion androidMinSdk
|
||||
targetSdkVersion androidTargetSdk
|
||||
}
|
||||
|
||||
compileOptions {
|
||||
sourceCompatibility = 1.8
|
||||
targetCompatibility = 1.8
|
||||
}
|
||||
}
|
||||
|
||||
apply from: '../gradle/publish-android.gradle'
|
||||
|
||||
description = 'microG implementation of play-services-tasks'
|
||||
|
||||
dependencies {
|
||||
// Dependencies from play-services-tasks:18.4.0
|
||||
api project(':play-services-basement')
|
||||
}
|
||||
46
play-services-tasks/ktx/build.gradle
Normal file
46
play-services-tasks/ktx/build.gradle
Normal file
|
|
@ -0,0 +1,46 @@
|
|||
/*
|
||||
* SPDX-FileCopyrightText: 2021, microG Project Team
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
apply plugin: 'com.android.library'
|
||||
apply plugin: 'kotlin-android'
|
||||
apply plugin: 'maven-publish'
|
||||
apply plugin: 'signing'
|
||||
|
||||
dependencies {
|
||||
api project(":play-services-tasks")
|
||||
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlinVersion"
|
||||
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:$coroutineVersion"
|
||||
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:$coroutineVersion"
|
||||
}
|
||||
|
||||
android {
|
||||
namespace "org.microg.gms.tasks.ktx"
|
||||
|
||||
compileSdkVersion androidCompileSdk
|
||||
buildToolsVersion "$androidBuildVersionTools"
|
||||
|
||||
defaultConfig {
|
||||
versionName version
|
||||
minSdkVersion androidMinSdk
|
||||
targetSdkVersion androidTargetSdk
|
||||
}
|
||||
|
||||
sourceSets {
|
||||
main.java.srcDirs += 'src/main/kotlin'
|
||||
}
|
||||
|
||||
compileOptions {
|
||||
sourceCompatibility = 1.8
|
||||
targetCompatibility = 1.8
|
||||
}
|
||||
|
||||
kotlinOptions {
|
||||
jvmTarget = "1.8"
|
||||
}
|
||||
}
|
||||
|
||||
apply from: '../../gradle/publish-android.gradle'
|
||||
|
||||
description = 'microG kotlin extensions for play-services-tasks'
|
||||
7
play-services-tasks/ktx/src/main/AndroidManifest.xml
Normal file
7
play-services-tasks/ktx/src/main/AndroidManifest.xml
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
~ SPDX-FileCopyrightText: 2021, microG Project Team
|
||||
~ SPDX-License-Identifier: Apache-2.0
|
||||
-->
|
||||
|
||||
<manifest />
|
||||
|
|
@ -0,0 +1,151 @@
|
|||
/*
|
||||
* SPDX-FileCopyrightText: 2016 JetBrains s.r.o.
|
||||
* SPDX-FileCopyrightText: 2021 microG Project Team
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
package com.google.android.gms.tasks
|
||||
|
||||
import kotlinx.coroutines.*
|
||||
import kotlin.coroutines.*
|
||||
|
||||
/**
|
||||
* Converts this deferred to the instance of [Task].
|
||||
* If deferred is cancelled then resulting task will be cancelled as well.
|
||||
*/
|
||||
fun <T> Deferred<T>.asTask(): Task<T> {
|
||||
val cancellation = CancellationTokenSource()
|
||||
val source = TaskCompletionSource<T>(cancellation.token)
|
||||
|
||||
invokeOnCompletion callback@{
|
||||
if (it is CancellationException) {
|
||||
cancellation.cancel()
|
||||
return@callback
|
||||
}
|
||||
|
||||
val t = getCompletionExceptionOrNull()
|
||||
if (t == null) {
|
||||
source.setResult(getCompleted())
|
||||
} else {
|
||||
source.setException(t as? Exception ?: RuntimeExecutionException(t))
|
||||
}
|
||||
}
|
||||
|
||||
return source.task
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts this task to an instance of [Deferred].
|
||||
* If task is cancelled then resulting deferred will be cancelled as well.
|
||||
* However, the opposite is not true: if the deferred is cancelled, the [Task] will not be cancelled.
|
||||
* For bi-directional cancellation, an overload that accepts [CancellationTokenSource] can be used.
|
||||
*/
|
||||
fun <T> Task<T>.asDeferred(): Deferred<T> = asDeferredImpl(null)
|
||||
|
||||
/**
|
||||
* Converts this task to an instance of [Deferred] with a [CancellationTokenSource] to control cancellation.
|
||||
* The cancellation of this function is bi-directional:
|
||||
* * If the given task is cancelled, the resulting deferred will be cancelled.
|
||||
* * If the resulting deferred is cancelled, the provided [cancellationTokenSource] will be cancelled.
|
||||
*
|
||||
* Providing a [CancellationTokenSource] that is unrelated to the receiving [Task] is not supported and
|
||||
* leads to an unspecified behaviour.
|
||||
*/
|
||||
@ExperimentalCoroutinesApi // Since 1.5.1, tentatively until 1.6.0
|
||||
fun <T> Task<T>.asDeferred(cancellationTokenSource: CancellationTokenSource): Deferred<T> =
|
||||
asDeferredImpl(cancellationTokenSource)
|
||||
|
||||
private fun <T> Task<T>.asDeferredImpl(cancellationTokenSource: CancellationTokenSource?): Deferred<T> {
|
||||
val deferred = CompletableDeferred<T>()
|
||||
if (isComplete) {
|
||||
val e = exception
|
||||
if (e == null) {
|
||||
if (isCanceled) {
|
||||
deferred.cancel()
|
||||
} else {
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
deferred.complete(result as T)
|
||||
}
|
||||
} else {
|
||||
deferred.completeExceptionally(e)
|
||||
}
|
||||
} else {
|
||||
addOnCompleteListener {
|
||||
val e = it.exception
|
||||
if (e == null) {
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
if (it.isCanceled) deferred.cancel() else deferred.complete(it.result as T)
|
||||
} else {
|
||||
deferred.completeExceptionally(e)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (cancellationTokenSource != null) {
|
||||
deferred.invokeOnCompletion {
|
||||
cancellationTokenSource.cancel()
|
||||
}
|
||||
}
|
||||
// Prevent casting to CompletableDeferred and manual completion.
|
||||
return object : Deferred<T> by deferred {}
|
||||
}
|
||||
|
||||
/**
|
||||
* Awaits the completion of the task without blocking a thread.
|
||||
*
|
||||
* This suspending function is cancellable.
|
||||
* If the [Job] of the current coroutine is cancelled or completed while this suspending function is waiting, this function
|
||||
* stops waiting for the completion stage and immediately resumes with [CancellationException].
|
||||
*
|
||||
* For bi-directional cancellation, an overload that accepts [CancellationTokenSource] can be used.
|
||||
*/
|
||||
suspend fun <T> Task<T>.await(): T = awaitImpl(null)
|
||||
|
||||
/**
|
||||
* Awaits the completion of the task that is linked to the given [CancellationTokenSource] to control cancellation.
|
||||
*
|
||||
* This suspending function is cancellable and cancellation is bi-directional:
|
||||
* * If the [Job] of the current coroutine is cancelled or completed while this suspending function is waiting, this function
|
||||
* cancels the [cancellationTokenSource] and throws a [CancellationException].
|
||||
* * If the task is cancelled, then this function will throw a [CancellationException].
|
||||
*
|
||||
* Providing a [CancellationTokenSource] that is unrelated to the receiving [Task] is not supported and
|
||||
* leads to an unspecified behaviour.
|
||||
*/
|
||||
@ExperimentalCoroutinesApi // Since 1.5.1, tentatively until 1.6.0
|
||||
suspend fun <T> Task<T>.await(cancellationTokenSource: CancellationTokenSource): T = awaitImpl(cancellationTokenSource)
|
||||
|
||||
private suspend fun <T> Task<T>.awaitImpl(cancellationTokenSource: CancellationTokenSource?): T {
|
||||
// fast path
|
||||
if (isComplete) {
|
||||
val e = exception
|
||||
return if (e == null) {
|
||||
if (isCanceled) {
|
||||
throw CancellationException("Task $this was cancelled normally.")
|
||||
} else {
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
result as T
|
||||
}
|
||||
} else {
|
||||
throw e
|
||||
}
|
||||
}
|
||||
|
||||
return suspendCancellableCoroutine { cont ->
|
||||
addOnCompleteListener {
|
||||
val e = it.exception
|
||||
if (e == null) {
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
if (it.isCanceled) cont.cancel() else cont.resume(it.result as T)
|
||||
} else {
|
||||
cont.resumeWithException(e)
|
||||
}
|
||||
}
|
||||
|
||||
if (cancellationTokenSource != null) {
|
||||
cont.invokeOnCancellation {
|
||||
cancellationTokenSource.cancel()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
7
play-services-tasks/src/main/AndroidManifest.xml
Normal file
7
play-services-tasks/src/main/AndroidManifest.xml
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
~ SPDX-FileCopyrightText: 2020, microG Project Team
|
||||
~ SPDX-License-Identifier: Apache-2.0
|
||||
-->
|
||||
|
||||
<manifest />
|
||||
|
|
@ -0,0 +1,88 @@
|
|||
/*
|
||||
* SPDX-FileCopyrightText: 2020 microG Project Team
|
||||
* SPDX-License-Identifier: Apache-2.0 AND CC-BY-4.0
|
||||
* Notice: Portions of this file are reproduced from work created and shared by Google and used
|
||||
* according to terms described in the Creative Commons 4.0 Attribution License.
|
||||
* See https://developers.google.com/readme/policies for details.
|
||||
*/
|
||||
|
||||
package com.google.android.gms.tasks;
|
||||
|
||||
import org.microg.gms.common.PublicApi;
|
||||
|
||||
/**
|
||||
* Propagates notification that operations should be canceled.
|
||||
* <p/>
|
||||
* Developers writing methods that return a Task should take a {@code CancellationToken} as a parameter if they wish to
|
||||
* make the Task cancelable (see below code snippet). A {@code CancellationToken} can only be created by creating a new
|
||||
* instance of {@link CancellationTokenSource}. {@code CancellationToken} is immutable and must be canceled by calling
|
||||
* {@link CancellationTokenSource#cancel()} on the {@link CancellationTokenSource} that creates it. It can only be
|
||||
* canceled once. If canceled, it should not be passed to future operations.
|
||||
* <p/>
|
||||
* When {@link CancellationTokenSource#cancel()} is called, all the Tasks with the {@code CancellationToken} from that
|
||||
* {@link CancellationTokenSource} will be canceled. This operation only flags those Tasks as canceled, and the API
|
||||
* author is responsible for stopping whatever the Task is actually doing to free up the resources.
|
||||
* <p/>
|
||||
* Cancellable {@link Task} example:
|
||||
* <pre>
|
||||
* public Task<Integer> doSomething(CancellationToken token) {
|
||||
*
|
||||
* // Attach a listener that will be called once cancellation is requested.
|
||||
* token.onCanceledRequested(new OnTokenCanceledListener() {
|
||||
* @Override
|
||||
* public void onCanceled() {
|
||||
* // Some other operations to cancel this Task, such as free resources...
|
||||
* }
|
||||
* });
|
||||
*
|
||||
* final TaskCompletionSource<Integer> tcs = new TaskCompletionSource<>(token);
|
||||
*
|
||||
* // do something...
|
||||
*
|
||||
* }
|
||||
*
|
||||
* CancellationTokenSource cts = new CancellationTokenSource();
|
||||
* Task<Integer> task = doSomething(cts.getToken());
|
||||
* cts.cancel();
|
||||
* </pre>
|
||||
* Cancellable {@link Task} example in {@link android.app.Activity} context:
|
||||
* <pre>
|
||||
* public class MyActivity extends Activity {
|
||||
* // ...
|
||||
*
|
||||
* @Override
|
||||
* public void onStart() {
|
||||
* super.onStart();
|
||||
*
|
||||
* // Typically use one cancellation source per lifecycle.
|
||||
* cancellationSource = new TaskCancellationSource();
|
||||
*
|
||||
* // That source's token can be passed to multiple calls.
|
||||
* doSomethingCancellable(cancellationSource.getToken())
|
||||
* .onSuccessTask(result -> doSomethingElse(result, cancellationSource.getToken()));
|
||||
* }
|
||||
*
|
||||
* @Override
|
||||
* public void onStop() {
|
||||
* super.onStop();
|
||||
* cancellationSource.cancel();
|
||||
* }
|
||||
* }
|
||||
* </pre>
|
||||
*/
|
||||
@PublicApi
|
||||
public abstract class CancellationToken {
|
||||
/**
|
||||
* Checks if cancellation has been requested from the {@link CancellationTokenSource}.
|
||||
*
|
||||
* @return {@code true} if cancellation is requested, {@code false} otherwise
|
||||
*/
|
||||
public abstract boolean isCancellationRequested();
|
||||
|
||||
/**
|
||||
* Adds an {@link OnTokenCanceledListener} to this {@link CancellationToken}.
|
||||
*
|
||||
* @param listener the listener that will fire once the cancellation request succeeds.
|
||||
*/
|
||||
public abstract CancellationToken onCanceledRequested(OnTokenCanceledListener listener);
|
||||
}
|
||||
|
|
@ -0,0 +1,46 @@
|
|||
/*
|
||||
* SPDX-FileCopyrightText: 2020, microG Project Team
|
||||
* SPDX-License-Identifier: Apache-2.0 AND CC-BY-4.0
|
||||
* Notice: Portions of this file are reproduced from work created and shared by Google and used
|
||||
* according to terms described in the Creative Commons 4.0 Attribution License.
|
||||
* See https://developers.google.com/readme/policies for details.
|
||||
*/
|
||||
|
||||
package com.google.android.gms.tasks;
|
||||
|
||||
import org.microg.gms.common.PublicApi;
|
||||
import org.microg.gms.tasks.CancellationTokenImpl;
|
||||
|
||||
/**
|
||||
* Creates a new {@link CancellationToken} or cancels one that has already created. There is a 1:1 {@link CancellationTokenSource} to {@link CancellationToken} relationship.
|
||||
* <p>
|
||||
* To create a {@link CancellationToken}, create a {@link CancellationTokenSource} first and then call {@link #getToken()} to get the {@link CancellationToken} for this {@link CancellationTokenSource}.
|
||||
*
|
||||
* @see CancellationToken
|
||||
*/
|
||||
@PublicApi
|
||||
public class CancellationTokenSource {
|
||||
private CancellationTokenImpl token = new CancellationTokenImpl();
|
||||
|
||||
/**
|
||||
* Creates a new {@link CancellationTokenSource} instance.
|
||||
*/
|
||||
public CancellationTokenSource() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Cancels the {@link CancellationToken} if cancellation has not been requested yet.
|
||||
*/
|
||||
public void cancel() {
|
||||
token.cancel();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the {@link CancellationToken} for this {@link CancellationTokenSource}.
|
||||
*
|
||||
* @return the {@link CancellationToken} that can be passed to asynchronous {@link Task} to cancel the Task.
|
||||
*/
|
||||
public CancellationToken getToken() {
|
||||
return token;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,62 @@
|
|||
/*
|
||||
* SPDX-FileCopyrightText: 2016 microG Project Team
|
||||
* SPDX-License-Identifier: Apache-2.0 AND CC-BY-4.0
|
||||
* Notice: Portions of this file are reproduced from work created and shared by Google and used
|
||||
* according to terms described in the Creative Commons 4.0 Attribution License.
|
||||
* See https://developers.google.com/readme/policies for details.
|
||||
*/
|
||||
|
||||
package com.google.android.gms.tasks;
|
||||
|
||||
import org.microg.gms.common.PublicApi;
|
||||
|
||||
/**
|
||||
* A function that is called to continue execution after completion of a {@link Task}.
|
||||
*
|
||||
* @see Task#continueWith(Continuation)
|
||||
* @see Task#continueWithTask(Continuation)
|
||||
*/
|
||||
@PublicApi
|
||||
public interface Continuation<TResult, TContinuationResult> {
|
||||
/**
|
||||
* Returns the result of applying this Continuation to {@code task}.
|
||||
* <p/>
|
||||
* To propagate failure from the completed Task call {@link Task#getResult()} and allow the
|
||||
* {@link RuntimeExecutionException} to propagate. The RuntimeExecutionException will be
|
||||
* unwrapped such that the Task returned by {@link Task#continueWith(Continuation)} or
|
||||
* {@link Task#continueWithTask(Continuation)} fails with the original exception.
|
||||
* <p/>
|
||||
* To suppress specific failures call {@link Task#getResult(Class)} and catch the exception
|
||||
* types of interest:
|
||||
* <pre>
|
||||
* task.continueWith(new Continuation<String, String>() {
|
||||
* @Override
|
||||
* public String then(Task<String> task) {
|
||||
* try {
|
||||
* return task.getResult(IOException.class);
|
||||
* } catch (FileNotFoundException e) {
|
||||
* return "Not found";
|
||||
* } catch (IOException e) {
|
||||
* return "Read failed";
|
||||
* }
|
||||
* }
|
||||
* }</pre>
|
||||
* <p/>
|
||||
* To suppress all failures guard any calls to {@link Task#getResult()} with {@link Task#isSuccessful()}:
|
||||
* <pre>
|
||||
* task.continueWith(new Continuation<String, String>() {
|
||||
* @Override
|
||||
* public String then(Task<String> task) {
|
||||
* if (task.isSuccessful()) {
|
||||
* return task.getResult();
|
||||
* } else {
|
||||
* return DEFAULT_VALUE;
|
||||
* }
|
||||
* }
|
||||
* }</pre>
|
||||
*
|
||||
* @param task the completed Task. Never null
|
||||
* @throws Exception if the result couldn't be produced
|
||||
*/
|
||||
TContinuationResult then(Task<TResult> task);
|
||||
}
|
||||
|
|
@ -0,0 +1,32 @@
|
|||
/*
|
||||
* SPDX-FileCopyrightText: 2020, microG Project Team
|
||||
* SPDX-License-Identifier: Apache-2.0 AND CC-BY-4.0
|
||||
* Notice: Portions of this file are reproduced from work created and shared by Google and used
|
||||
* according to terms described in the Creative Commons 4.0 Attribution License.
|
||||
* See https://developers.google.com/readme/policies for details.
|
||||
*/
|
||||
|
||||
package com.google.android.gms.tasks;
|
||||
|
||||
import org.microg.gms.common.PublicApi;
|
||||
|
||||
/**
|
||||
* An exception indicating that something attempted to set a result, exception, or cancellation on a {@link Task} that was already completed.
|
||||
*/
|
||||
@PublicApi
|
||||
public class DuplicateTaskCompletionException extends IllegalStateException {
|
||||
|
||||
private DuplicateTaskCompletionException(String s) {
|
||||
super(s);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a DuplicateTaskCompletionException from a {@link Task}.
|
||||
*
|
||||
* The {@link Task} must be complete.
|
||||
*/
|
||||
public static DuplicateTaskCompletionException of(Task<?> task) {
|
||||
if (!task.isComplete()) throw new IllegalStateException("Task is not yet completed");
|
||||
return new DuplicateTaskCompletionException("Task is already completed");
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,22 @@
|
|||
/*
|
||||
* SPDX-FileCopyrightText: 2020, microG Project Team
|
||||
* SPDX-License-Identifier: Apache-2.0 AND CC-BY-4.0
|
||||
* Notice: Portions of this file are reproduced from work created and shared by Google and used
|
||||
* according to terms described in the Creative Commons 4.0 Attribution License.
|
||||
* See https://developers.google.com/readme/policies for details.
|
||||
*/
|
||||
|
||||
package com.google.android.gms.tasks;
|
||||
|
||||
import org.microg.gms.common.PublicApi;
|
||||
|
||||
/**
|
||||
* Listener called when a {@link Task} is canceled.
|
||||
*/
|
||||
@PublicApi
|
||||
public interface OnCanceledListener {
|
||||
/**
|
||||
* Called when the Task is canceled successfully.
|
||||
*/
|
||||
public abstract void onCanceled();
|
||||
}
|
||||
|
|
@ -0,0 +1,26 @@
|
|||
/*
|
||||
* SPDX-FileCopyrightText: 2016, microG Project Team
|
||||
* SPDX-License-Identifier: Apache-2.0 AND CC-BY-4.0
|
||||
* Notice: Portions of this file are reproduced from work created and shared by Google and used
|
||||
* according to terms described in the Creative Commons 4.0 Attribution License.
|
||||
* See https://developers.google.com/readme/policies for details.
|
||||
*/
|
||||
|
||||
package com.google.android.gms.tasks;
|
||||
|
||||
import org.microg.gms.common.PublicApi;
|
||||
|
||||
/**
|
||||
* Listener called when a {@link Task} completes.
|
||||
*
|
||||
* @see Task#addOnCompleteListener(OnCompleteListener)
|
||||
*/
|
||||
@PublicApi
|
||||
public interface OnCompleteListener<TResult> {
|
||||
/**
|
||||
* Called when the Task completes.
|
||||
*
|
||||
* @param task the completed Task. Never null
|
||||
*/
|
||||
void onComplete(Task<TResult> task);
|
||||
}
|
||||
|
|
@ -0,0 +1,27 @@
|
|||
/*
|
||||
* SPDX-FileCopyrightText: 2016, microG Project Team
|
||||
* SPDX-License-Identifier: Apache-2.0 AND CC-BY-4.0
|
||||
* Notice: Portions of this file are reproduced from work created and shared by Google and used
|
||||
* according to terms described in the Creative Commons 4.0 Attribution License.
|
||||
* See https://developers.google.com/readme/policies for details.
|
||||
*/
|
||||
|
||||
package com.google.android.gms.tasks;
|
||||
|
||||
import org.microg.gms.common.PublicApi;
|
||||
|
||||
/**
|
||||
* Listener called when a {@link Task} fails with an exception.
|
||||
*
|
||||
* @see Task#addOnFailureListener(OnFailureListener)
|
||||
*/
|
||||
@PublicApi
|
||||
public interface OnFailureListener {
|
||||
|
||||
/**
|
||||
* Called when the Task fails with an exception.
|
||||
*
|
||||
* @param e the exception that caused the Task to fail. Never null
|
||||
*/
|
||||
void onFailure(Exception e);
|
||||
}
|
||||
|
|
@ -0,0 +1,26 @@
|
|||
/*
|
||||
* SPDX-FileCopyrightText: 2016, microG Project Team
|
||||
* SPDX-License-Identifier: Apache-2.0 AND CC-BY-4.0
|
||||
* Notice: Portions of this file are reproduced from work created and shared by Google and used
|
||||
* according to terms described in the Creative Commons 4.0 Attribution License.
|
||||
* See https://developers.google.com/readme/policies for details.
|
||||
*/
|
||||
|
||||
package com.google.android.gms.tasks;
|
||||
|
||||
import org.microg.gms.common.PublicApi;
|
||||
|
||||
/**
|
||||
* Listener called when a {@link Task} completes successfully.
|
||||
*
|
||||
* @see Task#addOnSuccessListener(OnSuccessListener)
|
||||
*/
|
||||
@PublicApi
|
||||
public interface OnSuccessListener<TResult> {
|
||||
/**
|
||||
* Called when the {@link Task} completes successfully.
|
||||
*
|
||||
* @param result the result of the Task
|
||||
*/
|
||||
void onSuccess(TResult result);
|
||||
}
|
||||
|
|
@ -0,0 +1,24 @@
|
|||
/*
|
||||
* SPDX-FileCopyrightText: 2020, microG Project Team
|
||||
* SPDX-License-Identifier: Apache-2.0 AND CC-BY-4.0
|
||||
* Notice: Portions of this file are reproduced from work created and shared by Google and used
|
||||
* according to terms described in the Creative Commons 4.0 Attribution License.
|
||||
* See https://developers.google.com/readme/policies for details.
|
||||
*/
|
||||
|
||||
package com.google.android.gms.tasks;
|
||||
|
||||
import org.microg.gms.common.PublicApi;
|
||||
|
||||
/**
|
||||
* Listener called when a {@link CancellationToken} is canceled successfully.
|
||||
*
|
||||
* @see CancellationToken#onCanceledRequested(OnTokenCanceledListener)
|
||||
*/
|
||||
@PublicApi
|
||||
public interface OnTokenCanceledListener {
|
||||
/**
|
||||
* Called when the {@link CancellationToken} is canceled successfully.
|
||||
*/
|
||||
void onCanceled();
|
||||
}
|
||||
|
|
@ -0,0 +1,25 @@
|
|||
/*
|
||||
* SPDX-FileCopyrightText: 2016, microG Project Team
|
||||
* SPDX-License-Identifier: Apache-2.0 AND CC-BY-4.0
|
||||
* Notice: Portions of this file are reproduced from work created and shared by Google and used
|
||||
* according to terms described in the Creative Commons 4.0 Attribution License.
|
||||
* See https://developers.google.com/readme/policies for details.
|
||||
*/
|
||||
|
||||
package com.google.android.gms.tasks;
|
||||
|
||||
import org.microg.gms.common.PublicApi;
|
||||
|
||||
import java.util.concurrent.ExecutionException;
|
||||
|
||||
/**
|
||||
* Runtime version of {@link ExecutionException}.
|
||||
*
|
||||
* @see Task#getResult(Class)
|
||||
*/
|
||||
@PublicApi
|
||||
public class RuntimeExecutionException extends RuntimeException {
|
||||
public RuntimeExecutionException(Throwable cause) {
|
||||
super(cause);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,42 @@
|
|||
/*
|
||||
* SPDX-FileCopyrightText: 2020 microG Project Team
|
||||
* SPDX-License-Identifier: Apache-2.0 AND CC-BY-4.0
|
||||
* Notice: Portions of this file are reproduced from work created and shared by Google and used
|
||||
* according to terms described in the Creative Commons 4.0 Attribution License.
|
||||
* See https://developers.google.com/readme/policies for details.
|
||||
*/
|
||||
|
||||
package com.google.android.gms.tasks;
|
||||
|
||||
import org.microg.gms.common.PublicApi;
|
||||
|
||||
/**
|
||||
* A function that is called to continue execution then a {@link Task} succeeds.
|
||||
* @see Task#onSuccessTask
|
||||
*/
|
||||
@PublicApi
|
||||
public interface SuccessContinuation<TResult, TContinuationResult> {
|
||||
/**
|
||||
* Returns the result of applying this SuccessContinuation to Task.
|
||||
* <p>
|
||||
* The SuccessContinuation only happens then the Task is successful. If the previous Task fails, the onSuccessTask
|
||||
* continuation will be skipped and failure listeners will be invoked.
|
||||
* <p>
|
||||
* <pre>
|
||||
* private Task<String> doSomething(String string) {
|
||||
* // do something
|
||||
* }
|
||||
* task.onSuccessTask(new SuccessContinuation<String, String>() {
|
||||
* @NonNull
|
||||
* @Override
|
||||
* public Task<String> then(String string) {
|
||||
* return doSomething(string);
|
||||
* }
|
||||
* });
|
||||
* </pre>
|
||||
*
|
||||
* @param result the result of completed Task
|
||||
* @throws Exception if the result couldn't be produced
|
||||
*/
|
||||
Task<TContinuationResult> then(TResult result) throws Exception;
|
||||
}
|
||||
|
|
@ -0,0 +1,292 @@
|
|||
/*
|
||||
* SPDX-FileCopyrightText: 2016 microG Project Team
|
||||
* SPDX-License-Identifier: Apache-2.0 AND CC-BY-4.0
|
||||
* Notice: Portions of this file are reproduced from work created and shared by Google and used
|
||||
* according to terms described in the Creative Commons 4.0 Attribution License.
|
||||
* See https://developers.google.com/readme/policies for details.
|
||||
*/
|
||||
|
||||
package com.google.android.gms.tasks;
|
||||
|
||||
import android.app.Activity;
|
||||
|
||||
import org.microg.gms.common.PublicApi;
|
||||
|
||||
import java.util.concurrent.Executor;
|
||||
|
||||
/**
|
||||
* Represents an asynchronous operation.
|
||||
*/
|
||||
@PublicApi
|
||||
public abstract class Task<TResult> {
|
||||
|
||||
public Task() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a listener that is called if the Task is canceled.
|
||||
* <p>
|
||||
* The listener will be called on main application thread. If the Task has already been canceled, a call to the listener will be immediately scheduled. If multiple listeners are added, they will be called in the order in which they were added.
|
||||
*
|
||||
* @return this Task
|
||||
*/
|
||||
public Task<TResult> addOnCanceledListener(OnCanceledListener listener) {
|
||||
throw new UnsupportedOperationException("addOnCanceledListener is not implemented");
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a listener that is called if the Task is canceled.
|
||||
* <p>
|
||||
* If the Task has already been canceled, a call to the listener will be immediately scheduled. If multiple listeners are added, they will be called in the order in which they were added.
|
||||
*
|
||||
* @param executor the executor to use to call the listener
|
||||
* @return this Task
|
||||
*/
|
||||
public Task<TResult> addOnCanceledListener(Executor executor, OnCanceledListener listener) {
|
||||
throw new UnsupportedOperationException("addOnCanceledListener is not implemented");
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds an Activity-scoped listener that is called if the Task is canceled.
|
||||
* <p>
|
||||
* The listener will be called on main application thread. If the Task has already been canceled, a call to the listener will be immediately scheduled. If multiple listeners are added, they will be called in the order in which they were added.
|
||||
* <p>
|
||||
* The listener will be automatically removed during {@link Activity#onStop()}.
|
||||
*
|
||||
* @return this Task
|
||||
*/
|
||||
public Task<TResult> addOnCanceledListener(Activity activity, OnCanceledListener listener) {
|
||||
throw new UnsupportedOperationException("addOnCanceledListener is not implemented");
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a listener that is called when the Task completes.
|
||||
* <p/>
|
||||
* The listener will be called on main application thread. If the Task is already complete, a
|
||||
* call to the listener will be immediately scheduled. If multiple listeners are added, they
|
||||
* will be called in the order in which they were added.
|
||||
*
|
||||
* @return this Task
|
||||
*/
|
||||
public Task<TResult> addOnCompleteListener(OnCompleteListener<TResult> listener) {
|
||||
throw new UnsupportedOperationException("addOnCompleteListener is not implemented");
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds an Activity-scoped listener that is called when the Task completes.
|
||||
* <p/>
|
||||
* The listener will be called on main application thread. If the Task is already complete, a
|
||||
* call to the listener will be immediately scheduled. If multiple listeners are added, they
|
||||
* will be called in the order in which they were added.
|
||||
* <p/>
|
||||
* The listener will be automatically removed during {@link Activity#onStop()}.
|
||||
*
|
||||
* @return this Task
|
||||
*/
|
||||
public Task<TResult> addOnCompleteListener(Activity activity, OnCompleteListener<TResult> listener) {
|
||||
throw new UnsupportedOperationException("addOnCompleteListener is not implemented");
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a listener that is called when the Task completes.
|
||||
* <p/>
|
||||
* If the Task is already complete, a call to the listener will be immediately scheduled. If
|
||||
* multiple listeners are added, they will be called in the order in which they were added.
|
||||
*
|
||||
* @param executor the executor to use to call the listener
|
||||
* @return this Task
|
||||
*/
|
||||
public Task<TResult> addOnCompleteListener(Executor executor, OnCompleteListener<TResult> listener) {
|
||||
throw new UnsupportedOperationException("addOnCompleteListener is not implemented");
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds an Activity-scoped listener that is called if the Task fails.
|
||||
* <p/>
|
||||
* The listener will be called on main application thread. If the Task has already failed, a
|
||||
* call to the listener will be immediately scheduled. If multiple listeners are added, they
|
||||
* will be called in the order in which they were added.
|
||||
* <p/>
|
||||
* The listener will be automatically removed during {@link Activity#onStop()}.
|
||||
*
|
||||
* @return this Task
|
||||
*/
|
||||
public abstract Task<TResult> addOnFailureListener(Activity activity, OnFailureListener listener);
|
||||
|
||||
/**
|
||||
* Adds a listener that is called if the Task fails.
|
||||
* <p/>
|
||||
* The listener will be called on main application thread. If the Task has already failed, a
|
||||
* call to the listener will be immediately scheduled. If multiple listeners are added, they
|
||||
* will be called in the order in which they were added.
|
||||
*
|
||||
* @return this Task
|
||||
*/
|
||||
public abstract Task<TResult> addOnFailureListener(OnFailureListener listener);
|
||||
|
||||
/**
|
||||
* Adds a listener that is called if the Task fails.
|
||||
* <p/>
|
||||
* If the Task has already failed, a call to the listener will be immediately scheduled. If
|
||||
* multiple listeners are added, they will be called in the order in which they were added.
|
||||
*
|
||||
* @param executor the executor to use to call the listener
|
||||
* @return this Task
|
||||
*/
|
||||
public abstract Task<TResult> addOnFailureListener(Executor executor, OnFailureListener listener);
|
||||
|
||||
|
||||
/**
|
||||
* Adds a listener that is called if the Task completes successfully.
|
||||
* <p/>
|
||||
* If multiple listeners are added, they will be called in the order in which they were added. If
|
||||
* the Task has already completed successfully, a call to the listener will be immediately scheduled.
|
||||
*
|
||||
* @param executor the executor to use to call the listener
|
||||
* @return this Task
|
||||
*/
|
||||
public abstract Task<TResult> addOnSuccessListener(Executor executor, OnSuccessListener<? super TResult> listener);
|
||||
|
||||
/**
|
||||
* Adds a listener that is called if the Task completes successfully.
|
||||
* <p/>
|
||||
* The listener will be called on the main application thread. If the Task has already
|
||||
* completed successfully, a call to the listener will be immediately scheduled. If multiple
|
||||
* listeners are added, they will be called in the order in which they were added.
|
||||
*
|
||||
* @return this Task
|
||||
*/
|
||||
public abstract Task<TResult> addOnSuccessListener(OnSuccessListener<? super TResult> listener);
|
||||
|
||||
/**
|
||||
* Adds an Activity-scoped listener that is called if the Task completes successfully.
|
||||
* <p/>
|
||||
* The listener will be called on the main application thread. If the Task has already
|
||||
* completed successfully, a call to the listener will be immediately scheduled. If multiple
|
||||
* listeners are added, they will be called in the order in which they were added.
|
||||
* <p/>
|
||||
* The listener will be automatically removed during {@link Activity#onStop()}.
|
||||
*
|
||||
* @return this Task
|
||||
*/
|
||||
public abstract Task<TResult> addOnSuccessListener(Activity activity, OnSuccessListener<? super TResult> listener);
|
||||
|
||||
|
||||
/**
|
||||
* Returns a new Task that will be completed with the result of applying the specified
|
||||
* Continuation to this Task.
|
||||
* <p/>
|
||||
* The Continuation will be called on the main application thread.
|
||||
* <p/>
|
||||
* If the previous Task is canceled, the returned Task will also be canceled and the Continuation would not execute.
|
||||
*
|
||||
* @see Continuation#then(Task)
|
||||
*/
|
||||
public <TContinuationResult> Task<TContinuationResult> continueWith(Continuation<TResult, TContinuationResult> continuation) {
|
||||
throw new UnsupportedOperationException("continueWith is not implemented");
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a new Task that will be completed with the result of applying the specified Continuation to this Task.
|
||||
* <p/>
|
||||
* If the previous Task is canceled, the returned Task will also be canceled and the Continuation would not execute.
|
||||
*
|
||||
* @param executor the executor to use to call the Continuation
|
||||
* @see Continuation#then(Task)
|
||||
*/
|
||||
public <TContinuationResult> Task<TContinuationResult> continueWith(Executor executor, Continuation<TResult, TContinuationResult> continuation) {
|
||||
throw new UnsupportedOperationException("continueWith is not implemented");
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a new Task that will be completed with the result of applying the specified
|
||||
* Continuation to this Task.
|
||||
* <p/>
|
||||
* The Continuation will be called on the main application thread.
|
||||
* <p/>
|
||||
* If the previous Task is canceled, the Continuation would still execute and task.isCanceled() is true can be
|
||||
* observed in the Continuation.
|
||||
*
|
||||
* @see Continuation#then(Task)
|
||||
*/
|
||||
public <TContinuationResult> Task<TContinuationResult> continueWithTask(Continuation<TResult, Task<TContinuationResult>> continuation) {
|
||||
throw new UnsupportedOperationException("continueWithTask is not implemented");
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a new Task that will be completed with the result of applying the specified Continuation to this Task.
|
||||
* <p/>
|
||||
* If the previous Task is canceled, the Continuation would still execute and task.isCanceled() is true can be
|
||||
* observed in the Continuation.
|
||||
*
|
||||
* @param executor the executor to use to call the Continuation
|
||||
* @see Continuation#then(Task)
|
||||
*/
|
||||
public <TContinuationResult> Task<TContinuationResult> continueWithTask(Executor executor, Continuation<TResult, Task<TContinuationResult>> continuation) {
|
||||
throw new UnsupportedOperationException("continueWithTask is not implemented");
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the exception that caused the Task to fail. Returns {@code null} if the Task is not
|
||||
* yet complete, or completed successfully.
|
||||
*/
|
||||
public abstract Exception getException();
|
||||
|
||||
/**
|
||||
* Gets the result of the Task, if it has already completed.
|
||||
*
|
||||
* @throws IllegalStateException if the Task is not yet complete
|
||||
* @throws RuntimeExecutionException if the Task failed with an exception
|
||||
*/
|
||||
public abstract TResult getResult();
|
||||
|
||||
/**
|
||||
* Gets the result of the Task, if it has already completed.
|
||||
*
|
||||
* @throws IllegalStateException if the Task is not yet complete
|
||||
* @throws X if the Task failed with an exception of type X
|
||||
* @throws RuntimeExecutionException if the Task failed with an exception that was not of type X
|
||||
*/
|
||||
public abstract <X extends Throwable> TResult getResult(Class<X> exceptionType) throws X;
|
||||
|
||||
/**
|
||||
* Returns {@code true} if the Task is canceled; {@code false} otherwise.
|
||||
*/
|
||||
public abstract boolean isCanceled();
|
||||
|
||||
/**
|
||||
* Returns {@code true} if the Task is complete; {@code false} otherwise.
|
||||
*/
|
||||
public abstract boolean isComplete();
|
||||
|
||||
/**
|
||||
* Returns {@code true} if the Task has completed successfully; {@code false} otherwise.
|
||||
*/
|
||||
public abstract boolean isSuccessful();
|
||||
|
||||
/**
|
||||
* Returns a new Task that will be completed with the result of applying the specified SuccessContinuation to this Task when this Task completes successfully. If the previous Task fails, the onSuccessTask completion will be skipped and failure listeners will be invoked.
|
||||
* <p>
|
||||
* The SuccessContinuation will be called on the main application thread.
|
||||
* <p>
|
||||
* If the previous Task is canceled, the returned Task will also be canceled and the SuccessContinuation would not execute.
|
||||
*
|
||||
* @see SuccessContinuation#then
|
||||
*/
|
||||
public <TContinuationResult> Task<TContinuationResult> onSuccessTask(SuccessContinuation<TResult, TContinuationResult> successContinuation) {
|
||||
throw new UnsupportedOperationException("onSuccessTask is not implemented");
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a new Task that will be completed with the result of applying the specified SuccessContinuation to this Task when this Task completes successfully. If the previous Task fails, the onSuccessTask completion will be skipped and failure listeners will be invoked.
|
||||
* <p>
|
||||
* If the previous Task is canceled, the returned Task will also be canceled and the SuccessContinuation would not execute.
|
||||
*
|
||||
* @param executor the executor to use to call the SuccessContinuation
|
||||
* @see SuccessContinuation#then
|
||||
*/
|
||||
public <TContinuationResult> Task<TContinuationResult> onSuccessTask(Executor executor, SuccessContinuation<TResult, TContinuationResult> successContinuation) {
|
||||
throw new UnsupportedOperationException("onSuccessTask is not implemented");
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,121 @@
|
|||
/*
|
||||
* SPDX-FileCopyrightText: 2016 microG Project Team
|
||||
* SPDX-License-Identifier: Apache-2.0 AND CC-BY-4.0
|
||||
* Notice: Portions of this file are reproduced from work created and shared by Google and used
|
||||
* according to terms described in the Creative Commons 4.0 Attribution License.
|
||||
* See https://developers.google.com/readme/policies for details.
|
||||
*/
|
||||
|
||||
package com.google.android.gms.tasks;
|
||||
|
||||
import org.microg.gms.common.PublicApi;
|
||||
import org.microg.gms.tasks.TaskImpl;
|
||||
|
||||
/**
|
||||
* Provides the ability to create an incomplete {@link Task}-based APIs.
|
||||
* <p/>
|
||||
* Use a {@code TaskCompletionSource} to set a result or exception on a Task returned from an asynchronous API:
|
||||
* <pre>
|
||||
* public class MarcoPolo {
|
||||
* public static Task<String> marco(int delay) {
|
||||
* TaskCompletionSource<String> taskCompletionSource = new TaskCompletionSource<>();
|
||||
*
|
||||
* new Handler().postDelayed(() -> taskCompletionSource.setResult("polo"), delay);
|
||||
*
|
||||
* return taskCompletionSource.getTask();
|
||||
* }
|
||||
* }
|
||||
* </pre>
|
||||
* And then your APIs can be used as any other {@link Task}-consuming APIs:
|
||||
* <pre>
|
||||
* public class MyActivity extends Activity {
|
||||
* @Override
|
||||
* public void onStart() {
|
||||
* super.onStart();
|
||||
*
|
||||
* marco(1000).addOnCompleteListener(
|
||||
* task -> Log.d(TAG, "got message after one second: " + task.getResult()));
|
||||
* }
|
||||
* }
|
||||
* </pre>
|
||||
*
|
||||
* and later complete it by either
|
||||
* calling {@link #setResult(TResult)} or {@link #setException(Exception)}.
|
||||
*/
|
||||
@PublicApi
|
||||
public class TaskCompletionSource<TResult> {
|
||||
private TaskImpl<TResult> task = new TaskImpl<>();
|
||||
|
||||
/**
|
||||
* Creates an instance of {@link TaskCompletionSource}.
|
||||
*/
|
||||
public TaskCompletionSource() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an instance of {@link TaskCompletionSource} with a {@link CancellationToken} so that the Task can be set to canceled when {@link CancellationToken} is canceled.
|
||||
*/
|
||||
public TaskCompletionSource(CancellationToken token) {
|
||||
token.onCanceledRequested(() -> {
|
||||
try {
|
||||
task.cancel();
|
||||
} catch (DuplicateTaskCompletionException ignored) {
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the Task.
|
||||
*/
|
||||
public Task<TResult> getTask() {
|
||||
return task;
|
||||
}
|
||||
|
||||
/**
|
||||
* Completes the Task with the specified exception.
|
||||
*
|
||||
* @throws IllegalStateException if the Task is already complete
|
||||
*/
|
||||
public void setException(Exception e) {
|
||||
task.setException(e);
|
||||
}
|
||||
|
||||
/**
|
||||
* Completes the Task with the specified exception, unless the Task has already completed.
|
||||
* If the Task has already completed, the call does nothing.
|
||||
*
|
||||
* @return {@code true} if the exception was set successfully, {@code false} otherwise
|
||||
*/
|
||||
public boolean trySetException(Exception e) {
|
||||
try {
|
||||
setException(e);
|
||||
return true;
|
||||
} catch (DuplicateTaskCompletionException ignored) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Completes the Task with the specified result.
|
||||
*
|
||||
* @throws IllegalStateException if the Task is already complete
|
||||
*/
|
||||
public void setResult(TResult result) {
|
||||
task.setResult(result);
|
||||
}
|
||||
|
||||
/**
|
||||
* Completes the Task with the specified result, unless the Task has already completed.
|
||||
* If the Task has already completed, the call does nothing.
|
||||
*
|
||||
* @return {@code true} if the result was set successfully, {@code false} otherwise
|
||||
*/
|
||||
public boolean trySetResult(TResult result) {
|
||||
try {
|
||||
setResult(result);
|
||||
return true;
|
||||
} catch (DuplicateTaskCompletionException ignored) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,33 @@
|
|||
/*
|
||||
* SPDX-FileCopyrightText: 2020, microG Project Team
|
||||
* SPDX-License-Identifier: Apache-2.0 AND CC-BY-4.0
|
||||
* Notice: Portions of this file are reproduced from work created and shared by Google and used
|
||||
* according to terms described in the Creative Commons 4.0 Attribution License.
|
||||
* See https://developers.google.com/readme/policies for details.
|
||||
*/
|
||||
|
||||
package com.google.android.gms.tasks;
|
||||
|
||||
import android.os.Handler;
|
||||
import android.os.Looper;
|
||||
|
||||
import org.microg.gms.common.PublicApi;
|
||||
|
||||
import java.util.concurrent.Executor;
|
||||
|
||||
/**
|
||||
* Standard {@link Executor} instances for use with {@link Task}.
|
||||
*/
|
||||
@PublicApi
|
||||
public final class TaskExecutors {
|
||||
/**
|
||||
* An Executor that uses the main application thread.
|
||||
*/
|
||||
public static final Executor MAIN_THREAD = new Executor() {
|
||||
private Handler handler = new Handler(Looper.getMainLooper());
|
||||
@Override
|
||||
public void execute(Runnable command) {
|
||||
handler.post(command);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
|
@ -0,0 +1,306 @@
|
|||
/*
|
||||
* SPDX-FileCopyrightText: 2016 microG Project Team
|
||||
* SPDX-License-Identifier: Apache-2.0 AND CC-BY-4.0
|
||||
* Notice: Portions of this file are reproduced from work created and shared by Google and used
|
||||
* according to terms described in the Creative Commons 4.0 Attribution License.
|
||||
* See https://developers.google.com/readme/policies for details.
|
||||
*/
|
||||
|
||||
package com.google.android.gms.tasks;
|
||||
|
||||
import android.os.Handler;
|
||||
import android.os.Looper;
|
||||
|
||||
import org.microg.gms.common.PublicApi;
|
||||
import org.microg.gms.tasks.CancellationTokenImpl;
|
||||
import org.microg.gms.tasks.TaskImpl;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.Callable;
|
||||
import java.util.concurrent.CancellationException;
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import java.util.concurrent.Executor;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.TimeoutException;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
|
||||
/**
|
||||
* {@link Task} utility methods.
|
||||
*/
|
||||
@PublicApi
|
||||
public final class Tasks {
|
||||
|
||||
/**
|
||||
* Blocks until the specified Task is complete.
|
||||
*
|
||||
* @return the Task's result
|
||||
* @throws ExecutionException if the Task fails
|
||||
* @throws InterruptedException if an interrupt occurs while waiting for the Task to complete
|
||||
* @throws TimeoutException if the specified timeout is reached before the Task completes
|
||||
*/
|
||||
public static <TResult> TResult await(Task<TResult> task, long timeout, TimeUnit unit) throws ExecutionException, InterruptedException, TimeoutException {
|
||||
if (task == null) throw new IllegalArgumentException("Task must not be null");
|
||||
if (timeout <= 0) throw new IllegalArgumentException("Timeout must be positive");
|
||||
if (unit == null) throw new IllegalArgumentException("TimeUnit must not be null");
|
||||
if (task.isComplete()) return handleCompletedTask(task);
|
||||
CountDownLatch latch = new CountDownLatch(1);
|
||||
task.addOnCompleteListener(Runnable::run, completedTask -> latch.countDown());
|
||||
if (latch.await(timeout, unit)) {
|
||||
return handleCompletedTask(task);
|
||||
}
|
||||
throw new TimeoutException("Timed out waiting for Task");
|
||||
}
|
||||
|
||||
/**
|
||||
* Blocks until the specified Task is complete.
|
||||
*
|
||||
* @return the Task's result
|
||||
* @throws ExecutionException if the Task fails
|
||||
* @throws InterruptedException if an interrupt occurs while waiting for the Task to complete
|
||||
*/
|
||||
public static <TResult> TResult await(Task<TResult> task) throws ExecutionException, InterruptedException {
|
||||
if (Looper.getMainLooper().getThread() == Thread.currentThread())
|
||||
throw new IllegalStateException("Must not be invoked on main thread");
|
||||
if (task == null) throw new IllegalArgumentException("Task must not be null");
|
||||
if (task.isComplete()) return handleCompletedTask(task);
|
||||
CountDownLatch latch = new CountDownLatch(1);
|
||||
task.addOnCompleteListener(Runnable::run, completedTask -> latch.countDown());
|
||||
latch.await();
|
||||
return handleCompletedTask(task);
|
||||
}
|
||||
|
||||
private static <TResult> TResult handleCompletedTask(Task<TResult> task) throws ExecutionException {
|
||||
if (task.isSuccessful()) {
|
||||
return task.getResult();
|
||||
}
|
||||
if (task.isCanceled()) {
|
||||
throw new CancellationException("Task is already canceled");
|
||||
}
|
||||
throw new ExecutionException(task.getException());
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a {@link Task} that will be completed with the result of the specified {@code Callable}.
|
||||
* <p/>
|
||||
* If a non-{@link Exception} throwable is thrown in the callable, the {@link Task} will be failed with a
|
||||
* {@link RuntimeException} whose cause is the original throwable.
|
||||
* <p/>
|
||||
* The {@code Callable} will be called on the main application thread.
|
||||
*
|
||||
* @deprecated Use {@link TaskCompletionSource} instead, which allows the caller to manage their own Executor.
|
||||
*/
|
||||
@Deprecated
|
||||
public static <TResult> Task<TResult> call(Callable<TResult> callable) {
|
||||
return call(TaskExecutors.MAIN_THREAD, callable);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a {@link Task} that will be completed with the result of the specified {@code Callable}.
|
||||
* <p/>
|
||||
* If a non-{@link Exception} throwable is thrown in the callable, the {@link Task} will be failed with a
|
||||
* {@link RuntimeException} whose cause is the original throwable.
|
||||
*
|
||||
* @param executor the Executor to use to call the {@code Callable}
|
||||
* @deprecated Use {@link TaskCompletionSource} instead, which allows the caller to manage their own Executor.
|
||||
*/
|
||||
@Deprecated
|
||||
public static <TResult> Task<TResult> call(Executor executor, Callable<TResult> callable) {
|
||||
if (executor == null) throw new IllegalArgumentException("Executor must not be null");
|
||||
if (callable == null) throw new IllegalArgumentException("Callable must not be null");
|
||||
TaskCompletionSource<TResult> taskCompletionSource = new TaskCompletionSource<>();
|
||||
executor.execute(() -> {
|
||||
try {
|
||||
taskCompletionSource.setResult(callable.call());
|
||||
} catch (Exception e) {
|
||||
taskCompletionSource.trySetException(e);
|
||||
} catch (Throwable t) {
|
||||
taskCompletionSource.trySetException(new RuntimeException(t));
|
||||
}
|
||||
});
|
||||
return taskCompletionSource.getTask();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a canceled Task.
|
||||
*/
|
||||
public static <TResult> Task<TResult> forCancelled() {
|
||||
TaskImpl<TResult> task = new TaskImpl<>();
|
||||
task.cancel();
|
||||
return task;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a completed Task with the specified exception.
|
||||
*/
|
||||
public static <TResult> Task<TResult> forException(Exception e) {
|
||||
TaskImpl<TResult> task = new TaskImpl<>();
|
||||
task.setException(e);
|
||||
return task;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a completed Task with the specified result.
|
||||
*/
|
||||
public static <TResult> Task<TResult> forResult(TResult result) {
|
||||
TaskImpl<TResult> task = new TaskImpl<>();
|
||||
task.setResult(result);
|
||||
return task;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a Task that completes successfully when all of the specified Tasks complete
|
||||
* successfully. Does not accept nulls.
|
||||
* <p/>
|
||||
* The returned Task would fail if any of the provided Tasks fail. The returned Task would be set to canceled if
|
||||
* any of the provided Tasks is canceled and no failure is detected.
|
||||
*
|
||||
* @throws NullPointerException if any of the provided Tasks are null
|
||||
*/
|
||||
public static Task<Void> whenAll(Collection<? extends Task<?>> tasks) {
|
||||
if (tasks == null || tasks.isEmpty()) {
|
||||
return forResult(null);
|
||||
}
|
||||
for (Task<?> task : tasks) {
|
||||
if (task == null) throw new NullPointerException("null tasks are not accepted");
|
||||
}
|
||||
TaskImpl<Void> allTask = new TaskImpl<>();
|
||||
AtomicInteger finishedTasks = new AtomicInteger(0);
|
||||
AtomicInteger failedTasks = new AtomicInteger(0);
|
||||
AtomicReference<Exception> exceptionReference = new AtomicReference<>(null);
|
||||
AtomicBoolean isCancelled = new AtomicBoolean(false);
|
||||
for (Task<?> task : tasks) {
|
||||
task.addOnCompleteListener(Runnable::run, completedTask -> {
|
||||
if (!completedTask.isSuccessful()) {
|
||||
if (completedTask.isCanceled()) {
|
||||
isCancelled.set(true);
|
||||
} else {
|
||||
exceptionReference.set(completedTask.getException());
|
||||
failedTasks.incrementAndGet();
|
||||
}
|
||||
}
|
||||
if (finishedTasks.incrementAndGet() != tasks.size()) return;
|
||||
Exception exception = exceptionReference.get();
|
||||
if (exception != null) {
|
||||
allTask.setException(new ExecutionException(failedTasks.get() + " out of " + tasks.size() + " underlying tasks failed", exception));
|
||||
} else if (isCancelled.get()) {
|
||||
allTask.cancel();
|
||||
} else {
|
||||
allTask.setResult(null);
|
||||
}
|
||||
});
|
||||
}
|
||||
return allTask;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a Task that completes successfully when all of the specified Tasks complete
|
||||
* successfully. Does not accept nulls.
|
||||
* <p/>
|
||||
* The returned Task would fail if any of the provided Tasks fail. The returned Task would be set to canceled if
|
||||
* any of the provided Tasks is canceled and no failure is detected.
|
||||
*
|
||||
* @throws NullPointerException if any of the provided Tasks are null
|
||||
*/
|
||||
public static Task<Void> whenAll(Task<?>... tasks) {
|
||||
if (tasks == null || tasks.length == 0) {
|
||||
return forResult(null);
|
||||
}
|
||||
return whenAll(Arrays.asList(tasks));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a Task with a list of Tasks that completes successfully when all of the specified Tasks complete. This
|
||||
* Task would always succeed even if any of the provided Tasks fail or canceled. Does not accept nulls.
|
||||
*
|
||||
* @throws NullPointerException if any of the provided Tasks are null
|
||||
*/
|
||||
public static Task<List<Task<?>>> whenAllComplete(Task<?>... tasks) {
|
||||
if (tasks == null || tasks.length == 0) {
|
||||
return forResult(Collections.emptyList());
|
||||
}
|
||||
return whenAllComplete(Arrays.asList(tasks));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a Task with a list of Tasks that completes successfully when all of the specified Tasks complete. This
|
||||
* Task would always succeed even if any of the provided Tasks fail or canceled. Does not accept nulls.
|
||||
*
|
||||
* @throws NullPointerException if any of the provided Tasks are null
|
||||
*/
|
||||
public static Task<List<Task<?>>> whenAllComplete(Collection<? extends Task<?>> tasks) {
|
||||
if (tasks == null || tasks.isEmpty()) {
|
||||
return forResult(Collections.emptyList());
|
||||
}
|
||||
return whenAll(tasks).continueWithTask(TaskExecutors.MAIN_THREAD, allTask -> forResult(new ArrayList<>(tasks)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a Task with a list of Task results that completes successfully when all of the specified Tasks complete
|
||||
* successfully. This Task would fail if any of the provided Tasks fail. Does not accept nulls.
|
||||
* <p/>
|
||||
* This Task would be set to canceled if any of the provided Tasks is canceled and no failure is detected.
|
||||
*
|
||||
* @throws NullPointerException if any of the provided Tasks are null
|
||||
*/
|
||||
public static <TResult> Task<List<TResult>> whenAllSuccess(Task<? extends TResult>... tasks) {
|
||||
if (tasks == null || tasks.length == 0) {
|
||||
return forResult(Collections.emptyList());
|
||||
}
|
||||
return whenAllSuccess(Arrays.asList(tasks));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a Task with a list of Task results that completes successfully when all of the specified Tasks complete
|
||||
* successfully. This Task would fail if any of the provided Tasks fail. Does not accept nulls.
|
||||
* <p/>
|
||||
* This Task would be set to canceled if any of the provided Tasks is canceled and no failure is detected.
|
||||
*
|
||||
* @throws NullPointerException if any of the provided Tasks are null
|
||||
*/
|
||||
public static <TResult> Task<List<TResult>> whenAllSuccess(Collection<? extends Task<? extends TResult>> tasks) {
|
||||
if (tasks == null || tasks.isEmpty()) {
|
||||
return forResult(Collections.emptyList());
|
||||
}
|
||||
return whenAll(tasks).continueWithTask(TaskExecutors.MAIN_THREAD, allTask -> {
|
||||
List<TResult> results = new ArrayList<>(tasks.size());
|
||||
for (Task<? extends TResult> task : tasks) {
|
||||
results.add(task.getResult());
|
||||
}
|
||||
return forResult(results);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a new Task which will return a TimeoutException if a result is not returned within the specified time
|
||||
* period.
|
||||
*
|
||||
* @return A new Task.
|
||||
*/
|
||||
public static <T> Task<T> withTimeout(Task<T> task, long timeout, TimeUnit unit) {
|
||||
if (task == null) throw new IllegalArgumentException("Task must not be null");
|
||||
if (timeout <= 0) throw new IllegalArgumentException("Timeout must be positive");
|
||||
if (unit == null) throw new IllegalArgumentException("TimeUnit must not be null");
|
||||
CancellationTokenImpl cancellationToken = new CancellationTokenImpl();
|
||||
TaskCompletionSource<T> taskCompletionSource = new TaskCompletionSource<>(cancellationToken);
|
||||
Handler handler = new Handler(Looper.getMainLooper());
|
||||
handler.postDelayed(() -> taskCompletionSource.trySetException(new TimeoutException()), unit.toMillis(timeout));
|
||||
task.addOnCompleteListener(completedTask -> {
|
||||
handler.removeCallbacksAndMessages(null);
|
||||
if (completedTask.isSuccessful()) {
|
||||
taskCompletionSource.trySetResult(completedTask.getResult());
|
||||
} else if (completedTask.isCanceled()) {
|
||||
cancellationToken.cancel();
|
||||
} else {
|
||||
taskCompletionSource.trySetException(completedTask.getException());
|
||||
}
|
||||
});
|
||||
return taskCompletionSource.getTask();
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,32 @@
|
|||
/*
|
||||
* SPDX-FileCopyrightText: 2020, microG Project Team
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
package org.microg.gms.tasks;
|
||||
|
||||
import com.google.android.gms.tasks.CancellationToken;
|
||||
import com.google.android.gms.tasks.DuplicateTaskCompletionException;
|
||||
import com.google.android.gms.tasks.OnTokenCanceledListener;
|
||||
|
||||
public class CancellationTokenImpl extends CancellationToken {
|
||||
private TaskImpl<Void> task = new TaskImpl<>();
|
||||
|
||||
@Override
|
||||
public boolean isCancellationRequested() {
|
||||
return task.isComplete();
|
||||
}
|
||||
|
||||
@Override
|
||||
public CancellationToken onCanceledRequested(OnTokenCanceledListener listener) {
|
||||
task.addOnSuccessListener(aVoid -> listener.onCanceled());
|
||||
return this;
|
||||
}
|
||||
|
||||
public void cancel() {
|
||||
try {
|
||||
task.cancel();
|
||||
} catch (DuplicateTaskCompletionException ignored) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,34 @@
|
|||
/*
|
||||
* SPDX-FileCopyrightText: 2020, microG Project Team
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
package org.microg.gms.tasks;
|
||||
|
||||
import com.google.android.gms.tasks.OnCanceledListener;
|
||||
import com.google.android.gms.tasks.Task;
|
||||
|
||||
import java.util.concurrent.Executor;
|
||||
|
||||
public class CancelledExecutor<TResult> extends UpdateExecutor<TResult> {
|
||||
private OnCanceledListener listener;
|
||||
|
||||
public CancelledExecutor(Executor executor, OnCanceledListener listener) {
|
||||
super(executor);
|
||||
this.listener = listener;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTaskUpdate(Task<TResult> task) {
|
||||
if (task.isCanceled()) {
|
||||
execute(() -> listener.onCanceled());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void cancel() {
|
||||
super.cancel();
|
||||
listener = null;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -0,0 +1,33 @@
|
|||
/*
|
||||
* SPDX-FileCopyrightText: 2020, microG Project Team
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
package org.microg.gms.tasks;
|
||||
|
||||
import com.google.android.gms.tasks.OnCompleteListener;
|
||||
import com.google.android.gms.tasks.Task;
|
||||
|
||||
import java.util.concurrent.Executor;
|
||||
|
||||
public class CompletedExecutor<TResult> extends UpdateExecutor<TResult> {
|
||||
private OnCompleteListener<TResult> listener;
|
||||
|
||||
public CompletedExecutor(Executor executor, OnCompleteListener<TResult> listener) {
|
||||
super(executor);
|
||||
this.listener = listener;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTaskUpdate(Task<TResult> task) {
|
||||
if (task.isComplete()) {
|
||||
execute(() -> listener.onComplete(task));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void cancel() {
|
||||
super.cancel();
|
||||
listener = null;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,46 @@
|
|||
/*
|
||||
* SPDX-FileCopyrightText: 2020, microG Project Team
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
package org.microg.gms.tasks;
|
||||
|
||||
import com.google.android.gms.tasks.Continuation;
|
||||
import com.google.android.gms.tasks.Task;
|
||||
import com.google.android.gms.tasks.TaskCompletionSource;
|
||||
|
||||
import java.util.concurrent.Executor;
|
||||
|
||||
public class ContinuationExecutor<TResult, TContinuationResult> extends UpdateExecutor<TResult> {
|
||||
private Continuation<TResult, TContinuationResult> continuation;
|
||||
private TaskCompletionSource<TContinuationResult> completionSource = new TaskCompletionSource<>();
|
||||
|
||||
public ContinuationExecutor(Executor executor, Continuation<TResult, TContinuationResult> continuation) {
|
||||
super(executor);
|
||||
this.continuation = continuation;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTaskUpdate(Task<TResult> task) {
|
||||
if (task.isComplete()) {
|
||||
execute(() -> {
|
||||
try {
|
||||
completionSource.setResult(continuation.then(task));
|
||||
} catch (Exception e) {
|
||||
completionSource.setException(e);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
public Task<TContinuationResult> getTask() {
|
||||
return completionSource.getTask();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void cancel() {
|
||||
super.cancel();
|
||||
continuation = null;
|
||||
completionSource = null;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,52 @@
|
|||
/*
|
||||
* SPDX-FileCopyrightText: 2020, microG Project Team
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
package org.microg.gms.tasks;
|
||||
|
||||
import com.google.android.gms.tasks.Continuation;
|
||||
import com.google.android.gms.tasks.Task;
|
||||
import com.google.android.gms.tasks.TaskCompletionSource;
|
||||
|
||||
import java.util.concurrent.Executor;
|
||||
|
||||
public class ContinuationWithExecutor<TResult, TContinuationResult> extends UpdateExecutor<TResult> {
|
||||
private Continuation<TResult, Task<TContinuationResult>> continuation;
|
||||
private TaskCompletionSource<TContinuationResult> completionSource = new TaskCompletionSource<>();
|
||||
|
||||
public ContinuationWithExecutor(Executor executor, Continuation<TResult, Task<TContinuationResult>> continuation) {
|
||||
super(executor);
|
||||
this.continuation = continuation;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTaskUpdate(Task<TResult> task) {
|
||||
if (task.isComplete()) {
|
||||
execute(() -> {
|
||||
try {
|
||||
continuation.then(task).addOnCompleteListener(this, (subTask) -> {
|
||||
if (subTask.isSuccessful()) {
|
||||
completionSource.setResult(subTask.getResult());
|
||||
} else {
|
||||
completionSource.setException(subTask.getException());
|
||||
}
|
||||
});
|
||||
} catch (Exception e) {
|
||||
completionSource.setException(e);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
public Task<TContinuationResult> getTask() {
|
||||
return completionSource.getTask();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void cancel() {
|
||||
super.cancel();
|
||||
continuation = null;
|
||||
completionSource = null;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,33 @@
|
|||
/*
|
||||
* SPDX-FileCopyrightText: 2020, microG Project Team
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
package org.microg.gms.tasks;
|
||||
|
||||
import com.google.android.gms.tasks.OnFailureListener;
|
||||
import com.google.android.gms.tasks.Task;
|
||||
|
||||
import java.util.concurrent.Executor;
|
||||
|
||||
public class FailureExecutor<TResult> extends UpdateExecutor<TResult> {
|
||||
private OnFailureListener listener;
|
||||
|
||||
public FailureExecutor(Executor executor, OnFailureListener listener) {
|
||||
super(executor);
|
||||
this.listener = listener;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTaskUpdate(Task<TResult> task) {
|
||||
if (task.isComplete() && !task.isSuccessful() && !task.isCanceled()) {
|
||||
execute(() -> listener.onFailure(task.getException()));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void cancel() {
|
||||
super.cancel();
|
||||
listener = null;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,54 @@
|
|||
/*
|
||||
* SPDX-FileCopyrightText: 2020, microG Project Team
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
package org.microg.gms.tasks;
|
||||
|
||||
import com.google.android.gms.tasks.SuccessContinuation;
|
||||
import com.google.android.gms.tasks.Task;
|
||||
import com.google.android.gms.tasks.TaskCompletionSource;
|
||||
|
||||
import java.util.concurrent.Executor;
|
||||
|
||||
public class SuccessContinuationExecutor<TResult, TContinuationResult> extends UpdateExecutor<TResult> {
|
||||
private SuccessContinuation<TResult, TContinuationResult> continuation;
|
||||
private TaskCompletionSource<TContinuationResult> completionSource = new TaskCompletionSource<>();
|
||||
|
||||
public SuccessContinuationExecutor(Executor executor, SuccessContinuation<TResult, TContinuationResult> continuation) {
|
||||
super(executor);
|
||||
this.continuation = continuation;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTaskUpdate(Task<TResult> task) {
|
||||
if (task.isSuccessful()) {
|
||||
execute(() -> {
|
||||
try {
|
||||
continuation.then(task.getResult()).addOnCompleteListener(this, (subTask) -> {
|
||||
if (subTask.isSuccessful()) {
|
||||
completionSource.setResult(subTask.getResult());
|
||||
} else {
|
||||
completionSource.setException(subTask.getException());
|
||||
}
|
||||
});
|
||||
} catch (Exception e) {
|
||||
completionSource.setException(e);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
completionSource.setException(task.getException());
|
||||
}
|
||||
}
|
||||
|
||||
public Task<TContinuationResult> getTask() {
|
||||
return completionSource.getTask();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void cancel() {
|
||||
super.cancel();
|
||||
continuation = null;
|
||||
completionSource = null;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,33 @@
|
|||
/*
|
||||
* SPDX-FileCopyrightText: 2020, microG Project Team
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
package org.microg.gms.tasks;
|
||||
|
||||
import com.google.android.gms.tasks.OnSuccessListener;
|
||||
import com.google.android.gms.tasks.Task;
|
||||
|
||||
import java.util.concurrent.Executor;
|
||||
|
||||
public class SuccessExecutor<TResult> extends UpdateExecutor<TResult> {
|
||||
private OnSuccessListener<? super TResult> listener;
|
||||
|
||||
public SuccessExecutor(Executor executor, OnSuccessListener<? super TResult> listener) {
|
||||
super(executor);
|
||||
this.listener = listener;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTaskUpdate(Task<TResult> task) {
|
||||
if (task.isSuccessful()) {
|
||||
execute(() -> listener.onSuccess(task.getResult()));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void cancel() {
|
||||
super.cancel();
|
||||
listener = null;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,240 @@
|
|||
/*
|
||||
* SPDX-FileCopyrightText: 2020, microG Project Team
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
package org.microg.gms.tasks;
|
||||
|
||||
import android.app.Activity;
|
||||
|
||||
import com.google.android.gms.tasks.Continuation;
|
||||
import com.google.android.gms.tasks.DuplicateTaskCompletionException;
|
||||
import com.google.android.gms.tasks.OnCanceledListener;
|
||||
import com.google.android.gms.tasks.OnCompleteListener;
|
||||
import com.google.android.gms.tasks.OnFailureListener;
|
||||
import com.google.android.gms.tasks.OnSuccessListener;
|
||||
import com.google.android.gms.tasks.RuntimeExecutionException;
|
||||
import com.google.android.gms.tasks.SuccessContinuation;
|
||||
import com.google.android.gms.tasks.Task;
|
||||
|
||||
import java.util.Queue;
|
||||
import java.util.concurrent.CancellationException;
|
||||
import java.util.concurrent.Executor;
|
||||
import java.util.concurrent.LinkedBlockingQueue;
|
||||
|
||||
import static com.google.android.gms.tasks.TaskExecutors.MAIN_THREAD;
|
||||
|
||||
public class TaskImpl<TResult> extends Task<TResult> {
|
||||
private final Object lock = new Object();
|
||||
private boolean completed;
|
||||
private boolean cancelled;
|
||||
private TResult result;
|
||||
private Exception exception;
|
||||
private Queue<UpdateListener<TResult>> completionQueue = new LinkedBlockingQueue<>();
|
||||
|
||||
@Override
|
||||
public Task<TResult> addOnCanceledListener(OnCanceledListener listener) {
|
||||
return addOnCanceledListener(MAIN_THREAD, listener);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Task<TResult> addOnCanceledListener(Executor executor, OnCanceledListener listener) {
|
||||
return enqueueOrInvoke(new CancelledExecutor<>(executor, listener));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Task<TResult> addOnCanceledListener(Activity activity, OnCanceledListener listener) {
|
||||
return enqueueOrInvoke(activity, new CancelledExecutor<>(MAIN_THREAD, listener));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Task<TResult> addOnCompleteListener(OnCompleteListener<TResult> listener) {
|
||||
return addOnCompleteListener(MAIN_THREAD, listener);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Task<TResult> addOnCompleteListener(Executor executor, OnCompleteListener<TResult> listener) {
|
||||
return enqueueOrInvoke(new CompletedExecutor<>(executor, listener));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Task<TResult> addOnCompleteListener(Activity activity, OnCompleteListener<TResult> listener) {
|
||||
return enqueueOrInvoke(activity, new CompletedExecutor<>(MAIN_THREAD, listener));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Task<TResult> addOnFailureListener(OnFailureListener listener) {
|
||||
return addOnFailureListener(MAIN_THREAD, listener);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Task<TResult> addOnFailureListener(Executor executor, OnFailureListener listener) {
|
||||
return enqueueOrInvoke(new FailureExecutor<>(executor, listener));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Task<TResult> addOnFailureListener(Activity activity, OnFailureListener listener) {
|
||||
return enqueueOrInvoke(activity, new FailureExecutor<>(MAIN_THREAD, listener));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Task<TResult> addOnSuccessListener(OnSuccessListener<? super TResult> listener) {
|
||||
return addOnSuccessListener(MAIN_THREAD, listener);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Task<TResult> addOnSuccessListener(Executor executor, OnSuccessListener<? super TResult> listener) {
|
||||
return enqueueOrInvoke(new SuccessExecutor<>(executor, listener));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Task<TResult> addOnSuccessListener(Activity activity, OnSuccessListener<? super TResult> listener) {
|
||||
return enqueueOrInvoke(activity, new SuccessExecutor<>(MAIN_THREAD, listener));
|
||||
}
|
||||
|
||||
@Override
|
||||
public <TContinuationResult> Task<TContinuationResult> continueWith(Continuation<TResult, TContinuationResult> continuation) {
|
||||
return continueWith(MAIN_THREAD, continuation);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <TContinuationResult> Task<TContinuationResult> continueWith(Executor executor, Continuation<TResult, TContinuationResult> continuation) {
|
||||
ContinuationExecutor<TResult, TContinuationResult> c = new ContinuationExecutor<>(executor, continuation);
|
||||
enqueueOrInvoke(c);
|
||||
return c.getTask();
|
||||
}
|
||||
|
||||
@Override
|
||||
public <TContinuationResult> Task<TContinuationResult> continueWithTask(Continuation<TResult, Task<TContinuationResult>> continuation) {
|
||||
return continueWithTask(MAIN_THREAD, continuation);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <TContinuationResult> Task<TContinuationResult> continueWithTask(Executor executor, Continuation<TResult, Task<TContinuationResult>> continuation) {
|
||||
ContinuationWithExecutor<TResult, TContinuationResult> c = new ContinuationWithExecutor<>(executor, continuation);
|
||||
enqueueOrInvoke(c);
|
||||
return c.getTask();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Exception getException() {
|
||||
synchronized (lock) {
|
||||
return exception;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public TResult getResult() {
|
||||
synchronized (lock) {
|
||||
if (!completed) throw new IllegalStateException("Task is not yet complete");
|
||||
if (cancelled) throw new CancellationException("Task is canceled");
|
||||
if (exception != null) throw new RuntimeExecutionException(exception);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public <X extends Throwable> TResult getResult(Class<X> exceptionType) throws X {
|
||||
synchronized (lock) {
|
||||
if (!completed) throw new IllegalStateException("Task is not yet complete");
|
||||
if (cancelled) throw new CancellationException("Task is canceled");
|
||||
if (exceptionType.isInstance(exception)) throw exceptionType.cast(exception);
|
||||
if (exception != null) throw new RuntimeExecutionException(exception);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isCanceled() {
|
||||
synchronized (lock) {
|
||||
return cancelled;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isComplete() {
|
||||
synchronized (lock) {
|
||||
return completed;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isSuccessful() {
|
||||
synchronized (lock) {
|
||||
return completed && !cancelled && exception == null;
|
||||
}
|
||||
}
|
||||
|
||||
private void registerActivityStop(Activity activity, UpdateListener<TResult> listener) {
|
||||
UpdateListenerLifecycleObserver.getObserverForActivity(activity).registerActivityStopListener(listener);
|
||||
}
|
||||
|
||||
private Task<TResult> enqueueOrInvoke(Activity activity, UpdateListener<TResult> listener) {
|
||||
synchronized (lock) {
|
||||
if (completed) {
|
||||
listener.onTaskUpdate(this);
|
||||
} else {
|
||||
completionQueue.offer(listener);
|
||||
registerActivityStop(activity, listener);
|
||||
}
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
private Task<TResult> enqueueOrInvoke(UpdateListener<TResult> listener) {
|
||||
synchronized (lock) {
|
||||
if (completed) {
|
||||
listener.onTaskUpdate(this);
|
||||
} else {
|
||||
completionQueue.offer(listener);
|
||||
}
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
private void notifyQueue() {
|
||||
UpdateListener<TResult> listener;
|
||||
while ((listener = completionQueue.poll()) != null) {
|
||||
listener.onTaskUpdate(this);
|
||||
}
|
||||
}
|
||||
|
||||
public void cancel() {
|
||||
synchronized (lock) {
|
||||
if (completed) throw DuplicateTaskCompletionException.of(this);
|
||||
this.completed = true;
|
||||
this.cancelled = true;
|
||||
notifyQueue();
|
||||
}
|
||||
}
|
||||
|
||||
public void setResult(TResult result) {
|
||||
synchronized (lock) {
|
||||
if (completed) throw DuplicateTaskCompletionException.of(this);
|
||||
this.completed = true;
|
||||
this.result = result;
|
||||
notifyQueue();
|
||||
}
|
||||
}
|
||||
|
||||
public void setException(Exception exception) {
|
||||
synchronized (lock) {
|
||||
if (completed) throw DuplicateTaskCompletionException.of(this);
|
||||
this.completed = true;
|
||||
this.exception = exception;
|
||||
notifyQueue();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public <TContinuationResult> Task<TContinuationResult> onSuccessTask(SuccessContinuation<TResult, TContinuationResult> successContinuation) {
|
||||
return onSuccessTask(MAIN_THREAD, successContinuation);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <TContinuationResult> Task<TContinuationResult> onSuccessTask(Executor executor, SuccessContinuation<TResult, TContinuationResult> successContinuation) {
|
||||
SuccessContinuationExecutor<TResult, TContinuationResult> c = new SuccessContinuationExecutor<>(executor, successContinuation);
|
||||
enqueueOrInvoke(c);
|
||||
return c.getTask();
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,27 @@
|
|||
/*
|
||||
* SPDX-FileCopyrightText: 2020, microG Project Team
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
package org.microg.gms.tasks;
|
||||
|
||||
import java.util.concurrent.Executor;
|
||||
|
||||
public abstract class UpdateExecutor<TResult> implements UpdateListener<TResult>, Executor {
|
||||
private Executor executor;
|
||||
|
||||
public UpdateExecutor(Executor executor) {
|
||||
this.executor = executor;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void execute(Runnable runnable) {
|
||||
if (executor == null) return;
|
||||
executor.execute(runnable);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void cancel() {
|
||||
executor = null;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,14 @@
|
|||
/*
|
||||
* SPDX-FileCopyrightText: 2020, microG Project Team
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
package org.microg.gms.tasks;
|
||||
|
||||
import com.google.android.gms.tasks.Task;
|
||||
|
||||
public interface UpdateListener<TResult> {
|
||||
void onTaskUpdate(Task<TResult> task);
|
||||
|
||||
void cancel();
|
||||
}
|
||||
|
|
@ -0,0 +1,98 @@
|
|||
/*
|
||||
* SPDX-FileCopyrightText: 2020, microG Project Team
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
package org.microg.gms.tasks;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.app.Application;
|
||||
import android.os.Bundle;
|
||||
|
||||
import java.lang.ref.WeakReference;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.WeakHashMap;
|
||||
|
||||
public class UpdateListenerLifecycleObserver {
|
||||
private static WeakHashMap<Activity, WeakReference<UpdateListenerLifecycleObserver>> map = new WeakHashMap<>();
|
||||
private static boolean activityLifecycleCallbacksRegistered = false;
|
||||
private List<WeakReference<UpdateListener<?>>> list = new ArrayList<>();
|
||||
|
||||
public synchronized static UpdateListenerLifecycleObserver getObserverForActivity(Activity activity) {
|
||||
WeakReference<UpdateListenerLifecycleObserver> ref = map.get(activity);
|
||||
if (ref != null) {
|
||||
UpdateListenerLifecycleObserver observer = ref.get();
|
||||
if (observer != null) {
|
||||
return observer;
|
||||
}
|
||||
}
|
||||
|
||||
if (!activityLifecycleCallbacksRegistered) {
|
||||
activity.getApplication().registerActivityLifecycleCallbacks(new MyActivityLifecycleCallbacks());
|
||||
activityLifecycleCallbacksRegistered = true;
|
||||
}
|
||||
|
||||
UpdateListenerLifecycleObserver newInstance = new UpdateListenerLifecycleObserver();
|
||||
map.put(activity, new WeakReference<>(newInstance));
|
||||
return newInstance;
|
||||
}
|
||||
|
||||
private UpdateListenerLifecycleObserver() {
|
||||
}
|
||||
|
||||
public synchronized void registerActivityStopListener(UpdateListener<?> listener) {
|
||||
list.add(new WeakReference<>(listener));
|
||||
}
|
||||
|
||||
public synchronized void onStop() {
|
||||
for (WeakReference<UpdateListener<?>> ref : list) {
|
||||
UpdateListener<?> listener = ref.get();
|
||||
listener.cancel();
|
||||
}
|
||||
list.clear();
|
||||
}
|
||||
|
||||
private static class MyActivityLifecycleCallbacks implements Application.ActivityLifecycleCallbacks {
|
||||
@Override
|
||||
public void onActivityCreated(Activity activity, Bundle savedInstanceState) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onActivityStarted(Activity activity) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onActivityResumed(Activity activity) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onActivityPaused(Activity activity) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onActivityStopped(Activity activity) {
|
||||
WeakReference<UpdateListenerLifecycleObserver> ref = map.get(activity);
|
||||
if (ref != null) {
|
||||
UpdateListenerLifecycleObserver observer = ref.get();
|
||||
if (observer != null) {
|
||||
observer.onStop();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onActivitySaveInstanceState(Activity activity, Bundle outState) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onActivityDestroyed(Activity activity) {
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue