Merge changes from topic "197568243"

* changes:
  Request new communal surface on layout changes.
  Always reparent SurfacePackage in setChildSurfacePackage.
  Allow for transparent communal views.
  Address communal UI glitches.
  Adjust the top of the communal view with keyguard.
  Suppress identical communal show requests.
This commit is contained in:
Bryce Lee
2021-09-01 22:16:04 +00:00
committed by Android (Google) Code Review
10 changed files with 397 additions and 78 deletions

View File

@@ -1889,10 +1889,12 @@ public class SurfaceView extends View implements ViewRootImpl.SurfaceChangedCall
public void setChildSurfacePackage(@NonNull SurfaceControlViewHost.SurfacePackage p) {
final SurfaceControl lastSc = mSurfacePackage != null ?
mSurfacePackage.getSurfaceControl() : null;
if (mSurfaceControl != null && lastSc != null) {
mTmpTransaction.reparent(lastSc, null).apply();
mSurfacePackage.release();
} else if (mSurfaceControl != null) {
if (mSurfaceControl != null) {
if (lastSc != null) {
mTmpTransaction.reparent(lastSc, null);
mSurfacePackage.release();
}
reparentSurfacePackage(mTmpTransaction, p);
mTmpTransaction.apply();
}

View File

@@ -28,6 +28,10 @@ import com.android.keyguard.KeyguardVisibilityHelper;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.statusbar.StatusBarState;
import com.android.systemui.statusbar.notification.AnimatableProperty;
import com.android.systemui.statusbar.notification.PropertyAnimator;
import com.android.systemui.statusbar.notification.stack.AnimationProperties;
import com.android.systemui.statusbar.notification.stack.StackStateAnimator;
import com.android.systemui.statusbar.phone.DozeParameters;
import com.android.systemui.statusbar.phone.UnlockedScreenOffAnimationController;
import com.android.systemui.statusbar.policy.KeyguardStateController;
@@ -38,6 +42,8 @@ import com.google.common.util.concurrent.ListenableFuture;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.ref.WeakReference;
import java.util.Objects;
import java.util.Optional;
import java.util.concurrent.Executor;
import javax.inject.Inject;
@@ -49,13 +55,16 @@ public class CommunalHostViewController extends ViewController<CommunalHostView>
private static final String TAG = "CommunalController";
private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
private static final String STATE_LIST_FORMAT = "[%s]";
private static final AnimationProperties COMMUNAL_ANIMATION_PROPERTIES =
new AnimationProperties().setDuration(StackStateAnimator.ANIMATION_DURATION_STANDARD);
private final Executor mMainExecutor;
private final CommunalStateController mCommunalStateController;
private final KeyguardUpdateMonitor mKeyguardUpdateMonitor;
private final KeyguardStateController mKeyguardStateController;
private final StatusBarStateController mStatusBarStateController;
private WeakReference<CommunalSource> mLastSource;
private WeakReference<CommunalSource> mCurrentSource;
private Optional<ShowRequest> mLastRequest = Optional.empty();
private int mState;
private float mQsExpansion;
private float mShadeExpansion;
@@ -72,12 +81,43 @@ public class CommunalHostViewController extends ViewController<CommunalHostView>
// Only show communal view when keyguard is showing and not dozing.
private static final int SHOW_COMMUNAL_VIEW_REQUIRED_STATES = STATE_KEYGUARD_SHOWING;
private static final int SHOW_COMMUNAL_VIEW_INVALID_STATES =
STATE_DOZING | STATE_BOUNCER_SHOWING | STATE_KEYGUARD_OCCLUDED;
STATE_DOZING | STATE_KEYGUARD_OCCLUDED;
private final KeyguardVisibilityHelper mKeyguardVisibilityHelper;
private ViewController<? extends View> mCommunalViewController;
private static class ShowRequest {
private boolean mShouldShow;
private WeakReference<CommunalSource> mSource;
ShowRequest(boolean shouldShow, WeakReference<CommunalSource> source) {
mShouldShow = shouldShow;
mSource = source;
}
CommunalSource getSource() {
return mSource != null ? mSource.get() : null;
}
boolean shouldShow() {
return mShouldShow;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof ShowRequest)) return false;
ShowRequest that = (ShowRequest) o;
return mShouldShow == that.mShouldShow && Objects.equals(getSource(), that.getSource());
}
@Override
public int hashCode() {
return Objects.hash(mShouldShow, mSource);
}
}
private KeyguardUpdateMonitorCallback mKeyguardUpdateCallback =
new KeyguardUpdateMonitorCallback() {
@Override
@@ -176,9 +216,7 @@ public class CommunalHostViewController extends ViewController<CommunalHostView>
}
}
@Override
public void init() {
super.init();
public void onInit() {
setState(STATE_KEYGUARD_SHOWING, mKeyguardStateController.isShowing());
setState(STATE_DOZING, mStatusBarStateController.isDozing());
}
@@ -253,18 +291,26 @@ public class CommunalHostViewController extends ViewController<CommunalHostView>
}
private void showSource() {
final ShowRequest request = new ShowRequest(
(mState & SHOW_COMMUNAL_VIEW_REQUIRED_STATES) == SHOW_COMMUNAL_VIEW_REQUIRED_STATES
&& (mState & SHOW_COMMUNAL_VIEW_INVALID_STATES) == 0
&& mCurrentSource != null,
mCurrentSource);
if (mLastRequest.isPresent() && Objects.equals(mLastRequest.get(), request)) {
return;
}
mLastRequest = Optional.of(request);
// Make sure all necessary states are present for showing communal and all invalid states
// are absent
mMainExecutor.execute(() -> {
final CommunalSource currentSource = mLastSource != null ? mLastSource.get() : null;
if (DEBUG) {
Log.d(TAG, "showSource. currentSource:" + currentSource);
Log.d(TAG, "showSource. currentSource:" + request.getSource());
}
if ((mState & SHOW_COMMUNAL_VIEW_REQUIRED_STATES) == SHOW_COMMUNAL_VIEW_REQUIRED_STATES
&& (mState & SHOW_COMMUNAL_VIEW_INVALID_STATES) == 0
&& currentSource != null) {
if (request.shouldShow()) {
mView.removeAllViews();
// Make view visible.
@@ -273,7 +319,7 @@ public class CommunalHostViewController extends ViewController<CommunalHostView>
final Context context = mView.getContext();
final ListenableFuture<CommunalSource.CommunalViewResult> listenableFuture =
currentSource.requestCommunalView(context);
request.getSource().requestCommunalView(context);
if (listenableFuture == null) {
Log.e(TAG, "could not request communal view");
@@ -308,10 +354,18 @@ public class CommunalHostViewController extends ViewController<CommunalHostView>
* @param source The new {@link CommunalSource}, {@code null} if not set.
*/
public void show(WeakReference<CommunalSource> source) {
mLastSource = source;
mCurrentSource = source;
showSource();
}
/**
* Update position of the view with an optional animation
*/
public void updatePosition(int y, boolean animate) {
PropertyAnimator.setProperty(mView, AnimatableProperty.Y, y, COMMUNAL_ANIMATION_PROPERTIES,
animate);
}
/**
* Invoked when the quick settings is expanded.
* @param expansionFraction the percentage the QS shade has been expanded.

View File

@@ -0,0 +1,74 @@
/*
* 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.util.Log;
import com.android.systemui.statusbar.phone.NotificationPanelViewController;
/**
* {@link CommunalHostViewPositionAlgorithm} calculates the position of the communal view given
* input such as the notification panel position.
*/
public class CommunalHostViewPositionAlgorithm {
private static final String TAG = "CommunalPositionAlg";
private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
/**
* @see NotificationPanelViewController#getExpandedFraction()
*/
private float mPanelExpansion;
/**
* Height of {@link CommunalHostView}.
*/
private int mCommunalHeight;
/**
* A data container for the result of the position algorithm.
*/
public static class Result {
/**
* The y translation of the clock.
*/
public int communalY;
}
/**
* Sets the conditions under which the result should be calculated from.
* @param panelExpansion The percentage the keyguard panel has been moved upwards.
* @param communalHeight The height of the communal panel.
*/
public void setup(float panelExpansion, int communalHeight) {
if (DEBUG) {
Log.d(TAG, "setup. panelExpansion:" + panelExpansion);
}
mPanelExpansion = panelExpansion;
mCommunalHeight = communalHeight;
}
/**
* Calculates the position based on factors input through {link {@link #setup(float, int)}}.
* @param result The resulting calculations.
*/
public void run(Result result) {
// The panel expansion relates to the keyguard expansion. At full expansion, the communal
// view should be aligned at the top (0). Otherwise, it should be shifted offscreen by the
// unexpanded amount.
result.communalY = (int) ((1 - mPanelExpansion) * -mCommunalHeight);
}
}

View File

@@ -38,6 +38,7 @@ import com.google.common.util.concurrent.ListenableFuture;
import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.Objects;
import java.util.concurrent.Executor;
import javax.inject.Inject;
@@ -78,6 +79,45 @@ public class CommunalSourceImpl implements CommunalSource {
}
}
static class Request {
private final int mWidth;
private final int mHeight;
private final int mDisplayId;
private final IBinder mHostToken;
Request(int width, int height, int displayId, IBinder hostToken) {
mWidth = width;
mHeight = height;
mDisplayId = displayId;
mHostToken = hostToken;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof Request)) return false;
Request request = (Request) o;
return mWidth == request.mWidth && mHeight == request.mHeight
&& mDisplayId == request.mDisplayId && Objects.equals(mHostToken,
request.mHostToken);
}
@Override
public int hashCode() {
return Objects.hash(mWidth, mHeight, mDisplayId, mHostToken);
}
@Override
public String toString() {
return "Request{"
+ "mWidth=" + mWidth
+ ", mHeight=" + mHeight
+ ", mDisplayId=" + mDisplayId
+ ", mHostToken=" + mHostToken
+ '}';
}
}
// mConnected is initialized to true as it is presumed instances are constructed with valid
// proxies. The source can never be reconnected once the proxy has died. Once this value
// becomes false, the source will always report disconnected to registering callbacks.
@@ -148,18 +188,15 @@ public class CommunalSourceImpl implements CommunalSource {
* Called internally to request a new {@link android.view.SurfaceControlViewHost.SurfacePackage}
* for showing communal content.
*
* @param hostToken The HostToken necessary to generate a {@link SurfaceControlViewHost}.
* @param displayId The id of the display the surface will be shown on.
* @param width The width of the surface.
* @param height The height of the surface.
* @param request A request with the parameters for the new communal surface.
* @return A future that returns the resulting
* {@link android.view.SurfaceControlViewHost.SurfacePackage}.
*/
protected ListenableFuture<SurfaceControlViewHost.SurfacePackage> requestCommunalSurface(
IBinder hostToken, int displayId, int width, int height) {
Request request) {
return CallbackToFutureAdapter.getFuture(completer -> {
mSourceProxy.getCommunalSurface(hostToken, width, height, displayId,
new ICommunalSurfaceCallback.Stub() {
mSourceProxy.getCommunalSurface(request.mHostToken, request.mWidth, request.mHeight,
request.mDisplayId, new ICommunalSurfaceCallback.Stub() {
@Override
public void onSurface(
SurfaceControlViewHost.SurfacePackage surfacePackage) {

View File

@@ -18,6 +18,7 @@ package com.android.systemui.communal.service;
import android.annotation.IntDef;
import android.content.res.Resources;
import android.graphics.PixelFormat;
import android.graphics.Region;
import android.util.Log;
import android.view.IWindow;
@@ -36,6 +37,7 @@ import com.android.systemui.util.ViewController;
import com.google.common.util.concurrent.ListenableFuture;
import java.util.Optional;
import java.util.concurrent.Executor;
/**
@@ -63,6 +65,8 @@ public class CommunalSurfaceViewController extends ViewController<SurfaceView> {
private int mCurrentState;
private Optional<CommunalSourceImpl.Request> mLastRequest = Optional.empty();
// The current in-flight request for a surface package.
private ListenableFuture<SurfaceControlViewHost.SurfacePackage> mCurrentSurfaceFuture;
@@ -99,6 +103,9 @@ public class CommunalSurfaceViewController extends ViewController<SurfaceView> {
mSurfaceViewTouchableRegion.set(left, top + topMargin, right, bottom - bottomMargin);
updateTouchExclusion();
// Trigger showing (or hiding) surface based on new dimensions.
showSurface();
}
};
@@ -125,6 +132,7 @@ public class CommunalSurfaceViewController extends ViewController<SurfaceView> {
@Override
protected void onInit() {
mView.getHolder().setFormat(PixelFormat.TRANSPARENT);
mView.getHolder().addCallback(mSurfaceHolderCallback);
mView.addOnLayoutChangeListener(mOnLayoutChangeListener);
}
@@ -147,7 +155,7 @@ public class CommunalSurfaceViewController extends ViewController<SurfaceView> {
mCurrentState = newState;
showSurface(newState == STATE_CAN_SHOW_SURFACE);
showSurface();
updateTouchExclusion();
}
@@ -165,25 +173,34 @@ public class CommunalSurfaceViewController extends ViewController<SurfaceView> {
}
}
private void showSurface(boolean show) {
private void showSurface() {
mView.setWillNotDraw(false);
if (!show) {
if (mCurrentState != STATE_CAN_SHOW_SURFACE) {
// If the surface is no longer showing, cancel any in-flight requests.
if (mCurrentSurfaceFuture != null) {
mCurrentSurfaceFuture.cancel(true);
mCurrentSurfaceFuture = null;
}
mLastRequest = Optional.empty();
mView.setWillNotDraw(true);
return;
}
final CommunalSourceImpl.Request request = new CommunalSourceImpl.Request(
mView.getMeasuredWidth(), mView.getMeasuredHeight(),
mView.getDisplay().getDisplayId(), mView.getHostToken());
if (mLastRequest.isPresent() && mLastRequest.get().equals(request)) {
return;
}
mLastRequest = Optional.of(request);
// Since this method is only called when the state has changed, mCurrentSurfaceFuture should
// be null here.
mCurrentSurfaceFuture = mSource.requestCommunalSurface(mView.getHostToken(),
mView.getDisplay().getDisplayId(), mView.getMeasuredWidth(),
mView.getMeasuredHeight());
mCurrentSurfaceFuture = mSource.requestCommunalSurface(request);
mCurrentSurfaceFuture.addListener(new Runnable() {
@Override

View File

@@ -108,6 +108,7 @@ import com.android.systemui.classifier.Classifier;
import com.android.systemui.classifier.FalsingCollector;
import com.android.systemui.communal.CommunalHostView;
import com.android.systemui.communal.CommunalHostViewController;
import com.android.systemui.communal.CommunalHostViewPositionAlgorithm;
import com.android.systemui.communal.CommunalSource;
import com.android.systemui.communal.CommunalSourceMonitor;
import com.android.systemui.communal.CommunalStateController;
@@ -118,6 +119,7 @@ import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.doze.DozeLog;
import com.android.systemui.fragments.FragmentHostManager.FragmentListener;
import com.android.systemui.fragments.FragmentService;
import com.android.systemui.idle.IdleHostView;
import com.android.systemui.idle.IdleHostViewController;
import com.android.systemui.idle.dagger.IdleViewComponent;
import com.android.systemui.media.KeyguardMediaController;
@@ -381,6 +383,12 @@ public class NotificationPanelViewController extends PanelViewController {
private final KeyguardClockPositionAlgorithm.Result
mClockPositionResult =
new KeyguardClockPositionAlgorithm.Result();
private final CommunalHostViewPositionAlgorithm
mCommunalPositionAlgorithm =
new CommunalHostViewPositionAlgorithm();
private final CommunalHostViewPositionAlgorithm.Result
mCommunalPositionResult =
new CommunalHostViewPositionAlgorithm.Result();
private boolean mIsExpanding;
private boolean mBlockTouches;
@@ -895,10 +903,20 @@ public class NotificationPanelViewController extends PanelViewController {
mIdleHostViewController = idleViewComponent.getIdleHostViewController();
mIdleHostViewController.init();
if (mCommunalView != null) {
CommunalViewComponent communalViewComponent =
mCommunalViewComponentFactory.build(mCommunalView);
mCommunalViewController =
communalViewComponent.getCommunalHostViewController();
mCommunalViewController.init();
}
updateViewControllers(
mView.findViewById(R.id.keyguard_status_view),
userAvatarView,
keyguardUserSwitcherView,
mView.findViewById(R.id.idle_host_view),
mCommunalView);
mNotificationContainerParent = mView.findViewById(R.id.notification_container_parent);
NotificationStackScrollLayout stackScrollLayout = mView.findViewById(
@@ -989,6 +1007,7 @@ public class NotificationPanelViewController extends PanelViewController {
private void updateViewControllers(KeyguardStatusView keyguardStatusView,
UserAvatarView userAvatarView,
KeyguardUserSwitcherView keyguardUserSwitcherView,
IdleHostView idleHostView,
CommunalHostView communalView) {
// Re-associate the KeyguardStatusViewController
KeyguardStatusViewComponent statusViewComponent =
@@ -996,13 +1015,9 @@ public class NotificationPanelViewController extends PanelViewController {
mKeyguardStatusViewController = statusViewComponent.getKeyguardStatusViewController();
mKeyguardStatusViewController.init();
if (communalView != null) {
CommunalViewComponent communalViewComponent =
mCommunalViewComponentFactory.build(communalView);
mCommunalViewController =
communalViewComponent.getCommunalHostViewController();
mCommunalViewController.init();
}
IdleViewComponent idleViewComponent = mIdleViewComponentFactory.build(idleHostView);
mIdleHostViewController = idleViewComponent.getIdleHostViewController();
mIdleHostViewController.init();
if (mKeyguardUserSwitcherController != null) {
// Try to close the switcher so that callbacks are triggered if necessary.
@@ -1167,7 +1182,7 @@ public class NotificationPanelViewController extends PanelViewController {
mBigClockContainer.removeAllViews();
updateViewControllers(mView.findViewById(R.id.keyguard_status_view), userAvatarView,
keyguardUserSwitcherView, mCommunalView);
keyguardUserSwitcherView, mView.findViewById(R.id.idle_host_view), mCommunalView);
// Update keyguard bottom area
int index = mView.indexOfChild(mKeyguardBottomArea);
@@ -1307,6 +1322,11 @@ public class NotificationPanelViewController extends PanelViewController {
boolean animate = mNotificationStackScrollLayoutController.isAddOrRemoveAnimationPending();
int stackScrollerPadding;
boolean onKeyguard = isOnKeyguard();
if (onKeyguard) {
updateCommunalViewAppearance();
}
if (onKeyguard || forceClockUpdate) {
updateClockAppearance();
}
@@ -1332,6 +1352,22 @@ public class NotificationPanelViewController extends PanelViewController {
mAnimateNextPositionUpdate = false;
}
private void updateCommunalViewAppearance() {
if (mCommunalViewController == null) {
return;
}
float expandedFraction =
mUnlockedScreenOffAnimationController.isScreenOffAnimationPlaying()
? 1.0f : getExpandedFraction();
mCommunalPositionAlgorithm.setup(expandedFraction, mCommunalView.getHeight());
mCommunalPositionAlgorithm.run(mCommunalPositionResult);
boolean animate =
mNotificationStackScrollLayoutController.isAddOrRemoveAnimationPending()
|| mAnimateNextPositionUpdate;
mCommunalViewController.updatePosition(mCommunalPositionResult.communalY, animate);
}
private void updateClockAppearance() {
int userSwitcherPreferredY = mStatusBarHeaderHeightKeyguard;
boolean bypassEnabled = mKeyguardBypassController.getBypassEnabled();
@@ -4583,8 +4619,6 @@ public class NotificationPanelViewController extends PanelViewController {
mStatusBarStateController.removeCallback(mStatusBarStateListener);
mConfigurationController.removeCallback(mConfigurationListener);
mCommunalSourceMonitor.removeCallback(mCommunalSourceMonitorCallback);
// Clear source when detached.
setCommunalSource(null /*source*/);
mFalsingManager.removeTapListener(mFalsingTapListener);
mCommunalStateController.removeCallback(mCommunalStateCallback);
}

View File

@@ -16,7 +16,10 @@
package com.android.systemui.communal;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.any;
import static org.mockito.Mockito.clearInvocations;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@@ -118,33 +121,6 @@ public class CommunalHostViewControllerTest extends SysuiTestCase {
verify(mCommunalView).setVisibility(View.INVISIBLE);
}
@Test
public void testHideOnBouncer() {
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();
verify(mCommunalView).setVisibility(View.VISIBLE);
Mockito.clearInvocations(mCommunalView);
// Trigger bouncer.
Mockito.clearInvocations(mCommunalView);
callbackCapture.getValue().onKeyguardBouncerChanged(true);
mFakeExecutor.runAllReady();
verify(mCommunalView).setVisibility(View.INVISIBLE);
// Hide bouncer
Mockito.clearInvocations(mCommunalView);
callbackCapture.getValue().onKeyguardBouncerChanged(false);
mFakeExecutor.runAllReady();
verify(mCommunalView).setVisibility(View.VISIBLE);
}
@Test
public void testHideOnOcclude() {
ArgumentCaptor<KeyguardUpdateMonitorCallback> callbackCapture =
@@ -226,4 +202,41 @@ public class CommunalHostViewControllerTest extends SysuiTestCase {
verify(mChildView).setAlpha(alpha);
verify(mCommunalView).setAlpha(alpha);
}
@Test
public void testMultipleShowRequestSuppression() {
// Ensure first request invokes source.
mController.show(new WeakReference<>(mCommunalSource));
mFakeExecutor.runAllReady();
verify(mCommunalSource).requestCommunalView(any());
clearInvocations(mCommunalSource);
// Ensure subsequent identical request is suppressed
mController.show(new WeakReference<>(mCommunalSource));
mFakeExecutor.runAllReady();
verify(mCommunalSource, never()).requestCommunalView(any());
}
@Test
public void testNoShowInvocationOnBouncer() {
ArgumentCaptor<KeyguardUpdateMonitorCallback> callbackCapture =
ArgumentCaptor.forClass(KeyguardUpdateMonitorCallback.class);
// Capture callback value for later use.
verify(mKeyguardUpdateMonitor).registerCallback(callbackCapture.capture());
// Set source so it will be cleared if in invalid state.
mController.show(new WeakReference<>(mCommunalSource));
mFakeExecutor.runAllReady();
clearInvocations(mCommunalStateController, mCommunalView);
// Change bouncer to showing.
callbackCapture.getValue().onKeyguardBouncerChanged(true);
mFakeExecutor.runAllReady();
// Verify that there were no requests to remove all child views or set the communal
// state to not showing.
verify(mCommunalStateController, never()).setCommunalViewShowing(eq(false));
verify(mCommunalView, never()).removeAllViews();
}
}

View File

@@ -0,0 +1,44 @@
/*
* 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 android.test.suitebuilder.annotation.SmallTest;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.communal.CommunalHostViewPositionAlgorithm.Result;
import org.junit.Test;
@SmallTest
public class CommunalHostViewPositionAlgorithmTest extends SysuiTestCase {
@Test
public void testOutput() {
final float expansion = 0.25f;
final int height = 120;
final CommunalHostViewPositionAlgorithm algorithm = new CommunalHostViewPositionAlgorithm();
algorithm.setup(expansion, height);
final Result result = new Result();
algorithm.run(result);
// Verify the communal view is shifted offscreen vertically by the correct amount.
assertThat((1 - expansion) * -height).isEqualTo(result.communalY);
}
}

View File

@@ -18,14 +18,15 @@ package com.android.systemui.communal.service;
import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.clearInvocations;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import android.content.res.Resources;
import android.graphics.PixelFormat;
import android.graphics.Region;
import android.os.IBinder;
import android.view.Display;
@@ -125,6 +126,7 @@ public class CommunalSurfaceViewControllerTest extends SysuiTestCase {
final ArgumentCaptor<SurfaceHolder.Callback> callbackCapture =
ArgumentCaptor.forClass(SurfaceHolder.Callback.class);
verify(mSurfaceHolder).addCallback(callbackCapture.capture());
verify(mSurfaceHolder).setFormat(PixelFormat.TRANSPARENT);
mCallback = callbackCapture.getValue();
final ArgumentCaptor<View.OnLayoutChangeListener> listenerCapture =
@@ -134,7 +136,7 @@ public class CommunalSurfaceViewControllerTest extends SysuiTestCase {
mPackageFuture = SettableFuture.create();
when(mCommunalSource.requestCommunalSurface(any(), anyInt(), anyInt(), anyInt()))
when(mCommunalSource.requestCommunalSurface(any()))
.thenReturn(mPackageFuture);
}
@@ -142,19 +144,20 @@ public class CommunalSurfaceViewControllerTest extends SysuiTestCase {
public void testSetSurfacePackage() {
// There should be no requests without the proper state.
verify(mCommunalSource, times(0))
.requestCommunalSurface(any(), anyInt(), anyInt(), anyInt());
.requestCommunalSurface(any());
// The full state must be present to make a request.
mController.onViewAttached();
verify(mCommunalSource, times(0))
.requestCommunalSurface(any(), anyInt(), anyInt(), anyInt());
.requestCommunalSurface(any());
clearInvocations(mSurfaceView);
// Request surface view once all conditions are met.
mCallback.surfaceCreated(mSurfaceHolder);
verify(mCommunalSource)
.requestCommunalSurface(mHostToken, DISPLAY_ID, MEASURED_WIDTH, MEASURED_HEIGHT);
final CommunalSourceImpl.Request expectedRequest = new CommunalSourceImpl.Request(
MEASURED_WIDTH, MEASURED_HEIGHT, DISPLAY_ID, mHostToken);
verify(mCommunalSource).requestCommunalSurface(eq(expectedRequest));
when(mSurfaceView.isAttachedToWindow()).thenReturn(true);
@@ -214,8 +217,9 @@ public class CommunalSurfaceViewControllerTest extends SysuiTestCase {
mFakeExecutor.runAllReady();
clearInvocations(mSurfaceView);
verify(mCommunalSource, times(1))
.requestCommunalSurface(mHostToken, DISPLAY_ID, MEASURED_WIDTH, MEASURED_HEIGHT);
final CommunalSourceImpl.Request expectedRequest = new CommunalSourceImpl.Request(
MEASURED_WIDTH, MEASURED_HEIGHT, DISPLAY_ID, mHostToken);
verify(mCommunalSource, times(1)).requestCommunalSurface(eq(expectedRequest));
mController.onViewDetached();
assertTrue(mPackageFuture.isCancelled());
@@ -268,4 +272,31 @@ public class CommunalSurfaceViewControllerTest extends SysuiTestCase {
verify(mNotificationShadeWindowController)
.setTouchExclusionRegion(eq(new Region()));
}
@Test
public void testLayoutChange() {
final int left = 0;
final int top = 0;
final int right = 200;
final int bottom = 100;
givenSurfacePresent();
// Layout change should trigger a request to get new communal surface.
mLayoutChangeListener.onLayoutChange(mSurfaceView, left, top, right, bottom, 0, 0, 0,
0);
// Note that the measured are preset and different than the layout input.
final CommunalSourceImpl.Request expectedRequest =
new CommunalSourceImpl.Request(MEASURED_WIDTH, MEASURED_HEIGHT, DISPLAY_ID,
mHostToken);
verify(mCommunalSource)
.requestCommunalSurface(eq(expectedRequest));
clearInvocations(mCommunalSource);
// Subsequent matching layout change should not trigger any request.
mLayoutChangeListener.onLayoutChange(mSurfaceView, left, top, right, bottom, 0, 0, 0,
0);
verify(mCommunalSource, never()).requestCommunalSurface(any());
}
}

View File

@@ -851,9 +851,10 @@ public class NotificationPanelViewTest extends SysuiTestCase {
@Test
public void testCommunalhostViewControllerInit() {
verify(mCommunalHostViewController, times(1)).init();
clearInvocations(mCommunalHostViewController);
givenViewAttached();
verify(mCommunalHostViewController).init();
verify(mCommunalHostViewController, never()).init();
}
@Test
@@ -874,8 +875,6 @@ public class NotificationPanelViewTest extends SysuiTestCase {
clearInvocations(mCommunalHostViewController);
givenViewDetached();
verify(mCommunalSourceMonitor).removeCallback(any());
verify(mCommunalHostViewController).show(sourceCapture.capture());
assertThat(sourceCapture.getValue()).isEqualTo(null);
}
@Test
@@ -910,6 +909,20 @@ public class NotificationPanelViewTest extends SysuiTestCase {
verify(mCommunalHostViewController).setAlpha(anyFloat());
}
@Test
public void testCommunalPositionUpdate() {
// Verify that the communal position is updated on interaction with the
// NotificationPanelViewController. Note that there a number of paths where the position
// might be updated and therefore the check isn't strictly on a single invocation.
clearInvocations(mCommunalHostViewController);
final View.OnLayoutChangeListener layoutChangeListener =
mNotificationPanelViewController.createLayoutChangeListener();
mNotificationPanelViewController.mStatusBarStateController.setState(KEYGUARD);
layoutChangeListener.onLayoutChange(mView, 0, 0, 200, 200, 0, 0, 200, 200);
verify(mCommunalHostViewController, atLeast(1))
.updatePosition(anyInt(), anyBoolean());
}
private void triggerPositionClockAndNotifications() {
mNotificationPanelViewController.closeQs();
}