From aae974ec5b255330b527621fabdbdd82fa77648c Mon Sep 17 00:00:00 2001 From: Marvin W Date: Sun, 12 Jul 2020 19:02:35 +0200 Subject: [PATCH] Complete play-services-tasks implementation --- play-services-tasks/build.gradle | 15 +- .../src/main/AndroidManifest.xml | 15 +- .../android/gms/tasks/CancellationToken.java | 28 ++ .../gms/tasks/CancellationTokenSource.java | 46 ++++ .../android/gms/tasks/Continuation.java | 18 +- .../DuplicateTaskCompletionException.java | 32 +++ .../android/gms/tasks/OnCanceledListener.java | 22 ++ .../android/gms/tasks/OnCompleteListener.java | 18 +- .../android/gms/tasks/OnFailureListener.java | 18 +- .../android/gms/tasks/OnSuccessListener.java | 18 +- .../gms/tasks/OnTokenCanceledListener.java | 24 ++ .../gms/tasks/RuntimeExecutionException.java | 18 +- .../gms/tasks/SuccessContinuation.java | 28 ++ .../com/google/android/gms/tasks/Task.java | 86 ++++++- .../gms/tasks/TaskCompletionSource.java | 70 ++++- .../android/gms/tasks/TaskExecutors.java | 33 +++ .../com/google/android/gms/tasks/Tasks.java | 18 +- .../gms/tasks/CancellationTokenImpl.java | 32 +++ .../microg/gms/tasks/CancelledExecutor.java | 34 +++ .../microg/gms/tasks/CompletedExecutor.java | 33 +++ .../gms/tasks/ContinuationExecutor.java | 46 ++++ .../gms/tasks/ContinuationWithExecutor.java | 52 ++++ .../org/microg/gms/tasks/FailureExecutor.java | 33 +++ .../tasks/SuccessContinuationExecutor.java | 54 ++++ .../org/microg/gms/tasks/SuccessExecutor.java | 33 +++ .../java/org/microg/gms/tasks/TaskImpl.java | 240 ++++++++++++++++++ .../org/microg/gms/tasks/UpdateExecutor.java | 27 ++ .../org/microg/gms/tasks/UpdateListener.java | 14 + .../UpdateListenerLifecycleObserver.java | 98 +++++++ 29 files changed, 1071 insertions(+), 132 deletions(-) create mode 100644 play-services-tasks/src/main/java/com/google/android/gms/tasks/CancellationToken.java create mode 100644 play-services-tasks/src/main/java/com/google/android/gms/tasks/CancellationTokenSource.java create mode 100644 play-services-tasks/src/main/java/com/google/android/gms/tasks/DuplicateTaskCompletionException.java create mode 100644 play-services-tasks/src/main/java/com/google/android/gms/tasks/OnCanceledListener.java create mode 100644 play-services-tasks/src/main/java/com/google/android/gms/tasks/OnTokenCanceledListener.java create mode 100644 play-services-tasks/src/main/java/com/google/android/gms/tasks/SuccessContinuation.java create mode 100644 play-services-tasks/src/main/java/com/google/android/gms/tasks/TaskExecutors.java create mode 100644 play-services-tasks/src/main/java/org/microg/gms/tasks/CancellationTokenImpl.java create mode 100644 play-services-tasks/src/main/java/org/microg/gms/tasks/CancelledExecutor.java create mode 100644 play-services-tasks/src/main/java/org/microg/gms/tasks/CompletedExecutor.java create mode 100644 play-services-tasks/src/main/java/org/microg/gms/tasks/ContinuationExecutor.java create mode 100644 play-services-tasks/src/main/java/org/microg/gms/tasks/ContinuationWithExecutor.java create mode 100644 play-services-tasks/src/main/java/org/microg/gms/tasks/FailureExecutor.java create mode 100644 play-services-tasks/src/main/java/org/microg/gms/tasks/SuccessContinuationExecutor.java create mode 100644 play-services-tasks/src/main/java/org/microg/gms/tasks/SuccessExecutor.java create mode 100644 play-services-tasks/src/main/java/org/microg/gms/tasks/TaskImpl.java create mode 100644 play-services-tasks/src/main/java/org/microg/gms/tasks/UpdateExecutor.java create mode 100644 play-services-tasks/src/main/java/org/microg/gms/tasks/UpdateListener.java create mode 100644 play-services-tasks/src/main/java/org/microg/gms/tasks/UpdateListenerLifecycleObserver.java diff --git a/play-services-tasks/build.gradle b/play-services-tasks/build.gradle index 7d1eb81f..bb8662f8 100644 --- a/play-services-tasks/build.gradle +++ b/play-services-tasks/build.gradle @@ -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' diff --git a/play-services-tasks/src/main/AndroidManifest.xml b/play-services-tasks/src/main/AndroidManifest.xml index 68340930..c7b833fe 100644 --- a/play-services-tasks/src/main/AndroidManifest.xml +++ b/play-services-tasks/src/main/AndroidManifest.xml @@ -1,18 +1,7 @@ diff --git a/play-services-tasks/src/main/java/com/google/android/gms/tasks/CancellationToken.java b/play-services-tasks/src/main/java/com/google/android/gms/tasks/CancellationToken.java new file mode 100644 index 00000000..00563c5b --- /dev/null +++ b/play-services-tasks/src/main/java/com/google/android/gms/tasks/CancellationToken.java @@ -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); +} diff --git a/play-services-tasks/src/main/java/com/google/android/gms/tasks/CancellationTokenSource.java b/play-services-tasks/src/main/java/com/google/android/gms/tasks/CancellationTokenSource.java new file mode 100644 index 00000000..044b0466 --- /dev/null +++ b/play-services-tasks/src/main/java/com/google/android/gms/tasks/CancellationTokenSource.java @@ -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. + *

