From 0b74dfc7fe76a922c6b69f5a2c6787323445ebda Mon Sep 17 00:00:00 2001 From: srowen Date: Tue, 30 Mar 2010 18:33:54 +0000 Subject: [PATCH] Update sms: and geo: handling to better respect new RFCs git-svn-id: https://zxing.googlecode.com/svn/trunk@1266 59b500cc-1b3d-0410-9834-0bbf25fbcc57 --- .../android/result/SMSResultHandler.java | 14 ++-- .../zxing/client/result/GeoParsedResult.java | 19 +++-- .../zxing/client/result/GeoResultParser.java | 9 ++- .../zxing/client/result/ResultParser.java | 2 + .../client/result/SMSMMSResultParser.java | 74 ++++++++++--------- .../zxing/client/result/SMSParsedResult.java | 71 ++++++++++++------ .../client/result/SMSTOMMSTOResultParser.java | 57 ++++++++++++++ .../result/GeoParsedResultTestCase.java | 3 +- .../result/ParsedReaderResultTestCase.java | 38 ++++++---- .../result/SMSMMSParsedResultTestCase.java | 11 ++- 10 files changed, 210 insertions(+), 88 deletions(-) create mode 100644 core/src/com/google/zxing/client/result/SMSTOMMSTOResultParser.java diff --git a/android/src/com/google/zxing/client/android/result/SMSResultHandler.java b/android/src/com/google/zxing/client/android/result/SMSResultHandler.java index 06da2927f..3675e7a33 100644 --- a/android/src/com/google/zxing/client/android/result/SMSResultHandler.java +++ b/android/src/com/google/zxing/client/android/result/SMSResultHandler.java @@ -53,10 +53,11 @@ public final class SMSResultHandler extends ResultHandler { SMSParsedResult smsResult = (SMSParsedResult) getResult(); switch (index) { case 0: - sendSMS(smsResult.getNumber(), smsResult.getBody()); + // Don't know of a way yet to express a SENDTO intent with multiple recipients + sendSMS(smsResult.getNumbers()[0], smsResult.getBody()); break; case 1: - sendMMS(smsResult.getNumber(), smsResult.getSubject(), smsResult.getBody()); + sendMMS(smsResult.getNumbers()[0], smsResult.getSubject(), smsResult.getBody()); break; } } @@ -65,11 +66,14 @@ public final class SMSResultHandler extends ResultHandler { public CharSequence getDisplayContents() { SMSParsedResult smsResult = (SMSParsedResult) getResult(); StringBuffer contents = new StringBuffer(); - ParsedResult.maybeAppend(PhoneNumberUtils.formatNumber(smsResult.getNumber()), contents); - ParsedResult.maybeAppend(smsResult.getVia(), contents); + String[] rawNumbers = smsResult.getNumbers(); + String[] formattedNumbers = new String[rawNumbers.length]; + for (int i = 0; i < rawNumbers.length; i++) { + formattedNumbers[i] = PhoneNumberUtils.formatNumber(rawNumbers[i]); + } + ParsedResult.maybeAppend(formattedNumbers, contents); ParsedResult.maybeAppend(smsResult.getSubject(), contents); ParsedResult.maybeAppend(smsResult.getBody(), contents); - ParsedResult.maybeAppend(smsResult.getTitle(), contents); return contents.toString(); } diff --git a/core/src/com/google/zxing/client/result/GeoParsedResult.java b/core/src/com/google/zxing/client/result/GeoParsedResult.java index aabebcd6a..947b9e8de 100644 --- a/core/src/com/google/zxing/client/result/GeoParsedResult.java +++ b/core/src/com/google/zxing/client/result/GeoParsedResult.java @@ -21,21 +21,28 @@ package com.google.zxing.client.result; */ public final class GeoParsedResult extends ParsedResult { - private final String geoURI; private final double latitude; private final double longitude; private final double altitude; - GeoParsedResult(String geoURI, double latitude, double longitude, double altitude) { + GeoParsedResult(double latitude, double longitude, double altitude) { super(ParsedResultType.GEO); - this.geoURI = geoURI; this.latitude = latitude; this.longitude = longitude; this.altitude = altitude; } public String getGeoURI() { - return geoURI; + StringBuffer result = new StringBuffer(); + result.append("geo:"); + result.append(latitude); + result.append(','); + result.append(longitude); + if (altitude > 0) { + result.append(','); + result.append(altitude); + } + return result.toString(); } /** @@ -60,11 +67,11 @@ public final class GeoParsedResult extends ParsedResult { } public String getDisplayResult() { - StringBuffer result = new StringBuffer(50); + StringBuffer result = new StringBuffer(); result.append(latitude); result.append(", "); result.append(longitude); - if (altitude > 0.0f) { + if (altitude > 0.0) { result.append(", "); result.append(altitude); result.append('m'); diff --git a/core/src/com/google/zxing/client/result/GeoResultParser.java b/core/src/com/google/zxing/client/result/GeoResultParser.java index f08605964..3d21bf365 100644 --- a/core/src/com/google/zxing/client/result/GeoResultParser.java +++ b/core/src/com/google/zxing/client/result/GeoResultParser.java @@ -47,6 +47,9 @@ final class GeoResultParser extends ResultParser { double latitude, longitude, altitude; try { latitude = Double.parseDouble(geoURIWithoutQuery.substring(0, latitudeEnd)); + if (latitude > 90.0 || latitude < -90.0) { + return null; + } if (longitudeEnd < 0) { longitude = Double.parseDouble(geoURIWithoutQuery.substring(latitudeEnd + 1)); altitude = 0.0; @@ -54,11 +57,13 @@ final class GeoResultParser extends ResultParser { longitude = Double.parseDouble(geoURIWithoutQuery.substring(latitudeEnd + 1, longitudeEnd)); altitude = Double.parseDouble(geoURIWithoutQuery.substring(longitudeEnd + 1)); } + if (longitude > 180.0 || longitude < -180.0 || altitude < 0) { + return null; + } } catch (NumberFormatException nfe) { return null; } - return new GeoParsedResult(rawText.startsWith("GEO:") ? "geo:" + rawText.substring(4) : rawText, - latitude, longitude, altitude); + return new GeoParsedResult(latitude, longitude, altitude); } } \ No newline at end of file diff --git a/core/src/com/google/zxing/client/result/ResultParser.java b/core/src/com/google/zxing/client/result/ResultParser.java index 18613ab7c..ce29b55f1 100644 --- a/core/src/com/google/zxing/client/result/ResultParser.java +++ b/core/src/com/google/zxing/client/result/ResultParser.java @@ -59,6 +59,8 @@ public abstract class ResultParser { return result; } else if ((result = SMSMMSResultParser.parse(theResult)) != null) { return result; + } else if ((result = SMSTOMMSTOResultParser.parse(theResult)) != null) { + return result; } else if ((result = GeoResultParser.parse(theResult)) != null) { return result; } else if ((result = URLTOResultParser.parse(theResult)) != null) { diff --git a/core/src/com/google/zxing/client/result/SMSMMSResultParser.java b/core/src/com/google/zxing/client/result/SMSMMSResultParser.java index 88bcfa38e..e309132ca 100644 --- a/core/src/com/google/zxing/client/result/SMSMMSResultParser.java +++ b/core/src/com/google/zxing/client/result/SMSMMSResultParser.java @@ -19,15 +19,20 @@ package com.google.zxing.client.result; import com.google.zxing.Result; import java.util.Hashtable; +import java.util.Vector; /** - *

