Initial checkin of RIM client from LifeMarks, after initial refactorings and style...
[zxing.git] / rim / src / com / google / zxing / client / rim / Camera.java
diff --git a/rim/src/com/google/zxing/client/rim/Camera.java b/rim/src/com/google/zxing/client/rim/Camera.java
new file mode 100644 (file)
index 0000000..2107798
--- /dev/null
@@ -0,0 +1,213 @@
+/*\r
+ * Copyright 2008 ZXing authors\r
+ *\r
+ * Licensed under the Apache License, Version 2.0 (the "License");\r
+ * you may not use this file except in compliance with the License.\r
+ * You may obtain a copy of the License at\r
+ *\r
+ *      http://www.apache.org/licenses/LICENSE-2.0\r
+ *\r
+ * Unless required by applicable law or agreed to in writing, software\r
+ * distributed under the License is distributed on an "AS IS" BASIS,\r
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
+ * See the License for the specific language governing permissions and\r
+ * limitations under the License.\r
+ */\r
+\r
+package com.google.zxing.client.rim;\r
+\r
+import com.google.zxing.client.rim.util.Log;\r
+import net.rim.blackberry.api.invoke.CameraArguments;\r
+import net.rim.blackberry.api.invoke.Invoke;\r
+import net.rim.device.api.system.Characters;\r
+import net.rim.device.api.system.EventInjector;\r
+import net.rim.device.api.ui.UiApplication;\r
+\r
+/**\r
+ * Singleton used to control access to the camera.\r
+ * Unfortunatly, the Camera API only allows invoking the camera.\r
+ * \r
+ * Note: This code still contains experimental code to determine and set the camera resolution by\r
+ * using system level key events, but didn't not function reliably and is not used.\r
+ *\r
+ * This code was contributed by LifeMarks.\r
+ *\r
+ * @author Matt York (matt@lifemarks.mobi)\r
+ */\r
+final class Camera {\r
+\r
+  /** milliseconds to wait before starting key strokes */\r
+  private static final int INITIALIZATION_TIME_MS = 500; // simulator seems to need >= 500\r
+  private static final int KEY_PAUSE_TIME_MS = 100; // simulator seems to need >= 100\r
+\r
+  private static Camera instance;\r
+\r
+  /** Attempting to set camera resolution is disabled. */\r
+  private final boolean setResolution = false;\r
+\r
+  private Camera() {\r
+  }\r
+\r
+  /**\r
+   * Returns the single instance of the camera.\r
+   */\r
+  static Camera getInstance() {\r
+    if (instance == null) {\r
+      instance = new Camera();\r
+    }\r
+    return instance;\r
+  }\r
+\r
+  /**\r
+   * Starts the blackberry camera application.\r
+   */\r
+  void invoke() {\r
+    Invoke.invokeApplication(Invoke.APP_TYPE_CAMERA, new CameraArguments());\r
+    if (setResolution) {\r
+      sleep(INITIALIZATION_TIME_MS);\r
+      setMinResolution();\r
+    }\r
+  }\r
+\r
+  /**\r
+   * Exits the blackberry camera application.\r
+   */\r
+  void exit() {\r
+    if (setResolution) {\r
+      setMaxResolution(); // for now, we dont know the original resolution setting. Assume it was max res.\r
+      sleep(KEY_PAUSE_TIME_MS);\r
+    }\r
+    sleep(3000); // this sleep is needed for the esc to be processed(3000 originally)\r
+    UiApplication app = UiApplication.getUiApplication();\r
+    if (app != null) {\r
+      Log.info("active app: " + app.getClass().getName());\r
+      if (app.isForeground()) {\r
+        Log.info("Lifemarks is the foreground app.");\r
+      } else {\r
+        Log.info("Lifemarks is not the foreground app. Attempt to close camera.");\r
+        keyUpAndDown(Characters.ESCAPE); // need two (no timeout in between esc key presses seems to work best)\r
+        keyUpAndDown(Characters.ESCAPE);\r
+      }\r
+    } else {\r
+      Log.error("??? app is null ???");\r
+    }\r
+  }\r
+\r
+  /**\r
+   * Sets the camera resolution to it's minimum.\r
+   * Note: currently disabled.\r
+   */\r
+  private static void setMaxResolution() {\r
+    Log.info("Setting resolution to max.");\r
+    accessResolutionMenuAfterSave();\r
+    sleep(KEY_PAUSE_TIME_MS);\r
+    keyUpAndDown(Characters.CONTROL_DOWN);\r
+    sleep(KEY_PAUSE_TIME_MS);\r
+    keyUpAndDown(Characters.CONTROL_DOWN); // min res\r
+    sleep(KEY_PAUSE_TIME_MS);\r
+    trackBallClick(); // out of res menu\r
+    sleep(KEY_PAUSE_TIME_MS);\r
+    trackBallClick(); // into res menu\r
+    sleep(KEY_PAUSE_TIME_MS);\r
+    keyUpAndDown(Characters.CONTROL_UP);\r
+    sleep(KEY_PAUSE_TIME_MS);\r
+    keyUpAndDown(Characters.CONTROL_UP); // max res\r
+    sleep(KEY_PAUSE_TIME_MS);\r
+    trackBallClick(); // out of res menu\r
+    sleep(KEY_PAUSE_TIME_MS);\r
+    keyUpAndDown(Characters.ESCAPE); // out of options\r
+    sleep(KEY_PAUSE_TIME_MS);\r
+    trackBallClick(); // yes to changes, even if there werent really any!\r
+    Log.info("Finished setting resolution to max.");\r
+  }\r
+\r
+  /**\r
+   * Sets the camera resolution to it's maximum.\r
+   * Note: currently disabled.\r
+   */\r
+  private static void setMinResolution() {\r
+    Log.info("Setting resolution to min.");\r
+    accessResolutionMenu();\r
+    sleep(KEY_PAUSE_TIME_MS);\r
+    keyUpAndDown(Characters.CONTROL_UP);\r
+    sleep(KEY_PAUSE_TIME_MS);\r
+    keyUpAndDown(Characters.CONTROL_UP); // max res\r
+    sleep(KEY_PAUSE_TIME_MS);\r
+    trackBallClick(); // out of res menu\r
+    sleep(KEY_PAUSE_TIME_MS);\r
+    trackBallClick(); // into res menu\r
+    sleep(KEY_PAUSE_TIME_MS);\r
+    keyUpAndDown(Characters.CONTROL_DOWN);\r
+    sleep(KEY_PAUSE_TIME_MS);\r
+    keyUpAndDown(Characters.CONTROL_DOWN); // min res\r
+    sleep(KEY_PAUSE_TIME_MS);\r
+    trackBallClick(); // out of res menu\r
+    sleep(KEY_PAUSE_TIME_MS);\r
+    keyUpAndDown(Characters.ESCAPE); // out of options\r
+    trackBallClick(); // yes to changes, even if there werent really any!\r
+  }\r
+\r
+  private static void accessResolutionMenu() {\r
+    keyUpAndDown(Characters.CONTROL_MENU);\r
+    sleep(KEY_PAUSE_TIME_MS);\r
+    keyUpAndDown(Characters.CONTROL_DOWN);\r
+    sleep(KEY_PAUSE_TIME_MS);\r
+    trackBallClick();\r
+    sleep(KEY_PAUSE_TIME_MS);\r
+    keyUpAndDown(Characters.CONTROL_DOWN);\r
+    sleep(KEY_PAUSE_TIME_MS);\r
+    keyUpAndDown(Characters.CONTROL_DOWN);\r
+    sleep(KEY_PAUSE_TIME_MS);\r
+    trackBallClick();\r
+  }\r
+\r
+  private static void accessResolutionMenuAfterSave() {\r
+    keyUpAndDown(Characters.CONTROL_MENU);\r
+    keyUpAndDown(Characters.CONTROL_DOWN, 6, 0); // seems to be down 6 items on bb and 4 on simulator\r
+    trackBallClick();\r
+    keyUpAndDown(Characters.CONTROL_DOWN);\r
+    keyUpAndDown(Characters.CONTROL_DOWN);\r
+    trackBallClick();\r
+  }\r
+\r
+  /**\r
+   * Puts the current thread to sleep for a given amount of time.\r
+   */\r
+  private static void sleep(int time) {\r
+    try {\r
+      Thread.sleep(time);\r
+    } catch (InterruptedException ie) {\r
+      // continue\r
+    }\r
+  }\r
+\r
+\r
+  private static void trackBallClick() {\r
+    EventInjector.invokeEvent(\r
+            new EventInjector.NavigationEvent(EventInjector.NavigationEvent.NAVIGATION_CLICK, 0, 0, 1));\r
+    EventInjector.invokeEvent(\r
+            new EventInjector.NavigationEvent(EventInjector.NavigationEvent.NAVIGATION_UNCLICK, 0, 0, 1));\r
+  }\r
+\r
+  /**\r
+   * Sends system level key events a given number of times with the given delay between them.\r
+   */\r
+  private static void keyUpAndDown(char character, int times, int delay) {\r
+    for (int i = 0; i < times; i++) {\r
+      keyUpAndDown(character);\r
+      if (delay > 0) {\r
+        sleep(delay);\r
+      }\r
+    }\r
+  }\r
+\r
+  /**\r
+   * Sends one system level key event.\r
+   */\r
+  private static void keyUpAndDown(char character) {\r
+    EventInjector.invokeEvent(new EventInjector.KeyEvent(EventInjector.KeyEvent.KEY_DOWN, character, 0, 1));\r
+    EventInjector.invokeEvent(new EventInjector.KeyEvent(EventInjector.KeyEvent.KEY_UP, character, 0, 1));\r
+  }\r
+\r
+}\r
+\r