11fcbab6ae5c99acab70eacc015d194e2c6ddd4e2Jorim Jaggi/* 21fcbab6ae5c99acab70eacc015d194e2c6ddd4e2Jorim Jaggi * Copyright (C) 2015 The Android Open Source Project 31fcbab6ae5c99acab70eacc015d194e2c6ddd4e2Jorim Jaggi * 41fcbab6ae5c99acab70eacc015d194e2c6ddd4e2Jorim Jaggi * Licensed under the Apache License, Version 2.0 (the "License"); 51fcbab6ae5c99acab70eacc015d194e2c6ddd4e2Jorim Jaggi * you may not use this file except in compliance with the License. 61fcbab6ae5c99acab70eacc015d194e2c6ddd4e2Jorim Jaggi * You may obtain a copy of the License at 71fcbab6ae5c99acab70eacc015d194e2c6ddd4e2Jorim Jaggi * 81fcbab6ae5c99acab70eacc015d194e2c6ddd4e2Jorim Jaggi * http://www.apache.org/licenses/LICENSE-2.0 91fcbab6ae5c99acab70eacc015d194e2c6ddd4e2Jorim Jaggi * 101fcbab6ae5c99acab70eacc015d194e2c6ddd4e2Jorim Jaggi * Unless required by applicable law or agreed to in writing, software 111fcbab6ae5c99acab70eacc015d194e2c6ddd4e2Jorim Jaggi * distributed under the License is distributed on an "AS IS" BASIS, 121fcbab6ae5c99acab70eacc015d194e2c6ddd4e2Jorim Jaggi * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 131fcbab6ae5c99acab70eacc015d194e2c6ddd4e2Jorim Jaggi * See the License for the specific language governing permissions and 141fcbab6ae5c99acab70eacc015d194e2c6ddd4e2Jorim Jaggi * limitations under the License. 151fcbab6ae5c99acab70eacc015d194e2c6ddd4e2Jorim Jaggi */ 161fcbab6ae5c99acab70eacc015d194e2c6ddd4e2Jorim Jaggi 17737af724eb31f513386e91ee5510cc6991350937Jorim Jaggipackage com.android.internal.policy; 181fcbab6ae5c99acab70eacc015d194e2c6ddd4e2Jorim Jaggi 191fcbab6ae5c99acab70eacc015d194e2c6ddd4e2Jorim Jaggiimport android.content.Context; 2019cf2972582198484816ac15ba83a4f46946082bJorim Jaggiimport android.content.res.Configuration; 21737af724eb31f513386e91ee5510cc6991350937Jorim Jaggiimport android.content.res.Resources; 2281fe2d1f0adc9e752d7f1a410d66af6a326fd6e2Jorim Jaggiimport android.graphics.Rect; 2319cf2972582198484816ac15ba83a4f46946082bJorim Jaggiimport android.hardware.display.DisplayManager; 24df012d5102735412d9f38513c103aa53df4bcab9Jorim Jaggiimport android.util.Log; 2519cf2972582198484816ac15ba83a4f46946082bJorim Jaggiimport android.view.Display; 2619cf2972582198484816ac15ba83a4f46946082bJorim Jaggiimport android.view.DisplayInfo; 271fcbab6ae5c99acab70eacc015d194e2c6ddd4e2Jorim Jaggi 281fcbab6ae5c99acab70eacc015d194e2c6ddd4e2Jorim Jaggiimport java.util.ArrayList; 291fcbab6ae5c99acab70eacc015d194e2c6ddd4e2Jorim Jaggi 301fcbab6ae5c99acab70eacc015d194e2c6ddd4e2Jorim Jaggi/** 311fcbab6ae5c99acab70eacc015d194e2c6ddd4e2Jorim Jaggi * Calculates the snap targets and the snap position given a position and a velocity. All positions 321fcbab6ae5c99acab70eacc015d194e2c6ddd4e2Jorim Jaggi * here are to be interpreted as the left/top edge of the divider rectangle. 33737af724eb31f513386e91ee5510cc6991350937Jorim Jaggi * 34737af724eb31f513386e91ee5510cc6991350937Jorim Jaggi * @hide 351fcbab6ae5c99acab70eacc015d194e2c6ddd4e2Jorim Jaggi */ 361fcbab6ae5c99acab70eacc015d194e2c6ddd4e2Jorim Jaggipublic class DividerSnapAlgorithm { 371fcbab6ae5c99acab70eacc015d194e2c6ddd4e2Jorim Jaggi 38df012d5102735412d9f38513c103aa53df4bcab9Jorim Jaggi private static final int MIN_FLING_VELOCITY_DP_PER_SECOND = 400; 39df012d5102735412d9f38513c103aa53df4bcab9Jorim Jaggi private static final int MIN_DISMISS_VELOCITY_DP_PER_SECOND = 600; 40df012d5102735412d9f38513c103aa53df4bcab9Jorim Jaggi 4181fe2d1f0adc9e752d7f1a410d66af6a326fd6e2Jorim Jaggi /** 4281fe2d1f0adc9e752d7f1a410d66af6a326fd6e2Jorim Jaggi * 3 snap targets: left/top has 16:9 ratio (for videos), 1:1, and right/bottom has 16:9 ratio 4381fe2d1f0adc9e752d7f1a410d66af6a326fd6e2Jorim Jaggi */ 4481fe2d1f0adc9e752d7f1a410d66af6a326fd6e2Jorim Jaggi private static final int SNAP_MODE_16_9 = 0; 4581fe2d1f0adc9e752d7f1a410d66af6a326fd6e2Jorim Jaggi 4681fe2d1f0adc9e752d7f1a410d66af6a326fd6e2Jorim Jaggi /** 4781fe2d1f0adc9e752d7f1a410d66af6a326fd6e2Jorim Jaggi * 3 snap targets: fixed ratio, 1:1, (1 - fixed ratio) 4881fe2d1f0adc9e752d7f1a410d66af6a326fd6e2Jorim Jaggi */ 4981fe2d1f0adc9e752d7f1a410d66af6a326fd6e2Jorim Jaggi private static final int SNAP_FIXED_RATIO = 1; 5081fe2d1f0adc9e752d7f1a410d66af6a326fd6e2Jorim Jaggi 5181fe2d1f0adc9e752d7f1a410d66af6a326fd6e2Jorim Jaggi /** 5281fe2d1f0adc9e752d7f1a410d66af6a326fd6e2Jorim Jaggi * 1 snap target: 1:1 5381fe2d1f0adc9e752d7f1a410d66af6a326fd6e2Jorim Jaggi */ 5481fe2d1f0adc9e752d7f1a410d66af6a326fd6e2Jorim Jaggi private static final int SNAP_ONLY_1_1 = 2; 5581fe2d1f0adc9e752d7f1a410d66af6a326fd6e2Jorim Jaggi 56737af724eb31f513386e91ee5510cc6991350937Jorim Jaggi private final float mMinFlingVelocityPxPerSecond; 57df012d5102735412d9f38513c103aa53df4bcab9Jorim Jaggi private final float mMinDismissVelocityPxPerSecond; 5881fe2d1f0adc9e752d7f1a410d66af6a326fd6e2Jorim Jaggi private final int mDisplayWidth; 5981fe2d1f0adc9e752d7f1a410d66af6a326fd6e2Jorim Jaggi private final int mDisplayHeight; 601fcbab6ae5c99acab70eacc015d194e2c6ddd4e2Jorim Jaggi private final int mDividerSize; 6181fe2d1f0adc9e752d7f1a410d66af6a326fd6e2Jorim Jaggi private final ArrayList<SnapTarget> mTargets = new ArrayList<>(); 6281fe2d1f0adc9e752d7f1a410d66af6a326fd6e2Jorim Jaggi private final Rect mInsets = new Rect(); 6381fe2d1f0adc9e752d7f1a410d66af6a326fd6e2Jorim Jaggi private final int mSnapMode; 6419cf2972582198484816ac15ba83a4f46946082bJorim Jaggi private final int mMinimalSizeResizableTask; 6581fe2d1f0adc9e752d7f1a410d66af6a326fd6e2Jorim Jaggi private final float mFixedRatio; 661b12ef55b74f47eedde8afe22b997d40c3668a31Jorim Jaggi private boolean mIsHorizontalDivision; 671fcbab6ae5c99acab70eacc015d194e2c6ddd4e2Jorim Jaggi 681fcbab6ae5c99acab70eacc015d194e2c6ddd4e2Jorim Jaggi /** The first target which is still splitting the screen */ 691fcbab6ae5c99acab70eacc015d194e2c6ddd4e2Jorim Jaggi private final SnapTarget mFirstSplitTarget; 701fcbab6ae5c99acab70eacc015d194e2c6ddd4e2Jorim Jaggi 711fcbab6ae5c99acab70eacc015d194e2c6ddd4e2Jorim Jaggi /** The last target which is still splitting the screen */ 721fcbab6ae5c99acab70eacc015d194e2c6ddd4e2Jorim Jaggi private final SnapTarget mLastSplitTarget; 731fcbab6ae5c99acab70eacc015d194e2c6ddd4e2Jorim Jaggi 741fcbab6ae5c99acab70eacc015d194e2c6ddd4e2Jorim Jaggi private final SnapTarget mDismissStartTarget; 751fcbab6ae5c99acab70eacc015d194e2c6ddd4e2Jorim Jaggi private final SnapTarget mDismissEndTarget; 76d434dcbfc9407baad28b6b40fea75b1b6050ad7eJorim Jaggi private final SnapTarget mMiddleTarget; 771fcbab6ae5c99acab70eacc015d194e2c6ddd4e2Jorim Jaggi 7819cf2972582198484816ac15ba83a4f46946082bJorim Jaggi public static DividerSnapAlgorithm create(Context ctx, Rect insets) { 7919cf2972582198484816ac15ba83a4f46946082bJorim Jaggi DisplayInfo displayInfo = new DisplayInfo(); 8019cf2972582198484816ac15ba83a4f46946082bJorim Jaggi ctx.getSystemService(DisplayManager.class).getDisplay( 8119cf2972582198484816ac15ba83a4f46946082bJorim Jaggi Display.DEFAULT_DISPLAY).getDisplayInfo(displayInfo); 8219cf2972582198484816ac15ba83a4f46946082bJorim Jaggi int dividerWindowWidth = ctx.getResources().getDimensionPixelSize( 8319cf2972582198484816ac15ba83a4f46946082bJorim Jaggi com.android.internal.R.dimen.docked_stack_divider_thickness); 8419cf2972582198484816ac15ba83a4f46946082bJorim Jaggi int dividerInsets = ctx.getResources().getDimensionPixelSize( 8519cf2972582198484816ac15ba83a4f46946082bJorim Jaggi com.android.internal.R.dimen.docked_stack_divider_insets); 8619cf2972582198484816ac15ba83a4f46946082bJorim Jaggi return new DividerSnapAlgorithm(ctx.getResources(), 8719cf2972582198484816ac15ba83a4f46946082bJorim Jaggi displayInfo.logicalWidth, displayInfo.logicalHeight, 8819cf2972582198484816ac15ba83a4f46946082bJorim Jaggi dividerWindowWidth - 2 * dividerInsets, 899832f8f463a7d5448af2294bce05eac85f4dbef0Winson ctx.getApplicationContext().getResources().getConfiguration().orientation 9019cf2972582198484816ac15ba83a4f46946082bJorim Jaggi == Configuration.ORIENTATION_PORTRAIT, 9119cf2972582198484816ac15ba83a4f46946082bJorim Jaggi insets); 9219cf2972582198484816ac15ba83a4f46946082bJorim Jaggi } 9319cf2972582198484816ac15ba83a4f46946082bJorim Jaggi 94df012d5102735412d9f38513c103aa53df4bcab9Jorim Jaggi public DividerSnapAlgorithm(Resources res, int displayWidth, int displayHeight, int dividerSize, 95df012d5102735412d9f38513c103aa53df4bcab9Jorim Jaggi boolean isHorizontalDivision, Rect insets) { 96df012d5102735412d9f38513c103aa53df4bcab9Jorim Jaggi mMinFlingVelocityPxPerSecond = 97df012d5102735412d9f38513c103aa53df4bcab9Jorim Jaggi MIN_FLING_VELOCITY_DP_PER_SECOND * res.getDisplayMetrics().density; 98df012d5102735412d9f38513c103aa53df4bcab9Jorim Jaggi mMinDismissVelocityPxPerSecond = 99df012d5102735412d9f38513c103aa53df4bcab9Jorim Jaggi MIN_DISMISS_VELOCITY_DP_PER_SECOND * res.getDisplayMetrics().density; 1001fcbab6ae5c99acab70eacc015d194e2c6ddd4e2Jorim Jaggi mDividerSize = dividerSize; 10181fe2d1f0adc9e752d7f1a410d66af6a326fd6e2Jorim Jaggi mDisplayWidth = displayWidth; 10281fe2d1f0adc9e752d7f1a410d66af6a326fd6e2Jorim Jaggi mDisplayHeight = displayHeight; 1031b12ef55b74f47eedde8afe22b997d40c3668a31Jorim Jaggi mIsHorizontalDivision = isHorizontalDivision; 10481fe2d1f0adc9e752d7f1a410d66af6a326fd6e2Jorim Jaggi mInsets.set(insets); 105737af724eb31f513386e91ee5510cc6991350937Jorim Jaggi mSnapMode = res.getInteger( 10681fe2d1f0adc9e752d7f1a410d66af6a326fd6e2Jorim Jaggi com.android.internal.R.integer.config_dockedStackDividerSnapMode); 107737af724eb31f513386e91ee5510cc6991350937Jorim Jaggi mFixedRatio = res.getFraction( 10881fe2d1f0adc9e752d7f1a410d66af6a326fd6e2Jorim Jaggi com.android.internal.R.fraction.docked_stack_divider_fixed_ratio, 1, 1); 10919cf2972582198484816ac15ba83a4f46946082bJorim Jaggi mMinimalSizeResizableTask = res.getDimensionPixelSize( 11019cf2972582198484816ac15ba83a4f46946082bJorim Jaggi com.android.internal.R.dimen.default_minimal_size_resizable_task); 11181fe2d1f0adc9e752d7f1a410d66af6a326fd6e2Jorim Jaggi calculateTargets(isHorizontalDivision); 1121fcbab6ae5c99acab70eacc015d194e2c6ddd4e2Jorim Jaggi mFirstSplitTarget = mTargets.get(1); 1131fcbab6ae5c99acab70eacc015d194e2c6ddd4e2Jorim Jaggi mLastSplitTarget = mTargets.get(mTargets.size() - 2); 1141fcbab6ae5c99acab70eacc015d194e2c6ddd4e2Jorim Jaggi mDismissStartTarget = mTargets.get(0); 1151fcbab6ae5c99acab70eacc015d194e2c6ddd4e2Jorim Jaggi mDismissEndTarget = mTargets.get(mTargets.size() - 1); 116d434dcbfc9407baad28b6b40fea75b1b6050ad7eJorim Jaggi mMiddleTarget = mTargets.get(mTargets.size() / 2); 1171fcbab6ae5c99acab70eacc015d194e2c6ddd4e2Jorim Jaggi } 1181fcbab6ae5c99acab70eacc015d194e2c6ddd4e2Jorim Jaggi 11919cf2972582198484816ac15ba83a4f46946082bJorim Jaggi /** 12019cf2972582198484816ac15ba83a4f46946082bJorim Jaggi * @return whether it's feasible to enable split screen in the current configuration, i.e. when 12119cf2972582198484816ac15ba83a4f46946082bJorim Jaggi * snapping in the middle both tasks are larger than the minimal task size. 12219cf2972582198484816ac15ba83a4f46946082bJorim Jaggi */ 12319cf2972582198484816ac15ba83a4f46946082bJorim Jaggi public boolean isSplitScreenFeasible() { 12419cf2972582198484816ac15ba83a4f46946082bJorim Jaggi int statusBarSize = mInsets.top; 12519cf2972582198484816ac15ba83a4f46946082bJorim Jaggi int navBarSize = mIsHorizontalDivision ? mInsets.bottom : mInsets.right; 12619cf2972582198484816ac15ba83a4f46946082bJorim Jaggi int size = mIsHorizontalDivision 12719cf2972582198484816ac15ba83a4f46946082bJorim Jaggi ? mDisplayHeight 12819cf2972582198484816ac15ba83a4f46946082bJorim Jaggi : mDisplayWidth; 12919cf2972582198484816ac15ba83a4f46946082bJorim Jaggi int availableSpace = size - navBarSize - statusBarSize - mDividerSize; 13019cf2972582198484816ac15ba83a4f46946082bJorim Jaggi return availableSpace / 2 >= mMinimalSizeResizableTask; 13119cf2972582198484816ac15ba83a4f46946082bJorim Jaggi } 13219cf2972582198484816ac15ba83a4f46946082bJorim Jaggi 1331fcbab6ae5c99acab70eacc015d194e2c6ddd4e2Jorim Jaggi public SnapTarget calculateSnapTarget(int position, float velocity) { 134df012d5102735412d9f38513c103aa53df4bcab9Jorim Jaggi return calculateSnapTarget(position, velocity, true /* hardDismiss */); 135df012d5102735412d9f38513c103aa53df4bcab9Jorim Jaggi } 136df012d5102735412d9f38513c103aa53df4bcab9Jorim Jaggi 137df012d5102735412d9f38513c103aa53df4bcab9Jorim Jaggi /** 138df012d5102735412d9f38513c103aa53df4bcab9Jorim Jaggi * @param position the top/left position of the divider 139df012d5102735412d9f38513c103aa53df4bcab9Jorim Jaggi * @param velocity current dragging velocity 140df012d5102735412d9f38513c103aa53df4bcab9Jorim Jaggi * @param hardDismiss if set, make it a bit harder to get reach the dismiss targets 141df012d5102735412d9f38513c103aa53df4bcab9Jorim Jaggi */ 142df012d5102735412d9f38513c103aa53df4bcab9Jorim Jaggi public SnapTarget calculateSnapTarget(int position, float velocity, boolean hardDismiss) { 143df012d5102735412d9f38513c103aa53df4bcab9Jorim Jaggi if (position < mFirstSplitTarget.position && velocity < -mMinDismissVelocityPxPerSecond) { 1441fcbab6ae5c99acab70eacc015d194e2c6ddd4e2Jorim Jaggi return mDismissStartTarget; 1451fcbab6ae5c99acab70eacc015d194e2c6ddd4e2Jorim Jaggi } 146df012d5102735412d9f38513c103aa53df4bcab9Jorim Jaggi if (position > mLastSplitTarget.position && velocity > mMinDismissVelocityPxPerSecond) { 1471fcbab6ae5c99acab70eacc015d194e2c6ddd4e2Jorim Jaggi return mDismissEndTarget; 1481fcbab6ae5c99acab70eacc015d194e2c6ddd4e2Jorim Jaggi } 149df012d5102735412d9f38513c103aa53df4bcab9Jorim Jaggi if (Math.abs(velocity) < mMinFlingVelocityPxPerSecond) { 150df012d5102735412d9f38513c103aa53df4bcab9Jorim Jaggi return snap(position, hardDismiss); 151df012d5102735412d9f38513c103aa53df4bcab9Jorim Jaggi } 1521fcbab6ae5c99acab70eacc015d194e2c6ddd4e2Jorim Jaggi if (velocity < 0) { 1531fcbab6ae5c99acab70eacc015d194e2c6ddd4e2Jorim Jaggi return mFirstSplitTarget; 1541fcbab6ae5c99acab70eacc015d194e2c6ddd4e2Jorim Jaggi } else { 1551fcbab6ae5c99acab70eacc015d194e2c6ddd4e2Jorim Jaggi return mLastSplitTarget; 1561fcbab6ae5c99acab70eacc015d194e2c6ddd4e2Jorim Jaggi } 1571fcbab6ae5c99acab70eacc015d194e2c6ddd4e2Jorim Jaggi } 1581fcbab6ae5c99acab70eacc015d194e2c6ddd4e2Jorim Jaggi 159737af724eb31f513386e91ee5510cc6991350937Jorim Jaggi public SnapTarget calculateNonDismissingSnapTarget(int position) { 160df012d5102735412d9f38513c103aa53df4bcab9Jorim Jaggi SnapTarget target = snap(position, false /* hardDismiss */); 161737af724eb31f513386e91ee5510cc6991350937Jorim Jaggi if (target == mDismissStartTarget) { 162737af724eb31f513386e91ee5510cc6991350937Jorim Jaggi return mFirstSplitTarget; 163737af724eb31f513386e91ee5510cc6991350937Jorim Jaggi } else if (target == mDismissEndTarget) { 164737af724eb31f513386e91ee5510cc6991350937Jorim Jaggi return mLastSplitTarget; 165737af724eb31f513386e91ee5510cc6991350937Jorim Jaggi } else { 166737af724eb31f513386e91ee5510cc6991350937Jorim Jaggi return target; 167737af724eb31f513386e91ee5510cc6991350937Jorim Jaggi } 168737af724eb31f513386e91ee5510cc6991350937Jorim Jaggi } 169737af724eb31f513386e91ee5510cc6991350937Jorim Jaggi 1705098159ae31bc59aa3857fecb1847f8d7bb73e54Jorim Jaggi public float calculateDismissingFraction(int position) { 1715098159ae31bc59aa3857fecb1847f8d7bb73e54Jorim Jaggi if (position < mFirstSplitTarget.position) { 1721b12ef55b74f47eedde8afe22b997d40c3668a31Jorim Jaggi return 1f - (float) (position - getStartInset()) 1731b12ef55b74f47eedde8afe22b997d40c3668a31Jorim Jaggi / (mFirstSplitTarget.position - getStartInset()); 1745098159ae31bc59aa3857fecb1847f8d7bb73e54Jorim Jaggi } else if (position > mLastSplitTarget.position) { 1755098159ae31bc59aa3857fecb1847f8d7bb73e54Jorim Jaggi return (float) (position - mLastSplitTarget.position) 17681ba11eccbc2519338782100c13cf4a5909ad6beJorim Jaggi / (mDismissEndTarget.position - mLastSplitTarget.position - mDividerSize); 1775098159ae31bc59aa3857fecb1847f8d7bb73e54Jorim Jaggi } 1785098159ae31bc59aa3857fecb1847f8d7bb73e54Jorim Jaggi return 0f; 1795098159ae31bc59aa3857fecb1847f8d7bb73e54Jorim Jaggi } 1805098159ae31bc59aa3857fecb1847f8d7bb73e54Jorim Jaggi 1815098159ae31bc59aa3857fecb1847f8d7bb73e54Jorim Jaggi public SnapTarget getClosestDismissTarget(int position) { 1828f8155ba8a1c66d539101e55f2d06ae0422682c0Jorim Jaggi if (position < mFirstSplitTarget.position) { 1838f8155ba8a1c66d539101e55f2d06ae0422682c0Jorim Jaggi return mDismissStartTarget; 1848f8155ba8a1c66d539101e55f2d06ae0422682c0Jorim Jaggi } else if (position > mLastSplitTarget.position) { 1858f8155ba8a1c66d539101e55f2d06ae0422682c0Jorim Jaggi return mDismissEndTarget; 1868f8155ba8a1c66d539101e55f2d06ae0422682c0Jorim Jaggi } else if (position - mDismissStartTarget.position 1878f8155ba8a1c66d539101e55f2d06ae0422682c0Jorim Jaggi < mDismissEndTarget.position - position) { 1885098159ae31bc59aa3857fecb1847f8d7bb73e54Jorim Jaggi return mDismissStartTarget; 1895098159ae31bc59aa3857fecb1847f8d7bb73e54Jorim Jaggi } else { 1905098159ae31bc59aa3857fecb1847f8d7bb73e54Jorim Jaggi return mDismissEndTarget; 1915098159ae31bc59aa3857fecb1847f8d7bb73e54Jorim Jaggi } 1925098159ae31bc59aa3857fecb1847f8d7bb73e54Jorim Jaggi } 1935098159ae31bc59aa3857fecb1847f8d7bb73e54Jorim Jaggi 194e435e982fa43832b183bac2d00d9415ac58ac06eJorim Jaggi public SnapTarget getFirstSplitTarget() { 195e435e982fa43832b183bac2d00d9415ac58ac06eJorim Jaggi return mFirstSplitTarget; 196e435e982fa43832b183bac2d00d9415ac58ac06eJorim Jaggi } 197e435e982fa43832b183bac2d00d9415ac58ac06eJorim Jaggi 198e435e982fa43832b183bac2d00d9415ac58ac06eJorim Jaggi public SnapTarget getLastSplitTarget() { 199e435e982fa43832b183bac2d00d9415ac58ac06eJorim Jaggi return mLastSplitTarget; 200e435e982fa43832b183bac2d00d9415ac58ac06eJorim Jaggi } 201e435e982fa43832b183bac2d00d9415ac58ac06eJorim Jaggi 202e435e982fa43832b183bac2d00d9415ac58ac06eJorim Jaggi public SnapTarget getDismissStartTarget() { 203e435e982fa43832b183bac2d00d9415ac58ac06eJorim Jaggi return mDismissStartTarget; 204e435e982fa43832b183bac2d00d9415ac58ac06eJorim Jaggi } 205e435e982fa43832b183bac2d00d9415ac58ac06eJorim Jaggi 206e435e982fa43832b183bac2d00d9415ac58ac06eJorim Jaggi public SnapTarget getDismissEndTarget() { 207e435e982fa43832b183bac2d00d9415ac58ac06eJorim Jaggi return mDismissEndTarget; 208e435e982fa43832b183bac2d00d9415ac58ac06eJorim Jaggi } 209e435e982fa43832b183bac2d00d9415ac58ac06eJorim Jaggi 2101b12ef55b74f47eedde8afe22b997d40c3668a31Jorim Jaggi private int getStartInset() { 2111b12ef55b74f47eedde8afe22b997d40c3668a31Jorim Jaggi if (mIsHorizontalDivision) { 2121b12ef55b74f47eedde8afe22b997d40c3668a31Jorim Jaggi return mInsets.top; 2131b12ef55b74f47eedde8afe22b997d40c3668a31Jorim Jaggi } else { 2141b12ef55b74f47eedde8afe22b997d40c3668a31Jorim Jaggi return mInsets.left; 2151b12ef55b74f47eedde8afe22b997d40c3668a31Jorim Jaggi } 2161b12ef55b74f47eedde8afe22b997d40c3668a31Jorim Jaggi } 2171b12ef55b74f47eedde8afe22b997d40c3668a31Jorim Jaggi 2181b12ef55b74f47eedde8afe22b997d40c3668a31Jorim Jaggi private int getEndInset() { 2191b12ef55b74f47eedde8afe22b997d40c3668a31Jorim Jaggi if (mIsHorizontalDivision) { 2201b12ef55b74f47eedde8afe22b997d40c3668a31Jorim Jaggi return mInsets.bottom; 2211b12ef55b74f47eedde8afe22b997d40c3668a31Jorim Jaggi } else { 2221b12ef55b74f47eedde8afe22b997d40c3668a31Jorim Jaggi return mInsets.right; 2231b12ef55b74f47eedde8afe22b997d40c3668a31Jorim Jaggi } 2241b12ef55b74f47eedde8afe22b997d40c3668a31Jorim Jaggi } 2251b12ef55b74f47eedde8afe22b997d40c3668a31Jorim Jaggi 226df012d5102735412d9f38513c103aa53df4bcab9Jorim Jaggi private SnapTarget snap(int position, boolean hardDismiss) { 2271fcbab6ae5c99acab70eacc015d194e2c6ddd4e2Jorim Jaggi int minIndex = -1; 228df012d5102735412d9f38513c103aa53df4bcab9Jorim Jaggi float minDistance = Float.MAX_VALUE; 2291fcbab6ae5c99acab70eacc015d194e2c6ddd4e2Jorim Jaggi int size = mTargets.size(); 2301fcbab6ae5c99acab70eacc015d194e2c6ddd4e2Jorim Jaggi for (int i = 0; i < size; i++) { 231df012d5102735412d9f38513c103aa53df4bcab9Jorim Jaggi SnapTarget target = mTargets.get(i); 232df012d5102735412d9f38513c103aa53df4bcab9Jorim Jaggi float distance = Math.abs(position - target.position); 233df012d5102735412d9f38513c103aa53df4bcab9Jorim Jaggi if (hardDismiss) { 234df012d5102735412d9f38513c103aa53df4bcab9Jorim Jaggi distance /= target.distanceMultiplier; 235df012d5102735412d9f38513c103aa53df4bcab9Jorim Jaggi } 2361fcbab6ae5c99acab70eacc015d194e2c6ddd4e2Jorim Jaggi if (distance < minDistance) { 2371fcbab6ae5c99acab70eacc015d194e2c6ddd4e2Jorim Jaggi minIndex = i; 2381fcbab6ae5c99acab70eacc015d194e2c6ddd4e2Jorim Jaggi minDistance = distance; 2391fcbab6ae5c99acab70eacc015d194e2c6ddd4e2Jorim Jaggi } 2401fcbab6ae5c99acab70eacc015d194e2c6ddd4e2Jorim Jaggi } 2411fcbab6ae5c99acab70eacc015d194e2c6ddd4e2Jorim Jaggi return mTargets.get(minIndex); 2421fcbab6ae5c99acab70eacc015d194e2c6ddd4e2Jorim Jaggi } 2431fcbab6ae5c99acab70eacc015d194e2c6ddd4e2Jorim Jaggi 24481fe2d1f0adc9e752d7f1a410d66af6a326fd6e2Jorim Jaggi private void calculateTargets(boolean isHorizontalDivision) { 24581fe2d1f0adc9e752d7f1a410d66af6a326fd6e2Jorim Jaggi mTargets.clear(); 2461fcbab6ae5c99acab70eacc015d194e2c6ddd4e2Jorim Jaggi int dividerMax = isHorizontalDivision 24781fe2d1f0adc9e752d7f1a410d66af6a326fd6e2Jorim Jaggi ? mDisplayHeight 24881fe2d1f0adc9e752d7f1a410d66af6a326fd6e2Jorim Jaggi : mDisplayWidth; 2498563943bd39107b5a7ff9ea475592d0040423ba1Jorim Jaggi mTargets.add(new SnapTarget(-mDividerSize, -mDividerSize, SnapTarget.FLAG_DISMISS_START, 2508563943bd39107b5a7ff9ea475592d0040423ba1Jorim Jaggi 0.35f)); 25181fe2d1f0adc9e752d7f1a410d66af6a326fd6e2Jorim Jaggi switch (mSnapMode) { 25281fe2d1f0adc9e752d7f1a410d66af6a326fd6e2Jorim Jaggi case SNAP_MODE_16_9: 25319cf2972582198484816ac15ba83a4f46946082bJorim Jaggi addRatio16_9Targets(isHorizontalDivision, dividerMax); 25481fe2d1f0adc9e752d7f1a410d66af6a326fd6e2Jorim Jaggi break; 25581fe2d1f0adc9e752d7f1a410d66af6a326fd6e2Jorim Jaggi case SNAP_FIXED_RATIO: 25619cf2972582198484816ac15ba83a4f46946082bJorim Jaggi addFixedDivisionTargets(isHorizontalDivision, dividerMax); 25781fe2d1f0adc9e752d7f1a410d66af6a326fd6e2Jorim Jaggi break; 25881fe2d1f0adc9e752d7f1a410d66af6a326fd6e2Jorim Jaggi case SNAP_ONLY_1_1: 25981fe2d1f0adc9e752d7f1a410d66af6a326fd6e2Jorim Jaggi addMiddleTarget(isHorizontalDivision); 26081fe2d1f0adc9e752d7f1a410d66af6a326fd6e2Jorim Jaggi break; 26181fe2d1f0adc9e752d7f1a410d66af6a326fd6e2Jorim Jaggi } 26281ba11eccbc2519338782100c13cf4a5909ad6beJorim Jaggi int navBarSize = isHorizontalDivision ? mInsets.bottom : mInsets.right; 2638563943bd39107b5a7ff9ea475592d0040423ba1Jorim Jaggi mTargets.add(new SnapTarget(dividerMax - navBarSize, dividerMax, 2648563943bd39107b5a7ff9ea475592d0040423ba1Jorim Jaggi SnapTarget.FLAG_DISMISS_END, 0.35f)); 26581fe2d1f0adc9e752d7f1a410d66af6a326fd6e2Jorim Jaggi } 2661fcbab6ae5c99acab70eacc015d194e2c6ddd4e2Jorim Jaggi 26719cf2972582198484816ac15ba83a4f46946082bJorim Jaggi private void addNonDismissingTargets(boolean isHorizontalDivision, int topPosition, 26819cf2972582198484816ac15ba83a4f46946082bJorim Jaggi int bottomPosition, int dividerMax) { 26919cf2972582198484816ac15ba83a4f46946082bJorim Jaggi maybeAddTarget(topPosition, topPosition - mInsets.top); 27019cf2972582198484816ac15ba83a4f46946082bJorim Jaggi addMiddleTarget(isHorizontalDivision); 27119cf2972582198484816ac15ba83a4f46946082bJorim Jaggi maybeAddTarget(bottomPosition, dividerMax - mInsets.bottom 27219cf2972582198484816ac15ba83a4f46946082bJorim Jaggi - (bottomPosition + mDividerSize)); 27319cf2972582198484816ac15ba83a4f46946082bJorim Jaggi } 2748563943bd39107b5a7ff9ea475592d0040423ba1Jorim Jaggi 27519cf2972582198484816ac15ba83a4f46946082bJorim Jaggi private void addFixedDivisionTargets(boolean isHorizontalDivision, int dividerMax) { 27681fe2d1f0adc9e752d7f1a410d66af6a326fd6e2Jorim Jaggi int start = isHorizontalDivision ? mInsets.top : mInsets.left; 27781fe2d1f0adc9e752d7f1a410d66af6a326fd6e2Jorim Jaggi int end = isHorizontalDivision 27881fe2d1f0adc9e752d7f1a410d66af6a326fd6e2Jorim Jaggi ? mDisplayHeight - mInsets.bottom 27981fe2d1f0adc9e752d7f1a410d66af6a326fd6e2Jorim Jaggi : mDisplayWidth - mInsets.right; 2808563943bd39107b5a7ff9ea475592d0040423ba1Jorim Jaggi int size = (int) (mFixedRatio * (end - start)) - mDividerSize / 2; 2818563943bd39107b5a7ff9ea475592d0040423ba1Jorim Jaggi int topPosition = start + size; 2828563943bd39107b5a7ff9ea475592d0040423ba1Jorim Jaggi int bottomPosition = end - size - mDividerSize; 28319cf2972582198484816ac15ba83a4f46946082bJorim Jaggi addNonDismissingTargets(isHorizontalDivision, topPosition, bottomPosition, dividerMax); 28481fe2d1f0adc9e752d7f1a410d66af6a326fd6e2Jorim Jaggi } 28581fe2d1f0adc9e752d7f1a410d66af6a326fd6e2Jorim Jaggi 28619cf2972582198484816ac15ba83a4f46946082bJorim Jaggi private void addRatio16_9Targets(boolean isHorizontalDivision, int dividerMax) { 28781fe2d1f0adc9e752d7f1a410d66af6a326fd6e2Jorim Jaggi int start = isHorizontalDivision ? mInsets.top : mInsets.left; 28881fe2d1f0adc9e752d7f1a410d66af6a326fd6e2Jorim Jaggi int end = isHorizontalDivision 28981fe2d1f0adc9e752d7f1a410d66af6a326fd6e2Jorim Jaggi ? mDisplayHeight - mInsets.bottom 29081fe2d1f0adc9e752d7f1a410d66af6a326fd6e2Jorim Jaggi : mDisplayWidth - mInsets.right; 29181fe2d1f0adc9e752d7f1a410d66af6a326fd6e2Jorim Jaggi int startOther = isHorizontalDivision ? mInsets.left : mInsets.top; 29281fe2d1f0adc9e752d7f1a410d66af6a326fd6e2Jorim Jaggi int endOther = isHorizontalDivision 29381fe2d1f0adc9e752d7f1a410d66af6a326fd6e2Jorim Jaggi ? mDisplayWidth - mInsets.right 29481fe2d1f0adc9e752d7f1a410d66af6a326fd6e2Jorim Jaggi : mDisplayHeight - mInsets.bottom; 29581fe2d1f0adc9e752d7f1a410d66af6a326fd6e2Jorim Jaggi float size = 9.0f / 16.0f * (endOther - startOther); 29681fe2d1f0adc9e752d7f1a410d66af6a326fd6e2Jorim Jaggi int sizeInt = (int) Math.floor(size); 29719cf2972582198484816ac15ba83a4f46946082bJorim Jaggi int topPosition = start + sizeInt; 29819cf2972582198484816ac15ba83a4f46946082bJorim Jaggi int bottomPosition = end - sizeInt - mDividerSize; 29919cf2972582198484816ac15ba83a4f46946082bJorim Jaggi addNonDismissingTargets(isHorizontalDivision, topPosition, bottomPosition, dividerMax); 30019cf2972582198484816ac15ba83a4f46946082bJorim Jaggi } 30119cf2972582198484816ac15ba83a4f46946082bJorim Jaggi 30219cf2972582198484816ac15ba83a4f46946082bJorim Jaggi /** 30319cf2972582198484816ac15ba83a4f46946082bJorim Jaggi * Adds a target at {@param position} but only if the area with size of {@param smallerSize} 30419cf2972582198484816ac15ba83a4f46946082bJorim Jaggi * meets the minimal size requirement. 30519cf2972582198484816ac15ba83a4f46946082bJorim Jaggi */ 30619cf2972582198484816ac15ba83a4f46946082bJorim Jaggi private void maybeAddTarget(int position, int smallerSize) { 30719cf2972582198484816ac15ba83a4f46946082bJorim Jaggi if (smallerSize >= mMinimalSizeResizableTask) { 3088563943bd39107b5a7ff9ea475592d0040423ba1Jorim Jaggi mTargets.add(new SnapTarget(position, position, SnapTarget.FLAG_NONE)); 30919cf2972582198484816ac15ba83a4f46946082bJorim Jaggi } 31081fe2d1f0adc9e752d7f1a410d66af6a326fd6e2Jorim Jaggi } 31181fe2d1f0adc9e752d7f1a410d66af6a326fd6e2Jorim Jaggi 31281fe2d1f0adc9e752d7f1a410d66af6a326fd6e2Jorim Jaggi private void addMiddleTarget(boolean isHorizontalDivision) { 3138563943bd39107b5a7ff9ea475592d0040423ba1Jorim Jaggi int position = DockedDividerUtils.calculateMiddlePosition(isHorizontalDivision, 3148563943bd39107b5a7ff9ea475592d0040423ba1Jorim Jaggi mInsets, mDisplayWidth, mDisplayHeight, mDividerSize); 3158563943bd39107b5a7ff9ea475592d0040423ba1Jorim Jaggi mTargets.add(new SnapTarget(position, position, SnapTarget.FLAG_NONE)); 3161fcbab6ae5c99acab70eacc015d194e2c6ddd4e2Jorim Jaggi } 3171fcbab6ae5c99acab70eacc015d194e2c6ddd4e2Jorim Jaggi 318d434dcbfc9407baad28b6b40fea75b1b6050ad7eJorim Jaggi public SnapTarget getMiddleTarget() { 319d434dcbfc9407baad28b6b40fea75b1b6050ad7eJorim Jaggi return mMiddleTarget; 320d434dcbfc9407baad28b6b40fea75b1b6050ad7eJorim Jaggi } 321d434dcbfc9407baad28b6b40fea75b1b6050ad7eJorim Jaggi 32250cd6361d7cb50bdc0ee199f42307885abc65f0bJorim Jaggi public SnapTarget getNextTarget(SnapTarget snapTarget) { 32350cd6361d7cb50bdc0ee199f42307885abc65f0bJorim Jaggi int index = mTargets.indexOf(snapTarget); 32450cd6361d7cb50bdc0ee199f42307885abc65f0bJorim Jaggi if (index != -1 && index < mTargets.size() - 1) { 32550cd6361d7cb50bdc0ee199f42307885abc65f0bJorim Jaggi return mTargets.get(index + 1); 32650cd6361d7cb50bdc0ee199f42307885abc65f0bJorim Jaggi } 32750cd6361d7cb50bdc0ee199f42307885abc65f0bJorim Jaggi return snapTarget; 32850cd6361d7cb50bdc0ee199f42307885abc65f0bJorim Jaggi } 32950cd6361d7cb50bdc0ee199f42307885abc65f0bJorim Jaggi 33050cd6361d7cb50bdc0ee199f42307885abc65f0bJorim Jaggi public SnapTarget getPreviousTarget(SnapTarget snapTarget) { 33150cd6361d7cb50bdc0ee199f42307885abc65f0bJorim Jaggi int index = mTargets.indexOf(snapTarget); 33250cd6361d7cb50bdc0ee199f42307885abc65f0bJorim Jaggi if (index != -1 && index > 0) { 33350cd6361d7cb50bdc0ee199f42307885abc65f0bJorim Jaggi return mTargets.get(index - 1); 33450cd6361d7cb50bdc0ee199f42307885abc65f0bJorim Jaggi } 33550cd6361d7cb50bdc0ee199f42307885abc65f0bJorim Jaggi return snapTarget; 33650cd6361d7cb50bdc0ee199f42307885abc65f0bJorim Jaggi } 33750cd6361d7cb50bdc0ee199f42307885abc65f0bJorim Jaggi 338ce9630da86342c7bf30f00d1ced34a1d051c55baWinson public boolean isFirstSplitTargetAvailable() { 339ce9630da86342c7bf30f00d1ced34a1d051c55baWinson return mFirstSplitTarget != mMiddleTarget; 340ce9630da86342c7bf30f00d1ced34a1d051c55baWinson } 341ce9630da86342c7bf30f00d1ced34a1d051c55baWinson 342ce9630da86342c7bf30f00d1ced34a1d051c55baWinson public boolean isLastSplitTargetAvailable() { 343ce9630da86342c7bf30f00d1ced34a1d051c55baWinson return mLastSplitTarget != mMiddleTarget; 344ce9630da86342c7bf30f00d1ced34a1d051c55baWinson } 345ce9630da86342c7bf30f00d1ced34a1d051c55baWinson 3461fcbab6ae5c99acab70eacc015d194e2c6ddd4e2Jorim Jaggi /** 347a212999f245032f033e6a0993fd2be9832a9b9ccMuyuan Li * Cycles through all non-dismiss targets with a stepping of {@param increment}. It moves left 348a212999f245032f033e6a0993fd2be9832a9b9ccMuyuan Li * if {@param increment} is negative and moves right otherwise. 349a212999f245032f033e6a0993fd2be9832a9b9ccMuyuan Li */ 350a212999f245032f033e6a0993fd2be9832a9b9ccMuyuan Li public SnapTarget cycleNonDismissTarget(SnapTarget snapTarget, int increment) { 351a212999f245032f033e6a0993fd2be9832a9b9ccMuyuan Li int index = mTargets.indexOf(snapTarget); 352a212999f245032f033e6a0993fd2be9832a9b9ccMuyuan Li if (index != -1) { 353a212999f245032f033e6a0993fd2be9832a9b9ccMuyuan Li SnapTarget newTarget = mTargets.get((index + mTargets.size() + increment) 354a212999f245032f033e6a0993fd2be9832a9b9ccMuyuan Li % mTargets.size()); 355a212999f245032f033e6a0993fd2be9832a9b9ccMuyuan Li if (newTarget == mDismissStartTarget) { 356a212999f245032f033e6a0993fd2be9832a9b9ccMuyuan Li return mLastSplitTarget; 357a212999f245032f033e6a0993fd2be9832a9b9ccMuyuan Li } else if (newTarget == mDismissEndTarget) { 358a212999f245032f033e6a0993fd2be9832a9b9ccMuyuan Li return mFirstSplitTarget; 359a212999f245032f033e6a0993fd2be9832a9b9ccMuyuan Li } else { 360a212999f245032f033e6a0993fd2be9832a9b9ccMuyuan Li return newTarget; 361a212999f245032f033e6a0993fd2be9832a9b9ccMuyuan Li } 362a212999f245032f033e6a0993fd2be9832a9b9ccMuyuan Li } 363a212999f245032f033e6a0993fd2be9832a9b9ccMuyuan Li return snapTarget; 364a212999f245032f033e6a0993fd2be9832a9b9ccMuyuan Li } 365a212999f245032f033e6a0993fd2be9832a9b9ccMuyuan Li 366a212999f245032f033e6a0993fd2be9832a9b9ccMuyuan Li /** 3671fcbab6ae5c99acab70eacc015d194e2c6ddd4e2Jorim Jaggi * Represents a snap target for the divider. 3681fcbab6ae5c99acab70eacc015d194e2c6ddd4e2Jorim Jaggi */ 3691fcbab6ae5c99acab70eacc015d194e2c6ddd4e2Jorim Jaggi public static class SnapTarget { 3701fcbab6ae5c99acab70eacc015d194e2c6ddd4e2Jorim Jaggi public static final int FLAG_NONE = 0; 3711fcbab6ae5c99acab70eacc015d194e2c6ddd4e2Jorim Jaggi 3721fcbab6ae5c99acab70eacc015d194e2c6ddd4e2Jorim Jaggi /** If the divider reaches this value, the left/top task should be dismissed. */ 3731fcbab6ae5c99acab70eacc015d194e2c6ddd4e2Jorim Jaggi public static final int FLAG_DISMISS_START = 1; 3741fcbab6ae5c99acab70eacc015d194e2c6ddd4e2Jorim Jaggi 3751fcbab6ae5c99acab70eacc015d194e2c6ddd4e2Jorim Jaggi /** If the divider reaches this value, the right/bottom task should be dismissed */ 3761fcbab6ae5c99acab70eacc015d194e2c6ddd4e2Jorim Jaggi public static final int FLAG_DISMISS_END = 2; 3771fcbab6ae5c99acab70eacc015d194e2c6ddd4e2Jorim Jaggi 3788563943bd39107b5a7ff9ea475592d0040423ba1Jorim Jaggi /** Position of this snap target. The right/bottom edge of the top/left task snaps here. */ 3791fcbab6ae5c99acab70eacc015d194e2c6ddd4e2Jorim Jaggi public final int position; 3808563943bd39107b5a7ff9ea475592d0040423ba1Jorim Jaggi 3818563943bd39107b5a7ff9ea475592d0040423ba1Jorim Jaggi /** 3828563943bd39107b5a7ff9ea475592d0040423ba1Jorim Jaggi * Like {@link #position}, but used to calculate the task bounds which might be different 3838563943bd39107b5a7ff9ea475592d0040423ba1Jorim Jaggi * from the stack bounds. 3848563943bd39107b5a7ff9ea475592d0040423ba1Jorim Jaggi */ 3858563943bd39107b5a7ff9ea475592d0040423ba1Jorim Jaggi public final int taskPosition; 3868563943bd39107b5a7ff9ea475592d0040423ba1Jorim Jaggi 3871fcbab6ae5c99acab70eacc015d194e2c6ddd4e2Jorim Jaggi public final int flag; 3881fcbab6ae5c99acab70eacc015d194e2c6ddd4e2Jorim Jaggi 389df012d5102735412d9f38513c103aa53df4bcab9Jorim Jaggi /** 390df012d5102735412d9f38513c103aa53df4bcab9Jorim Jaggi * Multiplier used to calculate distance to snap position. The lower this value, the harder 391df012d5102735412d9f38513c103aa53df4bcab9Jorim Jaggi * it's to snap on this target 392df012d5102735412d9f38513c103aa53df4bcab9Jorim Jaggi */ 393df012d5102735412d9f38513c103aa53df4bcab9Jorim Jaggi private final float distanceMultiplier; 394df012d5102735412d9f38513c103aa53df4bcab9Jorim Jaggi 3958563943bd39107b5a7ff9ea475592d0040423ba1Jorim Jaggi public SnapTarget(int position, int taskPosition, int flag) { 3968563943bd39107b5a7ff9ea475592d0040423ba1Jorim Jaggi this(position, taskPosition, flag, 1f); 397df012d5102735412d9f38513c103aa53df4bcab9Jorim Jaggi } 398df012d5102735412d9f38513c103aa53df4bcab9Jorim Jaggi 3998563943bd39107b5a7ff9ea475592d0040423ba1Jorim Jaggi public SnapTarget(int position, int taskPosition, int flag, float distanceMultiplier) { 4001fcbab6ae5c99acab70eacc015d194e2c6ddd4e2Jorim Jaggi this.position = position; 4018563943bd39107b5a7ff9ea475592d0040423ba1Jorim Jaggi this.taskPosition = taskPosition; 4021fcbab6ae5c99acab70eacc015d194e2c6ddd4e2Jorim Jaggi this.flag = flag; 403df012d5102735412d9f38513c103aa53df4bcab9Jorim Jaggi this.distanceMultiplier = distanceMultiplier; 4041fcbab6ae5c99acab70eacc015d194e2c6ddd4e2Jorim Jaggi } 4051fcbab6ae5c99acab70eacc015d194e2c6ddd4e2Jorim Jaggi } 4061fcbab6ae5c99acab70eacc015d194e2c6ddd4e2Jorim Jaggi} 407