43  public static final float MAX_SCALE_FACTOR = 3.0f;
 
   44  public static final float MIN_SCALE_FACTOR = 1.0f;
 
   45  private static final String TAG = 
"SessionView";
 
   46  private static final float SCALE_FACTOR_DELTA = 0.0001f;
 
   47  private static final float TOUCH_SCROLL_DELTA = 10.0f;
 
   50  private BitmapDrawable surface;
 
   51  private Stack<Rect> invalidRegions;
 
   52  private int touchPointerPaddingWidth = 0;
 
   53  private int touchPointerPaddingHeight = 0;
 
   56  private float scaleFactor = 1.0f;
 
   57  private Matrix scaleMatrix;
 
   58  private Matrix invScaleMatrix;
 
   59  private RectF invalidRegionF;
 
   60  private GestureDetector gestureDetector;
 
   61  private SessionState currentSession;
 
   64  private DoubleGestureDetector doubleGestureDetector;
 
   68    initSessionView(context);
 
   71  public SessionView(Context context, AttributeSet attrs)
 
   73    super(context, attrs);
 
   74    initSessionView(context);
 
   77  public SessionView(Context context, AttributeSet attrs, 
int defStyle)
 
   79    super(context, attrs, defStyle);
 
   80    initSessionView(context);
 
   83  private void initSessionView(Context context)
 
   85    invalidRegions = 
new Stack<>();
 
   86    gestureDetector = 
new GestureDetector(context, 
new SessionGestureListener(), 
null, 
true);
 
   87    doubleGestureDetector =
 
   88        new DoubleGestureDetector(context, 
null, 
new SessionDoubleGestureListener());
 
   91    scaleMatrix = 
new Matrix();
 
   92    invScaleMatrix = 
new Matrix();
 
   93    invalidRegionF = 
new RectF();
 
   95    setSystemUiVisibility(View.SYSTEM_UI_FLAG_HIDE_NAVIGATION |
 
   96                          View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY);
 
  100  @Override 
public boolean onHoverEvent(MotionEvent event)
 
  102    if (event.getAction() == MotionEvent.ACTION_HOVER_MOVE)
 
  105      float x = 
event.getX();
 
  106      float y = 
event.getY();
 
  108      MotionEvent mappedEvent = mapTouchEvent(event);
 
  109      LibFreeRDP.sendCursorEvent(currentSession.getInstance(), (
int)mappedEvent.getX(),
 
  110                                 (
int)mappedEvent.getY(), Mouse.getMoveEvent());
 
  116  public void setScaleGestureDetector(ScaleGestureDetector scaleGestureDetector)
 
  118    doubleGestureDetector.setScaleGestureDetector(scaleGestureDetector);
 
  123    this.sessionViewListener = sessionViewListener;
 
  126  public void addInvalidRegion(Rect invalidRegion)
 
  129    invalidRegionF.set(invalidRegion);
 
  130    scaleMatrix.mapRect(invalidRegionF);
 
  131    invalidRegionF.roundOut(invalidRegion);
 
  133    invalidRegions.add(invalidRegion);
 
  136  public void invalidateRegion()
 
  138    invalidate(invalidRegions.pop());
 
  141  public void onSurfaceChange(SessionState session)
 
  143    surface = session.getSurface();
 
  144    Bitmap bitmap = surface.getBitmap();
 
  145    width = bitmap.getWidth();
 
  146    height = bitmap.getHeight();
 
  147    surface.setBounds(0, 0, width, height);
 
  149    setMinimumWidth(width);
 
  150    setMinimumHeight(height);
 
  153    currentSession = session;
 
  156  public float getZoom()
 
  161  public void setZoom(
float factor)
 
  165    scaleFactor = factor;
 
  166    scaleMatrix.setScale(scaleFactor, scaleFactor);
 
  167    invScaleMatrix.setScale(1.0f / scaleFactor, 1.0f / scaleFactor);
 
  173  public boolean isAtMaxZoom()
 
  175    return (scaleFactor > (MAX_SCALE_FACTOR - SCALE_FACTOR_DELTA));
 
  178  public boolean isAtMinZoom()
 
  180    return (scaleFactor < (MIN_SCALE_FACTOR + SCALE_FACTOR_DELTA));
 
  183  public boolean zoomIn(
float factor)
 
  186    scaleFactor += factor;
 
  187    if (scaleFactor > (MAX_SCALE_FACTOR - SCALE_FACTOR_DELTA))
 
  189      scaleFactor = MAX_SCALE_FACTOR;
 
  192    setZoom(scaleFactor);
 
  196  public boolean zoomOut(
float factor)
 
  199    scaleFactor -= factor;
 
  200    if (scaleFactor < (MIN_SCALE_FACTOR + SCALE_FACTOR_DELTA))
 
  202      scaleFactor = MIN_SCALE_FACTOR;
 
  205    setZoom(scaleFactor);
 
  209  public void setTouchPointerPadding(
int width, 
int height)
 
  211    touchPointerPaddingWidth = width;
 
  212    touchPointerPaddingHeight = height;
 
  216  public int getTouchPointerPaddingWidth()
 
  218    return touchPointerPaddingWidth;
 
  221  public int getTouchPointerPaddingHeight()
 
  223    return touchPointerPaddingHeight;
 
  226  @Override 
