Initial checkin of RIM client from LifeMarks, after initial refactorings and style changes, etc. May still need work.

git-svn-id: https://zxing.googlecode.com/svn/trunk@546 59b500cc-1b3d-0410-9834-0bbf25fbcc57
This commit is contained in:
srowen 2008-08-04 18:39:26 +00:00
parent 3a1d1f1850
commit c58ae8b68d
17 changed files with 1469 additions and 154 deletions

View file

@ -8,7 +8,9 @@ Christian Brunschen (Google)
Daniel Switkin (Google) Daniel Switkin (Google)
David Albert (Bug Labs) David Albert (Bug Labs)
John Connolly (Bug Labs) John Connolly (Bug Labs)
Joseph Wain (Google)
Matthew Schulkind (Google) Matthew Schulkind (Google)
Matt York (LifeMarks)
Paul Hackenberger Paul Hackenberger
Sean Owen (Google) Sean Owen (Google)
Vince Francis Vince Francis (LifeMarks)

View file

@ -0,0 +1,67 @@
/*
* Copyright 2008 ZXing authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.google.zxing.client.rim;
import net.rim.device.api.ui.Field;
import net.rim.device.api.ui.FieldChangeListener;
import net.rim.device.api.ui.Manager;
import net.rim.device.api.ui.Screen;
import net.rim.device.api.ui.Ui;
import net.rim.device.api.ui.UiEngine;
import net.rim.device.api.ui.DrawStyle;
import net.rim.device.api.ui.component.ButtonField;
import net.rim.device.api.ui.component.LabelField;
import net.rim.device.api.ui.container.MainScreen;
import net.rim.device.api.ui.container.VerticalFieldManager;
/**
* The screen used to display the application 'about' information.
*
* This code was contributed by LifeMarks.
*
* @author Matt York (matt@lifemarks.mobi)
*/
final class AboutScreen extends MainScreen {
AboutScreen() {
setTitle(new LabelField("ZXing - About", DrawStyle.ELLIPSIS | USE_ALL_WIDTH));
Manager vfm = new VerticalFieldManager(FIELD_HCENTER);
Field title = new LabelField("ZXing - BlackBerry Client", FIELD_HCENTER);
Field uri = new LabelField("http://code.google.com/p/zxing/", FIELD_HCENTER);
vfm.add(title);
vfm.add(uri);
Field okButton = new ButtonField("OK", FIELD_HCENTER | ButtonField.CONSUME_CLICK);
okButton.setChangeListener(new ButtonListener(this));
vfm.add(okButton);
add(vfm);
}
/**
* Used to close the screen when the ok button is pressed.
*/
private static class ButtonListener implements FieldChangeListener {
private final Screen screen;
private ButtonListener(Screen screen) {
this.screen = screen;
}
public void fieldChanged(Field field, int context) {
UiEngine ui = Ui.getUiEngine();
ui.popScreen(screen);
}
}
}

View file

@ -0,0 +1,67 @@
/*
* Copyright 2008 ZXing authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.google.zxing.client.rim;
import com.google.zxing.client.rim.util.Log;
import net.rim.device.api.applicationcontrol.ApplicationPermissions;
import net.rim.device.api.applicationcontrol.ApplicationPermissionsManager;
/**
* Requests the necessary permissions for the application.
*
* This code was contributed by LifeMarks.
*
* @author Matt York (matt@lifemarks.mobi)
*/
final class AppPermissionsManager {
private static final ApplicationPermissionsManager apm = ApplicationPermissionsManager.getInstance();
private AppPermissionsManager() {
}
/**
* Requests the required application permissions. Currently the required permissions are
* event injection (sending system level key strokes to other running applications) and
* accessing files (accessing the file when a qrcode image is saved to the file system).
*/
static void setPermissions() {
setPermission(ApplicationPermissions.PERMISSION_EVENT_INJECTOR);
setPermission(ApplicationPermissions.PERMISSION_FILE_API);
}
private static boolean setPermission(int permission) {
boolean updatedPermissions = false;
ApplicationPermissions ap = apm.getApplicationPermissions();
if (ap.containsPermissionKey(permission)) {
int eventInjectorPermission = ap.getPermission(permission);
Log.info("permission (" + permission + "): " + eventInjectorPermission);
if (eventInjectorPermission != ApplicationPermissions.VALUE_ALLOW) {
Log.info("Setting permission to VALUE_ALLOW.");
ap.addPermission(permission);
updatedPermissions = apm.invokePermissionsRequest(ap);
}
} else {
Log.info("Setting permission (" + permission + ") to VALUE_ALLOW.");
ap.addPermission(permission);
updatedPermissions = apm.invokePermissionsRequest(ap);
}
Log.info("updatedPermissions: " + updatedPermissions);
return updatedPermissions;
}
}

View file

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

View file

