/* * SPDX-FileCopyrightText: 2020, microG Project Team * SPDX-License-Identifier: Apache-2.0 */ package org.microg.gms.tasks; import static com.google.android.gms.tasks.TaskExecutors.MAIN_THREAD; 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; public class TaskImpl extends Task { private final Object lock = new Object(); private boolean completed; private boolean cancelled; private TResult result; private Exception exception; private Queue> completionQueue = new LinkedBlockingQueue<>(); @Override public Task addOnCanceledListener(OnCanceledListener listener) { return addOnCanceledListener(MAIN_THREAD, listener); } @Override public Task addOnCanceledListener(Executor executor, OnCanceledListener listener) { return enqueueOrInvoke(new CancelledExecutor<>(executor, listener)); } @Override public Task addOnCanceledListener(Activity activity, OnCanceledListener listener) { return enqueueOrInvoke(activity, new CancelledExecutor<>(MAIN_THREAD, listener)); } @Override public Task addOnCompleteListener(OnCompleteListener listener) { return addOnCompleteListener(MAIN_THREAD, listener); } @Override public Task addOnCompleteListener(Executor executor, OnCompleteListener listener) { return enqueueOrInvoke(new CompletedExecutor<>(executor, listener)); } @Override public Task addOnCompleteListener(Activity activity, OnCompleteListener listener) { return enqueueOrInvoke(activity, new CompletedExecutor<>(MAIN_THREAD, listener)); } @Override public Task addOnFailureListener(OnFailureListener listener) { return addOnFailureListener(MAIN_THREAD, listener); } @Override public Task addOnFailureListener(Executor executor, OnFailureListener listener) { return enqueueOrInvoke(new FailureExecutor<>(executor, listener)); } @Override public Task addOnFailureListener(Activity activity, OnFailureListener listener) { return enqueueOrInvoke(activity, new FailureExecutor<>(MAIN_THREAD, listener)); } @Override public Task addOnSuccessListener(OnSuccessListener listener) { return addOnSuccessListener(MAIN_THREAD, listener); } @Override public Task addOnSuccessListener(Executor executor, OnSuccessListener listener) { return enqueueOrInvoke(new SuccessExecutor<>(executor, listener)); } @Override public Task addOnSuccessListener(Activity activity, OnSuccessListener listener) { return enqueueOrInvoke(activity, new SuccessExecutor<>(MAIN_THREAD, listener)); } @Override public Task continueWith(Continuation continuation) { return continueWith(MAIN_THREAD, continuation); } @Override public Task continueWith(Executor executor, Continuation continuation) { ContinuationExecutor c = new ContinuationExecutor<>(executor, continuation); enqueueOrInvoke(c); return c.getTask(); } @Override public Task continueWithTask(Continuation> continuation) { return continueWithTask(MAIN_THREAD, continuation); } @Override public Task continueWithTask(Executor executor, Continuation> continuation) { ContinuationWithExecutor 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 TResult getResult(Class 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 listener) { UpdateListenerLifecycleObserver.getObserverForActivity(activity).registerActivityStopListener(listener); } private Task enqueueOrInvoke(Activity activity, UpdateListener listener) { synchronized (lock) { if (completed) { listener.onTaskUpdate(this); } else { completionQueue.offer(listener); registerActivityStop(activity, listener); } } return this; } private Task enqueueOrInvoke(UpdateListener listener) { synchronized (lock) { if (completed) { listener.onTaskUpdate(this); } else { completionQueue.offer(listener); } } return this; } private void notifyQueue() { UpdateListener 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 Task onSuccessTask(SuccessContinuation successContinuation) { return onSuccessTask(MAIN_THREAD, successContinuation); } @Override public Task onSuccessTask(Executor executor, SuccessContinuation successContinuation) { SuccessContinuationExecutor c = new SuccessContinuationExecutor<>(executor, successContinuation); enqueueOrInvoke(c); return c.getTask(); } }