34 private static final String TAG =
"FreeRDP.SessionInputManager";
36 private static final int SCROLLING_TIMEOUT = 16;
37 private static final int SCROLLING_DISTANCE = 12;
38 private static final int SCROLLING_EDGE_MARGIN = 16;
39 private static final int MAX_DISCARDED_MOVE_EVENTS = 3;
40 private static final int SEND_MOVE_EVENT_TIMEOUT = 150;
42 private static final int MSG_SEND_MOVE_EVENT = 1;
43 private static final int MSG_SCROLLING_REQUESTED = 2;
45 private final Context context;
46 private final KeyboardMapper keyboardMapper;
51 private final PinchZoomListener pinchZoomListener =
new PinchZoomListener();
54 private long instance = 0;
55 private Bitmap bitmap;
56 private int screenWidth;
57 private int screenHeight;
58 private int discardedMoveEvents = 0;
60 private boolean softInputRequested =
false;
62 private boolean softInputVisible =
false;
64 private final Handler handler;
69 this.context = context;
70 this.scrollView = scrollView;
71 this.sessionView = sessionView;
72 this.touchPointerView = touchPointerView;
73 this.keyboard = keyboard;
74 this.handler =
new InputHandler();
76 this.keyboardMapper =
new KeyboardMapper();
77 this.keyboardMapper.init(context);
79 keyboard.setListener(
this);
83 public void attachSession(
long instance, Bitmap surface)
85 this.instance = instance;
86 this.bitmap = surface;
87 keyboardMapper.reset(
this);
91 public void setBitmap(Bitmap bitmap)
97 public ScaleGestureDetector.OnScaleGestureListener getPinchZoomListener()
99 return pinchZoomListener;
103 public void setScreenSize(
int width,
int height)
105 this.screenWidth = width;
106 this.screenHeight = height;
110 public void toggleKeyboard()
112 if (keyboard.getVisibility() == View.VISIBLE)
118 keyboard.setExpanded(
false,
false);
119 keyboard.setVisibility(View.VISIBLE);
120 setSoftInputState(
true);
125 public void hideKeyboards()
127 keyboard.setExpanded(
false,
false);
128 keyboard.setVisibility(View.GONE);
129 setSoftInputState(
false);
130 keyboardMapper.clearlAllModifiers();
132 scrollView.post(this::refreshSystemBars);
136 public boolean handleKeyboardBack()
138 if (keyboard.getVisibility() != View.VISIBLE)
141 if (keyboard.isExpanded())
143 keyboard.setExpanded(
false,
false);
146 scrollView.requestApplyInsets();
155 public boolean isSoftInputActive()
157 return keyboard.getVisibility() == View.VISIBLE && (softInputRequested || softInputVisible);
162 public void onImeVisibilityChanged(
boolean visible)
164 if (visible == softInputVisible)
166 softInputVisible = visible;
168 softInputRequested =
false;
172 private void refreshSystemBars()
178 private void setSoftInputState(
boolean state)
180 softInputRequested = state;
182 softInputVisible =
false;
183 InputMethodManager mgr =
184 (InputMethodManager)context.getSystemService(Context.INPUT_METHOD_SERVICE);
188 sessionView.requestFocus();
189 mgr.showSoftInput(sessionView, InputMethodManager.SHOW_IMPLICIT);
193 mgr.hideSoftInputFromWindow(sessionView.getWindowToken(), 0);
196 scrollView.requestApplyInsets();
200 public void cancelPendingEvents()
202 handler.removeMessages(MSG_SEND_MOVE_EVENT);
206 public boolean onGenericMotionEvent(MotionEvent e)
210 if (e.getAction() != MotionEvent.ACTION_SCROLL)
213 final float vScroll = e.getAxisValue(MotionEvent.AXIS_VSCROLL);
215 LibFreeRDP.sendCursorEvent(instance, 0, 0, Mouse.getScrollEvent(context,
false));
216 else if (vScroll > 0)
217 LibFreeRDP.sendCursorEvent(instance, 0, 0, Mouse.getScrollEvent(context,
true));
222 public boolean onAndroidKeyEvent(KeyEvent event)
226 return keyboardMapper.processAndroidKeyEvent(event);
231 public boolean onAndroidKeyLongPress(
int keyCode)
235 if (keyCode == KeyEvent.KEYCODE_BACK)
237 LibFreeRDP.disconnect(instance);
244 public boolean handleBackAsAltF4()
250 keyboardMapper.sendAltF4();
255 public void toggleTouchPointer()
257 if (touchPointerView.getVisibility() == View.VISIBLE)
259 touchPointerView.setVisibility(View.INVISIBLE);
260 sessionView.setTouchPointerPadding(0, 0);
264 touchPointerView.setVisibility(View.VISIBLE);
265 sessionView.setTouchPointerPadding(touchPointerView.getPointerWidth(),
266 touchPointerView.getPointerHeight());
273 private void sendDelayedMoveEvent(
int x,
int y)
275 if (handler.hasMessages(MSG_SEND_MOVE_EVENT))
277 handler.removeMessages(MSG_SEND_MOVE_EVENT);
278 discardedMoveEvents++;
281 discardedMoveEvents = 0;
283 if (discardedMoveEvents > MAX_DISCARDED_MOVE_EVENTS)
284 LibFreeRDP.sendCursorEvent(instance, x, y, Mouse.getMoveEvent());
286 handler.sendMessageDelayed(Message.obtain(
null, MSG_SEND_MOVE_EVENT, x, y),
287 SEND_MOVE_EVENT_TIMEOUT);
290 private void cancelDelayedMoveEvent()
292 handler.removeMessages(MSG_SEND_MOVE_EVENT);
295 private Point mapScreenCoordToSessionCoord(
int x,
int y)
297 View container = scrollView.getChildCount() > 0 ? scrollView.getChildAt(0) : sessionView;
298 int mappedX = (int)((
float)(x - container.getLeft() + scrollView.getScrollX()) /
299 sessionView.getZoom());
300 int mappedY = (int)((
float)(y - container.getTop() + scrollView.getScrollY()) /
301 sessionView.getZoom());
308 if (mappedX > bitmap.getWidth())
309 mappedX = bitmap.getWidth();
310 if (mappedY > bitmap.getHeight())
311 mappedY = bitmap.getHeight();
313 return new Point(mappedX, mappedY);
319 @Override
public void onSessionViewBeginTouch()
324 @Override
public void onSessionViewEndTouch()
329 @Override
public void onSessionViewLeftTouch(
int x,
int y,
boolean down)
334 cancelDelayedMoveEvent();
335 LibFreeRDP.sendCursorEvent(instance, x, y, Mouse.getLeftButtonEvent(context, down));
338 @Override
public void onSessionViewMiddleTouch(
int x,
int y,
boolean down)
342 LibFreeRDP.sendCursorEvent(instance, x, y, Mouse.getMiddleButtonEvent(down));
345 @Override
public void onSessionViewRightTouch(
int x,
int y,
boolean down)
349 LibFreeRDP.sendCursorEvent(instance, x, y, Mouse.getRightButtonEvent(context, down));
352 @Override
public void onSessionViewMove(
int x,
int y)
356 sendDelayedMoveEvent(x, y);
359 @Override
public void onSessionViewMouseMove(
int x,
int y)
363 LibFreeRDP.sendCursorEvent(instance, x, y, Mouse.getMoveEvent());
366 @Override
public void onSessionViewScroll(
boolean down)
370 LibFreeRDP.sendCursorEvent(instance, 0, 0, Mouse.getScrollEvent(context, down));
373 @Override
public void onSessionViewHScroll(
boolean right)
377 LibFreeRDP.sendCursorEvent(instance, 0, 0, Mouse.getHScrollEvent(context, right));
383 @Override
public void onTouchPointerClose()
385 touchPointerView.setVisibility(View.INVISIBLE);
386 sessionView.setTouchPointerPadding(0, 0);
389 @Override
public void onTouchPointerLeftClick(
int x,
int y,
boolean down)
393 Point p = mapScreenCoordToSessionCoord(x, y);
394 LibFreeRDP.sendCursorEvent(instance, p.x, p.y, Mouse.getLeftButtonEvent(context, down));
397 @Override
public void onTouchPointerRightClick(
int x,
int y,
boolean down)
401 Point p = mapScreenCoordToSessionCoord(x, y);
402 LibFreeRDP.sendCursorEvent(instance, p.x, p.y, Mouse.getRightButtonEvent(context, down));
405 @Override
public void onTouchPointerMove(
int x,
int y)
409 Point p = mapScreenCoordToSessionCoord(x, y);
410 LibFreeRDP.sendCursorEvent(instance, p.x, p.y, Mouse.getMoveEvent());
413 !handler.hasMessages(MSG_SCROLLING_REQUESTED))
415 handler.sendEmptyMessageDelayed(MSG_SCROLLING_REQUESTED, SCROLLING_TIMEOUT);
419 @Override
public void onTouchPointerMoveEnd()
421 handler.removeMessages(MSG_SCROLLING_REQUESTED);
424 @Override
public void onTouchPointerScroll(
boolean down)
428 LibFreeRDP.sendCursorEvent(instance, 0, 0, Mouse.getScrollEvent(context, down));
431 @Override
public void onTouchPointerToggleKeyboard()
436 @Override
public void onTouchPointerResetScrollZoom()
438 sessionView.setZoom(1.0f);
445 @Override
public void processVirtualKey(
int virtualKeyCode,
boolean down)
449 LibFreeRDP.sendKeyEvent(instance, virtualKeyCode, down);
452 @Override
public void processUnicodeKey(
int unicodeKey)
456 if (LibFreeRDP.isUnicodeInputSupported(instance))
458 LibFreeRDP.sendUnicodeKeyEvent(instance, unicodeKey,
true);
459 LibFreeRDP.sendUnicodeKeyEvent(instance, unicodeKey,
false);
462 keyboardMapper.processUnicodeFallback(unicodeKey);
465 @Override
public void switchKeyboard(
int keyboardType)
467 switch (keyboardType)
469 case KeyboardMapper.KEYBOARD_TYPE_FUNCTIONKEYS:
473 case KeyboardMapper.KEYBOARD_TYPE_NUMPAD:
482 @Override
public void modifiersChanged()
484 keyboard.refreshModifiers();
490 @Override
public void onKey(
int keycode)
492 keyboardMapper.processCustomKeyEvent(keycode);
495 @Override
public void onKeyLock(
int keycode)
497 keyboardMapper.processCustomKeyLock(keycode);
500 @Override
public int getModifierState(
int keycode)
502 return keyboardMapper.getModifierState(keycode);
505 @Override
public void onExpandedChanged(
boolean expanded)
508 setSoftInputState(!expanded);
514 private class InputHandler
extends Handler
518 super(Looper.getMainLooper());
521 @Override
public void handleMessage(Message msg)
525 case MSG_SEND_MOVE_EVENT:
528 LibFreeRDP.sendCursorEvent(instance, msg.arg1, msg.arg2, Mouse.getMoveEvent());
531 case MSG_SCROLLING_REQUESTED:
535 float[] pointerPos = touchPointerView.getPointerPosition();
536 final int ow = touchPointerView.getWidth();
537 final int oh = touchPointerView.getHeight();
538 final int pw = touchPointerView.getPointerWidth();
539 final int ph = touchPointerView.getPointerHeight();
541 if (pointerPos[0] >= ow - pw - SCROLLING_EDGE_MARGIN)
542 scrollX = SCROLLING_DISTANCE;
543 else if (pointerPos[0] <= SCROLLING_EDGE_MARGIN)
544 scrollX = -SCROLLING_DISTANCE;
546 if (pointerPos[1] >= oh - ph - SCROLLING_EDGE_MARGIN)
547 scrollY = SCROLLING_DISTANCE;
548 else if (pointerPos[1] <= SCROLLING_EDGE_MARGIN)
549 scrollY = -SCROLLING_DISTANCE;
551 scrollView.scrollBy(scrollX, scrollY);
553 final int maxX = sessionView.getWidth() - scrollView.getWidth();
554 final int maxY = sessionView.getHeight() - scrollView.getHeight();
555 if ((scrollX < 0 && scrollView.getScrollX() <= 0) ||
556 (scrollX > 0 && scrollView.getScrollX() >= maxX))
558 if ((scrollY < 0 && scrollView.getScrollY() <= 0) ||
559 (scrollY > 0 && scrollView.getScrollY() >= maxY))
562 if (scrollX != 0 || scrollY != 0)
563 handler.sendEmptyMessageDelayed(MSG_SCROLLING_REQUESTED, SCROLLING_TIMEOUT);
573 private class PinchZoomListener
extends ScaleGestureDetector.SimpleOnScaleGestureListener
575 private float scaleFactor = 1.0f;
577 @Override
public boolean onScaleBegin(ScaleGestureDetector detector)
583 @Override
public boolean onScale(ScaleGestureDetector detector)
586 scaleFactor *= detector.getScaleFactor();
587 scaleFactor = Math.max(
SessionView.MIN_SCALE_FACTOR,
588 Math.min(scaleFactor,
SessionView.MAX_SCALE_FACTOR));
589 sessionView.setZoom(scaleFactor);
591 if (!sessionView.isAtMinZoom() && !sessionView.isAtMaxZoom())
594 float transOriginX = scrollView.getScrollX() * detector.getScaleFactor();
595 float transOriginY = scrollView.getScrollY() * detector.getScaleFactor();
599 (scrollView.getScrollX() + detector.getFocusX()) * detector.getScaleFactor();
601 (scrollView.getScrollY() + detector.getFocusY()) * detector.getScaleFactor();
606 scrollView.scrollBy((
int)((transCenterX - transOriginX) - detector.getFocusX()),
607 (
int)((transCenterY - transOriginY) - detector.getFocusY()));
613 @Override
public void onScaleEnd(ScaleGestureDetector de)