@ -0,0 +1,65 @@
/*
* Copyright 2008 ZXing authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.google.zxing.client.rim;
import net.rim.device.api.ui.DrawStyle;
import net.rim.device.api.ui.Field;
import net.rim.device.api.ui.FieldChangeListener;
import net.rim.device.api.ui.Screen;
import net.rim.device.api.ui.Ui;
import net.rim.device.api.ui.UiEngine;
import net.rim.device.api.ui.Manager;
import net.rim.device.api.ui.component.ButtonField;
import net.rim.device.api.ui.component.LabelField;
import net.rim.device.api.ui.container.MainScreen;
import net.rim.device.api.ui.container.VerticalFieldManager;
/**
* The screen used to display the application help information.
*
* This code was contributed by LifeMarks.
*
* @author Matt York (matt@lifemarks.mobi)
*/
final class HelpScreen extends MainScreen {
HelpScreen() {
setTitle(new LabelField("ZXing - Help", DrawStyle.ELLIPSIS | USE_ALL_WIDTH));
Manager vfm = new VerticalFieldManager(FIELD_HCENTER);
Field aboutText = new LabelField("help info...", FIELD_HCENTER);
vfm.add(aboutText);
Field okButton = new ButtonField("OK", FIELD_HCENTER | ButtonField.CONSUME_CLICK);
okButton.setChangeListener(new ButtonListener(this));
vfm.add(okButton);
add(vfm);
}
/**
* Closes the screen when the OK button is pressed.
*/
private static class ButtonListener implements FieldChangeListener {
private final Screen screen;
private ButtonListener(Screen screen) {
this.screen = screen;
}
public void fieldChanged(Field field, int context) {
UiEngine ui = Ui.getUiEngine();
ui.popScreen(screen);
}
}
}

View file

@ -0,0 +1,81 @@
/*
* Copyright 2008 ZXing authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.google.zxing.client.rim;
import com.google.zxing.client.rim.persistence.history.DecodeHistory;
import com.google.zxing.client.rim.persistence.history.DecodeHistoryItem;
import com.google.zxing.client.rim.util.Log;
import net.rim.blackberry.api.browser.Browser;
import net.rim.blackberry.api.browser.BrowserSession;
import net.rim.device.api.ui.DrawStyle;
import net.rim.device.api.ui.Field;
import net.rim.device.api.ui.FieldChangeListener;
import net.rim.device.api.ui.Manager;
import net.rim.device.api.ui.FieldLabelProvider;
import net.rim.device.api.ui.component.ButtonField;
import net.rim.device.api.ui.component.LabelField;
import net.rim.device.api.ui.container.MainScreen;
import net.rim.device.api.ui.container.VerticalFieldManager;
/**
* The screen used to display the qrcode decoding history.
*
* This code was contributed by LifeMarks.
*
* @author Matt York (matt@lifemarks.mobi)
*/
final class HistoryScreen extends MainScreen {
HistoryScreen() {
setTitle(new LabelField("ZXing - History", DrawStyle.ELLIPSIS | USE_ALL_WIDTH));
Manager vfm = new VerticalFieldManager(FIELD_HCENTER | VERTICAL_SCROLL);
Log.debug("Num history items: " + DecodeHistory.getInstance().getNumItems());
DecodeHistory history = DecodeHistory.getInstance();
FieldChangeListener itemListener = new ButtonListener();
for (int i = 0; i < history.getNumItems(); i++) {
DecodeHistoryItem item = history.getItemAt(i);
Field labelButton = new ButtonField(item.getURI(), FIELD_HCENTER | ButtonField.CONSUME_CLICK);
labelButton.setChangeListener(itemListener);
vfm.add(labelButton);
}
Field okButton = new ButtonField("OK", FIELD_HCENTER | ButtonField.CONSUME_CLICK);
okButton.setChangeListener(itemListener);
add(vfm);
}
/**
* Closes the screen when the OK button is pressed.
*/
private static class ButtonListener implements FieldChangeListener {
public void fieldChanged(Field field, int context) {
if (field instanceof ButtonField) {
BrowserSession browserSession = Browser.getDefaultSession();
browserSession.displayPage(((FieldLabelProvider) field).getLabel());
}
}
}
/**
* Overriding this method removes the save changes prompt
*/
public boolean onSavePrompt() {
setDirty(false);
return true;
}
}

View file