+ * 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; + } +} diff --git a/play-services-tasks/src/main/java/com/google/android/gms/tasks/Continuation.java b/play-services-tasks/src/main/java/com/google/android/gms/tasks/Continuation.java index 26b69a81..7057ca71 100644 --- a/play-services-tasks/src/main/java/com/google/android/gms/tasks/Continuation.java +++ b/play-services-tasks/src/main/java/com/google/android/gms/tasks/Continuation.java @@ -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; diff --git a/play-services-tasks/src/main/java/com/google/android/gms/tasks/DuplicateTaskCompletionException.java b/play-services-tasks/src/main/java/com/google/android/gms/tasks/DuplicateTaskCompletionException.java new file mode 100644 index 00000000..81eaa700 --- /dev/null +++ b/play-services-tasks/src/main/java/com/google/android/gms/tasks/DuplicateTaskCompletionException.java @@ -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"); + } +} diff --git a/play-services-tasks/src/main/java/com/google/android/gms/tasks/OnCanceledListener.java b/play-services-tasks/src/main/java/com/google/android/gms/tasks/OnCanceledListener.java new file mode 100644 index 00000000..32fd7b0f --- /dev/null +++ b/play-services-tasks/src/main/java/com/google/android/gms/tasks/OnCanceledListener.java @@ -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(); +} diff --git a/play-services-tasks/src/main/java/com/google/android/gms/tasks/OnCompleteListener.java b/play-services-tasks/src/main/java/com/google/android/gms/tasks/OnCompleteListener.java index 2035472c..7cff8dcf 100644 --- a/play-services-tasks/src/main/java/com/google/android/gms/tasks/OnCompleteListener.java +++ b/play-services-tasks/src/main/java/com/google/android/gms/tasks/OnCompleteListener.java @@ -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; diff --git a/play-services-tasks/src/main/java/com/google/android/gms/tasks/OnFailureListener.java b/play-services-tasks/src/main/java/com/google/android/gms/tasks/OnFailureListener.java index c6e1124b..e9391531 100644 --- a/play-services-tasks/src/main/java/com/google/android/gms/tasks/OnFailureListener.java +++ b/play-services-tasks/src/main/java/com/google/android/gms/tasks/OnFailureListener.java @@ -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; diff --git a/play-services-tasks/src/main/java/com/google/android/gms/tasks/OnSuccessListener.java b/play-services-tasks/src/main/java/com/google/android/gms/tasks/OnSuccessListener.java index f6e6fbdd..5460c0a9 100644 --- a/play-services-tasks/src/main/java/com/google/android/gms/tasks/OnSuccessListener.java +++ b/play-services-tasks/src/main/java/com/google/android/gms/tasks/OnSuccessListener.java @@ -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; diff --git a/play-services-tasks/src/main/java/com/google/android/gms/tasks/OnTokenCanceledListener.java b/play-services-tasks/src/main/java/com/google/android/gms/tasks/OnTokenCanceledListener.java new file mode 100644 index 00000000..0ad2d686 --- /dev/null +++ b/play-services-tasks/src/main/java/com/google/android/gms/tasks/OnTokenCanceledListener.java @@ -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(); +} diff --git a/play-services-tasks/src/main/java/com/google/android/gms/tasks/RuntimeExecutionException.java b/play-services-tasks/src/main/java/com/google/android/gms/tasks/RuntimeExecutionException.java index 99482115..a15dfbe3 100644 --- a/play-services-tasks/src/main/java/com/google/android/gms/tasks/RuntimeExecutionException.java +++ b/play-services-tasks/src/main/java/com/google/android/gms/tasks/RuntimeExecutionException.java @@ -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; diff --git a/play-services-tasks/src/main/java/com/google/android/gms/tasks/SuccessContinuation.java b/play-services-tasks/src/main/java/com/google/android/gms/tasks/SuccessContinuation.java new file mode 100644 index 00000000..3feb598e --- /dev/null +++ b/play-services-tasks/src/main/java/com/google/android/gms/tasks/SuccessContinuation.java @@ -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 { + /** + * Returns the result of applying this SuccessContinuation to Task. + *

