FreeRDP
Loading...
Searching...
No Matches
TouchPointerView.java
1/*
2 Android Touch Pointer view
3
4 Copyright 2013 Thincast Technologies GmbH, Author: Martin Fleisz
5
6 This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
7 If a copy of the MPL was not distributed with this file, You can obtain one at
8 http://mozilla.org/MPL/2.0/.
9*/
10
11package com.freerdp.freerdpcore.presentation;
12
13import android.content.Context;
14import android.graphics.Matrix;
15import android.graphics.RectF;
16import android.os.Handler;
17import android.os.Message;
18import android.util.AttributeSet;
19import android.view.MotionEvent;
20import android.widget.ImageView;
21
22import com.freerdp.freerdpcore.R;
23import com.freerdp.freerdpcore.utils.GestureDetector;
24
25public class TouchPointerView extends ImageView
26{
27
28 private static final int POINTER_ACTION_CURSOR = 0;
29 private static final int POINTER_ACTION_CLOSE = 3;
30
31 // the touch pointer consists of 9 quadrants with the following functionality:
32 //
33 // -------------
34 // | 0 | 1 | 2 |
35 // -------------
36 // | 3 | 4 | 5 |
37 // -------------
38 // | 6 | 7 | 8 |
39 // -------------
40 //
41 // 0 ... contains the actual pointer (the tip must be centered in the quadrant)
42 // 1 ... is left empty
43 // 2, 3, 5, 6, 7, 8 ... function quadrants that issue a callback
44 // 4 ... pointer center used for left clicks and to drag the pointer
45 private static final int POINTER_ACTION_RCLICK = 2;
46 private static final int POINTER_ACTION_LCLICK = 4;
47 private static final int POINTER_ACTION_MOVE = 4;
48 private static final int POINTER_ACTION_SCROLL = 5;
49 private static final int POINTER_ACTION_RESET = 6;
50 private static final int POINTER_ACTION_KEYBOARD = 7;
51 private static final int POINTER_ACTION_EXTKEYBOARD = 8;
52 private static final float SCROLL_DELTA = 10.0f;
53 private static final int DEFAULT_TOUCH_POINTER_RESTORE_DELAY = 150;
54 private RectF pointerRect;
55 private final RectF[] pointerAreaRects = new RectF[9];
56 private Matrix translationMatrix;
57 private boolean pointerMoving = false;
58 private boolean pointerScrolling = false;
59 private TouchPointerListener listener = null;
60 private final UIHandler uiHandler = new UIHandler();
61 // gesture detection
62 private GestureDetector gestureDetector;
63 public TouchPointerView(Context context)
64 {
65 super(context);
66 initTouchPointer(context);
67 }
68
69 public TouchPointerView(Context context, AttributeSet attrs)
70 {
71 super(context, attrs);
72 initTouchPointer(context);
73 }
74
75 public TouchPointerView(Context context, AttributeSet attrs, int defStyle)
76 {
77 super(context, attrs, defStyle);
78 initTouchPointer(context);
79 }
80
81 private void initTouchPointer(Context context)
82 {
83 gestureDetector =
84 new GestureDetector(context, new TouchPointerGestureListener(), null, true);
85 gestureDetector.setLongPressTimeout(500);
86 translationMatrix = new Matrix();
87 setScaleType(ScaleType.MATRIX);
88 setImageMatrix(translationMatrix);
89
90 // init rects
91 final float rectSizeWidth = (float)getDrawable().getIntrinsicWidth() / 3.0f;
92 final float rectSizeHeight = (float)getDrawable().getIntrinsicWidth() / 3.0f;
93 for (int i = 0; i < 3; i++)
94 {
95 for (int j = 0; j < 3; j++)
96 {
97 int left = (int)(j * rectSizeWidth);
98 int top = (int)(i * rectSizeHeight);
99 int right = left + (int)rectSizeWidth;
100 int bottom = top + (int)rectSizeHeight;
101 pointerAreaRects[i * 3 + j] = new RectF(left, top, right, bottom);
102 }
103 }
104 pointerRect =
105 new RectF(0, 0, getDrawable().getIntrinsicWidth(), getDrawable().getIntrinsicHeight());
106 }
107
108 public void setTouchPointerListener(TouchPointerListener listener)
109 {
110 this.listener = listener;
111 }
112
113 public int getPointerWidth()
114 {
115 return getDrawable().getIntrinsicWidth();
116 }
117
118 public int getPointerHeight()
119 {
120 return getDrawable().getIntrinsicHeight();
121 }
122
123 public float[] getPointerPosition()
124 {
125 float[] curPos = new float[2];
126 translationMatrix.mapPoints(curPos);
127 return curPos;
128 }
129
130 private void movePointer(float deltaX, float deltaY)
131 {
132 translationMatrix.postTranslate(deltaX, deltaY);
133 setImageMatrix(translationMatrix);
134 }
135
136 private void ensureVisibility(int screen_width, int screen_height)
137 {
138 float[] curPos = new float[2];
139 translationMatrix.mapPoints(curPos);
140
141 if (curPos[0] > (screen_width - pointerRect.width()))
142 curPos[0] = screen_width - pointerRect.width();
143 if (curPos[0] < 0)
144 curPos[0] = 0;
145 if (curPos[1] > (screen_height - pointerRect.height()))
146 curPos[1] = screen_height - pointerRect.height();
147 if (curPos[1] < 0)
148 curPos[1] = 0;
149
150 translationMatrix.setTranslate(curPos[0], curPos[1]);
151 setImageMatrix(translationMatrix);
152 }
153
154 private void displayPointerImageAction(int resId)
155 {
156 setPointerImage(resId);
157 uiHandler.sendEmptyMessageDelayed(0, DEFAULT_TOUCH_POINTER_RESTORE_DELAY);
158 }
159
160 private void setPointerImage(int resId)
161 {
162 setImageResource(resId);
163 }
164
165 // returns the pointer area with the current translation matrix applied
166 private RectF getCurrentPointerArea(int area)
167 {
168 RectF transRect = new RectF(pointerAreaRects[area]);
169 translationMatrix.mapRect(transRect);
170 return transRect;
171 }
172
173 private boolean pointerAreaTouched(MotionEvent event, int area)
174 {
175 RectF transRect = new RectF(pointerAreaRects[area]);
176 translationMatrix.mapRect(transRect);
177 return transRect.contains(event.getX(), event.getY());
178 }
179
180 private boolean pointerTouched(MotionEvent event)
181 {
182 RectF transRect = new RectF(pointerRect);
183 translationMatrix.mapRect(transRect);
184 return transRect.contains(event.getX(), event.getY());
185 }
186
187 @Override public boolean onTouchEvent(MotionEvent event)
188 {
189 // check if pointer is being moved or if we are in scroll mode or if the pointer is touched
190 if (!pointerMoving && !pointerScrolling && !pointerTouched(event))
191 return false;
192 return gestureDetector.onTouchEvent(event);
193 }
194
195 @Override protected void onLayout(boolean changed, int left, int top, int right, int bottom)
196 {
197 // ensure touch pointer is visible
198 if (changed)
199 ensureVisibility(right - left, bottom - top);
200 }
201
202 // touch pointer listener - is triggered if an action field is
203 public interface TouchPointerListener {
204 void onTouchPointerClose();
205
206 void onTouchPointerLeftClick(int x, int y, boolean down);
207
208 void onTouchPointerRightClick(int x, int y, boolean down);
209
210 void onTouchPointerMove(int x, int y);
211
212 void onTouchPointerScroll(boolean down);
213
214 void onTouchPointerToggleKeyboard();
215
216 void onTouchPointerToggleExtKeyboard();
217
218 void onTouchPointerResetScrollZoom();
219 }
220
221 private class UIHandler extends Handler
222 {
223
224 UIHandler()
225 {
226 super();
227 }
228
229 @Override public void handleMessage(Message msg)
230 {
231 setPointerImage(R.drawable.touch_pointer_default);
232 }
233 }
234
235 private class TouchPointerGestureListener extends GestureDetector.SimpleOnGestureListener
236 {
237
238 private MotionEvent prevEvent = null;
239
240 public boolean onDown(MotionEvent e)
241 {
242 if (pointerAreaTouched(e, POINTER_ACTION_MOVE))
243 {
244 prevEvent = MotionEvent.obtain(e);
245 pointerMoving = true;
246 }
247 else if (pointerAreaTouched(e, POINTER_ACTION_SCROLL))
248 {
249 prevEvent = MotionEvent.obtain(e);
250 pointerScrolling = true;
251 setPointerImage(R.drawable.touch_pointer_scroll);
252 }
253
254 return true;
255 }
256
257 public boolean onUp(MotionEvent e)
258 {
259 if (prevEvent != null)
260 {
261 prevEvent.recycle();
262 prevEvent = null;
263 }
264
265 if (pointerScrolling)
266 setPointerImage(R.drawable.touch_pointer_default);
267
268 pointerMoving = false;
269 pointerScrolling = false;
270 return true;
271 }
272
273 public void onLongPress(MotionEvent e)
274 {
275 if (pointerAreaTouched(e, POINTER_ACTION_LCLICK))
276 {
277 setPointerImage(R.drawable.touch_pointer_active);
278 pointerMoving = true;
279 RectF rect = getCurrentPointerArea(POINTER_ACTION_CURSOR);
280 listener.onTouchPointerLeftClick((int)rect.centerX(), (int)rect.centerY(), true);
281 }
282 }
283
284 public void onLongPressUp(MotionEvent e)
285 {
286 if (pointerMoving)
287 {
288 setPointerImage(R.drawable.touch_pointer_default);
289 pointerMoving = false;
290 RectF rect = getCurrentPointerArea(POINTER_ACTION_CURSOR);
291 listener.onTouchPointerLeftClick((int)rect.centerX(), (int)rect.centerY(), false);
292 }
293 }
294
295 public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY)
296 {
297 if (pointerMoving)
298 {
299 // move pointer graphics
300 movePointer((int)(e2.getX() - prevEvent.getX()),
301 (int)(e2.getY() - prevEvent.getY()));
302 prevEvent.recycle();
303 prevEvent = MotionEvent.obtain(e2);
304
305 // send move notification
306 RectF rect = getCurrentPointerArea(POINTER_ACTION_CURSOR);
307 listener.onTouchPointerMove((int)rect.centerX(), (int)rect.centerY());
308 return true;
309 }
310 else if (pointerScrolling)
311 {
312 // calc if user scrolled up or down (or if any scrolling happened at all)
313 float deltaY = e2.getY() - prevEvent.getY();
314 if (deltaY > SCROLL_DELTA)
315 {
316 listener.onTouchPointerScroll(true);
317 prevEvent.recycle();
318 prevEvent = MotionEvent.obtain(e2);
319 }
320 else if (deltaY < -SCROLL_DELTA)
321 {
322 listener.onTouchPointerScroll(false);
323 prevEvent.recycle();
324 prevEvent = MotionEvent.obtain(e2);
325 }
326 return true;
327 }
328 return false;
329 }
330
331 public boolean onSingleTapUp(MotionEvent e)
332 {
333 // look what area got touched and fire actions accordingly
334 if (pointerAreaTouched(e, POINTER_ACTION_CLOSE))
335 listener.onTouchPointerClose();
336 else if (pointerAreaTouched(e, POINTER_ACTION_LCLICK))
337 {
338 displayPointerImageAction(R.drawable.touch_pointer_lclick);
339 RectF rect = getCurrentPointerArea(POINTER_ACTION_CURSOR);
340 listener.onTouchPointerLeftClick((int)rect.centerX(), (int)rect.centerY(), true);
341 listener.onTouchPointerLeftClick((int)rect.centerX(), (int)rect.centerY(), false);
342 }
343 else if (pointerAreaTouched(e, POINTER_ACTION_RCLICK))
344 {
345 displayPointerImageAction(R.drawable.touch_pointer_rclick);
346 RectF rect = getCurrentPointerArea(POINTER_ACTION_CURSOR);
347 listener.onTouchPointerRightClick((int)rect.centerX(), (int)rect.centerY(), true);
348 listener.onTouchPointerRightClick((int)rect.centerX(), (int)rect.centerY(), false);
349 }
350 else if (pointerAreaTouched(e, POINTER_ACTION_KEYBOARD))
351 {
352 displayPointerImageAction(R.drawable.touch_pointer_keyboard);
353 listener.onTouchPointerToggleKeyboard();
354 }
355 else if (pointerAreaTouched(e, POINTER_ACTION_EXTKEYBOARD))
356 {
357 displayPointerImageAction(R.drawable.touch_pointer_extkeyboard);
358 listener.onTouchPointerToggleExtKeyboard();
359 }
360 else if (pointerAreaTouched(e, POINTER_ACTION_RESET))
361 {
362 displayPointerImageAction(R.drawable.touch_pointer_reset);
363 listener.onTouchPointerResetScrollZoom();
364 }
365
366 return true;
367 }
368
369 public boolean onDoubleTap(MotionEvent e)
370 {
371 // issue a double click notification if performed in center quadrant
372 if (pointerAreaTouched(e, POINTER_ACTION_LCLICK))
373 {
374 RectF rect = getCurrentPointerArea(POINTER_ACTION_CURSOR);
375 listener.onTouchPointerLeftClick((int)rect.centerX(), (int)rect.centerY(), true);
376 listener.onTouchPointerLeftClick((int)rect.centerX(), (int)rect.centerY(), false);
377 }
378 return true;
379 }
380 }
381}