From a73fc5919aa98740477fc9e0e8e4ff7f7fdd388d Mon Sep 17 00:00:00 2001 From: srowen Date: Fri, 23 May 2008 22:40:13 +0000 Subject: [PATCH] Added SMSTO support, added subject/body param support in mailto:, improved handling of mailto: and tel: URIs git-svn-id: https://zxing.googlecode.com/svn/trunk@402 59b500cc-1b3d-0410-9834-0bbf25fbcc57 --- .../zxing/client/android/ResultHandler.java | 19 +-- .../zxing/client/android/ResultHandler.java | 15 ++- .../result/EmailAddressParsedResult.java | 40 +++++- .../result/EmailDoCoMoParsedResult.java | 20 +++ .../client/result/ParsedReaderResult.java | 115 +++++++++++++++++- .../client/result/ParsedReaderResultType.java | 1 + .../client/result/SMSTOParsedResult.java | 56 +++++++++ .../zxing/client/result/TelParsedResult.java | 11 +- .../google/zxing/client/j2me/ZXingMIDlet.java | 16 ++- 9 files changed, 263 insertions(+), 30 deletions(-) create mode 100644 core/src/com/google/zxing/client/result/SMSTOParsedResult.java diff --git a/android-m3/src/com/google/zxing/client/android/ResultHandler.java b/android-m3/src/com/google/zxing/client/android/ResultHandler.java index 4c0ad9a2a..071eb38a0 100755 --- a/android-m3/src/com/google/zxing/client/android/ResultHandler.java +++ b/android-m3/src/com/google/zxing/client/android/ResultHandler.java @@ -19,7 +19,6 @@ package com.google.zxing.client.android; import android.content.Intent; import android.net.ContentURI; import android.provider.Contacts; -import android.util.Log; import android.view.View; import android.widget.Button; import com.google.zxing.client.result.AddressBookAUParsedResult; @@ -31,6 +30,7 @@ import com.google.zxing.client.result.GeoParsedResult; import com.google.zxing.client.result.ParsedReaderResult; import com.google.zxing.client.result.ParsedReaderResultType; import com.google.zxing.client.result.SMSParsedResult; +import com.google.zxing.client.result.SMSTOParsedResult; import com.google.zxing.client.result.TelParsedResult; import com.google.zxing.client.result.UPCParsedResult; import com.google.zxing.client.result.URIParsedResult; @@ -47,8 +47,6 @@ import java.net.URISyntaxException; */ final class ResultHandler implements Button.OnClickListener { - private static final String TAG = "ResultHandler"; - private final Intent intent; private final BarcodeReaderCaptureActivity captureActivity; @@ -90,7 +88,7 @@ final class ResultHandler implements Button.OnClickListener { } else if (type.equals(ParsedReaderResultType.EMAIL)) { EmailDoCoMoParsedResult emailResult = (EmailDoCoMoParsedResult) result; try { - intent = new Intent(Intent.SENDTO_ACTION, new ContentURI(emailResult.getTo())); + intent = new Intent(Intent.SENDTO_ACTION, new ContentURI(emailResult.getMailtoURI())); } catch (URISyntaxException e) { } putExtra(intent, "subject", emailResult.getSubject()); @@ -98,26 +96,33 @@ final class ResultHandler implements Button.OnClickListener { } else if (type.equals(ParsedReaderResultType.EMAIL_ADDRESS)) { EmailAddressParsedResult emailResult = (EmailAddressParsedResult) result; try { - intent = new Intent(Intent.SENDTO_ACTION, new ContentURI("mailto:" + emailResult.getEmailAddress())); + intent = new Intent(Intent.SENDTO_ACTION, new ContentURI(emailResult.getMailtoURI())); } catch (URISyntaxException e) { } + putExtra(intent, "subject", emailResult.getSubject()); + putExtra(intent, "body", emailResult.getBody()); } else if (type.equals(ParsedReaderResultType.SMS)) { SMSParsedResult smsResult = (SMSParsedResult) result; try { intent = new Intent(Intent.SENDTO_ACTION, new ContentURI(smsResult.getSMSURI())); } catch (URISyntaxException e) { } + } else if (type.equals(ParsedReaderResultType.SMSTO)) { + SMSTOParsedResult smsToResult = (SMSTOParsedResult) result; + try { + intent = new Intent(Intent.SENDTO_ACTION, new ContentURI(smsToResult.getSMSURI())); + } catch (URISyntaxException e) { + } } else if (type.equals(ParsedReaderResultType.TEL)) { TelParsedResult telResult = (TelParsedResult) result; try { - intent = new Intent(Intent.DIAL_ACTION, new ContentURI("tel:" + telResult.getNumber())); + intent = new Intent(Intent.DIAL_ACTION, new ContentURI(telResult.getTelURI())); } catch (URISyntaxException e) { } } else if (type.equals(ParsedReaderResultType.GEO)) { GeoParsedResult geoResult = (GeoParsedResult) result; try { ContentURI geoURI = new ContentURI(geoResult.getGeoURI()); - Log.v(TAG, "Created geo URI: " + geoURI.toString()); intent = new Intent(Intent.VIEW_ACTION, geoURI); } catch (URISyntaxException e) { } diff --git a/android/src/com/google/zxing/client/android/ResultHandler.java b/android/src/com/google/zxing/client/android/ResultHandler.java index b9fb37998..43edd6e0f 100755 --- a/android/src/com/google/zxing/client/android/ResultHandler.java +++ b/android/src/com/google/zxing/client/android/ResultHandler.java @@ -30,6 +30,7 @@ import com.google.zxing.client.result.GeoParsedResult; import com.google.zxing.client.result.ParsedReaderResult; import com.google.zxing.client.result.ParsedReaderResultType; import com.google.zxing.client.result.SMSParsedResult; +import com.google.zxing.client.result.SMSTOParsedResult; import com.google.zxing.client.result.TelParsedResult; import com.google.zxing.client.result.UPCParsedResult; import com.google.zxing.client.result.URIParsedResult; @@ -78,21 +79,23 @@ final class ResultHandler implements Button.OnClickListener { intent = new Intent(Intent.VIEW_ACTION, Uri.parse(((URLTOParsedResult) result).getURI())); } else if (type.equals(ParsedReaderResultType.EMAIL)) { EmailDoCoMoParsedResult emailResult = (EmailDoCoMoParsedResult) result; - intent = new Intent(Intent.SENDTO_ACTION, Uri.parse(emailResult.getTo())); + intent = new Intent(Intent.SENDTO_ACTION, Uri.parse(emailResult.getMailtoURI())); putExtra(intent, "subject", emailResult.getSubject()); putExtra(intent, "body", emailResult.getBody()); } else if (type.equals(ParsedReaderResultType.EMAIL_ADDRESS)) { EmailAddressParsedResult emailResult = (EmailAddressParsedResult) result; - intent = new Intent(Intent.SENDTO_ACTION, Uri.parse("mailto:" + emailResult.getEmailAddress())); - } else if (type.equals(ParsedReaderResultType.SMS)) { - SMSParsedResult smsResult = (SMSParsedResult) result; - intent = new Intent(Intent.SENDTO_ACTION, Uri.parse(smsResult.getSMSURI())); + intent = new Intent(Intent.SENDTO_ACTION, Uri.parse(emailResult.getMailtoURI())); + putExtra(intent, "subject", emailResult.getSubject()); + putExtra(intent, "body", emailResult.getBody()); } else if (type.equals(ParsedReaderResultType.SMS)) { SMSParsedResult smsResult = (SMSParsedResult) result; intent = new Intent(Intent.SENDTO_ACTION, Uri.parse(smsResult.getSMSURI())); + } else if (type.equals(ParsedReaderResultType.SMSTO)) { + SMSTOParsedResult smsToResult = (SMSTOParsedResult) result; + intent = new Intent(Intent.SENDTO_ACTION, Uri.parse(smsToResult.getSMSURI())); } else if (type.equals(ParsedReaderResultType.TEL)) { TelParsedResult telResult = (TelParsedResult) result; - intent = new Intent(Intent.DIAL_ACTION, Uri.parse("tel:" + telResult.getNumber())); + intent = new Intent(Intent.DIAL_ACTION, Uri.parse(telResult.getTelURI())); } else if (type.equals(ParsedReaderResultType.GEO)) { GeoParsedResult geoResult = (GeoParsedResult) result; intent = new Intent(Intent.VIEW_ACTION, Uri.parse(geoResult.getGeoURI())); diff --git a/core/src/com/google/zxing/client/result/EmailAddressParsedResult.java b/core/src/com/google/zxing/client/result/EmailAddressParsedResult.java index fc334b61f..5564e7cbe 100644 --- a/core/src/com/google/zxing/client/result/EmailAddressParsedResult.java +++ b/core/src/com/google/zxing/client/result/EmailAddressParsedResult.java @@ -18,6 +18,8 @@ package com.google.zxing.client.result; import com.google.zxing.Result; +import java.util.Hashtable; + /** * Represents a result that encodes an e-mail address, either as a plain address * like "joe@example.org" or a mailto: URL like "mailto:joe@example.org". @@ -27,31 +29,63 @@ import com.google.zxing.Result; public final class EmailAddressParsedResult extends AbstractDoCoMoParsedResult { private final String emailAddress; + private final String subject; + private final String body; + private final String mailtoURI; - private EmailAddressParsedResult(String emailAddress) { + private EmailAddressParsedResult(String emailAddress, String subject, String body, String mailtoURI) { super(ParsedReaderResultType.EMAIL_ADDRESS); this.emailAddress = emailAddress; + this.subject = subject; + this.body = body; + this.mailtoURI = mailtoURI; } public static EmailAddressParsedResult parse(Result result) { String rawText = result.getText(); + if (rawText == null) { + return null; + } String emailAddress; - if (rawText != null && rawText.startsWith("mailto:")) { + if (rawText.startsWith("mailto:")) { // If it starts with mailto:, assume it is definitely trying to be an email address emailAddress = rawText.substring(7); + int queryStart = emailAddress.indexOf('?'); + if (queryStart >= 0) { + emailAddress = emailAddress.substring(0, queryStart); + } + Hashtable nameValues = parseNameValuePairs(rawText); + if (emailAddress.length() == 0) { + emailAddress = (String) nameValues.get("to"); + } + String subject = (String) nameValues.get("subject"); + String body = (String) nameValues.get("body"); + return new EmailAddressParsedResult(emailAddress, subject, body, rawText); } else { if (!EmailDoCoMoParsedResult.isBasicallyValidEmailAddress(rawText)) { return null; } emailAddress = rawText; + return new EmailAddressParsedResult(emailAddress, null, null, "mailto:" + emailAddress); } - return new EmailAddressParsedResult(emailAddress); } public String getEmailAddress() { return emailAddress; } + public String getSubject() { + return subject; + } + + public String getBody() { + return body; + } + + public String getMailtoURI() { + return mailtoURI; + } + public String getDisplayResult() { return emailAddress; } diff --git a/core/src/com/google/zxing/client/result/EmailDoCoMoParsedResult.java b/core/src/com/google/zxing/client/result/EmailDoCoMoParsedResult.java index d410e191f..bad6d427c 100644 --- a/core/src/com/google/zxing/client/result/EmailDoCoMoParsedResult.java +++ b/core/src/com/google/zxing/client/result/EmailDoCoMoParsedResult.java @@ -68,6 +68,26 @@ public final class EmailDoCoMoParsedResult extends AbstractDoCoMoParsedResult { return body; } + public String getMailtoURI() { + StringBuffer result = new StringBuffer(to); + boolean hasParams = false; + if (subject != null) { + result.append(hasParams ? '&' : '?'); + hasParams = true; + result.append("subject="); + result.append(subject); + // TODO we need to escape this? + } + if (body != null) { + result.append(hasParams ? '&' : '?'); + hasParams = true; + result.append("body="); + result.append(body); + // TODO we need to escape this? + } + return result.toString(); + } + public String getDisplayResult() { StringBuffer result = new StringBuffer(to); maybeAppend(subject, result); diff --git a/core/src/com/google/zxing/client/result/ParsedReaderResult.java b/core/src/com/google/zxing/client/result/ParsedReaderResult.java index 95bf73400..0f7b41a0e 100644 --- a/core/src/com/google/zxing/client/result/ParsedReaderResult.java +++ b/core/src/com/google/zxing/client/result/ParsedReaderResult.java @@ -18,6 +18,8 @@ package com.google.zxing.client.result; import com.google.zxing.Result; +import java.util.Hashtable; + /** *

