Implementato completamente lo scan del barcode tramite fotocamera.
This commit is contained in:
parent
e5a4113968
commit
cbce52fd83
@ -99,9 +99,9 @@ dependencies {
|
||||
implementation 'androidx.cardview:cardview:1.0.0'
|
||||
implementation 'androidx.recyclerview:recyclerview:1.1.0'
|
||||
implementation 'androidx.preference:preference:1.1.0'
|
||||
implementation 'com.squareup.okhttp3:okhttp:4.4.0'
|
||||
implementation 'com.squareup.retrofit2:retrofit:2.7.2'
|
||||
implementation 'com.squareup.retrofit2:converter-gson:2.7.2'
|
||||
implementation 'com.squareup.okhttp3:okhttp:4.4.1'
|
||||
implementation 'com.squareup.retrofit2:retrofit:2.8.0'
|
||||
implementation 'com.squareup.retrofit2:converter-gson:2.8.0'
|
||||
implementation 'com.annimon:stream:1.2.1'
|
||||
implementation 'androidx.lifecycle:lifecycle-runtime:2.2.0'
|
||||
implementation 'androidx.lifecycle:lifecycle-extensions:2.2.0'
|
||||
@ -109,7 +109,7 @@ dependencies {
|
||||
implementation 'org.apache.commons:commons-text:1.8'
|
||||
|
||||
//MVVM
|
||||
def dagger2_version = "2.26"
|
||||
def dagger2_version = '2.27'
|
||||
implementation 'androidx.lifecycle:lifecycle-extensions:2.2.0'
|
||||
implementation "androidx.lifecycle:lifecycle-common-java8:2.2.0"
|
||||
api "com.google.dagger:dagger:$dagger2_version"
|
||||
@ -145,8 +145,8 @@ dependencies {
|
||||
implementation project(':barcode_base_android_library')
|
||||
implementation project(':honeywellscannerlibrary')
|
||||
implementation project(':keyobardemulatorscannerlibrary')
|
||||
implementation 'com.kroegerama:barcode-kaiteki:1.1.1'
|
||||
androidTestImplementation 'org.junit.jupiter:junit-jupiter-api:5.6.0'
|
||||
implementation project(':barcode_kaiteki')
|
||||
androidTestImplementation 'org.junit.jupiter:junit-jupiter-api:5.6.1'
|
||||
androidTestImplementation 'org.testng:testng:7.1.0'
|
||||
}
|
||||
repositories {
|
||||
|
||||
@ -4,13 +4,26 @@ import android.app.Dialog;
|
||||
import android.content.Context;
|
||||
import android.widget.LinearLayout;
|
||||
|
||||
import androidx.fragment.app.DialogFragment;
|
||||
|
||||
public class UtilityDialog {
|
||||
|
||||
public static void setTo90PercentSize(Context context, Dialog dialog) {
|
||||
public static void setTo90PercentWidth(Context context, Dialog dialog) {
|
||||
int width = (int)(context.getResources().getDisplayMetrics().widthPixels*0.90);
|
||||
//int height = (int)(context.getResources().getDisplayMetrics().heightPixels*0.90);
|
||||
|
||||
dialog.getWindow().setLayout(width, LinearLayout.LayoutParams.WRAP_CONTENT);
|
||||
}
|
||||
|
||||
public static void setTo90PercentWidth(Context context, DialogFragment dialog) {
|
||||
int width = (int)(context.getResources().getDisplayMetrics().widthPixels*0.90);
|
||||
|
||||
dialog.getDialog().getWindow().setLayout(width, LinearLayout.LayoutParams.WRAP_CONTENT);
|
||||
}
|
||||
|
||||
// public static void setTo90PercentHeight(Context context, Dialog dialog) {
|
||||
// int height = (int)(context.getResources().getDisplayMetrics().heightPixels*0.90);
|
||||
//
|
||||
// dialog.getWindow().setLayout(width, LinearLayout.LayoutParams.WRAP_CONTENT);
|
||||
// }
|
||||
|
||||
}
|
||||
|
||||
@ -6,8 +6,6 @@ import android.content.Context;
|
||||
import android.graphics.Color;
|
||||
import android.graphics.drawable.ColorDrawable;
|
||||
import android.text.Editable;
|
||||
import android.text.Html;
|
||||
import android.text.SpannableString;
|
||||
import android.text.TextWatcher;
|
||||
import android.view.LayoutInflater;
|
||||
|
||||
@ -23,7 +21,6 @@ import it.integry.integrywmsnative.core.utility.UtilityFocus;
|
||||
import it.integry.integrywmsnative.core.utility.UtilityProgress;
|
||||
import it.integry.integrywmsnative.databinding.DialogPvEditArticoloBinding;
|
||||
import it.integry.integrywmsnative.gest.pv_ordine_acquisto_edit.helper.PVEditOrderHelper;
|
||||
import it.integry.integrywmsnative.view.dialogs.base.DialogSimpleMessageHelper;
|
||||
|
||||
public class EditArticoloDialog {
|
||||
|
||||
@ -52,7 +49,7 @@ public class EditArticoloDialog {
|
||||
|
||||
mDialog = new BaseDialog(context);
|
||||
mDialog.setContentView(mBinding.getRoot());
|
||||
UtilityDialog.setTo90PercentSize(mContext, mDialog);
|
||||
UtilityDialog.setTo90PercentWidth(mContext, mDialog);
|
||||
mDialog.getWindow().setBackgroundDrawable(new ColorDrawable(Color.TRANSPARENT));
|
||||
|
||||
int qtaCnf = (int)Math.ceil(mArticolo.getQtaOrd() / mArticolo.getQtaCnf());
|
||||
|
||||
@ -50,6 +50,7 @@ import it.integry.integrywmsnative.gest.spedizione_new.core.SpedizioneListModel;
|
||||
import it.integry.integrywmsnative.gest.spedizione_new.model.PickingObjectDTO;
|
||||
import it.integry.integrywmsnative.gest.vendita.dto.OrdineVenditaInevasoDTO;
|
||||
import it.integry.integrywmsnative.view.dialogs.base.DialogSimpleInputHelper;
|
||||
import it.integry.integrywmsnative.view.dialogs.camera_barcode_reader.DialogCameraBarcodeReader;
|
||||
import it.integry.integrywmsnative.view.dialogs.input_quantity_v2.DialogInputQuantityV2;
|
||||
import it.integry.integrywmsnative.view.dialogs.input_quantity_v2.DialogInputQuantityV2DTO;
|
||||
|
||||
@ -281,24 +282,10 @@ public class SpedizioneActivity extends AppCompatActivity implements SpedizioneV
|
||||
}
|
||||
|
||||
public void startCameraBarcode() {
|
||||
List<BarcodeFormat> availableBarcodeFormats = new ArrayList<>();
|
||||
availableBarcodeFormats.add(BarcodeFormat.QR_CODE);
|
||||
availableBarcodeFormats.add(BarcodeFormat.CODE_128);
|
||||
availableBarcodeFormats.add(BarcodeFormat.UPC_E);
|
||||
availableBarcodeFormats.add(BarcodeFormat.CODABAR);
|
||||
availableBarcodeFormats.add(BarcodeFormat.EAN_8);
|
||||
availableBarcodeFormats.add(BarcodeFormat.EAN_13);
|
||||
availableBarcodeFormats.add(BarcodeFormat.ITF);
|
||||
availableBarcodeFormats.add(BarcodeFormat.UPC_A);
|
||||
|
||||
BarcodeBottomSheet bbs = BarcodeBottomSheet.Companion.show(
|
||||
getSupportFragmentManager(),
|
||||
availableBarcodeFormats,
|
||||
false,
|
||||
"tag"
|
||||
);
|
||||
|
||||
|
||||
DialogCameraBarcodeReader.newInstance(this, data -> {
|
||||
this.onScanSuccessful.run(data);
|
||||
})
|
||||
.show(getSupportFragmentManager(), "camera_barcode");
|
||||
}
|
||||
|
||||
public void removeListFilter() {
|
||||
|
||||
@ -31,7 +31,7 @@ public class DialogAskAction {
|
||||
|
||||
mDialog = new BaseDialog(context);
|
||||
mDialog.setContentView(mBinding.getRoot());
|
||||
UtilityDialog.setTo90PercentSize(mContext, mDialog);
|
||||
UtilityDialog.setTo90PercentWidth(mContext, mDialog);
|
||||
mDialog.getWindow().setBackgroundDrawable(new ColorDrawable(Color.TRANSPARENT));
|
||||
|
||||
mBinding.bottomSheetActionsTitle.setText(title);
|
||||
|
||||
@ -2,28 +2,20 @@ package it.integry.integrywmsnative.view.dialogs.ask_cliente;
|
||||
|
||||
import android.app.Dialog;
|
||||
import android.content.Context;
|
||||
import android.content.DialogInterface;
|
||||
import android.graphics.Color;
|
||||
import android.graphics.drawable.ColorDrawable;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.WindowManager;
|
||||
import android.widget.ArrayAdapter;
|
||||
import android.widget.AutoCompleteTextView;
|
||||
import android.widget.LinearLayout;
|
||||
|
||||
import androidx.databinding.DataBindingUtil;
|
||||
|
||||
import java.util.AbstractMap;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import it.integry.integrywmsnative.R;
|
||||
import it.integry.integrywmsnative.core.expansion.RunnableArgs;
|
||||
import it.integry.integrywmsnative.core.expansion.RunnableArgss;
|
||||
import it.integry.integrywmsnative.core.model.MtbColt;
|
||||
import it.integry.integrywmsnative.core.model.MtbDepoPosizione;
|
||||
import it.integry.integrywmsnative.core.model.VtbDest;
|
||||
import it.integry.integrywmsnative.core.utility.UtilityDialog;
|
||||
import it.integry.integrywmsnative.databinding.DialogAskClienteBinding;
|
||||
@ -68,7 +60,7 @@ public class DialogAskCliente {
|
||||
mDialog.setContentView(mBinding.getRoot());
|
||||
mDialog.setCanceledOnTouchOutside(false);
|
||||
// mDialog.setCancelable(false);
|
||||
UtilityDialog.setTo90PercentSize(mContext, mDialog);
|
||||
UtilityDialog.setTo90PercentWidth(mContext, mDialog);
|
||||
mDialog.getWindow().setBackgroundDrawable(new ColorDrawable(Color.TRANSPARENT));
|
||||
mDialog.getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE);
|
||||
|
||||
|
||||
@ -1,7 +1,6 @@
|
||||
package it.integry.integrywmsnative.view.dialogs.ask_position_of_lu;
|
||||
|
||||
import android.app.Dialog;
|
||||
import android.app.ProgressDialog;
|
||||
import android.content.Context;
|
||||
|
||||
import androidx.appcompat.widget.AppCompatTextView;
|
||||
@ -85,7 +84,7 @@ public class DialogAskPositionOfLU {
|
||||
// mDialog.setCancelable(false);
|
||||
mDialog.getWindow().setBackgroundDrawable(new ColorDrawable(Color.TRANSPARENT));
|
||||
|
||||
UtilityDialog.setTo90PercentSize(mContext, mDialog);
|
||||
UtilityDialog.setTo90PercentWidth(mContext, mDialog);
|
||||
|
||||
mDialog.setOnShowListener(dialog -> {
|
||||
((AppCompatTextView) adapter.getPage(0).findViewById(R.id.description_text)).setText(mCheckForLineaProd ? R.string.ask_production_line_of_lu_message : R.string.ask_position_of_lu_message);
|
||||
|
||||
@ -1,31 +1,17 @@
|
||||
package it.integry.integrywmsnative.view.dialogs.base;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.app.Dialog;
|
||||
import android.content.Context;
|
||||
import android.content.res.ColorStateList;
|
||||
import android.graphics.Color;
|
||||
import android.graphics.Rect;
|
||||
import android.graphics.drawable.ColorDrawable;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.text.SpannableString;
|
||||
import android.text.Spanned;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.Window;
|
||||
import android.widget.LinearLayout;
|
||||
import android.widget.RelativeLayout;
|
||||
import android.widget.TextView;
|
||||
|
||||
import androidx.annotation.StringRes;
|
||||
import androidx.core.content.ContextCompat;
|
||||
import androidx.databinding.DataBindingUtil;
|
||||
|
||||
import java.util.HashMap;
|
||||
|
||||
import it.integry.integrywmsnative.R;
|
||||
import it.integry.integrywmsnative.core.expansion.RunnableArgs;
|
||||
import it.integry.integrywmsnative.core.utility.UtilityDialog;
|
||||
import it.integry.integrywmsnative.databinding.DialogBaseBinding;
|
||||
import it.integry.integrywmsnative.databinding.DialogInputGeneralBinding;
|
||||
|
||||
/**
|
||||
@ -60,7 +46,7 @@ public class DialogSimpleInputHelper {
|
||||
dialog.setCanceledOnTouchOutside(false);
|
||||
dialog.setContentView(mBinding.getRoot());
|
||||
dialog.getWindow().setBackgroundDrawable(new ColorDrawable(Color.TRANSPARENT));
|
||||
UtilityDialog.setTo90PercentSize(mContext,dialog);
|
||||
UtilityDialog.setTo90PercentWidth(mContext,dialog);
|
||||
|
||||
return dialog;
|
||||
}
|
||||
|
||||
@ -2,16 +2,11 @@ package it.integry.integrywmsnative.view.dialogs.basket_lu;
|
||||
|
||||
import android.app.Dialog;
|
||||
import android.content.Context;
|
||||
import android.content.DialogInterface;
|
||||
import android.graphics.Color;
|
||||
import android.graphics.drawable.ColorDrawable;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
|
||||
import androidx.core.content.ContextCompat;
|
||||
import androidx.databinding.DataBindingUtil;
|
||||
import androidx.recyclerview.widget.DividerItemDecoration;
|
||||
import androidx.recyclerview.widget.LinearLayoutManager;
|
||||
|
||||
import java.util.AbstractMap;
|
||||
import java.util.ArrayList;
|
||||
@ -21,12 +16,8 @@ import java.util.Map;
|
||||
import it.integry.integrywmsnative.R;
|
||||
import it.integry.integrywmsnative.core.expansion.RunnableArgs;
|
||||
import it.integry.integrywmsnative.core.model.MtbColt;
|
||||
import it.integry.integrywmsnative.core.rest.consumers.ColliMagazzinoRESTConsumer;
|
||||
import it.integry.integrywmsnative.core.utility.UtilityDialog;
|
||||
import it.integry.integrywmsnative.databinding.DialogBasketLuBinding;
|
||||
import it.integry.integrywmsnative.view.dialogs.ask_cliente.viewmodel.DialogAskCliente_Page1ViewModel;
|
||||
import it.integry.integrywmsnative.view.dialogs.ask_cliente.viewmodel.DialogAskCliente_Page2ViewModel;
|
||||
import it.integry.integrywmsnative.view.dialogs.basket_lu.pages.page1.DialogBasketLU_Page1_ListAdapter;
|
||||
import it.integry.integrywmsnative.view.dialogs.basket_lu.pages.page1.DialogBasketLU_Page1_ViewModel;
|
||||
import it.integry.integrywmsnative.view.dialogs.basket_lu.pages.page2.DialogBasketLU_Page2_ViewModel;
|
||||
|
||||
@ -57,7 +48,7 @@ public class DialogBasketLU {
|
||||
mDialog.setCanceledOnTouchOutside(false);
|
||||
mDialog.getWindow().setBackgroundDrawable(new ColorDrawable(Color.TRANSPARENT));
|
||||
|
||||
UtilityDialog.setTo90PercentSize(context, mDialog);
|
||||
UtilityDialog.setTo90PercentWidth(context, mDialog);
|
||||
|
||||
this.initViewPager();
|
||||
}
|
||||
|
||||
@ -0,0 +1,129 @@
|
||||
package it.integry.integrywmsnative.view.dialogs.camera_barcode_reader;
|
||||
|
||||
import android.app.Dialog;
|
||||
import android.content.Context;
|
||||
import android.graphics.Color;
|
||||
import android.graphics.drawable.ColorDrawable;
|
||||
import android.os.Bundle;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.databinding.DataBindingUtil;
|
||||
import androidx.fragment.app.DialogFragment;
|
||||
|
||||
import com.google.zxing.BarcodeFormat;
|
||||
import com.google.zxing.Result;
|
||||
import com.kroegerama.kaiteki.bcode.BarcodeResultListener;
|
||||
import com.orhanobut.logger.Logger;
|
||||
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import it.integry.barcode_base_android_library.model.BarcodeScanDTO;
|
||||
import it.integry.barcode_base_android_library.model.BarcodeType;
|
||||
import it.integry.integrywmsnative.R;
|
||||
import it.integry.integrywmsnative.core.expansion.RunnableArgs;
|
||||
import it.integry.integrywmsnative.core.utility.UtilityDialog;
|
||||
import it.integry.integrywmsnative.databinding.DialogCameraBarcodeReaderBinding;
|
||||
|
||||
public class DialogCameraBarcodeReader extends DialogFragment implements BarcodeResultListener {
|
||||
|
||||
private Context mContext;
|
||||
|
||||
private DialogCameraBarcodeReaderBinding mBindings;
|
||||
private final RunnableArgs<BarcodeScanDTO> onComplete;
|
||||
|
||||
public static DialogCameraBarcodeReader newInstance(@NonNull Context context, @NonNull final RunnableArgs<BarcodeScanDTO> onComplete) {
|
||||
return new DialogCameraBarcodeReader(context, onComplete);
|
||||
}
|
||||
|
||||
private DialogCameraBarcodeReader(@NonNull Context context, @NonNull final RunnableArgs<BarcodeScanDTO> onComplete){
|
||||
this.mContext = context;
|
||||
this.onComplete = onComplete;
|
||||
}
|
||||
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
|
||||
mBindings = DataBindingUtil.inflate(inflater, R.layout.dialog_camera_barcode_reader, container, false);
|
||||
|
||||
UtilityDialog.setTo90PercentWidth(mContext, this);
|
||||
|
||||
getDialog().setCanceledOnTouchOutside(false);
|
||||
getDialog().setCancelable(false);
|
||||
getDialog().getWindow().setBackgroundDrawable(new ColorDrawable(Color.TRANSPARENT));
|
||||
|
||||
this.initBarcodeView();
|
||||
|
||||
mBindings.buttonConfirm.setOnClickListener(v -> {
|
||||
dismiss();
|
||||
});
|
||||
|
||||
return mBindings.getRoot();
|
||||
}
|
||||
|
||||
|
||||
private void initBarcodeView() {
|
||||
List<BarcodeFormat> availableBarcodeFormats = new ArrayList<>();
|
||||
availableBarcodeFormats.add(BarcodeFormat.QR_CODE);
|
||||
availableBarcodeFormats.add(BarcodeFormat.CODE_128);
|
||||
availableBarcodeFormats.add(BarcodeFormat.UPC_E);
|
||||
availableBarcodeFormats.add(BarcodeFormat.CODABAR);
|
||||
availableBarcodeFormats.add(BarcodeFormat.EAN_8);
|
||||
availableBarcodeFormats.add(BarcodeFormat.EAN_13);
|
||||
availableBarcodeFormats.add(BarcodeFormat.ITF);
|
||||
availableBarcodeFormats.add(BarcodeFormat.UPC_A);
|
||||
|
||||
mBindings.bcode.setFormats(availableBarcodeFormats);
|
||||
mBindings.bcode.setBarcodeInverted(false);
|
||||
mBindings.bcode.setBarcodeResultListener(this);
|
||||
mBindings.bcode.bindToLifecycle(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onBarcodeResult(@NotNull Result result) {
|
||||
|
||||
BarcodeScanDTO barcodeScanDTO = new BarcodeScanDTO()
|
||||
.setByteValue(result.getRawBytes())
|
||||
.setStringValue(result.getText());
|
||||
|
||||
BarcodeType type = null;
|
||||
|
||||
switch (result.getBarcodeFormat()) {
|
||||
case CODE_128:
|
||||
type = BarcodeType.CODE128;
|
||||
case CODE_39:
|
||||
type = BarcodeType.CODE39;
|
||||
|
||||
case EAN_13:
|
||||
type = BarcodeType.EAN13;
|
||||
case EAN_8:
|
||||
type = BarcodeType.EAN8;
|
||||
|
||||
case UPC_A:
|
||||
type = BarcodeType.UPCA;
|
||||
case UPC_E:
|
||||
type = BarcodeType.UPCE;
|
||||
default:
|
||||
Logger.e("Barcode not recognized", "Barcode type " + result.getBarcodeFormat().toString() + " was not mapped in DialogCameraBarcodeReader.java");
|
||||
type = null;
|
||||
}
|
||||
|
||||
barcodeScanDTO.setType(type);
|
||||
|
||||
this.dismiss();
|
||||
this.onComplete.run(barcodeScanDTO);
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBarcodeScanCancelled() {
|
||||
|
||||
}
|
||||
}
|
||||
@ -4,7 +4,6 @@ import android.app.Dialog;
|
||||
import android.content.Context;
|
||||
import androidx.databinding.DataBindingUtil;
|
||||
import androidx.core.content.ContextCompat;
|
||||
import androidx.appcompat.app.AlertDialog;
|
||||
import androidx.recyclerview.widget.DividerItemDecoration;
|
||||
import androidx.recyclerview.widget.LinearLayoutManager;
|
||||
|
||||
@ -50,7 +49,7 @@ public class DialogChooseArtFromListaArts {
|
||||
mDialog.setCanceledOnTouchOutside(false);
|
||||
mDialog.getWindow().setBackgroundDrawable(new ColorDrawable(Color.TRANSPARENT));
|
||||
|
||||
UtilityDialog.setTo90PercentSize(context, mDialog);
|
||||
UtilityDialog.setTo90PercentWidth(context, mDialog);
|
||||
|
||||
initRecyclerView(bindings, listaArts);
|
||||
}
|
||||
|
||||
@ -1,11 +1,9 @@
|
||||
package it.integry.integrywmsnative.view.dialogs.choose_arts_from_lista_arts;
|
||||
|
||||
import android.app.Dialog;
|
||||
import android.app.ProgressDialog;
|
||||
import android.content.Context;
|
||||
|
||||
import androidx.databinding.DataBindingUtil;
|
||||
import androidx.appcompat.app.AlertDialog;
|
||||
import androidx.recyclerview.widget.LinearLayoutManager;
|
||||
|
||||
import android.graphics.Color;
|
||||
@ -102,7 +100,7 @@ public class DialogChooseArtsFromListaArts {
|
||||
if(this.mOnAbort != null) this.mOnAbort.run();
|
||||
});
|
||||
|
||||
UtilityDialog.setTo90PercentSize(context, mDialog);
|
||||
UtilityDialog.setTo90PercentWidth(context, mDialog);
|
||||
|
||||
|
||||
initRecyclerView(binding);
|
||||
|
||||
@ -32,6 +32,8 @@ public class DialogInputQuantityV2ViewModel {
|
||||
|
||||
|
||||
public void init() {
|
||||
//Quando ho un barcode peso devo calcolare numCnf dal peso medio
|
||||
|
||||
this.currentNumCnf.set(totalNumCnfToBeTaken != null ? totalNumCnfToBeTaken : BigDecimal.ONE);
|
||||
UtilityObservable.addPropertyChanged(this.currentNumCnf, this::onCurrentNumCnfChanged);
|
||||
|
||||
|
||||
51
app/src/main/res/layout/dialog_camera_barcode_reader.xml
Normal file
51
app/src/main/res/layout/dialog_camera_barcode_reader.xml
Normal file
@ -0,0 +1,51 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
|
||||
<layout>
|
||||
|
||||
<androidx.cardview.widget.CardView xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
app:cardCornerRadius="12dp"
|
||||
app:cardElevation="0dp">
|
||||
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
|
||||
<com.kroegerama.kaiteki.bcode.views.BarcodeView
|
||||
android:id="@+id/bcode"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:resultPointColor="#09E85E"
|
||||
app:resultPointSize="8dp"
|
||||
app:showResultPoints="true" />
|
||||
|
||||
|
||||
<RelativeLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:gravity="center|bottom"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
android:paddingBottom="8dp">
|
||||
|
||||
<com.google.android.material.button.MaterialButton
|
||||
android:id="@+id/button_confirm"
|
||||
style="@style/Button.PrimaryFull"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/action_close"
|
||||
android:layout_alignParentBottom="true"/>
|
||||
|
||||
</RelativeLayout>
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
</androidx.cardview.widget.CardView>
|
||||
</layout>
|
||||
@ -296,6 +296,7 @@
|
||||
<string name="cod_alis_name">Griglia: </string>
|
||||
<string name="seleziona_crea_ordine_a_pv">Seleziona / Crea ordine</string>
|
||||
<string name="add">Aggiungi</string>
|
||||
<string name="action_close">Chiudi</string>
|
||||
<string name="close_and_export">Chiudi ed esporta ordine</string>
|
||||
<string name="close_and_exit">Chiudi ordine</string>
|
||||
<string name="scan_art_barcode_to_add">Scansiona il codice di un articolo per aggiungerlo all\' ordine</string>
|
||||
|
||||
@ -302,6 +302,7 @@
|
||||
<string name="cod_alis_name">Grid: </string>
|
||||
<string name="seleziona_crea_ordine_a_pv">Select/Create order</string>
|
||||
<string name="add">Add</string>
|
||||
<string name="action_close">Close</string>
|
||||
<string name="close_and_export">Close and export</string>
|
||||
<string name="close_and_exit">Close order</string>
|
||||
<string name="scan_art_barcode_to_add">Scan an item barcode to add it to the order</string>
|
||||
|
||||
1
barcode_kaiteki/.gitignore
vendored
Normal file
1
barcode_kaiteki/.gitignore
vendored
Normal file
@ -0,0 +1 @@
|
||||
/build
|
||||
40
barcode_kaiteki/build.gradle
Normal file
40
barcode_kaiteki/build.gradle
Normal file
@ -0,0 +1,40 @@
|
||||
apply plugin: 'com.android.library'
|
||||
apply plugin: 'kotlin-android'
|
||||
apply plugin: 'kotlin-android-extensions'
|
||||
|
||||
android {
|
||||
compileSdkVersion 29
|
||||
|
||||
defaultConfig {
|
||||
minSdkVersion 21
|
||||
targetSdkVersion 29
|
||||
versionCode 1
|
||||
versionName "1.1.1"
|
||||
}
|
||||
compileOptions {
|
||||
kotlinOptions.freeCompilerArgs += ['-module-name', "barcode.kaiteki"]
|
||||
targetCompatibility = JavaVersion.VERSION_1_8
|
||||
sourceCompatibility = JavaVersion.VERSION_1_8
|
||||
}
|
||||
buildTypes {
|
||||
release {
|
||||
minifyEnabled false
|
||||
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation fileTree(dir: 'libs', include: ['*.jar'])
|
||||
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version"
|
||||
|
||||
implementation 'androidx.core:core-ktx:1.1.0'
|
||||
implementation 'androidx.appcompat:appcompat:1.1.0'
|
||||
implementation 'androidx.constraintlayout:constraintlayout:2.0.0-beta2'
|
||||
implementation 'com.google.android.material:material:1.1.0-alpha10'
|
||||
|
||||
api 'androidx.camera:camera-core:1.0.0-alpha03'
|
||||
api 'androidx.camera:camera-camera2:1.0.0-alpha03'
|
||||
|
||||
api 'com.google.zxing:core:3.4.0'
|
||||
}
|
||||
21
barcode_kaiteki/proguard-rules.pro
vendored
Normal file
21
barcode_kaiteki/proguard-rules.pro
vendored
Normal file
@ -0,0 +1,21 @@
|
||||
# Add project specific ProGuard rules here.
|
||||
# You can control the set of applied configuration files using the
|
||||
# proguardFiles setting in build.gradle.
|
||||
#
|
||||
# For more details, see
|
||||
# http://developer.android.com/guide/developing/tools/proguard.html
|
||||
|
||||
# If your project uses WebView with JS, uncomment the following
|
||||
# and specify the fully qualified class name to the JavaScript interface
|
||||
# class:
|
||||
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
|
||||
# public *;
|
||||
#}
|
||||
|
||||
# Uncomment this to preserve the line number information for
|
||||
# debugging stack traces.
|
||||
#-keepattributes SourceFile,LineNumberTable
|
||||
|
||||
# If you keep the line number information, uncomment this to
|
||||
# hide the original source file name.
|
||||
#-renamesourcefileattribute SourceFile
|
||||
6
barcode_kaiteki/src/main/AndroidManifest.xml
Normal file
6
barcode_kaiteki/src/main/AndroidManifest.xml
Normal file
@ -0,0 +1,6 @@
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
package="com.kroegerama.kaiteki.bcode">
|
||||
|
||||
<uses-permission android:name="android.permission.CAMERA" />
|
||||
|
||||
</manifest>
|
||||
@ -0,0 +1,70 @@
|
||||
package com.kroegerama.kaiteki.bcode
|
||||
|
||||
import android.graphics.ImageFormat
|
||||
import android.util.Log
|
||||
import androidx.camera.core.ImageAnalysis
|
||||
import androidx.camera.core.ImageProxy
|
||||
import com.google.zxing.BinaryBitmap
|
||||
import com.google.zxing.MultiFormatReader
|
||||
import com.google.zxing.PlanarYUVLuminanceSource
|
||||
import com.google.zxing.Result
|
||||
import com.google.zxing.common.HybridBinarizer
|
||||
|
||||
internal interface ResultListener {
|
||||
fun onResult(result: Result, imageWidth: Int, imageHeight: Int, imageRotation: Int)
|
||||
fun onNoResult()
|
||||
}
|
||||
|
||||
internal class BarcodeAnalyzer(
|
||||
private val listener: ResultListener,
|
||||
private val reader: MultiFormatReader
|
||||
) : ImageAnalysis.Analyzer {
|
||||
|
||||
var enabled = true
|
||||
var inverted = false
|
||||
|
||||
override fun analyze(image: ImageProxy, rotationDegrees: Int) {
|
||||
if (!enabled) return
|
||||
|
||||
//YUV_420 is normally the input type here, but other YUV types are also supported in theory
|
||||
if (ImageFormat.YUV_420_888 != image.format && ImageFormat.YUV_422_888 != image.format && ImageFormat.YUV_444_888 != image.format) {
|
||||
Log.e(TAG, "Unexpected format: ${image.format}")
|
||||
listener.onNoResult()
|
||||
return
|
||||
}
|
||||
val byteBuffer = image.image?.planes?.firstOrNull()?.buffer
|
||||
if (byteBuffer == null) {
|
||||
listener.onNoResult()
|
||||
return
|
||||
}
|
||||
|
||||
val data = ByteArray(byteBuffer.remaining()).also { byteBuffer.get(it) }
|
||||
|
||||
var width = image.width
|
||||
var height = image.height
|
||||
|
||||
val rotatedData = ByteArray(data.size)
|
||||
for (y in 0 until height) {
|
||||
for (x in 0 until width) rotatedData[x * height + height - y - 1] = data[x + y * width]
|
||||
}
|
||||
val tmp = width
|
||||
width = height
|
||||
height = tmp
|
||||
|
||||
val source = PlanarYUVLuminanceSource(rotatedData, width, height, 0, 0, width, height, false).let {
|
||||
if (inverted) it.invert() else it
|
||||
}
|
||||
val bitmap = BinaryBitmap(HybridBinarizer(source))
|
||||
|
||||
try {
|
||||
val result = reader.decodeWithState(bitmap)
|
||||
listener.onResult(result, width, height, rotationDegrees)
|
||||
} catch (e: Exception) {
|
||||
listener.onNoResult()
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
private const val TAG = "BarcodeAnalyzer"
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,16 @@
|
||||
package com.kroegerama.kaiteki.bcode
|
||||
|
||||
import com.google.zxing.Result
|
||||
|
||||
interface BarcodeResultListener {
|
||||
|
||||
/**
|
||||
* @param result zxing result
|
||||
*
|
||||
* @return return true to dismiss the dialog/fragment
|
||||
*/
|
||||
fun onBarcodeResult(result: Result): Boolean
|
||||
|
||||
fun onBarcodeScanCancelled()
|
||||
|
||||
}
|
||||
@ -0,0 +1,32 @@
|
||||
package com.kroegerama.kaiteki.bcode
|
||||
|
||||
import android.graphics.*
|
||||
import android.media.Image
|
||||
import com.google.zxing.PlanarYUVLuminanceSource
|
||||
import java.io.ByteArrayOutputStream
|
||||
|
||||
internal fun Image.toBitmap(): Bitmap {
|
||||
val yBuffer = planes[0].buffer // Y
|
||||
val uBuffer = planes[1].buffer // U
|
||||
val vBuffer = planes[2].buffer // V
|
||||
|
||||
val ySize = yBuffer.remaining()
|
||||
val uSize = uBuffer.remaining()
|
||||
val vSize = vBuffer.remaining()
|
||||
|
||||
val nv21 = ByteArray(ySize + uSize + vSize)
|
||||
|
||||
//U and V are swapped
|
||||
yBuffer.get(nv21, 0, ySize)
|
||||
vBuffer.get(nv21, ySize, vSize)
|
||||
uBuffer.get(nv21, ySize + vSize, uSize)
|
||||
|
||||
val yuvImage = YuvImage(nv21, ImageFormat.NV21, this.width, this.height, null)
|
||||
val out = ByteArrayOutputStream()
|
||||
yuvImage.compressToJpeg(Rect(0, 0, yuvImage.width, yuvImage.height), 50, out)
|
||||
val imageBytes = out.toByteArray()
|
||||
return BitmapFactory.decodeByteArray(imageBytes, 0, imageBytes.size)
|
||||
}
|
||||
|
||||
internal fun PlanarYUVLuminanceSource.toBitmap() =
|
||||
Bitmap.createBitmap(renderThumbnail(), thumbnailWidth, thumbnailHeight, Bitmap.Config.ARGB_8888)
|
||||
@ -0,0 +1,53 @@
|
||||
package com.kroegerama.kaiteki.bcode
|
||||
|
||||
import android.Manifest
|
||||
import android.content.Context
|
||||
import android.content.pm.PackageManager
|
||||
import android.content.res.TypedArray
|
||||
import android.os.Build
|
||||
import android.util.AttributeSet
|
||||
import androidx.core.content.ContextCompat
|
||||
import androidx.fragment.app.Fragment
|
||||
|
||||
internal fun AttributeSet?.handleArguments(
|
||||
context: Context, attrs: IntArray, defStyleAttr: Int, defStyleRes: Int,
|
||||
block: TypedArray.() -> Unit
|
||||
) = this?.let {
|
||||
val arr = context.obtainStyledAttributes(it, attrs, defStyleAttr, defStyleRes)
|
||||
block(arr)
|
||||
arr.recycle()
|
||||
}
|
||||
|
||||
internal typealias Style = R.styleable
|
||||
|
||||
internal val Context.hasCameraPermission
|
||||
get() = isPermissionGranted(Manifest.permission.CAMERA)
|
||||
|
||||
internal fun Fragment.requestCameraPermission(requestCode: Int) =
|
||||
requestPermission(Manifest.permission.CAMERA, requestCode)
|
||||
|
||||
internal val IntArray.isPermissionGranted
|
||||
get() = size > 0 && get(0) == PackageManager.PERMISSION_GRANTED
|
||||
|
||||
private fun Context.isPermissionGranted(permission: String) =
|
||||
ContextCompat.checkSelfPermission(this, permission) == PackageManager.PERMISSION_GRANTED
|
||||
|
||||
private fun Fragment.requestPermission(permission: String, requestCode: Int) {
|
||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) return
|
||||
if (context?.isPermissionGranted(permission) == true) return
|
||||
requestPermissions(arrayOf(permission), requestCode)
|
||||
}
|
||||
|
||||
internal class Debouncer(
|
||||
val debounceTime: Int
|
||||
) {
|
||||
private var lastShot = 0L
|
||||
|
||||
operator fun <T> invoke(block: () -> T) = if (System.currentTimeMillis() - lastShot > debounceTime) {
|
||||
block.invoke().also {
|
||||
lastShot = System.currentTimeMillis()
|
||||
}
|
||||
} else {
|
||||
null
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,65 @@
|
||||
package com.kroegerama.kaiteki.bcode.ui
|
||||
|
||||
import android.content.Context
|
||||
import android.os.Handler
|
||||
import android.util.Log
|
||||
import android.view.LayoutInflater
|
||||
import android.widget.Toast
|
||||
import androidx.appcompat.app.AlertDialog
|
||||
import androidx.core.os.postDelayed
|
||||
import androidx.lifecycle.LifecycleOwner
|
||||
import com.google.zxing.BarcodeFormat
|
||||
import com.google.zxing.Result
|
||||
import com.kroegerama.kaiteki.bcode.BarcodeResultListener
|
||||
import com.kroegerama.kaiteki.bcode.R
|
||||
import com.kroegerama.kaiteki.bcode.hasCameraPermission
|
||||
import com.kroegerama.kaiteki.bcode.views.BarcodeView
|
||||
|
||||
|
||||
fun Context.showBarcodeAlertDialog(
|
||||
owner: LifecycleOwner,
|
||||
listener: BarcodeResultListener,
|
||||
formats: List<BarcodeFormat> = listOf(BarcodeFormat.QR_CODE),
|
||||
barcodeInverted: Boolean = false
|
||||
) {
|
||||
if (!hasCameraPermission) {
|
||||
Log.w("BarcodeAlertDialog", "Camera permission required")
|
||||
Toast.makeText(this, "Camera permission required", Toast.LENGTH_LONG).show()
|
||||
return
|
||||
}
|
||||
|
||||
val view = LayoutInflater.from(this).inflate(R.layout.dlg_barcode, null, false)
|
||||
val bcode = view.findViewById<BarcodeView>(R.id.bcode)
|
||||
val handler = Handler()
|
||||
|
||||
val dlg = AlertDialog.Builder(this)
|
||||
.setOnDismissListener { bcode.unbind() }
|
||||
.setView(view)
|
||||
.setOnCancelListener {
|
||||
listener.onBarcodeScanCancelled()
|
||||
}
|
||||
.setNegativeButton(android.R.string.cancel) { _, _ ->
|
||||
listener.onBarcodeScanCancelled()
|
||||
}
|
||||
.show()
|
||||
|
||||
bcode.setFormats(formats)
|
||||
bcode.setBarcodeInverted(barcodeInverted)
|
||||
bcode.setBarcodeResultListener(object : BarcodeResultListener {
|
||||
override fun onBarcodeResult(result: Result): Boolean {
|
||||
val doDismiss = listener.onBarcodeResult(result)
|
||||
if (doDismiss) {
|
||||
handler.postDelayed(500) {
|
||||
dlg.dismiss()
|
||||
}
|
||||
}
|
||||
return doDismiss
|
||||
}
|
||||
|
||||
override fun onBarcodeScanCancelled() {
|
||||
//Ignore: BarcodeView will never emit this event
|
||||
}
|
||||
})
|
||||
|
||||
bcode.bindToLifecycle(owner)
|
||||
}
|
||||
@ -0,0 +1,109 @@
|
||||
package com.kroegerama.kaiteki.bcode.ui
|
||||
|
||||
import android.content.DialogInterface
|
||||
import android.os.Bundle
|
||||
import android.os.Handler
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import androidx.core.os.bundleOf
|
||||
import androidx.core.os.postDelayed
|
||||
import androidx.fragment.app.FragmentManager
|
||||
import com.google.android.material.bottomsheet.BottomSheetDialogFragment
|
||||
import com.google.zxing.BarcodeFormat
|
||||
import com.google.zxing.Result
|
||||
import com.kroegerama.kaiteki.bcode.*
|
||||
import kotlinx.android.synthetic.main.dlg_barcode.*
|
||||
|
||||
class BarcodeBottomSheet : BottomSheetDialogFragment(), BarcodeResultListener {
|
||||
|
||||
private val formats: List<BarcodeFormat>? by lazy {
|
||||
arguments?.getSerializable(KEY_FORMATS) as List<BarcodeFormat>
|
||||
}
|
||||
|
||||
private val barcodeInverted by lazy {
|
||||
arguments?.getBoolean(KEY_INVERTED, false) ?: false
|
||||
}
|
||||
|
||||
private val handler = Handler()
|
||||
|
||||
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View =
|
||||
inflater.inflate(R.layout.dlg_barcode, container, false)
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
|
||||
formats?.let(bcode::setFormats)
|
||||
bcode.setBarcodeInverted(barcodeInverted)
|
||||
bcode.setBarcodeResultListener(this)
|
||||
|
||||
if (requireContext().hasCameraPermission) {
|
||||
bcode.bindToLifecycle(this)
|
||||
} else {
|
||||
requestCameraPermission(REQUEST_CAMERA)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onRequestPermissionsResult(requestCode: Int, permissions: Array<out String>, grantResults: IntArray) {
|
||||
when (requestCode) {
|
||||
REQUEST_CAMERA ->
|
||||
if (grantResults.isPermissionGranted)
|
||||
bcode.bindToLifecycle(this)
|
||||
else
|
||||
dismissAllowingStateLoss()
|
||||
}
|
||||
super.onRequestPermissionsResult(requestCode, permissions, grantResults)
|
||||
}
|
||||
|
||||
override fun onStop() {
|
||||
super.onStop()
|
||||
bcode.unbind()
|
||||
}
|
||||
|
||||
override fun onBarcodeResult(result: Result): Boolean {
|
||||
if ((parentFragment as? BarcodeResultListener)?.onBarcodeResult(result) == true) {
|
||||
handler.postDelayed(500) {
|
||||
dismiss()
|
||||
}
|
||||
return true
|
||||
} else if ((activity as? BarcodeResultListener)?.onBarcodeResult(result) == true) {
|
||||
handler.postDelayed(500) {
|
||||
dismiss()
|
||||
}
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
override fun onBarcodeScanCancelled() {
|
||||
//Ignore: BarcodeView will never emit this event
|
||||
}
|
||||
|
||||
override fun onCancel(dialog: DialogInterface) {
|
||||
(parentFragment as? BarcodeResultListener)?.onBarcodeScanCancelled()
|
||||
(activity as? BarcodeResultListener)?.onBarcodeScanCancelled()
|
||||
super.onCancel(dialog)
|
||||
}
|
||||
|
||||
companion object {
|
||||
|
||||
private const val KEY_FORMATS = "formats"
|
||||
private const val KEY_INVERTED = "inverted"
|
||||
|
||||
private const val REQUEST_CAMERA = 0xbb_ca
|
||||
|
||||
fun show(
|
||||
fm: FragmentManager,
|
||||
formats: List<BarcodeFormat> = listOf(BarcodeFormat.QR_CODE),
|
||||
barcodeInverted: Boolean = false,
|
||||
tag: String? = null
|
||||
) = BarcodeBottomSheet().apply {
|
||||
arguments = bundleOf(
|
||||
KEY_FORMATS to formats,
|
||||
KEY_INVERTED to barcodeInverted
|
||||
)
|
||||
|
||||
show(fm, tag)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,120 @@
|
||||
package com.kroegerama.kaiteki.bcode.ui
|
||||
|
||||
import android.content.DialogInterface
|
||||
import android.os.Bundle
|
||||
import android.os.Handler
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.view.Window
|
||||
import androidx.core.os.bundleOf
|
||||
import androidx.core.os.postDelayed
|
||||
import androidx.fragment.app.DialogFragment
|
||||
import androidx.fragment.app.FragmentManager
|
||||
import com.google.zxing.BarcodeFormat
|
||||
import com.google.zxing.Result
|
||||
import com.kroegerama.kaiteki.bcode.*
|
||||
import kotlinx.android.synthetic.main.dlg_barcode.*
|
||||
|
||||
open class BarcodeDialog : DialogFragment(), BarcodeResultListener {
|
||||
|
||||
private val formats: List<BarcodeFormat>? by lazy {
|
||||
arguments?.getSerializable(KEY_FORMATS) as List<BarcodeFormat>
|
||||
}
|
||||
|
||||
private val barcodeInverted by lazy {
|
||||
arguments?.getBoolean(KEY_INVERTED, false) ?: false
|
||||
}
|
||||
|
||||
private val handler = Handler()
|
||||
|
||||
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View =
|
||||
inflater.inflate(R.layout.dlg_barcode, container, false).also {
|
||||
dialog?.window?.run {
|
||||
requestFeature(Window.FEATURE_NO_TITLE)
|
||||
requestFeature(Window.FEATURE_SWIPE_TO_DISMISS)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
|
||||
formats?.let(bcode::setFormats)
|
||||
bcode.setBarcodeInverted(barcodeInverted)
|
||||
bcode.setBarcodeResultListener(this)
|
||||
|
||||
if (requireContext().hasCameraPermission) {
|
||||
bcode.bindToLifecycle(this)
|
||||
} else {
|
||||
requestCameraPermission(REQUEST_CAMERA)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onRequestPermissionsResult(requestCode: Int, permissions: Array<out String>, grantResults: IntArray) {
|
||||
when (requestCode) {
|
||||
REQUEST_CAMERA ->
|
||||
if (grantResults.isPermissionGranted)
|
||||
bcode.bindToLifecycle(this)
|
||||
else
|
||||
dismissAllowingStateLoss()
|
||||
}
|
||||
super.onRequestPermissionsResult(requestCode, permissions, grantResults)
|
||||
}
|
||||
|
||||
override fun onBarcodeScanCancelled() {
|
||||
//Ignore: BarcodeView will never emit this event
|
||||
}
|
||||
|
||||
override fun onStop() {
|
||||
super.onStop()
|
||||
bcode.unbind()
|
||||
}
|
||||
|
||||
override fun onStart() {
|
||||
super.onStart()
|
||||
dialog?.window?.setLayout(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT)
|
||||
}
|
||||
|
||||
override fun onBarcodeResult(result: Result): Boolean {
|
||||
if ((parentFragment as? BarcodeResultListener)?.onBarcodeResult(result) == true) {
|
||||
handler.postDelayed(500) {
|
||||
dismiss()
|
||||
}
|
||||
return true
|
||||
} else if ((activity as? BarcodeResultListener)?.onBarcodeResult(result) == true) {
|
||||
handler.postDelayed(500) {
|
||||
dismiss()
|
||||
}
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
override fun onCancel(dialog: DialogInterface) {
|
||||
(parentFragment as? BarcodeResultListener)?.onBarcodeScanCancelled()
|
||||
(activity as? BarcodeResultListener)?.onBarcodeScanCancelled()
|
||||
super.onCancel(dialog)
|
||||
}
|
||||
|
||||
companion object {
|
||||
|
||||
private const val KEY_FORMATS = "formats"
|
||||
private const val KEY_INVERTED = "inverted"
|
||||
|
||||
private const val REQUEST_CAMERA = 0xbd_ca
|
||||
|
||||
fun show(
|
||||
fm: FragmentManager,
|
||||
formats: List<BarcodeFormat> = listOf(BarcodeFormat.QR_CODE),
|
||||
barcodeInverted: Boolean = false,
|
||||
tag: String? = null
|
||||
) = BarcodeDialog().apply {
|
||||
arguments = bundleOf(
|
||||
KEY_FORMATS to formats,
|
||||
KEY_INVERTED to barcodeInverted
|
||||
)
|
||||
|
||||
show(fm, tag)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,84 @@
|
||||
package com.kroegerama.kaiteki.bcode.ui
|
||||
|
||||
import android.os.Bundle
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import androidx.core.os.bundleOf
|
||||
import androidx.fragment.app.Fragment
|
||||
import com.google.zxing.BarcodeFormat
|
||||
import com.google.zxing.Result
|
||||
import com.kroegerama.kaiteki.bcode.*
|
||||
import kotlinx.android.synthetic.main.dlg_barcode.*
|
||||
|
||||
class BarcodeFragment : Fragment(), BarcodeResultListener {
|
||||
|
||||
private val formats: List<BarcodeFormat>? by lazy {
|
||||
arguments?.getSerializable(KEY_FORMATS) as List<BarcodeFormat>
|
||||
}
|
||||
|
||||
private val barcodeInverted by lazy {
|
||||
arguments?.getBoolean(KEY_INVERTED, false) ?: false
|
||||
}
|
||||
|
||||
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View =
|
||||
inflater.inflate(R.layout.dlg_barcode, container, false)
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
|
||||
formats?.let(bcode::setFormats)
|
||||
bcode.setBarcodeInverted(barcodeInverted)
|
||||
bcode.setBarcodeResultListener(this)
|
||||
|
||||
if (requireContext().hasCameraPermission) {
|
||||
bcode.bindToLifecycle(this)
|
||||
} else {
|
||||
requestCameraPermission(REQUEST_CAMERA)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onRequestPermissionsResult(requestCode: Int, permissions: Array<out String>, grantResults: IntArray) {
|
||||
when (requestCode) {
|
||||
REQUEST_CAMERA ->
|
||||
if (grantResults.isPermissionGranted)
|
||||
bcode.bindToLifecycle(this)
|
||||
}
|
||||
super.onRequestPermissionsResult(requestCode, permissions, grantResults)
|
||||
}
|
||||
|
||||
override fun onStop() {
|
||||
super.onStop()
|
||||
bcode.unbind()
|
||||
}
|
||||
|
||||
override fun onBarcodeResult(result: Result): Boolean {
|
||||
if ((parentFragment as? BarcodeResultListener)?.onBarcodeResult(result) == true) {
|
||||
return true
|
||||
} else if ((activity as? BarcodeResultListener)?.onBarcodeResult(result) == true) {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
override fun onBarcodeScanCancelled() {
|
||||
//Ignore: BarcodeView will never emit this event
|
||||
}
|
||||
|
||||
companion object {
|
||||
private const val KEY_FORMATS = "formats"
|
||||
private const val KEY_INVERTED = "inverted"
|
||||
|
||||
private const val REQUEST_CAMERA = 0xbf_ca
|
||||
|
||||
fun makeInstance(
|
||||
formats: List<BarcodeFormat> = listOf(BarcodeFormat.QR_CODE),
|
||||
barcodeInverted: Boolean = false
|
||||
) = BarcodeFragment().apply {
|
||||
arguments = bundleOf(
|
||||
KEY_FORMATS to formats,
|
||||
KEY_INVERTED to barcodeInverted
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,197 @@
|
||||
package com.kroegerama.kaiteki.bcode.views
|
||||
|
||||
import android.content.Context
|
||||
import android.content.res.Resources
|
||||
import android.graphics.Color
|
||||
import android.graphics.Matrix
|
||||
import android.os.Handler
|
||||
import android.os.HandlerThread
|
||||
import android.util.*
|
||||
import android.view.LayoutInflater
|
||||
import android.view.Surface
|
||||
import android.view.TextureView
|
||||
import android.view.ViewGroup
|
||||
import android.widget.FrameLayout
|
||||
import androidx.camera.core.*
|
||||
import androidx.lifecycle.LifecycleOwner
|
||||
import com.google.zxing.BarcodeFormat
|
||||
import com.google.zxing.DecodeHintType
|
||||
import com.google.zxing.MultiFormatReader
|
||||
import com.google.zxing.Result
|
||||
import com.kroegerama.kaiteki.bcode.*
|
||||
import com.kroegerama.kaiteki.bcode.R
|
||||
import kotlin.math.max
|
||||
|
||||
class BarcodeView @JvmOverloads constructor(
|
||||
context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0
|
||||
) : FrameLayout(context, attrs, defStyleAttr), ResultListener {
|
||||
|
||||
private val textureView: TextureView
|
||||
private val resultView: ResultPointView
|
||||
|
||||
private var bufferSize = SizeF(0f, 0f)
|
||||
|
||||
private var listener: BarcodeResultListener? = null
|
||||
|
||||
private val barcodeReader by lazy { MultiFormatReader() }
|
||||
|
||||
private val analyzer by lazy { BarcodeAnalyzer(this, barcodeReader) }
|
||||
|
||||
private val resultDebouncer = Debouncer(500)
|
||||
|
||||
init {
|
||||
LayoutInflater.from(context).inflate(R.layout.barcode_view, this)
|
||||
|
||||
keepScreenOn = true
|
||||
|
||||
textureView = findViewById(R.id.textureView)
|
||||
resultView = findViewById(R.id.resultView)
|
||||
|
||||
attrs.handleArguments(context, Style.BarcodeView, defStyleAttr, 0) {
|
||||
resultView.showResultPoints = getBoolean(Style.BarcodeView_showResultPoints, true)
|
||||
resultView.setResultPointColor(getColor(Style.BarcodeView_resultPointColor, Color.GREEN))
|
||||
val defaultSize =
|
||||
TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 8f, Resources.getSystem().displayMetrics)
|
||||
resultView.setPointSize(getDimension(Style.BarcodeView_resultPointSize, defaultSize))
|
||||
analyzer.inverted = getBoolean(Style.BarcodeView_barcodeInverted, false)
|
||||
}
|
||||
|
||||
textureView.addOnLayoutChangeListener { _, _, _, _, _, _, _, _, _ ->
|
||||
updateTransform()
|
||||
}
|
||||
}
|
||||
|
||||
override fun onResult(result: Result, imageWidth: Int, imageHeight: Int, imageRotation: Int) {
|
||||
resultView.setResult(result, imageWidth, imageHeight, imageRotation)
|
||||
|
||||
val d = resultDebouncer {
|
||||
listener?.onBarcodeResult(result)
|
||||
}
|
||||
if (d == true) {
|
||||
// dialog/fragment will be dismissed -> do not send any more events
|
||||
listener = null
|
||||
}
|
||||
}
|
||||
|
||||
override fun onNoResult() {
|
||||
resultView.clear()
|
||||
}
|
||||
|
||||
fun setBarcodeResultListener(listener: BarcodeResultListener) {
|
||||
this.listener = listener
|
||||
}
|
||||
|
||||
/**
|
||||
* enable scanning of inverted barcodes (e.g. white QR Code on black background)
|
||||
*/
|
||||
fun setBarcodeInverted(inverted: Boolean) {
|
||||
analyzer.inverted = inverted
|
||||
}
|
||||
|
||||
fun bindToLifecycle(owner: LifecycleOwner) {
|
||||
textureView.post { startPreview(owner) }
|
||||
}
|
||||
|
||||
fun unbind() {
|
||||
resultView.clear()
|
||||
listener = null
|
||||
CameraX.unbindAll()
|
||||
}
|
||||
|
||||
fun setFormats(formats: List<BarcodeFormat>) = barcodeReader.setHints(
|
||||
mapOf(
|
||||
DecodeHintType.POSSIBLE_FORMATS to formats
|
||||
)
|
||||
)
|
||||
|
||||
private fun startPreview(owner: LifecycleOwner) {
|
||||
val metrics = DisplayMetrics().also { textureView.display.getRealMetrics(it) }
|
||||
val screenSize = Size(metrics.widthPixels, metrics.heightPixels)
|
||||
val screenAspectRatio = Rational(metrics.widthPixels, metrics.heightPixels)
|
||||
val screenRotation = textureView.display.rotation
|
||||
|
||||
val previewConfig = PreviewConfig.Builder().apply {
|
||||
setLensFacing(CameraX.LensFacing.BACK)
|
||||
setTargetResolution(screenSize / 2)
|
||||
setTargetAspectRatio(screenAspectRatio)
|
||||
setTargetRotation(screenRotation)
|
||||
}.build()
|
||||
|
||||
val preview = Preview(previewConfig).apply {
|
||||
setOnPreviewOutputUpdateListener(::previewOutputUpdated)
|
||||
}
|
||||
|
||||
val analysisConfig = ImageAnalysisConfig.Builder().apply {
|
||||
setLensFacing(CameraX.LensFacing.BACK)
|
||||
setTargetResolution(screenSize / 2)
|
||||
setTargetAspectRatio(screenAspectRatio)
|
||||
setTargetRotation(textureView.display.rotation)
|
||||
|
||||
setImageReaderMode(ImageAnalysis.ImageReaderMode.ACQUIRE_LATEST_IMAGE)
|
||||
val analyzerThread = HandlerThread("BarcodeAnalyzer").apply { start() }
|
||||
setCallbackHandler(Handler(analyzerThread.looper))
|
||||
}.build()
|
||||
|
||||
val analysis = ImageAnalysis(analysisConfig).apply { analyzer = this@BarcodeView.analyzer }
|
||||
|
||||
CameraX.bindToLifecycle(owner, preview, analysis)
|
||||
}
|
||||
|
||||
private fun previewOutputUpdated(output: Preview.PreviewOutput) {
|
||||
// https://github.com/android/camera/blob/848cf1e2c8404599050d79086dee1d0c8951b66e/CameraXBasic/app/src/main/java/com/android/example/cameraxbasic/utils/AutoFitPreviewBuilder.kt#L100
|
||||
(textureView.parent as? ViewGroup)?.apply {
|
||||
val idx = indexOfChild(textureView)
|
||||
removeView(textureView)
|
||||
addView(textureView, idx)
|
||||
textureView.surfaceTexture = output.surfaceTexture
|
||||
}
|
||||
|
||||
bufferSize = SizeF(output.textureSize.height.toFloat(), output.textureSize.width.toFloat())
|
||||
updateTransform()
|
||||
}
|
||||
|
||||
private fun updateTransform() {
|
||||
val viewFinderWidth = textureView.width.toFloat()
|
||||
val viewFinderHeight = textureView.height.toFloat()
|
||||
val viewFinderRotation = when (textureView.display.rotation) {
|
||||
Surface.ROTATION_0 -> 0
|
||||
Surface.ROTATION_90 -> 90
|
||||
Surface.ROTATION_180 -> 180
|
||||
Surface.ROTATION_270 -> 270
|
||||
else -> return
|
||||
}
|
||||
val matrix = Matrix()
|
||||
val centerX = viewFinderWidth / 2f
|
||||
val centerY = viewFinderHeight / 2f
|
||||
|
||||
matrix.postRotate(-viewFinderRotation.toFloat(), centerX, centerY)
|
||||
|
||||
val bufferRatio = bufferSize.width / bufferSize.height
|
||||
val viewRatio = viewFinderWidth / viewFinderHeight
|
||||
|
||||
if (bufferRatio > viewRatio) {
|
||||
val factor = bufferRatio / viewRatio
|
||||
matrix.preScale(factor, 1f, centerX, centerY)
|
||||
} else {
|
||||
val factor = viewRatio / bufferRatio
|
||||
matrix.preScale(1f, factor, centerX, centerY)
|
||||
}
|
||||
|
||||
if (viewFinderRotation % 180 != 0) {
|
||||
if (bufferRatio > viewRatio) {
|
||||
val s = 1f / bufferRatio
|
||||
matrix.preScale(s, s, centerX, centerY)
|
||||
} else {
|
||||
val s = max(bufferRatio, 1f / viewRatio)
|
||||
matrix.preScale(s, s, centerX, centerY)
|
||||
}
|
||||
}
|
||||
|
||||
// val dbgScale = .95f
|
||||
// matrix.preScale(dbgScale, dbgScale, centerX, centerY)
|
||||
|
||||
textureView.setTransform(matrix)
|
||||
}
|
||||
}
|
||||
|
||||
private operator fun Size.div(other: Int): Size = Size(width / other, height / other)
|
||||
@ -0,0 +1,102 @@
|
||||
package com.kroegerama.kaiteki.bcode.views
|
||||
|
||||
import android.content.Context
|
||||
import android.content.res.Resources
|
||||
import android.graphics.*
|
||||
import android.util.AttributeSet
|
||||
import android.util.TypedValue
|
||||
import android.view.View
|
||||
import androidx.annotation.ColorInt
|
||||
import com.google.zxing.Result
|
||||
import com.kroegerama.kaiteki.bcode.BuildConfig
|
||||
import com.kroegerama.kaiteki.bcode.Style
|
||||
import com.kroegerama.kaiteki.bcode.handleArguments
|
||||
import kotlin.math.max
|
||||
|
||||
class ResultPointView @JvmOverloads constructor(
|
||||
context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0
|
||||
) : View(context, attrs, defStyleAttr) {
|
||||
|
||||
private val pPoints = Paint().apply {
|
||||
style = Paint.Style.STROKE
|
||||
color = Color.GREEN
|
||||
strokeWidth = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 8f, Resources.getSystem().displayMetrics)
|
||||
strokeCap = Paint.Cap.ROUND
|
||||
}
|
||||
|
||||
private var resultPoints = floatArrayOf()
|
||||
private var rect = RectF()
|
||||
|
||||
var showResultPoints = true
|
||||
set(value) {
|
||||
field = value
|
||||
invalidate()
|
||||
}
|
||||
|
||||
init {
|
||||
attrs.handleArguments(context, Style.ResultPointView, defStyleAttr, 0) {
|
||||
showResultPoints = getBoolean(Style.ResultPointView_showResultPoints, showResultPoints)
|
||||
|
||||
pPoints.color = getColor(Style.ResultPointView_resultPointColor, pPoints.color)
|
||||
pPoints.strokeWidth =
|
||||
getDimension(Style.ResultPointView_resultPointSize, pPoints.strokeWidth)
|
||||
}
|
||||
}
|
||||
|
||||
fun setResultPointColor(@ColorInt color: Int) {
|
||||
pPoints.color = color
|
||||
invalidate()
|
||||
}
|
||||
|
||||
fun setPointSize(size: Float) {
|
||||
pPoints.strokeWidth = size
|
||||
}
|
||||
|
||||
fun clear() {
|
||||
resultPoints = floatArrayOf()
|
||||
|
||||
postInvalidate()
|
||||
}
|
||||
|
||||
fun setResult(result: Result, imageWidth: Int, imageHeight: Int, imageRotation: Int) {
|
||||
if (!showResultPoints) return
|
||||
|
||||
val localMatrix = createMatrix(imageWidth.toFloat(), imageHeight.toFloat(), imageRotation)
|
||||
|
||||
resultPoints = result.resultPoints.flatMap { listOf(it.x, it.y) }.toFloatArray()
|
||||
localMatrix.mapPoints(resultPoints)
|
||||
|
||||
if (BuildConfig.DEBUG) {
|
||||
rect = RectF(0f, 0f, imageWidth.toFloat(), imageHeight.toFloat())
|
||||
localMatrix.mapRect(rect)
|
||||
}
|
||||
|
||||
postInvalidate()
|
||||
}
|
||||
|
||||
private fun createMatrix(imageWidth: Float, imageHeight: Float, imageRotation: Int) = Matrix().apply {
|
||||
preTranslate((width - imageWidth) / 2f, (height - imageHeight) / 2f)
|
||||
preRotate(imageRotation.toFloat(), imageWidth / 2f, imageHeight / 2f)
|
||||
|
||||
val wScale: Float
|
||||
val hScale: Float
|
||||
|
||||
if (imageRotation % 180 == 0) {
|
||||
wScale = width.toFloat() / imageWidth
|
||||
hScale = height.toFloat() / imageHeight
|
||||
} else {
|
||||
wScale = height.toFloat() / imageWidth
|
||||
hScale = width.toFloat() / imageHeight
|
||||
|
||||
}
|
||||
|
||||
val scale = max(wScale, hScale)
|
||||
preScale(scale, scale, imageWidth / 2f, imageHeight / 2f)
|
||||
}
|
||||
|
||||
override fun onDraw(canvas: Canvas) {
|
||||
if (showResultPoints) canvas.drawPoints(resultPoints, pPoints)
|
||||
|
||||
if (BuildConfig.DEBUG) canvas.drawRect(rect, pPoints)
|
||||
}
|
||||
}
|
||||
17
barcode_kaiteki/src/main/res/layout/barcode_view.xml
Normal file
17
barcode_kaiteki/src/main/res/layout/barcode_view.xml
Normal file
@ -0,0 +1,17 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<merge xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
tools:parentTag="android.widget.FrameLayout">
|
||||
|
||||
<TextureView
|
||||
android:id="@+id/textureView"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent" />
|
||||
|
||||
<com.kroegerama.kaiteki.bcode.views.ResultPointView
|
||||
android:id="@+id/resultView"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent" />
|
||||
</merge>
|
||||
9
barcode_kaiteki/src/main/res/layout/dlg_barcode.xml
Normal file
9
barcode_kaiteki/src/main/res/layout/dlg_barcode.xml
Normal file
@ -0,0 +1,9 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<com.kroegerama.kaiteki.bcode.views.BarcodeView xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
android:id="@+id/bcode"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
app:resultPointColor="#09E85E"
|
||||
app:resultPointSize="8dp"
|
||||
app:showResultPoints="true" />
|
||||
22
barcode_kaiteki/src/main/res/values/attrs.xml
Normal file
22
barcode_kaiteki/src/main/res/values/attrs.xml
Normal file
@ -0,0 +1,22 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
|
||||
<attr name="showResultPoints" format="boolean" />
|
||||
<attr name="resultPointColor" format="color" />
|
||||
<attr name="resultPointSize" format="dimension" />
|
||||
|
||||
<declare-styleable name="ResultPointView">
|
||||
<attr name="showResultPoints" />
|
||||
<attr name="resultPointColor" />
|
||||
<attr name="resultPointSize" />
|
||||
</declare-styleable>
|
||||
|
||||
<declare-styleable name="BarcodeView">
|
||||
<attr name="showResultPoints" />
|
||||
<attr name="resultPointColor" />
|
||||
<attr name="resultPointSize" />
|
||||
<attr name="barcodeInverted" format="boolean" />
|
||||
</declare-styleable>
|
||||
|
||||
|
||||
</resources>
|
||||
@ -2,7 +2,7 @@
|
||||
|
||||
buildscript {
|
||||
ext {
|
||||
kotlin_version = '1.3.61'
|
||||
kotlin_version = '1.3.71'
|
||||
}
|
||||
|
||||
repositories {
|
||||
|
||||
@ -1 +1 @@
|
||||
include ':app', ':pointmobilescannerlibrary', ':dynamic_vgalimenti', ':dynamic__base', ':zebrascannerlibrary', ':honeywellscannerlibrary', ':dynamic_ime', ':dynamic_frudis', ':dynamic_saporiveri_pv', ':keyobardemulatorscannerlibrary', ':barcode_base_android_library', ':dynamic_saporiveri'
|
||||
include ':app', ':pointmobilescannerlibrary', ':dynamic_vgalimenti', ':dynamic__base', ':zebrascannerlibrary', ':honeywellscannerlibrary', ':dynamic_ime', ':dynamic_frudis', ':dynamic_saporiveri_pv', ':keyobardemulatorscannerlibrary', ':barcode_base_android_library', ':dynamic_saporiveri', ':barcode_kaiteki'
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user