CommunalStateController Introduction.

This changelist adds the CommunalStateController, which
is responsible for reporting the whether the communal
view is showing and other state. The communal view state
is reported through the CommunalSurfaceViewController when
shown and CommunalSurfaceHostViewController when hiding.

Bug: 195601027
Test: atest CommunalSurfaceViewControllerTest#testCommunalStateControllerShowNotified
Test: atest CommunalHostViewControllerTest#testCommunalStateControllerHideNotified
Test: atest CommunalStateControllerTest
Change-Id: I2a18a50c8c78e811cd234773994f4591a32303b5
This commit is contained in:
Bryce Lee
2021-08-11 15:53:27 -07:00
parent b103b8b3e2
commit 1731df2cef
8 changed files with 253 additions and 10 deletions

View File

@@ -47,6 +47,7 @@ public class CommunalHostViewController extends ViewController<CommunalHostView>
private static final String STATE_LIST_FORMAT = "[%s]";
private final Executor mMainExecutor;
private final CommunalStateController mCommunalStateController;
private final KeyguardUpdateMonitor mKeyguardUpdateMonitor;
private final KeyguardStateController mKeyguardStateController;
private final StatusBarStateController mStatusBarStateController;
@@ -117,10 +118,12 @@ public class CommunalHostViewController extends ViewController<CommunalHostView>
@Inject
protected CommunalHostViewController(@Main Executor mainExecutor,
CommunalStateController communalStateController,
KeyguardUpdateMonitor keyguardUpdateMonitor,
KeyguardStateController keyguardStateController,
StatusBarStateController statusBarStateController, CommunalHostView view) {
super(view);
mCommunalStateController = communalStateController;
mKeyguardUpdateMonitor = keyguardUpdateMonitor;
mMainExecutor = mainExecutor;
mKeyguardStateController = keyguardStateController;
@@ -247,6 +250,7 @@ public class CommunalHostViewController extends ViewController<CommunalHostView>
} else {
mView.removeAllViews();
mView.setVisibility(View.INVISIBLE);
mCommunalStateController.setCommunalViewShowing(false);
}
});
}

View File

@@ -0,0 +1,91 @@
/*
* Copyright (C) 2021 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.systemui.communal;
import android.annotation.NonNull;
import com.android.internal.annotations.VisibleForTesting;
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.statusbar.policy.CallbackController;
import java.util.ArrayList;
import java.util.Objects;
import javax.inject.Inject;
/**
* CommunalStateController enables publishing and listening to communal-related state changes.
*/
@SysUISingleton
public class CommunalStateController implements
CallbackController<CommunalStateController.Callback> {
private final ArrayList<Callback> mCallbacks = new ArrayList<>();
private boolean mCommunalViewShowing;
/**
* Callback for communal events.
*/
public interface Callback {
/**
* Called when the visibility of the communal view changes.
*/
default void onCommunalViewShowingChanged() {
}
}
@VisibleForTesting
@Inject
public CommunalStateController() {
}
/**
* Sets whether the communal view is showing.
* @param communalViewShowing {@code true} if the view is showing, {@code false} otherwise.
*/
public void setCommunalViewShowing(boolean communalViewShowing) {
if (mCommunalViewShowing != communalViewShowing) {
mCommunalViewShowing = communalViewShowing;
final ArrayList<Callback> callbacks = new ArrayList<>(mCallbacks);
for (Callback callback : callbacks) {
callback.onCommunalViewShowingChanged();
}
}
}
/**
* Returns whether the communal view is showing.
* @return {@code true} if the view is showing, {@code false} otherwise.
*/
public boolean getCommunalViewShowing() {
return mCommunalViewShowing;
}
@Override
public void addCallback(@NonNull Callback callback) {
Objects.requireNonNull(callback, "Callback must not be null. b/128895449");
if (!mCallbacks.contains(callback)) {
mCallbacks.add(callback);
}
}
@Override
public void removeCallback(@NonNull Callback callback) {
Objects.requireNonNull(callback, "Callback must not be null. b/128895449");
mCallbacks.remove(callback);
}
}

View File