@ -16,35 +16,36 @@
package com.google.zxing.client.rim; package com.google.zxing.client.rim;
import com.google.zxing.client.rim.util.Log;
import net.rim.device.api.io.file.FileSystemJournal; import net.rim.device.api.io.file.FileSystemJournal;
import net.rim.device.api.io.file.FileSystemJournalEntry; import net.rim.device.api.io.file.FileSystemJournalEntry;
import net.rim.device.api.io.file.FileSystemJournalListener; import net.rim.device.api.io.file.FileSystemJournalListener;
import java.util.Date;
/** /**
* @author Sean Owen (srowen@google.com) * The listener that is fired when an image file is added to the file system.
*
* This code was contributed by LifeMarks.
*
* @author Matt York (matt@lifemarks.mobi)
*/ */
final class ImageCapturedJournalListener implements FileSystemJournalListener { final class QRCapturedJournalListener implements FileSystemJournalListener {
private final ZXingMainScreen screen; private final ZXingLMMainScreen screen;
private long lastUSN;
ImageCapturedJournalListener(ZXingMainScreen screen) { QRCapturedJournalListener(ZXingLMMainScreen screen) {
this.screen = screen; this.screen = screen;
} }
public void fileJournalChanged() { public void fileJournalChanged() {
long nextUSN = FileSystemJournal.getNextUSN(); long lookUSN = FileSystemJournal.getNextUSN() - 1; // the last file added to the filesystem
for (long lookUSN = nextUSN - 1; lookUSN >= lastUSN; --lookUSN) { Log.debug("lookUSN: " + lookUSN);
FileSystemJournalEntry entry = FileSystemJournal.getEntry(lookUSN); FileSystemJournalEntry entry = FileSystemJournal.getEntry(lookUSN);
if (entry == null) { if (entry != null && entry.getEvent() == FileSystemJournalEntry.FILE_ADDED) {
break; Log.info("Got file: " + entry.getPath() + " @: " + new Date());
} screen.imageSaved(entry.getPath());
if (entry.getEvent() == FileSystemJournalEntry.FILE_ADDED) {
screen.handleFile(entry.getPath());
} }
} }
lastUSN = nextUSN;
}
} }

View file

@ -0,0 +1,104 @@
/*
* Copyright 2008 ZXing authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.google.zxing.client.rim;
import com.google.zxing.client.rim.persistence.AppSettings;
import com.google.zxing.client.rim.persistence.history.DecodeHistory;
import com.google.zxing.client.rim.util.Log;
import net.rim.device.api.ui.Field;
import net.rim.device.api.ui.FieldChangeListener;
import net.rim.device.api.ui.Manager;
import net.rim.device.api.ui.Screen;
import net.rim.device.api.ui.Ui;
import net.rim.device.api.ui.UiEngine;
import net.rim.device.api.ui.DrawStyle;
import net.rim.device.api.ui.component.ButtonField;
import net.rim.device.api.ui.component.CheckboxField;
import net.rim.device.api.ui.component.LabelField;
import net.rim.device.api.ui.container.MainScreen;
import net.rim.device.api.ui.container.VerticalFieldManager;
/**
* Screen used to change application settings.
*
* This code was contributed by LifeMarks.
*
* @author Matt York (matt@lifemarks.mobi)
*/
final class SettingsScreen extends MainScreen {
private boolean changes;
private final AppSettings settings;
private final CheckboxField camResMsgCheckBox;
SettingsScreen() {
setTitle(new LabelField("ZXing - Settings", DrawStyle.ELLIPSIS | USE_ALL_WIDTH));
Manager vfm = new VerticalFieldManager(FIELD_HCENTER);
settings = AppSettings.getInstance();
Boolean cameraResMsgSetting = settings.getBooleanItem(AppSettings.SETTING_CAM_RES_MSG);
boolean cameraResMsgSettingBool = (cameraResMsgSetting != null) && cameraResMsgSetting.booleanValue();
// 0
camResMsgCheckBox = new CheckboxField("Don't show camera resolution message", cameraResMsgSettingBool);
camResMsgCheckBox.setChangeListener(new ButtonListener(this));
vfm.add(camResMsgCheckBox);
// 1
Field clearHistoryButton = new ButtonField("Clear History",FIELD_HCENTER | ButtonField.CONSUME_CLICK);
clearHistoryButton.setChangeListener(new ButtonListener(this));
vfm.add(clearHistoryButton);
// 2
Field okButton = new ButtonField("OK", FIELD_HCENTER | ButtonField.CONSUME_CLICK);
okButton.setChangeListener(new ButtonListener(this));
vfm.add(okButton);
add(vfm);
}
/**
* Listens for button clicks and executes the appropriate action.
*/
private final class ButtonListener implements FieldChangeListener {
private final Screen screen;
private ButtonListener(Screen screen) {
this.screen = screen;
}
public void fieldChanged(Field field, int context) {
Log.debug("Field: " + field.getIndex() + " , context: " + context);
switch (field.getIndex()) {
case 0:
settings.addItem(AppSettings.SETTING_CAM_RES_MSG,
(camResMsgCheckBox.getChecked()) ? Boolean.TRUE : Boolean.FALSE);
changes = true;
break;
case 1:
// TODO confirm
DecodeHistory.getInstance().clear();
DecodeHistory.getInstance().persist();
case 2: //ok
if (changes) {
AppSettings.getInstance().persist();
}
UiEngine ui = Ui.getUiEngine();
ui.popScreen(screen);
break;
}
}
}
}

View file

