From c10421187b1f1f6b24c4c272d739f23227528799 Mon Sep 17 00:00:00 2001 From: Marvin W Date: Sun, 24 Dec 2023 02:32:36 +0100 Subject: [PATCH] Add support for MLKit Vision Barcode API Also improve Play Services Vision Barcode API --- .../barcode-scanning/build.gradle | 54 ++++ .../src/main/AndroidManifest.xml | 9 + .../vision/barcode/aidls/IBarcodeScanner.aidl | 11 + .../barcode/aidls/IBarcodeScannerCreator.aidl | 9 + .../vision/barcode/internal/Barcode.aidl | 3 + .../internal/BarcodeScannerOptions.aidl | 3 + .../barcode/internal/ImageMetadata.aidl | 3 + .../vision/barcode/internal/Address.java | 32 ++ .../vision/barcode/internal/Barcode.java | 85 ++++++ .../internal/BarcodeScannerOptions.java | 27 ++ .../barcode/internal/CalendarDateTime.java | 41 +++ .../barcode/internal/CalendarEvent.java | 37 +++ .../vision/barcode/internal/ContactInfo.java | 45 +++ .../barcode/internal/DriverLicense.java | 66 ++++ .../mlkit/vision/barcode/internal/Email.java | 36 +++ .../vision/barcode/internal/GeoPoint.java | 27 ++ .../barcode/internal/ImageMetadata.java | 46 +++ .../vision/barcode/internal/PersonName.java | 45 +++ .../mlkit/vision/barcode/internal/Phone.java | 34 +++ .../mlkit/vision/barcode/internal/Sms.java | 27 ++ .../vision/barcode/internal/UrlBookmark.java | 27 ++ .../mlkit/vision/barcode/internal/WiFi.java | 34 +++ play-services-vision/core/build.gradle | 3 +- .../dynamite/barcode/ModuleDescriptor.java | 11 + .../barcode/mlkit/BarcodeScannerCreator.kt | 22 ++ .../gms/vision/barcode/BarcodeDecodeHelper.kt | 75 +++++ .../gms/vision/barcode/BarcodeDetector.kt | 286 ++++++++++++++---- .../gms/vision/barcode/BarcodeScanner.kt | 272 +++++++++++++++++ .../vision/barcode/DirectLuminanceSource.kt | 23 -- .../microg/gms/vision/barcode/Extensions.kt | 200 ------------ .../gms/vision/barcode/MultiBarcodeReader.kt | 114 +++++++ settings.gradle | 1 + 32 files changed, 1424 insertions(+), 284 deletions(-) create mode 100644 play-services-mlkit/barcode-scanning/build.gradle create mode 100644 play-services-mlkit/barcode-scanning/src/main/AndroidManifest.xml create mode 100644 play-services-mlkit/barcode-scanning/src/main/aidl/com/google/mlkit/vision/barcode/aidls/IBarcodeScanner.aidl create mode 100644 play-services-mlkit/barcode-scanning/src/main/aidl/com/google/mlkit/vision/barcode/aidls/IBarcodeScannerCreator.aidl create mode 100644 play-services-mlkit/barcode-scanning/src/main/aidl/com/google/mlkit/vision/barcode/internal/Barcode.aidl create mode 100644 play-services-mlkit/barcode-scanning/src/main/aidl/com/google/mlkit/vision/barcode/internal/BarcodeScannerOptions.aidl create mode 100644 play-services-mlkit/barcode-scanning/src/main/aidl/com/google/mlkit/vision/barcode/internal/ImageMetadata.aidl create mode 100644 play-services-mlkit/barcode-scanning/src/main/java/com/google/mlkit/vision/barcode/internal/Address.java create mode 100644 play-services-mlkit/barcode-scanning/src/main/java/com/google/mlkit/vision/barcode/internal/Barcode.java create mode 100644 play-services-mlkit/barcode-scanning/src/main/java/com/google/mlkit/vision/barcode/internal/BarcodeScannerOptions.java create mode 100644 play-services-mlkit/barcode-scanning/src/main/java/com/google/mlkit/vision/barcode/internal/CalendarDateTime.java create mode 100644 play-services-mlkit/barcode-scanning/src/main/java/com/google/mlkit/vision/barcode/internal/CalendarEvent.java create mode 100644 play-services-mlkit/barcode-scanning/src/main/java/com/google/mlkit/vision/barcode/internal/ContactInfo.java create mode 100644 play-services-mlkit/barcode-scanning/src/main/java/com/google/mlkit/vision/barcode/internal/DriverLicense.java create mode 100644 play-services-mlkit/barcode-scanning/src/main/java/com/google/mlkit/vision/barcode/internal/Email.java create mode 100644 play-services-mlkit/barcode-scanning/src/main/java/com/google/mlkit/vision/barcode/internal/GeoPoint.java create mode 100644 play-services-mlkit/barcode-scanning/src/main/java/com/google/mlkit/vision/barcode/internal/ImageMetadata.java create mode 100644 play-services-mlkit/barcode-scanning/src/main/java/com/google/mlkit/vision/barcode/internal/PersonName.java create mode 100644 play-services-mlkit/barcode-scanning/src/main/java/com/google/mlkit/vision/barcode/internal/Phone.java create mode 100644 play-services-mlkit/barcode-scanning/src/main/java/com/google/mlkit/vision/barcode/internal/Sms.java create mode 100644 play-services-mlkit/barcode-scanning/src/main/java/com/google/mlkit/vision/barcode/internal/UrlBookmark.java create mode 100644 play-services-mlkit/barcode-scanning/src/main/java/com/google/mlkit/vision/barcode/internal/WiFi.java create mode 100644 play-services-vision/core/src/main/java/com/google/android/gms/dynamite/descriptors/com/google/android/gms/vision/dynamite/barcode/ModuleDescriptor.java create mode 100644 play-services-vision/core/src/main/kotlin/com/google/android/gms/vision/barcode/mlkit/BarcodeScannerCreator.kt create mode 100644 play-services-vision/core/src/main/kotlin/org/microg/gms/vision/barcode/BarcodeDecodeHelper.kt create mode 100644 play-services-vision/core/src/main/kotlin/org/microg/gms/vision/barcode/BarcodeScanner.kt delete mode 100644 play-services-vision/core/src/main/kotlin/org/microg/gms/vision/barcode/DirectLuminanceSource.kt delete mode 100644 play-services-vision/core/src/main/kotlin/org/microg/gms/vision/barcode/Extensions.kt create mode 100644 play-services-vision/core/src/main/kotlin/org/microg/gms/vision/barcode/MultiBarcodeReader.kt diff --git a/play-services-mlkit/barcode-scanning/build.gradle b/play-services-mlkit/barcode-scanning/build.gradle new file mode 100644 index 0000000000..17ebd62682 --- /dev/null +++ b/play-services-mlkit/barcode-scanning/build.gradle @@ -0,0 +1,54 @@ +/* + * SPDX-FileCopyrightText: 2023 microG Project Team + * SPDX-License-Identifier: Apache-2.0 + */ + +apply plugin: 'com.android.library' +apply plugin: 'maven-publish' +apply plugin: 'signing' + +android { + namespace "com.google.mlkit.vision.barcode" + + compileSdkVersion androidCompileSdk + buildToolsVersion "$androidBuildVersionTools" + + buildFeatures { + aidl = true + } + + defaultConfig { + versionName version + minSdkVersion androidMinSdk + targetSdkVersion androidTargetSdk + } + + compileOptions { + sourceCompatibility = 1.8 + targetCompatibility = 1.8 + } +} + +apply from: '../../gradle/publish-android.gradle' + +description = 'microG implementation of play-services-mlkit-barcode-scanning' + +dependencies { + // Dependencies from play-services-mlkit-barcode-scanning:18.3.0 + //api "com.google.android.datatransport:transport-api:2.2.1" + //api "com.google.android.datatransport:transport-backend-cct:2.3.3" + //api "com.google.android.datatransport:transport-runtime:2.2.6" + api project(':play-services-base') + api project(':play-services-basement') + api project(':play-services-tasks') + //api "com.google.android.odml:image:1.0.0-beta1" + //api "com.google.firebase:firebase-components:16.1.0" + //api "com.google.firebase:firebase-encoders:16.1.0" + //api "com.google.firebase:firebase-encoders-json:17.1.0" + //api "com.google.mlkit:barcode-scanning-common:17.0.0" + //api "com.google.mlkit:common:18.9.0" + //api "com.google.mlkit:vision-common:17.3.0" + //api "com.google.mlkit:vision-interfaces:16.2.0" + + annotationProcessor project(":safe-parcel-processor") +} diff --git a/play-services-mlkit/barcode-scanning/src/main/AndroidManifest.xml b/play-services-mlkit/barcode-scanning/src/main/AndroidManifest.xml new file mode 100644 index 0000000000..7918bbe131 --- /dev/null +++ b/play-services-mlkit/barcode-scanning/src/main/AndroidManifest.xml @@ -0,0 +1,9 @@ + + + + + + \ No newline at end of file diff --git a/play-services-mlkit/barcode-scanning/src/main/aidl/com/google/mlkit/vision/barcode/aidls/IBarcodeScanner.aidl b/play-services-mlkit/barcode-scanning/src/main/aidl/com/google/mlkit/vision/barcode/aidls/IBarcodeScanner.aidl new file mode 100644 index 0000000000..1551c0dc52 --- /dev/null +++ b/play-services-mlkit/barcode-scanning/src/main/aidl/com/google/mlkit/vision/barcode/aidls/IBarcodeScanner.aidl @@ -0,0 +1,11 @@ +package com.google.mlkit.vision.barcode.aidls; + +import com.google.android.gms.dynamic.IObjectWrapper; +import com.google.mlkit.vision.barcode.internal.Barcode; +import com.google.mlkit.vision.barcode.internal.ImageMetadata; + +interface IBarcodeScanner { + void init() = 0; + void close() = 1; + List detect(IObjectWrapper image, in ImageMetadata metadata) = 2; +} \ No newline at end of file diff --git a/play-services-mlkit/barcode-scanning/src/main/aidl/com/google/mlkit/vision/barcode/aidls/IBarcodeScannerCreator.aidl b/play-services-mlkit/barcode-scanning/src/main/aidl/com/google/mlkit/vision/barcode/aidls/IBarcodeScannerCreator.aidl new file mode 100644 index 0000000000..40146a114c --- /dev/null +++ b/play-services-mlkit/barcode-scanning/src/main/aidl/com/google/mlkit/vision/barcode/aidls/IBarcodeScannerCreator.aidl @@ -0,0 +1,9 @@ +package com.google.mlkit.vision.barcode.aidls; + +import com.google.android.gms.dynamic.IObjectWrapper; +import com.google.mlkit.vision.barcode.aidls.IBarcodeScanner; +import com.google.mlkit.vision.barcode.internal.BarcodeScannerOptions; + +interface IBarcodeScannerCreator { + IBarcodeScanner create(IObjectWrapper wrappedContext, in BarcodeScannerOptions options) = 0; +} \ No newline at end of file diff --git a/play-services-mlkit/barcode-scanning/src/main/aidl/com/google/mlkit/vision/barcode/internal/Barcode.aidl b/play-services-mlkit/barcode-scanning/src/main/aidl/com/google/mlkit/vision/barcode/internal/Barcode.aidl new file mode 100644 index 0000000000..55784178f6 --- /dev/null +++ b/play-services-mlkit/barcode-scanning/src/main/aidl/com/google/mlkit/vision/barcode/internal/Barcode.aidl @@ -0,0 +1,3 @@ +package com.google.mlkit.vision.barcode.internal; + +parcelable Barcode; \ No newline at end of file diff --git a/play-services-mlkit/barcode-scanning/src/main/aidl/com/google/mlkit/vision/barcode/internal/BarcodeScannerOptions.aidl b/play-services-mlkit/barcode-scanning/src/main/aidl/com/google/mlkit/vision/barcode/internal/BarcodeScannerOptions.aidl new file mode 100644 index 0000000000..218ce3049d --- /dev/null +++ b/play-services-mlkit/barcode-scanning/src/main/aidl/com/google/mlkit/vision/barcode/internal/BarcodeScannerOptions.aidl @@ -0,0 +1,3 @@ +package com.google.mlkit.vision.barcode.internal; + +parcelable BarcodeScannerOptions; \ No newline at end of file diff --git a/play-services-mlkit/barcode-scanning/src/main/aidl/com/google/mlkit/vision/barcode/internal/ImageMetadata.aidl b/play-services-mlkit/barcode-scanning/src/main/aidl/com/google/mlkit/vision/barcode/internal/ImageMetadata.aidl new file mode 100644 index 0000000000..4bbe20c66b --- /dev/null +++ b/play-services-mlkit/barcode-scanning/src/main/aidl/com/google/mlkit/vision/barcode/internal/ImageMetadata.aidl @@ -0,0 +1,3 @@ +package com.google.mlkit.vision.barcode.internal; + +parcelable ImageMetadata; \ No newline at end of file diff --git a/play-services-mlkit/barcode-scanning/src/main/java/com/google/mlkit/vision/barcode/internal/Address.java b/play-services-mlkit/barcode-scanning/src/main/java/com/google/mlkit/vision/barcode/internal/Address.java new file mode 100644 index 0000000000..30bc6b5c12 --- /dev/null +++ b/play-services-mlkit/barcode-scanning/src/main/java/com/google/mlkit/vision/barcode/internal/Address.java @@ -0,0 +1,32 @@ +/* + * SPDX-FileCopyrightText: 2023 microG Project Team + * SPDX-License-Identifier: Apache-2.0 + */ + +package com.google.mlkit.vision.barcode.internal; + +import android.os.Parcel; +import androidx.annotation.NonNull; +import com.google.android.gms.common.internal.safeparcel.AbstractSafeParcelable; +import com.google.android.gms.common.internal.safeparcel.SafeParcelable; +import com.google.android.gms.common.internal.safeparcel.SafeParcelableCreatorAndWriter; + +@SafeParcelable.Class +public class Address extends AbstractSafeParcelable { + @Field(1) + public int type; + @Field(2) + public String[] addressLines; + + // TODO: Copied from com.google.mlkit.vision.barcode.common.Barcode.Address + public static final int UNKNOWN = 0; + public static final int WORK = 1; + public static final int HOME = 2; + + @Override + public void writeToParcel(@NonNull Parcel dest, int flags) { + CREATOR.writeToParcel(this, dest, flags); + } + + public static final SafeParcelableCreatorAndWriter
CREATOR = findCreator(Address.class); +} diff --git a/play-services-mlkit/barcode-scanning/src/main/java/com/google/mlkit/vision/barcode/internal/Barcode.java b/play-services-mlkit/barcode-scanning/src/main/java/com/google/mlkit/vision/barcode/internal/Barcode.java new file mode 100644 index 0000000000..a2837293c2 --- /dev/null +++ b/play-services-mlkit/barcode-scanning/src/main/java/com/google/mlkit/vision/barcode/internal/Barcode.java @@ -0,0 +1,85 @@ +/* + * SPDX-FileCopyrightText: 2023 microG Project Team + * SPDX-License-Identifier: Apache-2.0 + */ + +package com.google.mlkit.vision.barcode.internal; + +import android.graphics.Point; +import android.os.Parcel; +import androidx.annotation.NonNull; +import com.google.android.gms.common.internal.safeparcel.AbstractSafeParcelable; +import com.google.android.gms.common.internal.safeparcel.SafeParcelable; +import com.google.android.gms.common.internal.safeparcel.SafeParcelableCreatorAndWriter; + +@SafeParcelable.Class +public class Barcode extends AbstractSafeParcelable { + @Field(1) + public int format; + @Field(2) + public String displayValue; + @Field(3) + public String rawValue; + @Field(4) + public byte[] rawBytes; + @Field(5) + public Point[] cornerPoints; + @Field(6) + public int valueType; + @Field(7) + public Email email; + @Field(8) + public Phone phone; + @Field(9) + public Sms sms; + @Field(10) + public WiFi wifi; + @Field(11) + public UrlBookmark urlBookmark; + @Field(12) + public GeoPoint geoPoint; + @Field(13) + public CalendarEvent calendarEvent; + @Field(14) + public ContactInfo contactInfo; + @Field(15) + public DriverLicense driverLicense; + + // TODO: Copied from com.google.mlkit.vision.barcode.common.Barcode + public static final int UNKNOWN_FORMAT = -1; + public static final int ALL_FORMATS = 0; + 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; + public static final int AZTEC = 4096; + + public static final int UNKNOWN_TYPE = 0; + 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; + + @Override + public void writeToParcel(@NonNull Parcel dest, int flags) { + CREATOR.writeToParcel(this, dest, flags); + } + + public static final SafeParcelableCreatorAndWriter CREATOR = findCreator(Barcode.class); +} diff --git a/play-services-mlkit/barcode-scanning/src/main/java/com/google/mlkit/vision/barcode/internal/BarcodeScannerOptions.java b/play-services-mlkit/barcode-scanning/src/main/java/com/google/mlkit/vision/barcode/internal/BarcodeScannerOptions.java new file mode 100644 index 0000000000..c1df86bc06 --- /dev/null +++ b/play-services-mlkit/barcode-scanning/src/main/java/com/google/mlkit/vision/barcode/internal/BarcodeScannerOptions.java @@ -0,0 +1,27 @@ +/* + * SPDX-FileCopyrightText: 2023 microG Project Team + * SPDX-License-Identifier: Apache-2.0 + */ + +package com.google.mlkit.vision.barcode.internal; + +import android.os.Parcel; +import androidx.annotation.NonNull; +import com.google.android.gms.common.internal.safeparcel.AbstractSafeParcelable; +import com.google.android.gms.common.internal.safeparcel.SafeParcelable; +import com.google.android.gms.common.internal.safeparcel.SafeParcelableCreatorAndWriter; + +@SafeParcelable.Class +public class BarcodeScannerOptions extends AbstractSafeParcelable { + @Field(1) + public int supportedFormats; + @Field(2) + public boolean allPotentialBarcodesEnabled; + + @Override + public void writeToParcel(@NonNull Parcel dest, int flags) { + CREATOR.writeToParcel(this, dest, flags); + } + + public static final SafeParcelableCreatorAndWriter CREATOR = findCreator(BarcodeScannerOptions.class); +} diff --git a/play-services-mlkit/barcode-scanning/src/main/java/com/google/mlkit/vision/barcode/internal/CalendarDateTime.java b/play-services-mlkit/barcode-scanning/src/main/java/com/google/mlkit/vision/barcode/internal/CalendarDateTime.java new file mode 100644 index 0000000000..e59c07ae00 --- /dev/null +++ b/play-services-mlkit/barcode-scanning/src/main/java/com/google/mlkit/vision/barcode/internal/CalendarDateTime.java @@ -0,0 +1,41 @@ +/* + * SPDX-FileCopyrightText: 2023 microG Project Team + * SPDX-License-Identifier: Apache-2.0 + */ + +package com.google.mlkit.vision.barcode.internal; + +import android.os.Parcel; +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import com.google.android.gms.common.internal.safeparcel.AbstractSafeParcelable; +import com.google.android.gms.common.internal.safeparcel.SafeParcelable; +import com.google.android.gms.common.internal.safeparcel.SafeParcelableCreatorAndWriter; + +@SafeParcelable.Class +public class CalendarDateTime extends AbstractSafeParcelable { + @Field(1) + public int year; + @Field(2) + public int month; + @Field(3) + public int day; + @Field(4) + public int hours; + @Field(5) + public int minutes; + @Field(6) + public int seconds; + @Field(7) + public boolean isUtc; + @Field(8) + @Nullable + public String rawValue; + + @Override + public void writeToParcel(@NonNull Parcel dest, int flags) { + CREATOR.writeToParcel(this, dest, flags); + } + + public static final SafeParcelableCreatorAndWriter CREATOR = findCreator(CalendarDateTime.class); +} diff --git a/play-services-mlkit/barcode-scanning/src/main/java/com/google/mlkit/vision/barcode/internal/CalendarEvent.java b/play-services-mlkit/barcode-scanning/src/main/java/com/google/mlkit/vision/barcode/internal/CalendarEvent.java new file mode 100644 index 0000000000..d5c77f80fc --- /dev/null +++ b/play-services-mlkit/barcode-scanning/src/main/java/com/google/mlkit/vision/barcode/internal/CalendarEvent.java @@ -0,0 +1,37 @@ +/* + * SPDX-FileCopyrightText: 2023 microG Project Team + * SPDX-License-Identifier: Apache-2.0 + */ + +package com.google.mlkit.vision.barcode.internal; + +import android.os.Parcel; +import androidx.annotation.NonNull; +import com.google.android.gms.common.internal.safeparcel.AbstractSafeParcelable; +import com.google.android.gms.common.internal.safeparcel.SafeParcelable; +import com.google.android.gms.common.internal.safeparcel.SafeParcelableCreatorAndWriter; + +@SafeParcelable.Class +public class CalendarEvent extends AbstractSafeParcelable { + @Field(1) + public String summary; + @Field(2) + public String description; + @Field(3) + public String location; + @Field(4) + public String organizer; + @Field(5) + public String status; + @Field(6) + public CalendarDateTime start; + @Field(7) + public CalendarDateTime end; + + @Override + public void writeToParcel(@NonNull Parcel dest, int flags) { + CREATOR.writeToParcel(this, dest, flags); + } + + public static final SafeParcelableCreatorAndWriter CREATOR = findCreator(CalendarEvent.class); +} diff --git a/play-services-mlkit/barcode-scanning/src/main/java/com/google/mlkit/vision/barcode/internal/ContactInfo.java b/play-services-mlkit/barcode-scanning/src/main/java/com/google/mlkit/vision/barcode/internal/ContactInfo.java new file mode 100644 index 0000000000..a1e5880d9c --- /dev/null +++ b/play-services-mlkit/barcode-scanning/src/main/java/com/google/mlkit/vision/barcode/internal/ContactInfo.java @@ -0,0 +1,45 @@ +/* + * SPDX-FileCopyrightText: 2023 microG Project Team + * SPDX-License-Identifier: Apache-2.0 + */ + +package com.google.mlkit.vision.barcode.internal; + +import android.os.Parcel; +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import com.google.android.gms.common.internal.safeparcel.AbstractSafeParcelable; +import com.google.android.gms.common.internal.safeparcel.SafeParcelable; +import com.google.android.gms.common.internal.safeparcel.SafeParcelableCreatorAndWriter; + +@SafeParcelable.Class +public class ContactInfo extends AbstractSafeParcelable { + @Field(1) + @Nullable + public PersonName name; + @Field(2) + @Nullable + public String organization; + @Field(3) + @Nullable + public String title; + @Field(4) + @Nullable + public Phone[] phones; + @Field(5) + @Nullable + public Email[] emails; + @Field(6) + @Nullable + public String[] urls; + @Field(7) + @Nullable + public Address[] addresses; + + @Override + public void writeToParcel(@NonNull Parcel dest, int flags) { + CREATOR.writeToParcel(this, dest, flags); + } + + public static final SafeParcelableCreatorAndWriter CREATOR = findCreator(ContactInfo.class); +} diff --git a/play-services-mlkit/barcode-scanning/src/main/java/com/google/mlkit/vision/barcode/internal/DriverLicense.java b/play-services-mlkit/barcode-scanning/src/main/java/com/google/mlkit/vision/barcode/internal/DriverLicense.java new file mode 100644 index 0000000000..aec56aa21b --- /dev/null +++ b/play-services-mlkit/barcode-scanning/src/main/java/com/google/mlkit/vision/barcode/internal/DriverLicense.java @@ -0,0 +1,66 @@ +/* + * SPDX-FileCopyrightText: 2023 microG Project Team + * SPDX-License-Identifier: Apache-2.0 + */ + +package com.google.mlkit.vision.barcode.internal; + +import android.os.Parcel; +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import com.google.android.gms.common.internal.safeparcel.AbstractSafeParcelable; +import com.google.android.gms.common.internal.safeparcel.SafeParcelable; +import com.google.android.gms.common.internal.safeparcel.SafeParcelableCreatorAndWriter; + +@SafeParcelable.Class +public class DriverLicense extends AbstractSafeParcelable { + @Field(1) + @Nullable + public String documentType; + @Field(2) + @Nullable + public String firstName; + @Field(3) + @Nullable + public String middleName; + @Field(4) + @Nullable + public String listName; + @Field(5) + @Nullable + public String gender; + @Field(6) + @Nullable + public String addressStreet; + @Field(7) + @Nullable + public String addressCity; + @Field(8) + @Nullable + public String addressState; + @Field(9) + @Nullable + public String addressZip; + @Field(10) + @Nullable + public String licenseNumber; + @Field(11) + @Nullable + public String issueDate; + @Field(12) + @Nullable + public String expiryDate; + @Field(13) + @Nullable + public String birthDate; + @Field(14) + @Nullable + public String issuingCountry; + + @Override + public void writeToParcel(@NonNull Parcel dest, int flags) { + CREATOR.writeToParcel(this, dest, flags); + } + + public static final SafeParcelableCreatorAndWriter CREATOR = findCreator(DriverLicense.class); +} diff --git a/play-services-mlkit/barcode-scanning/src/main/java/com/google/mlkit/vision/barcode/internal/Email.java b/play-services-mlkit/barcode-scanning/src/main/java/com/google/mlkit/vision/barcode/internal/Email.java new file mode 100644 index 0000000000..1a4629564f --- /dev/null +++ b/play-services-mlkit/barcode-scanning/src/main/java/com/google/mlkit/vision/barcode/internal/Email.java @@ -0,0 +1,36 @@ +/* + * SPDX-FileCopyrightText: 2023 microG Project Team + * SPDX-License-Identifier: Apache-2.0 + */ + +package com.google.mlkit.vision.barcode.internal; + +import android.os.Parcel; +import androidx.annotation.NonNull; +import com.google.android.gms.common.internal.safeparcel.AbstractSafeParcelable; +import com.google.android.gms.common.internal.safeparcel.SafeParcelable; +import com.google.android.gms.common.internal.safeparcel.SafeParcelableCreatorAndWriter; + +@SafeParcelable.Class +public class Email extends AbstractSafeParcelable { + @Field(1) + public int type; + @Field(2) + public String address; + @Field(3) + public String subject; + @Field(4) + public String body; + + // TODO: Copied from com.google.mlkit.vision.barcode.common.Barcode.Email + public static final int UNKNOWN = 0; + public static final int WORK = 1; + public static final int HOME = 2; + + @Override + public void writeToParcel(@NonNull Parcel dest, int flags) { + CREATOR.writeToParcel(this, dest, flags); + } + + public static final SafeParcelableCreatorAndWriter CREATOR = findCreator(Email.class); +} diff --git a/play-services-mlkit/barcode-scanning/src/main/java/com/google/mlkit/vision/barcode/internal/GeoPoint.java b/play-services-mlkit/barcode-scanning/src/main/java/com/google/mlkit/vision/barcode/internal/GeoPoint.java new file mode 100644 index 0000000000..95623595e7 --- /dev/null +++ b/play-services-mlkit/barcode-scanning/src/main/java/com/google/mlkit/vision/barcode/internal/GeoPoint.java @@ -0,0 +1,27 @@ +/* + * SPDX-FileCopyrightText: 2023 microG Project Team + * SPDX-License-Identifier: Apache-2.0 + */ + +package com.google.mlkit.vision.barcode.internal; + +import android.os.Parcel; +import androidx.annotation.NonNull; +import com.google.android.gms.common.internal.safeparcel.AbstractSafeParcelable; +import com.google.android.gms.common.internal.safeparcel.SafeParcelable; +import com.google.android.gms.common.internal.safeparcel.SafeParcelableCreatorAndWriter; + +@SafeParcelable.Class +public class GeoPoint extends AbstractSafeParcelable { + @Field(1) + public double lat; + @Field(2) + public double lng; + + @Override + public void writeToParcel(@NonNull Parcel dest, int flags) { + CREATOR.writeToParcel(this, dest, flags); + } + + public static final SafeParcelableCreatorAndWriter CREATOR = findCreator(GeoPoint.class); +} diff --git a/play-services-mlkit/barcode-scanning/src/main/java/com/google/mlkit/vision/barcode/internal/ImageMetadata.java b/play-services-mlkit/barcode-scanning/src/main/java/com/google/mlkit/vision/barcode/internal/ImageMetadata.java new file mode 100644 index 0000000000..f66b090710 --- /dev/null +++ b/play-services-mlkit/barcode-scanning/src/main/java/com/google/mlkit/vision/barcode/internal/ImageMetadata.java @@ -0,0 +1,46 @@ +/* + * SPDX-FileCopyrightText: 2023 microG Project Team + * SPDX-License-Identifier: Apache-2.0 + */ + +package com.google.mlkit.vision.barcode.internal; + +import android.os.Parcel; +import androidx.annotation.NonNull; +import com.google.android.gms.common.internal.safeparcel.AbstractSafeParcelable; +import com.google.android.gms.common.internal.safeparcel.SafeParcelable; +import com.google.android.gms.common.internal.safeparcel.SafeParcelableCreatorAndWriter; +import org.microg.gms.utils.ToStringHelper; + +@SafeParcelable.Class +public class ImageMetadata extends AbstractSafeParcelable { + @Field(1) + public int format; + @Field(2) + public int width; + @Field(3) + public int height; + @Field(4) + public int rotation; + @Field(5) + public long timestamp; + + @NonNull + @Override + public String toString() { + return ToStringHelper.name("ImageMetadata") + .field("format", format) + .field("width", width) + .field("height", height) + .field("rotation", rotation) + .field("timestamp", timestamp) + .end(); + } + + @Override + public void writeToParcel(@NonNull Parcel dest, int flags) { + CREATOR.writeToParcel(this, dest, flags); + } + + public static final SafeParcelableCreatorAndWriter CREATOR = findCreator(ImageMetadata.class); +} diff --git a/play-services-mlkit/barcode-scanning/src/main/java/com/google/mlkit/vision/barcode/internal/PersonName.java b/play-services-mlkit/barcode-scanning/src/main/java/com/google/mlkit/vision/barcode/internal/PersonName.java new file mode 100644 index 0000000000..cf518d6f91 --- /dev/null +++ b/play-services-mlkit/barcode-scanning/src/main/java/com/google/mlkit/vision/barcode/internal/PersonName.java @@ -0,0 +1,45 @@ +/* + * SPDX-FileCopyrightText: 2023 microG Project Team + * SPDX-License-Identifier: Apache-2.0 + */ + +package com.google.mlkit.vision.barcode.internal; + +import android.os.Parcel; +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import com.google.android.gms.common.internal.safeparcel.AbstractSafeParcelable; +import com.google.android.gms.common.internal.safeparcel.SafeParcelable; +import com.google.android.gms.common.internal.safeparcel.SafeParcelableCreatorAndWriter; + +@SafeParcelable.Class +public class PersonName extends AbstractSafeParcelable { + @Field(1) + @Nullable + public String formattedName; + @Field(2) + @Nullable + public String pronunciation; + @Field(3) + @Nullable + public String prefix; + @Field(4) + @Nullable + public String first; + @Field(5) + @Nullable + public String middle; + @Field(6) + @Nullable + public String last; + @Field(7) + @Nullable + public String suffix; + + @Override + public void writeToParcel(@NonNull Parcel dest, int flags) { + CREATOR.writeToParcel(this, dest, flags); + } + + public static final SafeParcelableCreatorAndWriter CREATOR = findCreator(PersonName.class); +} diff --git a/play-services-mlkit/barcode-scanning/src/main/java/com/google/mlkit/vision/barcode/internal/Phone.java b/play-services-mlkit/barcode-scanning/src/main/java/com/google/mlkit/vision/barcode/internal/Phone.java new file mode 100644 index 0000000000..dfd2c2e598 --- /dev/null +++ b/play-services-mlkit/barcode-scanning/src/main/java/com/google/mlkit/vision/barcode/internal/Phone.java @@ -0,0 +1,34 @@ +/* + * SPDX-FileCopyrightText: 2023 microG Project Team + * SPDX-License-Identifier: Apache-2.0 + */ + +package com.google.mlkit.vision.barcode.internal; + +import android.os.Parcel; +import androidx.annotation.NonNull; +import com.google.android.gms.common.internal.safeparcel.AbstractSafeParcelable; +import com.google.android.gms.common.internal.safeparcel.SafeParcelable; +import com.google.android.gms.common.internal.safeparcel.SafeParcelableCreatorAndWriter; + +@SafeParcelable.Class +public class Phone extends AbstractSafeParcelable { + @Field(1) + public int type; + @Field(2) + public String number; + + // TODO: Copied from com.google.mlkit.vision.barcode.common.Barcode.Phone + 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; + + @Override + public void writeToParcel(@NonNull Parcel dest, int flags) { + CREATOR.writeToParcel(this, dest, flags); + } + + public static final SafeParcelableCreatorAndWriter CREATOR = findCreator(Phone.class); +} diff --git a/play-services-mlkit/barcode-scanning/src/main/java/com/google/mlkit/vision/barcode/internal/Sms.java b/play-services-mlkit/barcode-scanning/src/main/java/com/google/mlkit/vision/barcode/internal/Sms.java new file mode 100644 index 0000000000..0a6cfba604 --- /dev/null +++ b/play-services-mlkit/barcode-scanning/src/main/java/com/google/mlkit/vision/barcode/internal/Sms.java @@ -0,0 +1,27 @@ +/* + * SPDX-FileCopyrightText: 2023 microG Project Team + * SPDX-License-Identifier: Apache-2.0 + */ + +package com.google.mlkit.vision.barcode.internal; + +import android.os.Parcel; +import androidx.annotation.NonNull; +import com.google.android.gms.common.internal.safeparcel.AbstractSafeParcelable; +import com.google.android.gms.common.internal.safeparcel.SafeParcelable; +import com.google.android.gms.common.internal.safeparcel.SafeParcelableCreatorAndWriter; + +@SafeParcelable.Class +public class Sms extends AbstractSafeParcelable { + @Field(1) + public String message; + @Field(2) + public String phoneNumber; + + @Override + public void writeToParcel(@NonNull Parcel dest, int flags) { + CREATOR.writeToParcel(this, dest, flags); + } + + public static final SafeParcelableCreatorAndWriter CREATOR = findCreator(Sms.class); +} diff --git a/play-services-mlkit/barcode-scanning/src/main/java/com/google/mlkit/vision/barcode/internal/UrlBookmark.java b/play-services-mlkit/barcode-scanning/src/main/java/com/google/mlkit/vision/barcode/internal/UrlBookmark.java new file mode 100644 index 0000000000..d74803c89e --- /dev/null +++ b/play-services-mlkit/barcode-scanning/src/main/java/com/google/mlkit/vision/barcode/internal/UrlBookmark.java @@ -0,0 +1,27 @@ +/* + * SPDX-FileCopyrightText: 2023 microG Project Team + * SPDX-License-Identifier: Apache-2.0 + */ + +package com.google.mlkit.vision.barcode.internal; + +import android.os.Parcel; +import androidx.annotation.NonNull; +import com.google.android.gms.common.internal.safeparcel.AbstractSafeParcelable; +import com.google.android.gms.common.internal.safeparcel.SafeParcelable; +import com.google.android.gms.common.internal.safeparcel.SafeParcelableCreatorAndWriter; + +@SafeParcelable.Class +public class UrlBookmark extends AbstractSafeParcelable { + @Field(1) + public String title; + @Field(2) + public String url; + + @Override + public void writeToParcel(@NonNull Parcel dest, int flags) { + CREATOR.writeToParcel(this, dest, flags); + } + + public static final SafeParcelableCreatorAndWriter CREATOR = findCreator(UrlBookmark.class); +} diff --git a/play-services-mlkit/barcode-scanning/src/main/java/com/google/mlkit/vision/barcode/internal/WiFi.java b/play-services-mlkit/barcode-scanning/src/main/java/com/google/mlkit/vision/barcode/internal/WiFi.java new file mode 100644 index 0000000000..50d1582aa5 --- /dev/null +++ b/play-services-mlkit/barcode-scanning/src/main/java/com/google/mlkit/vision/barcode/internal/WiFi.java @@ -0,0 +1,34 @@ +/* + * SPDX-FileCopyrightText: 2023 microG Project Team + * SPDX-License-Identifier: Apache-2.0 + */ + +package com.google.mlkit.vision.barcode.internal; + +import android.os.Parcel; +import androidx.annotation.NonNull; +import com.google.android.gms.common.internal.safeparcel.AbstractSafeParcelable; +import com.google.android.gms.common.internal.safeparcel.SafeParcelable; +import com.google.android.gms.common.internal.safeparcel.SafeParcelableCreatorAndWriter; + +@SafeParcelable.Class +public class WiFi extends AbstractSafeParcelable { + @Field(1) + public String ssid; + @Field(2) + public String password; + @Field(3) + public int encryptionType; + + // TODO: Copied from com.google.mlkit.vision.barcode.common.Barcode.WiFi + public static final int OPEN = 1; + public static final int WPA = 2; + public static final int WEP = 3; + + @Override + public void writeToParcel(@NonNull Parcel dest, int flags) { + CREATOR.writeToParcel(this, dest, flags); + } + + public static final SafeParcelableCreatorAndWriter CREATOR = findCreator(WiFi.class); +} diff --git a/play-services-vision/core/build.gradle b/play-services-vision/core/build.gradle index 03f39c7f55..f3d29d0a3a 100644 --- a/play-services-vision/core/build.gradle +++ b/play-services-vision/core/build.gradle @@ -9,11 +9,12 @@ apply plugin: 'maven-publish' apply plugin: 'signing' dependencies { + api project(':play-services-mlkit-barcode-scanning') api project(':play-services-vision') implementation project(':play-services-base-core') implementation "androidx.annotation:annotation:$annotationVersion" - implementation 'com.google.zxing:core:3.4.1' + implementation "com.google.zxing:core:3.5.2" } android { diff --git a/play-services-vision/core/src/main/java/com/google/android/gms/dynamite/descriptors/com/google/android/gms/vision/dynamite/barcode/ModuleDescriptor.java b/play-services-vision/core/src/main/java/com/google/android/gms/dynamite/descriptors/com/google/android/gms/vision/dynamite/barcode/ModuleDescriptor.java new file mode 100644 index 0000000000..0bbb6a8c41 --- /dev/null +++ b/play-services-vision/core/src/main/java/com/google/android/gms/dynamite/descriptors/com/google/android/gms/vision/dynamite/barcode/ModuleDescriptor.java @@ -0,0 +1,11 @@ +/* + * SPDX-FileCopyrightText: 2020, microG Project Team + * SPDX-License-Identifier: Apache-2.0 + */ + +package com.google.android.gms.dynamite.descriptors.com.google.android.gms.vision.dynamite.barcode; + +public class ModuleDescriptor { + public static final String MODULE_ID = "com.google.android.gms.vision.dynamite.barcode"; + public static final int MODULE_VERSION = 1; +} diff --git a/play-services-vision/core/src/main/kotlin/com/google/android/gms/vision/barcode/mlkit/BarcodeScannerCreator.kt b/play-services-vision/core/src/main/kotlin/com/google/android/gms/vision/barcode/mlkit/BarcodeScannerCreator.kt new file mode 100644 index 0000000000..033a3ee30e --- /dev/null +++ b/play-services-vision/core/src/main/kotlin/com/google/android/gms/vision/barcode/mlkit/BarcodeScannerCreator.kt @@ -0,0 +1,22 @@ +/* + * SPDX-FileCopyrightText: 2023 microG Project Team + * SPDX-License-Identifier: Apache-2.0 + */ + +package com.google.android.gms.vision.barcode.mlkit + +import android.content.Context +import androidx.annotation.Keep +import com.google.android.gms.dynamic.IObjectWrapper +import com.google.android.gms.dynamic.unwrap +import com.google.mlkit.vision.barcode.aidls.IBarcodeScannerCreator +import com.google.mlkit.vision.barcode.aidls.IBarcodeScanner +import com.google.mlkit.vision.barcode.internal.BarcodeScannerOptions +import org.microg.gms.vision.barcode.BarcodeScanner + +@Keep +class BarcodeScannerCreator : IBarcodeScannerCreator.Stub() { + override fun create(context: IObjectWrapper, options: BarcodeScannerOptions): IBarcodeScanner { + return BarcodeScanner(context.unwrap()!!, options) + } +} \ No newline at end of file diff --git a/play-services-vision/core/src/main/kotlin/org/microg/gms/vision/barcode/BarcodeDecodeHelper.kt b/play-services-vision/core/src/main/kotlin/org/microg/gms/vision/barcode/BarcodeDecodeHelper.kt new file mode 100644 index 0000000000..61a3be8dd2 --- /dev/null +++ b/play-services-vision/core/src/main/kotlin/org/microg/gms/vision/barcode/BarcodeDecodeHelper.kt @@ -0,0 +1,75 @@ +/* + * SPDX-FileCopyrightText: 2023 microG Project Team + * SPDX-License-Identifier: Apache-2.0 + */ + +package org.microg.gms.vision.barcode + +import android.graphics.Bitmap +import android.graphics.ImageFormat +import android.media.Image +import android.os.Build.VERSION.SDK_INT +import android.util.Log +import androidx.annotation.RequiresApi +import com.google.zxing.* +import com.google.zxing.common.HybridBinarizer +import java.nio.ByteBuffer +import java.nio.IntBuffer + +private const val TAG = "BarcodeDecodeHelper" + +class BarcodeDecodeHelper(formats: List, multi: Boolean = true) { + private val reader = MultiBarcodeReader( + mapOf( + DecodeHintType.ALSO_INVERTED to true, + DecodeHintType.POSSIBLE_FORMATS to formats + ) + ) + + fun decodeFromSource(source: LuminanceSource): List { + return try { + reader.multiDecode(BinaryBitmap(HybridBinarizer(source))).also { + if (it.isNotEmpty()) reader.reset() + } + } catch (e: NotFoundException) { + emptyList() + } catch (e: FormatException) { + emptyList() + } catch (e: ChecksumException) { + emptyList() + } catch (e: Exception) { + Log.w(TAG, "Exception with $this: $e") + emptyList() + } + } + + fun decodeFromLuminanceBytes(bytes: ByteArray, width: Int, height: Int, rowStride: Int = width): List { + return decodeFromSource(PlanarYUVLuminanceSource(bytes, rowStride, height, 0, 0, width, height, false)) + } + + fun decodeFromLuminanceBytes(buffer: ByteBuffer, width: Int, height: Int, rowStride: Int = width): List { + val bytes = ByteArray(buffer.remaining()) + buffer.get(bytes) + buffer.rewind() + return decodeFromLuminanceBytes(bytes, width, height, rowStride) + } + + @RequiresApi(19) + fun decodeFromImage(image: Image): List { + if (image.format !in SUPPORTED_IMAGE_FORMATS) return emptyList() + val yPlane = image.planes[0] + return decodeFromLuminanceBytes(yPlane.buffer, image.width, image.height, yPlane.rowStride) + } + + fun decodeFromBitmap(bitmap: Bitmap): List { + val frameBuf: IntBuffer = IntBuffer.allocate(bitmap.byteCount) + bitmap.copyPixelsToBuffer(frameBuf) + return decodeFromSource(RGBLuminanceSource(bitmap.width, bitmap.height, frameBuf.array())) + } + + companion object { + @RequiresApi(19) + val SUPPORTED_IMAGE_FORMATS = + listOfNotNull(ImageFormat.YUV_420_888, if (SDK_INT >= 23) ImageFormat.YUV_422_888 else null, if (SDK_INT >= 23) ImageFormat.YUV_444_888 else null) + } +} \ No newline at end of file diff --git a/play-services-vision/core/src/main/kotlin/org/microg/gms/vision/barcode/BarcodeDetector.kt b/play-services-vision/core/src/main/kotlin/org/microg/gms/vision/barcode/BarcodeDetector.kt index e5c5957030..79379dd842 100644 --- a/play-services-vision/core/src/main/kotlin/org/microg/gms/vision/barcode/BarcodeDetector.kt +++ b/play-services-vision/core/src/main/kotlin/org/microg/gms/vision/barcode/BarcodeDetector.kt @@ -7,94 +7,260 @@ package org.microg.gms.vision.barcode import android.content.Context import android.graphics.Bitmap +import android.graphics.Point import android.util.Log import com.google.android.gms.dynamic.IObjectWrapper +import com.google.android.gms.dynamic.ObjectWrapper import com.google.android.gms.dynamic.unwrap import com.google.android.gms.vision.barcode.Barcode 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.vision.internal.FrameMetadataParcel import com.google.zxing.* -import com.google.zxing.aztec.AztecReader -import com.google.zxing.common.HybridBinarizer -import com.google.zxing.datamatrix.DataMatrixReader -import com.google.zxing.multi.MultipleBarcodeReader -import com.google.zxing.multi.qrcode.QRCodeMultiReader -import com.google.zxing.oned.* -import com.google.zxing.pdf417.PDF417Reader +import com.google.zxing.client.result.* import java.nio.ByteBuffer import java.nio.IntBuffer +import java.util.* -private const val TAG = "GmsVisionBarcode" +private const val TAG = "BarcodeDetector" class BarcodeDetector(val context: Context, val options: BarcodeDetectorOptions) : INativeBarcodeDetector.Stub() { + private val helper = BarcodeDecodeHelper(options.formats.gmsToZXingBarcodeFormats()) + private var loggedOnce = false + override fun detectBitmap(wrappedBitmap: IObjectWrapper, metadata: FrameMetadataParcel): Array { + if (!loggedOnce) Log.d(TAG, "detectBitmap(${ObjectWrapper.unwrap(wrappedBitmap)}, $metadata)").also { loggedOnce = true } val bitmap = wrappedBitmap.unwrap() ?: return emptyArray() - val frameBuf: IntBuffer = IntBuffer.allocate(bitmap.byteCount) - bitmap.copyPixelsToBuffer(frameBuf) - return detectFromSource(RGBLuminanceSource(metadata.width, metadata.height, frameBuf.array())) + return helper.decodeFromBitmap(bitmap) + .mapNotNull { runCatching { it.toGms(metadata) }.getOrNull() }.toTypedArray() } override fun detectBytes(wrappedByteBuffer: IObjectWrapper, metadata: FrameMetadataParcel): Array { - return detectFromSource(DirectLuminanceSource(metadata.width, metadata.height, wrappedByteBuffer.unwrap() - ?: return emptyArray())) + if (!loggedOnce) Log.d(TAG, "detectBytes(${ObjectWrapper.unwrap(wrappedByteBuffer)}, $metadata)").also { loggedOnce = true } + val bytes = wrappedByteBuffer.unwrap() ?: return emptyArray() + return helper.decodeFromLuminanceBytes(bytes, metadata.width, metadata.height) + .mapNotNull { runCatching { it.toGms(metadata) }.getOrNull() }.toTypedArray() + } + + override fun close() { + Log.d(TAG, "close()") } +} + +private fun Int.gmsToZXingBarcodeFormats(): List { + return listOfNotNull( + BarcodeFormat.AZTEC.takeIf { (this and Barcode.AZTEC) > 0 || this == Barcode.ALL_FORMATS }, + BarcodeFormat.CODABAR.takeIf { (this and Barcode.CODABAR) > 0 || this == Barcode.ALL_FORMATS }, + BarcodeFormat.CODE_39.takeIf { (this and Barcode.CODE_39) > 0 || this == Barcode.ALL_FORMATS }, + BarcodeFormat.CODE_93.takeIf { (this and Barcode.CODE_93) > 0 || this == Barcode.ALL_FORMATS }, + BarcodeFormat.CODE_128.takeIf { (this and Barcode.CODE_128) > 0 || this == Barcode.ALL_FORMATS }, + BarcodeFormat.DATA_MATRIX.takeIf { (this and Barcode.DATA_MATRIX) > 0 || this == Barcode.ALL_FORMATS }, + BarcodeFormat.EAN_8.takeIf { (this and Barcode.EAN_8) > 0 || this == Barcode.ALL_FORMATS }, + BarcodeFormat.EAN_13.takeIf { (this and Barcode.EAN_13) > 0 || this == Barcode.ALL_FORMATS }, + BarcodeFormat.ITF.takeIf { (this and Barcode.ITF) > 0 || this == Barcode.ALL_FORMATS }, + BarcodeFormat.PDF_417.takeIf { (this and Barcode.PDF417) > 0 || this == Barcode.ALL_FORMATS }, + BarcodeFormat.QR_CODE.takeIf { (this and Barcode.QR_CODE) > 0 || this == Barcode.ALL_FORMATS }, + BarcodeFormat.UPC_A.takeIf { (this and Barcode.UPC_A) > 0 || this == Barcode.ALL_FORMATS }, + BarcodeFormat.UPC_E.takeIf { (this and Barcode.UPC_E) > 0 || this == Barcode.ALL_FORMATS }, + ) +} + + +private fun BarcodeFormat.toGms(): Int = when (this) { + BarcodeFormat.AZTEC -> Barcode.AZTEC + BarcodeFormat.CODABAR -> Barcode.CODABAR + BarcodeFormat.CODE_39 -> Barcode.CODE_39 + BarcodeFormat.CODE_93 -> Barcode.CODE_93 + BarcodeFormat.CODE_128 -> Barcode.CODE_128 + BarcodeFormat.DATA_MATRIX -> Barcode.DATA_MATRIX + BarcodeFormat.EAN_13 -> Barcode.EAN_13 + BarcodeFormat.EAN_8 -> Barcode.EAN_8 + BarcodeFormat.ITF -> Barcode.ITF + BarcodeFormat.PDF_417 -> Barcode.PDF417 + BarcodeFormat.QR_CODE -> Barcode.QR_CODE + BarcodeFormat.UPC_A -> Barcode.UPC_A + BarcodeFormat.UPC_E -> Barcode.UPC_E + else -> throw UnsupportedOperationException() +} + +private fun ParsedResultType.toGms(): Int = when (this) { + ParsedResultType.ADDRESSBOOK -> Barcode.CONTACT_INFO + ParsedResultType.CALENDAR -> Barcode.CALENDAR_EVENT + ParsedResultType.EMAIL_ADDRESS -> Barcode.EMAIL + ParsedResultType.GEO -> Barcode.GEO + ParsedResultType.ISBN -> Barcode.ISBN + ParsedResultType.PRODUCT -> Barcode.PRODUCT + ParsedResultType.SMS -> Barcode.SMS + ParsedResultType.TEL -> Barcode.PHONE + ParsedResultType.TEXT -> Barcode.TEXT + ParsedResultType.URI -> Barcode.URL + ParsedResultType.WIFI -> Barcode.WIFI + else -> Barcode.TEXT +} - private fun mayDecodeType(image: BinaryBitmap, type: Int, reader: () -> Reader): List { - return if ((options.formats and type) != 0 || options.formats == Barcode.ALL_FORMATS) { - reader().run { - try { - if (this is MultipleBarcodeReader) { - decodeMultiple(image).map { it.toGms() } - } else { - listOf(decode(image).toGms()) - } - } catch (e: NotFoundException) { - emptyList() - } catch (e: FormatException) { - emptyList() - } catch (e: ChecksumException) { - emptyList() - } catch (e: Exception) { - Log.w(TAG, "Exception with $this: $e") - emptyList() - } +private fun AddressBookParsedResult.toGms(): Barcode.ContactInfo { + val contactInfo = Barcode.ContactInfo() + // TODO: contactInfo.name + contactInfo.organization = org + contactInfo.title = title + contactInfo.phones = phoneNumbers.orEmpty().mapIndexed { i, a -> + Barcode.Phone().apply { + type = when (phoneTypes?.getOrNull(i)) { + "WORK" -> Barcode.Phone.WORK + "HOME" -> Barcode.Phone.HOME + "FAX" -> Barcode.Phone.FAX + "MOBILE" -> Barcode.Phone.MOBILE + else -> Barcode.Phone.UNKNOWN + } + number = a + } + }.toTypedArray() + contactInfo.emails = emails.orEmpty().mapIndexed { i, a -> + Barcode.Email().apply { + type = when (emailTypes?.getOrNull(i)) { + "WORK" -> Barcode.Email.WORK + "HOME" -> Barcode.Email.HOME + else -> Barcode.Email.UNKNOWN } + address = a + } + }.toTypedArray() + contactInfo.urls = urLs + contactInfo.addresses = addresses.orEmpty().mapIndexed { i, a -> + Barcode.Address().apply { + type = when (addressTypes?.getOrNull(i)) { + "WORK" -> Barcode.Address.WORK + "HOME" -> Barcode.Address.HOME + else -> Barcode.Address.UNKNOWN + } + addressLines = a.split("\n").toTypedArray() + } + }.toTypedArray() + + return contactInfo +} + +private fun CalendarParsedResult.toGms(): Barcode.CalendarEvent { + fun createDateTime(timestamp: Long, isAllDay: Boolean) = Barcode.CalendarDateTime().apply { + val calendar = Calendar.getInstance() + calendar.time = Date(timestamp) + year = calendar.get(Calendar.YEAR) + month = calendar.get(Calendar.MONTH) + day = calendar.get(Calendar.DAY_OF_MONTH) + if (isAllDay) { + hours = -1 + minutes = -1 + seconds = -1 } else { - emptyList() + hours = calendar.get(Calendar.HOUR_OF_DAY) + minutes = calendar.get(Calendar.MINUTE) + seconds = calendar.get(Calendar.SECOND) } } - private fun detectFromImage(image: BinaryBitmap, results: MutableList): Int { - var resultsSize = results.size - results.addAll(mayDecodeType(image, Barcode.CODE_128) { Code128Reader() }) - results.addAll(mayDecodeType(image, Barcode.CODE_39) { Code39Reader() }) - results.addAll(mayDecodeType(image, Barcode.CODABAR) { CodaBarReader() }) - results.addAll(mayDecodeType(image, Barcode.DATA_MATRIX) { DataMatrixReader() }) - results.addAll(mayDecodeType(image, Barcode.EAN_13) { EAN13Reader() }) - results.addAll(mayDecodeType(image, Barcode.EAN_8) { EAN8Reader() }) - results.addAll(mayDecodeType(image, Barcode.ITF) { ITFReader() }) - results.addAll(mayDecodeType(image, Barcode.QR_CODE) { QRCodeMultiReader() }) - results.addAll(mayDecodeType(image, Barcode.UPC_A) { UPCAReader() }) - results.addAll(mayDecodeType(image, Barcode.UPC_E) { UPCEReader() }) - results.addAll(mayDecodeType(image, Barcode.PDF417) { PDF417Reader() }) - results.addAll(mayDecodeType(image, Barcode.AZTEC) { AztecReader() }) - return results.size - resultsSize + + val event = Barcode.CalendarEvent() + event.summary = summary + event.description = description + event.location = location + event.organizer = organizer + event.start = createDateTime(startTimestamp, isStartAllDay) + event.end = createDateTime(endTimestamp, isEndAllDay) + return event +} + +private fun EmailAddressParsedResult.toGms(): Barcode.Email { + val email = Barcode.Email() + email.address = tos?.getOrNull(0) + email.subject = subject + email.body = body + return email +} + +private fun GeoParsedResult.toGms(): Barcode.GeoPoint { + val geo = Barcode.GeoPoint() + geo.lat = latitude + geo.lng = longitude + return geo +} + +private fun TelParsedResult.toGms(): Barcode.Phone { + val phone = Barcode.Phone() + phone.number = number + return phone +} + +private fun SMSParsedResult.toGms(): Barcode.Sms { + val sms = Barcode.Sms() + sms.message = body + sms.phoneNumber = numbers?.getOrNull(0) + return sms +} + +private fun WifiParsedResult.toGms(): Barcode.WiFi { + val wifi = Barcode.WiFi() + wifi.ssid = ssid + wifi.password = password + wifi.encryptionType = when (networkEncryption) { + "OPEN" -> Barcode.WiFi.OPEN + "WEP" -> Barcode.WiFi.WEP + "WPA" -> Barcode.WiFi.WPA + "WPA2" -> Barcode.WiFi.WPA + else -> 0 } + return wifi +} - private fun detectFromSource(source: LuminanceSource): Array { - val results = arrayListOf() - try { - detectFromImage(BinaryBitmap(HybridBinarizer(source)), results) - detectFromImage(BinaryBitmap(HybridBinarizer(source.invert())), results) - } catch (e: Exception) { - Log.w(TAG, e) +private fun URIParsedResult.toGms(): Barcode.UrlBookmark { + val url = Barcode.UrlBookmark() + url.url = uri + url.title = title + return url +} + +private fun Result.toGms(metadata: FrameMetadataParcel): Barcode { + val barcode = Barcode() + barcode.format = barcodeFormat.toGms() + barcode.rawBytes = rawBytes + barcode.rawValue = text + barcode.cornerPoints = resultPoints.map { + when (metadata.rotation) { + 1 -> Point(metadata.height - it.y.toInt(), it.x.toInt()) + 2 -> Point(metadata.width - it.x.toInt(), metadata.height - it.y.toInt()) + 3 -> Point(it.y.toInt(), metadata.width - it.x.toInt()) + else -> Point(it.x.toInt(), it.y.toInt()) } + }.toTypedArray() - return results.distinctBy { it.rawValue }.toTypedArray() - } + val parsed = ResultParser.parseResult(this) - override fun close() { - Log.d(TAG, "close()") + barcode.displayValue = parsed.displayResult + barcode.valueFormat = parsed.type.toGms() + when (parsed) { + is EmailAddressParsedResult -> + barcode.email = parsed.toGms() + + is TelParsedResult -> + barcode.phone = parsed.toGms() + + is SMSParsedResult -> + barcode.sms = parsed.toGms() + + is WifiParsedResult -> + barcode.wifi = parsed.toGms() + + is URIParsedResult -> + barcode.url = parsed.toGms() + + is GeoParsedResult -> + barcode.geoPoint = parsed.toGms() + + is CalendarParsedResult -> + barcode.calendarEvent = parsed.toGms() + + is AddressBookParsedResult -> + barcode.contactInfo = parsed.toGms() } -} + + return barcode +} \ No newline at end of file diff --git a/play-services-vision/core/src/main/kotlin/org/microg/gms/vision/barcode/BarcodeScanner.kt b/play-services-vision/core/src/main/kotlin/org/microg/gms/vision/barcode/BarcodeScanner.kt new file mode 100644 index 0000000000..101b9bae28 --- /dev/null +++ b/play-services-vision/core/src/main/kotlin/org/microg/gms/vision/barcode/BarcodeScanner.kt @@ -0,0 +1,272 @@ +/* + * SPDX-FileCopyrightText: 2023 microG Project Team + * SPDX-License-Identifier: Apache-2.0 + */ + +package org.microg.gms.vision.barcode + +import android.content.Context +import android.graphics.ImageFormat +import android.graphics.Point +import android.media.Image +import android.os.Build.VERSION.SDK_INT +import android.os.Parcel +import android.util.Log +import com.google.android.gms.dynamic.IObjectWrapper +import com.google.android.gms.dynamic.ObjectWrapper +import com.google.android.gms.dynamic.unwrap +import com.google.mlkit.vision.barcode.aidls.IBarcodeScanner +import com.google.mlkit.vision.barcode.internal.* +import com.google.zxing.BarcodeFormat +import com.google.zxing.Result +import com.google.zxing.client.result.* +import org.microg.gms.utils.warnOnTransactionIssues +import java.nio.ByteBuffer +import java.util.* + +private const val TAG = "BarcodeScanner" + +class BarcodeScanner(val context: Context, val options: BarcodeScannerOptions) : IBarcodeScanner.Stub() { + private val helper = + BarcodeDecodeHelper(if (options.allPotentialBarcodesEnabled) BarcodeFormat.values().toList() else options.supportedFormats.mlKitToZXingBarcodeFormats()) + private var loggedOnce = false + + override fun init() { + Log.d(TAG, "init()") + } + + override fun close() { + Log.d(TAG, "close()") + } + + override fun detect(wrappedImage: IObjectWrapper, metadata: ImageMetadata): List { + if (!loggedOnce) Log.d(TAG, "detect(${ObjectWrapper.unwrap(wrappedImage)}, $metadata)").also { loggedOnce = true } + return when (metadata.format) { + ImageFormat.NV21 -> wrappedImage.unwrap()?.let { helper.decodeFromLuminanceBytes(it, metadata.width, metadata.height) } + ImageFormat.YUV_420_888 -> if (SDK_INT >= 19) wrappedImage.unwrap()?.let { image -> helper.decodeFromImage(image) } else null + + else -> null + }?.map { it.toMlKit(metadata) } ?: emptyList() + } + + override fun onTransact(code: Int, data: Parcel, reply: Parcel?, flags: Int): Boolean = + warnOnTransactionIssues(code, reply, flags, TAG) { super.onTransact(code, data, reply, flags) } +} + +private fun Int.mlKitToZXingBarcodeFormats(): List { + return listOfNotNull( + BarcodeFormat.AZTEC.takeIf { (this and Barcode.AZTEC) > 0 || this == Barcode.ALL_FORMATS }, + BarcodeFormat.CODABAR.takeIf { (this and Barcode.CODABAR) > 0 || this == Barcode.ALL_FORMATS }, + BarcodeFormat.CODE_39.takeIf { (this and Barcode.CODE_39) > 0 || this == Barcode.ALL_FORMATS }, + BarcodeFormat.CODE_93.takeIf { (this and Barcode.CODE_93) > 0 || this == Barcode.ALL_FORMATS }, + BarcodeFormat.CODE_128.takeIf { (this and Barcode.CODE_128) > 0 || this == Barcode.ALL_FORMATS }, + BarcodeFormat.DATA_MATRIX.takeIf { (this and Barcode.DATA_MATRIX) > 0 || this == Barcode.ALL_FORMATS }, + BarcodeFormat.EAN_8.takeIf { (this and Barcode.EAN_8) > 0 || this == Barcode.ALL_FORMATS }, + BarcodeFormat.EAN_13.takeIf { (this and Barcode.EAN_13) > 0 || this == Barcode.ALL_FORMATS }, + BarcodeFormat.ITF.takeIf { (this and Barcode.ITF) > 0 || this == Barcode.ALL_FORMATS }, + BarcodeFormat.PDF_417.takeIf { (this and Barcode.PDF417) > 0 || this == Barcode.ALL_FORMATS }, + BarcodeFormat.QR_CODE.takeIf { (this and Barcode.QR_CODE) > 0 || this == Barcode.ALL_FORMATS }, + BarcodeFormat.UPC_A.takeIf { (this and Barcode.UPC_A) > 0 || this == Barcode.ALL_FORMATS }, + BarcodeFormat.UPC_E.takeIf { (this and Barcode.UPC_E) > 0 || this == Barcode.ALL_FORMATS }, + ) +} + +private fun BarcodeFormat.toMlKit(): Int = when (this) { + BarcodeFormat.AZTEC -> Barcode.AZTEC + BarcodeFormat.CODABAR -> Barcode.CODABAR + BarcodeFormat.CODE_39 -> Barcode.CODE_39 + BarcodeFormat.CODE_93 -> Barcode.CODE_93 + BarcodeFormat.CODE_128 -> Barcode.CODE_128 + BarcodeFormat.DATA_MATRIX -> Barcode.DATA_MATRIX + BarcodeFormat.EAN_8 -> Barcode.EAN_8 + BarcodeFormat.EAN_13 -> Barcode.EAN_13 + BarcodeFormat.ITF -> Barcode.ITF + BarcodeFormat.PDF_417 -> Barcode.PDF417 + BarcodeFormat.QR_CODE -> Barcode.QR_CODE + BarcodeFormat.UPC_A -> Barcode.UPC_A + BarcodeFormat.UPC_E -> Barcode.UPC_E + else -> Barcode.UNKNOWN_FORMAT +} + +private fun ParsedResultType.toMlKit(): Int = when (this) { + ParsedResultType.ADDRESSBOOK -> Barcode.CONTACT_INFO + ParsedResultType.CALENDAR -> Barcode.CALENDAR_EVENT + ParsedResultType.EMAIL_ADDRESS -> Barcode.EMAIL + ParsedResultType.GEO -> Barcode.GEO + ParsedResultType.ISBN -> Barcode.ISBN + ParsedResultType.PRODUCT -> Barcode.PRODUCT + ParsedResultType.SMS -> Barcode.SMS + ParsedResultType.TEL -> Barcode.PHONE + ParsedResultType.TEXT -> Barcode.TEXT + ParsedResultType.URI -> Barcode.URL + ParsedResultType.WIFI -> Barcode.WIFI + else -> Barcode.UNKNOWN_TYPE +} + +private fun AddressBookParsedResult.toMlKit(): ContactInfo { + val contactInfo = ContactInfo() + // TODO: contactInfo.name + contactInfo.organization = org + contactInfo.title = title + contactInfo.phones = phoneNumbers.orEmpty().mapIndexed { i, a -> + Phone().apply { + type = when (phoneTypes?.getOrNull(i)) { + "WORK" -> Phone.WORK + "HOME" -> Phone.HOME + "FAX" -> Phone.FAX + "MOBILE" -> Phone.MOBILE + else -> Phone.UNKNOWN + } + number = a + } + }.toTypedArray() + contactInfo.emails = emails.orEmpty().mapIndexed { i, a -> + Email().apply { + type = when (emailTypes?.getOrNull(i)) { + "WORK" -> Email.WORK + "HOME" -> Email.HOME + else -> Email.UNKNOWN + } + address = a + } + }.toTypedArray() + contactInfo.urls = urLs + contactInfo.addresses = addresses.orEmpty().mapIndexed { i, a -> + Address().apply { + type = when (addressTypes?.getOrNull(i)) { + "WORK" -> Address.WORK + "HOME" -> Address.HOME + else -> Address.UNKNOWN + } + addressLines = a.split("\n").toTypedArray() + } + }.toTypedArray() + + return contactInfo +} + +private fun CalendarParsedResult.toMlKit(): CalendarEvent { + fun createDateTime(timestamp: Long, isAllDay: Boolean) = CalendarDateTime().apply { + val calendar = Calendar.getInstance() + calendar.time = Date(timestamp) + year = calendar.get(Calendar.YEAR) + month = calendar.get(Calendar.MONTH) + day = calendar.get(Calendar.DAY_OF_MONTH) + if (isAllDay) { + hours = -1 + minutes = -1 + seconds = -1 + } else { + hours = calendar.get(Calendar.HOUR_OF_DAY) + minutes = calendar.get(Calendar.MINUTE) + seconds = calendar.get(Calendar.SECOND) + } + } + + + val event = CalendarEvent() + event.summary = summary + event.description = description + event.location = location + event.organizer = organizer + event.start = createDateTime(startTimestamp, isStartAllDay) + event.end = createDateTime(endTimestamp, isEndAllDay) + return event +} + +private fun EmailAddressParsedResult.toMlKit(): Email { + val email = Email() + email.address = tos?.getOrNull(0) + email.subject = subject + email.body = body + return email +} + +private fun GeoParsedResult.toMlKit(): GeoPoint { + val geo = GeoPoint() + geo.lat = latitude + geo.lng = longitude + return geo +} + +private fun TelParsedResult.toMlKit(): Phone { + val phone = Phone() + phone.number = number + return phone +} + +private fun SMSParsedResult.toMlKit(): Sms { + val sms = Sms() + sms.message = body + sms.phoneNumber = numbers?.getOrNull(0) + return sms +} + +private fun WifiParsedResult.toMlKit(): WiFi { + val wifi = WiFi() + wifi.ssid = ssid + wifi.password = password + wifi.encryptionType = when (networkEncryption) { + "OPEN" -> WiFi.OPEN + "WEP" -> WiFi.WEP + "WPA" -> WiFi.WPA + "WPA2" -> WiFi.WPA + else -> 0 + } + return wifi +} + + +private fun URIParsedResult.toMlKit(): UrlBookmark { + val url = UrlBookmark() + url.url = uri + url.title = title + return url +} + +private fun Result.toMlKit(metadata: ImageMetadata): Barcode { + val barcode = Barcode() + barcode.format = barcodeFormat.toMlKit() + barcode.rawBytes = rawBytes + barcode.rawValue = text + barcode.cornerPoints = resultPoints.map { + when (metadata.rotation) { + 1 -> Point(metadata.height - it.y.toInt(), it.x.toInt()) + 2 -> Point(metadata.width - it.x.toInt(), metadata.height - it.y.toInt()) + 3 -> Point(it.y.toInt(), metadata.width - it.x.toInt()) + else -> Point(it.x.toInt(), it.y.toInt()) + } + }.toTypedArray() + + val parsed = ResultParser.parseResult(this) + + barcode.displayValue = parsed.displayResult + barcode.valueType = parsed.type.toMlKit() + when (parsed) { + is EmailAddressParsedResult -> + barcode.email = parsed.toMlKit() + + is TelParsedResult -> + barcode.phone = parsed.toMlKit() + + is SMSParsedResult -> + barcode.sms = parsed.toMlKit() + + is WifiParsedResult -> + barcode.wifi = parsed.toMlKit() + + is URIParsedResult -> + barcode.urlBookmark = parsed.toMlKit() + + is GeoParsedResult -> + barcode.geoPoint = parsed.toMlKit() + + is CalendarParsedResult -> + barcode.calendarEvent = parsed.toMlKit() + + is AddressBookParsedResult -> + barcode.contactInfo = parsed.toMlKit() + } + + return barcode +} \ No newline at end of file diff --git a/play-services-vision/core/src/main/kotlin/org/microg/gms/vision/barcode/DirectLuminanceSource.kt b/play-services-vision/core/src/main/kotlin/org/microg/gms/vision/barcode/DirectLuminanceSource.kt deleted file mode 100644 index f0a5fce0b1..0000000000 --- a/play-services-vision/core/src/main/kotlin/org/microg/gms/vision/barcode/DirectLuminanceSource.kt +++ /dev/null @@ -1,23 +0,0 @@ -/* - * SPDX-FileCopyrightText: 2020, microG Project Team - * SPDX-License-Identifier: Apache-2.0 - */ - -package org.microg.gms.vision.barcode - -import com.google.zxing.LuminanceSource -import java.nio.ByteBuffer - -class DirectLuminanceSource(width: Int, height: Int, val bytes: ByteBuffer) : LuminanceSource(width, height) { - override fun getRow(y: Int, row: ByteArray?): ByteArray { - val row = row?.takeIf { it.size >= width } ?: ByteArray(width) - bytes.position(width * y) - bytes.get(row, 0, width) - return row - } - - override fun getMatrix(): ByteArray { - return bytes.array() - } - -} diff --git a/play-services-vision/core/src/main/kotlin/org/microg/gms/vision/barcode/Extensions.kt b/play-services-vision/core/src/main/kotlin/org/microg/gms/vision/barcode/Extensions.kt deleted file mode 100644 index 19f270d2c2..0000000000 --- a/play-services-vision/core/src/main/kotlin/org/microg/gms/vision/barcode/Extensions.kt +++ /dev/null @@ -1,200 +0,0 @@ -/* - * SPDX-FileCopyrightText: 2020, microG Project Team - * SPDX-License-Identifier: Apache-2.0 - */ - -package org.microg.gms.vision.barcode - -import android.graphics.Point -import com.google.android.gms.vision.barcode.Barcode -import com.google.zxing.BarcodeFormat -import com.google.zxing.Result -import com.google.zxing.ResultPoint -import com.google.zxing.client.result.* -import java.util.* - -fun BarcodeFormat.toGms(): Int = when (this) { - BarcodeFormat.AZTEC -> Barcode.AZTEC - BarcodeFormat.CODABAR -> Barcode.CODABAR - BarcodeFormat.CODE_128 -> Barcode.CODE_128 - BarcodeFormat.CODE_39 -> Barcode.CODE_39 - BarcodeFormat.CODE_93 -> Barcode.CODE_93 - BarcodeFormat.DATA_MATRIX -> Barcode.DATA_MATRIX - BarcodeFormat.EAN_13 -> Barcode.EAN_13 - BarcodeFormat.EAN_8 -> Barcode.EAN_8 - BarcodeFormat.ITF -> Barcode.ITF - BarcodeFormat.PDF_417 -> Barcode.PDF417 - BarcodeFormat.QR_CODE -> Barcode.QR_CODE - BarcodeFormat.UPC_A -> Barcode.UPC_A - BarcodeFormat.UPC_E -> Barcode.UPC_E - else -> Barcode.ALL_FORMATS -} - -fun ParsedResultType.toGms(): Int = when (this) { - ParsedResultType.ADDRESSBOOK -> Barcode.CONTACT_INFO - ParsedResultType.CALENDAR -> Barcode.CALENDAR_EVENT - ParsedResultType.EMAIL_ADDRESS -> Barcode.EMAIL - ParsedResultType.GEO -> Barcode.GEO - ParsedResultType.ISBN -> Barcode.ISBN - ParsedResultType.PRODUCT -> Barcode.PRODUCT - ParsedResultType.SMS -> Barcode.SMS - ParsedResultType.TEL -> Barcode.PHONE - ParsedResultType.TEXT -> Barcode.TEXT - ParsedResultType.URI -> Barcode.URL - ParsedResultType.WIFI -> Barcode.WIFI - else -> Barcode.TEXT -} - -fun AddressBookParsedResult.toGms(): Barcode.ContactInfo { - val contactInfo = Barcode.ContactInfo() - // TODO: contactInfo.name - contactInfo.organization = org - contactInfo.title = title - contactInfo.phones = phoneNumbers.orEmpty().mapIndexed { i, a -> - Barcode.Phone().apply { - type = when (phoneTypes?.getOrNull(i)) { - "WORK" -> Barcode.Phone.WORK - "HOME" -> Barcode.Phone.HOME - "FAX" -> Barcode.Phone.FAX - "MOBILE" -> Barcode.Phone.MOBILE - else -> Barcode.Phone.UNKNOWN - } - number = a - } - }.toTypedArray() - contactInfo.emails = emails.orEmpty().mapIndexed { i, a -> - Barcode.Email().apply { - type = when (emailTypes?.getOrNull(i)) { - "WORK" -> Barcode.Email.WORK - "HOME" -> Barcode.Email.HOME - else -> Barcode.Email.UNKNOWN - } - address = a - } - }.toTypedArray() - contactInfo.urls = urLs - contactInfo.addresses = addresses.orEmpty().mapIndexed { i, a -> - Barcode.Address().apply { - type = when (addressTypes?.getOrNull(i)) { - "WORK" -> Barcode.Address.WORK - "HOME" -> Barcode.Address.HOME - else -> Barcode.Address.UNKNOWN - } - addressLines = a.split("\n").toTypedArray() - } - }.toTypedArray() - - return contactInfo -} - -fun CalendarParsedResult.toGms(): Barcode.CalendarEvent { - fun createDateTime(timestamp: Long, isAllDay: Boolean) = Barcode.CalendarDateTime().apply { - val calendar = Calendar.getInstance() - calendar.time = Date(timestamp) - year = calendar.get(Calendar.YEAR) - month = calendar.get(Calendar.MONTH) - day = calendar.get(Calendar.DAY_OF_MONTH) - if (isAllDay) { - hours = -1 - minutes = -1 - seconds = -1 - } else { - hours = calendar.get(Calendar.HOUR_OF_DAY) - minutes = calendar.get(Calendar.MINUTE) - seconds = calendar.get(Calendar.SECOND) - } - } - - - val event = Barcode.CalendarEvent() - event.summary = summary - event.description = description - event.location = location - event.organizer = organizer - event.start = createDateTime(startTimestamp, isStartAllDay) - event.end = createDateTime(endTimestamp, isEndAllDay) - return event -} - -fun EmailAddressParsedResult.toGms(): Barcode.Email { - val email = Barcode.Email() - email.address = tos?.getOrNull(0) - email.subject = subject - email.body = body - return email -} - -fun GeoParsedResult.toGms(): Barcode.GeoPoint { - val geo = Barcode.GeoPoint() - geo.lat = latitude - geo.lng = longitude - return geo -} - -fun TelParsedResult.toGms(): Barcode.Phone { - val phone = Barcode.Phone() - phone.number = number - return phone -} - -fun SMSParsedResult.toGms(): Barcode.Sms { - val sms = Barcode.Sms() - sms.message = body - sms.phoneNumber = numbers?.getOrNull(0) - return sms -} - -fun WifiParsedResult.toGms(): Barcode.WiFi { - val wifi = Barcode.WiFi() - wifi.ssid = ssid - wifi.password = password - wifi.encryptionType = when (networkEncryption) { - "OPEN" -> Barcode.WiFi.OPEN - "WEP" -> Barcode.WiFi.WEP - "WPA" -> Barcode.WiFi.WPA - "WPA2" -> Barcode.WiFi.WPA - else -> 0 - } - return wifi -} - -fun URIParsedResult.toGms(): Barcode.UrlBookmark { - val url = Barcode.UrlBookmark() - url.url = uri - url.title = title - return url -} - -fun ResultPoint.toPoint(): Point = Point(x.toInt(), y.toInt()) -fun Result.toGms(): Barcode { - val barcode = Barcode() - barcode.format = barcodeFormat.toGms() - barcode.rawBytes = rawBytes - barcode.rawValue = text - barcode.cornerPoints = resultPoints.map { it.toPoint() }.toTypedArray() - - val parsed = ResultParser.parseResult(this) - - barcode.displayValue = parsed.displayResult - barcode.valueFormat = parsed.type.toGms() - when (parsed) { - is EmailAddressParsedResult -> - barcode.email = parsed.toGms() - is TelParsedResult -> - barcode.phone = parsed.toGms() - is SMSParsedResult -> - barcode.sms = parsed.toGms() - is WifiParsedResult -> - barcode.wifi = parsed.toGms() - is URIParsedResult -> - barcode.url = parsed.toGms() - is GeoParsedResult -> - barcode.geoPoint = parsed.toGms() - is CalendarParsedResult -> - barcode.calendarEvent = parsed.toGms() - is AddressBookParsedResult -> - barcode.contactInfo = parsed.toGms() - } - - return barcode -} diff --git a/play-services-vision/core/src/main/kotlin/org/microg/gms/vision/barcode/MultiBarcodeReader.kt b/play-services-vision/core/src/main/kotlin/org/microg/gms/vision/barcode/MultiBarcodeReader.kt new file mode 100644 index 0000000000..4f0e948938 --- /dev/null +++ b/play-services-vision/core/src/main/kotlin/org/microg/gms/vision/barcode/MultiBarcodeReader.kt @@ -0,0 +1,114 @@ +/* + * SPDX-FileCopyrightText: 2023 microG Project Team + * SPDX-License-Identifier: Apache-2.0 + */ + +package org.microg.gms.vision.barcode + +import com.google.zxing.* +import com.google.zxing.multi.MultipleBarcodeReader +import kotlin.math.max +import kotlin.math.min + +class MultiBarcodeReader(val hints: Map) : MultipleBarcodeReader, Reader { + val delegate = MultiFormatReader().apply { setHints(hints) } + + fun multiDecode(image: BinaryBitmap): List { + return doDecodeMultiple(image) + } + + override fun decodeMultiple(image: BinaryBitmap): Array { + return multiDecode(image).toTypedArray() + } + + override fun decodeMultiple(image: BinaryBitmap, hints: MutableMap?): Array { + return multiDecode(image).toTypedArray() + } + + override fun decode(image: BinaryBitmap): Result { + return delegate.decodeWithState(image) + } + + override fun decode(image: BinaryBitmap, hints: MutableMap?): Result { + return delegate.decodeWithState(image) + } + + override fun reset() { + delegate.reset() + } + + // Derived from com.google.zxing.multi GenericMultipleBarcodeReader + // Copyright 2009 ZXing authors + // Licensed under the Apache License, Version 2.0 + private fun doDecodeMultiple( + image: BinaryBitmap, + results: MutableList = arrayListOf(), + xOffset: Int = 0, + yOffset: Int = 0, + currentDepth: Int = 0, + maxDepth: Int = 2 + ): List { + val result = kotlin.runCatching { delegate.decodeWithState(image) }.getOrNull() ?: return results + + if (results.none { it.text == result.text }) { + results.add(translateResultPoints(result, xOffset, yOffset)) + } + + val resultPoints = result.resultPoints + if (resultPoints != null && resultPoints.isNotEmpty() && currentDepth + 1 < maxDepth) { + val width = image.width + val height = image.height + var minX = width.toFloat() + var minY = height.toFloat() + var maxX = 0.0f + var maxY = 0.0f + + for (point in resultPoints) { + if (point != null) { + minX = min(point.x, minX) + minY = min(point.y, minY) + maxX = max(point.x, maxX) + maxY = max(point.y, maxY) + } + } + + if (minX > 100.0f) { + this.doDecodeMultiple(image.crop(0, 0, minX.toInt(), height), results, xOffset, yOffset, currentDepth + 1, maxDepth) + } + + if (minY > 100.0f) { + this.doDecodeMultiple(image.crop(0, 0, width, minY.toInt()), results, xOffset, yOffset, currentDepth + 1, maxDepth) + } + + if (maxX < (width - 100).toFloat()) { + this.doDecodeMultiple(image.crop(maxX.toInt(), 0, width - maxX.toInt(), height), results, xOffset + maxX.toInt(), yOffset, currentDepth + 1, maxDepth) + } + + if (maxY < (height - 100).toFloat()) { + this.doDecodeMultiple(image.crop(0, maxY.toInt(), width, height - maxY.toInt()), results, xOffset, yOffset + maxY.toInt(), currentDepth + 1, maxDepth) + } + } + return results + } + + private fun translateResultPoints(result: Result, xOffset: Int, yOffset: Int): Result { + val oldResultPoints = result.resultPoints + if (oldResultPoints == null) { + return result + } else { + val newResultPoints = arrayOfNulls(oldResultPoints.size) + + for (i in oldResultPoints.indices) { + val oldPoint = oldResultPoints[i] + if (oldPoint != null) { + newResultPoints[i] = ResultPoint(oldPoint.x + xOffset.toFloat(), oldPoint.y + yOffset.toFloat()) + } + } + + val newResult = Result(result.text, result.rawBytes, result.numBits, newResultPoints, result.barcodeFormat, result.timestamp) + newResult.putAllMetadata(result.resultMetadata) + return newResult + } + } + +} \ No newline at end of file diff --git a/settings.gradle b/settings.gradle index 1ef8d2227c..7ad47bc2ea 100644 --- a/settings.gradle +++ b/settings.gradle @@ -43,6 +43,7 @@ include ':play-services-iid' include ':play-services-location' include ':play-services-maps' include ':play-services-measurement-base' +sublude ':play-services-mlkit:barcode-scanning' include ':play-services-nearby' include ':play-services-oss-licenses' include ':play-services-pay'