blob: 824d10a3aa19b1ab8e51d7644fef22d1f576367f [file] [log] [blame]
Jorim Jaggi1fcbab62015-11-04 16:39:50 +01001/*
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
17package com.android.systemui.stackdivider;
18
19import android.animation.Animator;
20import android.animation.AnimatorListenerAdapter;
21import android.animation.ValueAnimator;
22import android.animation.ValueAnimator.AnimatorUpdateListener;
23import android.annotation.Nullable;
Jorim Jaggi50981592015-12-29 17:54:12 +010024import android.app.ActivityManager.StackId;
Jorim Jaggi1fcbab62015-11-04 16:39:50 +010025import android.content.Context;
26import android.content.res.Configuration;
27import android.graphics.Rect;
28import android.graphics.Region.Op;
Filip Gruszczynskif6d8e9e2015-11-12 13:39:20 -080029import android.hardware.display.DisplayManager;
Jorim Jaggi1fcbab62015-11-04 16:39:50 +010030import android.util.AttributeSet;
Filip Gruszczynskif6d8e9e2015-11-12 13:39:20 -080031import android.view.Display;
32import android.view.DisplayInfo;
Jorim Jaggi1fcbab62015-11-04 16:39:50 +010033import android.view.MotionEvent;
Jun Mukaid4eaef72015-10-30 15:54:33 -070034import android.view.PointerIcon;
Jorim Jaggi1fcbab62015-11-04 16:39:50 +010035import android.view.VelocityTracker;
36import android.view.View;
37import android.view.View.OnTouchListener;
Jorim Jaggid8fb3ac2016-01-05 15:37:42 +010038import android.view.ViewConfiguration;
Jorim Jaggi1fcbab62015-11-04 16:39:50 +010039import android.view.ViewTreeObserver.InternalInsetsInfo;
40import android.view.ViewTreeObserver.OnComputeInternalInsetsListener;
Jorim Jaggi81fe2d12015-12-21 14:45:18 +010041import android.view.WindowInsets;
Jorim Jaggi1fcbab62015-11-04 16:39:50 +010042import android.view.WindowManager;
43import android.view.animation.AnimationUtils;
44import android.view.animation.Interpolator;
45import android.view.animation.PathInterpolator;
46import android.widget.FrameLayout;
47import android.widget.ImageButton;
48
Jorim Jaggi737af722015-12-31 10:42:27 +010049import com.android.internal.policy.DividerSnapAlgorithm;
50import com.android.internal.policy.DockedDividerUtils;
Jorim Jaggi1fcbab62015-11-04 16:39:50 +010051import com.android.systemui.R;
Jorim Jaggi737af722015-12-31 10:42:27 +010052import com.android.internal.policy.DividerSnapAlgorithm.SnapTarget;
Jorim Jaggi1fcbab62015-11-04 16:39:50 +010053import com.android.systemui.statusbar.FlingAnimationUtils;
54
Jorim Jaggie48f4282015-11-06 17:32:44 +010055import static android.view.PointerIcon.STYLE_HORIZONTAL_DOUBLE_ARROW;
56import static android.view.PointerIcon.STYLE_VERTICAL_DOUBLE_ARROW;
57
Jorim Jaggi1fcbab62015-11-04 16:39:50 +010058/**
59 * Docked stack divider.
60 */
61public class DividerView extends FrameLayout implements OnTouchListener,
62 OnComputeInternalInsetsListener {
63
Jorim Jaggi514b2cf2016-01-04 13:06:34 +010064 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 Jaggi1fcbab62015-11-04 16:39:50 +010069 private static final String TAG = "DividerView";
70
Jorim Jaggidc249c42015-12-15 14:57:31 -080071 private static final int TASK_POSITION_SAME = Integer.MAX_VALUE;
Jorim Jaggi50981592015-12-29 17:54:12 +010072 private static final float DIM_START_FRACTION = 0.5f;
73 private static final float DIM_DAMP_FACTOR = 1.7f;
Jorim Jaggidc249c42015-12-15 14:57:31 -080074
Jorim Jaggie435e982015-12-30 13:54:32 +010075 private static final PathInterpolator SLOWDOWN_INTERPOLATOR =
76 new PathInterpolator(0.5f, 1f, 0.5f, 1f);
77
Jorim Jaggi514b2cf2016-01-04 13:06:34 +010078 private DividerHandleView mHandle;
Jorim Jaggi1fcbab62015-11-04 16:39:50 +010079 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 Jaggid8fb3ac2016-01-05 15:37:42 +010085 private boolean mMoving;
86 private int mTouchSlop;
Jorim Jaggi1fcbab62015-11-04 16:39:50 +010087
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 Jaggidc249c42015-12-15 14:57:31 -080095 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 Jaggi1fcbab62015-11-04 16:39:50 +0100101 private final Rect mLastResizeRect = new Rect();
Jorim Jaggi870ab5a2015-12-02 18:37:54 -0800102 private final WindowManagerProxy mWindowManagerProxy = WindowManagerProxy.getInstance();
Jorim Jaggi1fcbab62015-11-04 16:39:50 +0100103 private Interpolator mFastOutSlowInInterpolator;
Jorim Jaggi1fcbab62015-11-04 16:39:50 +0100104 private DividerWindowManager mWindowManager;
105 private VelocityTracker mVelocityTracker;
106 private FlingAnimationUtils mFlingAnimationUtils;
Jorim Jaggidc249c42015-12-15 14:57:31 -0800107 private DividerSnapAlgorithm mSnapAlgorithm;
Jorim Jaggi81fe2d12015-12-21 14:45:18 +0100108 private final Rect mStableInsets = new Rect();
Jorim Jaggi1fcbab62015-11-04 16:39:50 +0100109
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 Jaggi514b2cf2016-01-04 13:06:34 +0100130 mHandle = (DividerHandleView) findViewById(R.id.docked_divider_handle);
Jorim Jaggi1fcbab62015-11-04 16:39:50 +0100131 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 Jaggid8fb3ac2016-01-05 15:37:42 +0100142 mTouchSlop = ViewConfiguration.get(mContext).getScaledTouchSlop();
Jorim Jaggi1fcbab62015-11-04 16:39:50 +0100143 mFlingAnimationUtils = new FlingAnimationUtils(getContext(), 0.3f);
144 updateDisplayInfo();
Jorim Jaggie48f4282015-11-06 17:32:44 +0100145 boolean landscape = getResources().getConfiguration().orientation
146 == Configuration.ORIENTATION_LANDSCAPE;
Jun Mukaid4eaef72015-10-30 15:54:33 -0700147 mHandle.setPointerIcon(PointerIcon.getSystemIcon(getContext(),
148 landscape ? STYLE_HORIZONTAL_DOUBLE_ARROW : STYLE_VERTICAL_DOUBLE_ARROW));
Jorim Jaggi1fcbab62015-11-04 16:39:50 +0100149 getViewTreeObserver().addOnComputeInternalInsetsListener(this);
150 }
151
Jorim Jaggi81fe2d12015-12-21 14:45:18 +0100152 @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 Jaggi1fcbab62015-11-04 16:39:50 +0100159 public void setWindowManager(DividerWindowManager windowManager) {
160 mWindowManager = windowManager;
161 }
162
Jorim Jaggidd98d412015-11-18 15:57:38 -0800163 public WindowManagerProxy getWindowManagerProxy() {
164 return mWindowManagerProxy;
165 }
166
Jorim Jaggi514b2cf2016-01-04 13:06:34 +0100167 public boolean startDragging(boolean animate) {
168 mHandle.setTouching(true, animate);
Jorim Jaggidd98d412015-11-18 15:57:38 -0800169 mDockSide = mWindowManagerProxy.getDockSide();
Jorim Jaggi737af722015-12-31 10:42:27 +0100170 mSnapAlgorithm = new DividerSnapAlgorithm(getContext().getResources(),
171 mFlingAnimationUtils.getMinVelocityPxPerSecond(), mDisplayWidth,
Jorim Jaggi81fe2d12015-12-21 14:45:18 +0100172 mDisplayHeight, mDividerSize, isHorizontalDivision(), mStableInsets);
Jorim Jaggidd98d412015-11-18 15:57:38 -0800173 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 Jaggi514b2cf2016-01-04 13:06:34 +0100184 mHandle.setTouching(false, true /* animate */);
Jorim Jaggidd98d412015-11-18 15:57:38 -0800185 fling(position, velocity);
186 mWindowManager.setSlippery(true);
187 releaseBackground();
188 }
189
Jorim Jaggidc249c42015-12-15 14:57:31 -0800190 public DividerSnapAlgorithm getSnapAlgorithm() {
191 return mSnapAlgorithm;
192 }
193
Jorim Jaggi1fcbab62015-11-04 16:39:50 +0100194 @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 Jaggi514b2cf2016-01-04 13:06:34 +0100205 boolean result = startDragging(true /* animate */);
Jorim Jaggi1fcbab62015-11-04 16:39:50 +0100206 if (isHorizontalDivision()) {
207 mStartPosition = mTempInt2[1] + mDividerInsets;
208 } else {
209 mStartPosition = mTempInt2[0] + mDividerInsets;
210 }
Jorim Jaggid8fb3ac2016-01-05 15:37:42 +0100211 mMoving = false;
Jorim Jaggidd98d412015-11-18 15:57:38 -0800212 return result;
Jorim Jaggi1fcbab62015-11-04 16:39:50 +0100213 case MotionEvent.ACTION_MOVE:
214 mVelocityTracker.addMovement(event);
215 int x = (int) event.getX();
216 int y = (int) event.getY();
Jorim Jaggid8fb3ac2016-01-05 15:37:42 +0100217 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 Jaggidc249c42015-12-15 14:57:31 -0800226 int position = calculatePosition(x, y);
227 SnapTarget snapTarget = mSnapAlgorithm.calculateSnapTarget(position,
228 0 /* velocity */);
Jorim Jaggie435e982015-12-30 13:54:32 +0100229 resizeStack(calculatePosition(x, y), snapTarget.position, snapTarget);
Jorim Jaggi1fcbab62015-11-04 16:39:50 +0100230 }
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 Jaggidd98d412015-11-18 15:57:38 -0800240 int position = calculatePosition(x, y);
241 stopDragging(position, isHorizontalDivision() ? mVelocityTracker.getYVelocity()
242 : mVelocityTracker.getXVelocity());
Jorim Jaggid8fb3ac2016-01-05 15:37:42 +0100243 mMoving = false;
Jorim Jaggi1fcbab62015-11-04 16:39:50 +0100244 break;
245 }
246 return true;
247 }
248
249 private void convertToScreenCoordinates(MotionEvent event) {
250 event.setLocation(event.getRawX(), event.getRawY());
251 }
252
Jorim Jaggidd98d412015-11-18 15:57:38 -0800253 private void fling(int position, float velocity) {
Jorim Jaggidc249c42015-12-15 14:57:31 -0800254 final SnapTarget snapTarget = mSnapAlgorithm.calculateSnapTarget(position, velocity);
Jorim Jaggi1fcbab62015-11-04 16:39:50 +0100255
256 ValueAnimator anim = ValueAnimator.ofInt(position, snapTarget.position);
257 anim.addUpdateListener(new AnimatorUpdateListener() {
258 @Override
259 public void onAnimationUpdate(ValueAnimator animation) {
Jorim Jaggidc249c42015-12-15 14:57:31 -0800260 resizeStack((Integer) animation.getAnimatedValue(),
261 animation.getAnimatedFraction() == 1f
262 ? TASK_POSITION_SAME
Jorim Jaggie435e982015-12-30 13:54:32 +0100263 : snapTarget.position, snapTarget);
Jorim Jaggi1fcbab62015-11-04 16:39:50 +0100264 }
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 Jaggi50981592015-12-29 17:54:12 +0100295 mWindowManagerProxy.setResizeDimLayer(false, -1, 0f);
Jorim Jaggi1fcbab62015-11-04 16:39:50 +0100296 }
297
298 private void liftBackground() {
299 if (isHorizontalDivision()) {
Jorim Jaggi79b39f02015-12-17 20:04:31 -0800300 mBackground.animate().scaleY(1.4f);
Jorim Jaggi1fcbab62015-11-04 16:39:50 +0100301 } else {
Jorim Jaggi79b39f02015-12-17 20:04:31 -0800302 mBackground.animate().scaleX(1.4f);
Jorim Jaggi1fcbab62015-11-04 16:39:50 +0100303 }
304 mBackground.animate()
Jorim Jaggi514b2cf2016-01-04 13:06:34 +0100305 .setInterpolator(TOUCH_RESPONSE_INTERPOLATOR)
306 .setDuration(TOUCH_ANIMATION_DURATION)
307 .translationZ(mTouchElevation)
308 .start();
Jorim Jaggi1fcbab62015-11-04 16:39:50 +0100309
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 Jaggi514b2cf2016-01-04 13:06:34 +0100313 .setInterpolator(TOUCH_RESPONSE_INTERPOLATOR)
314 .setDuration(TOUCH_ANIMATION_DURATION)
315 .translationZ(mTouchElevation)
316 .start();
Jorim Jaggi1fcbab62015-11-04 16:39:50 +0100317 }
318
319 private void releaseBackground() {
320 mBackground.animate()
321 .setInterpolator(mFastOutSlowInInterpolator)
Jorim Jaggi514b2cf2016-01-04 13:06:34 +0100322 .setDuration(TOUCH_RELEASE_ANIMATION_DURATION)
Jorim Jaggi1fcbab62015-11-04 16:39:50 +0100323 .translationZ(0)
324 .scaleX(1f)
Jorim Jaggi514b2cf2016-01-04 13:06:34 +0100325 .scaleY(1f)
326 .start();
Jorim Jaggi1fcbab62015-11-04 16:39:50 +0100327 mHandle.animate()
328 .setInterpolator(mFastOutSlowInInterpolator)
Jorim Jaggi514b2cf2016-01-04 13:06:34 +0100329 .setDuration(TOUCH_RELEASE_ANIMATION_DURATION)
330 .translationZ(0)
331 .start();
Jorim Jaggi1fcbab62015-11-04 16:39:50 +0100332 }
333
334 @Override
335 protected void onConfigurationChanged(Configuration newConfig) {
336 super.onConfigurationChanged(newConfig);
337 updateDisplayInfo();
338 }
339
340 private void updateDisplayInfo() {
Filip Gruszczynskif6d8e9e2015-11-12 13:39:20 -0800341 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 Jaggi1fcbab62015-11-04 16:39:50 +0100348 }
349
350 private int calculatePosition(int touchX, int touchY) {
351 return isHorizontalDivision() ? calculateYPosition(touchY) : calculateXPosition(touchX);
352 }
353
Jorim Jaggidd98d412015-11-18 15:57:38 -0800354 public boolean isHorizontalDivision() {
355 return getResources().getConfiguration().orientation == Configuration.ORIENTATION_PORTRAIT;
Jorim Jaggi1fcbab62015-11-04 16:39:50 +0100356 }
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 Jaggidc249c42015-12-15 14:57:31 -0800366 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 Jaggi737af722015-12-31 10:42:27 +0100395 public void calculateBoundsForPosition(int position, int dockSide, Rect outRect) {
396 DockedDividerUtils.calculateBoundsForPosition(position, dockSide, outRect, mDisplayWidth,
397 mDisplayHeight, mDividerSize);
398 }
399
Jorim Jaggie435e982015-12-30 13:54:32 +0100400 public void resizeStack(int position, int taskPosition, SnapTarget taskSnapTarget) {
Jorim Jaggidc249c42015-12-15 14:57:31 -0800401 calculateBoundsForPosition(position, mDockSide, mDockedRect);
402
403 if (mDockedRect.equals(mLastResizeRect)) {
Jorim Jaggi1fcbab62015-11-04 16:39:50 +0100404 return;
405 }
Jorim Jaggic97ba492015-11-06 22:00:18 +0100406
407 // Make sure shadows are updated
408 mBackground.invalidate();
409
Jorim Jaggidc249c42015-12-15 14:57:31 -0800410 mLastResizeRect.set(mDockedRect);
411 if (taskPosition != TASK_POSITION_SAME) {
412 calculateBoundsForPosition(position, invertDockSide(mDockSide), mOtherRect);
Jorim Jaggie435e982015-12-30 13:54:32 +0100413 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 Jaggidc249c42015-12-15 14:57:31 -0800420 alignTopLeft(mDockedRect, mDockedTaskRect);
421 alignTopLeft(mOtherRect, mOtherTaskRect);
422 mDockedInsetRect.set(mDockedTaskRect);
423 mOtherInsetRect.set(mOtherTaskRect);
Jorim Jaggie435e982015-12-30 13:54:32 +0100424 if (dockSideTopLeft(mDockSide)) {
Jorim Jaggidc249c42015-12-15 14:57:31 -0800425 alignTopLeft(mDockedRect, mDockedInsetRect);
426 alignBottomRight(mOtherRect, mOtherInsetRect);
427 } else {
428 alignBottomRight(mDockedRect, mDockedInsetRect);
429 alignTopLeft(mOtherRect, mOtherInsetRect);
430 }
Jorim Jaggie435e982015-12-30 13:54:32 +0100431 applyDismissingParallax(mDockedTaskRect, mDockSide, taskSnapTarget, position,
432 taskPositionDocked);
433 applyDismissingParallax(mOtherTaskRect, dockSideInverted, taskSnapTarget, position,
434 taskPositionOther);
Jorim Jaggidc249c42015-12-15 14:57:31 -0800435 mWindowManagerProxy.resizeDockedStack(mDockedRect, mDockedTaskRect, mDockedInsetRect,
436 mOtherTaskRect, mOtherInsetRect);
437 } else {
438 mWindowManagerProxy.resizeDockedStack(mDockedRect, null, null, null, null);
439 }
Jorim Jaggi50981592015-12-29 17:54:12 +0100440 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 Jaggie435e982015-12-30 13:54:32 +0100448 /**
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 Jaggi50981592015-12-29 17:54:12 +0100528 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 Jaggi1fcbab62015-11-04 16:39:50 +0100535 }
536
Jorim Jaggie435e982015-12-30 13:54:32 +0100537 /**
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 Jaggi1fcbab62015-11-04 16:39:50 +0100551 @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}