Loading libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/split/SplitScreenConstants.java +1 −1 Original line number Diff line number Diff line Loading @@ -170,7 +170,7 @@ public class SplitScreenConstants { SNAP_TO_NONE, SNAP_TO_START_AND_DISMISS, SNAP_TO_END_AND_DISMISS, SNAP_TO_MINIMIZE SNAP_TO_MINIMIZE, }) public @interface SnapPosition {} Loading libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragLayout.java +158 −31 Original line number Diff line number Diff line Loading @@ -23,6 +23,7 @@ import static android.content.pm.ActivityInfo.CONFIG_UI_MODE; import static android.view.ViewGroup.LayoutParams.MATCH_PARENT; import static android.view.ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_REGION; import static com.android.wm.shell.Flags.enableFlexibleSplit; import static com.android.wm.shell.draganddrop.SplitDragPolicy.Target.TYPE_SPLIT_BOTTOM; import static com.android.wm.shell.draganddrop.SplitDragPolicy.Target.TYPE_SPLIT_LEFT; import static com.android.wm.shell.draganddrop.SplitDragPolicy.Target.TYPE_SPLIT_RIGHT; Loading @@ -43,14 +44,18 @@ import android.graphics.Color; import android.graphics.Insets; import android.graphics.Point; import android.graphics.Rect; import android.graphics.RectF; import android.graphics.Region; import android.graphics.drawable.Drawable; import android.util.Log; import android.view.DragEvent; import android.view.SurfaceControl; import android.view.View; import android.view.ViewGroup; import android.view.ViewTreeObserver; import android.view.WindowInsets; import android.view.WindowInsets.Type; import android.widget.FrameLayout; import android.widget.LinearLayout; import android.window.WindowContainerToken; Loading @@ -67,14 +72,22 @@ import com.android.wm.shell.shared.animation.Interpolators; import com.android.wm.shell.splitscreen.SplitScreenController; import java.io.PrintWriter; import java.util.Arrays; import java.util.Collection; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Objects; import java.util.function.BiConsumer; /** * Coordinates the visible drop targets for the current drag within a single display. */ public class DragLayout extends LinearLayout implements ViewTreeObserver.OnComputeInternalInsetsListener, DragLayoutProvider { implements ViewTreeObserver.OnComputeInternalInsetsListener, DragLayoutProvider, DragZoneAnimator{ static final boolean DEBUG_LAYOUT = false; // While dragging the status bar is hidden. private static final int HIDE_STATUS_BAR_FLAGS = StatusBarManager.DISABLE_NOTIFICATION_ICONS | StatusBarManager.DISABLE_NOTIFICATION_ALERTS Loading Loading @@ -108,13 +121,19 @@ public class DragLayout extends LinearLayout // The last position that was handled by the drag layout private final Point mLastPosition = new Point(); // Used with enableFlexibleSplit() flag private List<SplitDragPolicy.Target> mTargets; private Map<SplitDragPolicy.Target, DropZoneView> mTargetDropMap = new HashMap<>(); private FrameLayout mAnimatingRootLayout; // Used with enableFlexibleSplit() flag @SuppressLint("WrongConstant") public DragLayout(Context context, SplitScreenController splitScreenController, IconProvider iconProvider) { super(context); mSplitScreenController = splitScreenController; mIconProvider = iconProvider; mPolicy = new SplitDragPolicy(context, splitScreenController); mPolicy = new SplitDragPolicy(context, splitScreenController, this); mStatusBarManager = context.getSystemService(StatusBarManager.class); mLastConfiguration.setTo(context.getResources().getConfiguration()); Loading Loading @@ -211,12 +230,27 @@ public class DragLayout extends LinearLayout boolean isLeftRightSplit = mSplitScreenController != null && mSplitScreenController.isLeftRightSplit(); if (isLeftRightSplit) { if (enableFlexibleSplit()) { mTargetDropMap.values().forEach(dzv -> dzv.setBottomInset(mInsets.bottom)); } else { mDropZoneView1.setBottomInset(mInsets.bottom); mDropZoneView2.setBottomInset(mInsets.bottom); } } else { if (enableFlexibleSplit()) { Collection<DropZoneView> dropViews = mTargetDropMap.values(); final DropZoneView[] bottomView = {null}; dropViews.forEach(dropZoneView -> { bottomView[0] = dropZoneView; dropZoneView.setBottomInset(0); }); // TODO(b/349828130): necessary? maybe with UI polish // bottomView[0].setBottomInset(mInsets.bottom); } else { mDropZoneView1.setBottomInset(0); mDropZoneView2.setBottomInset(mInsets.bottom); } } return super.onApplyWindowInsets(insets); } Loading @@ -233,18 +267,32 @@ public class DragLayout extends LinearLayout final boolean themeChanged = (diff & CONFIG_ASSETS_PATHS) != 0 || (diff & CONFIG_UI_MODE) != 0; if (themeChanged) { if (enableFlexibleSplit()) { mTargetDropMap.values().forEach(DropZoneView::onThemeChange); } else { mDropZoneView1.onThemeChange(); mDropZoneView2.onThemeChange(); } } mLastConfiguration.setTo(newConfig); requestLayout(); } private void updateContainerMarginsForSingleTask() { if (enableFlexibleSplit()) { DropZoneView firstDropZone = mTargetDropMap.values().stream().findFirst().get(); mTargetDropMap.values().stream() .filter(dropZoneView -> dropZoneView != firstDropZone) .forEach(dropZoneView -> dropZoneView.setContainerMargin(0, 0, 0, 0)); firstDropZone.setContainerMargin( mDisplayMargin, mDisplayMargin, mDisplayMargin, mDisplayMargin ); } else { mDropZoneView1.setContainerMargin( mDisplayMargin, mDisplayMargin, mDisplayMargin, mDisplayMargin); mDropZoneView2.setContainerMargin(0, 0, 0, 0); } } private void updateContainerMargins(boolean isLeftRightSplit) { final float halfMargin = mDisplayMargin / 2f; Loading Loading @@ -305,6 +353,21 @@ public class DragLayout extends LinearLayout updateContainerMarginsForSingleTask(); } } } else { ActivityManager.RunningTaskInfo[] taskInfos = mSplitScreenController.getAllTaskInfos(); boolean anyTasksNull = Arrays.stream(taskInfos).anyMatch(Objects::isNull); if (enableFlexibleSplit() && taskInfos != null && !anyTasksNull) { int i = 0; for (DropZoneView v : mTargetDropMap.values()) { if (i >= taskInfos.length) { // TODO(b/349828130) Support once we add 3 StageRoots continue; } ActivityManager.RunningTaskInfo task = taskInfos[i]; v.setAppInfo(getResizingBackgroundColor(task), mIconProvider.getIcon(task.topActivityInfo)); i++; } } else { // We're already in split so get taskInfo from the controller to populate icon / color. ActivityManager.RunningTaskInfo topOrLeftTask = Loading @@ -320,6 +383,7 @@ public class DragLayout extends LinearLayout mDropZoneView1.setAppInfo(topOrLeftColor, topOrLeftIcon); mDropZoneView2.setAppInfo(bottomOrRightColor, bottomOrRightIcon); } } // Update the dropzones to match existing split sizes Rect topOrLeftBounds = new Rect(); Loading Loading @@ -391,7 +455,14 @@ public class DragLayout extends LinearLayout @NonNull @Override public void addDraggingView(ViewGroup rootView) { // TODO(b/349828130) We need to separate out view + logic here if (enableFlexibleSplit()) { removeAllViews(); mAnimatingRootLayout = new FrameLayout(getContext()); addView(mAnimatingRootLayout, new LinearLayout.LayoutParams(MATCH_PARENT, MATCH_PARENT)); ((LayoutParams) mAnimatingRootLayout.getLayoutParams()).weight = 1; } rootView.addView(this, new ViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT)); } Loading @@ -409,6 +480,24 @@ public class DragLayout extends LinearLayout // Inset the draw region by a little bit target.drawRegion.inset(mDisplayMargin, mDisplayMargin); } if (enableFlexibleSplit()) { mTargets = targets; mTargetDropMap.clear(); for (int i = 0; i < mTargets.size(); i++) { DropZoneView v = new DropZoneView(getContext()); SplitDragPolicy.Target t = mTargets.get(i); FrameLayout.LayoutParams params = new FrameLayout.LayoutParams(t.drawRegion.width(), t.drawRegion.height()); mAnimatingRootLayout.addView(v, params); v.setTranslationX(t.drawRegion.left); v.setTranslationY(t.drawRegion.top); mTargetDropMap.put(t, v); if (DEBUG_LAYOUT) { v.setDebugIndex(t.index); } } } } /** Loading @@ -433,6 +522,9 @@ public class DragLayout extends LinearLayout if (target == null) { // Animating to no target animateSplitContainers(false, null /* animCompleteCallback */); if (enableFlexibleSplit()) { animateHighlight(target); } } else if (mCurrentTarget == null) { if (mPolicy.getNumTargets() == 1) { animateFullscreenContainer(true); Loading @@ -440,10 +532,14 @@ public class DragLayout extends LinearLayout animateSplitContainers(true, null /* animCompleteCallback */); animateHighlight(target); } } else if (mCurrentTarget.type != target.type) { } else if (mCurrentTarget.type != target.type || enableFlexibleSplit()) { // Switching between targets if (enableFlexibleSplit()) { animateHighlight(target); } else { mDropZoneView1.animateSwitch(); mDropZoneView2.animateSwitch(); } // Announce for accessibility. switch (target.type) { case TYPE_SPLIT_LEFT: Loading Loading @@ -490,6 +586,9 @@ public class DragLayout extends LinearLayout mDropZoneView2.setForceIgnoreBottomMargin(false); updateContainerMargins(mIsLeftRightSplit); mCurrentTarget = null; if (enableFlexibleSplit()) { mAnimatingRootLayout.removeAllViews(); } } /** Loading Loading @@ -566,9 +665,20 @@ public class DragLayout extends LinearLayout mStatusBarManager.disable(visible ? HIDE_STATUS_BAR_FLAGS : DISABLE_NONE); Animator animator; if (enableFlexibleSplit()) { DropZoneView anyDropZoneView = null; for (DropZoneView dz : mTargetDropMap.values()) { dz.setShowingMargin(visible); anyDropZoneView = dz; } animator = anyDropZoneView != null ? anyDropZoneView.getAnimator() : null; } else { mDropZoneView1.setShowingMargin(visible); mDropZoneView2.setShowingMargin(visible); Animator animator = mDropZoneView1.getAnimator(); animator = mDropZoneView1.getAnimator(); } if (animCompleteCallback != null) { if (animator != null) { animator.addListener(new AnimatorListenerAdapter() { Loading @@ -584,7 +694,24 @@ public class DragLayout extends LinearLayout } } @Override public void animateDragTargets( @NonNull List<? extends BiConsumer<SplitDragPolicy.Target, View>> viewsToAnimate) { for (Map.Entry<SplitDragPolicy.Target, DropZoneView> entry : mTargetDropMap.entrySet()) { viewsToAnimate.get(0).accept(entry.getKey(), entry.getValue()); } } private void animateHighlight(SplitDragPolicy.Target target) { if (enableFlexibleSplit()) { for (Map.Entry<SplitDragPolicy.Target, DropZoneView> dzv : mTargetDropMap.entrySet()) { // Highlight the view w/ the matching target, unhighlight the rest dzv.getValue().setShowingHighlight(dzv.getKey() == target); } mPolicy.onHoveringOver(target); return; } if (target.type == TYPE_SPLIT_LEFT || target.type == TYPE_SPLIT_TOP) { mDropZoneView1.setShowingHighlight(true); mDropZoneView2.setShowingHighlight(false); Loading libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragZoneAnimator.kt 0 → 100644 +29 −0 Original line number Diff line number Diff line /* * Copyright (C) 2024 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.wm.shell.draganddrop import android.view.View import java.util.function.BiConsumer interface DragZoneAnimator { /** * Each consumer will be called for the corresponding DropZoneView. * This must match the number of targets in [.mTargets] otherwise will * throw an [IllegalStateException] */ fun animateDragTargets(viewsToAnimate: List<BiConsumer<SplitDragPolicy.Target, View>>) } No newline at end of file libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DropTarget.kt +1 −1 Original line number Diff line number Diff line Loading @@ -47,7 +47,7 @@ interface DropTarget { /** * Called when user is hovering Drag object over the given Target */ fun onHoveringOver(target: SplitDragPolicy.Target) {} fun onHoveringOver(target: SplitDragPolicy.Target?) {} /** * Called when the user has dropped the provided target (need not be the same target as * [onHoveringOver]) Loading libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DropZoneView.java +30 −0 Original line number Diff line number Diff line Loading @@ -20,12 +20,14 @@ import static com.android.wm.shell.shared.animation.Interpolators.FAST_OUT_SLOW_ import android.animation.Animator; import android.animation.ObjectAnimator; import android.annotation.SuppressLint; import android.content.Context; import android.graphics.Canvas; import android.graphics.Color; import android.graphics.Path; import android.graphics.drawable.ColorDrawable; import android.graphics.drawable.Drawable; import android.graphics.drawable.GradientDrawable; import android.util.AttributeSet; import android.util.FloatProperty; import android.view.Gravity; Loading @@ -33,6 +35,7 @@ import android.view.View; import android.view.ViewGroup; import android.widget.FrameLayout; import android.widget.ImageView; import android.widget.TextView; import androidx.annotation.Nullable; Loading Loading @@ -83,6 +86,7 @@ public class DropZoneView extends FrameLayout { private int mTargetBackgroundColor; private ObjectAnimator mMarginAnimator; private float mMarginPercent; private TextView mDebugIndex; // Renders a highlight or neutral transparent color private ColorDrawable mColorDrawable; Loading Loading @@ -125,6 +129,22 @@ public class DropZoneView extends FrameLayout { mMarginView = new MarginView(context); addView(mMarginView, new FrameLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT)); if (DEBUG_LAYOUT) { mDebugIndex = new TextView(context); mDebugIndex.setVisibility(GONE); mDebugIndex.setTextColor(Color.YELLOW); addView(mDebugIndex, new FrameLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT, Gravity.START | Gravity.TOP)); View borderView = new View(context); addView(borderView, new FrameLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT)); GradientDrawable border = new GradientDrawable(); border.setShape(GradientDrawable.RECTANGLE); border.setStroke(5, Color.RED); borderView.setBackground(border); } } public void onThemeChange() { Loading Loading @@ -236,6 +256,16 @@ public class DropZoneView extends FrameLayout { } } @SuppressLint("SetTextI18n") public void setDebugIndex(int index) { if (!DEBUG_LAYOUT) { return; } mDebugIndex.setText("Index:\n" + index); mDebugIndex.setVisibility(VISIBLE); } private void animateBackground(int startColor, int endColor) { if (DEBUG_LAYOUT) { ProtoLog.v(ShellProtoLogGroup.WM_SHELL_DRAG_AND_DROP, Loading Loading
libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/split/SplitScreenConstants.java +1 −1 Original line number Diff line number Diff line Loading @@ -170,7 +170,7 @@ public class SplitScreenConstants { SNAP_TO_NONE, SNAP_TO_START_AND_DISMISS, SNAP_TO_END_AND_DISMISS, SNAP_TO_MINIMIZE SNAP_TO_MINIMIZE, }) public @interface SnapPosition {} Loading
libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragLayout.java +158 −31 Original line number Diff line number Diff line Loading @@ -23,6 +23,7 @@ import static android.content.pm.ActivityInfo.CONFIG_UI_MODE; import static android.view.ViewGroup.LayoutParams.MATCH_PARENT; import static android.view.ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_REGION; import static com.android.wm.shell.Flags.enableFlexibleSplit; import static com.android.wm.shell.draganddrop.SplitDragPolicy.Target.TYPE_SPLIT_BOTTOM; import static com.android.wm.shell.draganddrop.SplitDragPolicy.Target.TYPE_SPLIT_LEFT; import static com.android.wm.shell.draganddrop.SplitDragPolicy.Target.TYPE_SPLIT_RIGHT; Loading @@ -43,14 +44,18 @@ import android.graphics.Color; import android.graphics.Insets; import android.graphics.Point; import android.graphics.Rect; import android.graphics.RectF; import android.graphics.Region; import android.graphics.drawable.Drawable; import android.util.Log; import android.view.DragEvent; import android.view.SurfaceControl; import android.view.View; import android.view.ViewGroup; import android.view.ViewTreeObserver; import android.view.WindowInsets; import android.view.WindowInsets.Type; import android.widget.FrameLayout; import android.widget.LinearLayout; import android.window.WindowContainerToken; Loading @@ -67,14 +72,22 @@ import com.android.wm.shell.shared.animation.Interpolators; import com.android.wm.shell.splitscreen.SplitScreenController; import java.io.PrintWriter; import java.util.Arrays; import java.util.Collection; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Objects; import java.util.function.BiConsumer; /** * Coordinates the visible drop targets for the current drag within a single display. */ public class DragLayout extends LinearLayout implements ViewTreeObserver.OnComputeInternalInsetsListener, DragLayoutProvider { implements ViewTreeObserver.OnComputeInternalInsetsListener, DragLayoutProvider, DragZoneAnimator{ static final boolean DEBUG_LAYOUT = false; // While dragging the status bar is hidden. private static final int HIDE_STATUS_BAR_FLAGS = StatusBarManager.DISABLE_NOTIFICATION_ICONS | StatusBarManager.DISABLE_NOTIFICATION_ALERTS Loading Loading @@ -108,13 +121,19 @@ public class DragLayout extends LinearLayout // The last position that was handled by the drag layout private final Point mLastPosition = new Point(); // Used with enableFlexibleSplit() flag private List<SplitDragPolicy.Target> mTargets; private Map<SplitDragPolicy.Target, DropZoneView> mTargetDropMap = new HashMap<>(); private FrameLayout mAnimatingRootLayout; // Used with enableFlexibleSplit() flag @SuppressLint("WrongConstant") public DragLayout(Context context, SplitScreenController splitScreenController, IconProvider iconProvider) { super(context); mSplitScreenController = splitScreenController; mIconProvider = iconProvider; mPolicy = new SplitDragPolicy(context, splitScreenController); mPolicy = new SplitDragPolicy(context, splitScreenController, this); mStatusBarManager = context.getSystemService(StatusBarManager.class); mLastConfiguration.setTo(context.getResources().getConfiguration()); Loading Loading @@ -211,12 +230,27 @@ public class DragLayout extends LinearLayout boolean isLeftRightSplit = mSplitScreenController != null && mSplitScreenController.isLeftRightSplit(); if (isLeftRightSplit) { if (enableFlexibleSplit()) { mTargetDropMap.values().forEach(dzv -> dzv.setBottomInset(mInsets.bottom)); } else { mDropZoneView1.setBottomInset(mInsets.bottom); mDropZoneView2.setBottomInset(mInsets.bottom); } } else { if (enableFlexibleSplit()) { Collection<DropZoneView> dropViews = mTargetDropMap.values(); final DropZoneView[] bottomView = {null}; dropViews.forEach(dropZoneView -> { bottomView[0] = dropZoneView; dropZoneView.setBottomInset(0); }); // TODO(b/349828130): necessary? maybe with UI polish // bottomView[0].setBottomInset(mInsets.bottom); } else { mDropZoneView1.setBottomInset(0); mDropZoneView2.setBottomInset(mInsets.bottom); } } return super.onApplyWindowInsets(insets); } Loading @@ -233,18 +267,32 @@ public class DragLayout extends LinearLayout final boolean themeChanged = (diff & CONFIG_ASSETS_PATHS) != 0 || (diff & CONFIG_UI_MODE) != 0; if (themeChanged) { if (enableFlexibleSplit()) { mTargetDropMap.values().forEach(DropZoneView::onThemeChange); } else { mDropZoneView1.onThemeChange(); mDropZoneView2.onThemeChange(); } } mLastConfiguration.setTo(newConfig); requestLayout(); } private void updateContainerMarginsForSingleTask() { if (enableFlexibleSplit()) { DropZoneView firstDropZone = mTargetDropMap.values().stream().findFirst().get(); mTargetDropMap.values().stream() .filter(dropZoneView -> dropZoneView != firstDropZone) .forEach(dropZoneView -> dropZoneView.setContainerMargin(0, 0, 0, 0)); firstDropZone.setContainerMargin( mDisplayMargin, mDisplayMargin, mDisplayMargin, mDisplayMargin ); } else { mDropZoneView1.setContainerMargin( mDisplayMargin, mDisplayMargin, mDisplayMargin, mDisplayMargin); mDropZoneView2.setContainerMargin(0, 0, 0, 0); } } private void updateContainerMargins(boolean isLeftRightSplit) { final float halfMargin = mDisplayMargin / 2f; Loading Loading @@ -305,6 +353,21 @@ public class DragLayout extends LinearLayout updateContainerMarginsForSingleTask(); } } } else { ActivityManager.RunningTaskInfo[] taskInfos = mSplitScreenController.getAllTaskInfos(); boolean anyTasksNull = Arrays.stream(taskInfos).anyMatch(Objects::isNull); if (enableFlexibleSplit() && taskInfos != null && !anyTasksNull) { int i = 0; for (DropZoneView v : mTargetDropMap.values()) { if (i >= taskInfos.length) { // TODO(b/349828130) Support once we add 3 StageRoots continue; } ActivityManager.RunningTaskInfo task = taskInfos[i]; v.setAppInfo(getResizingBackgroundColor(task), mIconProvider.getIcon(task.topActivityInfo)); i++; } } else { // We're already in split so get taskInfo from the controller to populate icon / color. ActivityManager.RunningTaskInfo topOrLeftTask = Loading @@ -320,6 +383,7 @@ public class DragLayout extends LinearLayout mDropZoneView1.setAppInfo(topOrLeftColor, topOrLeftIcon); mDropZoneView2.setAppInfo(bottomOrRightColor, bottomOrRightIcon); } } // Update the dropzones to match existing split sizes Rect topOrLeftBounds = new Rect(); Loading Loading @@ -391,7 +455,14 @@ public class DragLayout extends LinearLayout @NonNull @Override public void addDraggingView(ViewGroup rootView) { // TODO(b/349828130) We need to separate out view + logic here if (enableFlexibleSplit()) { removeAllViews(); mAnimatingRootLayout = new FrameLayout(getContext()); addView(mAnimatingRootLayout, new LinearLayout.LayoutParams(MATCH_PARENT, MATCH_PARENT)); ((LayoutParams) mAnimatingRootLayout.getLayoutParams()).weight = 1; } rootView.addView(this, new ViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT)); } Loading @@ -409,6 +480,24 @@ public class DragLayout extends LinearLayout // Inset the draw region by a little bit target.drawRegion.inset(mDisplayMargin, mDisplayMargin); } if (enableFlexibleSplit()) { mTargets = targets; mTargetDropMap.clear(); for (int i = 0; i < mTargets.size(); i++) { DropZoneView v = new DropZoneView(getContext()); SplitDragPolicy.Target t = mTargets.get(i); FrameLayout.LayoutParams params = new FrameLayout.LayoutParams(t.drawRegion.width(), t.drawRegion.height()); mAnimatingRootLayout.addView(v, params); v.setTranslationX(t.drawRegion.left); v.setTranslationY(t.drawRegion.top); mTargetDropMap.put(t, v); if (DEBUG_LAYOUT) { v.setDebugIndex(t.index); } } } } /** Loading @@ -433,6 +522,9 @@ public class DragLayout extends LinearLayout if (target == null) { // Animating to no target animateSplitContainers(false, null /* animCompleteCallback */); if (enableFlexibleSplit()) { animateHighlight(target); } } else if (mCurrentTarget == null) { if (mPolicy.getNumTargets() == 1) { animateFullscreenContainer(true); Loading @@ -440,10 +532,14 @@ public class DragLayout extends LinearLayout animateSplitContainers(true, null /* animCompleteCallback */); animateHighlight(target); } } else if (mCurrentTarget.type != target.type) { } else if (mCurrentTarget.type != target.type || enableFlexibleSplit()) { // Switching between targets if (enableFlexibleSplit()) { animateHighlight(target); } else { mDropZoneView1.animateSwitch(); mDropZoneView2.animateSwitch(); } // Announce for accessibility. switch (target.type) { case TYPE_SPLIT_LEFT: Loading Loading @@ -490,6 +586,9 @@ public class DragLayout extends LinearLayout mDropZoneView2.setForceIgnoreBottomMargin(false); updateContainerMargins(mIsLeftRightSplit); mCurrentTarget = null; if (enableFlexibleSplit()) { mAnimatingRootLayout.removeAllViews(); } } /** Loading Loading @@ -566,9 +665,20 @@ public class DragLayout extends LinearLayout mStatusBarManager.disable(visible ? HIDE_STATUS_BAR_FLAGS : DISABLE_NONE); Animator animator; if (enableFlexibleSplit()) { DropZoneView anyDropZoneView = null; for (DropZoneView dz : mTargetDropMap.values()) { dz.setShowingMargin(visible); anyDropZoneView = dz; } animator = anyDropZoneView != null ? anyDropZoneView.getAnimator() : null; } else { mDropZoneView1.setShowingMargin(visible); mDropZoneView2.setShowingMargin(visible); Animator animator = mDropZoneView1.getAnimator(); animator = mDropZoneView1.getAnimator(); } if (animCompleteCallback != null) { if (animator != null) { animator.addListener(new AnimatorListenerAdapter() { Loading @@ -584,7 +694,24 @@ public class DragLayout extends LinearLayout } } @Override public void animateDragTargets( @NonNull List<? extends BiConsumer<SplitDragPolicy.Target, View>> viewsToAnimate) { for (Map.Entry<SplitDragPolicy.Target, DropZoneView> entry : mTargetDropMap.entrySet()) { viewsToAnimate.get(0).accept(entry.getKey(), entry.getValue()); } } private void animateHighlight(SplitDragPolicy.Target target) { if (enableFlexibleSplit()) { for (Map.Entry<SplitDragPolicy.Target, DropZoneView> dzv : mTargetDropMap.entrySet()) { // Highlight the view w/ the matching target, unhighlight the rest dzv.getValue().setShowingHighlight(dzv.getKey() == target); } mPolicy.onHoveringOver(target); return; } if (target.type == TYPE_SPLIT_LEFT || target.type == TYPE_SPLIT_TOP) { mDropZoneView1.setShowingHighlight(true); mDropZoneView2.setShowingHighlight(false); Loading
libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragZoneAnimator.kt 0 → 100644 +29 −0 Original line number Diff line number Diff line /* * Copyright (C) 2024 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.wm.shell.draganddrop import android.view.View import java.util.function.BiConsumer interface DragZoneAnimator { /** * Each consumer will be called for the corresponding DropZoneView. * This must match the number of targets in [.mTargets] otherwise will * throw an [IllegalStateException] */ fun animateDragTargets(viewsToAnimate: List<BiConsumer<SplitDragPolicy.Target, View>>) } No newline at end of file
libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DropTarget.kt +1 −1 Original line number Diff line number Diff line Loading @@ -47,7 +47,7 @@ interface DropTarget { /** * Called when user is hovering Drag object over the given Target */ fun onHoveringOver(target: SplitDragPolicy.Target) {} fun onHoveringOver(target: SplitDragPolicy.Target?) {} /** * Called when the user has dropped the provided target (need not be the same target as * [onHoveringOver]) Loading
libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DropZoneView.java +30 −0 Original line number Diff line number Diff line Loading @@ -20,12 +20,14 @@ import static com.android.wm.shell.shared.animation.Interpolators.FAST_OUT_SLOW_ import android.animation.Animator; import android.animation.ObjectAnimator; import android.annotation.SuppressLint; import android.content.Context; import android.graphics.Canvas; import android.graphics.Color; import android.graphics.Path; import android.graphics.drawable.ColorDrawable; import android.graphics.drawable.Drawable; import android.graphics.drawable.GradientDrawable; import android.util.AttributeSet; import android.util.FloatProperty; import android.view.Gravity; Loading @@ -33,6 +35,7 @@ import android.view.View; import android.view.ViewGroup; import android.widget.FrameLayout; import android.widget.ImageView; import android.widget.TextView; import androidx.annotation.Nullable; Loading Loading @@ -83,6 +86,7 @@ public class DropZoneView extends FrameLayout { private int mTargetBackgroundColor; private ObjectAnimator mMarginAnimator; private float mMarginPercent; private TextView mDebugIndex; // Renders a highlight or neutral transparent color private ColorDrawable mColorDrawable; Loading Loading @@ -125,6 +129,22 @@ public class DropZoneView extends FrameLayout { mMarginView = new MarginView(context); addView(mMarginView, new FrameLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT)); if (DEBUG_LAYOUT) { mDebugIndex = new TextView(context); mDebugIndex.setVisibility(GONE); mDebugIndex.setTextColor(Color.YELLOW); addView(mDebugIndex, new FrameLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT, Gravity.START | Gravity.TOP)); View borderView = new View(context); addView(borderView, new FrameLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT)); GradientDrawable border = new GradientDrawable(); border.setShape(GradientDrawable.RECTANGLE); border.setStroke(5, Color.RED); borderView.setBackground(border); } } public void onThemeChange() { Loading Loading @@ -236,6 +256,16 @@ public class DropZoneView extends FrameLayout { } } @SuppressLint("SetTextI18n") public void setDebugIndex(int index) { if (!DEBUG_LAYOUT) { return; } mDebugIndex.setText("Index:\n" + index); mDebugIndex.setVisibility(VISIBLE); } private void animateBackground(int startColor, int endColor) { if (DEBUG_LAYOUT) { ProtoLog.v(ShellProtoLogGroup.WM_SHELL_DRAG_AND_DROP, Loading