OLD | NEW |
(Empty) | |
| 1 // Copyright 2012 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. |
| 4 |
| 5 package org.chromium.content.browser.input; |
| 6 |
| 7 import android.content.Context; |
| 8 import android.graphics.Canvas; |
| 9 import android.graphics.drawable.Drawable; |
| 10 import android.view.MotionEvent; |
| 11 import android.view.View; |
| 12 import android.widget.PopupWindow; |
| 13 |
| 14 import org.chromium.base.CalledByNative; |
| 15 import org.chromium.base.JNINamespace; |
| 16 import org.chromium.content.browser.PositionObserver; |
| 17 |
| 18 import java.lang.ref.WeakReference; |
| 19 |
| 20 /** |
| 21 * View that displays a selection or insertion handle for text editing. |
| 22 * |
| 23 * While a HandleView is logically a child of some other view, it does not exist
in that View's |
| 24 * hierarchy. |
| 25 * |
| 26 */ |
| 27 @JNINamespace("content") |
| 28 public class PopupTouchHandleDrawable extends View { |
| 29 private Drawable mDrawable; |
| 30 private final PopupWindow mContainer; |
| 31 private final Context mContext; |
| 32 private final PositionObserver.Listener mParentPositionListener; |
| 33 |
| 34 // The weak delegate reference allows the PopupTouchHandleDrawable to be own
ed by a native |
| 35 // object that might have a different lifetime (or a cyclic lifetime) with r
espect to the |
| 36 // delegate, allowing garbage collection of any Java references. |
| 37 private final WeakReference<PopupTouchHandleDrawableDelegate> mDelegate; |
| 38 |
| 39 // The observer reference will only be non-null while it is attached to mPar
entPositionListener. |
| 40 private PositionObserver mParentPositionObserver; |
| 41 |
| 42 // The position of the handle relative to the parent view. |
| 43 private int mPositionX; |
| 44 private int mPositionY; |
| 45 |
| 46 // The position of the parent relative to the application's root view. |
| 47 private int mParentPositionX; |
| 48 private int mParentPositionY; |
| 49 |
| 50 // The offset from this handles position to the "tip" of the handle. |
| 51 private float mHotspotX; |
| 52 private float mHotspotY; |
| 53 |
| 54 private float mAlpha; |
| 55 |
| 56 private final int[] mTempScreenCoords = new int[2]; |
| 57 |
| 58 static final int LEFT = 0; |
| 59 static final int CENTER = 1; |
| 60 static final int RIGHT = 2; |
| 61 private int mOrientation = -1; |
| 62 |
| 63 /** |
| 64 * Provides additional interaction behaviors necessary for handle |
| 65 * manipulation and interaction. |
| 66 */ |
| 67 public interface PopupTouchHandleDrawableDelegate { |
| 68 /** |
| 69 * @return The parent View of the PopupWindow. |
| 70 */ |
| 71 View getParent(); |
| 72 |
| 73 /** |
| 74 * @return A position observer for the parent View, used to keep the |
| 75 * absolutely positioned PopupWindow in-sync with the parent. |
| 76 */ |
| 77 PositionObserver getParentPositionObserver(); |
| 78 |
| 79 /** |
| 80 * Should route MotionEvents to the appropriate logic layer for |
| 81 * performing handle manipulation. |
| 82 */ |
| 83 boolean onTouchHandleEvent(MotionEvent ev); |
| 84 } |
| 85 |
| 86 public PopupTouchHandleDrawable(PopupTouchHandleDrawableDelegate delegate) { |
| 87 super(delegate.getParent().getContext()); |
| 88 mContext = delegate.getParent().getContext(); |
| 89 mDelegate = new WeakReference<PopupTouchHandleDrawableDelegate>(delegate
); |
| 90 mContainer = new PopupWindow(mContext, null, android.R.attr.textSelectHa
ndleWindowStyle); |
| 91 mContainer.setSplitTouchEnabled(true); |
| 92 mContainer.setClippingEnabled(false); |
| 93 mAlpha = 1.f; |
| 94 mParentPositionListener = new PositionObserver.Listener() { |
| 95 @Override |
| 96 public void onPositionChanged(int x, int y) { |
| 97 updateParentPosition(x, y); |
| 98 } |
| 99 }; |
| 100 } |
| 101 |
| 102 @Override |
| 103 public boolean onTouchEvent(MotionEvent event) { |
| 104 final PopupTouchHandleDrawableDelegate delegate = mDelegate.get(); |
| 105 if (delegate == null) { |
| 106 // If the delegate is gone, we should immediately dispose of the pop
up. |
| 107 hide(); |
| 108 return false; |
| 109 } |
| 110 |
| 111 // Convert from PopupWindow local coordinates to |
| 112 // parent view local coordinates prior to forwarding. |
| 113 delegate.getParent().getLocationOnScreen(mTempScreenCoords); |
| 114 final float offsetX = event.getRawX() - event.getX() - mTempScreenCoords
[0]; |
| 115 final float offsetY = event.getRawY() - event.getY() - mTempScreenCoords
[1]; |
| 116 final MotionEvent offsetEvent = MotionEvent.obtainNoHistory(event); |
| 117 offsetEvent.offsetLocation(offsetX, offsetY); |
| 118 final boolean handled = delegate.onTouchHandleEvent(offsetEvent); |
| 119 offsetEvent.recycle(); |
| 120 return handled; |
| 121 } |
| 122 |
| 123 private void setOrientation(int orientation) { |
| 124 assert orientation >= LEFT && orientation <= RIGHT; |
| 125 if (mOrientation == orientation) return; |
| 126 |
| 127 final boolean hadValidOrientation = mOrientation != -1; |
| 128 mOrientation = orientation; |
| 129 |
| 130 final int oldAdjustedPositionX = getAdjustedPositionX(); |
| 131 final int oldAdjustedPositionY = getAdjustedPositionY(); |
| 132 |
| 133 switch (orientation) { |
| 134 case LEFT: { |
| 135 mDrawable = HandleViewResources.getLeftHandleDrawable(mContext); |
| 136 mHotspotX = (mDrawable.getIntrinsicWidth() * 3) / 4f; |
| 137 break; |
| 138 } |
| 139 |
| 140 case RIGHT: { |
| 141 mDrawable = HandleViewResources.getRightHandleDrawable(mContext)
; |
| 142 mHotspotX = mDrawable.getIntrinsicWidth() / 4f; |
| 143 break; |
| 144 } |
| 145 |
| 146 case CENTER: |
| 147 default: { |
| 148 mDrawable = HandleViewResources.getCenterHandleDrawable(mContext
); |
| 149 mHotspotX = mDrawable.getIntrinsicWidth() / 2f; |
| 150 break; |
| 151 } |
| 152 } |
| 153 mHotspotY = 0; |
| 154 |
| 155 // Force handle repositioning to accommodate the new orientation's hotsp
ot. |
| 156 if (hadValidOrientation) positionAt(oldAdjustedPositionX, oldAdjustedPos
itionY); |
| 157 mDrawable.setAlpha((int) (255 * mAlpha)); |
| 158 |
| 159 invalidate(); |
| 160 } |
| 161 |
| 162 private void updateParentPosition(int parentPositionX, int parentPositionY)
{ |
| 163 mParentPositionX = parentPositionX; |
| 164 mParentPositionY = parentPositionY; |
| 165 onPositionChanged(); |
| 166 } |
| 167 |
| 168 private int getContainerPositionX() { |
| 169 return mParentPositionX + mPositionX; |
| 170 } |
| 171 |
| 172 private int getContainerPositionY() { |
| 173 return mParentPositionY + mPositionY; |
| 174 } |
| 175 |
| 176 private void onPositionChanged() { |
| 177 mContainer.update(getContainerPositionX(), getContainerPositionY(), |
| 178 getRight() - getLeft(), getBottom() - getTop()); |
| 179 } |
| 180 |
| 181 // x and y are in physical pixels. |
| 182 private void moveTo(int x, int y) { |
| 183 mPositionX = x; |
| 184 mPositionY = y; |
| 185 onPositionChanged(); |
| 186 } |
| 187 |
| 188 @Override |
| 189 protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { |
| 190 if (mDrawable == null) { |
| 191 setMeasuredDimension(0, 0); |
| 192 return; |
| 193 } |
| 194 setMeasuredDimension(mDrawable.getIntrinsicWidth(), |
| 195 mDrawable.getIntrinsicHeight()); |
| 196 } |
| 197 |
| 198 @Override |
| 199 protected void onDraw(Canvas c) { |
| 200 if (mDrawable == null) return; |
| 201 mDrawable.setBounds(0, 0, getRight() - getLeft(), getBottom() - getTop()
); |
| 202 mDrawable.draw(c); |
| 203 } |
| 204 |
| 205 // x and y are in physical pixels. |
| 206 private void positionAt(int x, int y) { |
| 207 moveTo(x - Math.round(mHotspotX), y - Math.round(mHotspotY)); |
| 208 } |
| 209 |
| 210 // Returns the x coordinate of the position that the handle appears to be po
inting to relative |
| 211 // to the handles "parent" view. |
| 212 private int getAdjustedPositionX() { |
| 213 return mPositionX + Math.round(mHotspotX); |
| 214 } |
| 215 |
| 216 // Returns the y coordinate of the position that the handle appears to be po
inting to relative |
| 217 // to the handles "parent" view. |
| 218 private int getAdjustedPositionY() { |
| 219 return mPositionY + Math.round(mHotspotY); |
| 220 } |
| 221 |
| 222 @CalledByNative |
| 223 private void show() { |
| 224 if (mContainer.isShowing()) return; |
| 225 |
| 226 final PopupTouchHandleDrawableDelegate delegate = mDelegate.get(); |
| 227 if (delegate == null) { |
| 228 hide(); |
| 229 return; |
| 230 } |
| 231 |
| 232 mParentPositionObserver = delegate.getParentPositionObserver(); |
| 233 assert mParentPositionObserver != null; |
| 234 |
| 235 // While hidden, the parent position may have become stale. It must be u
pdated before |
| 236 // checking isPositionVisible(). |
| 237 updateParentPosition(mParentPositionObserver.getPositionX(), |
| 238 mParentPositionObserver.getPositionY()); |
| 239 mParentPositionObserver.addListener(mParentPositionListener); |
| 240 mContainer.setContentView(this); |
| 241 mContainer.showAtLocation(delegate.getParent(), 0, |
| 242 getContainerPositionX(), getContainerPositionY()); |
| 243 } |
| 244 |
| 245 @CalledByNative |
| 246 private void hide() { |
| 247 mContainer.dismiss(); |
| 248 if (mParentPositionObserver != null) { |
| 249 mParentPositionObserver.removeListener(mParentPositionListener); |
| 250 // Clear the strong reference to allow garbage collection. |
| 251 mParentPositionObserver = null; |
| 252 } |
| 253 } |
| 254 |
| 255 @CalledByNative |
| 256 private void setRightOrientation() { |
| 257 setOrientation(RIGHT); |
| 258 } |
| 259 |
| 260 @CalledByNative |
| 261 private void setLeftOrientation() { |
| 262 setOrientation(LEFT); |
| 263 } |
| 264 |
| 265 @CalledByNative |
| 266 private void setCenterOrientation() { |
| 267 setOrientation(CENTER); |
| 268 } |
| 269 |
| 270 @CalledByNative |
| 271 private void setOpacity(float alpha) { |
| 272 if (mAlpha == alpha) return; |
| 273 mAlpha = alpha; |
| 274 if (mDrawable != null) mDrawable.setAlpha((int) (255 * mAlpha)); |
| 275 } |
| 276 |
| 277 @CalledByNative |
| 278 private void setFocus(float x, float y) { |
| 279 positionAt((int) x, (int) y); |
| 280 } |
| 281 |
| 282 @CalledByNative |
| 283 private void setVisible(boolean visible) { |
| 284 setVisibility(visible ? VISIBLE : INVISIBLE); |
| 285 } |
| 286 |
| 287 @CalledByNative |
| 288 private boolean containsPoint(float x, float y) { |
| 289 if (mDrawable == null) return false; |
| 290 final int width = mDrawable.getIntrinsicWidth(); |
| 291 final int height = mDrawable.getIntrinsicHeight(); |
| 292 return x > mPositionX && x < (mPositionX + width) |
| 293 && y > mPositionY && y < (mPositionY + height); |
| 294 } |
| 295 } |
OLD | NEW |