+ * 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 then(TResult result) throws Exception; +} diff --git a/play-services-tasks/src/main/java/com/google/android/gms/tasks/Task.java b/play-services-tasks/src/main/java/com/google/android/gms/tasks/Task.java index b9963173..f7223779 100644 --- a/play-services-tasks/src/main/java/com/google/android/gms/tasks/Task.java +++ b/play-services-tasks/src/main/java/com/google/android/gms/tasks/Task.java @@ -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 { public Task() { } + /** + * Adds a listener that is called if the Task is canceled. + *

+ * 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 addOnCanceledListener(OnCanceledListener listener) { + throw new UnsupportedOperationException("addOnCanceledListener is not implemented"); + } + + /** + * Adds a listener that is called if the Task is canceled. + *

+ * 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 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. + *

+ * 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. + *

+ * The listener will be automatically removed during {@link Activity#onStop()}. + * + * @return this Task + */ + public Task addOnCanceledListener(Activity activity, OnCanceledListener listener) { + throw new UnsupportedOperationException("addOnCanceledListener is not implemented"); + } + /** * Adds a listener that is called when the Task completes. *

@@ -184,7 +212,7 @@ public abstract class Task { * @param executor the executor to use to call the Continuation * @see Continuation#then(Task) */ - public Task continueWithTask(Executor executor, Continuation> var2) { + public Task continueWithTask(Executor executor, Continuation> continuation) { throw new UnsupportedOperationException("continueWithTask is not implemented"); } @@ -211,6 +239,11 @@ public abstract class Task { */ public abstract TResult getResult(Class 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 { */ 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. + *

+ * The SuccessContinuation will be called on the main application thread. + *

+ * If the previous Task is canceled, the returned Task will also be canceled and the SuccessContinuation would not execute. + * + * @see SuccessContinuation#then + */ + public Task onSuccessTask(SuccessContinuation 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. + *

+ * 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 Task onSuccessTask(Executor executor, SuccessContinuation successContinuation) { + throw new UnsupportedOperationException("onSuccessTask is not implemented"); + } + } diff --git a/play-services-tasks/src/main/java/com/google/android/gms/tasks/TaskCompletionSource.java b/play-services-tasks/src/main/java/com/google/android/gms/tasks/TaskCompletionSource.java index 32def519..00c4a972 100644 --- a/play-services-tasks/src/main/java/com/google/android/gms/tasks/TaskCompletionSource.java +++ b/play-services-tasks/src/main/java/com/google/android/gms/tasks/TaskCompletionSource.java @@ -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 { + private TaskImpl 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 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; + } } } diff --git a/play-services-tasks/src/main/java/com/google/android/gms/tasks/TaskExecutors.java b/play-services-tasks/src/main/java/com/google/android/gms/tasks/TaskExecutors.java new file mode 100644 index 00000000..c4947e13 --- /dev/null +++ b/play-services-tasks/src/main/java/com/google/android/gms/tasks/TaskExecutors.java @@ -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); + } + }; +} diff --git a/play-services-tasks/src/main/java/com/google/android/gms/tasks/Tasks.java b/play-services-tasks/src/main/java/com/google/android/gms/tasks/Tasks.java index 41f31c7b..43452562 100644 --- a/play-services-tasks/src/main/java/com/google/android/gms/tasks/Tasks.java +++ b/play-services-tasks/src/main/java/com/google/android/gms/tasks/Tasks.java @@ -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; diff --git a/play-services-tasks/src/main/java/org/microg/gms/tasks/CancellationTokenImpl.java b/play-services-tasks/src/main/java/org/microg/gms/tasks/CancellationTokenImpl.java new file mode 100644 index 00000000..b2a69096 --- /dev/null +++ b/play-services-tasks/src/main/java/org/microg/gms/tasks/CancellationTokenImpl.java @@ -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 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) { + } + } +} diff --git a/play-services-tasks/src/main/java/org/microg/gms/tasks/CancelledExecutor.java b/play-services-tasks/src/main/java/org/microg/gms/tasks/CancelledExecutor.java new file mode 100644 index 00000000..6b495749 --- /dev/null +++ b/play-services-tasks/src/main/java/org/microg/gms/tasks/CancelledExecutor.java @@ -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 extends UpdateExecutor { + private OnCanceledListener listener; + + public CancelledExecutor(Executor executor, OnCanceledListener listener) { + super(executor); + this.listener = listener; + } + + @Override + public void onTaskUpdate(Task task) { + if (task.isCanceled()) { + execute(() -> listener.onCanceled()); + } + } + + @Override + public void cancel() { + super.cancel(); + listener = null; + } +} + diff --git a/play-services-tasks/src/main/java/org/microg/gms/tasks/CompletedExecutor.java b/play-services-tasks/src/main/java/org/microg/gms/tasks/CompletedExecutor.java new file mode 100644 index 00000000..18267d7f --- /dev/null +++ b/play-services-tasks/src/main/java/org/microg/gms/tasks/CompletedExecutor.java @@ -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 extends UpdateExecutor { + private OnCompleteListener listener; + + public CompletedExecutor(Executor executor, OnCompleteListener listener) { + super(executor); + this.listener = listener; + } + + @Override + public void onTaskUpdate(Task task) { + if (task.isComplete()) { + execute(() -> listener.onComplete(task)); + } + } + + @Override + public void cancel() { + super.cancel(); + listener = null; + } +} diff --git a/play-services-tasks/src/main/java/org/microg/gms/tasks/ContinuationExecutor.java b/play-services-tasks/src/main/java/org/microg/gms/tasks/ContinuationExecutor.java new file mode 100644 index 00000000..33606df3 --- /dev/null +++ b/play-services-tasks/src/main/java/org/microg/gms/tasks/ContinuationExecutor.java @@ -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 extends UpdateExecutor { + private Continuation continuation; + private TaskCompletionSource completionSource = new TaskCompletionSource<>(); + + public ContinuationExecutor(Executor executor, Continuation continuation) { + super(executor); + this.continuation = continuation; + } + + @Override + public void onTaskUpdate(Task task) { + if (task.isComplete()) { + execute(() -> { + try { + completionSource.setResult(continuation.then(task)); + } catch (Exception e) { + completionSource.setException(e); + } + }); + } + } + + public Task getTask() { + return completionSource.getTask(); + } + + @Override + public void cancel() { + super.cancel(); + continuation = null; + completionSource = null; + } +} diff --git a/play-services-tasks/src/main/java/org/microg/gms/tasks/ContinuationWithExecutor.java b/play-services-tasks/src/main/java/org/microg/gms/tasks/ContinuationWithExecutor.java new file mode 100644 index 00000000..6ec69b0d --- /dev/null +++ b/play-services-tasks/src/main/java/org/microg/gms/tasks/ContinuationWithExecutor.java @@ -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 extends UpdateExecutor { + private Continuation> continuation; + private TaskCompletionSource completionSource = new TaskCompletionSource<>(); + + public ContinuationWithExecutor(Executor executor, Continuation> continuation) { + super(executor); + this.continuation = continuation; + } + + @Override + public void onTaskUpdate(Task 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 getTask() { + return completionSource.getTask(); + } + + @Override + public void cancel() { + super.cancel(); + continuation = null; + completionSource = null; + } +} diff --git a/play-services-tasks/src/main/java/org/microg/gms/tasks/FailureExecutor.java b/play-services-tasks/src/main/java/org/microg/gms/tasks/FailureExecutor.java new file mode 100644 index 00000000..3a670337 --- /dev/null +++ b/play-services-tasks/src/main/java/org/microg/gms/tasks/FailureExecutor.java @@ -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 extends UpdateExecutor { + private OnFailureListener listener; + + public FailureExecutor(Executor executor, OnFailureListener listener) { + super(executor); + this.listener = listener; + } + + @Override + public void onTaskUpdate(Task task) { + if (task.isComplete() && !task.isSuccessful() && !task.isCanceled()) { + execute(() -> listener.onFailure(task.getException())); + } + } + + @Override + public void cancel() { + super.cancel(); + listener = null; + } +} diff --git a/play-services-tasks/src/main/java/org/microg/gms/tasks/SuccessContinuationExecutor.java b/play-services-tasks/src/main/java/org/microg/gms/tasks/SuccessContinuationExecutor.java new file mode 100644 index 00000000..963ae64f --- /dev/null +++ b/play-services-tasks/src/main/java/org/microg/gms/tasks/SuccessContinuationExecutor.java @@ -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 extends UpdateExecutor { + private SuccessContinuation continuation; + private TaskCompletionSource completionSource = new TaskCompletionSource<>(); + + public SuccessContinuationExecutor(Executor executor, SuccessContinuation continuation) { + super(executor); + this.continuation = continuation; + } + + @Override + public void onTaskUpdate(Task 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 getTask() { + return completionSource.getTask(); + } + + @Override + public void cancel() { + super.cancel(); + continuation = null; + completionSource = null; + } +} diff --git a/play-services-tasks/src/main/java/org/microg/gms/tasks/SuccessExecutor.java b/play-services-tasks/src/main/java/org/microg/gms/tasks/SuccessExecutor.java new file mode 100644 index 00000000..da6816d0 --- /dev/null +++ b/play-services-tasks/src/main/java/org/microg/gms/tasks/SuccessExecutor.java @@ -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 extends UpdateExecutor { + private OnSuccessListener listener; + + public SuccessExecutor(Executor executor, OnSuccessListener listener) { + super(executor); + this.listener = listener; + } + + @Override + public void onTaskUpdate(Task task) { + if (task.isSuccessful()) { + execute(() -> listener.onSuccess(task.getResult())); + } + } + + @Override + public void cancel() { + super.cancel(); + listener = null; + } +} diff --git a/play-services-tasks/src/main/java/org/microg/gms/tasks/TaskImpl.java b/play-services-tasks/src/main/java/org/microg/gms/tasks/TaskImpl.java new file mode 100644 index 00000000..ae5c42f1 --- /dev/null +++ b/play-services-tasks/src/main/java/org/microg/gms/tasks/TaskImpl.java @@ -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 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(); + } +} diff --git a/play-services-tasks/src/main/java/org/microg/gms/tasks/UpdateExecutor.java b/play-services-tasks/src/main/java/org/microg/gms/tasks/UpdateExecutor.java new file mode 100644 index 00000000..6c448646 --- /dev/null +++ b/play-services-tasks/src/main/java/org/microg/gms/tasks/UpdateExecutor.java @@ -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 implements UpdateListener, 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; + } +} diff --git a/play-services-tasks/src/main/java/org/microg/gms/tasks/UpdateListener.java b/play-services-tasks/src/main/java/org/microg/gms/tasks/UpdateListener.java new file mode 100644 index 00000000..d28c265a --- /dev/null +++ b/play-services-tasks/src/main/java/org/microg/gms/tasks/UpdateListener.java @@ -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 { + void onTaskUpdate(Task task); + + void cancel(); +} diff --git a/play-services-tasks/src/main/java/org/microg/gms/tasks/UpdateListenerLifecycleObserver.java b/play-services-tasks/src/main/java/org/microg/gms/tasks/UpdateListenerLifecycleObserver.java new file mode 100644 index 00000000..dd307c50 --- /dev/null +++ b/play-services-tasks/src/main/java/org/microg/gms/tasks/UpdateListenerLifecycleObserver.java @@ -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> map = new WeakHashMap<>(); + private static boolean activityLifecycleCallbacksRegistered = false; + private List>> list = new ArrayList<>(); + + public synchronized static UpdateListenerLifecycleObserver getObserverForActivity(Activity activity) { + WeakReference 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> 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 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) { + + } + } +}