@ -0,0 +1,331 @@
/*
* Copyright 2008 ZXing authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.google.zxing.client.rim;
import com.google.zxing.DecodeHintType;
import com.google.zxing.MonochromeBitmapSource;
import com.google.zxing.MultiFormatReader;
import com.google.zxing.Reader;
import com.google.zxing.ReaderException;
import com.google.zxing.Result;
import com.google.zxing.client.j2me.LCDUIImageMonochromeBitmapSource;
import com.google.zxing.client.rim.persistence.AppSettings;
import com.google.zxing.client.rim.persistence.history.DecodeHistory;
import com.google.zxing.client.rim.persistence.history.DecodeHistoryItem;
import com.google.zxing.client.rim.util.Log;
import com.google.zxing.client.rim.util.ReasonableTimer;
import com.google.zxing.client.rim.util.URLDecoder;
import net.rim.blackberry.api.browser.Browser;
import net.rim.blackberry.api.browser.BrowserSession;
import net.rim.device.api.ui.DrawStyle;
import net.rim.device.api.ui.Field;
import net.rim.device.api.ui.FieldChangeListener;
import net.rim.device.api.ui.Manager;
import net.rim.device.api.ui.UiApplication;
import net.rim.device.api.ui.component.ButtonField;
import net.rim.device.api.ui.component.Dialog;
import net.rim.device.api.ui.component.LabelField;
import net.rim.device.api.ui.container.DialogFieldManager;
import net.rim.device.api.ui.container.MainScreen;
import net.rim.device.api.ui.container.PopupScreen;
import net.rim.device.api.ui.container.VerticalFieldManager;
import javax.microedition.io.Connector;
import javax.microedition.io.file.FileConnection;
import javax.microedition.lcdui.Image;
import java.io.IOException;
import java.io.InputStream;
import java.util.Hashtable;
/**
* The main appication menu screen.
*
* This code was contributed by LifeMarks.
*
* @author Matt York (matt@lifemarks.mobi)
*/
final class ZXingLMMainScreen extends MainScreen {
private final ZXingUiApplication app;
private final QRCapturedJournalListener imageListener;
private PopupScreen popup;
private final Reader reader;
private final Hashtable readerHints;
ZXingLMMainScreen() {
super(DEFAULT_MENU | DEFAULT_CLOSE);
setTitle(new LabelField("ZXing", DrawStyle.ELLIPSIS | USE_ALL_WIDTH));
setChangeListener(null);
Manager vfm = new VerticalFieldManager(USE_ALL_WIDTH);
FieldChangeListener buttonListener = new ButtonListener();
//0
Field snapButton = new ButtonField("Snap", FIELD_HCENTER | ButtonField.CONSUME_CLICK | USE_ALL_WIDTH);
snapButton.setChangeListener(buttonListener);
vfm.add(snapButton);
//1
Field historyButton = new ButtonField("History", FIELD_HCENTER | ButtonField.CONSUME_CLICK);
historyButton.setChangeListener(buttonListener);
vfm.add(historyButton);
//2
Field settingsButton = new ButtonField("Settings", FIELD_HCENTER | ButtonField.CONSUME_CLICK);
settingsButton.setChangeListener(buttonListener);
vfm.add(settingsButton);
//3
Field aboutButton = new ButtonField("About", FIELD_HCENTER | ButtonField.CONSUME_CLICK);
aboutButton.setChangeListener(buttonListener);
vfm.add(aboutButton);
//4
Field helpButton = new ButtonField("Help", FIELD_HCENTER | ButtonField.CONSUME_CLICK);
helpButton.setChangeListener(buttonListener);
vfm.add(helpButton);
vfm.setChangeListener(null);
add(vfm);
app = (ZXingUiApplication) UiApplication.getUiApplication();
imageListener = new QRCapturedJournalListener(this);
reader = new MultiFormatReader();
readerHints = new Hashtable(1);
readerHints.put(DecodeHintType.TRY_HARDER, Boolean.TRUE);
}
/**
* Handles the newly created file. If the file is a jpg image, from the camera, the images is assumed to be
* a qrcode and decoding is attempted.
*/
void imageSaved(String imagePath) {
Log.info("Image saved: " + imagePath);
app.removeFileSystemJournalListener(imageListener);
if (imagePath.endsWith(".jpg") && imagePath.indexOf("IMG") >= 0) // a blackberry camera image file
{
Log.info("imageSaved - Got file: " + imagePath);
Camera.getInstance().exit();
Log.info("camera exit finished");
app.requestForeground();
DialogFieldManager manager = new DialogFieldManager();
popup = new PopupScreen(manager);
manager.addCustomField(new LabelField("Decoding image..."));
app.pushScreen(popup); // original
Log.info("started progress screen.");
Runnable fct = new FileConnectionThread(imagePath);
Log.info("Starting file connection thread.");
app.invokeLater(fct);
Log.info("Finished file connection thread.");
} else {
Log.error("Failed to locate camera image.");
}
}
/**
* Closes the application and persists all required data.
*/
public void close() {
app.removeFileSystemJournalListener(imageListener);
DecodeHistory.getInstance().persist();
super.close();
}
/**
* This method is overriden to remove the 'save changes' dialog when exiting.
*/
public boolean onSavePrompt() {
setDirty(false);
return true;
}
/**
* Listens for selected buttons and starts the required screen.
*/
private final class ButtonListener implements FieldChangeListener {
public void fieldChanged(Field field, int context) {
Log.debug("*** fieldChanged: " + field.getIndex());
switch (field.getIndex()) {
case 0: // snap
try {
app.addFileSystemJournalListener(imageListener);
Camera.getInstance().invoke(); // start camera
return;
}
catch (Exception e) {
Log.error("!!! Problem invoking camera.!!!: " + e);
}
break;
case 1: // history
app.pushScreen(new HistoryScreen());
break;
case 2: // settings
app.pushScreen(new SettingsScreen());
break;
case 3: //about
app.pushScreen(new AboutScreen());
break;
case 4: //help
app.pushScreen(new HelpScreen());
break;
}
}
}
/**
* Thread that decodes the newly created image. If the image is successfully decoded and the data is a URL,
* the browser is invoked and pointed to the given URL.
*/
private final class FileConnectionThread implements Runnable {
private final String imagePath;
private FileConnectionThread(String imagePath) {
this.imagePath = imagePath;
}
public void run() {
FileConnection file = null;
InputStream is = null;
Image capturedImage = null;
try {
file = (FileConnection) Connector.open("file://" + imagePath, Connector.READ_WRITE);
is = file.openInputStream();
capturedImage = Image.createImage(is);
} catch (IOException e) {
Log.error("Problem creating image: " + e);
removeProgressBar();
invalidate();
showMessage("An error occured processing the image.");
return;
} finally {
if (is != null) {
try {
is.close();
} catch (IOException ioe) {
}
}
if (file != null && file.exists()) {
if (file.isOpen()) {
//file.close();
}
//file.delete();
Log.info("Deleted image file.");
}
}
if (capturedImage != null) {
Log.info("Got image...");
MonochromeBitmapSource source = new LCDUIImageMonochromeBitmapSource(capturedImage);
Result result;
ReasonableTimer decodingTimer = null;
try {
decodingTimer = new ReasonableTimer();
Log.info("Attempting to decode image...");
result = reader.decode(source, readerHints);
decodingTimer.finished();
} catch (ReaderException e) {
Log.error("Could not decode image: " + e);
decodingTimer.finished();
removeProgressBar();
invalidate();
boolean showResolutionMsg =
!AppSettings.getInstance().getBooleanItem(AppSettings.SETTING_CAM_RES_MSG).booleanValue();
if (showResolutionMsg) {
showMessage("A QR Code was not found in the image. " +
"We detected that the decoding process took quite a while. " +
"It will be much faster if you decrease your camera's resolution (640x480).");
} else {
showMessage("A QR Code was not found in the image.");
}
return;
}
if (result != null) {
String resultText = result.getText();
Log.info("result: " + resultText);
if (isURI(resultText)) {
resultText = URLDecoder.decode(resultText);
removeProgressBar();
invalidate();
if (!decodingTimer.wasResonableTime() &&
!AppSettings.getInstance().getBooleanItem(AppSettings.SETTING_CAM_RES_MSG).booleanValue()) {
showMessage("We detected that the decoding process took quite a while. " +
"It will be much faster if you decrease your camera's resolution (640x480).");
}
DecodeHistory.getInstance().addHistoryItem(new DecodeHistoryItem(resultText));
invokeBrowser(resultText);
return;
}
} else {
removeProgressBar();
invalidate();
showMessage("A QR Code was not found in the image.");
return;
}
}
removeProgressBar();
invalidate();
}
/**
* Quick check to see if the result of decoding the qr code was a valid uri.
*/
private boolean isURI(String uri) {
return uri.startsWith("http://");
}
/**
* Invokes the web browser and browses to the given uri.
*/
private void invokeBrowser(String uri) {
BrowserSession browserSession = Browser.getDefaultSession();
browserSession.displayPage(uri);
}
/**
* Syncronized version of removing progress dialog.
* NOTE: All methods accessing the gui that are in seperate threads should syncronize on app.getEventLock()
*/
private void removeProgressBar() {
synchronized (app.getAppEventLock()) {
if (popup != null) {
app.popScreen(popup);
}
}
}
/**
* Syncronized version of showing a message dialog.
* NOTE: All methods accessing the gui that are in seperate threads should syncronize on app.getEventLock()
*/
private void showMessage(String message) {
synchronized (app.getAppEventLock()) {
Dialog.alert(message);
}
}
}
}

