Jorim Jaggi | 1fcbab6 | 2015-11-04 16:39:50 +0100 | [diff] [blame] | 1 | /* |
| 2 | * Copyright (C) 2015 The Android Open Source Project |
| 3 | * |
| 4 | * Licensed under the Apache License, Version 2.0 (the "License"); |
| 5 | * you may not use this file except in compliance with the License. |
| 6 | * You may obtain a copy of the License at |
| 7 | * |
| 8 | * http://www.apache.org/licenses/LICENSE-2.0 |
| 9 | * |
| 10 | * Unless required by applicable law or agreed to in writing, software |
| 11 | * distributed under the License is distributed on an "AS IS" BASIS, |
| 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| 13 | * See the License for the specific language governing permissions and |
| 14 | * limitations under the License. |
| 15 | */ |
| 16 | |
| 17 | package com.android.systemui.stackdivider; |
| 18 | |
| 19 | import android.animation.Animator; |
| 20 | import android.animation.AnimatorListenerAdapter; |
| 21 | import android.animation.ValueAnimator; |
| 22 | import android.animation.ValueAnimator.AnimatorUpdateListener; |
| 23 | import android.annotation.Nullable; |
Jorim Jaggi | 5098159 | 2015-12-29 17:54:12 +0100 | [diff] [blame] | 24 | import android.app.ActivityManager.StackId; |
Jorim Jaggi | 1fcbab6 | 2015-11-04 16:39:50 +0100 | [diff] [blame] | 25 | import android.content.Context; |
| 26 | import android.content.res.Configuration; |
| 27 | import android.graphics.Rect; |
| 28 | import android.graphics.Region.Op; |
Filip Gruszczynski | f6d8e9e | 2015-11-12 13:39:20 -0800 | [diff] [blame] | 29 | import android.hardware.display.DisplayManager; |
Jorim Jaggi | 1fcbab6 | 2015-11-04 16:39:50 +0100 | [diff] [blame] | 30 | import android.util.AttributeSet; |
Filip Gruszczynski | f6d8e9e | 2015-11-12 13:39:20 -0800 | [diff] [blame] | 31 | import android.view.Display; |
| 32 | import android.view.DisplayInfo; |
Jorim Jaggi | 1fcbab6 | 2015-11-04 16:39:50 +0100 | [diff] [blame] | 33 | import android.view.MotionEvent; |
Jun Mukai | d4eaef7 | 2015-10-30 15:54:33 -0700 | [diff] [blame] | 34 | import android.view.PointerIcon; |
Jorim Jaggi | 1fcbab6 | 2015-11-04 16:39:50 +0100 | [diff] [blame] | 35 | import android.view.VelocityTracker; |
| 36 | import android.view.View; |
| 37 | import android.view.View.OnTouchListener; |
Jorim Jaggi | d8fb3ac | 2016-01-05 15:37:42 +0100 | [diff] [blame] | 38 | import android.view.ViewConfiguration; |
Jorim Jaggi | 1fcbab6 | 2015-11-04 16:39:50 +0100 | [diff] [blame] | 39 | import android.view.ViewTreeObserver.InternalInsetsInfo; |
| 40 | import android.view.ViewTreeObserver.OnComputeInternalInsetsListener; |
Jorim Jaggi | 81fe2d1 | 2015-12-21 14:45:18 +0100 | [diff] [blame] | 41 | import android.view.WindowInsets; |
Jorim Jaggi | 1fcbab6 | 2015-11-04 16:39:50 +0100 | [diff] [blame] | 42 | import android.view.WindowManager; |
| 43 | import android.view.animation.AnimationUtils; |
| 44 | import android.view.animation.Interpolator; |
| 45 | import android.view.animation.PathInterpolator; |
| 46 | import android.widget.FrameLayout; |
| 47 | import android.widget.ImageButton; |
| 48 | |
Jorim Jaggi | 737af72 | 2015-12-31 10:42:27 +0100 | [diff] [blame] | 49 | import com.android.internal.policy.DividerSnapAlgorithm; |
| 50 | import com.android.internal.policy.DockedDividerUtils; |
Jorim Jaggi | 1fcbab6 | 2015-11-04 16:39:50 +0100 | [diff] [blame] | 51 | import com.android.systemui.R; |
Jorim Jaggi | 737af72 | 2015-12-31 10:42:27 +0100 | [diff] [blame] | 52 | import com.android.internal.policy.DividerSnapAlgorithm.SnapTarget; |
Jorim Jaggi | 1fcbab6 | 2015-11-04 16:39:50 +0100 | [diff] [blame] | 53 | import com.android.systemui.statusbar.FlingAnimationUtils; |
| 54 | |
Jorim Jaggi | e48f428 | 2015-11-06 17:32:44 +0100 | [diff] [blame] | 55 | import static android.view.PointerIcon.STYLE_HORIZONTAL_DOUBLE_ARROW; |
| 56 | import static android.view.PointerIcon.STYLE_VERTICAL_DOUBLE_ARROW; |
| 57 | |
Jorim Jaggi | 1fcbab6 | 2015-11-04 16:39:50 +0100 | [diff] [blame] | 58 | /** |
| 59 | * Docked stack divider. |
| 60 | */ |
| 61 | public class DividerView extends FrameLayout implements OnTouchListener, |
| 62 | OnComputeInternalInsetsListener { |
| 63 | |
Jorim Jaggi | 514b2cf | 2016-01-04 13:06:34 +0100 | [diff] [blame] | 64 | static final long TOUCH_ANIMATION_DURATION = 150; |
| 65 | static final long TOUCH_RELEASE_ANIMATION_DURATION = 200; |
| 66 | static final Interpolator TOUCH_RESPONSE_INTERPOLATOR = |
| 67 | new PathInterpolator(0.3f, 0f, 0.1f, 1f); |
| 68 | |
Jorim Jaggi | 1fcbab6 | 2015-11-04 16:39:50 +0100 | [diff] [blame] | 69 | private static final String TAG = "DividerView"; |
| 70 | |
Jorim Jaggi | dc249c4 | 2015-12-15 14:57:31 -0800 | [diff] [blame] | 71 | private static final int TASK_POSITION_SAME = Integer.MAX_VALUE; |
Jorim Jaggi | 5098159 | 2015-12-29 17:54:12 +0100 | [diff] [blame] | 72 | private static final float DIM_START_FRACTION = 0.5f; |
| 73 | private static final float DIM_DAMP_FACTOR = 1.7f; |
Jorim Jaggi | dc249c4 | 2015-12-15 14:57:31 -0800 | [diff] [blame] | 74 | |
Jorim Jaggi | e435e98 | 2015-12-30 13:54:32 +0100 | [diff] [blame] | 75 | private static final PathInterpolator SLOWDOWN_INTERPOLATOR = |
| 76 | new PathInterpolator(0.5f, 1f, 0.5f, 1f); |
| 77 | |
Jorim Jaggi | 514b2cf | 2016-01-04 13:06:34 +0100 | [diff] [blame] | 78 | private DividerHandleView mHandle; |
Jorim Jaggi | 1fcbab6 | 2015-11-04 16:39:50 +0100 | [diff] [blame] | 79 | private View mBackground; |
| 80 | private int mStartX; |
| 81 | private int mStartY; |
| 82 | private int mStartPosition; |
| 83 | private int mDockSide; |
| 84 | private final int[] mTempInt2 = new int[2]; |
Jorim Jaggi | d8fb3ac | 2016-01-05 15:37:42 +0100 | [diff] [blame] | 85 | private boolean mMoving; |
| 86 | private int mTouchSlop; |
Jorim Jaggi | 1fcbab6 | 2015-11-04 16:39:50 +0100 | [diff] [blame] | 87 | |
| 88 | private int mDividerInsets; |
| 89 | private int mDisplayWidth; |
| 90 | private int mDisplayHeight; |
| 91 | private int mDividerWindowWidth; |
| 92 | private int mDividerSize; |
| 93 | private int mTouchElevation; |
| 94 | |
Jorim Jaggi | dc249c4 | 2015-12-15 14:57:31 -0800 | [diff] [blame] | 95 | private final Rect mDockedRect = new Rect(); |
| 96 | private final Rect mDockedTaskRect = new Rect(); |
| 97 | private final Rect mOtherTaskRect = new Rect(); |
| 98 | private final Rect mOtherRect = new Rect(); |
| 99 | private final Rect mDockedInsetRect = new Rect(); |
| 100 | private final Rect mOtherInsetRect = new Rect(); |
Jorim Jaggi | 1fcbab6 | 2015-11-04 16:39:50 +0100 | [diff] [blame] | 101 | private final Rect mLastResizeRect = new Rect(); |
Jorim Jaggi | 870ab5a | 2015-12-02 18:37:54 -0800 | [diff] [blame] | 102 | private final WindowManagerProxy mWindowManagerProxy = WindowManagerProxy.getInstance(); |
Jorim Jaggi | 1fcbab6 | 2015-11-04 16:39:50 +0100 | [diff] [blame] | 103 | private Interpolator mFastOutSlowInInterpolator; |
Jorim Jaggi | 1fcbab6 | 2015-11-04 16:39:50 +0100 | [diff] [blame] | 104 | private DividerWindowManager mWindowManager; |
| 105 | private VelocityTracker mVelocityTracker; |
| 106 | private FlingAnimationUtils mFlingAnimationUtils; |
Jorim Jaggi | dc249c4 | 2015-12-15 14:57:31 -0800 | [diff] [blame] | 107 | private DividerSnapAlgorithm mSnapAlgorithm; |
Jorim Jaggi | 81fe2d1 | 2015-12-21 14:45:18 +0100 | [diff] [blame] | 108 | private final Rect mStableInsets = new Rect(); |
Jorim Jaggi | 1fcbab6 | 2015-11-04 16:39:50 +0100 | [diff] [blame] | 109 | |
| 110 | public DividerView(Context context) { |
| 111 | super(context); |
| 112 | } |
| 113 | |
| 114 | public DividerView(Context context, @Nullable AttributeSet attrs) { |
| 115 | super(context, attrs); |
| 116 | } |
| 117 | |
| 118 | public DividerView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) { |
| 119 | super(context, attrs, defStyleAttr); |
| 120 | } |
| 121 | |
| 122 | public DividerView(Context context, @Nullable AttributeSet attrs, int defStyleAttr, |
| 123 | int defStyleRes) { |
| 124 | super(context, attrs, defStyleAttr, defStyleRes); |
| 125 | } |
| 126 | |
| 127 | @Override |
| 128 | protected void onFinishInflate() { |
| 129 | super.onFinishInflate(); |
Jorim Jaggi | 514b2cf | 2016-01-04 13:06:34 +0100 | [diff] [blame] | 130 | mHandle = (DividerHandleView) findViewById(R.id.docked_divider_handle); |
Jorim Jaggi | 1fcbab6 | 2015-11-04 16:39:50 +0100 | [diff] [blame] | 131 | mBackground = findViewById(R.id.docked_divider_background); |
| 132 | mHandle.setOnTouchListener(this); |
| 133 | mDividerWindowWidth = getResources().getDimensionPixelSize( |
| 134 | com.android.internal.R.dimen.docked_stack_divider_thickness); |
| 135 | mDividerInsets = getResources().getDimensionPixelSize( |
| 136 | com.android.internal.R.dimen.docked_stack_divider_insets); |
| 137 | mDividerSize = mDividerWindowWidth - 2 * mDividerInsets; |
| 138 | mTouchElevation = getResources().getDimensionPixelSize( |
| 139 | R.dimen.docked_stack_divider_lift_elevation); |
| 140 | mFastOutSlowInInterpolator = AnimationUtils.loadInterpolator(getContext(), |
| 141 | android.R.interpolator.fast_out_slow_in); |
Jorim Jaggi | d8fb3ac | 2016-01-05 15:37:42 +0100 | [diff] [blame] | 142 | mTouchSlop = ViewConfiguration.get(mContext).getScaledTouchSlop(); |
Jorim Jaggi | 1fcbab6 | 2015-11-04 16:39:50 +0100 | [diff] [blame] | 143 | mFlingAnimationUtils = new FlingAnimationUtils(getContext(), 0.3f); |
| 144 | updateDisplayInfo(); |
Jorim Jaggi | e48f428 | 2015-11-06 17:32:44 +0100 | [diff] [blame] | 145 | boolean landscape = getResources().getConfiguration().orientation |
| 146 | == Configuration.ORIENTATION_LANDSCAPE; |
Jun Mukai | d4eaef7 | 2015-10-30 15:54:33 -0700 | [diff] [blame] | 147 | mHandle.setPointerIcon(PointerIcon.getSystemIcon(getContext(), |
| 148 | landscape ? STYLE_HORIZONTAL_DOUBLE_ARROW : STYLE_VERTICAL_DOUBLE_ARROW)); |
Jorim Jaggi | 1fcbab6 | 2015-11-04 16:39:50 +0100 | [diff] [blame] | 149 | getViewTreeObserver().addOnComputeInternalInsetsListener(this); |
| 150 | } |
| 151 | |
Jorim Jaggi | 81fe2d1 | 2015-12-21 14:45:18 +0100 | [diff] [blame] | 152 | @Override |
| 153 | public WindowInsets onApplyWindowInsets(WindowInsets insets) { |
| 154 | mStableInsets.set(insets.getStableInsetLeft(), insets.getStableInsetTop(), |
| 155 | insets.getStableInsetRight(), insets.getStableInsetBottom()); |
| 156 | return super.onApplyWindowInsets(insets); |
| 157 | } |
| 158 | |
Jorim Jaggi | 1fcbab6 | 2015-11-04 16:39:50 +0100 | [diff] [blame] | 159 | public void setWindowManager(DividerWindowManager windowManager) { |
| 160 | mWindowManager = windowManager; |
| 161 | } |
| 162 | |
Jorim Jaggi | dd98d41 | 2015-11-18 15:57:38 -0800 | [diff] [blame] | 163 | public WindowManagerProxy getWindowManagerProxy() { |
| 164 | return mWindowManagerProxy; |
| 165 | } |
| 166 | |
Jorim Jaggi | 514b2cf | 2016-01-04 13:06:34 +0100 | [diff] [blame] | 167 | public boolean startDragging(boolean animate) { |
| 168 | mHandle.setTouching(true, animate); |
Jorim Jaggi | dd98d41 | 2015-11-18 15:57:38 -0800 | [diff] [blame] | 169 | mDockSide = mWindowManagerProxy.getDockSide(); |
Jorim Jaggi | 737af72 | 2015-12-31 10:42:27 +0100 | [diff] [blame] | 170 | mSnapAlgorithm = new DividerSnapAlgorithm(getContext().getResources(), |
| 171 | mFlingAnimationUtils.getMinVelocityPxPerSecond(), mDisplayWidth, |
Jorim Jaggi | 81fe2d1 | 2015-12-21 14:45:18 +0100 | [diff] [blame] | 172 | mDisplayHeight, mDividerSize, isHorizontalDivision(), mStableInsets); |
Jorim Jaggi | dd98d41 | 2015-11-18 15:57:38 -0800 | [diff] [blame] | 173 | if (mDockSide != WindowManager.DOCKED_INVALID) { |
| 174 | mWindowManagerProxy.setResizing(true); |
| 175 | mWindowManager.setSlippery(false); |
| 176 | liftBackground(); |
| 177 | return true; |
| 178 | } else { |
| 179 | return false; |
| 180 | } |
| 181 | } |
| 182 | |
| 183 | public void stopDragging(int position, float velocity) { |
Jorim Jaggi | 514b2cf | 2016-01-04 13:06:34 +0100 | [diff] [blame] | 184 | mHandle.setTouching(false, true /* animate */); |
Jorim Jaggi | dd98d41 | 2015-11-18 15:57:38 -0800 | [diff] [blame] | 185 | fling(position, velocity); |
| 186 | mWindowManager.setSlippery(true); |
| 187 | releaseBackground(); |
| 188 | } |
| 189 | |
Jorim Jaggi | dc249c4 | 2015-12-15 14:57:31 -0800 | [diff] [blame] | 190 | public DividerSnapAlgorithm getSnapAlgorithm() { |
| 191 | return mSnapAlgorithm; |
| 192 | } |
| 193 | |
Jorim Jaggi | 1fcbab6 | 2015-11-04 16:39:50 +0100 | [diff] [blame] | 194 | @Override |
| 195 | public boolean onTouch(View v, MotionEvent event) { |
| 196 | convertToScreenCoordinates(event); |
| 197 | final int action = event.getAction() & MotionEvent.ACTION_MASK; |
| 198 | switch (action) { |
| 199 | case MotionEvent.ACTION_DOWN: |
| 200 | mVelocityTracker = VelocityTracker.obtain(); |
| 201 | mVelocityTracker.addMovement(event); |
| 202 | mStartX = (int) event.getX(); |
| 203 | mStartY = (int) event.getY(); |
| 204 | getLocationOnScreen(mTempInt2); |
Jorim Jaggi | 514b2cf | 2016-01-04 13:06:34 +0100 | [diff] [blame] | 205 | boolean result = startDragging(true /* animate */); |
Jorim Jaggi | 1fcbab6 | 2015-11-04 16:39:50 +0100 | [diff] [blame] | 206 | if (isHorizontalDivision()) { |
| 207 | mStartPosition = mTempInt2[1] + mDividerInsets; |
| 208 | } else { |
| 209 | mStartPosition = mTempInt2[0] + mDividerInsets; |
| 210 | } |
Jorim Jaggi | d8fb3ac | 2016-01-05 15:37:42 +0100 | [diff] [blame] | 211 | mMoving = false; |
Jorim Jaggi | dd98d41 | 2015-11-18 15:57:38 -0800 | [diff] [blame] | 212 | return result; |
Jorim Jaggi | 1fcbab6 | 2015-11-04 16:39:50 +0100 | [diff] [blame] | 213 | case MotionEvent.ACTION_MOVE: |
| 214 | mVelocityTracker.addMovement(event); |
| 215 | int x = (int) event.getX(); |
| 216 | int y = (int) event.getY(); |
Jorim Jaggi | d8fb3ac | 2016-01-05 15:37:42 +0100 | [diff] [blame] | 217 | boolean exceededTouchSlop = |
| 218 | isHorizontalDivision() && Math.abs(y - mStartY) > mTouchSlop |
| 219 | || (!isHorizontalDivision() && Math.abs(x - mStartX) > mTouchSlop); |
| 220 | if (!mMoving && exceededTouchSlop) { |
| 221 | mStartX = x; |
| 222 | mStartY = y; |
| 223 | mMoving = true; |
| 224 | } |
| 225 | if (mMoving && mDockSide != WindowManager.DOCKED_INVALID) { |
Jorim Jaggi | dc249c4 | 2015-12-15 14:57:31 -0800 | [diff] [blame] | 226 | int position = calculatePosition(x, y); |
| 227 | SnapTarget snapTarget = mSnapAlgorithm.calculateSnapTarget(position, |
| 228 | 0 /* velocity */); |
Jorim Jaggi | e435e98 | 2015-12-30 13:54:32 +0100 | [diff] [blame] | 229 | resizeStack(calculatePosition(x, y), snapTarget.position, snapTarget); |
Jorim Jaggi | 1fcbab6 | 2015-11-04 16:39:50 +0100 | [diff] [blame] | 230 | } |
| 231 | break; |
| 232 | case MotionEvent.ACTION_UP: |
| 233 | case MotionEvent.ACTION_CANCEL: |
| 234 | mVelocityTracker.addMovement(event); |
| 235 | |
| 236 | x = (int) event.getRawX(); |
| 237 | y = (int) event.getRawY(); |
| 238 | |
| 239 | mVelocityTracker.computeCurrentVelocity(1000); |
Jorim Jaggi | dd98d41 | 2015-11-18 15:57:38 -0800 | [diff] [blame] | 240 | int position = calculatePosition(x, y); |
| 241 | stopDragging(position, isHorizontalDivision() ? mVelocityTracker.getYVelocity() |
| 242 | : mVelocityTracker.getXVelocity()); |
Jorim Jaggi | d8fb3ac | 2016-01-05 15:37:42 +0100 | [diff] [blame] | 243 | mMoving = false; |
Jorim Jaggi | 1fcbab6 | 2015-11-04 16:39:50 +0100 | [diff] [blame] | 244 | break; |
| 245 | } |
| 246 | return true; |
| 247 | } |
| 248 | |
| 249 | private void convertToScreenCoordinates(MotionEvent event) { |
| 250 | event.setLocation(event.getRawX(), event.getRawY()); |
| 251 | } |
| 252 | |
Jorim Jaggi | dd98d41 | 2015-11-18 15:57:38 -0800 | [diff] [blame] | 253 | private void fling(int position, float velocity) { |
Jorim Jaggi | dc249c4 | 2015-12-15 14:57:31 -0800 | [diff] [blame] | 254 | final SnapTarget snapTarget = mSnapAlgorithm.calculateSnapTarget(position, velocity); |
Jorim Jaggi | 1fcbab6 | 2015-11-04 16:39:50 +0100 | [diff] [blame] | 255 | |
| 256 | ValueAnimator anim = ValueAnimator.ofInt(position, snapTarget.position); |
| 257 | anim.addUpdateListener(new AnimatorUpdateListener() { |
| 258 | @Override |
| 259 | public void onAnimationUpdate(ValueAnimator animation) { |
Jorim Jaggi | dc249c4 | 2015-12-15 14:57:31 -0800 | [diff] [blame] | 260 | resizeStack((Integer) animation.getAnimatedValue(), |
| 261 | animation.getAnimatedFraction() == 1f |
| 262 | ? TASK_POSITION_SAME |
Jorim Jaggi | e435e98 | 2015-12-30 13:54:32 +0100 | [diff] [blame] | 263 | : snapTarget.position, snapTarget); |
Jorim Jaggi | 1fcbab6 | 2015-11-04 16:39:50 +0100 | [diff] [blame] | 264 | } |
| 265 | }); |
| 266 | anim.addListener(new AnimatorListenerAdapter() { |
| 267 | @Override |
| 268 | public void onAnimationEnd(Animator animation) { |
| 269 | commitSnapFlags(snapTarget); |
| 270 | mWindowManagerProxy.setResizing(false); |
| 271 | mDockSide = WindowManager.DOCKED_INVALID; |
| 272 | } |
| 273 | }); |
| 274 | mFlingAnimationUtils.apply(anim, position, snapTarget.position, velocity); |
| 275 | anim.start(); |
| 276 | } |
| 277 | |
| 278 | private void commitSnapFlags(SnapTarget target) { |
| 279 | if (target.flag == SnapTarget.FLAG_NONE) { |
| 280 | return; |
| 281 | } |
| 282 | boolean dismissOrMaximize; |
| 283 | if (target.flag == SnapTarget.FLAG_DISMISS_START) { |
| 284 | dismissOrMaximize = mDockSide == WindowManager.DOCKED_LEFT |
| 285 | || mDockSide == WindowManager.DOCKED_TOP; |
| 286 | } else { |
| 287 | dismissOrMaximize = mDockSide == WindowManager.DOCKED_RIGHT |
| 288 | || mDockSide == WindowManager.DOCKED_BOTTOM; |
| 289 | } |
| 290 | if (dismissOrMaximize) { |
| 291 | mWindowManagerProxy.dismissDockedStack(); |
| 292 | } else { |
| 293 | mWindowManagerProxy.maximizeDockedStack(); |
| 294 | } |
Jorim Jaggi | 5098159 | 2015-12-29 17:54:12 +0100 | [diff] [blame] | 295 | mWindowManagerProxy.setResizeDimLayer(false, -1, 0f); |
Jorim Jaggi | 1fcbab6 | 2015-11-04 16:39:50 +0100 | [diff] [blame] | 296 | } |
| 297 | |
| 298 | private void liftBackground() { |
| 299 | if (isHorizontalDivision()) { |
Jorim Jaggi | 79b39f0 | 2015-12-17 20:04:31 -0800 | [diff] [blame] | 300 | mBackground.animate().scaleY(1.4f); |
Jorim Jaggi | 1fcbab6 | 2015-11-04 16:39:50 +0100 | [diff] [blame] | 301 | } else { |
Jorim Jaggi | 79b39f0 | 2015-12-17 20:04:31 -0800 | [diff] [blame] | 302 | mBackground.animate().scaleX(1.4f); |
Jorim Jaggi | 1fcbab6 | 2015-11-04 16:39:50 +0100 | [diff] [blame] | 303 | } |
| 304 | mBackground.animate() |
Jorim Jaggi | 514b2cf | 2016-01-04 13:06:34 +0100 | [diff] [blame] | 305 | .setInterpolator(TOUCH_RESPONSE_INTERPOLATOR) |
| 306 | .setDuration(TOUCH_ANIMATION_DURATION) |
| 307 | .translationZ(mTouchElevation) |
| 308 | .start(); |
Jorim Jaggi | 1fcbab6 | 2015-11-04 16:39:50 +0100 | [diff] [blame] | 309 | |
| 310 | // Lift handle as well so it doesn't get behind the background, even though it doesn't |
| 311 | // cast shadow. |
| 312 | mHandle.animate() |
Jorim Jaggi | 514b2cf | 2016-01-04 13:06:34 +0100 | [diff] [blame] | 313 | .setInterpolator(TOUCH_RESPONSE_INTERPOLATOR) |
| 314 | .setDuration(TOUCH_ANIMATION_DURATION) |
| 315 | .translationZ(mTouchElevation) |
| 316 | .start(); |
Jorim Jaggi | 1fcbab6 | 2015-11-04 16:39:50 +0100 | [diff] [blame] | 317 | } |
| 318 | |
| 319 | private void releaseBackground() { |
| 320 | mBackground.animate() |
| 321 | .setInterpolator(mFastOutSlowInInterpolator) |
Jorim Jaggi | 514b2cf | 2016-01-04 13:06:34 +0100 | [diff] [blame] | 322 | .setDuration(TOUCH_RELEASE_ANIMATION_DURATION) |
Jorim Jaggi | 1fcbab6 | 2015-11-04 16:39:50 +0100 | [diff] [blame] | 323 | .translationZ(0) |
| 324 | .scaleX(1f) |
Jorim Jaggi | 514b2cf | 2016-01-04 13:06:34 +0100 | [diff] [blame] | 325 | .scaleY(1f) |
| 326 | .start(); |
Jorim Jaggi | 1fcbab6 | 2015-11-04 16:39:50 +0100 | [diff] [blame] | 327 | mHandle.animate() |
| 328 | .setInterpolator(mFastOutSlowInInterpolator) |
Jorim Jaggi | 514b2cf | 2016-01-04 13:06:34 +0100 | [diff] [blame] | 329 | .setDuration(TOUCH_RELEASE_ANIMATION_DURATION) |
| 330 | .translationZ(0) |
| 331 | .start(); |
Jorim Jaggi | 1fcbab6 | 2015-11-04 16:39:50 +0100 | [diff] [blame] | 332 | } |
| 333 | |
| 334 | @Override |
| 335 | protected void onConfigurationChanged(Configuration newConfig) { |
| 336 | super.onConfigurationChanged(newConfig); |
| 337 | updateDisplayInfo(); |
| 338 | } |
| 339 | |
| 340 | private void updateDisplayInfo() { |
Filip Gruszczynski | f6d8e9e | 2015-11-12 13:39:20 -0800 | [diff] [blame] | 341 | final DisplayManager displayManager = |
| 342 | (DisplayManager) mContext.getSystemService(Context.DISPLAY_SERVICE); |
| 343 | Display display = displayManager.getDisplay(Display.DEFAULT_DISPLAY); |
| 344 | final DisplayInfo info = new DisplayInfo(); |
| 345 | display.getDisplayInfo(info); |
| 346 | mDisplayWidth = info.logicalWidth; |
| 347 | mDisplayHeight = info.logicalHeight; |
Jorim Jaggi | 1fcbab6 | 2015-11-04 16:39:50 +0100 | [diff] [blame] | 348 | } |
| 349 | |
| 350 | private int calculatePosition(int touchX, int touchY) { |
| 351 | return isHorizontalDivision() ? calculateYPosition(touchY) : calculateXPosition(touchX); |
| 352 | } |
| 353 | |
Jorim Jaggi | dd98d41 | 2015-11-18 15:57:38 -0800 | [diff] [blame] | 354 | public boolean isHorizontalDivision() { |
| 355 | return getResources().getConfiguration().orientation == Configuration.ORIENTATION_PORTRAIT; |
Jorim Jaggi | 1fcbab6 | 2015-11-04 16:39:50 +0100 | [diff] [blame] | 356 | } |
| 357 | |
| 358 | private int calculateXPosition(int touchX) { |
| 359 | return mStartPosition + touchX - mStartX; |
| 360 | } |
| 361 | |
| 362 | private int calculateYPosition(int touchY) { |
| 363 | return mStartPosition + touchY - mStartY; |
| 364 | } |
| 365 | |
Jorim Jaggi | dc249c4 | 2015-12-15 14:57:31 -0800 | [diff] [blame] | 366 | private int invertDockSide(int dockSide) { |
| 367 | switch (dockSide) { |
| 368 | case WindowManager.DOCKED_LEFT: |
| 369 | return WindowManager.DOCKED_RIGHT; |
| 370 | case WindowManager.DOCKED_TOP: |
| 371 | return WindowManager.DOCKED_BOTTOM; |
| 372 | case WindowManager.DOCKED_RIGHT: |
| 373 | return WindowManager.DOCKED_LEFT; |
| 374 | case WindowManager.DOCKED_BOTTOM: |
| 375 | return WindowManager.DOCKED_TOP; |
| 376 | default: |
| 377 | return WindowManager.DOCKED_INVALID; |
| 378 | } |
| 379 | } |
| 380 | |
| 381 | private void alignTopLeft(Rect containingRect, Rect rect) { |
| 382 | int width = rect.width(); |
| 383 | int height = rect.height(); |
| 384 | rect.set(containingRect.left, containingRect.top, |
| 385 | containingRect.left + width, containingRect.top + height); |
| 386 | } |
| 387 | |
| 388 | private void alignBottomRight(Rect containingRect, Rect rect) { |
| 389 | int width = rect.width(); |
| 390 | int height = rect.height(); |
| 391 | rect.set(containingRect.right - width, containingRect.bottom - height, |
| 392 | containingRect.right, containingRect.bottom); |
| 393 | } |
| 394 | |
Jorim Jaggi | 737af72 | 2015-12-31 10:42:27 +0100 | [diff] [blame] | 395 | public void calculateBoundsForPosition(int position, int dockSide, Rect outRect) { |
| 396 | DockedDividerUtils.calculateBoundsForPosition(position, dockSide, outRect, mDisplayWidth, |
| 397 | mDisplayHeight, mDividerSize); |
| 398 | } |
| 399 | |
Jorim Jaggi | e435e98 | 2015-12-30 13:54:32 +0100 | [diff] [blame] | 400 | public void resizeStack(int position, int taskPosition, SnapTarget taskSnapTarget) { |
Jorim Jaggi | dc249c4 | 2015-12-15 14:57:31 -0800 | [diff] [blame] | 401 | calculateBoundsForPosition(position, mDockSide, mDockedRect); |
| 402 | |
| 403 | if (mDockedRect.equals(mLastResizeRect)) { |
Jorim Jaggi | 1fcbab6 | 2015-11-04 16:39:50 +0100 | [diff] [blame] | 404 | return; |
| 405 | } |
Jorim Jaggi | c97ba49 | 2015-11-06 22:00:18 +0100 | [diff] [blame] | 406 | |
| 407 | // Make sure shadows are updated |
| 408 | mBackground.invalidate(); |
| 409 | |
Jorim Jaggi | dc249c4 | 2015-12-15 14:57:31 -0800 | [diff] [blame] | 410 | mLastResizeRect.set(mDockedRect); |
| 411 | if (taskPosition != TASK_POSITION_SAME) { |
| 412 | calculateBoundsForPosition(position, invertDockSide(mDockSide), mOtherRect); |
Jorim Jaggi | e435e98 | 2015-12-30 13:54:32 +0100 | [diff] [blame] | 413 | int dockSideInverted = invertDockSide(mDockSide); |
| 414 | int taskPositionDocked = |
| 415 | restrictDismissingTaskPosition(taskPosition, mDockSide, taskSnapTarget); |
| 416 | int taskPositionOther = |
| 417 | restrictDismissingTaskPosition(taskPosition, dockSideInverted, taskSnapTarget); |
| 418 | calculateBoundsForPosition(taskPositionDocked, mDockSide, mDockedTaskRect); |
| 419 | calculateBoundsForPosition(taskPositionOther, dockSideInverted, mOtherTaskRect); |
Jorim Jaggi | dc249c4 | 2015-12-15 14:57:31 -0800 | [diff] [blame] | 420 | alignTopLeft(mDockedRect, mDockedTaskRect); |
| 421 | alignTopLeft(mOtherRect, mOtherTaskRect); |
| 422 | mDockedInsetRect.set(mDockedTaskRect); |
| 423 | mOtherInsetRect.set(mOtherTaskRect); |
Jorim Jaggi | e435e98 | 2015-12-30 13:54:32 +0100 | [diff] [blame] | 424 | if (dockSideTopLeft(mDockSide)) { |
Jorim Jaggi | dc249c4 | 2015-12-15 14:57:31 -0800 | [diff] [blame] | 425 | alignTopLeft(mDockedRect, mDockedInsetRect); |
| 426 | alignBottomRight(mOtherRect, mOtherInsetRect); |
| 427 | } else { |
| 428 | alignBottomRight(mDockedRect, mDockedInsetRect); |
| 429 | alignTopLeft(mOtherRect, mOtherInsetRect); |
| 430 | } |
Jorim Jaggi | e435e98 | 2015-12-30 13:54:32 +0100 | [diff] [blame] | 431 | applyDismissingParallax(mDockedTaskRect, mDockSide, taskSnapTarget, position, |
| 432 | taskPositionDocked); |
| 433 | applyDismissingParallax(mOtherTaskRect, dockSideInverted, taskSnapTarget, position, |
| 434 | taskPositionOther); |
Jorim Jaggi | dc249c4 | 2015-12-15 14:57:31 -0800 | [diff] [blame] | 435 | mWindowManagerProxy.resizeDockedStack(mDockedRect, mDockedTaskRect, mDockedInsetRect, |
| 436 | mOtherTaskRect, mOtherInsetRect); |
| 437 | } else { |
| 438 | mWindowManagerProxy.resizeDockedStack(mDockedRect, null, null, null, null); |
| 439 | } |
Jorim Jaggi | 5098159 | 2015-12-29 17:54:12 +0100 | [diff] [blame] | 440 | float fraction = mSnapAlgorithm.calculateDismissingFraction(position); |
| 441 | fraction = Math.max(0, |
| 442 | Math.min((fraction / DIM_START_FRACTION - 1f) / DIM_DAMP_FACTOR, 1f)); |
| 443 | mWindowManagerProxy.setResizeDimLayer(fraction != 0f, |
| 444 | getStackIdForDismissTarget(mSnapAlgorithm.getClosestDismissTarget(position)), |
| 445 | fraction); |
| 446 | } |
| 447 | |
Jorim Jaggi | e435e98 | 2015-12-30 13:54:32 +0100 | [diff] [blame] | 448 | /** |
| 449 | * When the snap target is dismissing one side, make sure that the dismissing side doesn't get |
| 450 | * 0 size. |
| 451 | */ |
| 452 | private int restrictDismissingTaskPosition(int taskPosition, int dockSide, |
| 453 | SnapTarget snapTarget) { |
| 454 | if (snapTarget.flag == SnapTarget.FLAG_DISMISS_START && dockSideTopLeft(dockSide)) { |
| 455 | return mSnapAlgorithm.getFirstSplitTarget().position; |
| 456 | } else if (snapTarget.flag == SnapTarget.FLAG_DISMISS_END |
| 457 | && dockSideBottomRight(dockSide)) { |
| 458 | return mSnapAlgorithm.getLastSplitTarget().position; |
| 459 | } else { |
| 460 | return taskPosition; |
| 461 | } |
| 462 | } |
| 463 | |
| 464 | /** |
| 465 | * Applies a parallax to the task when dismissing. |
| 466 | */ |
| 467 | private void applyDismissingParallax(Rect taskRect, int dockSide, SnapTarget snapTarget, |
| 468 | int position, int taskPosition) { |
| 469 | float fraction = Math.min(1, Math.max(0, |
| 470 | mSnapAlgorithm.calculateDismissingFraction(position))); |
| 471 | SnapTarget dismissTarget = null; |
| 472 | SnapTarget splitTarget = null; |
| 473 | if ((snapTarget.flag == SnapTarget.FLAG_DISMISS_START |
| 474 | || snapTarget == mSnapAlgorithm.getFirstSplitTarget()) |
| 475 | && dockSideTopLeft(dockSide)) { |
| 476 | dismissTarget = mSnapAlgorithm.getDismissStartTarget(); |
| 477 | splitTarget = mSnapAlgorithm.getFirstSplitTarget(); |
| 478 | } else if ((snapTarget.flag == SnapTarget.FLAG_DISMISS_END |
| 479 | || snapTarget == mSnapAlgorithm.getLastSplitTarget()) |
| 480 | && dockSideBottomRight(dockSide)) { |
| 481 | dismissTarget = mSnapAlgorithm.getDismissEndTarget(); |
| 482 | splitTarget = mSnapAlgorithm.getLastSplitTarget(); |
| 483 | } |
| 484 | if (dismissTarget != null && fraction > 0f |
| 485 | && isDismissing(splitTarget, position, dockSide)) { |
| 486 | fraction = calculateParallaxDismissingFraction(fraction); |
| 487 | int offsetPosition = (int) (taskPosition + |
| 488 | fraction * (dismissTarget.position - splitTarget.position)); |
| 489 | int width = taskRect.width(); |
| 490 | int height = taskRect.height(); |
| 491 | switch (dockSide) { |
| 492 | case WindowManager.DOCKED_LEFT: |
| 493 | taskRect.left = offsetPosition - width; |
| 494 | taskRect.right = offsetPosition; |
| 495 | break; |
| 496 | case WindowManager.DOCKED_RIGHT: |
| 497 | taskRect.left = offsetPosition + mDividerSize; |
| 498 | taskRect.right = offsetPosition + width + mDividerSize; |
| 499 | break; |
| 500 | case WindowManager.DOCKED_TOP: |
| 501 | taskRect.top = offsetPosition - height; |
| 502 | taskRect.bottom = offsetPosition; |
| 503 | break; |
| 504 | case WindowManager.DOCKED_BOTTOM: |
| 505 | taskRect.top = offsetPosition + mDividerSize; |
| 506 | taskRect.bottom = offsetPosition + height + mDividerSize; |
| 507 | break; |
| 508 | } |
| 509 | } |
| 510 | } |
| 511 | |
| 512 | /** |
| 513 | * @return for a specified {@code fraction}, this returns an adjusted value that simulates a |
| 514 | * slowing down parallax effect |
| 515 | */ |
| 516 | private static float calculateParallaxDismissingFraction(float fraction) { |
| 517 | return SLOWDOWN_INTERPOLATOR.getInterpolation(fraction) / 3.5f; |
| 518 | } |
| 519 | |
| 520 | private static boolean isDismissing(SnapTarget snapTarget, int position, int dockSide) { |
| 521 | if (dockSide == WindowManager.DOCKED_TOP || dockSide == WindowManager.DOCKED_LEFT) { |
| 522 | return position < snapTarget.position; |
| 523 | } else { |
| 524 | return position > snapTarget.position; |
| 525 | } |
| 526 | } |
| 527 | |
Jorim Jaggi | 5098159 | 2015-12-29 17:54:12 +0100 | [diff] [blame] | 528 | private int getStackIdForDismissTarget(SnapTarget dismissTarget) { |
| 529 | if (dismissTarget.flag == SnapTarget.FLAG_DISMISS_START && |
| 530 | (mDockSide == WindowManager.DOCKED_LEFT || mDockSide == WindowManager.DOCKED_TOP)) { |
| 531 | return StackId.DOCKED_STACK_ID; |
| 532 | } else { |
| 533 | return StackId.FULLSCREEN_WORKSPACE_STACK_ID; |
| 534 | } |
Jorim Jaggi | 1fcbab6 | 2015-11-04 16:39:50 +0100 | [diff] [blame] | 535 | } |
| 536 | |
Jorim Jaggi | e435e98 | 2015-12-30 13:54:32 +0100 | [diff] [blame] | 537 | /** |
| 538 | * @return true if and only if {@code dockSide} is top or left |
| 539 | */ |
| 540 | private static boolean dockSideTopLeft(int dockSide) { |
| 541 | return dockSide == WindowManager.DOCKED_TOP || dockSide == WindowManager.DOCKED_LEFT; |
| 542 | } |
| 543 | |
| 544 | /** |
| 545 | * @return true if and only if {@code dockSide} is bottom or right |
| 546 | */ |
| 547 | private static boolean dockSideBottomRight(int dockSide) { |
| 548 | return dockSide == WindowManager.DOCKED_BOTTOM || dockSide == WindowManager.DOCKED_RIGHT; |
| 549 | } |
| 550 | |
Jorim Jaggi | 1fcbab6 | 2015-11-04 16:39:50 +0100 | [diff] [blame] | 551 | @Override |
| 552 | public void onComputeInternalInsets(InternalInsetsInfo inoutInfo) { |
| 553 | inoutInfo.setTouchableInsets(InternalInsetsInfo.TOUCHABLE_INSETS_REGION); |
| 554 | inoutInfo.touchableRegion.set(mHandle.getLeft(), mHandle.getTop(), mHandle.getRight(), |
| 555 | mHandle.getBottom()); |
| 556 | inoutInfo.touchableRegion.op(mBackground.getLeft(), mBackground.getTop(), |
| 557 | mBackground.getRight(), mBackground.getBottom(), Op.UNION); |
| 558 | } |
| 559 | } |