Parses an "sms:" URI result, which specifies a number to SMS and optional - * "via" number. See - * the IETF draft on this.

+ *

Parses an "sms:" URI result, which specifies a number to SMS. + * See RFC 5724 on this.

* - *

This actually also parses URIs starting with "mms:", "smsto:", "mmsto:", "SMSTO:", and - * "MMSTO:", and treats them all the same way, and effectively converts them to an "sms:" URI - * for purposes of forwarding to the platform.

+ *

This class supports "via" syntax for numbers, which is not part of the spec. + * For example "+12125551212;via=+12124440101" may appear as a number. + * It also supports a "subject" query parameter, which is not mentioned in the spec. + * These are included since they were mentioned in earlier IETF drafts and might be + * used.

+ * + *

This actually also parses URIs starting with "mms:" and treats them all the same way, + * and effectively converts them to an "sms:" URI for purposes of forwarding to the platform.

* * @author Sean Owen */ @@ -41,14 +46,8 @@ final class SMSMMSResultParser extends ResultParser { if (rawText == null) { return null; } - int prefixLength; - if (rawText.startsWith("sms:") || rawText.startsWith("SMS:") || - rawText.startsWith("mms:") || rawText.startsWith("MMS:")) { - prefixLength = 4; - } else if (rawText.startsWith("smsto:") || rawText.startsWith("SMSTO:") || - rawText.startsWith("mmsto:") || rawText.startsWith("MMSTO:")) { - prefixLength = 6; - } else { + if (!(rawText.startsWith("sms:") || rawText.startsWith("SMS:") || + rawText.startsWith("mms:") || rawText.startsWith("MMS:"))) { return null; } @@ -64,40 +63,45 @@ final class SMSMMSResultParser extends ResultParser { } // Drop sms, query portion - int queryStart = rawText.indexOf('?', prefixLength); + int queryStart = rawText.indexOf('?', 4); String smsURIWithoutQuery; // If it's not query syntax, the question mark is part of the subject or message if (queryStart < 0 || !querySyntax) { - smsURIWithoutQuery = rawText.substring(prefixLength); + smsURIWithoutQuery = rawText.substring(4); } else { - smsURIWithoutQuery = rawText.substring(prefixLength, queryStart); + smsURIWithoutQuery = rawText.substring(4, queryStart); } - int numberEnd = smsURIWithoutQuery.indexOf(';'); - String number; - String via; + + int lastComma = -1; + int comma; + Vector numbers = new Vector(1); + Vector vias = new Vector(1); + while ((comma = smsURIWithoutQuery.indexOf(',', lastComma + 1)) > lastComma) { + String numberPart = smsURIWithoutQuery.substring(lastComma + 1, comma); + addNumberVia(numbers, vias, numberPart); + lastComma = comma; + } + addNumberVia(numbers, vias, smsURIWithoutQuery.substring(lastComma + 1)); + + return new SMSParsedResult(toStringArray(numbers), toStringArray(vias), subject, body); + } + + private static void addNumberVia(Vector numbers, Vector vias, String numberPart) { + int numberEnd = numberPart.indexOf(';'); if (numberEnd < 0) { - number = smsURIWithoutQuery; - via = null; + numbers.addElement(numberPart); + vias.addElement(null); } else { - number = smsURIWithoutQuery.substring(0, numberEnd); - String maybeVia = smsURIWithoutQuery.substring(numberEnd + 1); + numbers.addElement(numberPart.substring(0, numberEnd)); + String maybeVia = numberPart.substring(numberEnd + 1); + String via; if (maybeVia.startsWith("via=")) { via = maybeVia.substring(4); } else { via = null; } + vias.addElement(via); } - - // Thanks to dominik.wild for suggesting this enhancement to support - // smsto:number:body URIs - if (body == null) { - int bodyStart = number.indexOf(':'); - if (bodyStart >= 0) { - body = number.substring(bodyStart + 1); - number = number.substring(0, bodyStart); - } - } - return new SMSParsedResult("sms:" + number, number, via, subject, body, null); } } \ No newline at end of file diff --git a/core/src/com/google/zxing/client/result/SMSParsedResult.java b/core/src/com/google/zxing/client/result/SMSParsedResult.java index cccb4bbc2..93792ae6b 100644 --- a/core/src/com/google/zxing/client/result/SMSParsedResult.java +++ b/core/src/com/google/zxing/client/result/SMSParsedResult.java @@ -21,33 +21,68 @@ package com.google.zxing.client.result; */ public final class SMSParsedResult extends ParsedResult { - private final String smsURI; - private final String number; - private final String via; + private final String[] numbers; + private final String[] vias; private final String subject; private final String body; - private final String title; - public SMSParsedResult(String smsURI, String number, String via, String subject, String body, String title) { + public SMSParsedResult(String number, String via, String subject, String body) { super(ParsedResultType.SMS); - this.smsURI = smsURI; - this.number = number; - this.via = via; + this.numbers = new String[] {number}; + this.vias = new String[] {via}; + this.subject = subject; + this.body = body; + } + + public SMSParsedResult(String[] numbers, String[] vias, String subject, String body) { + super(ParsedResultType.SMS); + this.numbers = numbers; + this.vias = vias; this.subject = subject; this.body = body; - this.title = title; } public String getSMSURI() { - return smsURI; + StringBuffer result = new StringBuffer(); + result.append("sms:"); + boolean first = true; + for (int i = 0; i < numbers.length; i++) { + if (first) { + first = false; + } else { + result.append(','); + } + result.append(numbers[i]); + if (vias[i] != null) { + result.append(";via="); + result.append(vias[i]); + } + } + boolean hasBody = body != null; + boolean hasSubject = subject != null; + if (hasBody || hasSubject) { + result.append('?'); + if (hasBody) { + result.append("body="); + result.append(body); + } + if (hasSubject) { + if (hasBody) { + result.append('&'); + } + result.append("subject="); + result.append(subject); + } + } + return result.toString(); } - public String getNumber() { - return number; + public String[] getNumbers() { + return numbers; } - public String getVia() { - return via; + public String[] getVias() { + return vias; } public String getSubject() { @@ -58,17 +93,11 @@ public final class SMSParsedResult extends ParsedResult { return body; } - public String getTitle() { - return title; - } - public String getDisplayResult() { StringBuffer result = new StringBuffer(100); - maybeAppend(number, result); - maybeAppend(via, result); + maybeAppend(numbers, result); maybeAppend(subject, result); maybeAppend(body, result); - maybeAppend(title, result); return result.toString(); } diff --git a/core/src/com/google/zxing/client/result/SMSTOMMSTOResultParser.java b/core/src/com/google/zxing/client/result/SMSTOMMSTOResultParser.java new file mode 100644 index 000000000..76418d040 --- /dev/null +++ b/core/src/com/google/zxing/client/result/SMSTOMMSTOResultParser.java @@ -0,0 +1,57 @@ +/* + * 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.result; + +import com.google.zxing.Result; + +/** + *

Parses an "smsto:" URI result, whose format is not standardized but appears to be like: + * smsto:number(:body).

+ * + *

This actually also parses URIs starting with "smsto:", "mmsto:", "SMSTO:", and + * "MMSTO:", and treats them all the same way, and effectively converts them to an "sms:" URI + * for purposes of forwarding to the platform.

+ * + * @author Sean Owen + */ +final class SMSTOMMSTOResultParser extends ResultParser { + + private SMSTOMMSTOResultParser() { + } + + public static SMSParsedResult parse(Result result) { + String rawText = result.getText(); + if (rawText == null) { + return null; + } + if (!(rawText.startsWith("smsto:") || rawText.startsWith("SMSTO:") || + rawText.startsWith("mmsto:") || rawText.startsWith("MMSTO:"))) { + return null; + } + // Thanks to dominik.wild for suggesting this enhancement to support + // smsto:number:body URIs + String number = rawText.substring(6); + String body = null; + int bodyStart = number.indexOf(':'); + if (bodyStart >= 0) { + body = number.substring(bodyStart + 1); + number = number.substring(0, bodyStart); + } + return new SMSParsedResult(number, null, null, body); + } + +} \ No newline at end of file diff --git a/core/test/src/com/google/zxing/client/result/GeoParsedResultTestCase.java b/core/test/src/com/google/zxing/client/result/GeoParsedResultTestCase.java index f10820e57..77c56ccfa 100644 --- a/core/test/src/com/google/zxing/client/result/GeoParsedResultTestCase.java +++ b/core/test/src/com/google/zxing/client/result/GeoParsedResultTestCase.java @@ -29,7 +29,8 @@ public final class GeoParsedResultTestCase extends TestCase { public void testGeo() { doTest("geo:1,2", 1.0, 2.0, 0.0); - doTest("geo:100.33,-32.3344,3.35", 100.33, -32.3344, 3.35); + doTest("geo:80.33,-32.3344,3.35", 80.33, -32.3344, 3.35); + doTest("geo:-20.33,132.3344,0.01", -20.33, 132.3344, 0.01); } private static void doTest(String contents, double latitude, double longitude, double altitude) { diff --git a/core/test/src/com/google/zxing/client/result/ParsedReaderResultTestCase.java b/core/test/src/com/google/zxing/client/result/ParsedReaderResultTestCase.java index ae7ccf951..df6638369 100644 --- a/core/test/src/com/google/zxing/client/result/ParsedReaderResultTestCase.java +++ b/core/test/src/com/google/zxing/client/result/ParsedReaderResultTestCase.java @@ -158,7 +158,7 @@ public final class ParsedReaderResultTestCase extends TestCase { doTestResult("geo:1,2", "1.0, 2.0", ParsedResultType.GEO); doTestResult("GEO:1,2", "1.0, 2.0", ParsedResultType.GEO); doTestResult("geo:1,2,3", "1.0, 2.0, 3.0m", ParsedResultType.GEO); - doTestResult("geo:100.33,-32.3344,3.35", "100.33, -32.3344, 3.35m", ParsedResultType.GEO); + doTestResult("geo:80.33,-32.3344,3.35", "80.33, -32.3344, 3.35m", ParsedResultType.GEO); doTestResult("geo", "geo", ParsedResultType.TEXT); doTestResult("geography", "geography", ParsedResultType.TEXT); } @@ -221,18 +221,22 @@ public final class ParsedReaderResultTestCase extends TestCase { public void testSMS() { doTestResult("sms:+15551212", "+15551212", ParsedResultType.SMS); doTestResult("SMS:+15551212", "+15551212", ParsedResultType.SMS); + doTestResult("sms:+15551212;via=999333", "+15551212", ParsedResultType.SMS); + doTestResult("sms:+15551212?subject=foo&body=bar", "+15551212\nfoo\nbar", ParsedResultType.SMS); + doTestResult("sms:+15551212,+12124440101", "+15551212\n+12124440101", ParsedResultType.SMS); + } + + public void testSMSTO() { doTestResult("SMSTO:+15551212", "+15551212", ParsedResultType.SMS); doTestResult("smsto:+15551212", "+15551212", ParsedResultType.SMS); - doTestResult("sms:+15551212;via=999333", "+15551212\n999333", ParsedResultType.SMS); - doTestResult("sms:+15551212?subject=foo&body=bar", "+15551212\nfoo\nbar", ParsedResultType.SMS); - doTestResult("sms:+15551212:subject", "+15551212\nsubject", ParsedResultType.SMS); - doTestResult("sms:+15551212:My message", "+15551212\nMy message", ParsedResultType.SMS); + doTestResult("smsto:+15551212:subject", "+15551212\nsubject", ParsedResultType.SMS); + doTestResult("smsto:+15551212:My message", "+15551212\nMy message", ParsedResultType.SMS); // Need to handle question mark in the subject - doTestResult("sms:+15551212:What's up?", "+15551212\nWhat's up?", ParsedResultType.SMS); + doTestResult("smsto:+15551212:What's up?", "+15551212\nWhat's up?", ParsedResultType.SMS); // Need to handle colon in the subject - doTestResult("sms:+15551212:Directions: Do this", "+15551212\nDirections: Do this", + doTestResult("smsto:+15551212:Directions: Do this", "+15551212\nDirections: Do this", ParsedResultType.SMS); - doTestResult("sms:212-555-1212:Here's a longer message. Should be fine.", + doTestResult("smsto:212-555-1212:Here's a longer message. Should be fine.", "212-555-1212\nHere's a longer message. Should be fine.", ParsedResultType.SMS); } @@ -240,16 +244,20 @@ public final class ParsedReaderResultTestCase extends TestCase { public void testMMS() { doTestResult("mms:+15551212", "+15551212", ParsedResultType.SMS); doTestResult("MMS:+15551212", "+15551212", ParsedResultType.SMS); + doTestResult("mms:+15551212;via=999333", "+15551212", ParsedResultType.SMS); + doTestResult("mms:+15551212?subject=foo&body=bar", "+15551212\nfoo\nbar", ParsedResultType.SMS); + doTestResult("mms:+15551212,+12124440101", "+15551212\n+12124440101", ParsedResultType.SMS); + } + + public void testMMSTO() { doTestResult("MMSTO:+15551212", "+15551212", ParsedResultType.SMS); doTestResult("mmsto:+15551212", "+15551212", ParsedResultType.SMS); - doTestResult("mms:+15551212;via=999333", "+15551212\n999333", ParsedResultType.SMS); - doTestResult("mms:+15551212?subject=foo&body=bar", "+15551212\nfoo\nbar", ParsedResultType.SMS); - doTestResult("mms:+15551212:subject", "+15551212\nsubject", ParsedResultType.SMS); - doTestResult("mms:+15551212:My message", "+15551212\nMy message", ParsedResultType.SMS); - doTestResult("mms:+15551212:What's up?", "+15551212\nWhat's up?", ParsedResultType.SMS); - doTestResult("mms:+15551212:Directions: Do this", "+15551212\nDirections: Do this", + doTestResult("mmsto:+15551212:subject", "+15551212\nsubject", ParsedResultType.SMS); + doTestResult("mmsto:+15551212:My message", "+15551212\nMy message", ParsedResultType.SMS); + doTestResult("mmsto:+15551212:What's up?", "+15551212\nWhat's up?", ParsedResultType.SMS); + doTestResult("mmsto:+15551212:Directions: Do this", "+15551212\nDirections: Do this", ParsedResultType.SMS); - doTestResult("mms:212-555-1212:Here's a longer message. Should be fine.", + doTestResult("mmsto:212-555-1212:Here's a longer message. Should be fine.", "212-555-1212\nHere's a longer message. Should be fine.", ParsedResultType.SMS); } diff --git a/core/test/src/com/google/zxing/client/result/SMSMMSParsedResultTestCase.java b/core/test/src/com/google/zxing/client/result/SMSMMSParsedResultTestCase.java index 6dc284816..20d362a2c 100644 --- a/core/test/src/com/google/zxing/client/result/SMSMMSParsedResultTestCase.java +++ b/core/test/src/com/google/zxing/client/result/SMSMMSParsedResultTestCase.java @@ -20,6 +20,8 @@ import com.google.zxing.BarcodeFormat; import com.google.zxing.Result; import junit.framework.TestCase; +import java.util.Arrays; + /** * Tests {@link SMSParsedResult}. * @@ -40,15 +42,18 @@ public final class SMSMMSParsedResultTestCase extends TestCase { } private static void doTest(String contents, String number, String subject, String body, String via) { + doTest(contents, new String[] {number}, subject, body, new String[] {via}); + } + + private static void doTest(String contents, String[] numbers, String subject, String body, String[] vias) { Result fakeResult = new Result(contents, null, null, BarcodeFormat.QR_CODE); ParsedResult result = ResultParser.parseResult(fakeResult); assertSame(ParsedResultType.SMS, result.getType()); SMSParsedResult smsResult = (SMSParsedResult) result; - assertEquals(number, smsResult.getNumber()); + assertTrue(Arrays.equals(numbers, smsResult.getNumbers())); assertEquals(subject, smsResult.getSubject()); assertEquals(body, smsResult.getBody()); - assertEquals(via, smsResult.getVia()); - assertEquals("sms:" + number, smsResult.getSMSURI()); + assertTrue(Arrays.equals(vias, smsResult.getVias())); } }