View file

@ -1,129 +0,0 @@
/*
* Copyright 2008 ZXing authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.google.zxing.client.rim;
import com.google.zxing.MonochromeBitmapSource;
import com.google.zxing.MultiFormatReader;
import com.google.zxing.Reader;
import com.google.zxing.ReaderException;
import com.google.zxing.Result;
import com.google.zxing.client.j2me.LCDUIImageMonochromeBitmapSource;
import net.rim.blackberry.api.invoke.CameraArguments;
import net.rim.blackberry.api.invoke.Invoke;
import net.rim.device.api.system.Characters;
import net.rim.device.api.system.EventInjector;
import net.rim.device.api.ui.UiApplication;
import net.rim.device.api.ui.component.Dialog;
import net.rim.device.api.ui.component.LabelField;
import net.rim.device.api.ui.container.MainScreen;
import javax.microedition.io.Connector;
import javax.microedition.io.file.FileConnection;
import javax.microedition.lcdui.Image;
import java.io.IOException;
import java.io.InputStream;
/**
* @author Sean Owen (srowen@google.com)
*/
final class ZXingMainScreen extends MainScreen {
private final ZXingUIApp app;
private final ImageCapturedJournalListener captureListener;
ZXingMainScreen() {
setTitle("ZXing Barcode Reader");
add(new LabelField("UNDER CONSTRUCTION"));
add(new LabelField("1. Press 'Enter' at right to launch the Camera application"));
add(new LabelField("2. Configure Camera to capture 640x480 image"));
add(new LabelField("3. Take a picture of a barcode"));
add(new LabelField("4. If not returned to this application to see result, close Camera application"));
app = (ZXingUIApp) UiApplication.getUiApplication();
captureListener = new ImageCapturedJournalListener(this);
app.addFileSystemJournalListener(captureListener);
}
public boolean keyChar(char c, int status, int time) {
if (c == Characters.ENTER) {
Invoke.invokeApplication(Invoke.APP_TYPE_CAMERA, new CameraArguments());
return true;
} else {
return super.keyChar(c, status, time);
}
}
public void close() {
app.removeFileSystemJournalListener(captureListener);
super.close();
}
private void showMessage(String msg) {
synchronized (app.getAppEventLock()) {
Dialog.alert(msg);
}
}
void handleFile(String path) {
if (path.endsWith(".jpg") && path.indexOf("IMG") >= 0) {
// Get out of camera app
EventInjector.invokeEvent(new EventInjector.KeyEvent(EventInjector.KeyEvent.KEY_DOWN, Characters.ESCAPE, 0, 1));
EventInjector.invokeEvent(new EventInjector.KeyEvent(EventInjector.KeyEvent.KEY_UP, Characters.ESCAPE, 0, 1));
// Try to come to foreground for good measure
app.requestForeground();
try {
FileConnection file = null;
InputStream is = null;
Image capturedImage;
try {
file = (FileConnection) Connector.open("file://" + path);
is = file.openInputStream();
capturedImage = Image.createImage(is);
} finally {
if (is != null) {
try {
is.close();
} catch (IOException ioe) {
// continue
}
}
if (file != null) {
try {
file.close();
} catch (IOException ioe) {
// continue
}
}
}
MonochromeBitmapSource source = new LCDUIImageMonochromeBitmapSource(capturedImage);
Reader reader = new MultiFormatReader();
Result result = reader.decode(source);
// If decode was successful...
try {
file.delete();
} catch (IOException ioe) {
// continue
}
showMessage(result.getText());
} catch (IOException ioe) {
showMessage(ioe.getMessage());
} catch (ReaderException re) {
showMessage("Sorry, no barcode was found.");
}
}
}
}

