The Android Open Source Project | 31dd503 | 2009-03-03 19:32:27 -0800 | [diff] [blame] | 1 | /* |
| 2 | * Copyright (C) 2008 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 | |
Joe Onorato | a590252 | 2009-07-30 13:37:37 -0700 | [diff] [blame] | 17 | package com.android.launcher2; |
The Android Open Source Project | 31dd503 | 2009-03-03 19:32:27 -0800 | [diff] [blame] | 18 | |
Adam Cohen | 716b51e | 2011-06-30 12:09:54 -0700 | [diff] [blame] | 19 | import android.animation.Animator; |
| 20 | import android.animation.AnimatorListenerAdapter; |
| 21 | import android.animation.TimeInterpolator; |
| 22 | import android.animation.ValueAnimator; |
| 23 | import android.animation.ValueAnimator.AnimatorUpdateListener; |
The Android Open Source Project | 31dd503 | 2009-03-03 19:32:27 -0800 | [diff] [blame] | 24 | import android.content.Context; |
Adam Cohen | 716b51e | 2011-06-30 12:09:54 -0700 | [diff] [blame] | 25 | import android.content.res.Resources; |
Winson Chung | 360e63f | 2012-04-27 13:48:05 -0700 | [diff] [blame] | 26 | import android.graphics.Canvas; |
Adam Cohen | 6788269 | 2011-03-11 15:29:03 -0800 | [diff] [blame] | 27 | import android.graphics.Rect; |
Winson Chung | 360e63f | 2012-04-27 13:48:05 -0700 | [diff] [blame] | 28 | import android.graphics.drawable.Drawable; |
The Android Open Source Project | 31dd503 | 2009-03-03 19:32:27 -0800 | [diff] [blame] | 29 | import android.util.AttributeSet; |
The Android Open Source Project | 31dd503 | 2009-03-03 19:32:27 -0800 | [diff] [blame] | 30 | import android.view.KeyEvent; |
Michael Jurka | 0e26059 | 2010-06-30 17:07:39 -0700 | [diff] [blame] | 31 | import android.view.MotionEvent; |
Romain Guy | ea3763c | 2010-01-11 18:02:04 -0800 | [diff] [blame] | 32 | import android.view.View; |
Michael Jurka | 8b805b1 | 2012-04-18 14:23:14 -0700 | [diff] [blame] | 33 | import android.view.ViewGroup; |
Adam Cohen | 76fc085 | 2011-06-17 13:26:23 -0700 | [diff] [blame] | 34 | import android.view.ViewParent; |
Adam Cohen | 3371da0 | 2011-10-25 21:38:29 -0700 | [diff] [blame] | 35 | import android.view.accessibility.AccessibilityEvent; |
| 36 | import android.view.accessibility.AccessibilityManager; |
Adam Cohen | 716b51e | 2011-06-30 12:09:54 -0700 | [diff] [blame] | 37 | import android.view.animation.DecelerateInterpolator; |
Adam Cohen | 3e8f811 | 2011-07-02 18:03:00 -0700 | [diff] [blame] | 38 | import android.view.animation.Interpolator; |
The Android Open Source Project | 31dd503 | 2009-03-03 19:32:27 -0800 | [diff] [blame] | 39 | import android.widget.FrameLayout; |
Adam Cohen | ac8c876 | 2011-07-13 11:15:27 -0700 | [diff] [blame] | 40 | import android.widget.TextView; |
The Android Open Source Project | 31dd503 | 2009-03-03 19:32:27 -0800 | [diff] [blame] | 41 | |
Adam Cohen | 716b51e | 2011-06-30 12:09:54 -0700 | [diff] [blame] | 42 | import com.android.launcher.R; |
| 43 | |
Adam Cohen | 1d9af7d | 2011-06-22 15:26:58 -0700 | [diff] [blame] | 44 | import java.util.ArrayList; |
Adam Cohen | 6788269 | 2011-03-11 15:29:03 -0800 | [diff] [blame] | 45 | |
The Android Open Source Project | 31dd503 | 2009-03-03 19:32:27 -0800 | [diff] [blame] | 46 | /** |
Michael Jurka | 0e26059 | 2010-06-30 17:07:39 -0700 | [diff] [blame] | 47 | * A ViewGroup that coordinates dragging across its descendants |
The Android Open Source Project | 31dd503 | 2009-03-03 19:32:27 -0800 | [diff] [blame] | 48 | */ |
Michael Jurka | 8b805b1 | 2012-04-18 14:23:14 -0700 | [diff] [blame] | 49 | public class DragLayer extends FrameLayout implements ViewGroup.OnHierarchyChangeListener { |
Adam Cohen | 120980b | 2010-12-08 11:05:37 -0800 | [diff] [blame] | 50 | private DragController mDragController; |
| 51 | private int[] mTmpXY = new int[2]; |
The Android Open Source Project | 31dd503 | 2009-03-03 19:32:27 -0800 | [diff] [blame] | 52 | |
Adam Cohen | 716b51e | 2011-06-30 12:09:54 -0700 | [diff] [blame] | 53 | private int mXDown, mYDown; |
| 54 | private Launcher mLauncher; |
| 55 | |
Adam Cohen | 6788269 | 2011-03-11 15:29:03 -0800 | [diff] [blame] | 56 | // Variables relating to resizing widgets |
| 57 | private final ArrayList<AppWidgetResizeFrame> mResizeFrames = |
| 58 | new ArrayList<AppWidgetResizeFrame>(); |
| 59 | private AppWidgetResizeFrame mCurrentResizeFrame; |
Adam Cohen | 716b51e | 2011-06-30 12:09:54 -0700 | [diff] [blame] | 60 | |
| 61 | // Variables relating to animation of views after drop |
| 62 | private ValueAnimator mDropAnim = null; |
Adam Cohen | 8dfcba4 | 2011-07-07 16:38:18 -0700 | [diff] [blame] | 63 | private ValueAnimator mFadeOutAnim = null; |
| 64 | private TimeInterpolator mCubicEaseOutInterpolator = new DecelerateInterpolator(1.5f); |
Winson Chung | 7bd1bbb | 2012-02-13 18:29:29 -0800 | [diff] [blame] | 65 | private DragView mDropView = null; |
Adam Cohen | 6441de0 | 2011-12-14 14:25:32 -0800 | [diff] [blame] | 66 | private int mAnchorViewInitialScrollX = 0; |
| 67 | private View mAnchorView = null; |
Adam Cohen | 8dfcba4 | 2011-07-07 16:38:18 -0700 | [diff] [blame] | 68 | |
Adam Cohen | 3371da0 | 2011-10-25 21:38:29 -0700 | [diff] [blame] | 69 | private boolean mHoverPointClosesFolder = false; |
| 70 | private Rect mHitRect = new Rect(); |
Adam Cohen | 21b4110 | 2011-11-01 17:29:52 -0700 | [diff] [blame] | 71 | private int mWorkspaceIndex = -1; |
Adam Cohen | ebea84d | 2011-11-09 17:20:41 -0800 | [diff] [blame] | 72 | private int mQsbIndex = -1; |
Adam Cohen | ed66b2b | 2012-01-23 17:28:51 -0800 | [diff] [blame] | 73 | public static final int ANIMATION_END_DISAPPEAR = 0; |
| 74 | public static final int ANIMATION_END_FADE_OUT = 1; |
| 75 | public static final int ANIMATION_END_REMAIN_VISIBLE = 2; |
Adam Cohen | 6788269 | 2011-03-11 15:29:03 -0800 | [diff] [blame] | 76 | |
The Android Open Source Project | 31dd503 | 2009-03-03 19:32:27 -0800 | [diff] [blame] | 77 | /** |
| 78 | * Used to create a new DragLayer from XML. |
| 79 | * |
| 80 | * @param context The application's context. |
Michael Jurka | 0e26059 | 2010-06-30 17:07:39 -0700 | [diff] [blame] | 81 | * @param attrs The attributes set containing the Workspace's customization values. |
The Android Open Source Project | 31dd503 | 2009-03-03 19:32:27 -0800 | [diff] [blame] | 82 | */ |
| 83 | public DragLayer(Context context, AttributeSet attrs) { |
| 84 | super(context, attrs); |
Winson Chung | be62afa | 2011-02-03 23:14:57 -0800 | [diff] [blame] | 85 | |
| 86 | // Disable multitouch across the workspace/all apps/customize tray |
| 87 | setMotionEventSplittingEnabled(false); |
Adam Cohen | 21b4110 | 2011-11-01 17:29:52 -0700 | [diff] [blame] | 88 | setChildrenDrawingOrderEnabled(true); |
Michael Jurka | 8b805b1 | 2012-04-18 14:23:14 -0700 | [diff] [blame] | 89 | setOnHierarchyChangeListener(this); |
Winson Chung | 360e63f | 2012-04-27 13:48:05 -0700 | [diff] [blame] | 90 | |
| 91 | mLeftHoverDrawable = getResources().getDrawable(R.drawable.page_hover_left_holo); |
| 92 | mRightHoverDrawable = getResources().getDrawable(R.drawable.page_hover_right_holo); |
The Android Open Source Project | 31dd503 | 2009-03-03 19:32:27 -0800 | [diff] [blame] | 93 | } |
| 94 | |
Winson Chung | 4c98d92 | 2011-05-31 16:50:48 -0700 | [diff] [blame] | 95 | public void setup(Launcher launcher, DragController controller) { |
| 96 | mLauncher = launcher; |
Joe Onorato | 00acb12 | 2009-08-04 16:04:30 -0400 | [diff] [blame] | 97 | mDragController = controller; |
The Android Open Source Project | 31dd503 | 2009-03-03 19:32:27 -0800 | [diff] [blame] | 98 | } |
Adam Cohen | 76fc085 | 2011-06-17 13:26:23 -0700 | [diff] [blame] | 99 | |
The Android Open Source Project | 31dd503 | 2009-03-03 19:32:27 -0800 | [diff] [blame] | 100 | @Override |
| 101 | public boolean dispatchKeyEvent(KeyEvent event) { |
Joe Onorato | 00acb12 | 2009-08-04 16:04:30 -0400 | [diff] [blame] | 102 | return mDragController.dispatchKeyEvent(event) || super.dispatchKeyEvent(event); |
The Android Open Source Project | 31dd503 | 2009-03-03 19:32:27 -0800 | [diff] [blame] | 103 | } |
| 104 | |
Adam Cohen | 3371da0 | 2011-10-25 21:38:29 -0700 | [diff] [blame] | 105 | private boolean isEventOverFolderTextRegion(Folder folder, MotionEvent ev) { |
| 106 | getDescendantRectRelativeToSelf(folder.getEditTextRegion(), mHitRect); |
| 107 | if (mHitRect.contains((int) ev.getX(), (int) ev.getY())) { |
| 108 | return true; |
| 109 | } |
| 110 | return false; |
| 111 | } |
| 112 | |
| 113 | private boolean isEventOverFolder(Folder folder, MotionEvent ev) { |
| 114 | getDescendantRectRelativeToSelf(folder, mHitRect); |
| 115 | if (mHitRect.contains((int) ev.getX(), (int) ev.getY())) { |
| 116 | return true; |
| 117 | } |
| 118 | return false; |
| 119 | } |
| 120 | |
Adam Cohen | 76fc085 | 2011-06-17 13:26:23 -0700 | [diff] [blame] | 121 | private boolean handleTouchDown(MotionEvent ev, boolean intercept) { |
Adam Cohen | 6788269 | 2011-03-11 15:29:03 -0800 | [diff] [blame] | 122 | Rect hitRect = new Rect(); |
| 123 | int x = (int) ev.getX(); |
| 124 | int y = (int) ev.getY(); |
Adam Cohen | 1b607ed | 2011-03-03 17:26:50 -0800 | [diff] [blame] | 125 | |
Adam Cohen | 6788269 | 2011-03-11 15:29:03 -0800 | [diff] [blame] | 126 | for (AppWidgetResizeFrame child: mResizeFrames) { |
| 127 | child.getHitRect(hitRect); |
| 128 | if (hitRect.contains(x, y)) { |
| 129 | if (child.beginResizeIfPointInRegion(x - child.getLeft(), y - child.getTop())) { |
| 130 | mCurrentResizeFrame = child; |
| 131 | mXDown = x; |
| 132 | mYDown = y; |
| 133 | requestDisallowInterceptTouchEvent(true); |
| 134 | return true; |
| 135 | } |
Patrick Dubroy | d1837cc | 2011-03-07 13:38:54 -0800 | [diff] [blame] | 136 | } |
Adam Cohen | 1b607ed | 2011-03-03 17:26:50 -0800 | [diff] [blame] | 137 | } |
Adam Cohen | 76fc085 | 2011-06-17 13:26:23 -0700 | [diff] [blame] | 138 | |
Adam Cohen | 1d9af7d | 2011-06-22 15:26:58 -0700 | [diff] [blame] | 139 | Folder currentFolder = mLauncher.getWorkspace().getOpenFolder(); |
Winson Chung | 7d7541e | 2011-09-16 20:14:36 -0700 | [diff] [blame] | 140 | if (currentFolder != null && !mLauncher.isFolderClingVisible() && intercept) { |
Adam Cohen | 1d9af7d | 2011-06-22 15:26:58 -0700 | [diff] [blame] | 141 | if (currentFolder.isEditingName()) { |
Adam Cohen | 3371da0 | 2011-10-25 21:38:29 -0700 | [diff] [blame] | 142 | if (!isEventOverFolderTextRegion(currentFolder, ev)) { |
Adam Cohen | 1d9af7d | 2011-06-22 15:26:58 -0700 | [diff] [blame] | 143 | currentFolder.dismissEditingName(); |
Adam Cohen | 76fc085 | 2011-06-17 13:26:23 -0700 | [diff] [blame] | 144 | return true; |
Adam Cohen | 2801caf | 2011-05-13 20:57:39 -0700 | [diff] [blame] | 145 | } |
| 146 | } |
Adam Cohen | 76fc085 | 2011-06-17 13:26:23 -0700 | [diff] [blame] | 147 | |
Adam Cohen | 1d9af7d | 2011-06-22 15:26:58 -0700 | [diff] [blame] | 148 | getDescendantRectRelativeToSelf(currentFolder, hitRect); |
Adam Cohen | 3371da0 | 2011-10-25 21:38:29 -0700 | [diff] [blame] | 149 | if (!isEventOverFolder(currentFolder, ev)) { |
Adam Cohen | 76fc085 | 2011-06-17 13:26:23 -0700 | [diff] [blame] | 150 | mLauncher.closeFolder(); |
| 151 | return true; |
| 152 | } |
Adam Cohen | 2801caf | 2011-05-13 20:57:39 -0700 | [diff] [blame] | 153 | } |
Adam Cohen | 6788269 | 2011-03-11 15:29:03 -0800 | [diff] [blame] | 154 | return false; |
| 155 | } |
| 156 | |
| 157 | @Override |
| 158 | public boolean onInterceptTouchEvent(MotionEvent ev) { |
| 159 | if (ev.getAction() == MotionEvent.ACTION_DOWN) { |
Adam Cohen | 76fc085 | 2011-06-17 13:26:23 -0700 | [diff] [blame] | 160 | if (handleTouchDown(ev, true)) { |
Adam Cohen | 6788269 | 2011-03-11 15:29:03 -0800 | [diff] [blame] | 161 | return true; |
| 162 | } |
| 163 | } |
| 164 | clearAllResizeFrames(); |
Joe Onorato | 4db5231 | 2009-10-06 11:17:43 -0700 | [diff] [blame] | 165 | return mDragController.onInterceptTouchEvent(ev); |
The Android Open Source Project | 31dd503 | 2009-03-03 19:32:27 -0800 | [diff] [blame] | 166 | } |
| 167 | |
| 168 | @Override |
Adam Cohen | 3371da0 | 2011-10-25 21:38:29 -0700 | [diff] [blame] | 169 | public boolean onInterceptHoverEvent(MotionEvent ev) { |
Michael Jurka | 023c71e | 2012-10-19 17:32:24 +0200 | [diff] [blame] | 170 | if (mLauncher == null || mLauncher.getWorkspace() == null) { |
| 171 | return false; |
| 172 | } |
Adam Cohen | 3371da0 | 2011-10-25 21:38:29 -0700 | [diff] [blame] | 173 | Folder currentFolder = mLauncher.getWorkspace().getOpenFolder(); |
| 174 | if (currentFolder == null) { |
| 175 | return false; |
| 176 | } else { |
Michael Jurka | 8b805b1 | 2012-04-18 14:23:14 -0700 | [diff] [blame] | 177 | AccessibilityManager accessibilityManager = (AccessibilityManager) |
| 178 | getContext().getSystemService(Context.ACCESSIBILITY_SERVICE); |
| 179 | if (accessibilityManager.isTouchExplorationEnabled()) { |
Adam Cohen | 3371da0 | 2011-10-25 21:38:29 -0700 | [diff] [blame] | 180 | final int action = ev.getAction(); |
| 181 | boolean isOverFolder; |
| 182 | switch (action) { |
| 183 | case MotionEvent.ACTION_HOVER_ENTER: |
| 184 | isOverFolder = isEventOverFolder(currentFolder, ev); |
| 185 | if (!isOverFolder) { |
| 186 | sendTapOutsideFolderAccessibilityEvent(currentFolder.isEditingName()); |
| 187 | mHoverPointClosesFolder = true; |
| 188 | return true; |
| 189 | } else if (isOverFolder) { |
| 190 | mHoverPointClosesFolder = false; |
| 191 | } else { |
| 192 | return true; |
| 193 | } |
| 194 | case MotionEvent.ACTION_HOVER_MOVE: |
| 195 | isOverFolder = isEventOverFolder(currentFolder, ev); |
| 196 | if (!isOverFolder && !mHoverPointClosesFolder) { |
| 197 | sendTapOutsideFolderAccessibilityEvent(currentFolder.isEditingName()); |
| 198 | mHoverPointClosesFolder = true; |
| 199 | return true; |
| 200 | } else if (isOverFolder) { |
| 201 | mHoverPointClosesFolder = false; |
| 202 | } else { |
| 203 | return true; |
| 204 | } |
| 205 | } |
| 206 | } |
| 207 | } |
| 208 | return false; |
| 209 | } |
| 210 | |
| 211 | private void sendTapOutsideFolderAccessibilityEvent(boolean isEditingName) { |
Michael Jurka | 8b805b1 | 2012-04-18 14:23:14 -0700 | [diff] [blame] | 212 | AccessibilityManager accessibilityManager = (AccessibilityManager) |
| 213 | getContext().getSystemService(Context.ACCESSIBILITY_SERVICE); |
| 214 | if (accessibilityManager.isEnabled()) { |
Adam Cohen | 3371da0 | 2011-10-25 21:38:29 -0700 | [diff] [blame] | 215 | int stringId = isEditingName ? R.string.folder_tap_to_rename : R.string.folder_tap_to_close; |
| 216 | AccessibilityEvent event = AccessibilityEvent.obtain( |
| 217 | AccessibilityEvent.TYPE_VIEW_FOCUSED); |
| 218 | onInitializeAccessibilityEvent(event); |
Michael Jurka | 8b805b1 | 2012-04-18 14:23:14 -0700 | [diff] [blame] | 219 | event.getText().add(getContext().getString(stringId)); |
| 220 | accessibilityManager.sendAccessibilityEvent(event); |
Adam Cohen | 3371da0 | 2011-10-25 21:38:29 -0700 | [diff] [blame] | 221 | } |
| 222 | } |
| 223 | |
| 224 | @Override |
| 225 | public boolean onHoverEvent(MotionEvent ev) { |
| 226 | // If we've received this, we've already done the necessary handling |
| 227 | // in onInterceptHoverEvent. Return true to consume the event. |
| 228 | return false; |
| 229 | } |
| 230 | |
| 231 | @Override |
The Android Open Source Project | 31dd503 | 2009-03-03 19:32:27 -0800 | [diff] [blame] | 232 | public boolean onTouchEvent(MotionEvent ev) { |
Adam Cohen | 6788269 | 2011-03-11 15:29:03 -0800 | [diff] [blame] | 233 | boolean handled = false; |
| 234 | int action = ev.getAction(); |
| 235 | |
| 236 | int x = (int) ev.getX(); |
| 237 | int y = (int) ev.getY(); |
| 238 | |
| 239 | if (ev.getAction() == MotionEvent.ACTION_DOWN) { |
| 240 | if (ev.getAction() == MotionEvent.ACTION_DOWN) { |
Adam Cohen | 76fc085 | 2011-06-17 13:26:23 -0700 | [diff] [blame] | 241 | if (handleTouchDown(ev, false)) { |
Adam Cohen | 6788269 | 2011-03-11 15:29:03 -0800 | [diff] [blame] | 242 | return true; |
| 243 | } |
| 244 | } |
| 245 | } |
| 246 | |
| 247 | if (mCurrentResizeFrame != null) { |
| 248 | handled = true; |
| 249 | switch (action) { |
| 250 | case MotionEvent.ACTION_MOVE: |
| 251 | mCurrentResizeFrame.visualizeResizeForDelta(x - mXDown, y - mYDown); |
| 252 | break; |
| 253 | case MotionEvent.ACTION_CANCEL: |
| 254 | case MotionEvent.ACTION_UP: |
Adam Cohen | bebf042 | 2012-04-11 18:06:28 -0700 | [diff] [blame] | 255 | mCurrentResizeFrame.visualizeResizeForDelta(x - mXDown, y - mYDown); |
| 256 | mCurrentResizeFrame.onTouchUp(); |
Adam Cohen | 6788269 | 2011-03-11 15:29:03 -0800 | [diff] [blame] | 257 | mCurrentResizeFrame = null; |
| 258 | } |
| 259 | } |
| 260 | if (handled) return true; |
Joe Onorato | 4db5231 | 2009-10-06 11:17:43 -0700 | [diff] [blame] | 261 | return mDragController.onTouchEvent(ev); |
Romain Guy | 91a9c96 | 2009-06-12 13:52:17 -0700 | [diff] [blame] | 262 | } |
Romain Guy | ea3763c | 2010-01-11 18:02:04 -0800 | [diff] [blame] | 263 | |
Adam Cohen | 35e7e64 | 2011-07-17 14:47:18 -0700 | [diff] [blame] | 264 | /** |
| 265 | * Determine the rect of the descendant in this DragLayer's coordinates |
| 266 | * |
| 267 | * @param descendant The descendant whose coordinates we want to find. |
| 268 | * @param r The rect into which to place the results. |
| 269 | * @return The factor by which this descendant is scaled relative to this DragLayer. |
| 270 | */ |
Adam Cohen | ac8c876 | 2011-07-13 11:15:27 -0700 | [diff] [blame] | 271 | public float getDescendantRectRelativeToSelf(View descendant, Rect r) { |
Adam Cohen | 716b51e | 2011-06-30 12:09:54 -0700 | [diff] [blame] | 272 | mTmpXY[0] = 0; |
| 273 | mTmpXY[1] = 0; |
Adam Cohen | ac8c876 | 2011-07-13 11:15:27 -0700 | [diff] [blame] | 274 | float scale = getDescendantCoordRelativeToSelf(descendant, mTmpXY); |
Adam Cohen | 8dfcba4 | 2011-07-07 16:38:18 -0700 | [diff] [blame] | 275 | r.set(mTmpXY[0], mTmpXY[1], |
| 276 | mTmpXY[0] + descendant.getWidth(), mTmpXY[1] + descendant.getHeight()); |
Adam Cohen | ac8c876 | 2011-07-13 11:15:27 -0700 | [diff] [blame] | 277 | return scale; |
Adam Cohen | 716b51e | 2011-06-30 12:09:54 -0700 | [diff] [blame] | 278 | } |
Adam Cohen | 76fc085 | 2011-06-17 13:26:23 -0700 | [diff] [blame] | 279 | |
Adam Cohen | 307fe23 | 2012-08-16 17:55:58 -0700 | [diff] [blame] | 280 | public float getLocationInDragLayer(View child, int[] loc) { |
Adam Cohen | 35e7e64 | 2011-07-17 14:47:18 -0700 | [diff] [blame] | 281 | loc[0] = 0; |
| 282 | loc[1] = 0; |
Adam Cohen | 307fe23 | 2012-08-16 17:55:58 -0700 | [diff] [blame] | 283 | return getDescendantCoordRelativeToSelf(child, loc); |
Adam Cohen | 35e7e64 | 2011-07-17 14:47:18 -0700 | [diff] [blame] | 284 | } |
| 285 | |
| 286 | /** |
| 287 | * Given a coordinate relative to the descendant, find the coordinate in this DragLayer's |
| 288 | * coordinates. |
| 289 | * |
| 290 | * @param descendant The descendant to which the passed coordinate is relative. |
| 291 | * @param coord The coordinate that we want mapped. |
Adam Cohen | 307fe23 | 2012-08-16 17:55:58 -0700 | [diff] [blame] | 292 | * @return The factor by which this descendant is scaled relative to this DragLayer. Caution |
| 293 | * this scale factor is assumed to be equal in X and Y, and so if at any point this |
| 294 | * assumption fails, we will need to return a pair of scale factors. |
Adam Cohen | 35e7e64 | 2011-07-17 14:47:18 -0700 | [diff] [blame] | 295 | */ |
Adam Cohen | b7e1618 | 2011-07-15 17:55:02 -0700 | [diff] [blame] | 296 | public float getDescendantCoordRelativeToSelf(View descendant, int[] coord) { |
Winson Chung | 557d6ed | 2011-07-08 15:34:52 -0700 | [diff] [blame] | 297 | float scale = 1.0f; |
| 298 | float[] pt = {coord[0], coord[1]}; |
| 299 | descendant.getMatrix().mapPoints(pt); |
| 300 | scale *= descendant.getScaleX(); |
| 301 | pt[0] += descendant.getLeft(); |
| 302 | pt[1] += descendant.getTop(); |
Adam Cohen | 76fc085 | 2011-06-17 13:26:23 -0700 | [diff] [blame] | 303 | ViewParent viewParent = descendant.getParent(); |
| 304 | while (viewParent instanceof View && viewParent != this) { |
| 305 | final View view = (View)viewParent; |
Winson Chung | 557d6ed | 2011-07-08 15:34:52 -0700 | [diff] [blame] | 306 | view.getMatrix().mapPoints(pt); |
| 307 | scale *= view.getScaleX(); |
| 308 | pt[0] += view.getLeft() - view.getScrollX(); |
| 309 | pt[1] += view.getTop() - view.getScrollY(); |
Adam Cohen | 76fc085 | 2011-06-17 13:26:23 -0700 | [diff] [blame] | 310 | viewParent = view.getParent(); |
| 311 | } |
Adam Cohen | fc53cd2 | 2011-07-20 15:45:11 -0700 | [diff] [blame] | 312 | coord[0] = (int) Math.round(pt[0]); |
| 313 | coord[1] = (int) Math.round(pt[1]); |
Winson Chung | 557d6ed | 2011-07-08 15:34:52 -0700 | [diff] [blame] | 314 | return scale; |
Adam Cohen | 76fc085 | 2011-06-17 13:26:23 -0700 | [diff] [blame] | 315 | } |
| 316 | |
Adam Cohen | 3e8f811 | 2011-07-02 18:03:00 -0700 | [diff] [blame] | 317 | public void getViewRectRelativeToSelf(View v, Rect r) { |
| 318 | int[] loc = new int[2]; |
Adam Cohen | 8dfcba4 | 2011-07-07 16:38:18 -0700 | [diff] [blame] | 319 | getLocationInWindow(loc); |
Adam Cohen | 3e8f811 | 2011-07-02 18:03:00 -0700 | [diff] [blame] | 320 | int x = loc[0]; |
| 321 | int y = loc[1]; |
Adam Cohen | 716b51e | 2011-06-30 12:09:54 -0700 | [diff] [blame] | 322 | |
Adam Cohen | 8dfcba4 | 2011-07-07 16:38:18 -0700 | [diff] [blame] | 323 | v.getLocationInWindow(loc); |
Adam Cohen | 3e8f811 | 2011-07-02 18:03:00 -0700 | [diff] [blame] | 324 | int vX = loc[0]; |
| 325 | int vY = loc[1]; |
Adam Cohen | 716b51e | 2011-06-30 12:09:54 -0700 | [diff] [blame] | 326 | |
Adam Cohen | 3e8f811 | 2011-07-02 18:03:00 -0700 | [diff] [blame] | 327 | int left = vX - x; |
| 328 | int top = vY - y; |
| 329 | r.set(left, top, left + v.getMeasuredWidth(), top + v.getMeasuredHeight()); |
Adam Cohen | 716b51e | 2011-06-30 12:09:54 -0700 | [diff] [blame] | 330 | } |
| 331 | |
Romain Guy | ea3763c | 2010-01-11 18:02:04 -0800 | [diff] [blame] | 332 | @Override |
| 333 | public boolean dispatchUnhandledMove(View focused, int direction) { |
| 334 | return mDragController.dispatchUnhandledMove(focused, direction); |
| 335 | } |
Adam Cohen | 120980b | 2010-12-08 11:05:37 -0800 | [diff] [blame] | 336 | |
Adam Cohen | 6788269 | 2011-03-11 15:29:03 -0800 | [diff] [blame] | 337 | public static class LayoutParams extends FrameLayout.LayoutParams { |
| 338 | public int x, y; |
| 339 | public boolean customPosition = false; |
| 340 | |
| 341 | /** |
| 342 | * {@inheritDoc} |
| 343 | */ |
| 344 | public LayoutParams(int width, int height) { |
| 345 | super(width, height); |
| 346 | } |
| 347 | |
| 348 | public void setWidth(int width) { |
| 349 | this.width = width; |
| 350 | } |
| 351 | |
| 352 | public int getWidth() { |
| 353 | return width; |
| 354 | } |
| 355 | |
| 356 | public void setHeight(int height) { |
| 357 | this.height = height; |
| 358 | } |
| 359 | |
| 360 | public int getHeight() { |
| 361 | return height; |
| 362 | } |
| 363 | |
| 364 | public void setX(int x) { |
| 365 | this.x = x; |
| 366 | } |
| 367 | |
| 368 | public int getX() { |
| 369 | return x; |
| 370 | } |
| 371 | |
| 372 | public void setY(int y) { |
| 373 | this.y = y; |
| 374 | } |
| 375 | |
| 376 | public int getY() { |
| 377 | return y; |
| 378 | } |
| 379 | } |
| 380 | |
| 381 | protected void onLayout(boolean changed, int l, int t, int r, int b) { |
| 382 | super.onLayout(changed, l, t, r, b); |
| 383 | int count = getChildCount(); |
| 384 | for (int i = 0; i < count; i++) { |
| 385 | View child = getChildAt(i); |
| 386 | final FrameLayout.LayoutParams flp = (FrameLayout.LayoutParams) child.getLayoutParams(); |
| 387 | if (flp instanceof LayoutParams) { |
| 388 | final LayoutParams lp = (LayoutParams) flp; |
| 389 | if (lp.customPosition) { |
| 390 | child.layout(lp.x, lp.y, lp.x + lp.width, lp.y + lp.height); |
| 391 | } |
| 392 | } |
| 393 | } |
| 394 | } |
| 395 | |
| 396 | public void clearAllResizeFrames() { |
| 397 | if (mResizeFrames.size() > 0) { |
| 398 | for (AppWidgetResizeFrame frame: mResizeFrames) { |
Adam Cohen | bebf042 | 2012-04-11 18:06:28 -0700 | [diff] [blame] | 399 | frame.commitResize(); |
Adam Cohen | 6788269 | 2011-03-11 15:29:03 -0800 | [diff] [blame] | 400 | removeView(frame); |
| 401 | } |
| 402 | mResizeFrames.clear(); |
| 403 | } |
| 404 | } |
| 405 | |
| 406 | public boolean hasResizeFrames() { |
| 407 | return mResizeFrames.size() > 0; |
| 408 | } |
| 409 | |
| 410 | public boolean isWidgetBeingResized() { |
| 411 | return mCurrentResizeFrame != null; |
| 412 | } |
| 413 | |
| 414 | public void addResizeFrame(ItemInfo itemInfo, LauncherAppWidgetHostView widget, |
| 415 | CellLayout cellLayout) { |
| 416 | AppWidgetResizeFrame resizeFrame = new AppWidgetResizeFrame(getContext(), |
Michael Jurka | 3a9fced | 2012-04-13 14:44:29 -0700 | [diff] [blame] | 417 | widget, cellLayout, this); |
Adam Cohen | 6788269 | 2011-03-11 15:29:03 -0800 | [diff] [blame] | 418 | |
| 419 | LayoutParams lp = new LayoutParams(-1, -1); |
| 420 | lp.customPosition = true; |
| 421 | |
| 422 | addView(resizeFrame, lp); |
| 423 | mResizeFrames.add(resizeFrame); |
| 424 | |
| 425 | resizeFrame.snapToWidget(false); |
| 426 | } |
Adam Cohen | 716b51e | 2011-06-30 12:09:54 -0700 | [diff] [blame] | 427 | |
| 428 | public void animateViewIntoPosition(DragView dragView, final View child) { |
Winson Chung | 557d6ed | 2011-07-08 15:34:52 -0700 | [diff] [blame] | 429 | animateViewIntoPosition(dragView, child, null); |
| 430 | } |
Adam Cohen | ac8c876 | 2011-07-13 11:15:27 -0700 | [diff] [blame] | 431 | |
Winson Chung | 7bd1bbb | 2012-02-13 18:29:29 -0800 | [diff] [blame] | 432 | public void animateViewIntoPosition(DragView dragView, final int[] pos, float alpha, |
| 433 | float scaleX, float scaleY, int animationEndStyle, Runnable onFinishRunnable, |
| 434 | int duration) { |
Adam Cohen | b7e1618 | 2011-07-15 17:55:02 -0700 | [diff] [blame] | 435 | Rect r = new Rect(); |
| 436 | getViewRectRelativeToSelf(dragView, r); |
| 437 | final int fromX = r.left; |
| 438 | final int fromY = r.top; |
| 439 | |
Winson Chung | 7bd1bbb | 2012-02-13 18:29:29 -0800 | [diff] [blame] | 440 | animateViewIntoPosition(dragView, fromX, fromY, pos[0], pos[1], alpha, 1, 1, scaleX, scaleY, |
Adam Cohen | ed66b2b | 2012-01-23 17:28:51 -0800 | [diff] [blame] | 441 | onFinishRunnable, animationEndStyle, duration, null); |
| 442 | } |
| 443 | |
Winson Chung | 557d6ed | 2011-07-08 15:34:52 -0700 | [diff] [blame] | 444 | public void animateViewIntoPosition(DragView dragView, final View child, |
| 445 | final Runnable onFinishAnimationRunnable) { |
Adam Cohen | 6441de0 | 2011-12-14 14:25:32 -0800 | [diff] [blame] | 446 | animateViewIntoPosition(dragView, child, -1, onFinishAnimationRunnable, null); |
Adam Cohen | ed51cc9 | 2011-08-01 20:28:08 -0700 | [diff] [blame] | 447 | } |
| 448 | |
| 449 | public void animateViewIntoPosition(DragView dragView, final View child, int duration, |
Adam Cohen | 6441de0 | 2011-12-14 14:25:32 -0800 | [diff] [blame] | 450 | final Runnable onFinishAnimationRunnable, View anchorView) { |
Michael Jurka | a52570f | 2012-03-20 03:18:20 -0700 | [diff] [blame] | 451 | ShortcutAndWidgetContainer parentChildren = (ShortcutAndWidgetContainer) child.getParent(); |
Adam Cohen | 716b51e | 2011-06-30 12:09:54 -0700 | [diff] [blame] | 452 | CellLayout.LayoutParams lp = (CellLayout.LayoutParams) child.getLayoutParams(); |
Winson Chung | eecf02d | 2012-03-02 17:14:58 -0800 | [diff] [blame] | 453 | parentChildren.measureChild(child); |
Adam Cohen | 716b51e | 2011-06-30 12:09:54 -0700 | [diff] [blame] | 454 | |
Adam Cohen | 3e8f811 | 2011-07-02 18:03:00 -0700 | [diff] [blame] | 455 | Rect r = new Rect(); |
| 456 | getViewRectRelativeToSelf(dragView, r); |
Adam Cohen | 716b51e | 2011-06-30 12:09:54 -0700 | [diff] [blame] | 457 | |
| 458 | int coord[] = new int[2]; |
Adam Cohen | 307fe23 | 2012-08-16 17:55:58 -0700 | [diff] [blame] | 459 | float childScale = child.getScaleX(); |
| 460 | coord[0] = lp.x + (int) (child.getMeasuredWidth() * (1 - childScale) / 2); |
| 461 | coord[1] = lp.y + (int) (child.getMeasuredHeight() * (1 - childScale) / 2); |
Winson Chung | eecf02d | 2012-03-02 17:14:58 -0800 | [diff] [blame] | 462 | |
Adam Cohen | 8dfcba4 | 2011-07-07 16:38:18 -0700 | [diff] [blame] | 463 | // Since the child hasn't necessarily been laid out, we force the lp to be updated with |
Adam Cohen | ac8c876 | 2011-07-13 11:15:27 -0700 | [diff] [blame] | 464 | // the correct coordinates (above) and use these to determine the final location |
Winson Chung | 557d6ed | 2011-07-08 15:34:52 -0700 | [diff] [blame] | 465 | float scale = getDescendantCoordRelativeToSelf((View) child.getParent(), coord); |
Adam Cohen | 307fe23 | 2012-08-16 17:55:58 -0700 | [diff] [blame] | 466 | // We need to account for the scale of the child itself, as the above only accounts for |
| 467 | // for the scale in parents. |
| 468 | scale *= childScale; |
Adam Cohen | ac8c876 | 2011-07-13 11:15:27 -0700 | [diff] [blame] | 469 | int toX = coord[0]; |
| 470 | int toY = coord[1]; |
Andrew Flynn | 4497ebf | 2012-05-09 11:28:00 -0700 | [diff] [blame] | 471 | if (child instanceof TextView) { |
| 472 | TextView tv = (TextView) child; |
| 473 | |
| 474 | // The child may be scaled (always about the center of the view) so to account for it, |
| 475 | // we have to offset the position by the scaled size. Once we do that, we can center |
| 476 | // the drag view about the scaled child view. |
| 477 | toY += Math.round(scale * tv.getPaddingTop()); |
| 478 | toY -= dragView.getMeasuredHeight() * (1 - scale) / 2; |
| 479 | toX -= (dragView.getMeasuredWidth() - Math.round(scale * child.getMeasuredWidth())) / 2; |
| 480 | } else if (child instanceof FolderIcon) { |
Winson Chung | ea359c6 | 2011-08-03 17:06:35 -0700 | [diff] [blame] | 481 | // Account for holographic blur padding on the drag view |
Adam Cohen | 307fe23 | 2012-08-16 17:55:58 -0700 | [diff] [blame] | 482 | toY -= scale * Workspace.DRAG_BITMAP_PADDING / 2; |
| 483 | toY -= (1 - scale) * dragView.getMeasuredHeight() / 2; |
Winson Chung | ea359c6 | 2011-08-03 17:06:35 -0700 | [diff] [blame] | 484 | // Center in the x coordinate about the target's drawable |
| 485 | toX -= (dragView.getMeasuredWidth() - Math.round(scale * child.getMeasuredWidth())) / 2; |
Adam Cohen | ac8c876 | 2011-07-13 11:15:27 -0700 | [diff] [blame] | 486 | } else { |
| 487 | toY -= (Math.round(scale * (dragView.getHeight() - child.getMeasuredHeight()))) / 2; |
| 488 | toX -= (Math.round(scale * (dragView.getMeasuredWidth() |
| 489 | - child.getMeasuredWidth()))) / 2; |
| 490 | } |
| 491 | |
| 492 | final int fromX = r.left; |
| 493 | final int fromY = r.top; |
Adam Cohen | 716b51e | 2011-06-30 12:09:54 -0700 | [diff] [blame] | 494 | child.setVisibility(INVISIBLE); |
Adam Cohen | 8dfcba4 | 2011-07-07 16:38:18 -0700 | [diff] [blame] | 495 | Runnable onCompleteRunnable = new Runnable() { |
| 496 | public void run() { |
| 497 | child.setVisibility(VISIBLE); |
Adam Cohen | d41fbf5 | 2012-02-16 23:53:59 -0800 | [diff] [blame] | 498 | if (onFinishAnimationRunnable != null) { |
| 499 | onFinishAnimationRunnable.run(); |
| 500 | } |
Adam Cohen | 8dfcba4 | 2011-07-07 16:38:18 -0700 | [diff] [blame] | 501 | } |
| 502 | }; |
Adam Cohen | ed66b2b | 2012-01-23 17:28:51 -0800 | [diff] [blame] | 503 | animateViewIntoPosition(dragView, fromX, fromY, toX, toY, 1, 1, 1, scale, scale, |
Adam Cohen | d41fbf5 | 2012-02-16 23:53:59 -0800 | [diff] [blame] | 504 | onCompleteRunnable, ANIMATION_END_DISAPPEAR, duration, anchorView); |
Winson Chung | 557d6ed | 2011-07-08 15:34:52 -0700 | [diff] [blame] | 505 | } |
| 506 | |
Adam Cohen | d41fbf5 | 2012-02-16 23:53:59 -0800 | [diff] [blame] | 507 | public void animateViewIntoPosition(final DragView view, final int fromX, final int fromY, |
Adam Cohen | ed66b2b | 2012-01-23 17:28:51 -0800 | [diff] [blame] | 508 | final int toX, final int toY, float finalAlpha, float initScaleX, float initScaleY, |
| 509 | float finalScaleX, float finalScaleY, Runnable onCompleteRunnable, |
| 510 | int animationEndStyle, int duration, View anchorView) { |
Adam Cohen | 8dfcba4 | 2011-07-07 16:38:18 -0700 | [diff] [blame] | 511 | Rect from = new Rect(fromX, fromY, fromX + |
| 512 | view.getMeasuredWidth(), fromY + view.getMeasuredHeight()); |
Adam Cohen | 3e8f811 | 2011-07-02 18:03:00 -0700 | [diff] [blame] | 513 | Rect to = new Rect(toX, toY, toX + view.getMeasuredWidth(), toY + view.getMeasuredHeight()); |
Adam Cohen | ed66b2b | 2012-01-23 17:28:51 -0800 | [diff] [blame] | 514 | animateView(view, from, to, finalAlpha, initScaleX, initScaleY, finalScaleX, finalScaleY, duration, |
| 515 | null, null, onCompleteRunnable, animationEndStyle, anchorView); |
Adam Cohen | 3e8f811 | 2011-07-02 18:03:00 -0700 | [diff] [blame] | 516 | } |
| 517 | |
Adam Cohen | b7e1618 | 2011-07-15 17:55:02 -0700 | [diff] [blame] | 518 | /** |
| 519 | * This method animates a view at the end of a drag and drop animation. |
| 520 | * |
| 521 | * @param view The view to be animated. This view is drawn directly into DragLayer, and so |
| 522 | * doesn't need to be a child of DragLayer. |
| 523 | * @param from The initial location of the view. Only the left and top parameters are used. |
| 524 | * @param to The final location of the view. Only the left and top parameters are used. This |
| 525 | * location doesn't account for scaling, and so should be centered about the desired |
| 526 | * final location (including scaling). |
| 527 | * @param finalAlpha The final alpha of the view, in case we want it to fade as it animates. |
| 528 | * @param finalScale The final scale of the view. The view is scaled about its center. |
| 529 | * @param duration The duration of the animation. |
| 530 | * @param motionInterpolator The interpolator to use for the location of the view. |
| 531 | * @param alphaInterpolator The interpolator to use for the alpha of the view. |
| 532 | * @param onCompleteRunnable Optional runnable to run on animation completion. |
| 533 | * @param fadeOut Whether or not to fade out the view once the animation completes. If true, |
| 534 | * the runnable will execute after the view is faded out. |
Adam Cohen | 6441de0 | 2011-12-14 14:25:32 -0800 | [diff] [blame] | 535 | * @param anchorView If not null, this represents the view which the animated view stays |
| 536 | * anchored to in case scrolling is currently taking place. Note: currently this is |
| 537 | * only used for the X dimension for the case of the workspace. |
Adam Cohen | b7e1618 | 2011-07-15 17:55:02 -0700 | [diff] [blame] | 538 | */ |
Winson Chung | 7bd1bbb | 2012-02-13 18:29:29 -0800 | [diff] [blame] | 539 | public void animateView(final DragView view, final Rect from, final Rect to, |
| 540 | final float finalAlpha, final float initScaleX, final float initScaleY, |
| 541 | final float finalScaleX, final float finalScaleY, int duration, |
| 542 | final Interpolator motionInterpolator, final Interpolator alphaInterpolator, |
| 543 | final Runnable onCompleteRunnable, final int animationEndStyle, View anchorView) { |
| 544 | |
Adam Cohen | 716b51e | 2011-06-30 12:09:54 -0700 | [diff] [blame] | 545 | // Calculate the duration of the animation based on the object's distance |
Adam Cohen | 3e8f811 | 2011-07-02 18:03:00 -0700 | [diff] [blame] | 546 | final float dist = (float) Math.sqrt(Math.pow(to.left - from.left, 2) + |
| 547 | Math.pow(to.top - from.top, 2)); |
Adam Cohen | 716b51e | 2011-06-30 12:09:54 -0700 | [diff] [blame] | 548 | final Resources res = getResources(); |
| 549 | final float maxDist = (float) res.getInteger(R.integer.config_dropAnimMaxDist); |
Adam Cohen | 3e8f811 | 2011-07-02 18:03:00 -0700 | [diff] [blame] | 550 | |
| 551 | // If duration < 0, this is a cue to compute the duration based on the distance |
| 552 | if (duration < 0) { |
| 553 | duration = res.getInteger(R.integer.config_dropAnimMaxDuration); |
| 554 | if (dist < maxDist) { |
Adam Cohen | 8dfcba4 | 2011-07-07 16:38:18 -0700 | [diff] [blame] | 555 | duration *= mCubicEaseOutInterpolator.getInterpolation(dist / maxDist); |
Adam Cohen | 3e8f811 | 2011-07-02 18:03:00 -0700 | [diff] [blame] | 556 | } |
Winson Chung | 61b0c69 | 2012-02-23 16:31:13 -0800 | [diff] [blame] | 557 | duration = Math.max(duration, res.getInteger(R.integer.config_dropAnimMinDuration)); |
Adam Cohen | 716b51e | 2011-06-30 12:09:54 -0700 | [diff] [blame] | 558 | } |
| 559 | |
Winson Chung | 043f2af | 2012-03-01 16:09:54 -0800 | [diff] [blame] | 560 | // Fall back to cubic ease out interpolator for the animation if none is specified |
| 561 | TimeInterpolator interpolator = null; |
Adam Cohen | 3e8f811 | 2011-07-02 18:03:00 -0700 | [diff] [blame] | 562 | if (alphaInterpolator == null || motionInterpolator == null) { |
Winson Chung | 043f2af | 2012-03-01 16:09:54 -0800 | [diff] [blame] | 563 | interpolator = mCubicEaseOutInterpolator; |
Adam Cohen | 3e8f811 | 2011-07-02 18:03:00 -0700 | [diff] [blame] | 564 | } |
Adam Cohen | 716b51e | 2011-06-30 12:09:54 -0700 | [diff] [blame] | 565 | |
Winson Chung | 043f2af | 2012-03-01 16:09:54 -0800 | [diff] [blame] | 566 | // Animate the view |
Winson Chung | 7bd1bbb | 2012-02-13 18:29:29 -0800 | [diff] [blame] | 567 | final float initAlpha = view.getAlpha(); |
Winson Chung | 043f2af | 2012-03-01 16:09:54 -0800 | [diff] [blame] | 568 | final float dropViewScale = view.getScaleX(); |
| 569 | AnimatorUpdateListener updateCb = new AnimatorUpdateListener() { |
| 570 | @Override |
Adam Cohen | 716b51e | 2011-06-30 12:09:54 -0700 | [diff] [blame] | 571 | public void onAnimationUpdate(ValueAnimator animation) { |
| 572 | final float percent = (Float) animation.getAnimatedValue(); |
Winson Chung | 7bd1bbb | 2012-02-13 18:29:29 -0800 | [diff] [blame] | 573 | final int width = view.getMeasuredWidth(); |
| 574 | final int height = view.getMeasuredHeight(); |
Adam Cohen | 716b51e | 2011-06-30 12:09:54 -0700 | [diff] [blame] | 575 | |
Adam Cohen | 3e8f811 | 2011-07-02 18:03:00 -0700 | [diff] [blame] | 576 | float alphaPercent = alphaInterpolator == null ? percent : |
| 577 | alphaInterpolator.getInterpolation(percent); |
| 578 | float motionPercent = motionInterpolator == null ? percent : |
| 579 | motionInterpolator.getInterpolation(percent); |
| 580 | |
Winson Chung | 7bd1bbb | 2012-02-13 18:29:29 -0800 | [diff] [blame] | 581 | float initialScaleX = initScaleX * dropViewScale; |
| 582 | float initialScaleY = initScaleY * dropViewScale; |
| 583 | float scaleX = finalScaleX * percent + initialScaleX * (1 - percent); |
| 584 | float scaleY = finalScaleY * percent + initialScaleY * (1 - percent); |
| 585 | float alpha = finalAlpha * alphaPercent + initAlpha * (1 - alphaPercent); |
| 586 | |
| 587 | float fromLeft = from.left + (initialScaleX - 1f) * width / 2; |
| 588 | float fromTop = from.top + (initialScaleY - 1f) * height / 2; |
| 589 | |
| 590 | int x = (int) (fromLeft + Math.round(((to.left - fromLeft) * motionPercent))); |
| 591 | int y = (int) (fromTop + Math.round(((to.top - fromTop) * motionPercent))); |
| 592 | |
| 593 | int xPos = x - mDropView.getScrollX() + (mAnchorView != null |
| 594 | ? (mAnchorViewInitialScrollX - mAnchorView.getScrollX()) : 0); |
| 595 | int yPos = y - mDropView.getScrollY(); |
| 596 | |
| 597 | mDropView.setTranslationX(xPos); |
| 598 | mDropView.setTranslationY(yPos); |
| 599 | mDropView.setScaleX(scaleX); |
| 600 | mDropView.setScaleY(scaleY); |
| 601 | mDropView.setAlpha(alpha); |
Adam Cohen | 716b51e | 2011-06-30 12:09:54 -0700 | [diff] [blame] | 602 | } |
Winson Chung | 043f2af | 2012-03-01 16:09:54 -0800 | [diff] [blame] | 603 | }; |
| 604 | animateView(view, updateCb, duration, interpolator, onCompleteRunnable, animationEndStyle, |
| 605 | anchorView); |
| 606 | } |
| 607 | |
| 608 | public void animateView(final DragView view, AnimatorUpdateListener updateCb, int duration, |
| 609 | TimeInterpolator interpolator, final Runnable onCompleteRunnable, |
| 610 | final int animationEndStyle, View anchorView) { |
| 611 | // Clean up the previous animations |
| 612 | if (mDropAnim != null) mDropAnim.cancel(); |
| 613 | if (mFadeOutAnim != null) mFadeOutAnim.cancel(); |
| 614 | |
| 615 | // Show the drop view if it was previously hidden |
| 616 | mDropView = view; |
| 617 | mDropView.cancelAnimation(); |
| 618 | mDropView.resetLayoutParams(); |
| 619 | |
| 620 | // Set the anchor view if the page is scrolling |
| 621 | if (anchorView != null) { |
| 622 | mAnchorViewInitialScrollX = anchorView.getScrollX(); |
| 623 | } |
| 624 | mAnchorView = anchorView; |
| 625 | |
| 626 | // Create and start the animation |
| 627 | mDropAnim = new ValueAnimator(); |
| 628 | mDropAnim.setInterpolator(interpolator); |
| 629 | mDropAnim.setDuration(duration); |
| 630 | mDropAnim.setFloatValues(0f, 1f); |
| 631 | mDropAnim.addUpdateListener(updateCb); |
Adam Cohen | 3e8f811 | 2011-07-02 18:03:00 -0700 | [diff] [blame] | 632 | mDropAnim.addListener(new AnimatorListenerAdapter() { |
| 633 | public void onAnimationEnd(Animator animation) { |
Adam Cohen | 8dfcba4 | 2011-07-07 16:38:18 -0700 | [diff] [blame] | 634 | if (onCompleteRunnable != null) { |
| 635 | onCompleteRunnable.run(); |
| 636 | } |
Adam Cohen | ed66b2b | 2012-01-23 17:28:51 -0800 | [diff] [blame] | 637 | switch (animationEndStyle) { |
| 638 | case ANIMATION_END_DISAPPEAR: |
| 639 | clearAnimatedView(); |
| 640 | break; |
| 641 | case ANIMATION_END_FADE_OUT: |
Adam Cohen | 8dfcba4 | 2011-07-07 16:38:18 -0700 | [diff] [blame] | 642 | fadeOutDragView(); |
Adam Cohen | ed66b2b | 2012-01-23 17:28:51 -0800 | [diff] [blame] | 643 | break; |
| 644 | case ANIMATION_END_REMAIN_VISIBLE: |
| 645 | break; |
Adam Cohen | 3e8f811 | 2011-07-02 18:03:00 -0700 | [diff] [blame] | 646 | } |
| 647 | } |
| 648 | }); |
Adam Cohen | 716b51e | 2011-06-30 12:09:54 -0700 | [diff] [blame] | 649 | mDropAnim.start(); |
| 650 | } |
| 651 | |
Adam Cohen | ed66b2b | 2012-01-23 17:28:51 -0800 | [diff] [blame] | 652 | public void clearAnimatedView() { |
Winson Chung | 7bd1bbb | 2012-02-13 18:29:29 -0800 | [diff] [blame] | 653 | if (mDropAnim != null) { |
| 654 | mDropAnim.cancel(); |
| 655 | } |
| 656 | if (mDropView != null) { |
Winson Chung | 043f2af | 2012-03-01 16:09:54 -0800 | [diff] [blame] | 657 | mDragController.onDeferredEndDrag(mDropView); |
Winson Chung | 7bd1bbb | 2012-02-13 18:29:29 -0800 | [diff] [blame] | 658 | } |
Adam Cohen | ed66b2b | 2012-01-23 17:28:51 -0800 | [diff] [blame] | 659 | mDropView = null; |
Adam Cohen | ed66b2b | 2012-01-23 17:28:51 -0800 | [diff] [blame] | 660 | invalidate(); |
| 661 | } |
| 662 | |
| 663 | public View getAnimatedView() { |
| 664 | return mDropView; |
| 665 | } |
| 666 | |
Adam Cohen | 8dfcba4 | 2011-07-07 16:38:18 -0700 | [diff] [blame] | 667 | private void fadeOutDragView() { |
| 668 | mFadeOutAnim = new ValueAnimator(); |
| 669 | mFadeOutAnim.setDuration(150); |
| 670 | mFadeOutAnim.setFloatValues(0f, 1f); |
| 671 | mFadeOutAnim.removeAllUpdateListeners(); |
| 672 | mFadeOutAnim.addUpdateListener(new AnimatorUpdateListener() { |
| 673 | public void onAnimationUpdate(ValueAnimator animation) { |
| 674 | final float percent = (Float) animation.getAnimatedValue(); |
Winson Chung | 7bd1bbb | 2012-02-13 18:29:29 -0800 | [diff] [blame] | 675 | |
| 676 | float alpha = 1 - percent; |
| 677 | mDropView.setAlpha(alpha); |
Adam Cohen | 8dfcba4 | 2011-07-07 16:38:18 -0700 | [diff] [blame] | 678 | } |
| 679 | }); |
| 680 | mFadeOutAnim.addListener(new AnimatorListenerAdapter() { |
| 681 | public void onAnimationEnd(Animator animation) { |
Winson Chung | 7bd1bbb | 2012-02-13 18:29:29 -0800 | [diff] [blame] | 682 | if (mDropView != null) { |
Winson Chung | 043f2af | 2012-03-01 16:09:54 -0800 | [diff] [blame] | 683 | mDragController.onDeferredEndDrag(mDropView); |
Winson Chung | 7bd1bbb | 2012-02-13 18:29:29 -0800 | [diff] [blame] | 684 | } |
Adam Cohen | 8dfcba4 | 2011-07-07 16:38:18 -0700 | [diff] [blame] | 685 | mDropView = null; |
Adam Cohen | ed66b2b | 2012-01-23 17:28:51 -0800 | [diff] [blame] | 686 | invalidate(); |
Adam Cohen | 8dfcba4 | 2011-07-07 16:38:18 -0700 | [diff] [blame] | 687 | } |
| 688 | }); |
| 689 | mFadeOutAnim.start(); |
| 690 | } |
| 691 | |
Adam Cohen | 716b51e | 2011-06-30 12:09:54 -0700 | [diff] [blame] | 692 | @Override |
Michael Jurka | 8b805b1 | 2012-04-18 14:23:14 -0700 | [diff] [blame] | 693 | public void onChildViewAdded(View parent, View child) { |
Adam Cohen | 21b4110 | 2011-11-01 17:29:52 -0700 | [diff] [blame] | 694 | updateChildIndices(); |
| 695 | } |
| 696 | |
| 697 | @Override |
Michael Jurka | 8b805b1 | 2012-04-18 14:23:14 -0700 | [diff] [blame] | 698 | public void onChildViewRemoved(View parent, View child) { |
Adam Cohen | 21b4110 | 2011-11-01 17:29:52 -0700 | [diff] [blame] | 699 | updateChildIndices(); |
| 700 | } |
| 701 | |
| 702 | private void updateChildIndices() { |
| 703 | if (mLauncher != null) { |
| 704 | mWorkspaceIndex = indexOfChild(mLauncher.getWorkspace()); |
Adam Cohen | ebea84d | 2011-11-09 17:20:41 -0800 | [diff] [blame] | 705 | mQsbIndex = indexOfChild(mLauncher.getSearchBar()); |
Adam Cohen | 21b4110 | 2011-11-01 17:29:52 -0700 | [diff] [blame] | 706 | } |
| 707 | } |
| 708 | |
| 709 | @Override |
| 710 | protected int getChildDrawingOrder(int childCount, int i) { |
Adam Cohen | e29c55a | 2012-08-23 12:18:16 -0700 | [diff] [blame] | 711 | // TODO: We have turned off this custom drawing order because it now effects touch |
| 712 | // dispatch order. We need to sort that issue out and then decide how to go about this. |
| 713 | if (true || LauncherApplication.isScreenLandscape(getContext()) || |
| 714 | mWorkspaceIndex == -1 || mQsbIndex == -1 || |
Adam Cohen | 21b4110 | 2011-11-01 17:29:52 -0700 | [diff] [blame] | 715 | mLauncher.getWorkspace().isDrawingBackgroundGradient()) { |
| 716 | return i; |
| 717 | } |
| 718 | |
| 719 | // This ensures that the workspace is drawn above the hotseat and qsb, |
| 720 | // except when the workspace is drawing a background gradient, in which |
| 721 | // case we want the workspace to stay behind these elements. |
Adam Cohen | ebea84d | 2011-11-09 17:20:41 -0800 | [diff] [blame] | 722 | if (i == mQsbIndex) { |
Adam Cohen | 21b4110 | 2011-11-01 17:29:52 -0700 | [diff] [blame] | 723 | return mWorkspaceIndex; |
| 724 | } else if (i == mWorkspaceIndex) { |
Adam Cohen | ebea84d | 2011-11-09 17:20:41 -0800 | [diff] [blame] | 725 | return mQsbIndex; |
Adam Cohen | 21b4110 | 2011-11-01 17:29:52 -0700 | [diff] [blame] | 726 | } else { |
| 727 | return i; |
| 728 | } |
| 729 | } |
Winson Chung | 360e63f | 2012-04-27 13:48:05 -0700 | [diff] [blame] | 730 | |
| 731 | private boolean mInScrollArea; |
| 732 | private Drawable mLeftHoverDrawable; |
| 733 | private Drawable mRightHoverDrawable; |
| 734 | |
| 735 | void onEnterScrollArea(int direction) { |
| 736 | mInScrollArea = true; |
| 737 | invalidate(); |
| 738 | } |
| 739 | |
| 740 | void onExitScrollArea() { |
| 741 | mInScrollArea = false; |
| 742 | invalidate(); |
| 743 | } |
| 744 | |
Winson Chung | fe1fe26 | 2013-04-01 16:52:31 -0700 | [diff] [blame^] | 745 | /** |
| 746 | * Note: this is a reimplementation of View.isLayoutRtl() since that is currently hidden api. |
| 747 | */ |
| 748 | private boolean isLayoutRtl() { |
| 749 | return (getLayoutDirection() == LAYOUT_DIRECTION_RTL); |
| 750 | } |
| 751 | |
Winson Chung | 360e63f | 2012-04-27 13:48:05 -0700 | [diff] [blame] | 752 | @Override |
| 753 | protected void dispatchDraw(Canvas canvas) { |
| 754 | super.dispatchDraw(canvas); |
| 755 | |
| 756 | if (mInScrollArea && !LauncherApplication.isScreenLarge()) { |
| 757 | Workspace workspace = mLauncher.getWorkspace(); |
| 758 | int width = workspace.getWidth(); |
| 759 | Rect childRect = new Rect(); |
| 760 | getDescendantRectRelativeToSelf(workspace.getChildAt(0), childRect); |
| 761 | |
| 762 | int page = workspace.getNextPage(); |
Winson Chung | fe1fe26 | 2013-04-01 16:52:31 -0700 | [diff] [blame^] | 763 | final boolean isRtl = isLayoutRtl(); |
| 764 | CellLayout leftPage = (CellLayout) workspace.getChildAt(isRtl ? page + 1 : page - 1); |
| 765 | CellLayout rightPage = (CellLayout) workspace.getChildAt(isRtl ? page - 1 : page + 1); |
Winson Chung | 360e63f | 2012-04-27 13:48:05 -0700 | [diff] [blame] | 766 | |
| 767 | if (leftPage != null && leftPage.getIsDragOverlapping()) { |
| 768 | mLeftHoverDrawable.setBounds(0, childRect.top, |
| 769 | mLeftHoverDrawable.getIntrinsicWidth(), childRect.bottom); |
| 770 | mLeftHoverDrawable.draw(canvas); |
| 771 | } else if (rightPage != null && rightPage.getIsDragOverlapping()) { |
| 772 | mRightHoverDrawable.setBounds(width - mRightHoverDrawable.getIntrinsicWidth(), |
| 773 | childRect.top, width, childRect.bottom); |
| 774 | mRightHoverDrawable.draw(canvas); |
| 775 | } |
| 776 | } |
| 777 | } |
The Android Open Source Project | 31dd503 | 2009-03-03 19:32:27 -0800 | [diff] [blame] | 778 | } |