43 public static final float MAX_SCALE_FACTOR = 3.0f;
44 public static final float MIN_SCALE_FACTOR = 0.75f;
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 boolean railMode =
false;
58 private float scaleFactor = 1.0f;
59 private Matrix scaleMatrix;
60 private Matrix invScaleMatrix;
61 private RectF invalidRegionF;
62 private GestureDetector gestureDetector;
63 private SessionState currentSession;
65 private int[] cursorPixels =
null;
66 private int cursorWidth = 0;
67 private int cursorHeight = 0;
68 private int cursorHotX = 0;
69 private int cursorHotY = 0;
72 private DoubleGestureDetector doubleGestureDetector;
76 initSessionView(context);
79 public SessionView(Context context, AttributeSet attrs)
81 super(context, attrs);
82 initSessionView(context);
85 public SessionView(Context context, AttributeSet attrs,
int defStyle)
87 super(context, attrs, defStyle);
88 initSessionView(context);
91 private void initSessionView(Context context)
95 setFocusableInTouchMode(
true);
97 invalidRegions =
new Stack<>();
98 gestureDetector =
new GestureDetector(context,
new SessionGestureListener(),
null,
true);
99 doubleGestureDetector =
100 new DoubleGestureDetector(context,
null,
new SessionDoubleGestureListener());
103 scaleMatrix =
new Matrix();
104 invScaleMatrix =
new Matrix();
105 invalidRegionF =
new RectF();
109 @Override
public boolean onHoverEvent(MotionEvent event)
111 if (event.getAction() == MotionEvent.ACTION_HOVER_MOVE)
114 float x =
event.getX();
115 float y =
event.getY();
117 MotionEvent mappedEvent = mapTouchEvent(event);
118 sessionViewListener.onSessionViewMouseMove((
int)mappedEvent.getX(),
119 (
int)mappedEvent.getY());
120 mappedEvent.recycle();
126 public void setScaleGestureDetector(ScaleGestureDetector scaleGestureDetector)
128 doubleGestureDetector.setScaleGestureDetector(scaleGestureDetector);
133 this.sessionViewListener = sessionViewListener;
136 public void addInvalidRegion(Rect invalidRegion)
139 invalidRegionF.set(invalidRegion);
140 scaleMatrix.mapRect(invalidRegionF);
141 invalidRegionF.roundOut(invalidRegion);
143 invalidRegions.add(invalidRegion);
146 public void invalidateRegion()
148 invalidate(invalidRegions.pop());
151 public void onSurfaceChange(SessionState session)
153 surface = session.getSurface();
154 Bitmap bitmap = surface.getBitmap();
155 width = bitmap.getWidth();
156 height = bitmap.getHeight();
157 surface.setBounds(0, 0, width, height);
159 setMinimumWidth(width);
160 setMinimumHeight(height);
163 currentSession = session;
166 public float getZoom()
171 public void setRailMode(
boolean rail)
173 if (railMode == rail)
181 void onZoomChanged(
float zoom);
186 zoomChangedListener = l;
189 public void setZoom(
float factor)
191 scaleFactor = factor;
192 scaleMatrix.setScale(scaleFactor, scaleFactor);
193 invScaleMatrix.setScale(1.0f / scaleFactor, 1.0f / scaleFactor);
195 if (cursorPixels !=
null)
198 if (zoomChangedListener !=
null)
199 zoomChangedListener.onZoomChanged(scaleFactor);
204 public boolean isAtMaxZoom()
206 return (scaleFactor > (MAX_SCALE_FACTOR - SCALE_FACTOR_DELTA));
209 public boolean isAtMinZoom()
211 return (scaleFactor < (MIN_SCALE_FACTOR + SCALE_FACTOR_DELTA));
214 public boolean zoomIn(
float factor)
217 scaleFactor += factor;
218 if (scaleFactor > (MAX_SCALE_FACTOR - SCALE_FACTOR_DELTA))
220 scaleFactor = MAX_SCALE_FACTOR;
223 setZoom(scaleFactor);
227 public boolean zoomOut(
float factor)
230 scaleFactor -= factor;
231 if (scaleFactor < (MIN_SCALE_FACTOR + SCALE_FACTOR_DELTA))
233 scaleFactor = MIN_SCALE_FACTOR;
236 setZoom(scaleFactor);
240 public void setTouchPointerPadding(
int width,
int height)
242 touchPointerPaddingWidth = width;
243 touchPointerPaddingHeight = height;
247 public int getTouchPointerPaddingWidth()
249 return touchPointerPaddingWidth;
252 public int getTouchPointerPaddingHeight()
254 return touchPointerPaddingHeight;
257 @Override
public void onMeasure(
int widthMeasureSpec,
int heightMeasureSpec)
259 Log.v(TAG, width +
"x" + height);
260 this.setMeasuredDimension((
int)(width * scaleFactor) + touchPointerPaddingWidth,
261 (
int)(height * scaleFactor) + touchPointerPaddingHeight);
264 @Override
public void onDraw(@NonNull Canvas canvas)
266 super.onDraw(canvas);
269 canvas.concat(scaleMatrix);
270 canvas.drawColor(Color.BLACK);
271 if (!railMode && surface !=
null)
273 surface.draw(canvas);
279 private MotionEvent mapTouchEvent(MotionEvent event)
281 MotionEvent mappedEvent = MotionEvent.obtain(event);
282 float[] coordinates = { mappedEvent.getX(), mappedEvent.getY() };
283 invScaleMatrix.mapPoints(coordinates);
284 mappedEvent.setLocation(coordinates[0], coordinates[1]);
289 private MotionEvent mapDoubleTouchEvent(MotionEvent event)
291 MotionEvent mappedEvent = MotionEvent.obtain(event);
292 float[] coordinates = { (mappedEvent.getX(0) + mappedEvent.getX(1)) / 2,
293 (mappedEvent.getY(0) + mappedEvent.getY(1)) / 2 };
294 invScaleMatrix.mapPoints(coordinates);
295 mappedEvent.setLocation(coordinates[0], coordinates[1]);
299 @Override
public boolean onTouchEvent(MotionEvent event)
304 if (event.isFromSource(InputDevice.SOURCE_MOUSE))
306 int action =
event.getActionMasked();
307 if (action == MotionEvent.ACTION_MOVE && event.getButtonState() != 0)
309 MotionEvent mapped = mapTouchEvent(event);
310 sessionViewListener.onSessionViewMouseMove((
int)mapped.getX(), (
int)mapped.getY());
317 boolean res = gestureDetector.onTouchEvent(event);
318 res |= doubleGestureDetector.onTouchEvent(event);
323 @Override
public boolean onGenericMotionEvent(MotionEvent event)
325 final boolean isPointer =
event.isFromSource(InputDevice.SOURCE_CLASS_POINTER);
329 final boolean isMouse =
event.isFromSource(InputDevice.SOURCE_MOUSE);
330 int action =
event.getActionMasked();
332 if (isMouse && (action == MotionEvent.ACTION_BUTTON_PRESS ||
333 action == MotionEvent.ACTION_BUTTON_RELEASE))
335 boolean down = action == MotionEvent.ACTION_BUTTON_PRESS;
336 MotionEvent mapped = mapTouchEvent(event);
337 int x = (int)mapped.getX();
338 int y = (int)mapped.getY();
341 switch (event.getActionButton())
343 case MotionEvent.BUTTON_PRIMARY:
345 sessionViewListener.onSessionViewBeginTouch();
346 sessionViewListener.onSessionViewLeftTouch(x, y, down);
348 sessionViewListener.onSessionViewEndTouch();
350 case MotionEvent.BUTTON_SECONDARY:
352 sessionViewListener.onSessionViewBeginTouch();
353 sessionViewListener.onSessionViewRightTouch(x, y, down);
355 case MotionEvent.BUTTON_TERTIARY:
356 sessionViewListener.onSessionViewMiddleTouch(x, y, down);
363 if (action == MotionEvent.ACTION_SCROLL)
365 float vScroll =
event.getAxisValue(MotionEvent.AXIS_VSCROLL);
366 float hScroll =
event.getAxisValue(MotionEvent.AXIS_HSCROLL);
368 sessionViewListener.onSessionViewScroll(vScroll > 0);
370 sessionViewListener.onSessionViewHScroll(hScroll > 0);
379 void onSessionViewBeginTouch();
381 void onSessionViewEndTouch();
383 void onSessionViewLeftTouch(
int x,
int y,
boolean down);
385 void onSessionViewMiddleTouch(
int x,
int y,
boolean down);
387 void onSessionViewRightTouch(
int x,
int y,
boolean down);
389 void onSessionViewMove(
int x,
int y);
391 void onSessionViewMouseMove(
int x,
int y);
393 void onSessionViewScroll(
boolean down);
395 void onSessionViewHScroll(
boolean right);
398 public void setRemoteCursor(
int[] pixels,
int width,
int height,
int hotX,
int hotY)
400 if (pixels ==
null || width == 0 || height == 0)
403 setPointerIcon(PointerIcon.getSystemIcon(getContext(), PointerIcon.TYPE_NULL));
406 cursorPixels = pixels;
408 cursorHeight = height;
414 private void applyScaledCursor()
416 int scaledWidth = Math.max(1, (
int)(cursorWidth * scaleFactor));
417 int scaledHeight = Math.max(1, (
int)(cursorHeight * scaleFactor));
419 Bitmap.createBitmap(cursorPixels, cursorWidth, cursorHeight, Bitmap.Config.ARGB_8888);
420 Bitmap scaled = Bitmap.createScaledBitmap(bm, scaledWidth, scaledHeight,
true);
422 PointerIcon.create(scaled, cursorHotX * scaleFactor, cursorHotY * scaleFactor);
423 setPointerIcon(icon);
426 public void setDefaultCursor()
428 setPointerIcon(PointerIcon.getSystemIcon(getContext(), PointerIcon.TYPE_ARROW));
431 private class SessionGestureListener
extends GestureDetector.SimpleOnGestureListener
433 boolean longPressInProgress =
false;
435 public boolean onDown(MotionEvent e)
440 public boolean onUp(MotionEvent e)
442 sessionViewListener.onSessionViewEndTouch();
446 public void onLongPress(MotionEvent e)
448 MotionEvent mappedEvent = mapTouchEvent(e);
449 sessionViewListener.onSessionViewBeginTouch();
450 sessionViewListener.onSessionViewLeftTouch((
int)mappedEvent.getX(),
451 (
int)mappedEvent.getY(),
true);
452 longPressInProgress =
true;
455 public void onLongPressUp(MotionEvent e)
457 MotionEvent mappedEvent = mapTouchEvent(e);
458 sessionViewListener.onSessionViewLeftTouch((
int)mappedEvent.getX(),
459 (
int)mappedEvent.getY(),
false);
460 longPressInProgress =
false;
461 sessionViewListener.onSessionViewEndTouch();
464 public boolean onScroll(MotionEvent e1, MotionEvent e2,
float distanceX,
float distanceY)
466 if (longPressInProgress)
468 MotionEvent mappedEvent = mapTouchEvent(e2);
469 sessionViewListener.onSessionViewMove((
int)mappedEvent.getX(),
470 (
int)mappedEvent.getY());
477 public boolean onDoubleTap(MotionEvent e)
480 MotionEvent mappedEvent = mapTouchEvent(e);
481 sessionViewListener.onSessionViewLeftTouch((
int)mappedEvent.getX(),
482 (
int)mappedEvent.getY(),
true);
483 sessionViewListener.onSessionViewLeftTouch((
int)mappedEvent.getX(),
484 (
int)mappedEvent.getY(),
false);
488 public boolean onSingleTapUp(MotionEvent e)
492 if (e.getButtonState() != 0)
496 MotionEvent mappedEvent = mapTouchEvent(e);
497 sessionViewListener.onSessionViewBeginTouch();
498 sessionViewListener.onSessionViewLeftTouch((
int)mappedEvent.getX(),
499 (
int)mappedEvent.getY(),
true);
500 sessionViewListener.onSessionViewLeftTouch((
int)mappedEvent.getX(),
501 (
int)mappedEvent.getY(),
false);
502 sessionViewListener.onSessionViewEndTouch();
507 private class SessionDoubleGestureListener
508 implements DoubleGestureDetector.OnDoubleGestureListener
510 private MotionEvent prevEvent =
null;
512 public boolean onDoubleTouchDown(MotionEvent e)
514 sessionViewListener.onSessionViewBeginTouch();
515 prevEvent = MotionEvent.obtain(e);
519 public boolean onDoubleTouchUp(MotionEvent e)
521 if (prevEvent !=
null)
526 sessionViewListener.onSessionViewEndTouch();
530 public boolean onDoubleTouchScroll(MotionEvent e1, MotionEvent e2)
533 float deltaY = e2.getY() - prevEvent.getY();
534 if (deltaY > TOUCH_SCROLL_DELTA)
536 sessionViewListener.onSessionViewScroll(
true);
538 prevEvent = MotionEvent.obtain(e2);
540 else if (deltaY < -TOUCH_SCROLL_DELTA)
542 sessionViewListener.onSessionViewScroll(
false);
544 prevEvent = MotionEvent.obtain(e2);
549 public boolean onDoubleTouchSingleTap(MotionEvent e)
552 MotionEvent mappedEvent = mapDoubleTouchEvent(e);
553 sessionViewListener.onSessionViewRightTouch((
int)mappedEvent.getX(),
554 (
int)mappedEvent.getY(),
true);
555 sessionViewListener.onSessionViewRightTouch((
int)mappedEvent.getX(),
556 (
int)mappedEvent.getY(),
false);
561 @Override
public InputConnection onCreateInputConnection(EditorInfo outAttrs)
563 outAttrs.actionLabel =
null;
564 outAttrs.inputType = InputType.TYPE_NULL;
565 outAttrs.imeOptions = EditorInfo.IME_ACTION_NONE | EditorInfo.IME_FLAG_NO_EXTRACT_UI |
566 EditorInfo.IME_FLAG_NO_FULLSCREEN;
567 return new BaseInputConnection(
this,
false);