OLD | NEW |
1 // Copyright 2013 The Chromium Authors. All rights reserved. | 1 // Copyright 2013 The Chromium Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 package org.chromium.chromoting; | 5 package org.chromium.chromoting; |
6 | 6 |
7 import android.content.Context; | 7 import android.content.Context; |
8 import android.graphics.Matrix; | 8 import android.graphics.Matrix; |
9 import android.graphics.Point; | 9 import android.graphics.Point; |
10 import android.graphics.PointF; | 10 import android.graphics.PointF; |
11 import android.graphics.Rect; | 11 import android.graphics.Rect; |
12 import android.view.GestureDetector; | 12 import android.view.GestureDetector; |
13 import android.view.MotionEvent; | 13 import android.view.MotionEvent; |
14 import android.view.ScaleGestureDetector; | 14 import android.view.ScaleGestureDetector; |
15 import android.view.ViewConfiguration; | 15 import android.view.ViewConfiguration; |
16 | 16 |
17 /** | 17 /** |
18 * This class is responsible for handling Touch input from the user. Touch even
ts which manipulate | 18 * This class is responsible for handling Touch input from the user. Touch even
ts which manipulate |
19 * the local canvas are handled in this class and any input which should be sent
to the remote host | 19 * the local canvas are handled in this class and any input which should be sent
to the remote host |
20 * are passed to the InputStrategyInterface implementation set by the DesktopVie
w. | 20 * are passed to the InputStrategyInterface implementation set by the DesktopVie
w. |
21 */ | 21 */ |
22 public class TouchInputHandler { | 22 public class TouchInputHandler { |
| 23 private static final float EPSILON = 0.001f; |
| 24 |
23 private final AbstractDesktopView mViewer; | 25 private final AbstractDesktopView mViewer; |
24 private final Context mContext; | 26 private final Context mContext; |
25 private final RenderData mRenderData; | 27 private final RenderData mRenderData; |
26 private final DesktopCanvas mDesktopCanvas; | 28 private final DesktopCanvas mDesktopCanvas; |
27 private InputStrategyInterface mInputStrategy; | 29 private InputStrategyInterface mInputStrategy; |
28 | 30 |
29 private GestureDetector mScroller; | 31 private GestureDetector mScroller; |
30 private ScaleGestureDetector mZoomer; | 32 private ScaleGestureDetector mZoomer; |
31 private TapGestureDetector mTapDetector; | 33 private TapGestureDetector mTapDetector; |
32 | 34 |
(...skipping 321 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
354 resizeImageToFitScreen(); | 356 resizeImageToFitScreen(); |
355 } | 357 } |
356 | 358 |
357 private void resizeImageToFitScreen() { | 359 private void resizeImageToFitScreen() { |
358 mDesktopCanvas.resizeImageToFitScreen(); | 360 mDesktopCanvas.resizeImageToFitScreen(); |
359 float screenCenterX; | 361 float screenCenterX; |
360 float screenCenterY; | 362 float screenCenterY; |
361 synchronized (mRenderData) { | 363 synchronized (mRenderData) { |
362 screenCenterX = (float) mRenderData.screenWidth / 2; | 364 screenCenterX = (float) mRenderData.screenWidth / 2; |
363 screenCenterY = (float) mRenderData.screenHeight / 2; | 365 screenCenterY = (float) mRenderData.screenHeight / 2; |
| 366 |
| 367 float[] imagePoint = mapScreenPointToImagePoint(screenCenterX, scree
nCenterY); |
| 368 mDesktopCanvas.setViewportPosition(imagePoint[0], imagePoint[1]); |
364 } | 369 } |
365 moveCursorToScreenPoint(screenCenterX, screenCenterY); | 370 moveCursorToScreenPoint(screenCenterX, screenCenterY); |
| 371 mDesktopCanvas.repositionImage(true); |
366 } | 372 } |
367 | 373 |
368 private void setInputStrategy(InputStrategyInterface inputStrategy) { | 374 private void setInputStrategy(InputStrategyInterface inputStrategy) { |
369 // Since the rules for flinging differ between input modes, we want to s
top running the | 375 // Since the rules for flinging differ between input modes, we want to s
top running the |
370 // current fling animation when the mode changes to prevent a wonky expe
rience. | 376 // current fling animation when the mode changes to prevent a wonky expe
rience. |
371 mCursorAnimationJob.abortAnimation(); | 377 mCursorAnimationJob.abortAnimation(); |
372 mScrollAnimationJob.abortAnimation(); | 378 mScrollAnimationJob.abortAnimation(); |
373 mInputStrategy = inputStrategy; | 379 mInputStrategy = inputStrategy; |
374 } | 380 } |
375 | 381 |
(...skipping 16 matching lines...) Expand all Loading... |
392 // keep the cursor centered, if possible, as the viewport moves. | 398 // keep the cursor centered, if possible, as the viewport moves. |
393 if (followCursor) { | 399 if (followCursor) { |
394 moveCursor((int) newPos.x, (int) newPos.y); | 400 moveCursor((int) newPos.x, (int) newPos.y); |
395 } | 401 } |
396 | 402 |
397 mDesktopCanvas.repositionImage(true); | 403 mDesktopCanvas.repositionImage(true); |
398 } | 404 } |
399 | 405 |
400 /** Moves the cursor to the specified position on the screen. */ | 406 /** Moves the cursor to the specified position on the screen. */ |
401 private void moveCursorToScreenPoint(float screenX, float screenY) { | 407 private void moveCursorToScreenPoint(float screenX, float screenY) { |
402 float[] mappedValues = {screenX, screenY}; | 408 float[] imagePoint = mapScreenPointToImagePoint(screenX, screenY); |
403 synchronized (mRenderData) { | 409 moveCursor((int) imagePoint[0], (int) imagePoint[1]); |
404 Matrix canvasToImage = new Matrix(); | |
405 mRenderData.transform.invert(canvasToImage); | |
406 canvasToImage.mapPoints(mappedValues); | |
407 } | |
408 moveCursor((int) mappedValues[0], (int) mappedValues[1]); | |
409 } | 410 } |
410 | 411 |
411 /** Moves the cursor to the specified position on the remote host. */ | 412 /** Moves the cursor to the specified position on the remote host. */ |
412 private void moveCursor(int newX, int newY) { | 413 private void moveCursor(int newX, int newY) { |
413 synchronized (mRenderData) { | 414 synchronized (mRenderData) { |
414 boolean cursorMoved = mRenderData.setCursorPosition(newX, newY); | 415 boolean cursorMoved = mRenderData.setCursorPosition(newX, newY); |
415 if (cursorMoved) { | 416 if (cursorMoved) { |
416 mInputStrategy.injectCursorMoveEvent(newX, newY); | 417 mInputStrategy.injectCursorMoveEvent(newX, newY); |
417 } | 418 } |
418 } | 419 } |
(...skipping 11 matching lines...) Expand all Loading... |
430 } else { | 431 } else { |
431 return false; | 432 return false; |
432 } | 433 } |
433 | 434 |
434 mSuppressCursorMovement = true; | 435 mSuppressCursorMovement = true; |
435 mSuppressFling = true; | 436 mSuppressFling = true; |
436 mSwipeCompleted = true; | 437 mSwipeCompleted = true; |
437 return true; | 438 return true; |
438 } | 439 } |
439 | 440 |
| 441 /** Translates a point in screen coordinates to a location on the desktop im
age. */ |
| 442 private float[] mapScreenPointToImagePoint(float screenX, float screenY) { |
| 443 float[] mappedPoints = {screenX, screenY}; |
| 444 Matrix screenToImage = new Matrix(); |
| 445 synchronized (mRenderData) { |
| 446 mRenderData.transform.invert(screenToImage); |
| 447 } |
| 448 screenToImage.mapPoints(mappedPoints); |
| 449 |
| 450 return mappedPoints; |
| 451 } |
| 452 |
440 /** Responds to touch events filtered by the gesture detectors. */ | 453 /** Responds to touch events filtered by the gesture detectors. */ |
441 private class GestureListener extends GestureDetector.SimpleOnGestureListene
r | 454 private class GestureListener extends GestureDetector.SimpleOnGestureListene
r |
442 implements ScaleGestureDetector.OnScaleGestureListener, | 455 implements ScaleGestureDetector.OnScaleGestureListener, |
443 TapGestureDetector.OnTapListener { | 456 TapGestureDetector.OnTapListener { |
444 /** | 457 /** |
445 * Called when the user drags one or more fingers across the touchscreen
. | 458 * Called when the user drags one or more fingers across the touchscreen
. |
446 */ | 459 */ |
447 @Override | 460 @Override |
448 public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX,
float distanceY) { | 461 public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX,
float distanceY) { |
449 int pointerCount = e2.getPointerCount(); | 462 int pointerCount = e2.getPointerCount(); |
(...skipping 113 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
563 | 576 |
564 /** Called when the user taps the screen with one or more fingers. */ | 577 /** Called when the user taps the screen with one or more fingers. */ |
565 @Override | 578 @Override |
566 public boolean onTap(int pointerCount, float x, float y) { | 579 public boolean onTap(int pointerCount, float x, float y) { |
567 int button = mouseButtonFromPointerCount(pointerCount); | 580 int button = mouseButtonFromPointerCount(pointerCount); |
568 if (button == InputStub.BUTTON_UNDEFINED) { | 581 if (button == InputStub.BUTTON_UNDEFINED) { |
569 return false; | 582 return false; |
570 } | 583 } |
571 | 584 |
572 if (!mInputStrategy.isIndirectInputMode()) { | 585 if (!mInputStrategy.isIndirectInputMode()) { |
573 if (!mapScreenPointToImage(x, y)) { | 586 if (screenPointLiesOutsideImageBoundary(x, y)) { |
574 return false; | 587 return false; |
575 } | 588 } |
576 moveCursorToScreenPoint(x, y); | 589 moveCursorToScreenPoint(x, y); |
577 } | 590 } |
578 | 591 |
579 if (mInputStrategy.onTap(button)) { | 592 if (mInputStrategy.onTap(button)) { |
580 Point pos; | 593 Point pos; |
581 synchronized (mRenderData) { | 594 synchronized (mRenderData) { |
582 pos = mRenderData.getCursorPosition(); | 595 pos = mRenderData.getCursorPosition(); |
583 } | 596 } |
584 mViewer.showInputFeedback(mInputStrategy.getShortPressFeedbackTy
pe(), pos); | 597 mViewer.showInputFeedback(mInputStrategy.getShortPressFeedbackTy
pe(), pos); |
585 } | 598 } |
586 return true; | 599 return true; |
587 } | 600 } |
588 | 601 |
589 /** Called when a long-press is triggered for one or more fingers. */ | 602 /** Called when a long-press is triggered for one or more fingers. */ |
590 @Override | 603 @Override |
591 public void onLongPress(int pointerCount, float x, float y) { | 604 public void onLongPress(int pointerCount, float x, float y) { |
592 int button = mouseButtonFromPointerCount(pointerCount); | 605 int button = mouseButtonFromPointerCount(pointerCount); |
593 if (button == InputStub.BUTTON_UNDEFINED) { | 606 if (button == InputStub.BUTTON_UNDEFINED) { |
594 return; | 607 return; |
595 } | 608 } |
596 | 609 |
597 if (!mInputStrategy.isIndirectInputMode()) { | 610 if (!mInputStrategy.isIndirectInputMode()) { |
598 if (!mapScreenPointToImage(x, y)) { | 611 if (screenPointLiesOutsideImageBoundary(x, y)) { |
599 return; | 612 return; |
600 } | 613 } |
601 moveCursorToScreenPoint(x, y); | 614 moveCursorToScreenPoint(x, y); |
602 } | 615 } |
603 | 616 |
604 if (mInputStrategy.onPressAndHold(button)) { | 617 if (mInputStrategy.onPressAndHold(button)) { |
605 Point pos; | 618 Point pos; |
606 synchronized (mRenderData) { | 619 synchronized (mRenderData) { |
607 pos = mRenderData.getCursorPosition(); | 620 pos = mRenderData.getCursorPosition(); |
608 } | 621 } |
(...skipping 10 matching lines...) Expand all Loading... |
619 return InputStub.BUTTON_LEFT; | 632 return InputStub.BUTTON_LEFT; |
620 case 2: | 633 case 2: |
621 return InputStub.BUTTON_RIGHT; | 634 return InputStub.BUTTON_RIGHT; |
622 case 3: | 635 case 3: |
623 return InputStub.BUTTON_MIDDLE; | 636 return InputStub.BUTTON_MIDDLE; |
624 default: | 637 default: |
625 return InputStub.BUTTON_UNDEFINED; | 638 return InputStub.BUTTON_UNDEFINED; |
626 } | 639 } |
627 } | 640 } |
628 | 641 |
629 /** Verifies the given point maps to a valid location within the desktop
image. */ | 642 /** Determines whether the given screen point lies outside the desktop i
mage. */ |
630 private boolean mapScreenPointToImage(float screenX, float screenY) { | 643 private boolean screenPointLiesOutsideImageBoundary(float screenX, float
screenY) { |
631 float[] mappedPoints = {screenX, screenY}; | 644 float[] mappedPoints = mapScreenPointToImagePoint(screenX, screenY); |
632 int imageWidth; | 645 float imageWidth; |
633 int imageHeight; | 646 float imageHeight; |
634 Matrix screenToImage = new Matrix(); | |
635 synchronized (mRenderData) { | 647 synchronized (mRenderData) { |
636 mRenderData.transform.invert(screenToImage); | 648 imageWidth = (float) mRenderData.imageWidth + EPSILON; |
637 imageWidth = mRenderData.imageWidth; | 649 imageHeight = (float) mRenderData.imageHeight + EPSILON; |
638 imageHeight = mRenderData.imageHeight; | |
639 } | 650 } |
640 screenToImage.mapPoints(mappedPoints); | |
641 | 651 |
642 return (mappedPoints[0] >= 0 && mappedPoints[0] <= imageWidth) | 652 return mappedPoints[0] < -EPSILON || mappedPoints[0] > imageWidth |
643 && (mappedPoints[1] >= 0 && mappedPoints[1] <= imageHeight); | 653 || mappedPoints[1] < -EPSILON || mappedPoints[1] > imageHeig
ht; |
644 } | 654 } |
645 } | 655 } |
646 } | 656 } |
OLD | NEW |