Merge "Move communal surface handling to controller."
This commit is contained in:
@@ -67,6 +67,8 @@ public class CommunalHostViewController extends ViewController<CommunalHostView>
|
||||
private static final int SHOW_COMMUNAL_VIEW_INVALID_STATES =
|
||||
STATE_DOZING | STATE_BOUNCER_SHOWING | STATE_KEYGUARD_OCCLUDED;
|
||||
|
||||
private ViewController<? extends View> mCommunalViewController;
|
||||
|
||||
private KeyguardUpdateMonitorCallback mKeyguardUpdateCallback =
|
||||
new KeyguardUpdateMonitorCallback() {
|
||||
@Override
|
||||
@@ -220,7 +222,7 @@ public class CommunalHostViewController extends ViewController<CommunalHostView>
|
||||
|
||||
final Context context = mView.getContext();
|
||||
|
||||
final ListenableFuture<View> listenableFuture =
|
||||
final ListenableFuture<CommunalSource.CommunalViewResult> listenableFuture =
|
||||
currentSource.requestCommunalView(context);
|
||||
|
||||
if (listenableFuture == null) {
|
||||
@@ -230,11 +232,14 @@ public class CommunalHostViewController extends ViewController<CommunalHostView>
|
||||
|
||||
listenableFuture.addListener(() -> {
|
||||
try {
|
||||
final View view = listenableFuture.get();
|
||||
view.setLayoutParams(new ViewGroup.LayoutParams(
|
||||
final CommunalSource.CommunalViewResult result = listenableFuture.get();
|
||||
result.view.setLayoutParams(new ViewGroup.LayoutParams(
|
||||
ViewGroup.LayoutParams.MATCH_PARENT,
|
||||
ViewGroup.LayoutParams.MATCH_PARENT));
|
||||
mView.addView(view);
|
||||
mView.addView(result.view);
|
||||
|
||||
mCommunalViewController = result.viewController;
|
||||
mCommunalViewController.init();
|
||||
} catch (Exception e) {
|
||||
Log.e(TAG, "could not obtain communal view through callback:" + e);
|
||||
}
|
||||
|
||||
@@ -19,6 +19,8 @@ package com.android.systemui.communal;
|
||||
import android.content.Context;
|
||||
import android.view.View;
|
||||
|
||||
import com.android.systemui.util.ViewController;
|
||||
|
||||
import com.google.common.util.concurrent.ListenableFuture;
|
||||
|
||||
/**
|
||||
@@ -27,14 +29,39 @@ import com.google.common.util.concurrent.ListenableFuture;
|
||||
* Callbacks may also be registered to listen to state changes.
|
||||
*/
|
||||
public interface CommunalSource {
|
||||
/**
|
||||
* {@link CommunalViewResult} is handed back from {@link #requestCommunalView(Context)} and
|
||||
* contains the view to be displayed and its associated controller.
|
||||
*/
|
||||
class CommunalViewResult {
|
||||
/**
|
||||
* The resulting communal view.
|
||||
*/
|
||||
public final View view;
|
||||
/**
|
||||
* The controller for the communal view.
|
||||
*/
|
||||
public final ViewController<? extends View> viewController;
|
||||
|
||||
/**
|
||||
* The default constructor for {@link CommunalViewResult}.
|
||||
* @param view The communal view.
|
||||
* @param viewController The communal view's controller.
|
||||
*/
|
||||
public CommunalViewResult(View view, ViewController<? extends View> viewController) {
|
||||
this.view = view;
|
||||
this.viewController = viewController;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Requests a communal surface that can be displayed inside {@link CommunalHostView}.
|
||||
*
|
||||
* @param context The {@link View} {@link Context} to build the resulting view from
|
||||
* @return A future that can be listened upon for the resulting {@link View}. The value will be
|
||||
* {@code null} in case of a failure.
|
||||
* @return A future that can be listened upon for the resulting {@link CommunalViewResult}. The
|
||||
* value will be {@code null} in case of a failure.
|
||||
*/
|
||||
ListenableFuture<View> requestCommunalView(Context context);
|
||||
ListenableFuture<CommunalViewResult> requestCommunalView(Context context);
|
||||
|
||||
/**
|
||||
* Adds a {@link Callback} to receive future status updates regarding this
|
||||
|
||||
@@ -21,7 +21,7 @@ import android.os.IBinder;
|
||||
import android.os.RemoteException;
|
||||
import android.util.Log;
|
||||
import android.view.SurfaceControlViewHost;
|
||||
import android.view.View;
|
||||
import android.view.SurfaceView;
|
||||
|
||||
import androidx.concurrent.futures.CallbackToFutureAdapter;
|
||||
|
||||
@@ -106,13 +106,15 @@ public class CommunalSourceImpl implements CommunalSource {
|
||||
}
|
||||
|
||||
@Override
|
||||
public ListenableFuture<View> requestCommunalView(Context context) {
|
||||
public ListenableFuture<CommunalViewResult> requestCommunalView(Context context) {
|
||||
if (DEBUG) {
|
||||
Log.d(TAG, "Received request for communal view");
|
||||
}
|
||||
ListenableFuture<View> packageFuture =
|
||||
ListenableFuture<CommunalViewResult> packageFuture =
|
||||
CallbackToFutureAdapter.getFuture(completer -> {
|
||||
completer.set(new CommunalSurfaceView(context, mMainExecutor, this));
|
||||
final SurfaceView view = new SurfaceView(context);
|
||||
completer.set(new CommunalViewResult(view,
|
||||
new CommunalSurfaceViewController(view, mMainExecutor, this)));
|
||||
return "CommunalSourceImpl::requestCommunalSurface::getCommunalSurface";
|
||||
});
|
||||
|
||||
|
||||
@@ -1,96 +0,0 @@
|
||||
/*
|
||||
* 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.service;
|
||||
|
||||
import android.content.Context;
|
||||
import android.util.Log;
|
||||
import android.view.SurfaceControlViewHost;
|
||||
import android.view.SurfaceHolder;
|
||||
import android.view.SurfaceView;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
import com.google.common.util.concurrent.ListenableFuture;
|
||||
|
||||
import java.util.concurrent.Executor;
|
||||
|
||||
/**
|
||||
* {@link CommunalSurfaceView} can be used to display remote surfaces returned by the
|
||||
* {@link CommunalService}. The necessary information for the request are derived from the view's
|
||||
* events from being attached to the parent container.
|
||||
*/
|
||||
public class CommunalSurfaceView extends SurfaceView {
|
||||
private static final String TAG = "CommunalSurfaceView";
|
||||
private static final boolean DEBUG = false;
|
||||
private final Executor mMainExecutor;
|
||||
private final CommunalSourceImpl mSource;
|
||||
|
||||
public CommunalSurfaceView(Context context, Executor executor, CommunalSourceImpl source) {
|
||||
super(context);
|
||||
mSource = source;
|
||||
mMainExecutor = executor;
|
||||
|
||||
getHolder().addCallback(new SurfaceHolder.Callback() {
|
||||
@Override
|
||||
public void surfaceCreated(@NonNull SurfaceHolder holder) {
|
||||
onSurfaceCreated();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void surfaceChanged(@NonNull SurfaceHolder holder, int format, int width,
|
||||
int height) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void surfaceDestroyed(@NonNull SurfaceHolder holder) {
|
||||
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void onSurfaceCreated() {
|
||||
setWillNotDraw(false);
|
||||
|
||||
final ListenableFuture<SurfaceControlViewHost.SurfacePackage> surfaceFuture =
|
||||
mSource.requestCommunalSurface(this.getHostToken(),
|
||||
getDisplay().getDisplayId(), getMeasuredWidth(), getMeasuredHeight());
|
||||
|
||||
surfaceFuture.addListener(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
SurfaceControlViewHost.SurfacePackage surfacePackage = surfaceFuture.get();
|
||||
|
||||
if (DEBUG) {
|
||||
Log.d(TAG, "Received surface package:" + surfacePackage);
|
||||
}
|
||||
|
||||
if (surfacePackage != null) {
|
||||
setChildSurfacePackage(surfacePackage);
|
||||
setZOrderOnTop(true);
|
||||
postInvalidate();
|
||||
} else {
|
||||
Log.e(TAG, "couldn't get the surface package");
|
||||
}
|
||||
} catch (Exception e) {
|
||||
Log.e(TAG, "An error occurred retrieving the future result:" + e);
|
||||
}
|
||||
}
|
||||
}, mMainExecutor);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,153 @@
|
||||
/*
|
||||
* 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.service;
|
||||
|
||||
import android.annotation.IntDef;
|
||||
import android.util.Log;
|
||||
import android.view.SurfaceControlViewHost;
|
||||
import android.view.SurfaceHolder;
|
||||
import android.view.SurfaceView;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
import com.android.systemui.util.ViewController;
|
||||
|
||||
import com.google.common.util.concurrent.ListenableFuture;
|
||||
|
||||
import java.util.concurrent.Executor;
|
||||
|
||||
/**
|
||||
* {@link CommunalSurfaceViewController} coordinates requesting communal surfaces to populate a
|
||||
* {@link SurfaceView} with.
|
||||
*/
|
||||
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 CommunalSourceImpl mSource;
|
||||
|
||||
@IntDef({STATE_SURFACE_CREATED, STATE_SURFACE_VIEW_ATTACHED})
|
||||
private @interface State {}
|
||||
|
||||
private static final int STATE_SURFACE_CREATED = 1 << 0;
|
||||
private static final int STATE_SURFACE_VIEW_ATTACHED = 1 << 1;
|
||||
|
||||
private static final int STATE_CAN_SHOW_SURFACE =
|
||||
STATE_SURFACE_CREATED | STATE_SURFACE_VIEW_ATTACHED;
|
||||
|
||||
private int mCurrentState;
|
||||
|
||||
private final SurfaceHolder.Callback mSurfaceHolderCallback = new SurfaceHolder.Callback() {
|
||||
@Override
|
||||
public void surfaceCreated(@NonNull SurfaceHolder holder) {
|
||||
setState(STATE_SURFACE_CREATED, true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void surfaceChanged(@NonNull SurfaceHolder holder, int format, int width,
|
||||
int height) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void surfaceDestroyed(@NonNull SurfaceHolder holder) {
|
||||
setState(STATE_SURFACE_CREATED, false);
|
||||
}
|
||||
};
|
||||
|
||||
protected CommunalSurfaceViewController(SurfaceView view, Executor executor,
|
||||
CommunalSourceImpl source) {
|
||||
super(view);
|
||||
mSource = source;
|
||||
mMainExecutor = executor;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void init() {
|
||||
super.init();
|
||||
mView.getHolder().addCallback(mSurfaceHolderCallback);
|
||||
}
|
||||
|
||||
private void setState(@State int state, boolean enabled) {
|
||||
if (DEBUG) {
|
||||
Log.d(TAG, "setState. state:" + state + " enable:" + enabled);
|
||||
}
|
||||
|
||||
final int newState = enabled ? mCurrentState | state : mCurrentState & ~state;
|
||||
|
||||
// no new state is available
|
||||
if (newState == mCurrentState) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (DEBUG) {
|
||||
Log.d(TAG, "setState. new state:" + mCurrentState);
|
||||
}
|
||||
|
||||
mCurrentState = newState;
|
||||
|
||||
if (newState == STATE_CAN_SHOW_SURFACE) {
|
||||
showSurface();
|
||||
}
|
||||
}
|
||||
|
||||
private void showSurface() {
|
||||
mView.setWillNotDraw(false);
|
||||
|
||||
final ListenableFuture<SurfaceControlViewHost.SurfacePackage> surfaceFuture =
|
||||
mSource.requestCommunalSurface(mView.getHostToken(),
|
||||
mView.getDisplay().getDisplayId(), mView.getMeasuredWidth(),
|
||||
mView.getMeasuredHeight());
|
||||
|
||||
surfaceFuture.addListener(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
// If the request is received after detached, ignore.
|
||||
if (!mView.isAttachedToWindow()) {
|
||||
return;
|
||||
}
|
||||
|
||||
SurfaceControlViewHost.SurfacePackage surfacePackage = surfaceFuture.get();
|
||||
|
||||
if (DEBUG) {
|
||||
Log.d(TAG, "Received surface package:" + surfacePackage);
|
||||
}
|
||||
|
||||
if (surfacePackage != null) {
|
||||
mView.setChildSurfacePackage(surfacePackage);
|
||||
mView.setZOrderOnTop(true);
|
||||
mView.postInvalidate();
|
||||
} else {
|
||||
Log.e(TAG, "couldn't get the surface package");
|
||||
}
|
||||
} catch (Exception e) {
|
||||
Log.e(TAG, "An error occurred retrieving the future result:" + e);
|
||||
}
|
||||
}
|
||||
}, mMainExecutor);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onViewAttached() {
|
||||
setState(STATE_SURFACE_VIEW_ATTACHED, true);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onViewDetached() {
|
||||
setState(STATE_SURFACE_VIEW_ATTACHED, false);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,129 @@
|
||||
/*
|
||||
* 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.service;
|
||||
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.ArgumentMatchers.anyInt;
|
||||
import static org.mockito.Mockito.times;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import android.os.IBinder;
|
||||
import android.view.Display;
|
||||
import android.view.SurfaceControlViewHost;
|
||||
import android.view.SurfaceHolder;
|
||||
import android.view.SurfaceView;
|
||||
|
||||
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.SettableFuture;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.mockito.ArgumentCaptor;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.MockitoAnnotations;
|
||||
|
||||
@SmallTest
|
||||
public class CommunalSurfaceViewControllerTest extends SysuiTestCase {
|
||||
private static final int MEASURED_HEIGHT = 200;
|
||||
private static final int MEASURED_WIDTH = 500;
|
||||
private static final int DISPLAY_ID = 3;
|
||||
|
||||
@Mock
|
||||
private Display mDisplay;
|
||||
|
||||
@Mock
|
||||
private IBinder mHostToken;
|
||||
|
||||
@Mock
|
||||
private SurfaceView mSurfaceView;
|
||||
|
||||
@Mock
|
||||
private SurfaceHolder mSurfaceHolder;
|
||||
|
||||
@Mock
|
||||
private CommunalSourceImpl mCommunalSource;
|
||||
|
||||
@Mock
|
||||
private SurfaceControlViewHost.SurfacePackage mSurfacePackage;
|
||||
|
||||
private FakeExecutor mFakeExecutor = new FakeExecutor(new FakeSystemClock());
|
||||
|
||||
private SurfaceHolder.Callback mCallback;
|
||||
|
||||
private CommunalSurfaceViewController mController;
|
||||
|
||||
private SettableFuture<SurfaceControlViewHost.SurfacePackage> mPackageFuture;
|
||||
|
||||
@Before
|
||||
public void setup() {
|
||||
MockitoAnnotations.initMocks(this);
|
||||
final ArgumentCaptor<SurfaceHolder.Callback> callbackCapture =
|
||||
ArgumentCaptor.forClass(SurfaceHolder.Callback.class);
|
||||
when(mSurfaceView.getHolder()).thenReturn(mSurfaceHolder);
|
||||
when(mSurfaceView.getDisplay()).thenReturn(mDisplay);
|
||||
when(mDisplay.getDisplayId()).thenReturn(DISPLAY_ID);
|
||||
when(mSurfaceView.getHostToken()).thenReturn(mHostToken);
|
||||
when(mSurfaceView.getMeasuredWidth()).thenReturn(MEASURED_WIDTH);
|
||||
when(mSurfaceView.getMeasuredHeight()).thenReturn(MEASURED_HEIGHT);
|
||||
when(mSurfaceView.isAttachedToWindow()).thenReturn(false);
|
||||
mController = new CommunalSurfaceViewController(mSurfaceView, mFakeExecutor,
|
||||
mCommunalSource);
|
||||
mController.init();
|
||||
verify(mSurfaceHolder).addCallback(callbackCapture.capture());
|
||||
mCallback = callbackCapture.getValue();
|
||||
|
||||
mPackageFuture = SettableFuture.create();
|
||||
|
||||
when(mCommunalSource.requestCommunalSurface(any(), anyInt(), anyInt(), anyInt()))
|
||||
.thenReturn(mPackageFuture);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSetSurfacePackage() {
|
||||
// There should be no requests without the proper state.
|
||||
verify(mCommunalSource, times(0))
|
||||
.requestCommunalSurface(any(), anyInt(), anyInt(), anyInt());
|
||||
|
||||
// The full state must be present to make a request.
|
||||
mController.onViewAttached();
|
||||
verify(mCommunalSource, times(0))
|
||||
.requestCommunalSurface(any(), anyInt(), anyInt(), anyInt());
|
||||
|
||||
// Request surface view once all conditions are met.
|
||||
mCallback.surfaceCreated(mSurfaceHolder);
|
||||
verify(mCommunalSource)
|
||||
.requestCommunalSurface(mHostToken, DISPLAY_ID, MEASURED_WIDTH, MEASURED_HEIGHT);
|
||||
|
||||
when(mSurfaceView.isAttachedToWindow()).thenReturn(true);
|
||||
|
||||
// Respond to request.
|
||||
mPackageFuture.set(mSurfacePackage);
|
||||
mFakeExecutor.runAllReady();
|
||||
|
||||
|
||||
// Make sure SurfaceView is set.
|
||||
verify(mSurfaceView).setChildSurfacePackage(mSurfacePackage);
|
||||
verify(mSurfaceView).setZOrderOnTop(true);
|
||||
verify(mSurfaceView).setWillNotDraw(false);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user