View file

@ -16,19 +16,29 @@
package com.google.zxing.client.rim; package com.google.zxing.client.rim;
import com.google.zxing.client.rim.persistence.AppSettings;
import com.google.zxing.client.rim.persistence.history.DecodeHistory;
import net.rim.device.api.ui.UiApplication; import net.rim.device.api.ui.UiApplication;
/** /**
* @author Sean Owen (srowen@google.com) * Starts the application with the MenuScreen screen on the stack.
* As well, the required permissions are requested and the history and app settings are initialized.
*
* This code was contributed by LifeMarks.
*
* @author Matt York (matt@lifemarks.mobi)
*/ */
public final class ZXingUIApp extends UiApplication { public final class ZXingUiApplication extends UiApplication {
public static void main(String[] args) { private ZXingUiApplication() {
new ZXingUIApp().enterEventDispatcher(); pushScreen(new ZXingLMMainScreen());
} }
ZXingUIApp() { public static void main(String[] args) {
pushScreen(new ZXingMainScreen()); AppPermissionsManager.setPermissions();
DecodeHistory.getInstance();
AppSettings.getInstance();
new ZXingUiApplication().enterEventDispatcher();
} }
} }

View file

@ -0,0 +1,100 @@
/*
* Copyright 2008 ZXing authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.google.zxing.client.rim.persistence;
import net.rim.device.api.system.PersistentObject;
import net.rim.device.api.system.PersistentStore;
import java.util.Hashtable;
/**
* Singleton object that represents the persistent application settings data.
*
* This code was contributed by LifeMarks.
*
* @author Matt York (matt@lifemarks.mobi)
*/
public final class AppSettings {
public static final String SETTING_CAM_RES_MSG = "setting_cam_res_msg";
private static final long ID_LONG = 0x92ac4e8ac35b8aa0L;
private static AppSettings instance;
private final PersistentObject store;
private final Hashtable settingsItems;
private AppSettings() {
store = PersistentStore.getPersistentObject(ID_LONG);
Hashtable temp = (Hashtable) store.getContents();
settingsItems = temp == null ? new Hashtable() : temp;
}
public static AppSettings getInstance() {
if (instance == null) {
instance = new AppSettings();
}
return instance;
}
/**
* Adds a setting object.
*/
public void addItem(String itemName, Object itemValue) {
settingsItems.put(itemName, itemValue);
}
/**
* Returns all settings objects.
*/
public Hashtable getItems() {
return settingsItems;
}
/**
* Gets a particular settings object by name.
*/
Object getItem(String itemName) {
return settingsItems.get(itemName);
}
/**
* Gets a particular boolean type settings object by name.
*/
public Boolean getBooleanItem(String itemName) {
Object value = getItem(itemName);
return value instanceof Boolean ? (Boolean) value : Boolean.FALSE;
}
/**
* Returns the number of settings.
*/
public int getNumItems() {
return settingsItems.size();
}
/**
* Persists the settings to the device.
*/
public void persist() {
synchronized (store) {
store.setContents(settingsItems);
store.commit();
}
}
}

