// Copyright 2016 Google Inc. All Rights Reserved. package com.android.contacts.util.concurrent; import android.os.Handler; import com.google.common.util.concurrent.AsyncFunction; import com.google.common.util.concurrent.Futures; import com.google.common.util.concurrent.ListenableFuture; import com.google.common.util.concurrent.MoreExecutors; import java.util.concurrent.CancellationException; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; import java.util.concurrent.atomic.AtomicBoolean; /** * Has utility methods for operating on ListenableFutures */ public class FuturesUtil { /** * See * {@link FuturesUtil#withTimeout(ListenableFuture, long, TimeUnit, ScheduledExecutorService)} */ public static ListenableFuture withTimeout(final ListenableFuture future, long time, TimeUnit unit, Handler handler) { return withTimeout(future, time, unit, ContactsExecutors.newHandlerExecutor(handler)); } /** * Returns a future that completes with the result from the input future unless the specified * time elapses before it finishes in which case the result will contain a TimeoutException and * the input future will be canceled. * *

Guava has Futures.withTimeout but it isn't available until v19.0 and we depend on v14.0 * right now. Replace usages of this method if we upgrade our dependency.

*/ public static ListenableFuture withTimeout(final ListenableFuture future, long time, TimeUnit unit, ScheduledExecutorService executor) { final AtomicBoolean didTimeout = new AtomicBoolean(false); executor.schedule(new Runnable() { @Override public void run() { didTimeout.set(!future.isDone() && !future.isCancelled()); future.cancel(true); } }, time, unit); return Futures.catchingAsync(future, Throwable.class, new AsyncFunction() { @Override public ListenableFuture apply(Throwable t) throws Exception { if ((t instanceof CancellationException) && didTimeout.get()) { return Futures.immediateFailedFuture(new TimeoutException("Timeout expired")); } return Futures.immediateFailedFuture(t); } }, MoreExecutors.directExecutor()); } }