diff --git a/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayStateController.java b/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayStateController.java index bc9bdfca8701c..66679bb4ee449 100644 --- a/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayStateController.java +++ b/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayStateController.java @@ -17,15 +17,20 @@ package com.android.systemui.dreams; import androidx.annotation.NonNull; +import androidx.concurrent.futures.CallbackToFutureAdapter; import com.android.internal.annotations.VisibleForTesting; import com.android.systemui.dagger.SysUISingleton; +import com.android.systemui.dagger.qualifiers.Main; import com.android.systemui.statusbar.policy.CallbackController; +import com.google.common.util.concurrent.ListenableFuture; + import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; import java.util.Objects; +import java.util.concurrent.Executor; import javax.inject.Inject; @@ -77,12 +82,14 @@ public class DreamOverlayStateController implements } } + private final Executor mExecutor; private final ArrayList mCallbacks = new ArrayList<>(); private final HashMap mComplications = new HashMap<>(); @VisibleForTesting @Inject - public DreamOverlayStateController() { + public DreamOverlayStateController(@Main Executor executor) { + mExecutor = executor; } /** @@ -90,11 +97,16 @@ public class DreamOverlayStateController implements * @param provider The {@link ComplicationProvider} providing the dream. * @return The {@link ComplicationToken} tied to the supplied {@link ComplicationProvider}. */ - public ComplicationToken addComplication(ComplicationProvider provider) { - final ComplicationToken token = new ComplicationToken(mNextComplicationTokenId++); - mComplications.put(token, provider); - notifyCallbacks(); - return token; + public ListenableFuture addComplication(ComplicationProvider provider) { + return CallbackToFutureAdapter.getFuture(completer -> { + mExecutor.execute(() -> { + final ComplicationToken token = new ComplicationToken(mNextComplicationTokenId++); + mComplications.put(token, provider); + notifyCallbacks(); + completer.set(token); + }); + return "DreamOverlayStateController::addComplication"; + }); } /** @@ -103,14 +115,19 @@ public class DreamOverlayStateController implements * to be removed. * @return The removed {@link ComplicationProvider}, {@code null} if not found. */ - public ComplicationProvider removeComplication(ComplicationToken token) { - final ComplicationProvider removedComplication = mComplications.remove(token); + public ListenableFuture removeComplication(ComplicationToken token) { + return CallbackToFutureAdapter.getFuture(completer -> { + mExecutor.execute(() -> { + final ComplicationProvider removedComplication = mComplications.remove(token); - if (removedComplication != null) { - notifyCallbacks(); - } + if (removedComplication != null) { + notifyCallbacks(); + } + completer.set(removedComplication); + }); - return removedComplication; + return "DreamOverlayStateController::removeComplication"; + }); } private void notifyCallbacks() { @@ -121,24 +138,28 @@ public class DreamOverlayStateController implements @Override public void addCallback(@NonNull Callback callback) { - Objects.requireNonNull(callback, "Callback must not be null. b/128895449"); - if (mCallbacks.contains(callback)) { - return; - } + mExecutor.execute(() -> { + Objects.requireNonNull(callback, "Callback must not be null. b/128895449"); + if (mCallbacks.contains(callback)) { + return; + } - mCallbacks.add(callback); + mCallbacks.add(callback); - if (mComplications.isEmpty()) { - return; - } + if (mComplications.isEmpty()) { + return; + } - callback.onComplicationsChanged(); + callback.onComplicationsChanged(); + }); } @Override public void removeCallback(@NonNull Callback callback) { - Objects.requireNonNull(callback, "Callback must not be null. b/128895449"); - mCallbacks.remove(callback); + mExecutor.execute(() -> { + Objects.requireNonNull(callback, "Callback must not be null. b/128895449"); + mCallbacks.remove(callback); + }); } /** diff --git a/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayStateControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayStateControllerTest.java index 1f4333e861aaf..efc3c7c7611ad 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayStateControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayStateControllerTest.java @@ -27,6 +27,10 @@ import android.testing.AndroidTestingRunner; import androidx.test.filters.SmallTest; import com.android.systemui.SysuiTestCase; +import com.android.systemui.util.concurrency.FakeExecutor; +import com.android.systemui.util.time.FakeSystemClock; + +import com.google.common.util.concurrent.ListenableFuture; import org.junit.Before; import org.junit.Test; @@ -45,20 +49,25 @@ public class DreamOverlayStateControllerTest extends SysuiTestCase { @Mock ComplicationProvider mProvider; + final FakeExecutor mExecutor = new FakeExecutor(new FakeSystemClock()); + @Before public void setup() { MockitoAnnotations.initMocks(this); } @Test - public void testCallback() { - final DreamOverlayStateController stateController = new DreamOverlayStateController(); + public void testCallback() throws Exception { + final DreamOverlayStateController stateController = new DreamOverlayStateController( + mExecutor); stateController.addCallback(mCallback); // Add complication and verify callback is notified. - final DreamOverlayStateController.ComplicationToken token = + final ListenableFuture tokenFuture = stateController.addComplication(mProvider); + mExecutor.runAllReady(); + verify(mCallback, times(1)).onComplicationsChanged(); final Collection providers = stateController.getComplications(); @@ -68,19 +77,23 @@ public class DreamOverlayStateControllerTest extends SysuiTestCase { clearInvocations(mCallback); // Remove complication and verify callback is notified. - stateController.removeComplication(token); + stateController.removeComplication(tokenFuture.get()); + mExecutor.runAllReady(); verify(mCallback, times(1)).onComplicationsChanged(); assertTrue(providers.isEmpty()); } @Test public void testNotifyOnCallbackAdd() { - final DreamOverlayStateController stateController = new DreamOverlayStateController(); - final DreamOverlayStateController.ComplicationToken token = - stateController.addComplication(mProvider); + final DreamOverlayStateController stateController = + new DreamOverlayStateController(mExecutor); + + stateController.addComplication(mProvider); + mExecutor.runAllReady(); // Verify callback occurs on add when an overlay is already present. stateController.addCallback(mCallback); + mExecutor.runAllReady(); verify(mCallback, times(1)).onComplicationsChanged(); } }