Index: res/values-it/strings.xml
===================================================================
--- res/values-it/strings.xml
+++ res/values-it/strings.xml
@@ -5,6 +5,10 @@
Stai utilizzando l\'ultimo ritrovato in materia di rispetto della tua privacy.
Cerca
QR Code
+ Si
+ No
+ Installare Barcode Scanner?
+ Questa azione richiede un\'altra app per scansionare i codici QR. Vuoi installare Barcode Scanner?
Numero fermata
Nome fermata
Inserisci il numero della fermata
@@ -21,7 +25,7 @@
Linee: %1$s
Scegli la fermata…
Nessun passaggio
- Nessun QR code
+ Nessun QR code trovato, prova ad usare un\'altra app
Preferiti
Aiuto
Informazioni
Index: res/values/strings.xml
===================================================================
--- res/values/strings.xml
+++ res/values/strings.xml
@@ -8,6 +8,11 @@
Search
Scan QR Code
+ Yes
+ No
+ Install Barcode Scanner?
+ This application requires an app to scan the QR codes. Would you like to install Barcode Scanner now?
+
Bus stop number
Bus stop name
Insert bus stop number
@@ -27,7 +32,7 @@
Lines: %1$s
Line: %1$s
No timetable found
- No QR code
+ No QR code found, try using another app to scan
Unexpected internal error, cannot extract data from GTT/5T website
Help
About
Index: src/com/google/zxing/integration/android/IntentIntegrator.java
===================================================================
--- src/com/google/zxing/integration/android/IntentIntegrator.java
+++ src/com/google/zxing/integration/android/IntentIntegrator.java
@@ -347,6 +347,7 @@
PackageManager pm = activity.getPackageManager();
List availableApps = pm.queryIntentActivities(intent, PackageManager.MATCH_DEFAULT_ONLY);
if (availableApps != null) {
+ Log.d("IntentIntegrator","Available app to scan QR Code: "+availableApps);
for (String targetApp : targetApplications) {
if (contains(availableApps, targetApp)) {
return targetApp;
Index: src/it/reyboz/bustorino/fragments/MainScreenFragment.java
===================================================================
--- src/it/reyboz/bustorino/fragments/MainScreenFragment.java
+++ src/it/reyboz/bustorino/fragments/MainScreenFragment.java
@@ -3,9 +3,11 @@
import android.Manifest;
import android.content.Context;
+import android.content.Intent;
import android.content.pm.PackageManager;
import android.location.Criteria;
import android.location.Location;
+import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
@@ -38,7 +40,6 @@
import android.widget.Toast;
import com.google.android.material.floatingactionbutton.FloatingActionButton;
-import com.google.zxing.integration.android.IntentIntegrator;
import java.util.Map;
@@ -47,9 +48,13 @@
import it.reyboz.bustorino.middleware.AppLocationManager;
import it.reyboz.bustorino.middleware.AsyncArrivalsSearcher;
import it.reyboz.bustorino.middleware.AsyncStopsSearcher;
+import it.reyboz.bustorino.middleware.BarcodeScanContract;
+import it.reyboz.bustorino.middleware.BarcodeScanOptions;
+import it.reyboz.bustorino.middleware.BarcodeScanUtils;
import it.reyboz.bustorino.util.LocationCriteria;
import it.reyboz.bustorino.util.Permissions;
+import static it.reyboz.bustorino.backend.utils.getBusStopIDFromUri;
import static it.reyboz.bustorino.util.Permissions.LOCATION_PERMISSIONS;
import static it.reyboz.bustorino.util.Permissions.LOCATION_PERMISSION_GIVEN;
@@ -117,6 +122,34 @@
new AsyncArrivalsSearcher(fragmentHelper, arrivalsFetchers, getContext()).execute();
}
};
+ //
+ private final ActivityResultLauncher barcodeLauncher = registerForActivityResult(new BarcodeScanContract(),
+ result -> {
+ if(result!=null && result.getContents()!=null) {
+ //Toast.makeText(MyActivity.this, "Cancelled", Toast.LENGTH_LONG).show();
+ Uri uri;
+ try {
+ uri = Uri.parse(result.getContents()); // this apparently prevents NullPointerException. Somehow.
+ } catch (NullPointerException e) {
+ if (getContext()!=null)
+ Toast.makeText(getContext().getApplicationContext(),
+ R.string.no_qrcode, Toast.LENGTH_SHORT).show();
+ return;
+ }
+ String busStopID = getBusStopIDFromUri(uri);
+ busStopSearchByIDEditText.setText(busStopID);
+ requestArrivalsForStopID(busStopID);
+
+ } else {
+ //Toast.makeText(MyActivity.this, "Scanned: " + result.getContents(), Toast.LENGTH_LONG).show();
+ if (getContext()!=null)
+ Toast.makeText(getContext().getApplicationContext(),
+ R.string.no_qrcode, Toast.LENGTH_SHORT).show();
+
+
+
+ }
+ });
/// LOCATION STUFF ///
boolean pendingNearbyStopsRequest = false;
@@ -454,8 +487,14 @@
* @param v View QRButton clicked
*/
public void onQRButtonClick(View v) {
- IntentIntegrator integrator = new IntentIntegrator(getActivity());
- integrator.initiateScan();
+
+ BarcodeScanOptions scanOptions = new BarcodeScanOptions();
+ Intent intent = scanOptions.createScanIntent();
+ if(!BarcodeScanUtils.checkTargetPackageExists(getContext(), intent)){
+ BarcodeScanUtils.showDownloadDialog(null, this);
+ }else {
+ barcodeLauncher.launch(scanOptions);
+ }
}
public void onHideHint(View v) {
Index: src/it/reyboz/bustorino/middleware/BarcodeScanContract.java
===================================================================
--- /dev/null
+++ src/it/reyboz/bustorino/middleware/BarcodeScanContract.java
@@ -0,0 +1,33 @@
+/*
+ * Based on ZXing Android Embedded, Copyright 2021 ZXing Android Embedded authors.
+
+ */
+
+package it.reyboz.bustorino.middleware;
+
+import android.content.Context;
+import android.content.Intent;
+
+import androidx.activity.result.contract.ActivityResultContract;
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
+import com.google.zxing.integration.android.IntentIntegrator;
+import com.google.zxing.integration.android.IntentResult;
+
+public class BarcodeScanContract extends ActivityResultContract {
+
+ @NonNull
+ @Override
+ public Intent createIntent(@NonNull Context context, BarcodeScanOptions input) {
+ return input.createScanIntent();
+ }
+
+
+ @Override
+ public IntentResult parseResult(int resultCode, @Nullable Intent intent) {
+ return IntentIntegrator.parseActivityResult(IntentIntegrator.REQUEST_CODE, resultCode, intent);
+ }
+
+
+}
Index: src/it/reyboz/bustorino/middleware/BarcodeScanOptions.java
===================================================================
--- /dev/null
+++ src/it/reyboz/bustorino/middleware/BarcodeScanOptions.java
@@ -0,0 +1,172 @@
+
+/*
+ * Based on ZXing Android Embedded, Copyright 2021 ZXing Android Embedded authors.
+
+ */
+package it.reyboz.bustorino.middleware;
+
+import android.content.Intent;
+import android.os.Bundle;
+
+
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+public class BarcodeScanOptions {
+
+ public static final String BS_PACKAGE = "com.google.zxing.client.android";
+
+ // supported barcode formats
+
+ // Product Codes
+ public static final String UPC_A = "UPC_A";
+ public static final String UPC_E = "UPC_E";
+ public static final String EAN_8 = "EAN_8";
+ public static final String EAN_13 = "EAN_13";
+ public static final String RSS_14 = "RSS_14";
+
+ // Other 1D
+ public static final String CODE_39 = "CODE_39";
+ public static final String CODE_93 = "CODE_93";
+ public static final String CODE_128 = "CODE_128";
+ public static final String ITF = "ITF";
+
+ public static final String RSS_EXPANDED = "RSS_EXPANDED";
+
+ // 2D
+ public static final String QR_CODE = "QR_CODE";
+ public static final String DATA_MATRIX = "DATA_MATRIX";
+ public static final String PDF_417 = "PDF_417";
+
+
+ public static final Collection PRODUCT_CODE_TYPES = list(UPC_A, UPC_E, EAN_8, EAN_13, RSS_14);
+ public static final Collection ONE_D_CODE_TYPES =
+ list(UPC_A, UPC_E, EAN_8, EAN_13, RSS_14, CODE_39, CODE_93, CODE_128,
+ ITF, RSS_14, RSS_EXPANDED);
+
+ public static final Collection ALL_CODE_TYPES = null;
+
+ private final Map moreExtras = new HashMap<>(3);
+
+ private Collection desiredBarcodeFormats;
+
+
+ private int cameraId = 0;
+
+ public BarcodeScanOptions() {
+
+ }
+
+ public Map getMoreExtras() {
+ return moreExtras;
+ }
+
+ public final BarcodeScanOptions addExtra(String key, Object value) {
+ moreExtras.put(key, value);
+ return this;
+ }
+
+ public final BarcodeScanOptions setCameraID(int cameraID){
+ this.cameraId = cameraID;
+ return this;
+ }
+
+ /**
+ * Set the desired barcode formats to scan.
+ *
+ * @param desiredBarcodeFormats names of {@code BarcodeFormat}s to scan for
+ * @return this
+ */
+ public BarcodeScanOptions setDesiredBarcodeFormats(Collection desiredBarcodeFormats) {
+ this.desiredBarcodeFormats = desiredBarcodeFormats;
+ return this;
+ }
+
+ /**
+ * Set the desired barcode formats to scan.
+ *
+ * @param desiredBarcodeFormats names of {@code BarcodeFormat}s to scan for
+ * @return this
+ */
+ public BarcodeScanOptions setDesiredBarcodeFormats(String... desiredBarcodeFormats) {
+ this.desiredBarcodeFormats = Arrays.asList(desiredBarcodeFormats);
+ return this;
+ }
+
+
+ /**
+ * Create an scan intent with the specified options.
+ *
+ * @return the intent
+ */
+ public Intent createScanIntent() {
+ Intent intentScan = new Intent(BS_PACKAGE + ".SCAN");
+ intentScan.addCategory(Intent.CATEGORY_DEFAULT);
+
+ // check which types of codes to scan for
+ if (desiredBarcodeFormats != null) {
+ // set the desired barcode types
+ StringBuilder joinedByComma = new StringBuilder();
+ for (String format : desiredBarcodeFormats) {
+ if (joinedByComma.length() > 0) {
+ joinedByComma.append(',');
+ }
+ joinedByComma.append(format);
+ }
+ intentScan.putExtra("SCAN_FORMATS", joinedByComma.toString());
+ }
+
+ // check requested camera ID
+ if (cameraId >= 0) {
+ intentScan.putExtra("SCAN_CAMERA_ID", cameraId);
+ }
+
+ intentScan.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
+ intentScan.addFlags(Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET);
+ attachMoreExtras(intentScan);
+ return intentScan;
+ }
+
+ private static List list(String... values) {
+ return Collections.unmodifiableList(Arrays.asList(values));
+ }
+
+ private void attachMoreExtras(Intent intent) {
+ for (Map.Entry entry : moreExtras.entrySet()) {
+ String key = entry.getKey();
+ Object value = entry.getValue();
+ // Kind of hacky
+ if (value instanceof Integer) {
+ intent.putExtra(key, (Integer) value);
+ } else if (value instanceof Long) {
+ intent.putExtra(key, (Long) value);
+ } else if (value instanceof Boolean) {
+ intent.putExtra(key, (Boolean) value);
+ } else if (value instanceof Double) {
+ intent.putExtra(key, (Double) value);
+ } else if (value instanceof Float) {
+ intent.putExtra(key, (Float) value);
+ } else if (value instanceof Bundle) {
+ intent.putExtra(key, (Bundle) value);
+ } else if (value instanceof int[]) {
+ intent.putExtra(key, (int[]) value);
+ } else if (value instanceof long[]) {
+ intent.putExtra(key, (long[]) value);
+ } else if (value instanceof boolean[]) {
+ intent.putExtra(key, (boolean[]) value);
+ } else if (value instanceof double[]) {
+ intent.putExtra(key, (double[]) value);
+ } else if (value instanceof float[]) {
+ intent.putExtra(key, (float[]) value);
+ } else if (value instanceof String[]) {
+ intent.putExtra(key, (String[]) value);
+ } else {
+ intent.putExtra(key, value.toString());
+ }
+ }
+ }
+}
\ No newline at end of file
Index: src/it/reyboz/bustorino/middleware/BarcodeScanUtils.java
===================================================================
--- /dev/null
+++ src/it/reyboz/bustorino/middleware/BarcodeScanUtils.java
@@ -0,0 +1,62 @@
+package it.reyboz.bustorino.middleware;
+
+import android.app.Activity;
+import android.app.AlertDialog;
+import android.content.ActivityNotFoundException;
+import android.content.Context;
+import android.content.DialogInterface;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
+import android.net.Uri;
+import android.util.Log;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.fragment.app.Fragment;
+
+import java.util.List;
+
+import it.reyboz.bustorino.R;
+
+public class BarcodeScanUtils {
+
+
+ public static boolean checkTargetPackageExists(Context context,Intent intent) {
+ PackageManager pm = context.getPackageManager();
+ List availableApps = pm.queryIntentActivities(intent, PackageManager.MATCH_DEFAULT_ONLY);
+ if (availableApps != null) {
+ return !availableApps.isEmpty();
+ }
+ return false;
+ }
+
+ public static AlertDialog showDownloadDialog(@Nullable Activity activity,@Nullable final Fragment fragment) {
+ if (activity == null){
+ if (fragment==null) throw new IllegalArgumentException("Cannot put both activity and fragment null");
+ activity = fragment.getActivity();
+ }
+ AlertDialog.Builder downloadDialog = new AlertDialog.Builder(activity);
+ downloadDialog.setTitle(R.string.title_barcode_scanner_install);
+ downloadDialog.setMessage(R.string.message_install_barcode_scanner);
+ final Activity finalActivity = activity;
+ downloadDialog.setPositiveButton(R.string.yes, (dialogInterface, i) -> {
+ final String packageName = BarcodeScanOptions.BS_PACKAGE;
+ Uri uri = Uri.parse("market://details?id=" + packageName);
+ Intent intent = new Intent(Intent.ACTION_VIEW, uri);
+ try {
+ if (fragment == null) {
+ finalActivity.startActivity(intent);
+ } else {
+ fragment.startActivity(intent);
+ }
+ } catch (ActivityNotFoundException anfe) {
+ // Hmm, market is not installed
+ Log.w("BusTO-BarcodeScanUtils", "Google Play is not installed; cannot install " + packageName);
+ }
+ });
+ downloadDialog.setNegativeButton(R.string.no, null);
+ downloadDialog.setCancelable(true);
+ return downloadDialog.show();
+ }
+}
Index: src/it/reyboz/bustorino/middleware/GeneralActivity.java
===================================================================
--- src/it/reyboz/bustorino/middleware/GeneralActivity.java
+++ src/it/reyboz/bustorino/middleware/GeneralActivity.java
@@ -1,3 +1,20 @@
+/*
+ BusTO - Arrival times for Turin public transport.
+ Copyright (C) 2021 Fabio Mazza
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see .
+ */
package it.reyboz.bustorino.middleware;
import android.Manifest;