@@ -38,18 +38,21 @@ import javax.inject.Inject;
public class CommunalService extends Service {
final Executor mMainExecutor;
final CommunalSourceMonitor mMonitor;
private final CommunalSourceImpl.Factory mSourceFactory;
private ICommunalHost.Stub mBinder = new ICommunalHost.Stub() {
@Override
public void setSource(ICommunalSource source) {
mMonitor.setSource(
source != null ? new CommunalSourceImpl(mMainExecutor, source) : null);
source != null ? mSourceFactory.create(source) : null);
}
};
@Inject
CommunalService(@Main Executor mainExecutor, CommunalSourceMonitor monitor) {
CommunalService(@Main Executor mainExecutor, CommunalSourceImpl.Factory sourceFactory,
CommunalSourceMonitor monitor) {
mMainExecutor = mainExecutor;
mSourceFactory = sourceFactory;
mMonitor = monitor;
}

View File

@@ -26,6 +26,7 @@ import android.view.SurfaceView;
import androidx.concurrent.futures.CallbackToFutureAdapter;
import com.android.systemui.communal.CommunalSource;
import com.android.systemui.communal.CommunalStateController;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.shared.communal.ICommunalSource;
import com.android.systemui.shared.communal.ICommunalSurfaceCallback;
@@ -49,17 +50,20 @@ public class CommunalSourceImpl implements CommunalSource {
private static final boolean DEBUG = false;
private final ICommunalSource mSourceProxy;
private final Executor mMainExecutor;
private final CommunalStateController mCommunalStateController;
static class Factory {
private final Executor mExecutor;
private final CommunalStateController mCommunalStateController;
@Inject
Factory(@Main Executor executor) {
Factory(@Main Executor executor, CommunalStateController communalStateController) {
mExecutor = executor;
mCommunalStateController = communalStateController;
}
public CommunalSource create(ICommunalSource source) {
return new CommunalSourceImpl(mExecutor, source);
return new CommunalSourceImpl(mExecutor, mCommunalStateController, source);
}
}
@@ -71,8 +75,10 @@ public class CommunalSourceImpl implements CommunalSource {
// A list of {@link Callback} that have registered to receive updates.
private final ArrayList<WeakReference<Callback>> mCallbacks = Lists.newArrayList();
public CommunalSourceImpl(Executor mainExecutor, ICommunalSource sourceProxy) {
public CommunalSourceImpl(Executor mainExecutor,
CommunalStateController communalStateController, ICommunalSource sourceProxy) {
mMainExecutor = mainExecutor;
mCommunalStateController = communalStateController;
mSourceProxy = sourceProxy;
try {
@@ -114,7 +120,8 @@ public class CommunalSourceImpl implements CommunalSource {
CallbackToFutureAdapter.getFuture(completer -> {
final SurfaceView view = new SurfaceView(context);
completer.set(new CommunalViewResult(view,
new CommunalSurfaceViewController(view, mMainExecutor, this)));
new CommunalSurfaceViewController(view, mMainExecutor,
mCommunalStateController, this)));
return "CommunalSourceImpl::requestCommunalSurface::getCommunalSurface";
});

View File

@@ -24,6 +24,7 @@ import android.view.SurfaceView;
import androidx.annotation.NonNull;
import com.android.systemui.communal.CommunalStateController;
import com.android.systemui.util.ViewController;
import com.google.common.util.concurrent.ListenableFuture;
@@ -38,6 +39,7 @@ public class CommunalSurfaceViewController extends ViewController<SurfaceView> {
private static final String TAG = "CommunalSurfaceViewCtlr";
private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
private final Executor mMainExecutor;
private final CommunalStateController mCommunalStateController;
private final CommunalSourceImpl mSource;
@IntDef({STATE_SURFACE_CREATED, STATE_SURFACE_VIEW_ATTACHED})
@@ -72,8 +74,9 @@ public class CommunalSurfaceViewController extends ViewController<SurfaceView> {
};
protected CommunalSurfaceViewController(SurfaceView view, Executor executor,
CommunalSourceImpl source) {
CommunalStateController communalStateController, CommunalSourceImpl source) {
super(view);
mCommunalStateController = communalStateController;
mSource = source;
mMainExecutor = executor;
}
@@ -146,6 +149,7 @@ public class CommunalSurfaceViewController extends ViewController<SurfaceView> {
mView.setChildSurfacePackage(surfacePackage);
mView.setZOrderOnTop(true);
mView.postInvalidate();
mCommunalStateController.setCommunalViewShowing(true);
} else {
Log.e(TAG, "couldn't get the surface package");
}

View File

@@ -16,6 +16,7 @@
package com.android.systemui.communal;
import static org.mockito.Mockito.clearInvocations;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@@ -45,6 +46,9 @@ import java.lang.ref.WeakReference;
@SmallTest
@RunWith(AndroidTestingRunner.class)
public class CommunalHostViewControllerTest extends SysuiTestCase {
@Mock
private CommunalStateController mCommunalStateController;
@Mock
private KeyguardUpdateMonitor mKeyguardUpdateMonitor;
@@ -71,8 +75,9 @@ public class CommunalHostViewControllerTest extends SysuiTestCase {
when(mKeyguardStateController.isShowing()).thenReturn(true);
when(mCommunalView.isAttachedToWindow()).thenReturn(true);
mController = new CommunalHostViewController(mFakeExecutor, mKeyguardUpdateMonitor,
mKeyguardStateController, mStatusBarStateController, mCommunalView);
mController = new CommunalHostViewController(mFakeExecutor, mCommunalStateController,
mKeyguardUpdateMonitor, mKeyguardStateController, mStatusBarStateController,
mCommunalView);
mController.init();
mFakeExecutor.runAllReady();
Mockito.clearInvocations(mCommunalView);
@@ -152,4 +157,25 @@ public class CommunalHostViewControllerTest extends SysuiTestCase {
mFakeExecutor.runAllReady();
verify(mCommunalView).setVisibility(View.VISIBLE);
}
@Test
public void testCommunalStateControllerHideNotified() {
ArgumentCaptor<KeyguardUpdateMonitorCallback> callbackCapture =
ArgumentCaptor.forClass(KeyguardUpdateMonitorCallback.class);
// Capture callback value for later use.
verify(mKeyguardUpdateMonitor).registerCallback(callbackCapture.capture());
// Establish a visible communal view.
mController.show(new WeakReference<>(mCommunalSource));
mFakeExecutor.runAllReady();
// Occlude
clearInvocations(mCommunalStateController);
callbackCapture.getValue().onKeyguardOccludedChanged(true);
mFakeExecutor.runAllReady();
// Verify state controller is notified communal view is hidden.
verify(mCommunalStateController).setCommunalViewShowing(false);
}
}

View File

@@ -0,0 +1,91 @@
/*
* Copyright (C) 2021 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.systemui.communal;
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.Mockito.clearInvocations;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
import android.testing.AndroidTestingRunner;
import androidx.test.filters.SmallTest;
import com.android.systemui.SysuiTestCase;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
@SmallTest
@RunWith(AndroidTestingRunner.class)
public class CommunalStateControllerTest extends SysuiTestCase {
@Mock
private CommunalStateController.Callback mCallback;
@Before
public void setup() {
MockitoAnnotations.initMocks(this);
}
@Test
public void testDefaultCommunalViewShowingState() {
// The state controller should report the communal view as not showing by default.
final CommunalStateController stateController = new CommunalStateController();
assertThat(stateController.getCommunalViewShowing()).isFalse();
}
@Test
public void testNotifyCommunalSurfaceShow() {
final CommunalStateController stateController = new CommunalStateController();
stateController.addCallback(mCallback);
// Verify setting communal view to showing propagates to callback.
stateController.setCommunalViewShowing(true);
verify(mCallback).onCommunalViewShowingChanged();
assertThat(stateController.getCommunalViewShowing()).isTrue();
clearInvocations(mCallback);
// Verify setting communal view to not showing propagates to callback.
stateController.setCommunalViewShowing(false);
verify(mCallback).onCommunalViewShowingChanged();
assertThat(stateController.getCommunalViewShowing()).isFalse();
}
@Test
public void testCallbackRegistration() {
final CommunalStateController stateController = new CommunalStateController();
stateController.addCallback(mCallback);
// Verify setting communal view to showing propagates to callback.
stateController.setCommunalViewShowing(true);
verify(mCallback).onCommunalViewShowingChanged();
clearInvocations(mCallback);
stateController.removeCallback(mCallback);
clearInvocations(mCallback);
// Verify callback not invoked after removing from state controller.
stateController.setCommunalViewShowing(false);
verify(mCallback, never()).onCommunalViewShowingChanged();
}
}

View File

@@ -33,6 +33,7 @@ import android.view.SurfaceView;
import androidx.test.filters.SmallTest;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.communal.CommunalStateController;
import com.android.systemui.util.concurrency.FakeExecutor;
import com.android.systemui.util.time.FakeSystemClock;
@@ -68,6 +69,9 @@ public class CommunalSurfaceViewControllerTest extends SysuiTestCase {
@Mock
private SurfaceControlViewHost.SurfacePackage mSurfacePackage;
@Mock
private CommunalStateController mCommunalStateController;
private FakeExecutor mFakeExecutor = new FakeExecutor(new FakeSystemClock());
private SurfaceHolder.Callback mCallback;
@@ -89,7 +93,7 @@ public class CommunalSurfaceViewControllerTest extends SysuiTestCase {
when(mSurfaceView.getMeasuredHeight()).thenReturn(MEASURED_HEIGHT);
when(mSurfaceView.isAttachedToWindow()).thenReturn(false);
mController = new CommunalSurfaceViewController(mSurfaceView, mFakeExecutor,
mCommunalSource);
mCommunalStateController, mCommunalSource);
mController.init();
verify(mSurfaceHolder).addCallback(callbackCapture.capture());
mCallback = callbackCapture.getValue();
@@ -131,6 +135,19 @@ public class CommunalSurfaceViewControllerTest extends SysuiTestCase {
verify(mSurfaceView).setWillNotDraw(false);
}
@Test
public void testCommunalStateControllerShowNotified() {
// Move CommunalSurfaceView to show
mController.onViewAttached();
mCallback.surfaceCreated(mSurfaceHolder);
when(mSurfaceView.isAttachedToWindow()).thenReturn(true);
mPackageFuture.set(mSurfacePackage);
mFakeExecutor.runAllReady();
// Ensure state controller is informed that the communal view is showing.
verify(mCommunalStateController).setCommunalViewShowing(true);
}
// Invoked to setup surface view package.
private void givenSurfacePresent() {
mController.onViewAttached();