View file

@ -0,0 +1,101 @@
/*
* Copyright 2008 ZXing authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.google.zxing.client.rim.persistence.history;
import net.rim.device.api.system.PersistentObject;
import net.rim.device.api.system.PersistentStore;
import java.util.Vector;
/**
* Singleton used to persist the history of qrcode URLs decoded by the client.
*
* This code was contributed by LifeMarks.
*
* @author Matt York (matt@lifemarks.mobi)
*/
public class DecodeHistory {
private static final long ID_LONG = 0xb7cc76147b48ad0dL;
private static DecodeHistory instance;
private final PersistentObject store;
private final Vector historyItems;
private DecodeHistory() {
store = PersistentStore.getPersistentObject(ID_LONG);
Vector temp = (Vector) store.getContents();
historyItems = temp == null ? new Vector() : temp;
}
/**
* Returns the single instance of this class.
*/
public static DecodeHistory getInstance() {
if (instance == null) {
instance = new DecodeHistory();
}
return instance;
}
/**
* Adds a history object.
*/
public void addHistoryItem(DecodeHistoryItem item) {
historyItems.addElement(item);
}
/**
* Returns all history objects.
*/
public Vector getItems() {
return historyItems;
}
/**
* Gets a particular history object at a given index.
*/
public DecodeHistoryItem getItemAt(int index) {
return (DecodeHistoryItem) historyItems.elementAt(index);
}
/**
* Returns the number of history objects.
*/
public int getNumItems() {
return historyItems.size();
}
/**
* Clears the history.
*/
public void clear() {
historyItems.setSize(0);
}
/**
* Persists the history to the device.
*/
public void persist() {
synchronized (store) {
store.setContents(historyItems);
store.commit();
}
}
}

View file

@ -0,0 +1,60 @@
/*
* Copyright 2008 ZXing authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.google.zxing.client.rim.persistence.history;
import net.rim.device.api.util.Persistable;
import java.util.Date;
/**
* A single decoded history item that is stored by the decode history.
*
* This code was contributed by LifeMarks.
*
* @author Matt York (matt@lifemarks.mobi)
*/
public final class DecodeHistoryItem implements Persistable {
private String date;
private String uri;
private DecodeHistoryItem() {
date = new Date().toString();
}
public DecodeHistoryItem(String uri) {
this();
this.uri = uri;
}
public void setDate(String date) {
this.date = date;
}
public String getDate() {
return date;
}
public void setURI(String uri) {
this.uri = uri;
}
public String getURI() {
return uri;
}
}

View file

