DividerSnapAlgorithm.java revision 3e8747414520ee348cf4b9c4a6afd9ff80b5a8f8
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 17package com.android.internal.policy; 18 19import android.content.Context; 20import android.content.res.Resources; 21import android.graphics.Rect; 22 23import java.util.ArrayList; 24 25/** 26 * Calculates the snap targets and the snap position given a position and a velocity. All positions 27 * here are to be interpreted as the left/top edge of the divider rectangle. 28 * 29 * @hide 30 */ 31public class DividerSnapAlgorithm { 32 33 /** 34 * 3 snap targets: left/top has 16:9 ratio (for videos), 1:1, and right/bottom has 16:9 ratio 35 */ 36 private static final int SNAP_MODE_16_9 = 0; 37 38 /** 39 * 3 snap targets: fixed ratio, 1:1, (1 - fixed ratio) 40 */ 41 private static final int SNAP_FIXED_RATIO = 1; 42 43 /** 44 * 1 snap target: 1:1 45 */ 46 private static final int SNAP_ONLY_1_1 = 2; 47 48 private final float mMinFlingVelocityPxPerSecond; 49 private final int mDisplayWidth; 50 private final int mDisplayHeight; 51 private final int mDividerSize; 52 private final ArrayList<SnapTarget> mTargets = new ArrayList<>(); 53 private final Rect mInsets = new Rect(); 54 private final int mSnapMode; 55 private final float mFixedRatio; 56 57 /** The first target which is still splitting the screen */ 58 private final SnapTarget mFirstSplitTarget; 59 60 /** The last target which is still splitting the screen */ 61 private final SnapTarget mLastSplitTarget; 62 63 private final SnapTarget mDismissStartTarget; 64 private final SnapTarget mDismissEndTarget; 65 private final SnapTarget mMiddleTarget; 66 67 public DividerSnapAlgorithm(Resources res, float minFlingVelocityPxPerSecond, 68 int displayWidth, int displayHeight, int dividerSize, boolean isHorizontalDivision, 69 Rect insets) { 70 mMinFlingVelocityPxPerSecond = minFlingVelocityPxPerSecond; 71 mDividerSize = dividerSize; 72 mDisplayWidth = displayWidth; 73 mDisplayHeight = displayHeight; 74 mInsets.set(insets); 75 mSnapMode = res.getInteger( 76 com.android.internal.R.integer.config_dockedStackDividerSnapMode); 77 mFixedRatio = res.getFraction( 78 com.android.internal.R.fraction.docked_stack_divider_fixed_ratio, 1, 1); 79 calculateTargets(isHorizontalDivision); 80 mFirstSplitTarget = mTargets.get(1); 81 mLastSplitTarget = mTargets.get(mTargets.size() - 2); 82 mDismissStartTarget = mTargets.get(0); 83 mDismissEndTarget = mTargets.get(mTargets.size() - 1); 84 mMiddleTarget = mTargets.get(mTargets.size() / 2); 85 } 86 87 public SnapTarget calculateSnapTarget(int position, float velocity) { 88 if (Math.abs(velocity) < mMinFlingVelocityPxPerSecond) { 89 return snap(position); 90 } 91 if (position < mFirstSplitTarget.position && velocity < 0) { 92 return mDismissStartTarget; 93 } 94 if (position > mLastSplitTarget.position && velocity > 0) { 95 return mDismissEndTarget; 96 } 97 if (velocity < 0) { 98 return mFirstSplitTarget; 99 } else { 100 return mLastSplitTarget; 101 } 102 } 103 104 public SnapTarget calculateNonDismissingSnapTarget(int position) { 105 SnapTarget target = snap(position); 106 if (target == mDismissStartTarget) { 107 return mFirstSplitTarget; 108 } else if (target == mDismissEndTarget) { 109 return mLastSplitTarget; 110 } else { 111 return target; 112 } 113 } 114 115 public float calculateDismissingFraction(int position) { 116 if (position < mFirstSplitTarget.position) { 117 return 1f - (float) position / mFirstSplitTarget.position; 118 } else if (position > mLastSplitTarget.position) { 119 return (float) (position - mLastSplitTarget.position) 120 / (mDismissEndTarget.position - mLastSplitTarget.position); 121 } 122 return 0f; 123 } 124 125 public SnapTarget getClosestDismissTarget(int position) { 126 if (position - mDismissStartTarget.position < mDismissEndTarget.position - position) { 127 return mDismissStartTarget; 128 } else { 129 return mDismissEndTarget; 130 } 131 } 132 133 public SnapTarget getFirstSplitTarget() { 134 return mFirstSplitTarget; 135 } 136 137 public SnapTarget getLastSplitTarget() { 138 return mLastSplitTarget; 139 } 140 141 public SnapTarget getDismissStartTarget() { 142 return mDismissStartTarget; 143 } 144 145 public SnapTarget getDismissEndTarget() { 146 return mDismissEndTarget; 147 } 148 149 private SnapTarget snap(int position) { 150 int minIndex = -1; 151 int minDistance = Integer.MAX_VALUE; 152 int size = mTargets.size(); 153 for (int i = 0; i < size; i++) { 154 int distance = Math.abs(position - mTargets.get(i).position); 155 if (distance < minDistance) { 156 minIndex = i; 157 minDistance = distance; 158 } 159 } 160 return mTargets.get(minIndex); 161 } 162 163 private void calculateTargets(boolean isHorizontalDivision) { 164 mTargets.clear(); 165 int dividerMax = isHorizontalDivision 166 ? mDisplayHeight 167 : mDisplayWidth; 168 mTargets.add(new SnapTarget(-mDividerSize, SnapTarget.FLAG_DISMISS_START)); 169 switch (mSnapMode) { 170 case SNAP_MODE_16_9: 171 addRatio16_9Targets(isHorizontalDivision); 172 break; 173 case SNAP_FIXED_RATIO: 174 addFixedDivisionTargets(isHorizontalDivision); 175 break; 176 case SNAP_ONLY_1_1: 177 addMiddleTarget(isHorizontalDivision); 178 break; 179 } 180 mTargets.add(new SnapTarget(dividerMax, SnapTarget.FLAG_DISMISS_END)); 181 } 182 183 private void addFixedDivisionTargets(boolean isHorizontalDivision) { 184 int start = isHorizontalDivision ? mInsets.top : mInsets.left; 185 int end = isHorizontalDivision 186 ? mDisplayHeight - mInsets.bottom 187 : mDisplayWidth - mInsets.right; 188 mTargets.add(new SnapTarget((int) (start + mFixedRatio * (end - start)) - mDividerSize / 2, 189 SnapTarget.FLAG_NONE)); 190 addMiddleTarget(isHorizontalDivision); 191 mTargets.add(new SnapTarget((int) (start + (1 - mFixedRatio) * (end - start)) 192 - mDividerSize / 2, SnapTarget.FLAG_NONE)); 193 } 194 195 private void addRatio16_9Targets(boolean isHorizontalDivision) { 196 int start = isHorizontalDivision ? mInsets.top : mInsets.left; 197 int end = isHorizontalDivision 198 ? mDisplayHeight - mInsets.bottom 199 : mDisplayWidth - mInsets.right; 200 int startOther = isHorizontalDivision ? mInsets.left : mInsets.top; 201 int endOther = isHorizontalDivision 202 ? mDisplayWidth - mInsets.right 203 : mDisplayHeight - mInsets.bottom; 204 float size = 9.0f / 16.0f * (endOther - startOther); 205 int sizeInt = (int) Math.floor(size); 206 mTargets.add(new SnapTarget(start + sizeInt, SnapTarget.FLAG_NONE)); 207 addMiddleTarget(isHorizontalDivision); 208 mTargets.add(new SnapTarget(end - sizeInt - mDividerSize, SnapTarget.FLAG_NONE)); 209 } 210 211 private void addMiddleTarget(boolean isHorizontalDivision) { 212 mTargets.add(new SnapTarget(DockedDividerUtils.calculateMiddlePosition(isHorizontalDivision, 213 mInsets, mDisplayWidth, mDisplayHeight, mDividerSize), SnapTarget.FLAG_NONE)); 214 } 215 216 public SnapTarget getMiddleTarget() { 217 return mMiddleTarget; 218 } 219 220 /** 221 * Represents a snap target for the divider. 222 */ 223 public static class SnapTarget { 224 public static final int FLAG_NONE = 0; 225 226 /** If the divider reaches this value, the left/top task should be dismissed. */ 227 public static final int FLAG_DISMISS_START = 1; 228 229 /** If the divider reaches this value, the right/bottom task should be dismissed */ 230 public static final int FLAG_DISMISS_END = 2; 231 232 public final int position; 233 public final int flag; 234 235 public SnapTarget(int position, int flag) { 236 this.position = position; 237 this.flag = flag; 238 } 239 } 240} 241