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 float scaleFactor = 1.0f;
57 private Matrix scaleMatrix;
58 private Matrix invScaleMatrix;
59 private RectF invalidRegionF;
60 private GestureDetector gestureDetector;
61 private SessionState currentSession;
63 private int[] cursorPixels =
null;
64 private int cursorWidth = 0;
65 private int cursorHeight = 0;
66 private int cursorHotX = 0;
67 private int cursorHotY = 0;
70 private DoubleGestureDetector doubleGestureDetector;
74 initSessionView(context);
77 public SessionView(Context context, AttributeSet attrs)
79 super(context, attrs);
80 initSessionView(context);
83 public SessionView(Context context, AttributeSet attrs,
int defStyle)
85 super(context, attrs, defStyle);
86 initSessionView(context);
89 private void initSessionView(Context context)
93 setFocusableInTouchMode(
true);
95 invalidRegions =
new Stack<>();
96 gestureDetector =
new GestureDetector(context,
new SessionGestureListener(),
null,
true);
97 doubleGestureDetector =
98 new DoubleGestureDetector(context,
null,
new SessionDoubleGestureListener());
101 scaleMatrix =
new Matrix();
102 invScaleMatrix =
new Matrix();
103 invalidRegionF =
new RectF();
107 @Override
public boolean onHoverEvent(MotionEvent event)
109 if (event.getAction() == MotionEvent.ACTION_HOVER_MOVE)
112 float x =
event.getX();
113 float y =
event.getY();
115 MotionEvent mappedEvent = mapTouchEvent(event);
116 sessionViewListener.onSessionViewMouseMove((
int)mappedEvent.getX(),
117 (
int)mappedEvent.getY());
118 mappedEvent.recycle();
124 public void setScaleGestureDetector(ScaleGestureDetector scaleGestureDetector)
126 doubleGestureDetector.setScaleGestureDetector(scaleGestureDetector);
131 this.sessionViewListener = sessionViewListener;
134 public void addInvalidRegion(Rect invalidRegion)
137 invalidRegionF.set(invalidRegion);
138 scaleMatrix.mapRect(invalidRegionF);
139 invalidRegionF.roundOut(invalidRegion);
141 invalidRegions.add(invalidRegion);
144 public void invalidateRegion()
146 invalidate(invalidRegions.pop());
149 public void onSurfaceChange(SessionState session)
151 surface = session.getSurface();
152 Bitmap bitmap = surface.getBitmap();
153 width = bitmap.getWidth();
154 height = bitmap.getHeight();
155 surface.setBounds(0, 0, width, height);
157 setMinimumWidth(width);
158 setMinimumHeight(height);
161 currentSession = session;
164 public float getZoom()
169 public void setZoom(
float factor)
171 scaleFactor = factor;
172 scaleMatrix.setScale(scaleFactor, scaleFactor);
173 invScaleMatrix.setScale(1.0f / scaleFactor, 1.0f / scaleFactor);
175 if (cursorPixels !=
null)
181 public boolean isAtMaxZoom()
183 return (scaleFactor > (MAX_SCALE_FACTOR - SCALE_FACTOR_DELTA));
186 public boolean isAtMinZoom()
188 return (scaleFactor < (MIN_SCALE_FACTOR + SCALE_FACTOR_DELTA));
191 public boolean zoomIn(
float factor)
194 scaleFactor += factor;
195 if (scaleFactor > (MAX_SCALE_FACTOR - SCALE_FACTOR_DELTA))
197 scaleFactor = MAX_SCALE_FACTOR;
200 setZoom(scaleFactor);
204 public boolean zoomOut(
float factor)
207 scaleFactor -= factor;
208 if (scaleFactor < (MIN_SCALE_FACTOR + SCALE_FACTOR_DELTA))
210 scaleFactor = MIN_SCALE_FACTOR;
213 setZoom(scaleFactor);
217 public void setTouchPointerPadding(
int width,
int height)
219 touchPointerPaddingWidth = width;
220 touchPointerPaddingHeight = height;
224 public int getTouchPointerPaddingWidth()
226 return touchPointerPaddingWidth;
229 public int getTouchPointerPaddingHeight()
231 return touchPointerPaddingHeight;
234 @Override
public void onMeasure(
int widthMeasureSpec,
int heightMeasureSpec)
236 Log.v(TAG, width +
"x" + height);
237 this.setMeasuredDimension((
int)(width * scaleFactor) + touchPointerPaddingWidth,
238 (
int)(height * scaleFactor) + touchPointerPaddingHeight);
241 @Override
public void onDraw(@NonNull Canvas canvas)
243 super.onDraw(canvas);
246 canvas.concat(scaleMatrix);
247 canvas.drawColor(Color.BLACK);
250 surface.draw(canvas);
256 private MotionEvent mapTouchEvent(MotionEvent event)
258 MotionEvent mappedEvent = MotionEvent.obtain(event);
259 float[] coordinates = { mappedEvent.getX(), mappedEvent.getY() };
260 invScaleMatrix.mapPoints(coordinates);
261 mappedEvent.setLocation(coordinates[0], coordinates[1]);
266 private MotionEvent mapDoubleTouchEvent(MotionEvent event)
268 MotionEvent mappedEvent = MotionEvent.obtain(event);
269 float[] coordinates = { (mappedEvent.getX(0) + mappedEvent.getX(1)) / 2,
270 (mappedEvent.getY(0) + mappedEvent.getY(1)) / 2 };
271 invScaleMatrix.mapPoints(coordinates);
272 mappedEvent.setLocation(coordinates[0], coordinates[1]);
276 @Override
public boolean onTouchEvent(MotionEvent event)
281 if (event.isFromSource(InputDevice.SOURCE_MOUSE))
283 int action =
event.getActionMasked();
284 if (action == MotionEvent.ACTION_MOVE && event.getButtonState() != 0)
286 MotionEvent mapped = mapTouchEvent(event);
287 sessionViewListener.onSessionViewMouseMove((
int)mapped.getX(), (
int)mapped.getY());
294 boolean res = gestureDetector.onTouchEvent(event);
295 res |= doubleGestureDetector.onTouchEvent(event);
300 @Override
public boolean onGenericMotionEvent(MotionEvent event)
302 final boolean isPointer =
event.isFromSource(InputDevice.SOURCE_CLASS_POINTER);
306 final boolean isMouse =
event.isFromSource(InputDevice.SOURCE_MOUSE);
307 int action =
event.getActionMasked();
309 if (isMouse && (action == MotionEvent.ACTION_BUTTON_PRESS ||
310 action == MotionEvent.ACTION_BUTTON_RELEASE))
312 boolean down = action == MotionEvent.ACTION_BUTTON_PRESS;
313 MotionEvent mapped = mapTouchEvent(event);
314 int x = (int)mapped.getX();
315 int y = (int)mapped.getY();
318 switch (event.getActionButton())
320 case MotionEvent.BUTTON_PRIMARY:
322 sessionViewListener.onSessionViewBeginTouch();
323 sessionViewListener.onSessionViewLeftTouch(x, y, down);
325 sessionViewListener.onSessionViewEndTouch();
327 case MotionEvent.BUTTON_SECONDARY:
329 sessionViewListener.onSessionViewBeginTouch();
330 sessionViewListener.onSessionViewRightTouch(x, y, down);
332 case MotionEvent.BUTTON_TERTIARY:
333 sessionViewListener.onSessionViewMiddleTouch(x, y, down);
340 if (action == MotionEvent.ACTION_SCROLL)
342 float vScroll =
event.getAxisValue(MotionEvent.AXIS_VSCROLL);
343 float hScroll =
event.getAxisValue(MotionEvent.AXIS_HSCROLL);
345 sessionViewListener.onSessionViewScroll(vScroll > 0);
347 sessionViewListener.onSessionViewHScroll(hScroll > 0);
356 void onSessionViewBeginTouch();
358 void onSessionViewEndTouch();
360 void onSessionViewLeftTouch(
int x,
int y,
boolean down);
362 void onSessionViewMiddleTouch(
int x,
int y,
boolean down);
364 void onSessionViewRightTouch(
int x,
int y,
boolean down);
366 void onSessionViewMove(
int x,
int y);
368 void onSessionViewMouseMove(
int x,
int y);
370 void onSessionViewScroll(
boolean down);
372 void onSessionViewHScroll(
boolean right);
375 public void setRemoteCursor(
int[] pixels,
int width,
int height,
int hotX,
int hotY)
377 if (pixels ==
null || width == 0 || height == 0)
380 setPointerIcon(PointerIcon.getSystemIcon(getContext(), PointerIcon.TYPE_NULL));
383 cursorPixels = pixels;
385 cursorHeight = height;
391 private void applyScaledCursor()
393 int scaledWidth = Math.max(1, (
int)(cursorWidth * scaleFactor));
394 int scaledHeight = Math.max(1, (
int)(cursorHeight * scaleFactor));
396 Bitmap.createBitmap(cursorPixels, cursorWidth, cursorHeight, Bitmap.Config.ARGB_8888);
397 Bitmap scaled = Bitmap.createScaledBitmap(bm, scaledWidth, scaledHeight,
true);
399 PointerIcon.create(scaled, cursorHotX * scaleFactor, cursorHotY * scaleFactor);
400 setPointerIcon(icon);
403 public void setDefaultCursor()
405 setPointerIcon(PointerIcon.getSystemIcon(getContext(), PointerIcon.TYPE_ARROW));
408 private class SessionGestureListener
extends GestureDetector.SimpleOnGestureListener
410 boolean longPressInProgress =
false;
412 public boolean onDown(MotionEvent e)
417 public boolean onUp(MotionEvent e)
419 sessionViewListener.onSessionViewEndTouch();
423 public void onLongPress(MotionEvent e)
425 MotionEvent mappedEvent = mapTouchEvent(e);
426 sessionViewListener.onSessionViewBeginTouch();
427 sessionViewListener.onSessionViewLeftTouch((
int)mappedEvent.getX(),
428 (
int)mappedEvent.getY(),
true);
429 longPressInProgress =
true;
432 public void onLongPressUp(MotionEvent e)
434 MotionEvent mappedEvent = mapTouchEvent(e);
435 sessionViewListener.onSessionViewLeftTouch((
int)mappedEvent.getX(),
436 (
int)mappedEvent.getY(),
false);
437 longPressInProgress =
false;
438 sessionViewListener.onSessionViewEndTouch();
441 public boolean onScroll(MotionEvent e1, MotionEvent e2,
float distanceX,
float distanceY)
443 if (longPressInProgress)
445 MotionEvent mappedEvent = mapTouchEvent(e2);
446 sessionViewListener.onSessionViewMove((
int)mappedEvent.getX(),
447 (
int)mappedEvent.getY());
454 public boolean onDoubleTap(MotionEvent e)
457 MotionEvent mappedEvent = mapTouchEvent(e);
458 sessionViewListener.onSessionViewLeftTouch((
int)mappedEvent.getX(),
459 (
int)mappedEvent.getY(),
true);
460 sessionViewListener.onSessionViewLeftTouch((
int)mappedEvent.getX(),
461 (
int)mappedEvent.getY(),
false);
465 public boolean onSingleTapUp(MotionEvent e)
469 if (e.getButtonState() != 0)
473 MotionEvent mappedEvent = mapTouchEvent(e);
474 sessionViewListener.onSessionViewBeginTouch();
475 sessionViewListener.onSessionViewLeftTouch((
int)mappedEvent.getX(),
476 (
int)mappedEvent.getY(),
true);
477 sessionViewListener.onSessionViewLeftTouch((
int)mappedEvent.getX(),
478 (
int)mappedEvent.getY(),
false);
479 sessionViewListener.onSessionViewEndTouch();
484 private class SessionDoubleGestureListener
485 implements DoubleGestureDetector.OnDoubleGestureListener
487 private MotionEvent prevEvent =
null;
489 public boolean onDoubleTouchDown(MotionEvent e)
491 sessionViewListener.onSessionViewBeginTouch();
492 prevEvent = MotionEvent.obtain(e);
496 public boolean onDoubleTouchUp(MotionEvent e)
498 if (prevEvent !=
null)
503 sessionViewListener.onSessionViewEndTouch();
507 public boolean onDoubleTouchScroll(MotionEvent e1, MotionEvent e2)
510 float deltaY = e2.getY() - prevEvent.getY();
511 if (deltaY > TOUCH_SCROLL_DELTA)
513 sessionViewListener.onSessionViewScroll(
true);
515 prevEvent = MotionEvent.obtain(e2);
517 else if (deltaY < -TOUCH_SCROLL_DELTA)
519 sessionViewListener.onSessionViewScroll(
false);
521 prevEvent = MotionEvent.obtain(e2);
526 public boolean onDoubleTouchSingleTap(MotionEvent e)
529 MotionEvent mappedEvent = mapDoubleTouchEvent(e);
530 sessionViewListener.onSessionViewRightTouch((
int)mappedEvent.getX(),
531 (
int)mappedEvent.getY(),
true);
532 sessionViewListener.onSessionViewRightTouch((
int)mappedEvent.getX(),
533 (
int)mappedEvent.getY(),
false);
538 @Override
public InputConnection onCreateInputConnection(EditorInfo outAttrs)
540 outAttrs.actionLabel =
null;
541 outAttrs.inputType = InputType.TYPE_NULL;
542 outAttrs.imeOptions = EditorInfo.IME_ACTION_NONE | EditorInfo.IME_FLAG_NO_EXTRACT_UI |
543 EditorInfo.IME_FLAG_NO_FULLSCREEN;
544 return new BaseInputConnection(
this,
false);