public void onMeasure(
int widthMeasureSpec, 
int heightMeasureSpec)
 
  228    Log.v(TAG, width + 
"x" + height);
 
  229    this.setMeasuredDimension((
int)(width * scaleFactor) + touchPointerPaddingWidth,
 
  230                              (
int)(height * scaleFactor) + touchPointerPaddingHeight);
 
  233  @Override 
public void onDraw(@NonNull Canvas canvas)
 
  235    super.onDraw(canvas);
 
  238    canvas.concat(scaleMatrix);
 
  239    canvas.drawColor(Color.BLACK);
 
  242      surface.draw(canvas);
 
  249  @Override 
public boolean dispatchKeyEventPreIme(KeyEvent event)
 
  251    if (event.getKeyCode() == KeyEvent.KEYCODE_BACK &&
 
  252        event.getAction() == KeyEvent.ACTION_DOWN)
 
  254    return super.dispatchKeyEventPreIme(event);
 
  258  private MotionEvent mapTouchEvent(MotionEvent event)
 
  260    MotionEvent mappedEvent = MotionEvent.obtain(event);
 
  261    float[] coordinates = { mappedEvent.getX(), mappedEvent.getY() };
 
  262    invScaleMatrix.mapPoints(coordinates);
 
  263    mappedEvent.setLocation(coordinates[0], coordinates[1]);
 
  268  private MotionEvent mapDoubleTouchEvent(MotionEvent event)
 
  270    MotionEvent mappedEvent = MotionEvent.obtain(event);
 
  271    float[] coordinates = { (mappedEvent.getX(0) + mappedEvent.getX(1)) / 2,
 
  272                          (mappedEvent.getY(0) + mappedEvent.getY(1)) / 2 };
 
  273    invScaleMatrix.mapPoints(coordinates);
 
  274    mappedEvent.setLocation(coordinates[0], coordinates[1]);
 
  278  @Override 
public boolean onTouchEvent(MotionEvent event)
 
  280    boolean res = gestureDetector.onTouchEvent(event);
 
  281    res |= doubleGestureDetector.onTouchEvent(event);
 
  286    void onSessionViewBeginTouch();
 
  288    void onSessionViewEndTouch();
 
  290    void onSessionViewLeftTouch(
int x, 
int y, 
boolean down);
 
  292    void onSessionViewRightTouch(
int x, 
int y, 
boolean down);
 
  294    void onSessionViewMove(
int x, 
int y);
 
  296    void onSessionViewScroll(
boolean down);
 
 
  299  private class SessionGestureListener 
extends GestureDetector.SimpleOnGestureListener
 
  301    boolean longPressInProgress = 
false;
 
  303    public boolean onDown(MotionEvent e)
 
  308    public boolean onUp(MotionEvent e)
 
  310      sessionViewListener.onSessionViewEndTouch();
 
  314    public void onLongPress(MotionEvent e)
 
  316      MotionEvent mappedEvent = mapTouchEvent(e);
 
  317      sessionViewListener.onSessionViewBeginTouch();
 
  318      sessionViewListener.onSessionViewLeftTouch((
int)mappedEvent.getX(),
 
  319                                                 (
int)mappedEvent.getY(), 
true);
 
  320      longPressInProgress = 
true;
 
  323    public void onLongPressUp(MotionEvent e)
 
  325      MotionEvent mappedEvent = mapTouchEvent(e);
 
  326      sessionViewListener.onSessionViewLeftTouch((
int)mappedEvent.getX(),
 
  327                                                 (
int)mappedEvent.getY(), 
false);
 
  328      longPressInProgress = 