@ -0,0 +1,83 @@
/*
* Copyright 2008 ZXing authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.google.zxing.client.rim.util;
import net.rim.device.api.system.EventLogger;
/**
* Used to write logging messages. When debugging, System.out is used to write to the simulator.
* When running on a real device, the EventLogger is used. To access the event log on a real device,
* go to the home screen, hold down ALT and type lglg.
*
* This code was contributed by LifeMarks.
*
* @author Matt York (matt@lifemarks.mobi)
*/
public final class Log {
private static final String LOG_ID_STRING = "zxing";
private static final long LOG_ID_LONG = 0x351e9b79fd52317L;
/** Used to determine if the log message should be set to System.out */
private static final boolean logToSystemOut;
static {
// Initializes the logger. Currently set to not log to System.out and log
// at the INFO level.
EventLogger.register(LOG_ID_LONG, LOG_ID_STRING, EventLogger.VIEWER_STRING);
EventLogger.setMinimumLevel(EventLogger.DEBUG_INFO); // set this to change logging level message.
logToSystemOut = false; // needs to be false for deployment to blackberry device and true for debuging on simulator.
}
private Log() {
}
/**
* Logs the given message at the debug level.
*/
public static void debug(String message) {
EventLogger.logEvent(LOG_ID_LONG, message.getBytes(), EventLogger.DEBUG_INFO);
logToSystemOut(message);
}
/**
* Logs the given message at the info level.
*/
public static void info(String message) {
EventLogger.logEvent(LOG_ID_LONG, message.getBytes(), EventLogger.INFORMATION);
logToSystemOut(message);
}
/**
* Logs the given message at the error level.
*/
public static void error(String message) {
EventLogger.logEvent(LOG_ID_LONG, message.getBytes(), EventLogger.ERROR);
logToSystemOut(message);
}
/**
* Logs the given message to system.out.
* This is useful when debugging on the simulator.
*/
private static void logToSystemOut(String message) {
if (logToSystemOut) {
System.out.println(message);
}
}
}

View file

@ -0,0 +1,75 @@
/*
* Copyright 2008 ZXing authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.google.zxing.client.rim.util;
/**
* Used to determine if something happend within a specified amount of time.
* For example, if a QR code was decoded in a resonable amount of time.
* If not, perhaps the user should lower their camera resolution.
*
* This code was contributed by LifeMarks.
*
* @author Matt York (matt@lifemarks.mobi)
*/
public final class ReasonableTimer {
// 2000 too low for qr decoding
private static final long DEF_RESONABLE_TIME = 2500; // in ms
private long reasonableTime;
private final long startTime;
private long finishTime;
public ReasonableTimer() {
startTime = System.currentTimeMillis();
reasonableTime = DEF_RESONABLE_TIME;
}
public ReasonableTimer(long reasonableTime) {
startTime = System.currentTimeMillis();
this.reasonableTime = reasonableTime;
}
/**
* Stops the timing.
*/
public void finished() {
finishTime = System.currentTimeMillis();
}
/**
* Returns true if the timer finished in a reasonable amount of time.
*/
public boolean wasResonableTime() {
return finishTime - startTime <= reasonableTime;
}
/**
* Sets the reasonable time to the given time
*/
public void setResonableTime(long reasonableTime) {
this.reasonableTime = reasonableTime;
}
/**
* Returns the reasonable time.
*/
public long getResonableTime() {
return reasonableTime;
}
}

View file

@ -0,0 +1,84 @@
/*
* Copyright 2008 ZXing authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.google.zxing.client.rim.util;
import java.util.Enumeration;
import java.util.Hashtable;
/**
* Used to decode URL encoded characters.
*
* This code was contributed by LifeMarks.
*
* @author Matt York (matt@lifemarks.mobi)
*/
public final class URLDecoder {
private URLDecoder() {
}
private static final Hashtable decodingMap;
static {
decodingMap = new Hashtable(37);
decodingMap.put("%21", "!");
decodingMap.put("%2A", "*");
decodingMap.put("%2a", "*");
decodingMap.put("%27", "'");
decodingMap.put("%28", "(");
decodingMap.put("%29", ")");
decodingMap.put("%3B", ";");
decodingMap.put("%3b", ";");
decodingMap.put("%3A", ":");
decodingMap.put("%3a", ":");
decodingMap.put("%40", "@");
decodingMap.put("%26", "&");
decodingMap.put("%3D", "=");
decodingMap.put("%3d", "=");
decodingMap.put("%3B", "+");
decodingMap.put("%3b", "+");
decodingMap.put("%24", "$");
decodingMap.put("%2C", "`");
decodingMap.put("%2c", "`");
decodingMap.put("%2F", "/");
decodingMap.put("%2f", "/");
decodingMap.put("%3F", "?");
decodingMap.put("%3f", "?");
decodingMap.put("%25", "%");
decodingMap.put("%23", "#");
decodingMap.put("%5B", "[");
decodingMap.put("%5b", "[");
decodingMap.put("%5D", "]");
decodingMap.put("%5d", "]");
}
public static String decode(String uri) {
Log.info("Original uri: " + uri);
if (uri.indexOf('%') >= 0) { // skip this if no encoded chars
Enumeration keys = decodingMap.keys();
while (keys.hasMoreElements()) {
String encodedChar = (String) keys.nextElement();
int encodedCharIndex = uri.indexOf(encodedChar);
while (encodedCharIndex != -1) {
uri = uri.substring(0, encodedCharIndex) + decodingMap.get(encodedChar) + uri.substring(encodedCharIndex + encodedChar.length());
encodedCharIndex = uri.indexOf(encodedChar, encodedCharIndex);
}
}
}
Log.info("Final URI: " + uri);
return uri;
}
}