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