Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 5a0f8772 authored by Bryce Lee's avatar Bryce Lee
Browse files

Marshal access to DreamOverlayStateController.

This changelist marshals calls on DreamoverlayStateController
on a single executor to ensure consistency and ordered
execution.

Bug: 211497162
Test: atest DreamOverlayStateController
Change-Id: If6870b9f1212bae845f39500b4d08925531f8de2
parent d92bda85
Loading
Loading
Loading
Loading
+46 −25
Original line number Diff line number Diff line
@@ -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<Callback> mCallbacks = new ArrayList<>();
    private final HashMap<ComplicationToken, ComplicationProvider> 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) {
    public ListenableFuture<ComplicationToken> addComplication(ComplicationProvider provider) {
        return CallbackToFutureAdapter.getFuture(completer -> {
            mExecutor.execute(() -> {
                final ComplicationToken token = new ComplicationToken(mNextComplicationTokenId++);
                mComplications.put(token, provider);
                notifyCallbacks();
        return token;
                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) {
    public ListenableFuture<ComplicationProvider> removeComplication(ComplicationToken token) {
        return CallbackToFutureAdapter.getFuture(completer -> {
            mExecutor.execute(() -> {
                final ComplicationProvider removedComplication = mComplications.remove(token);

                if (removedComplication != null) {
                    notifyCallbacks();
                }
                completer.set(removedComplication);
            });

        return removedComplication;
            return "DreamOverlayStateController::removeComplication";
        });
    }

    private void notifyCallbacks() {
@@ -121,6 +138,7 @@ public class DreamOverlayStateController implements

    @Override
    public void addCallback(@NonNull Callback callback) {
        mExecutor.execute(() -> {
            Objects.requireNonNull(callback, "Callback must not be null. b/128895449");
            if (mCallbacks.contains(callback)) {
                return;
@@ -133,12 +151,15 @@ public class DreamOverlayStateController implements
            }

            callback.onComplicationsChanged();
        });
    }

    @Override
    public void removeCallback(@NonNull Callback callback) {
        mExecutor.execute(() -> {
            Objects.requireNonNull(callback, "Callback must not be null. b/128895449");
            mCallbacks.remove(callback);
        });
    }

    /**
+20 −7
Original line number Diff line number Diff line
@@ -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<DreamOverlayStateController.ComplicationToken> tokenFuture =
                stateController.addComplication(mProvider);

        mExecutor.runAllReady();

        verify(mCallback, times(1)).onComplicationsChanged();

        final Collection<ComplicationProvider> 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 =
        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();
    }
}