summaryrefslogtreecommitdiff
path: root/java/com/android/dialer/common
diff options
context:
space:
mode:
authorcalderwoodra <calderwoodra@google.com>2018-04-11 13:50:14 -0700
committerCopybara-Service <copybara-piper@google.com>2018-04-11 17:18:59 -0700
commita5a7d014d36fc42a428794b7d62070549cc8ba7b (patch)
tree5514dabc0a33afbcd21c16f82876ab490071b34b /java/com/android/dialer/common
parentd4d9fb5e6dcaa284c955d112f21cc420f0f7f3f2 (diff)
Add support for support fragments in UiListener.
Bug: 36841782 Test: SupportUiListenerTest PiperOrigin-RevId: 192502743 Change-Id: Id06ed732528db1ae486def86ecc2f44828635d81
Diffstat (limited to 'java/com/android/dialer/common')
-rw-r--r--java/com/android/dialer/common/concurrent/DialerExecutorComponent.java9
-rw-r--r--java/com/android/dialer/common/concurrent/SupportUiListener.java154
-rw-r--r--java/com/android/dialer/common/concurrent/UiListener.java2
3 files changed, 165 insertions, 0 deletions
diff --git a/java/com/android/dialer/common/concurrent/DialerExecutorComponent.java b/java/com/android/dialer/common/concurrent/DialerExecutorComponent.java
index 734891430..f4552b203 100644
--- a/java/com/android/dialer/common/concurrent/DialerExecutorComponent.java
+++ b/java/com/android/dialer/common/concurrent/DialerExecutorComponent.java
@@ -50,6 +50,15 @@ public abstract class DialerExecutorComponent {
return UiListener.create(fragmentManager, taskId);
}
+ /**
+ * Version of {@link #createUiListener(FragmentManager, String)} that accepts support fragment
+ * manager.
+ */
+ public <OutputT> SupportUiListener<OutputT> createUiListener(
+ android.support.v4.app.FragmentManager fragmentManager, String taskId) {
+ return SupportUiListener.create(fragmentManager, taskId);
+ }
+
public static DialerExecutorComponent get(Context context) {
return ((DialerExecutorComponent.HasComponent)
((HasRootComponent) context.getApplicationContext()).component())
diff --git a/java/com/android/dialer/common/concurrent/SupportUiListener.java b/java/com/android/dialer/common/concurrent/SupportUiListener.java
new file mode 100644
index 000000000..5e3958619
--- /dev/null
+++ b/java/com/android/dialer/common/concurrent/SupportUiListener.java
@@ -0,0 +1,154 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * 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
+ */
+
+package com.android.dialer.common.concurrent;
+
+import android.content.Context;
+import android.os.Bundle;
+import android.support.annotation.MainThread;
+import android.support.annotation.NonNull;
+import android.support.annotation.Nullable;
+import android.support.v4.app.Fragment;
+import android.support.v4.app.FragmentManager;
+import com.android.dialer.common.Assert;
+import com.android.dialer.common.LogUtil;
+import com.android.dialer.common.concurrent.DialerExecutor.FailureListener;
+import com.android.dialer.common.concurrent.DialerExecutor.SuccessListener;
+import com.google.common.util.concurrent.FutureCallback;
+import com.google.common.util.concurrent.Futures;
+import com.google.common.util.concurrent.ListenableFuture;
+
+
+/**
+ * A headless fragment for use in UI components that interact with ListenableFutures.
+ *
+ * <p>Callbacks are only executed if the UI component is still alive.
+ *
+ * <p>Example usage: <code><pre>
+ * public class MyActivity extends AppCompatActivity {
+ *
+ * private SupportUiListener&lt;MyOutputType&gt uiListener;
+ *
+ * public void onCreate(Bundle bundle) {
+ * super.onCreate(bundle);
+ *
+ * // Must be called in onCreate!
+ * uiListener = DialerExecutorComponent.get(context).createUiListener(fragmentManager, taskId);
+ * }
+ *
+ * private void onSuccess(MyOutputType output) { ... }
+ * private void onFailure(Throwable throwable) { ... }
+ *
+ * private void userDidSomething() {
+ * ListenableFuture&lt;MyOutputType&gt; future = callSomeMethodReturningListenableFuture(input);
+ * uiListener.listen(this, future, this::onSuccess, this::onFailure);
+ * }
+ * }
+ * </pre></code>
+ */
+public class SupportUiListener<OutputT> extends Fragment {
+
+ private CallbackWrapper<OutputT> callbackWrapper;
+
+ @MainThread
+ static <OutputT> SupportUiListener<OutputT> create(
+ FragmentManager fragmentManager, String taskId) {
+ @SuppressWarnings("unchecked")
+ SupportUiListener<OutputT> uiListener =
+ (SupportUiListener<OutputT>) fragmentManager.findFragmentByTag(taskId);
+
+ if (uiListener == null) {
+ LogUtil.i("SupportUiListener.create", "creating new SupportUiListener for " + taskId);
+ uiListener = new SupportUiListener<>();
+ // When launching an activity with the screen off, its onSaveInstanceState() is called before
+ // its fragments are created, which means we can't use commit() and need to use
+ // commitAllowingStateLoss(). This is not a problem for SupportUiListener which saves no
+ // state.
+ fragmentManager.beginTransaction().add(uiListener, taskId).commitAllowingStateLoss();
+ }
+ return uiListener;
+ }
+
+ /**
+ * Adds the specified listeners to the provided future.
+ *
+ * <p>The listeners are not called if the UI component this {@link SupportUiListener} is declared
+ * in is dead.
+ */
+ @MainThread
+ public void listen(
+ Context context,
+ @NonNull ListenableFuture<OutputT> future,
+ @NonNull SuccessListener<OutputT> successListener,
+ @NonNull FailureListener failureListener) {
+ callbackWrapper =
+ new CallbackWrapper<>(Assert.isNotNull(successListener), Assert.isNotNull(failureListener));
+ Futures.addCallback(
+ Assert.isNotNull(future),
+ callbackWrapper,
+ DialerExecutorComponent.get(context).uiExecutor());
+ }
+
+ private static class CallbackWrapper<OutputT> implements FutureCallback<OutputT> {
+ private SuccessListener<OutputT> successListener;
+ private FailureListener failureListener;
+
+ private CallbackWrapper(
+ SuccessListener<OutputT> successListener, FailureListener failureListener) {
+ this.successListener = successListener;
+ this.failureListener = failureListener;
+ }
+
+ @Override
+ public void onSuccess(@Nullable OutputT output) {
+ if (successListener == null) {
+ LogUtil.i("SupportUiListener.runTask", "task succeeded but UI is dead");
+ } else {
+ successListener.onSuccess(output);
+ }
+ }
+
+ @Override
+ public void onFailure(Throwable throwable) {
+ LogUtil.e("SupportUiListener.runTask", "task failed", throwable);
+ if (failureListener == null) {
+ LogUtil.i("SupportUiListener.runTask", "task failed but UI is dead");
+ } else {
+ failureListener.onFailure(throwable);
+ }
+ }
+ }
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setRetainInstance(true);
+ // Note: We use commitAllowingStateLoss when attaching the fragment so it may not be safe to
+ // read savedInstanceState in all situations. (But it's not anticipated that this fragment
+ // should need to rely on saved state.)
+ }
+
+ @Override
+ public void onDetach() {
+ super.onDetach();
+ LogUtil.enterBlock("SupportUiListener.onDetach");
+ if (callbackWrapper != null) {
+ callbackWrapper.successListener = null;
+ callbackWrapper.failureListener = null;
+ }
+ }
+}
+
diff --git a/java/com/android/dialer/common/concurrent/UiListener.java b/java/com/android/dialer/common/concurrent/UiListener.java
index b5922f9c8..a2d976f3b 100644
--- a/java/com/android/dialer/common/concurrent/UiListener.java
+++ b/java/com/android/dialer/common/concurrent/UiListener.java
@@ -31,6 +31,7 @@ import com.google.common.util.concurrent.FutureCallback;
import com.google.common.util.concurrent.Futures;
import com.google.common.util.concurrent.ListenableFuture;
+
/**
* A headless fragment for use in UI components that interact with ListenableFutures.
*
@@ -148,3 +149,4 @@ public class UiListener<OutputT> extends Fragment {
}
}
}
+