Abstract class representing the result of decoding a barcode, as more than * a String -- as some type of structured data. This might be a subclass which represents @@ -62,6 +64,8 @@ public abstract class ParsedReaderResult { return result; } else if ((result = SMSParsedResult.parse(theResult)) != null) { return result; + } else if ((result = SMSTOParsedResult.parse(theResult)) != null) { + return result; } else if ((result = GeoParsedResult.parse(theResult)) != null) { return result; } else if ((result = URLTOParsedResult.parse(theResult)) != null) { @@ -70,12 +74,6 @@ public abstract class ParsedReaderResult { return result; } else if ((result = UPCParsedResult.parse(theResult)) != null) { return result; - //} else if ((result = NDEFTextParsedResult.parse(theResult)) != null) { - // return result; - //} else if ((result = NDEFURIParsedResult.parse(theResult)) != null) { - // return result; - //} else if ((result = NDEFSmartPosterParsedResult.parse(theResult)) != null) { - // return result; } return TextParsedResult.parse(theResult); } @@ -114,4 +112,109 @@ public abstract class ParsedReaderResult { return escaped; } + protected static String urlDecode(String escaped) { + + // No we can't use java.net.URLDecoder here. JavaME doesn't have it. + if (escaped == null) { + return null; + } + char[] escapedArray = escaped.toCharArray(); + + int first = findFirstEscape(escapedArray); + if (first < 0) { + return escaped; + } + + int max = escapedArray.length; + // final length is at most 2 less than original due to at least 1 unescaping + StringBuffer unescaped = new StringBuffer(max - 2); + // Can append everything up to first escape character + unescaped.append(escapedArray, 0, first); + + for (int i = first; i < max; i++) { + char c = escapedArray[i]; + if (c == '+') { + // + is translated directly into a space + unescaped.append(' '); + } else if (c == '%') { + // Are there even two more chars? if not we will just copy the escaped sequence and be done + if (i >= max - 2) { + unescaped.append('%'); // append that % and move on + } else { + int firstDigitValue = parseHexDigit(escapedArray[++i]); + int secondDigitValue = parseHexDigit(escapedArray[++i]); + if (firstDigitValue < 0 || secondDigitValue < 0) { + // bad digit, just move on + unescaped.append('%'); + unescaped.append(escapedArray[i-1]); + unescaped.append(escapedArray[i]); + } + unescaped.append((char) ((firstDigitValue << 4) + secondDigitValue)); + } + } else { + unescaped.append(c); + } + } + return unescaped.toString(); + } + + private static int findFirstEscape(char[] escapedArray) { + int max = escapedArray.length; + for (int i = 0; i < max; i++) { + char c = escapedArray[i]; + if (c == '+' || c == '%') { + return i; + } + } + return -1; + } + + private static int parseHexDigit(char c) { + if (c >= 'a') { + if (c <= 'f') { + return 10 + (c - 'a'); + } + } else if (c >= 'A') { + if (c <= 'F') { + return 10 + (c - 'A'); + } + } else if (c >= '0') { + if (c <= '9') { + return c - '0'; + } + } + return -1; + } + + protected static Hashtable parseNameValuePairs(String uri) { + int paramStart = uri.indexOf('?'); + if (paramStart < 0) { + return null; + } + Hashtable result = new Hashtable(3); + paramStart++; + int paramEnd; + while ((paramEnd = uri.indexOf('&', paramStart)) >= 0) { + appendKeyValue(uri, paramStart, paramEnd, result); + paramStart = paramEnd + 1; + } + appendKeyValue(uri, paramStart, uri.length(), result); + return result; + } + + private static void appendKeyValue(String uri, int paramStart, int paramEnd, Hashtable result) { + int separator = uri.indexOf('=', paramStart); + if (separator >= 0) { + // key = value + String key = uri.substring(paramStart, separator); + String value = uri.substring(separator + 1, paramEnd); + value = urlDecode(value); + result.put(key, value); + } else { + // key, no value + String key = uri.substring(paramStart, paramEnd); + result.put(key, null); + } + } + } diff --git a/core/src/com/google/zxing/client/result/ParsedReaderResultType.java b/core/src/com/google/zxing/client/result/ParsedReaderResultType.java index 815f4cf58..91582cfc6 100644 --- a/core/src/com/google/zxing/client/result/ParsedReaderResultType.java +++ b/core/src/com/google/zxing/client/result/ParsedReaderResultType.java @@ -37,6 +37,7 @@ public final class ParsedReaderResultType { public static final ParsedReaderResultType GEO = new ParsedReaderResultType("GEO"); public static final ParsedReaderResultType TEL = new ParsedReaderResultType("TEL"); public static final ParsedReaderResultType SMS = new ParsedReaderResultType("SMS"); + public static final ParsedReaderResultType SMSTO = new ParsedReaderResultType("SMSTO"); // "optional" types public static final ParsedReaderResultType NDEF_TEXT = new ParsedReaderResultType("NDEF_TEXT"); diff --git a/core/src/com/google/zxing/client/result/SMSTOParsedResult.java b/core/src/com/google/zxing/client/result/SMSTOParsedResult.java new file mode 100644 index 000000000..6af68abf2 --- /dev/null +++ b/core/src/com/google/zxing/client/result/SMSTOParsedResult.java @@ -0,0 +1,56 @@ +/* + * Copyright 2008 Google Inc. + * + * 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.result; + +import com.google.zxing.Result; + +/** + * Represents a "SMSTO:" result, which specifies a number to SMS. + * + * @author srowen@google.com (Sean Owen) + */ +public final class SMSTOParsedResult extends ParsedReaderResult { + + private final String number; + + private SMSTOParsedResult(String number) { + super(ParsedReaderResultType.SMSTO); + this.number = number; + } + + public static SMSTOParsedResult parse(Result result) { + String rawText = result.getText(); + if (rawText == null || !rawText.startsWith("SMSTO:")) { + return null; + } + String number = rawText.substring(6); + return new SMSTOParsedResult(number); + } + + public String getNumber() { + return number; + } + + public String getDisplayResult() { + return number; + } + + public String getSMSURI() { + return "sms:" + number; + } + +} \ No newline at end of file diff --git a/core/src/com/google/zxing/client/result/TelParsedResult.java b/core/src/com/google/zxing/client/result/TelParsedResult.java index 4b927869f..1545031ac 100644 --- a/core/src/com/google/zxing/client/result/TelParsedResult.java +++ b/core/src/com/google/zxing/client/result/TelParsedResult.java @@ -26,10 +26,12 @@ import com.google.zxing.Result; public final class TelParsedResult extends ParsedReaderResult { private final String number; + private final String telURI; - private TelParsedResult(String number) { + private TelParsedResult(String number, String telURI) { super(ParsedReaderResultType.TEL); this.number = number; + this.telURI = telURI; } public static TelParsedResult parse(Result result) { @@ -37,6 +39,7 @@ public final class TelParsedResult extends ParsedReaderResult { if (rawText == null || !rawText.startsWith("tel:")) { return null; } + String telURI = rawText; // Drop tel, query portion int queryStart = rawText.indexOf('?', 4); if (queryStart < 0) { @@ -44,13 +47,17 @@ public final class TelParsedResult extends ParsedReaderResult { } else { rawText = rawText.substring(4, queryStart); } - return new TelParsedResult(rawText); + return new TelParsedResult(rawText, telURI); } public String getNumber() { return number; } + public String getTelURI() { + return telURI; + } + public String getDisplayResult() { return number; } diff --git a/javame/src/com/google/zxing/client/j2me/ZXingMIDlet.java b/javame/src/com/google/zxing/client/j2me/ZXingMIDlet.java index faf238a34..505fc323e 100644 --- a/javame/src/com/google/zxing/client/j2me/ZXingMIDlet.java +++ b/javame/src/com/google/zxing/client/j2me/ZXingMIDlet.java @@ -23,6 +23,7 @@ import com.google.zxing.client.result.EmailDoCoMoParsedResult; import com.google.zxing.client.result.ParsedReaderResult; import com.google.zxing.client.result.ParsedReaderResultType; import com.google.zxing.client.result.SMSParsedResult; +import com.google.zxing.client.result.SMSTOParsedResult; import com.google.zxing.client.result.TelParsedResult; import com.google.zxing.client.result.UPCParsedResult; import com.google.zxing.client.result.URIParsedResult; @@ -205,21 +206,24 @@ public final class ZXingMIDlet extends MIDlet { String uri = ((URLTOParsedResult) result).getURI(); showOpenURL("Open Web Page?", uri, uri); } else if (type.equals(ParsedReaderResultType.EMAIL)) { - String email = ((EmailDoCoMoParsedResult) result).getTo(); - showOpenURL("Compose E-mail?", email, "mailto:" + email); + EmailDoCoMoParsedResult emailResult = (EmailDoCoMoParsedResult) result; + showOpenURL("Compose E-mail?", emailResult.getTo(), emailResult.getMailtoURI()); } else if (type.equals(ParsedReaderResultType.EMAIL_ADDRESS)) { - String email = ((EmailAddressParsedResult) result).getEmailAddress(); - showOpenURL("Compose E-mail?", email, "mailto:" + email); + EmailAddressParsedResult emailResult = (EmailAddressParsedResult) result; + showOpenURL("Compose E-mail?", emailResult.getEmailAddress(), emailResult.getMailtoURI()); } else if (type.equals(ParsedReaderResultType.SMS)) { SMSParsedResult smsResult = (SMSParsedResult) result; showOpenURL("Compose SMS?", smsResult.getNumber(), smsResult.getSMSURI()); + } else if (type.equals(ParsedReaderResultType.SMSTO)) { + SMSTOParsedResult smsToResult = (SMSTOParsedResult) result; + showOpenURL("Compose SMS?", smsToResult.getNumber(), smsToResult.getSMSURI()); } else if (type.equals(ParsedReaderResultType.UPC)) { String upc = ((UPCParsedResult) result).getUPC(); String uri = "http://www.upcdatabase.com/item.asp?upc=" + upc; showOpenURL("Look Up Barcode Online?", upc, uri); } else if (type.equals(ParsedReaderResultType.TEL)) { - String number = ((TelParsedResult) result).getNumber(); - showOpenURL("Dial Number?", number, "tel:" + number); + TelParsedResult telResult = (TelParsedResult) result; + showOpenURL("Dial Number?", telResult.getNumber(), telResult.getTelURI()); } else { showAlert("Barcode Detected", result.getDisplayResult()); }