false;
 
  329      sessionViewListener.onSessionViewEndTouch();
 
  332    public boolean onScroll(MotionEvent e1, MotionEvent e2, 
float distanceX, 
float distanceY)
 
  334      if (longPressInProgress)
 
  336        MotionEvent mappedEvent = mapTouchEvent(e2);
 
  337        sessionViewListener.onSessionViewMove((
int)mappedEvent.getX(),
 
  338                                              (
int)mappedEvent.getY());
 
  345    public boolean onDoubleTap(MotionEvent e)
 
  348      MotionEvent mappedEvent = mapTouchEvent(e);
 
  349      sessionViewListener.onSessionViewLeftTouch((
int)mappedEvent.getX(),
 
  350                                                 (
int)mappedEvent.getY(), 
true);
 
  351      sessionViewListener.onSessionViewLeftTouch((
int)mappedEvent.getX(),
 
  352                                                 (
int)mappedEvent.getY(), 
false);
 
  356    public boolean onSingleTapUp(MotionEvent e)
 
  359      MotionEvent mappedEvent = mapTouchEvent(e);
 
  360      sessionViewListener.onSessionViewBeginTouch();
 
  361      switch (e.getButtonState())
 
  363        case MotionEvent.BUTTON_PRIMARY:
 
  364          sessionViewListener.onSessionViewLeftTouch((
int)mappedEvent.getX(),
 
  365                                                     (
int)mappedEvent.getY(), 
true);
 
  366          sessionViewListener.onSessionViewLeftTouch((
int)mappedEvent.getX(),
 
  367                                                     (
int)mappedEvent.getY(), 
false);
 
  369        case MotionEvent.BUTTON_SECONDARY:
 
  370          sessionViewListener.onSessionViewRightTouch((
int)mappedEvent.getX(),
 
  371                                                      (
int)mappedEvent.getY(), 
true);
 
  372          sessionViewListener.onSessionViewRightTouch((
int)mappedEvent.getX(),
 
  373                                                      (
int)mappedEvent.getY(), 
false);
 
  374          sessionViewListener.onSessionViewLeftTouch((
int)mappedEvent.getX(),
 
  375                                                     (
int)mappedEvent.getY(), 
true);
 
  376          sessionViewListener.onSessionViewLeftTouch((
int)mappedEvent.getX(),
 
  377                                                     (
int)mappedEvent.getY(), 
false);
 
  380      sessionViewListener.onSessionViewEndTouch();
 
  385  private class SessionDoubleGestureListener
 
  386      implements DoubleGestureDetector.OnDoubleGestureListener
 
  388    private MotionEvent prevEvent = 
null;
 
  390    public boolean onDoubleTouchDown(MotionEvent e)
 
  392      sessionViewListener.onSessionViewBeginTouch();
 
  393      prevEvent = MotionEvent.obtain(e);
 
  397    public boolean onDoubleTouchUp(MotionEvent e)
 
  399      if (prevEvent != 
null)
 
  404      sessionViewListener.onSessionViewEndTouch();
 
  408    public boolean onDoubleTouchScroll(MotionEvent e1, MotionEvent e2)
 
  411      float deltaY = e2.getY() - prevEvent.getY();
 
  412      if (deltaY > TOUCH_SCROLL_DELTA)
 
  414        sessionViewListener.onSessionViewScroll(
true);
 
  416        prevEvent = MotionEvent.obtain(e2);
 
  418      else if (deltaY < -TOUCH_SCROLL_DELTA)
 
  420        sessionViewListener.onSessionViewScroll(
false);
 
  422        prevEvent = MotionEvent.obtain(e2);
 
  427    public boolean onDoubleTouchSingleTap(MotionEvent e)
 
  430      MotionEvent mappedEvent = mapDoubleTouchEvent(e);
 
  431      sessionViewListener.onSessionViewRightTouch((
int)mappedEvent.getX(),
 
  432                                                  (
int)mappedEvent.getY(), 
true);
 
  433      sessionViewListener.onSessionViewRightTouch((
int)mappedEvent.getX(),
 
  434                                                  (
int)mappedEvent.getY(), 
false);
 
  439  @Override 
public InputConnection onCreateInputConnection(EditorInfo outAttrs)
 
  441    super.onCreateInputConnection(outAttrs);
 
  442    outAttrs.inputType = InputType.TYPE_CLASS_TEXT;