diff --git a/play-services-vision-api/build.gradle b/play-services-vision-api/build.gradle new file mode 100644 index 0000000..fa92012 --- /dev/null +++ b/play-services-vision-api/build.gradle @@ -0,0 +1,68 @@ +/* + * Copyright 2013-2015 microG Project Team + * + * 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. + */ + +apply plugin: 'com.android.library' + +String getMyVersionName() { + def stdout = new ByteArrayOutputStream() + if (rootProject.file("gradlew").exists()) + exec { commandLine 'git', 'describe', '--tags', '--always', '--dirty'; standardOutput = stdout } + else // automatic build system, don't tag dirty + exec { commandLine 'git', 'describe', '--tags', '--always'; standardOutput = stdout } + return stdout.toString().trim().substring(1) +} + +group = 'org.microg' +version = getMyVersionName() + +android { + compileSdkVersion androidCompileSdk() + buildToolsVersion "$androidBuildVersionTools" + + aidlPackageWhiteList "com/google/android/gms/vision/BarcodeDetectorOptions.aidl" + aidlPackageWhiteList "com/google/android/gms/vision/INativeBarcodeDetectorCreator.aidl" + aidlPackageWhiteList "com/google/android/gms/vision/INativeBarcodeDetector.aidl" + + defaultConfig { + versionName getMyVersionName() + minSdkVersion androidMinSdk() + targetSdkVersion androidTargetSdk() + } + + testOptions { + unitTests { + includeAndroidResources = true + } + } + + dependencies { + testImplementation 'junit:junit:4.12' + testImplementation "com.google.truth:truth:1.0.1" + testImplementation 'org.robolectric:robolectric:4.3' + } + + + compileOptions { + sourceCompatibility JavaVersion.VERSION_1_8 + targetCompatibility JavaVersion.VERSION_1_8 + } +} + +dependencies { + api project(':play-services-basement') + implementation 'com.google.zxing:android-core:3.3.0' + implementation 'com.google.zxing:core:3.3.0' +} diff --git a/play-services-vision-api/gradle.properties b/play-services-vision-api/gradle.properties new file mode 100644 index 0000000..ac62dca --- /dev/null +++ b/play-services-vision-api/gradle.properties @@ -0,0 +1,34 @@ +# +# Copyright 2013-2016 microG Project Team +# +# 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. +# + +POM_NAME=Play Services Internal Vision API +POM_DESCRIPTION=Interfaces and objects for IPC between Play Services Library and Play Services Core + +POM_PACKAGING=aar + +POM_URL=https://github.com/microg/android_external_GmsApi + +POM_SCM_URL=https://github.com/microg/android_external_GmsApi +POM_SCM_CONNECTION=scm:git@github.com:microg/android_external_GmsApi.git +POM_SCM_DEV_CONNECTION=scm:git@github.com:microg/android_external_GmsApi.git + +POM_LICENCE_NAME=The Apache Software License, Version 2.0 +POM_LICENCE_URL=http://www.apache.org/licenses/LICENSE-2.0.txt +POM_LICENCE_DIST=repo + +POM_DEVELOPER_ID=mar-v-in +POM_DEVELOPER_NAME=Marvin W + diff --git a/play-services-vision-api/src/main/AndroidManifest.xml b/play-services-vision-api/src/main/AndroidManifest.xml new file mode 100644 index 0000000..517ffd4 --- /dev/null +++ b/play-services-vision-api/src/main/AndroidManifest.xml @@ -0,0 +1,18 @@ + + + + diff --git a/play-services-vision-api/src/main/aidl/com/google/android/gms/vision/barcode/internal/client/Barcode.aidl b/play-services-vision-api/src/main/aidl/com/google/android/gms/vision/barcode/internal/client/Barcode.aidl new file mode 100644 index 0000000..92a2274 --- /dev/null +++ b/play-services-vision-api/src/main/aidl/com/google/android/gms/vision/barcode/internal/client/Barcode.aidl @@ -0,0 +1,3 @@ +package com.google.android.gms.vision.barcode.internal.client; + +parcelable Barcode; \ No newline at end of file diff --git a/play-services-vision-api/src/main/aidl/com/google/android/gms/vision/barcode/internal/client/BarcodeDetectorOptions.aidl b/play-services-vision-api/src/main/aidl/com/google/android/gms/vision/barcode/internal/client/BarcodeDetectorOptions.aidl new file mode 100644 index 0000000..bb2dfcd --- /dev/null +++ b/play-services-vision-api/src/main/aidl/com/google/android/gms/vision/barcode/internal/client/BarcodeDetectorOptions.aidl @@ -0,0 +1,3 @@ +package com.google.android.gms.vision.barcode.internal.client; + +parcelable BarcodeDetectorOptions; \ No newline at end of file diff --git a/play-services-vision-api/src/main/aidl/com/google/android/gms/vision/barcode/internal/client/FrameMetadata.aidl b/play-services-vision-api/src/main/aidl/com/google/android/gms/vision/barcode/internal/client/FrameMetadata.aidl new file mode 100644 index 0000000..76bf50e --- /dev/null +++ b/play-services-vision-api/src/main/aidl/com/google/android/gms/vision/barcode/internal/client/FrameMetadata.aidl @@ -0,0 +1,3 @@ +package com.google.android.gms.vision.barcode.internal.client; + +parcelable FrameMetadata; \ No newline at end of file diff --git a/play-services-vision-api/src/main/aidl/com/google/android/gms/vision/barcode/internal/client/INativeBarcodeDetector.aidl b/play-services-vision-api/src/main/aidl/com/google/android/gms/vision/barcode/internal/client/INativeBarcodeDetector.aidl new file mode 100644 index 0000000..948b923 --- /dev/null +++ b/play-services-vision-api/src/main/aidl/com/google/android/gms/vision/barcode/internal/client/INativeBarcodeDetector.aidl @@ -0,0 +1,13 @@ +package com.google.android.gms.vision.barcode.internal.client; + +import com.google.android.gms.vision.barcode.internal.client.BarcodeDetectorOptions; +import com.google.android.gms.vision.barcode.internal.client.FrameMetadata; +import com.google.android.gms.vision.barcode.internal.client.Barcode; +import com.google.android.gms.dynamic.IObjectWrapper; + +interface INativeBarcodeDetector { + void unk0(IObjectWrapper byteBuffer, in FrameMetadata metadata) = 0; + Barcode[] detect(IObjectWrapper bitmap, in FrameMetadata metadata) = 1; + // Probably wraps a ByteBuffer? but is otherwise the same functionality? + void unk2(IObjectWrapper unk1, in FrameMetadata metadata) = 2; +} \ No newline at end of file diff --git a/play-services-vision-api/src/main/aidl/com/google/android/gms/vision/barcode/internal/client/INativeBarcodeDetectorCreator.aidl b/play-services-vision-api/src/main/aidl/com/google/android/gms/vision/barcode/internal/client/INativeBarcodeDetectorCreator.aidl new file mode 100644 index 0000000..3f67b93 --- /dev/null +++ b/play-services-vision-api/src/main/aidl/com/google/android/gms/vision/barcode/internal/client/INativeBarcodeDetectorCreator.aidl @@ -0,0 +1,10 @@ +package com.google.android.gms.vision.barcode.internal.client; + +import com.google.android.gms.vision.barcode.internal.client.BarcodeDetectorOptions; +import com.google.android.gms.vision.barcode.internal.client.INativeBarcodeDetector; +import com.google.android.gms.dynamic.IObjectWrapper; + +interface INativeBarcodeDetectorCreator { + // unk is probably Context + INativeBarcodeDetector create(IObjectWrapper unk, in BarcodeDetectorOptions options) = 0; +} diff --git a/play-services-vision-api/src/main/java/com/google/android/gms/vision/barcode/internal/client/Barcode.java b/play-services-vision-api/src/main/java/com/google/android/gms/vision/barcode/internal/client/Barcode.java new file mode 100644 index 0000000..f694bc1 --- /dev/null +++ b/play-services-vision-api/src/main/java/com/google/android/gms/vision/barcode/internal/client/Barcode.java @@ -0,0 +1,447 @@ +package com.google.android.gms.vision.barcode.internal.client; + +import android.graphics.Point; +import android.util.Log; +import android.util.Patterns; + +import org.microg.safeparcel.AutoSafeParcelable; +import org.microg.safeparcel.SafeParceled; + +import java.net.MalformedURLException; +import java.util.regex.Matcher; +import java.util.regex.Pattern; +import java.net.URL; + +/** + * Represents a barcode that can be detected by the com.google.android.gms.vision.barcode and + * com.google.firebase.ml.vision.barcode packages. + * + * Not all types are supported yet. + */ +public class Barcode extends AutoSafeParcelable { + public static final String TAG = Barcode.class.getSimpleName(); + + public static final int CONTACT_INFO = 1; + public static final int EMAIL = 2; + public static final int ISBN = 3; + public static final int PHONE = 4; + public static final int PRODUCT = 5; + public static final int SMS = 6; + public static final int TEXT = 7; + public static final int URL = 8; + public static final int WIFI = 9; + public static final int GEO = 10; + public static final int CALENDAR_EVENT = 11; + public static final int DRIVER_LICENSE = 12; + + @SafeParceled(1) + private final int versionCode = 1; + @SafeParceled(2) + public int format; + @SafeParceled(3) + public String rawValue; + @SafeParceled(4) + public String displayValue; + @SafeParceled(5) + public int valueFormat; + @SafeParceled(6) + public Point[] cornerPoints; + @SafeParceled(7) + public Barcode.Email email; + @SafeParceled(8) + public Barcode.Phone phone; + @SafeParceled(9) + public Barcode.Sms sms; + @SafeParceled(10) + public Barcode.WiFi wifi; + @SafeParceled(11) + public Barcode.UrlBookmark url; + @SafeParceled(12) + public Barcode.GeoPoint geoPoint; + @SafeParceled(13) + public Barcode.CalendarEvent calendarEvent; + @SafeParceled(14) + public Barcode.ContactInfo contactInfo; + @SafeParceled(15) + public Barcode.DriverLicense driverLicense; + + public Barcode() {} + + public Barcode(String rawValue) { + this.valueFormat = TEXT; + this.rawValue = rawValue; + this.displayValue = rawValue; + detectAndSetType(); + } + + public Barcode(String rawValue, Point[] points) { + this(rawValue); + this.cornerPoints = points; + } + + // TODO: Not all types are handled yet, calendarEvent, contactInfo, and driverLicense are not supported yet. + private void detectAndSetType() { + try { + // We do this for the side effect when the URI is invalid + new URL(this.rawValue); + this.valueFormat = Barcode.URL; + this.url = new Barcode.UrlBookmark(); + this.url.url = this.rawValue; + return; + } catch(MalformedURLException _ex) {} + + int schemeSeparatorIndex = this.rawValue.indexOf(":"); + if (schemeSeparatorIndex == -1 || this.rawValue.length() == schemeSeparatorIndex + 1) { + this.valueFormat = Barcode.TEXT; + return; + } + String scheme = this.rawValue.substring(0, schemeSeparatorIndex); + String rest = this.rawValue.substring(schemeSeparatorIndex + 1); + switch(scheme.toLowerCase()) { + case "mailto": + this.valueFormat = Barcode.EMAIL; + this.email = Barcode.Email.parse(rest); + break; + case "tel": + this.valueFormat = Barcode.PHONE; + this.phone = Barcode.Phone.parse(rest); + break; + case "sms": + case "smsto": + this.valueFormat = Barcode.SMS; + this.sms = Barcode.Sms.parse(rest); + break; + case "mebkm": + this.valueFormat = Barcode.URL; + this.url = Barcode.UrlBookmark.parse(rest); + break; + case "wifi": + this.valueFormat = Barcode.WIFI; + this.wifi = Barcode.WiFi.parse(rest); + break; + case "geo": + this.geoPoint = Barcode.GeoPoint.parse(rest); + if (this.geoPoint != null) { + this.valueFormat = Barcode.GEO; + } + break; + default: + Log.d(TAG, "Scheme " + scheme + " is not supported yet"); + this.valueFormat = Barcode.TEXT; + break; + } + } + + + private static String extractMatch(String value, Pattern pattern, int group) { + Matcher match = pattern.matcher(value); + if (!match.matches() || match.group(group) == null) { + return null; + } + return match.group(group); + } + + private static String extractMatch(String value, Pattern pattern) { + return extractMatch(value, pattern, 0); + } + + public static Creator CREATOR = new AutoCreator<>(Barcode.class); + + public static class Email extends AutoSafeParcelable { + public static final int UNKNOWN = 0; + public static final int WORK = 1; + public static final int HOME = 2; + + @SafeParceled(1) + final int versionCode = 1; + @SafeParceled(2) + public int type; + @SafeParceled(3) + public String address; + @SafeParceled(4) + public String subject; + @SafeParceled(5) + public String body; + + public static Email parse(String value) { + Email result = new Email(); + result.type = UNKNOWN; + + int index = value.indexOf('?'); + if (index != -1) { + result.address = extractMatch(value.substring(0, index), Patterns.EMAIL_ADDRESS); + } else { + result.address = extractMatch(value, Patterns.EMAIL_ADDRESS); + } + result.subject = extractMatch(value, Pattern.compile(".*?subject=([^&]*).*"), 1); + result.body = extractMatch(value, Pattern.compile(".*?body=([^&]*).*"), 1); + return result; + } + + public static Creator CREATOR = new AutoCreator<>(Email.class); + } + + public static class Phone extends AutoSafeParcelable { + public static final int UNKNOWN = 0; + public static final int WORK = 1; + public static final int HOME = 2; + public static final int FAX = 3; + public static final int MOBILE = 4; + + @SafeParceled(1) + final int versionCode = 1; + @SafeParceled(2) + public int type; + @SafeParceled(3) + public String number; + + public static Phone parse(String value) { + Phone phone = new Phone(); + phone.type = UNKNOWN; + phone.number = value; + return phone; + } + + public static Creator CREATOR = new AutoCreator<>(Phone.class); + } + + public static class Sms extends AutoSafeParcelable { + @SafeParceled(1) + final int versionCode = 1; + @SafeParceled(2) + public String message; + @SafeParceled(3) + public String phoneNumber; + + public static Sms parse(String value) { + Sms result = new Sms(); + result.phoneNumber = extractMatch(value, Pattern.compile("([+]?[0-9]+).*"), 1); + result.message = extractMatch(value, Pattern.compile(".*?(body=|:|,)(.*)$"), 2); + return result; + } + + public static Creator CREATOR = new AutoCreator<>(Sms.class); + } + + public static class WiFi extends AutoSafeParcelable { + public static final int OPEN = 1; + public static final int WPA = 2; + public static final int WEP = 3; + + @SafeParceled(1) + final int versionCode = 1; + @SafeParceled(2) + public String ssid; + @SafeParceled(3) + public String password; + @SafeParceled(4) + public int encryptionType; + + public static WiFi parse(String value) { + WiFi result = new WiFi(); + result.password = extractMatch(value, Pattern.compile(".*?(P|p):(.*?);.*"), 2); + result.ssid = extractMatch(value, Pattern.compile(".*?(S|s):(.*?);.*"), 2); + result.encryptionType = OPEN; + String encryptionType = extractMatch(value, Pattern.compile(".*?(T|t):(.*?);.*"), 2); + switch (encryptionType.toLowerCase()) { + case "wpa": + result.encryptionType = WPA; + break; + case "wep": + result.encryptionType = WEP; + break; + } + return result; + } + + public static Creator CREATOR = new AutoCreator<>(WiFi.class); + } + + public static class UrlBookmark extends AutoSafeParcelable { + @SafeParceled(1) + final int versionCode = 1; + @SafeParceled(2) + public String title; + @SafeParceled(3) + public String url; + + public static UrlBookmark parse(String value) { + UrlBookmark result = new UrlBookmark(); + result.title = extractMatch(value, Pattern.compile(".*?(TITLE|title):(.*?);.*"), 2); + result.url = extractMatch(value, Pattern.compile(".*?(URL|url):(.*?);.*"), 2); + return result; + } + + public static Creator CREATOR = new AutoCreator<>(UrlBookmark.class); + } + + public static class GeoPoint extends AutoSafeParcelable { + @SafeParceled(1) + final int versionCode = 1; + @SafeParceled(2) + public double lat; + @SafeParceled(3) + public double lng; + + public GeoPoint(double lat, double lng) { + this.lat = lat; + this.lng = lng; + } + + public GeoPoint() {} + + public static GeoPoint parse(String value) { + GeoPoint result = new GeoPoint(); + int commaIndex = value.indexOf(','); + // We need at least one number after the comma for longitude + if (commaIndex == -1 || value.length() <= commaIndex) { + return null; + } + try { + result.lat = Double.parseDouble(value.substring(0, commaIndex)); + result.lng = Double.parseDouble(value.substring(commaIndex + 1)); + } catch(NumberFormatException e) { + return null; + } + return result; + } + + public static Creator CREATOR = new AutoCreator<>(GeoPoint.class); + } + + public static class CalendarDateTime extends AutoSafeParcelable { + @SafeParceled(1) + final int versionCode = 1; + @SafeParceled(2) + public int year; + @SafeParceled(3) + public int month; + @SafeParceled(4) + public int day; + @SafeParceled(5) + public int hours; + @SafeParceled(6) + public int minutes; + @SafeParceled(7) + public int seconds; + @SafeParceled(8) + public boolean isUtc; + @SafeParceled(9) + public String rawValue; + + public static Creator CREATOR = new AutoCreator<>(CalendarDateTime.class); + } + + public static class CalendarEvent extends AutoSafeParcelable { + @SafeParceled(1) + final int versionCode = 1; + @SafeParceled(2) + public String summary; + @SafeParceled(3) + public String description; + @SafeParceled(4) + public String location; + @SafeParceled(5) + public String organizer; + @SafeParceled(6) + public String status; + @SafeParceled(7) + public Barcode.CalendarDateTime start; + @SafeParceled(8) + public Barcode.CalendarDateTime end; + + public static Creator CREATOR = new AutoCreator<>(CalendarEvent.class); + } + + public static class PersonName extends AutoSafeParcelable { + @SafeParceled(1) + final int versionCode = 1; + @SafeParceled(2) + public String formattedName; + @SafeParceled(3) + public String pronunciation; + @SafeParceled(4) + public String prefix; + @SafeParceled(5) + public String first; + @SafeParceled(6) + public String middle; + @SafeParceled(7) + public String last; + @SafeParceled(8) + public String suffix; + + public static Creator CREATOR = new AutoCreator<>(PersonName.class); + } + + public static class Address extends AutoSafeParcelable { + public static final int UNKNOWN = 0; + public static final int WORK = 1; + public static final int HOME = 2; + + @SafeParceled(1) + final int versionCode = 1; + @SafeParceled(2) + public int type; + @SafeParceled(3) + public String[] addressLines; + + public static Creator
CREATOR = new AutoCreator<>(Address.class); + } + + public static class DriverLicense extends AutoSafeParcelable { + @SafeParceled(1) + final int versionCode = 1; + @SafeParceled(2) + public String documentType; + @SafeParceled(3) + public String firstName; + @SafeParceled(4) + public String middleName; + @SafeParceled(5) + public String lastName; + @SafeParceled(6) + public String gender; + @SafeParceled(7) + public String addressStreet; + @SafeParceled(8) + public String addressCity; + @SafeParceled(9) + public String addressState; + @SafeParceled(10) + public String addressZip; + @SafeParceled(11) + public String licenseNumber; + @SafeParceled(12) + public String issueDate; + @SafeParceled(13) + public String expiryDate; + @SafeParceled(14) + public String birthDate; + @SafeParceled(15) + public String issuingCountry; + + public static Creator CREATOR = new AutoCreator<>(DriverLicense.class); + } + + public static class ContactInfo extends AutoSafeParcelable { + @SafeParceled(1) + final int versionCode = 1; + @SafeParceled(2) + public Barcode.PersonName name; + @SafeParceled(3) + public String organization; + @SafeParceled(4) + public String title; + @SafeParceled(5) + public Barcode.Phone[] phones; + @SafeParceled(6) + public Barcode.Email[] emails; + @SafeParceled(7) + public String[] urls; + @SafeParceled(8) + public Barcode.Address[] addresses; + + public static Creator CREATOR = new AutoCreator<>(ContactInfo.class); + } +} \ No newline at end of file diff --git a/play-services-vision-api/src/main/java/com/google/android/gms/vision/barcode/internal/client/BarcodeDetector.java b/play-services-vision-api/src/main/java/com/google/android/gms/vision/barcode/internal/client/BarcodeDetector.java new file mode 100644 index 0000000..74b357e --- /dev/null +++ b/play-services-vision-api/src/main/java/com/google/android/gms/vision/barcode/internal/client/BarcodeDetector.java @@ -0,0 +1,146 @@ +package com.google.android.gms.vision.barcode.internal.client; + +import android.graphics.Bitmap; +import android.graphics.Point; +import android.os.RemoteException; +import android.util.Log; + +import java.nio.ByteBuffer; +import java.nio.IntBuffer; +import java.util.ArrayList; +import java.util.List; + +import com.google.android.gms.dynamic.IObjectWrapper; +import com.google.android.gms.dynamic.ObjectWrapper; +import com.google.zxing.BinaryBitmap; +import com.google.zxing.ChecksumException; +import com.google.zxing.FormatException; +import com.google.zxing.NotFoundException; +import com.google.zxing.Reader; +import com.google.zxing.Result; +import com.google.zxing.ResultPoint; +import com.google.zxing.common.HybridBinarizer; +import com.google.zxing.datamatrix.DataMatrixReader; +import com.google.zxing.oned.CodaBarReader; +import com.google.zxing.oned.Code128Reader; +import com.google.zxing.oned.Code39Reader; +import com.google.zxing.oned.Code93Reader; +import com.google.zxing.oned.EAN13Reader; +import com.google.zxing.oned.EAN8Reader; +import com.google.zxing.oned.ITFReader; +import com.google.zxing.oned.UPCAReader; +import com.google.zxing.oned.UPCEReader; +import com.google.zxing.pdf417.PDF417Reader; +import com.google.zxing.qrcode.QRCodeReader; +import com.google.zxing.RGBLuminanceSource; + +public class BarcodeDetector extends INativeBarcodeDetector.Stub { + public static final String TAG = BarcodeDetector.class.getSimpleName(); + + // TODO: Can we force these to be a bitfield somehow? + public static final int CODE_128 = 1; + public static final int CODE_39 = 2; + public static final int CODE_93 = 4; + public static final int CODABAR = 8; + public static final int DATA_MATRIX = 16; + public static final int EAN_13 = 32; + public static final int EAN_8 = 64; + public static final int ITF = 128; + public static final int QR_CODE = 256; + public static final int UPC_A = 512; + public static final int UPC_E = 1024; + public static final int PDF417 = 2048; + + private int formats; + + public BarcodeDetector(BarcodeDetectorOptions options) { + this.formats = options.formats; + } + + @Override + public void unk0(IObjectWrapper byteBuffer, FrameMetadata metadata) throws RemoteException { + Log.i(TAG, "Unkown method 0 called"); + } + + @Override + public Barcode[] detect(IObjectWrapper wrappedBitmap, FrameMetadata metadata) throws RemoteException { + Bitmap bitmap = ObjectWrapper.unwrapTyped(wrappedBitmap, Bitmap.class); + if (bitmap == null) { + Log.e(TAG, "Could not unwrap Bitmap"); + return null; + } + + IntBuffer frameBuf = IntBuffer.allocate(bitmap.getByteCount()); + bitmap.copyPixelsToBuffer(frameBuf); + int[] frameBytes = frameBuf.array(); + + RGBLuminanceSource source = new RGBLuminanceSource(bitmap.getHeight(), bitmap.getWidth(), frameBytes); + HybridBinarizer binarizer = new HybridBinarizer(source); + BinaryBitmap binaryBitmap = new BinaryBitmap(binarizer); + List barcodes = new ArrayList<>(); + + if ((this.formats & CODE_128) != 0) { + tryDetect(new Code128Reader(), binaryBitmap, barcodes); + } + if ((this.formats & CODE_39) != 0) { + tryDetect(new Code39Reader(), binaryBitmap, barcodes); + } + if ((this.formats & CODE_93) != 0) { + tryDetect(new Code93Reader(), binaryBitmap, barcodes); + } + if ((this.formats & CODABAR) != 0) { + tryDetect(new CodaBarReader(), binaryBitmap, barcodes); + } + if ((this.formats & DATA_MATRIX) != 0) { + tryDetect(new DataMatrixReader(), binaryBitmap, barcodes); + } + if ((this.formats & EAN_13) != 0) { + tryDetect(new EAN13Reader(), binaryBitmap, barcodes); + } + if ((this.formats & EAN_8) != 0) { + tryDetect(new EAN8Reader(), binaryBitmap, barcodes); + } + if ((this.formats & ITF) != 0) { + tryDetect(new ITFReader(), binaryBitmap, barcodes); + } + if ((this.formats & QR_CODE) != 0) { + tryDetect(new QRCodeReader(), binaryBitmap, barcodes); + } + if ((this.formats & UPC_A) != 0) { + tryDetect(new UPCAReader(), binaryBitmap, barcodes); + } + if ((this.formats & UPC_E) != 0) { + tryDetect(new UPCEReader(), binaryBitmap, barcodes); + } + if ((this.formats & PDF417) != 0) { + tryDetect(new PDF417Reader(), binaryBitmap, barcodes); + } + + return barcodes.toArray(new Barcode[0]); + } + + @Override + public void unk2(IObjectWrapper unk1, FrameMetadata metadata) throws RemoteException { + Log.d(TAG, "Unknown 2 called"); + } + + private void tryDetect(Reader reader, BinaryBitmap bitmap, List results) { + try { + Result result = reader.decode(bitmap); + if (result.getText() != null) { + // Try to fill in the corner information, not sure that this is correct. + List corners = new ArrayList<>(); + for (ResultPoint point : result.getResultPoints()) { + corners.add(new Point((int)point.getX(), (int)point.getY())); + } + results.add(new Barcode(result.getText(), corners.toArray(new Point[]{}))); + } + } catch (FormatException e) { + e.printStackTrace(); + } catch (ChecksumException e) { + e.printStackTrace(); + } catch (NotFoundException e) { + Log.d(TAG, "No barcode found when detecting with " + reader.getClass().getName()); + } + } +} diff --git a/play-services-vision-api/src/main/java/com/google/android/gms/vision/barcode/internal/client/BarcodeDetectorOptions.java b/play-services-vision-api/src/main/java/com/google/android/gms/vision/barcode/internal/client/BarcodeDetectorOptions.java new file mode 100644 index 0000000..c66bb8f --- /dev/null +++ b/play-services-vision-api/src/main/java/com/google/android/gms/vision/barcode/internal/client/BarcodeDetectorOptions.java @@ -0,0 +1,14 @@ +package com.google.android.gms.vision.barcode.internal.client; + +import org.microg.safeparcel.AutoSafeParcelable; +import org.microg.safeparcel.SafeParceled; + +public class BarcodeDetectorOptions extends AutoSafeParcelable { + @SafeParceled(1) + public int versionCode = 1; + @SafeParceled(2) + public int formats; + + public static Creator CREATOR = new AutoCreator(BarcodeDetectorOptions.class); + +} diff --git a/play-services-vision-api/src/main/java/com/google/android/gms/vision/barcode/internal/client/FrameMetadata.java b/play-services-vision-api/src/main/java/com/google/android/gms/vision/barcode/internal/client/FrameMetadata.java new file mode 100644 index 0000000..90364d2 --- /dev/null +++ b/play-services-vision-api/src/main/java/com/google/android/gms/vision/barcode/internal/client/FrameMetadata.java @@ -0,0 +1,21 @@ +package com.google.android.gms.vision.barcode.internal.client; + +import org.microg.safeparcel.AutoSafeParcelable; +import org.microg.safeparcel.SafeParceled; + +public class FrameMetadata extends AutoSafeParcelable { + @SafeParceled(1) + private final int versionCode = 1; + @SafeParceled(2) + public int width; + @SafeParceled(3) + public int height; + @SafeParceled(4) + public int id; + @SafeParceled(5) + public long timestampMillis; + @SafeParceled(6) + public int rotation; + + public static Creator CREATOR = new AutoCreator(FrameMetadata.class); +} diff --git a/play-services-vision-api/src/test/java/BarcodeTypesTest.java b/play-services-vision-api/src/test/java/BarcodeTypesTest.java new file mode 100644 index 0000000..4954b37 --- /dev/null +++ b/play-services-vision-api/src/test/java/BarcodeTypesTest.java @@ -0,0 +1,161 @@ +import com.google.android.gms.vision.barcode.internal.client.Barcode; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.robolectric.RobolectricTestRunner; + +import static com.google.common.truth.Truth.assertThat; +import static com.google.common.truth.Truth.assertWithMessage; + +/** + * Verifies that we make a reasonable effort to decode the different barcode types that google + * defines. Most of the barcode data comes from these sites: + * * http://goqr.me/ + * * https://github.com/zxing/zxing/wiki/Barcode-Contents + * * https://github.com/codebude/QRCoder/wiki/Advanced-usage---Payload-generators + */ +@RunWith(RobolectricTestRunner.class) +public class BarcodeTypesTest { + @Test + public void geoPoints() { + Barcode barcode = new Barcode("geo:12.334,13.337"); + assertThat(barcode.valueFormat).isEqualTo(Barcode.GEO); + assertThat(barcode.rawValue).isEqualTo("geo:12.334,13.337"); + assertThat(barcode.geoPoint.lat).isEqualTo(12.334); + assertThat(barcode.geoPoint.lng).isEqualTo(13.337); + + barcode = new Barcode("geo:12.no,13.yes"); + assertThat(barcode.valueFormat).isEqualTo(Barcode.TEXT); + assertThat(barcode.rawValue).isEqualTo("geo:12.no,13.yes"); + assertThat(barcode.geoPoint).isNull(); + } + + @Test + public void phoneNumber() { + Barcode barcode = new Barcode("tel:+12387784"); + assertThat(barcode.valueFormat).isEqualTo(Barcode.PHONE); + assertThat(barcode.phone).isNotNull(); + assertThat(barcode.phone.number).isEqualTo("+12387784"); + assertThat(barcode.phone.type).isEqualTo(Barcode.Phone.UNKNOWN); + + barcode = new Barcode("tel:+2763test"); + assertThat(barcode.valueFormat).isEqualTo(Barcode.PHONE); + assertThat(barcode.phone).isNotNull(); + assertThat(barcode.phone.number).isEqualTo("+2763test"); + assertThat(barcode.phone.type).isEqualTo(Barcode.Phone.UNKNOWN); + } + + @Test + public void mail() { + Barcode barcode = new Barcode("mailto:test@lel.com"); + assertThat(barcode.valueFormat).isEqualTo(Barcode.EMAIL); + assertThat(barcode.email).isNotNull(); + assertThat(barcode.email.address).isEqualTo("test@lel.com"); + assertThat(barcode.email.type).isEqualTo(Barcode.Email.UNKNOWN); + assertThat(barcode.email.body).isNull(); + assertThat(barcode.email.subject).isNull(); + + barcode = new Barcode("mailto:test@lel.com?subject=hello"); + assertThat(barcode.valueFormat).isEqualTo(Barcode.EMAIL); + assertThat(barcode.email).isNotNull(); + assertThat(barcode.email.address).isEqualTo("test@lel.com"); + assertThat(barcode.email.type).isEqualTo(Barcode.Email.UNKNOWN); + assertThat(barcode.email.body).isNull(); + assertThat(barcode.email.subject).isEqualTo("hello"); + + barcode = new Barcode("mailto:test@lel.com?body=have a good day"); + assertThat(barcode.valueFormat).isEqualTo(Barcode.EMAIL); + assertThat(barcode.email).isNotNull(); + assertThat(barcode.email.address).isEqualTo("test@lel.com"); + assertThat(barcode.email.type).isEqualTo(Barcode.Email.UNKNOWN); + assertThat(barcode.email.body).isEqualTo("have a good day"); + assertThat(barcode.email.subject).isNull(); + + barcode = new Barcode("mailto:test@lel.com?body=have a good day&subject=this is a header"); + assertThat(barcode.valueFormat).isEqualTo(Barcode.EMAIL); + assertThat(barcode.email).isNotNull(); + assertThat(barcode.email.address).isEqualTo("test@lel.com"); + assertThat(barcode.email.type).isEqualTo(Barcode.Email.UNKNOWN); + assertThat(barcode.email.body).isEqualTo("have a good day"); + assertThat(barcode.email.subject).isEqualTo("this is a header"); + } + + @Test + public void sms() { + Barcode barcode = new Barcode("sms:+91827454"); + assertThat(barcode.valueFormat).isEqualTo(Barcode.SMS); + assertThat(barcode.sms).isNotNull(); + assertThat(barcode.sms.phoneNumber).isEqualTo("+91827454"); + assertThat(barcode.sms.message).isNull(); + + barcode = new Barcode("sms:91827454?body=have a good day"); + assertThat(barcode.valueFormat).isEqualTo(Barcode.SMS); + assertThat(barcode.sms).isNotNull(); + assertThat(barcode.sms.phoneNumber).isEqualTo("91827454"); + assertThat(barcode.sms.message).isEqualTo("have a good day"); + + barcode = new Barcode("sms:91827454,have a good day"); + assertThat(barcode.valueFormat).isEqualTo(Barcode.SMS); + assertThat(barcode.sms).isNotNull(); + assertThat(barcode.sms.phoneNumber).isEqualTo("91827454"); + assertThat(barcode.sms.message).isEqualTo("have a good day"); + + barcode = new Barcode("smsto:91827454:have a good day"); + assertThat(barcode.valueFormat).isEqualTo(Barcode.SMS); + assertThat(barcode.sms).isNotNull(); + assertThat(barcode.sms.phoneNumber).isEqualTo("91827454"); + assertThat(barcode.sms.message).isEqualTo("have a good day"); + } + + @Test + public void wifi() { + Barcode barcode = new Barcode("wifi:T:wpa;S:network;P:password;;"); + assertThat(barcode.valueFormat).isEqualTo(Barcode.WIFI); + assertThat(barcode.wifi).isNotNull(); + assertThat(barcode.wifi.encryptionType).isEqualTo(Barcode.WiFi.WPA); + assertThat(barcode.wifi.password).isEqualTo("password"); + assertThat(barcode.wifi.ssid).isEqualTo("network"); + + barcode = new Barcode("wifi:T:WEP;S:network;P:password;;"); + assertThat(barcode.valueFormat).isEqualTo(Barcode.WIFI); + assertThat(barcode.wifi).isNotNull(); + assertThat(barcode.wifi.encryptionType).isEqualTo(Barcode.WiFi.WEP); + + barcode = new Barcode("wifi:T:nopass;S:network;P:password;;"); + assertThat(barcode.valueFormat).isEqualTo(Barcode.WIFI); + assertThat(barcode.wifi).isNotNull(); + assertThat(barcode.wifi.encryptionType).isEqualTo(Barcode.WiFi.OPEN); + + barcode = new Barcode("wifi:T:dummy;S:network;P:password;;"); + assertThat(barcode.valueFormat).isEqualTo(Barcode.WIFI); + assertThat(barcode.wifi).isNotNull(); + assertThat(barcode.wifi.encryptionType).isEqualTo(Barcode.WiFi.OPEN); + } + + @Test + public void bookmark() { + Barcode barcode = new Barcode("http://lel.se"); + assertThat(barcode.valueFormat).isEqualTo(Barcode.URL); + assertThat(barcode.url).isNotNull(); + assertThat(barcode.url.url).isEqualTo("http://lel.se"); + assertThat(barcode.url.title).isNull(); + + barcode = new Barcode("mebkm:title:hello;url:http://lel.se;;"); + assertThat(barcode.valueFormat).isEqualTo(Barcode.URL); + assertThat(barcode.url).isNotNull(); + assertThat(barcode.url.url).isEqualTo("http://lel.se"); + assertThat(barcode.url.title).isEqualTo("hello"); + + barcode = new Barcode("mebkm:title:hello;url:http://lel.se;"); + assertThat(barcode.valueFormat).isEqualTo(Barcode.URL); + assertThat(barcode.url).isNotNull(); + assertThat(barcode.url.url).isEqualTo("http://lel.se"); + assertThat(barcode.url.title).isEqualTo("hello"); + + barcode = new Barcode("mebkm:url:http://lel.se;"); + assertThat(barcode.valueFormat).isEqualTo(Barcode.URL); + assertThat(barcode.url).isNotNull(); + assertThat(barcode.url.url).isEqualTo("http://lel.se"); + assertThat(barcode.url.title).isNull(); + } +} \ No newline at end of file diff --git a/play-services-vision-api/src/test/resources/robolectric.properties b/play-services-vision-api/src/test/resources/robolectric.properties new file mode 100644 index 0000000..89a6c8b --- /dev/null +++ b/play-services-vision-api/src/test/resources/robolectric.properties @@ -0,0 +1 @@ +sdk=28 \ No newline at end of file diff --git a/settings.gradle b/settings.gradle index a625f04..245021a 100644 --- a/settings.gradle +++ b/settings.gradle @@ -5,3 +5,4 @@ include ':play-services-cast-framework-api' include ':play-services-iid-api' include ':play-services-location-api' include ':play-services-wearable-api' +include ':play-services-vision-api'