Complete play-services-tasks implementation

This commit is contained in:
Marvin W 2020-07-12 19:02:35 +02:00
parent 0522ed41fb
commit aae974ec5b
No known key found for this signature in database
GPG Key ID: 072E9235DB996F2A
29 changed files with 1071 additions and 132 deletions

View File

@ -1,17 +1,6 @@
/*
* Copyright 2013-2015 microG Project Team
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* SPDX-FileCopyrightText: 2020, microG Project Team
* SPDX-License-Identifier: Apache-2.0
*/
apply plugin: 'com.android.library'

View File

@ -1,18 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
~ Copyright (C) 2013-2017 microG Project Team
~
~ Licensed under the Apache License, Version 2.0 (the "License");
~ you may not use this file except in compliance with the License.
~ You may obtain a copy of the License at
~
~ http://www.apache.org/licenses/LICENSE-2.0
~
~ Unless required by applicable law or agreed to in writing, software
~ distributed under the License is distributed on an "AS IS" BASIS,
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and
~ limitations under the License.
~ SPDX-FileCopyrightText: 2020, microG Project Team
~ SPDX-License-Identifier: Apache-2.0
-->
<manifest package="org.microg.gms.tasks"/>

View File

@ -0,0 +1,28 @@
/*
* 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;
@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);
}

View File

@ -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;
}
}

View File

@ -1,17 +1,9 @@
/*
* Copyright (C) 2013-2017 microG Project Team
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* 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;

View File

@ -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");
}
}

View File

@ -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();
}

View File

@ -1,17 +1,9 @@
/*
* Copyright (C) 2013-2017 microG Project Team
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* 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;

View File

@ -1,17 +1,9 @@
/*
* Copyright (C) 2013-2017 microG Project Team
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* 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;

View File

@ -1,17 +1,9 @@
/*
* Copyright (C) 2013-2017 microG Project Team
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* 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;

View File

@ -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();
}

View File

@ -1,17 +1,9 @@
/*
* Copyright (C) 2013-2017 microG Project Team
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* 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;

View File

@ -0,0 +1,28 @@
/*
* 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.
*
* @param result the result of completed Task
* @throws Exception if the result couldn't be produced
*/
Task<TContinuationResult> then(TResult result) throws Exception;
}

View File

@ -1,17 +1,9 @@
/*
* Copyright (C) 2013-2017 microG Project Team
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* 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;
@ -31,6 +23,42 @@ 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/>
@ -184,7 +212,7 @@ public abstract class Task<TResult> {
* @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>> var2) {
public <TContinuationResult> Task<TContinuationResult> continueWithTask(Executor executor, Continuation<TResult, Task<TContinuationResult>> continuation) {
throw new UnsupportedOperationException("continueWithTask is not implemented");
}
@ -211,6 +239,11 @@ public abstract class Task<TResult> {
*/
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.
*/
@ -221,4 +254,29 @@ public abstract class Task<TResult> {
*/
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");
}
}

View File

@ -1,22 +1,15 @@
/*
* Copyright (C) 2013-2017 microG Project Team
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* 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} and later complete it by either
@ -24,29 +17,78 @@ import org.microg.gms.common.PublicApi;
*/
@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 null;
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;
}
}
}

View File

@ -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);
}
};
}

View File

@ -1,17 +1,9 @@
/*
* Copyright (C) 2013-2017 microG Project Team
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* 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;

View File

@ -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) {
}
}
}

View File

@ -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;
}
}

View File

@ -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;
}
}

View File

@ -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;
}
}

View File

@ -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;
}
}

View File

@ -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;
}
}

View File

@ -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;
}
}

View File

@ -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;
}
}

View File

@ -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();
}
}

View File

@ -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;
}
}

View File

@ -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();
}

View File

@ -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) {
}
}
}