Merge "Fix showing black background when resizing the divider bar."
This commit is contained in:
@@ -27,9 +27,9 @@
|
||||
<com.android.wm.shell.legacysplitscreen.MinimizedDockShadow
|
||||
style="@style/DockedDividerMinimizedShadow"
|
||||
android:id="@+id/minimized_dock_shadow"
|
||||
android:alpha="0"/>">
|
||||
android:alpha="0"/>
|
||||
|
||||
<com.android.wm.shell.legacysplitscreen.DividerHandleView
|
||||
<com.android.wm.shell.common.split.DividerHandleView
|
||||
style="@style/DockedDividerHandle"
|
||||
android:id="@+id/docked_divider_handle"
|
||||
android:contentDescription="@string/accessibility_divider"
|
||||
|
||||
@@ -24,4 +24,10 @@
|
||||
android:id="@+id/docked_divider_background"
|
||||
android:background="@color/docked_divider_background"/>
|
||||
|
||||
<com.android.wm.shell.common.split.DividerHandleView
|
||||
style="@style/DockedDividerHandle"
|
||||
android:id="@+id/docked_divider_handle"
|
||||
android:contentDescription="@string/accessibility_divider"
|
||||
android:background="@null"/>
|
||||
|
||||
</com.android.wm.shell.common.split.DividerView>
|
||||
|
||||
@@ -96,7 +96,8 @@ class AppPair implements ShellTaskOrganizer.TaskListener, SplitLayout.LayoutChan
|
||||
mTaskInfo2 = task2;
|
||||
mSplitLayout = new SplitLayout(
|
||||
mDisplayController.getDisplayContext(mRootTaskInfo.displayId),
|
||||
mRootTaskInfo.configuration, this, b -> b.setParent(mRootTaskLeash));
|
||||
mRootTaskInfo.configuration, this /* layoutChangeListener */,
|
||||
b -> b.setParent(mRootTaskLeash));
|
||||
|
||||
final WindowContainerToken token1 = task1.token;
|
||||
final WindowContainerToken token2 = task2.token;
|
||||
@@ -191,22 +192,7 @@ class AppPair implements ShellTaskOrganizer.TaskListener, SplitLayout.LayoutChan
|
||||
|
||||
if (mSplitLayout != null
|
||||
&& mSplitLayout.updateConfiguration(mRootTaskInfo.configuration)) {
|
||||
// Update bounds when root bounds or its orientation changed.
|
||||
final WindowContainerTransaction wct = new WindowContainerTransaction();
|
||||
final SurfaceControl dividerLeash = mSplitLayout.getDividerLeash();
|
||||
final Rect dividerBounds = mSplitLayout.getDividerBounds();
|
||||
final Rect bounds1 = mSplitLayout.getBounds1();
|
||||
final Rect bounds2 = mSplitLayout.getBounds2();
|
||||
|
||||
wct.setBounds(mTaskInfo1.token, bounds1)
|
||||
.setBounds(mTaskInfo2.token, bounds2);
|
||||
mController.getTaskOrganizer().applyTransaction(wct);
|
||||
mSyncQueue.runInSync(t -> t
|
||||
.setPosition(mTaskLeash1, bounds1.left, bounds1.top)
|
||||
.setPosition(mTaskLeash2, bounds2.left, bounds2.top)
|
||||
.setPosition(dividerLeash, dividerBounds.left, dividerBounds.top)
|
||||
// Resets layer to divider bar to make sure it is always on top.
|
||||
.setLayer(dividerLeash, Integer.MAX_VALUE));
|
||||
onBoundsChanged(mSplitLayout);
|
||||
}
|
||||
} else if (taskInfo.taskId == getTaskId1()) {
|
||||
mTaskInfo1 = taskInfo;
|
||||
@@ -262,6 +248,10 @@ class AppPair implements ShellTaskOrganizer.TaskListener, SplitLayout.LayoutChan
|
||||
final Rect bounds1 = layout.getBounds1();
|
||||
final Rect bounds2 = layout.getBounds2();
|
||||
mSyncQueue.runInSync(t -> t
|
||||
// Ignores the original surface bounds so that the app could fill up the gap
|
||||
// between each surface with corresponding background while resizing.
|
||||
.setWindowCrop(mTaskLeash1, bounds1.width(), bounds1.height())
|
||||
.setWindowCrop(mTaskLeash2, bounds2.width(), bounds2.height())
|
||||
.setPosition(dividerLeash, dividerBounds.left, dividerBounds.top)
|
||||
.setPosition(mTaskLeash1, bounds1.left, bounds1.top)
|
||||
.setPosition(mTaskLeash2, bounds2.left, bounds2.top));
|
||||
@@ -279,6 +269,10 @@ class AppPair implements ShellTaskOrganizer.TaskListener, SplitLayout.LayoutChan
|
||||
.setBounds(mTaskInfo2.token, bounds2);
|
||||
mController.getTaskOrganizer().applyTransaction(wct);
|
||||
mSyncQueue.runInSync(t -> t
|
||||
// Resets layer of divider bar to make sure it is always on top.
|
||||
.setLayer(dividerLeash, Integer.MAX_VALUE)
|
||||
.setWindowCrop(mTaskLeash1, bounds1.width(), bounds1.height())
|
||||
.setWindowCrop(mTaskLeash2, bounds2.width(), bounds2.height())
|
||||
.setPosition(dividerLeash, dividerBounds.left, dividerBounds.top)
|
||||
.setPosition(mTaskLeash1, bounds1.left, bounds1.top)
|
||||
.setPosition(mTaskLeash2, bounds2.left, bounds2.top));
|
||||
|
||||
@@ -14,7 +14,10 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.android.wm.shell.legacysplitscreen;
|
||||
package com.android.wm.shell.common.split;
|
||||
|
||||
import static com.android.wm.shell.common.split.DividerView.TOUCH_ANIMATION_DURATION;
|
||||
import static com.android.wm.shell.common.split.DividerView.TOUCH_RELEASE_ANIMATION_DURATION;
|
||||
|
||||
import android.animation.Animator;
|
||||
import android.animation.AnimatorListenerAdapter;
|
||||
@@ -84,7 +87,8 @@ public class DividerHandleView extends View {
|
||||
mCircleDiameter = (mWidth + mHeight) / 3;
|
||||
}
|
||||
|
||||
void setTouching(boolean touching, boolean animate) {
|
||||
/** Sets touching state for this handle view. */
|
||||
public void setTouching(boolean touching, boolean animate) {
|
||||
if (touching == mTouching) {
|
||||
return;
|
||||
}
|
||||
@@ -116,8 +120,8 @@ public class DividerHandleView extends View {
|
||||
mAnimator = new AnimatorSet();
|
||||
mAnimator.playTogether(widthAnimator, heightAnimator);
|
||||
mAnimator.setDuration(touching
|
||||
? DividerView.TOUCH_ANIMATION_DURATION
|
||||
: DividerView.TOUCH_RELEASE_ANIMATION_DURATION);
|
||||
? TOUCH_ANIMATION_DURATION
|
||||
: TOUCH_RELEASE_ANIMATION_DURATION);
|
||||
mAnimator.setInterpolator(touching
|
||||
? Interpolators.TOUCH_RESPONSE
|
||||
: Interpolators.FAST_OUT_SLOW_IN);
|
||||
@@ -33,17 +33,23 @@ import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import com.android.internal.policy.DividerSnapAlgorithm;
|
||||
import com.android.wm.shell.R;
|
||||
import com.android.wm.shell.animation.Interpolators;
|
||||
|
||||
/**
|
||||
* Stack divider for app pair.
|
||||
*/
|
||||
// TODO(b/172704238): add handle view to indicate touching status.
|
||||
public class DividerView extends FrameLayout implements View.OnTouchListener {
|
||||
public static final long TOUCH_ANIMATION_DURATION = 150;
|
||||
public static final long TOUCH_RELEASE_ANIMATION_DURATION = 200;
|
||||
|
||||
private final int mTouchSlop = ViewConfiguration.get(getContext()).getScaledTouchSlop();
|
||||
|
||||
private SplitLayout mSplitLayout;
|
||||
private SurfaceControlViewHost mViewHost;
|
||||
private DragListener mDragListener;
|
||||
private DividerHandleView mHandle;
|
||||
private View mBackground;
|
||||
private int mTouchElevation;
|
||||
|
||||
private VelocityTracker mVelocityTracker;
|
||||
private boolean mMoving;
|
||||
@@ -70,16 +76,18 @@ public class DividerView extends FrameLayout implements View.OnTouchListener {
|
||||
/** Sets up essential dependencies of the divider bar. */
|
||||
public void setup(
|
||||
SplitLayout layout,
|
||||
SurfaceControlViewHost viewHost,
|
||||
@Nullable DragListener dragListener) {
|
||||
SurfaceControlViewHost viewHost) {
|
||||
mSplitLayout = layout;
|
||||
mViewHost = viewHost;
|
||||
mDragListener = dragListener;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onFinishInflate() {
|
||||
super.onFinishInflate();
|
||||
mHandle = findViewById(R.id.docked_divider_handle);
|
||||
mBackground = findViewById(R.id.docked_divider_background);
|
||||
mTouchElevation = getResources().getDimensionPixelSize(
|
||||
R.dimen.docked_stack_divider_lift_elevation);
|
||||
setOnTouchListener(this);
|
||||
}
|
||||
|
||||
@@ -97,7 +105,7 @@ public class DividerView extends FrameLayout implements View.OnTouchListener {
|
||||
case MotionEvent.ACTION_DOWN:
|
||||
mVelocityTracker = VelocityTracker.obtain();
|
||||
mVelocityTracker.addMovement(event);
|
||||
setSlippery(false);
|
||||
setTouching();
|
||||
mStartPos = touchPos;
|
||||
mMoving = false;
|
||||
break;
|
||||
@@ -106,9 +114,6 @@ public class DividerView extends FrameLayout implements View.OnTouchListener {
|
||||
if (!mMoving && Math.abs(touchPos - mStartPos) > mTouchSlop) {
|
||||
mStartPos = touchPos;
|
||||
mMoving = true;
|
||||
if (mDragListener != null) {
|
||||
mDragListener.onDragStart();
|
||||
}
|
||||
}
|
||||
if (mMoving) {
|
||||
final int position = mSplitLayout.getDividePosition() + touchPos - mStartPos;
|
||||
@@ -122,11 +127,8 @@ public class DividerView extends FrameLayout implements View.OnTouchListener {
|
||||
final float velocity = isLandscape
|
||||
? mVelocityTracker.getXVelocity()
|
||||
: mVelocityTracker.getYVelocity();
|
||||
setSlippery(true);
|
||||
releaseTouching();
|
||||
mMoving = false;
|
||||
if (mDragListener != null) {
|
||||
mDragListener.onDragEnd();
|
||||
}
|
||||
|
||||
final int position = mSplitLayout.getDividePosition() + touchPos - mStartPos;
|
||||
final DividerSnapAlgorithm.SnapTarget snapTarget =
|
||||
@@ -137,6 +139,45 @@ public class DividerView extends FrameLayout implements View.OnTouchListener {
|
||||
return true;
|
||||
}
|
||||
|
||||
private void setTouching() {
|
||||
setSlippery(false);
|
||||
mHandle.setTouching(true, true);
|
||||
if (isLandscape()) {
|
||||
mBackground.animate().scaleX(1.4f);
|
||||
} else {
|
||||
mBackground.animate().scaleY(1.4f);
|
||||
}
|
||||
mBackground.animate()
|
||||
.setInterpolator(Interpolators.TOUCH_RESPONSE)
|
||||
.setDuration(TOUCH_ANIMATION_DURATION)
|
||||
.translationZ(mTouchElevation)
|
||||
.start();
|
||||
// Lift handle as well so it doesn't get behind the background, even though it doesn't
|
||||
// cast shadow.
|
||||
mHandle.animate()
|
||||
.setInterpolator(Interpolators.TOUCH_RESPONSE)
|
||||
.setDuration(TOUCH_ANIMATION_DURATION)
|
||||
.translationZ(mTouchElevation)
|
||||
.start();
|
||||
}
|
||||
|
||||
private void releaseTouching() {
|
||||
setSlippery(true);
|
||||
mHandle.setTouching(false, true);
|
||||
mBackground.animate()
|
||||
.setInterpolator(Interpolators.FAST_OUT_SLOW_IN)
|
||||
.setDuration(TOUCH_RELEASE_ANIMATION_DURATION)
|
||||
.translationZ(0)
|
||||
.scaleX(1f)
|
||||
.scaleY(1f)
|
||||
.start();
|
||||
mHandle.animate()
|
||||
.setInterpolator(Interpolators.FAST_OUT_SLOW_IN)
|
||||
.setDuration(TOUCH_RELEASE_ANIMATION_DURATION)
|
||||
.translationZ(0)
|
||||
.start();
|
||||
}
|
||||
|
||||
private void setSlippery(boolean slippery) {
|
||||
if (mViewHost == null) {
|
||||
return;
|
||||
@@ -159,13 +200,4 @@ public class DividerView extends FrameLayout implements View.OnTouchListener {
|
||||
private boolean isLandscape() {
|
||||
return getResources().getConfiguration().orientation == ORIENTATION_LANDSCAPE;
|
||||
}
|
||||
|
||||
/** Monitors dragging action of the divider bar. */
|
||||
// TODO(b/172704238): add listeners to deal with resizing state of the app windows.
|
||||
public interface DragListener {
|
||||
/** Called when start dragging. */
|
||||
void onDragStart();
|
||||
/** Called when stop dragging. */
|
||||
void onDragEnd();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -156,12 +156,14 @@ public class SplitLayout {
|
||||
void updateDivideBounds(int position) {
|
||||
updateBounds(position);
|
||||
mLayoutChangeListener.onBoundsChanging(this);
|
||||
mSplitWindowManager.setResizingSplits(true);
|
||||
}
|
||||
|
||||
void setDividePosition(int position) {
|
||||
mDividePosition = position;
|
||||
updateBounds(mDividePosition);
|
||||
mLayoutChangeListener.onBoundsChanged(this);
|
||||
mSplitWindowManager.setResizingSplits(false);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -172,9 +174,11 @@ public class SplitLayout {
|
||||
switch (snapTarget.flag) {
|
||||
case FLAG_DISMISS_START:
|
||||
mLayoutChangeListener.onSnappedToDismiss(false /* snappedToEnd */);
|
||||
mSplitWindowManager.setResizingSplits(false);
|
||||
break;
|
||||
case FLAG_DISMISS_END:
|
||||
mLayoutChangeListener.onSnappedToDismiss(true /* snappedToEnd */);
|
||||
mSplitWindowManager.setResizingSplits(false);
|
||||
break;
|
||||
default:
|
||||
flingDividePosition(currentPosition, snapTarget.position);
|
||||
|
||||
@@ -25,6 +25,7 @@ import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_NO_MOVE_ANIMA
|
||||
import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_TRUSTED_OVERLAY;
|
||||
import static android.view.WindowManager.LayoutParams.TYPE_DOCK_DIVIDER;
|
||||
|
||||
import android.app.ActivityTaskManager;
|
||||
import android.content.Context;
|
||||
import android.content.res.Configuration;
|
||||
import android.graphics.PixelFormat;
|
||||
@@ -32,6 +33,8 @@ import android.graphics.Rect;
|
||||
import android.graphics.Region;
|
||||
import android.os.Binder;
|
||||
import android.os.IBinder;
|
||||
import android.os.RemoteException;
|
||||
import android.util.Slog;
|
||||
import android.view.IWindow;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.SurfaceControl;
|
||||
@@ -47,11 +50,13 @@ import com.android.wm.shell.R;
|
||||
* Holds view hierarchy of a root surface and helps to inflate {@link DividerView} for a split.
|
||||
*/
|
||||
public final class SplitWindowManager extends WindowlessWindowManager {
|
||||
private static final String TAG = SplitWindowManager.class.getSimpleName();
|
||||
private static final String DIVIDER_WINDOW_TITLE = "SplitDivider";
|
||||
|
||||
private final ParentContainerCallbacks mParentContainerCallbacks;
|
||||
private Context mContext;
|
||||
private SurfaceControlViewHost mViewHost;
|
||||
final private ParentContainerCallbacks mParentContainerCallbacks;
|
||||
private boolean mResizingSplits;
|
||||
|
||||
public interface ParentContainerCallbacks {
|
||||
void attachToParentSurface(SurfaceControl.Builder b);
|
||||
@@ -104,7 +109,7 @@ public final class SplitWindowManager extends WindowlessWindowManager {
|
||||
lp.setTitle(DIVIDER_WINDOW_TITLE);
|
||||
lp.privateFlags |= PRIVATE_FLAG_NO_MOVE_ANIMATION | PRIVATE_FLAG_TRUSTED_OVERLAY;
|
||||
mViewHost.setView(dividerView, lp);
|
||||
dividerView.setup(splitLayout, mViewHost, null /* dragListener */);
|
||||
dividerView.setup(splitLayout, mViewHost);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -117,6 +122,16 @@ public final class SplitWindowManager extends WindowlessWindowManager {
|
||||
mViewHost = null;
|
||||
}
|
||||
|
||||
void setResizingSplits(boolean resizing) {
|
||||
if (resizing == mResizingSplits) return;
|
||||
try {
|
||||
ActivityTaskManager.getService().setSplitScreenResizing(resizing);
|
||||
mResizingSplits = resizing;
|
||||
} catch (RemoteException e) {
|
||||
Slog.w(TAG, "Error calling setSplitScreenResizing", e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets {@link SurfaceControl} of the surface holding divider view. @return {@code null} if not
|
||||
* feasible.
|
||||
|
||||
@@ -20,6 +20,9 @@ import static android.view.PointerIcon.TYPE_HORIZONTAL_DOUBLE_ARROW;
|
||||
import static android.view.PointerIcon.TYPE_VERTICAL_DOUBLE_ARROW;
|
||||
import static android.view.WindowManager.DOCKED_RIGHT;
|
||||
|
||||
import static com.android.wm.shell.common.split.DividerView.TOUCH_ANIMATION_DURATION;
|
||||
import static com.android.wm.shell.common.split.DividerView.TOUCH_RELEASE_ANIMATION_DURATION;
|
||||
|
||||
import android.animation.AnimationHandler;
|
||||
import android.animation.Animator;
|
||||
import android.animation.AnimatorListenerAdapter;
|
||||
@@ -66,6 +69,7 @@ import com.android.internal.policy.DockedDividerUtils;
|
||||
import com.android.wm.shell.R;
|
||||
import com.android.wm.shell.animation.FlingAnimationUtils;
|
||||
import com.android.wm.shell.animation.Interpolators;
|
||||
import com.android.wm.shell.common.split.DividerHandleView;
|
||||
|
||||
import java.util.function.Consumer;
|
||||
|
||||
@@ -82,9 +86,6 @@ public class DividerView extends FrameLayout implements OnTouchListener,
|
||||
void onDraggingEnd();
|
||||
}
|
||||
|
||||
static final long TOUCH_ANIMATION_DURATION = 150;
|
||||
static final long TOUCH_RELEASE_ANIMATION_DURATION = 200;
|
||||
|
||||
public static final int INVALID_RECENTS_GROW_TARGET = -1;
|
||||
|
||||
private static final int LOG_VALUE_RESIZE_50_50 = 0;
|
||||
|
||||
@@ -75,15 +75,16 @@ public class SplitLayoutTests extends ShellTestCase {
|
||||
}
|
||||
|
||||
@Test
|
||||
@UiThreadTest
|
||||
public void testSnapToTarget() {
|
||||
DividerSnapAlgorithm.SnapTarget snapTarget = getSnapTarget(0 /* position */,
|
||||
DividerSnapAlgorithm.SnapTarget.FLAG_NONE);
|
||||
mSplitLayout.snapToTarget(0 /* currentPosition */, snapTarget);
|
||||
verify(mLayoutChangeListener).onBoundsChanging(any(SplitLayout.class));
|
||||
public void testSetDividePosition() {
|
||||
mSplitLayout.setDividePosition(anyInt());
|
||||
verify(mLayoutChangeListener).onBoundsChanged(any(SplitLayout.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
@UiThreadTest
|
||||
public void testSnapToDismissTarget() {
|
||||
// verify it callbacks properly when the snap target indicates dismissing split.
|
||||
snapTarget = getSnapTarget(0 /* position */,
|
||||
DividerSnapAlgorithm.SnapTarget snapTarget = getSnapTarget(0 /* position */,
|
||||
DividerSnapAlgorithm.SnapTarget.FLAG_DISMISS_START);
|
||||
mSplitLayout.snapToTarget(0 /* currentPosition */, snapTarget);
|
||||
verify(mLayoutChangeListener).onSnappedToDismiss(eq(false));
|
||||
|
||||
@@ -3843,7 +3843,8 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
|
||||
if (task == null) {
|
||||
return false;
|
||||
}
|
||||
if (!inSplitScreenWindowingMode() && !inFreeformWindowingMode()) {
|
||||
if (!inSplitScreenWindowingMode() && !inFreeformWindowingMode()
|
||||
&& !task.getRootTask().mCreatedByOrganizer) {
|
||||
return false;
|
||||
}
|
||||
// TODO(157912944): formalize drag-resizing so that exceptions aren't hardcoded like this
|
||||
|
||||
Reference in New Issue
Block a user