|
1 /* |
|
2 * Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). |
|
3 * All rights reserved. |
|
4 * This component and the accompanying materials are made available |
|
5 * under the terms of "Eclipse Public License v1.0" |
|
6 * which accompanies this distribution, and is available |
|
7 * at the URL "http://www.eclipse.org/legal/epl-v10.html". |
|
8 * |
|
9 * Initial Contributors: |
|
10 * Nokia Corporation - initial contribution. |
|
11 * |
|
12 * Contributors: |
|
13 * |
|
14 * Description: |
|
15 * |
|
16 */ |
|
17 |
|
18 package com.nokia.mid.ui; |
|
19 |
|
20 import org.eclipse.swt.widgets.Internal_PackageSupport; |
|
21 import com.nokia.mid.ui.internal.OS; |
|
22 |
|
23 ; |
|
24 |
|
25 /** |
|
26 * This class is a Java extension to support tactile feedback (audio, vibra, |
|
27 * piezo, etc.) on touch-enabled devices. Tactile feedback is meant to give user |
|
28 * more clear feeling of touch events, like tapping and dragging. The actual |
|
29 * physical tactile feedback depends on the device tactile feedback |
|
30 * implementation, hardware and device settings. A device may implement the |
|
31 * feedback with different hardware methods, for example, vibra, audio or piezo |
|
32 * hardware. |
|
33 * <p> |
|
34 * The API supports both LCDUI and eSWT UI toolkits. |
|
35 * <p> |
|
36 * Using this class is safe when a device does not support tactile feedback. In |
|
37 * this case methods of this class can be called but this has no effect. |
|
38 * <p> |
|
39 * Tactile feedback can be generated in two ways: |
|
40 * <ul> |
|
41 * <li>By producing direct feedback from the application.</li> |
|
42 * <li>By adding feedback areas to area registry, in which case the feedback |
|
43 * will be produced by the tactile feedback system automatically when the |
|
44 * defined screen area (with defined feedback) is touched.</li> |
|
45 * </ul> |
|
46 * <p> |
|
47 * Direct feedback is given instantly with {@link #directFeedback |
|
48 * directFeedback} specifying the feedback type. Direct feedback can be used |
|
49 * when the user action is not just a tap in a certain area. For example if the |
|
50 * user drags from one menu item to the other the application may give direct |
|
51 * feedback to emphasize focus traverse. |
|
52 * <p> |
|
53 * In case of area registry for eSWT Control, the registry needs to be updated |
|
54 * every time when the size or position of a UI component changes, or when e.g. |
|
55 * a state of a UI component changes in such way that feedback type also |
|
56 * changes. Typically the area update may be implemented in |
|
57 * <code>controlMoved</code> and <code>controlResized</code> methods of |
|
58 * <code>ControlListener</code> interface. |
|
59 * <p> |
|
60 * In case of area registry for LCDUI Canvas or CustomItem no further actions |
|
61 * are required to update tactile feedback areas when a CustomItem is moved in a |
|
62 * Form or when a Canvas is changed due to e.g. orientation switch. |
|
63 * <p> |
|
64 * There are quite tight latency requirements for tactile feedback, and in most |
|
65 * cases feedback should be triggered in less than 30ms from the time when user |
|
66 * touched the screen. For the area registry the feedback system is responsible |
|
67 * of fulfilling this requirement, but in case of direct feedback it is the |
|
68 * responsibility of the user of this API. |
|
69 * <p> |
|
70 * The tactile feedback has 2 different styles: Basic and Sensitive. Each type |
|
71 * is defined in the device settings, not by this API. Each style of feedback is |
|
72 * used in different cases. Typically Basic style is used to emphasize the tap |
|
73 * event, while Sensitive style is used mostly at dragging operations, to |
|
74 * emphasize focus traverse, text selection, etc. Choosing a specific style for |
|
75 * each use case in up to developer, however it is good to follow the behavior |
|
76 * of native applications. |
|
77 * <p> |
|
78 * The goal of the API is to enable user-created UI components to have tactile |
|
79 * feedback. The UI component types supported by the API are limited to |
|
80 * user-defined components only: <code>javax.microedition.lcdui.Canvas</code>, |
|
81 * <code>javax.microedition.lcdui.CustomItem</code>, |
|
82 * <code>org.eclipse.swt.widgets.Control</code>. Other UI components (i.e. List, |
|
83 * TextBox, etc.) get default tactile feedback automatically from the platform. |
|
84 * |
|
85 * @version 0.001 |
|
86 * @since 1.2 |
|
87 */ |
|
88 |
|
89 public class TactileFeedback |
|
90 { |
|
91 |
|
92 /** |
|
93 * Constant for specifying basic tactile feedback style. Actual style |
|
94 * behaviour is set through device settings. |
|
95 */ |
|
96 public static final int FEEDBACK_STYLE_BASIC = 1; |
|
97 |
|
98 /** |
|
99 * Constant for specifying sensitive tactile feedback style. Actual style |
|
100 * behaviour is set through device settings. |
|
101 */ |
|
102 public static final int FEEDBACK_STYLE_SENSITIVE = 2; |
|
103 |
|
104 private final String invalidFeedbackStyleMsg = "Invalid feedback style specified"; |
|
105 private final String invalidControlTypeMsg = "Invalid object provided for tactile feedback registration"; |
|
106 |
|
107 private static final int TYPE_INVALID = 0; |
|
108 private static final int TYPE_ESWT = 1; |
|
109 private static final int TYPE_LCDUI = 2; |
|
110 private static boolean feedbackEnabled; |
|
111 |
|
112 /** |
|
113 * Constructs tactile feedback engine object. The object may be used in both |
|
114 * LCDUI and eSWT java UI toolkits. TactileFeedback object must be |
|
115 * constructed before using any tactile feedback methods. |
|
116 */ |
|
117 public TactileFeedback() |
|
118 { |
|
119 } |
|
120 |
|
121 /** |
|
122 * Triggers direct tactile feedback of the specified style. |
|
123 * |
|
124 * @param style |
|
125 * The style of the direct feedback. Use predefined values |
|
126 * FEEDBACK_STYLE_BASIC, FEEDBACK_STYLE_SENSITIVE. Actual style |
|
127 * behavior is set through device settings. |
|
128 * @throws IllegalArgumentException |
|
129 * if the style parameter has invalid type. |
|
130 */ |
|
131 public void directFeedback(int style) throws IllegalArgumentException |
|
132 { |
|
133 if ((style != FEEDBACK_STYLE_BASIC) |
|
134 && (style != FEEDBACK_STYLE_SENSITIVE)) |
|
135 throw new IllegalArgumentException(invalidFeedbackStyleMsg); |
|
136 if (org.eclipse.swt.widgets.Display.getCurrent() == null) |
|
137 { |
|
138 final int fStyle = style; |
|
139 com.nokia.mj.impl.nokialcdui.LCDUIInvoker |
|
140 .eSWTUIThreadRunnerSyncExec(new Runnable() |
|
141 { |
|
142 public void run() |
|
143 { |
|
144 OS.MTouchFeedback_InstantFeedback(fStyle); |
|
145 } |
|
146 }); |
|
147 |
|
148 } |
|
149 else |
|
150 { |
|
151 OS.MTouchFeedback_InstantFeedback(style); |
|
152 } |
|
153 } |
|
154 |
|
155 /** |
|
156 * Queries the device if it supports tactile feedback. It is safe to use |
|
157 * this class even if the device does not support tactile feedback. Methods |
|
158 * of this class can be called but this has no effect. |
|
159 * |
|
160 * @return true if the device supports tactile feedback, false otherwise. |
|
161 */ |
|
162 public boolean isTouchFeedbackSupported() |
|
163 { |
|
164 if (org.eclipse.swt.widgets.Display.getCurrent() == null) |
|
165 { |
|
166 com.nokia.mj.impl.nokialcdui.LCDUIInvoker |
|
167 .eSWTUIThreadRunnerSyncExec(new Runnable() |
|
168 { |
|
169 |
|
170 public void run() |
|
171 { |
|
172 feedbackEnabled = OS |
|
173 .MTouchFeedback_TouchFeedbackSupported(); |
|
174 } |
|
175 }); |
|
176 } |
|
177 else |
|
178 { |
|
179 feedbackEnabled = OS.MTouchFeedback_TouchFeedbackSupported(); |
|
180 } |
|
181 return feedbackEnabled; |
|
182 } |
|
183 |
|
184 /** |
|
185 * Registers area within a UI component for tactile feedback. If areas |
|
186 * overlap then the feedback is given from the last registered area. |
|
187 * {@link #moveFeedbackAreaToFirstPriority moveFeedbackAreaToFirstPriority} |
|
188 * can be used to bring a certain area covered by another area into action. |
|
189 * If an area with specified <code>id</code> is already registered it is |
|
190 * updated. When updating an existing area there is no need to unregister it |
|
191 * before re-registering again with new coordinates and/or feedback style. |
|
192 * The area specified by <code>x</code>, <code>y</code>, <code>width</code> |
|
193 * and <code>height</code> parameters may be located on the screen just |
|
194 * partially. |
|
195 * |
|
196 * <p> |
|
197 * In case of area registry for eSWT Control, the registry needs to be |
|
198 * updated every time when the size or position of a UI component changes, |
|
199 * or when e.g. a state of a UI component changes in such way that feedback |
|
200 * type also changes. Typically the area update may be implemented in |
|
201 * <code>controlMoved</code> and <code>controlResized</code> methods of |
|
202 * <code>ControlListener</code> interface. The update of an area may be |
|
203 * implemented by calling registerFeedbackArea with the same area id but new |
|
204 * coordinates and/or feedback style. |
|
205 * <p> |
|
206 * In case of area registry for LCDUI Canvas or CustomItem no further |
|
207 * actions are required to update tactile feedback areas when a CustomItem |
|
208 * is moved in a Form or when a Canvas is changed due to e.g. orientation |
|
209 * switch. |
|
210 * |
|
211 * @param uiObject |
|
212 * The UI component for tactile feedback registration. Valid |
|
213 * object types are: <code>javax.microedition.lcdui.Canvas</code> |
|
214 * , <code>javax.microedition.lcdui.CustomItem</code>, |
|
215 * <code>org.eclipse.swt.widgets.Control</code>. |
|
216 * @param id |
|
217 * Id of the new tactile feedback area to be registered. Id's are |
|
218 * used to identify particular tactile feedback area within one |
|
219 * UI component. Id's do not need to be consecutive numbers. |
|
220 * @param x |
|
221 * x-coordinate of the top-left corner of tactile feedback |
|
222 * rectangle to register. |
|
223 * @param y |
|
224 * y-coordinate of the top-left corner of tactile feedback |
|
225 * rectangle to register. |
|
226 * @param width |
|
227 * Width of tactile feedback rectangle to register. |
|
228 * @param height |
|
229 * Height of tactile feedback rectangle to register. |
|
230 * @param style |
|
231 * The style of the feedback for specified area. Use predefined |
|
232 * values <code>FEEDBACK_STYLE_BASIC</code>, |
|
233 * <code>FEEDBACK_STYLE_SENSITIVE</code>. Actual style behaviour |
|
234 * is defined through device settings. |
|
235 * @throws IllegalArgumentException |
|
236 * if the uiObject parameter has invalid type. |
|
237 * @throws IllegalArgumentException |
|
238 * if the style parameter has invalid type. |
|
239 */ |
|
240 public void registerFeedbackArea(Object uiObject, int id, int x, int y, |
|
241 int width, int height, int style) throws IllegalArgumentException |
|
242 { |
|
243 int type = controlType(uiObject); |
|
244 if (type == TYPE_INVALID) |
|
245 throw new IllegalArgumentException(invalidControlTypeMsg); |
|
246 |
|
247 if ((style != FEEDBACK_STYLE_BASIC) |
|
248 && (style != FEEDBACK_STYLE_SENSITIVE)) |
|
249 throw new IllegalArgumentException(invalidFeedbackStyleMsg); |
|
250 |
|
251 int controlHandle = getControlHandle(uiObject); |
|
252 if (type == TYPE_LCDUI) |
|
253 { |
|
254 final int fControlHandle = controlHandle; |
|
255 final int fId = id; |
|
256 final int fX = x; |
|
257 final int fY = y; |
|
258 final int fWidth = width; |
|
259 final int fHeight = height; |
|
260 final int fStyle = style; |
|
261 com.nokia.mj.impl.nokialcdui.LCDUIInvoker |
|
262 .eSWTUIThreadRunnerSyncExec(new Runnable() |
|
263 { |
|
264 |
|
265 public void run() |
|
266 { |
|
267 OS.MTouchFeedback_SetFeedbackArea(fControlHandle, |
|
268 fId, fX, fY, fWidth, fHeight, fStyle); |
|
269 } |
|
270 }); |
|
271 } |
|
272 else |
|
273 { |
|
274 OS.MTouchFeedback_SetFeedbackArea(controlHandle, id, x, y, width, |
|
275 height, style); |
|
276 } |
|
277 } |
|
278 |
|
279 /** |
|
280 * Unregisters tactile feedback area within a UI component. |
|
281 * |
|
282 * @param uiObject |
|
283 * The UI component for tactile feedback area de-registration. |
|
284 * Valid object types are: |
|
285 * <code>javax.microedition.lcdui.Canvas</code>, |
|
286 * <code>javax.microedition.lcdui.CustomItem</code>, |
|
287 * <code>org.eclipse.swt.widgets.Control</code>. |
|
288 * @param id |
|
289 * Id of the tactile feedback area to be unregistered. Id's are |
|
290 * used to identify particular tactile feedback area within one |
|
291 * UI component. If given id was not registered by |
|
292 * {@link #registerFeedbackArea registerFeedbackArea} then the |
|
293 * call has no effect. |
|
294 * @throws IllegalArgumentException |
|
295 * if the uiObject parameter has invalid type. |
|
296 */ |
|
297 public void unregisterFeedbackArea(Object uiObject, int id) |
|
298 throws IllegalArgumentException |
|
299 { |
|
300 |
|
301 int type = controlType(uiObject); |
|
302 if (type == TYPE_INVALID) |
|
303 throw new IllegalArgumentException(invalidControlTypeMsg); |
|
304 |
|
305 int controlHandle = getControlHandle(uiObject); |
|
306 if (type == TYPE_LCDUI) |
|
307 { |
|
308 final int fControlHandle = controlHandle; |
|
309 final int fId = id; |
|
310 com.nokia.mj.impl.nokialcdui.LCDUIInvoker |
|
311 .eSWTUIThreadRunnerSyncExec(new Runnable() |
|
312 { |
|
313 |
|
314 public void run() |
|
315 { |
|
316 OS.MTouchFeedback_RemoveFeedbackArea( |
|
317 fControlHandle, fId); |
|
318 } |
|
319 }); |
|
320 } |
|
321 else |
|
322 { |
|
323 OS.MTouchFeedback_RemoveFeedbackArea(controlHandle, id); |
|
324 } |
|
325 |
|
326 } |
|
327 |
|
328 /** |
|
329 * Removes all tactile feedback for a UI component. |
|
330 * |
|
331 * @param uiObject |
|
332 * The UI component for tactile feedback area de-registration. |
|
333 * Valid object types are: |
|
334 * <code>javax.microedition.lcdui.Canvas</code>, |
|
335 * <code>javax.microedition.lcdui.CustomItem</code>, |
|
336 * <code>org.eclipse.swt.widgets.Control</code>. |
|
337 * @throws IllegalArgumentException |
|
338 * if the uiObject parameter has invalid type. |
|
339 */ |
|
340 public void removeFeedbackForComponent(Object uiObject) |
|
341 { |
|
342 if (isTouchFeedbackSupported() == false) |
|
343 return; |
|
344 |
|
345 int type = controlType(uiObject); |
|
346 if (type == TYPE_INVALID) |
|
347 throw new IllegalArgumentException(invalidControlTypeMsg); |
|
348 |
|
349 int controlHandle = getControlHandle(uiObject); |
|
350 |
|
351 if (type == TYPE_LCDUI) |
|
352 { |
|
353 final int fControlHandle = controlHandle; |
|
354 com.nokia.mj.impl.nokialcdui.LCDUIInvoker |
|
355 .eSWTUIThreadRunnerSyncExec(new Runnable() |
|
356 { |
|
357 |
|
358 public void run() |
|
359 { |
|
360 OS.MTouchFeedback_RemoveFeedbackForControl(fControlHandle); |
|
361 } |
|
362 }); |
|
363 } |
|
364 else |
|
365 { |
|
366 OS.MTouchFeedback_RemoveFeedbackForControl(controlHandle); |
|
367 } |
|
368 |
|
369 } |
|
370 |
|
371 /** |
|
372 * Moves the specified tactile feedback area to first priority. Priority is |
|
373 * significant in case of overlapping tactile feedback areas. If the tactile |
|
374 * feedback styles of registered areas are different, the feedback from |
|
375 * first-priority area will be given. |
|
376 * |
|
377 * @param uiObject |
|
378 * The UI component for tactile feedback area de-registration. |
|
379 * Valid object types are: |
|
380 * <code>javax.microedition.lcdui.Canvas</code>, |
|
381 * <code>javax.microedition.lcdui.CustomItem</code>, |
|
382 * <code>org.eclipse.swt.widgets.Control</code>. |
|
383 * @param id |
|
384 * Id of the tactile feedback area to be unregistered. Id's are |
|
385 * used to identify particular tactile feedback area within one |
|
386 * UI component. If given id was not registered by |
|
387 * {@link #registerFeedbackArea registerFeedbackArea} then the |
|
388 * call has no effect. |
|
389 * @throws IllegalArgumentException |
|
390 * if the uiObject parameter has invalid type. |
|
391 */ |
|
392 public void moveFeedbackAreaToFirstPriority(Object uiObject, int id) |
|
393 { |
|
394 if (isTouchFeedbackSupported() == false) |
|
395 return; |
|
396 |
|
397 int type = controlType(uiObject); |
|
398 if (type == TYPE_INVALID) |
|
399 throw new IllegalArgumentException(invalidControlTypeMsg); |
|
400 |
|
401 int controlHandle = getControlHandle(uiObject); |
|
402 if (type == TYPE_LCDUI) |
|
403 { |
|
404 final int fControlHandle = controlHandle; |
|
405 final int fId = id; |
|
406 com.nokia.mj.impl.nokialcdui.LCDUIInvoker |
|
407 .eSWTUIThreadRunnerSyncExec(new Runnable() |
|
408 { |
|
409 |
|
410 public void run() |
|
411 { |
|
412 OS.MTouchFeedback_MoveFeedbackAreaToFirstPriority( |
|
413 fControlHandle, fId); |
|
414 } |
|
415 }); |
|
416 } |
|
417 else |
|
418 { |
|
419 OS.MTouchFeedback_MoveFeedbackAreaToFirstPriority( |
|
420 controlHandle, id); |
|
421 } |
|
422 |
|
423 } |
|
424 |
|
425 |
|
426 private int controlType(Object obj) |
|
427 { |
|
428 if ((obj instanceof javax.microedition.lcdui.Canvas) |
|
429 || (obj instanceof javax.microedition.lcdui.CustomItem)) |
|
430 { |
|
431 return TYPE_LCDUI; |
|
432 } |
|
433 else if ((obj instanceof org.eclipse.swt.widgets.Control)) |
|
434 { |
|
435 return TYPE_ESWT; |
|
436 } |
|
437 return TYPE_INVALID; |
|
438 } |
|
439 |
|
440 private int getControlHandle(Object uiObject) |
|
441 { |
|
442 int controlHandle = 0; |
|
443 org.eclipse.swt.widgets.Control eSwtControl = null; |
|
444 if (uiObject instanceof javax.microedition.lcdui.Canvas |
|
445 || uiObject instanceof javax.microedition.lcdui.CustomItem) |
|
446 { |
|
447 eSwtControl = com.nokia.mj.impl.nokialcdui.LCDUIInvoker |
|
448 .getEswtControl(uiObject); |
|
449 } |
|
450 else if (uiObject instanceof org.eclipse.swt.widgets.Control) |
|
451 { |
|
452 eSwtControl = (org.eclipse.swt.widgets.Control) uiObject; |
|
453 } |
|
454 |
|
455 if (eSwtControl != null) |
|
456 { |
|
457 controlHandle = Internal_PackageSupport.topHandle(eSwtControl); |
|
458 } |
|
459 return controlHandle; |
|
460 } |
|
461 |
|
462 } |