From a75f5b9a6f79a47f93cf6652814f30ab52775211 Mon Sep 17 00:00:00 2001 From: Ahmed Al-Omairi Date: Thu, 28 Aug 2025 21:06:46 +0300 Subject: [PATCH] add pinpad fragment, 10 second counter will navigate to payment --- .../pos/ui/cashier/CashierViewModel.java | 138 ++++++++++++++-- .../dspread/pos/ui/main/MainViewModel.java | 3 + .../pos/ui/payment/PaymentViewModel.java | 2 +- .../dspread/pos/ui/pinpad/PinpadFragment.java | 150 ++++++++++++++++++ .../pos/ui/pinpad/PinpadViewModel.java | 36 +++++ .../src/main/res/layout/fragment_pinpad.xml | 48 ++++++ .../main/res/menu/activity_main_drawer.xml | 5 + 7 files changed, 364 insertions(+), 18 deletions(-) create mode 100644 pos_android_app/src/main/java/com/dspread/pos/ui/pinpad/PinpadFragment.java create mode 100644 pos_android_app/src/main/java/com/dspread/pos/ui/pinpad/PinpadViewModel.java create mode 100644 pos_android_app/src/main/res/layout/fragment_pinpad.xml diff --git a/pos_android_app/src/main/java/com/dspread/pos/ui/cashier/CashierViewModel.java b/pos_android_app/src/main/java/com/dspread/pos/ui/cashier/CashierViewModel.java index c0acde2..79d4d0a 100644 --- a/pos_android_app/src/main/java/com/dspread/pos/ui/cashier/CashierViewModel.java +++ b/pos_android_app/src/main/java/com/dspread/pos/ui/cashier/CashierViewModel.java @@ -1,16 +1,26 @@ package com.dspread.pos.ui.cashier; import android.app.Application; +import android.content.Context; import android.graphics.Bitmap; +import android.os.Handler; +import android.os.Looper; +import android.os.RemoteException; import android.util.Log; import androidx.annotation.NonNull; +import androidx.databinding.ObservableBoolean; import androidx.databinding.ObservableField; import androidx.lifecycle.LiveData; import androidx.lifecycle.MutableLiveData; import com.dspread.pos.common.base.BaseAppViewModel; import com.dspread.pos.data.local.PreferencesManager; +import com.dspread.pos.printerAPI.PrinterHelper; +import com.dspread.print.device.PrintListener; +import com.dspread.print.device.PrinterDevice; +import com.dspread.print.device.PrinterManager; +import com.dspread.print.device.PrinterInitListener; import org.json.JSONException; import org.json.JSONObject; @@ -19,45 +29,142 @@ public class CashierViewModel extends BaseAppViewModel { // Enum to represent payment status public enum PaymentStatus { - WAITING, SUCCESS, FAILED + WAITING, SUCCESS, FAILED, PRINTER_NOT_AVAILABLE } private static final String TAG = "CashierVM"; - - - // LiveData to hold payment information private final MutableLiveData paymentInfo = new MutableLiveData<>(); public LiveData getPaymentInfo() { return paymentInfo; } - public ObservableField amount = new ObservableField<>("0.00"); public ObservableField currencyCode = new ObservableField<>("RUB"); - - // LiveData to track payment status private final MutableLiveData paymentStatus = new MutableLiveData<>(PaymentStatus.WAITING); public LiveData getPaymentStatus() { return paymentStatus; } - public final ObservableField mainLogo = new ObservableField<>(); public final ObservableField mainLogoBitmap = new ObservableField<>(); + public ObservableBoolean isPrinting = new ObservableBoolean(false); + private Context mContext; + + protected PrinterDevice mPrinter; public CashierViewModel(@NonNull Application application) { super(application); + mContext = application.getApplicationContext(); + initializePrinter(); +} + + private void initializePrinter() { + try { + PrinterManager instance = PrinterManager.getInstance(); + mPrinter = instance.getPrinter(); + + if (mPrinter != null) { + PrinterHelper.getInstance().setPrinter(mPrinter); + PrinterHelper.getInstance().initPrinter(mContext); + + // Set print listener + mPrinter.setPrintListener(new PrintListener() { + @Override + public void printResult(boolean success, String message, PrinterDevice.ResultType resultType) { + Log.d(TAG, "Print result: " + success + ", Message: " + message); + isPrinting.set(false); + + if (success) { + paymentStatus.setValue(PaymentStatus.SUCCESS); + Log.d(TAG, "Receipt printed successfully"); + } else { + paymentStatus.setValue(PaymentStatus.FAILED); + Log.e(TAG, "Receipt printing failed: " + message); + } + } + }); + + Log.d(TAG, "Printer initialized successfully"); + } else { + Log.e(TAG, "Printer device not available"); + paymentStatus.setValue(PaymentStatus.PRINTER_NOT_AVAILABLE); + } + + } catch (Exception e) { + Log.e(TAG, "Error initializing printer: " + e.getMessage()); + paymentStatus.setValue(PaymentStatus.PRINTER_NOT_AVAILABLE); + } } public void printReceipt() { String amountValue = amount.get(); String currencyCodeValue = currencyCode.get(); - if (amountValue != null && currencyCodeValue != null) { - Log.d(TAG, "Printing Receipt: " + amountValue + " " + currencyCodeValue); - // Simulate printing logic here - // For example, send the payment info to a printer service - } else { + + if (amountValue == null || currencyCodeValue == null) { Log.d(TAG, "No payment info to print"); + return; + } + + Log.d(TAG, "Printing Receipt: " + amountValue + " " + currencyCodeValue); + + // Check if printer is available + if (mPrinter == null) { + Log.e(TAG, "Printer not available"); + paymentStatus.setValue(PaymentStatus.PRINTER_NOT_AVAILABLE); + return; + } + + isPrinting.set(true); + paymentStatus.setValue(PaymentStatus.WAITING); + + try { + double amountDouble = Double.parseDouble(amountValue); + + // Execute the print command + PrinterHelper.getInstance().printReceipt( + mContext, + prefs, + amountDouble, + currencyCodeValue, + "Cashier", + "www.mulberrypos.ru", + "www.mulberrypos.ru" + ); + + } catch (NumberFormatException e) { + Log.e(TAG, "Invalid amount format: " + amountValue); + isPrinting.set(false); + paymentStatus.setValue(PaymentStatus.FAILED); + } catch (RemoteException e) { + Log.e(TAG, "Remote exception during printing: " + e.getMessage()); + isPrinting.set(false); + paymentStatus.setValue(PaymentStatus.FAILED); + } catch (Exception e) { + Log.e(TAG, "Unexpected error during printing: " + e.getMessage()); + isPrinting.set(false); + paymentStatus.setValue(PaymentStatus.FAILED); } } + // Method to reinitialize printer if connection is lost + public void reinitializePrinter() { + initializePrinter(); + } + + // Check if printer is available + public boolean isPrinterAvailable() { + return mPrinter != null; + } + + // Get printer status + public void checkPrinterStatus() { + try { + if (mPrinter != null) { + PrinterHelper.getInstance().getPrinterStatus(); + } + } catch (RemoteException e) { + Log.e(TAG, "Error checking printer status: " + e.getMessage()); + } + } + + @Override protected void onPaymentRedirected(String jsonString) { @@ -78,6 +185,7 @@ public class CashierViewModel extends BaseAppViewModel { case "success": Log.d(TAG, "Redirect Payment Success"); paymentStatus.setValue(PaymentStatus.SUCCESS); + printReceipt(); break; case "failed": Log.d(TAG, "Redirect Payment Failed"); @@ -94,8 +202,4 @@ public class CashierViewModel extends BaseAppViewModel { } - - - - } diff --git a/pos_android_app/src/main/java/com/dspread/pos/ui/main/MainViewModel.java b/pos_android_app/src/main/java/com/dspread/pos/ui/main/MainViewModel.java index 4933335..e46b564 100644 --- a/pos_android_app/src/main/java/com/dspread/pos/ui/main/MainViewModel.java +++ b/pos_android_app/src/main/java/com/dspread/pos/ui/main/MainViewModel.java @@ -20,6 +20,7 @@ import com.dspread.pos.posAPI.POS; import com.dspread.pos.ui.cashier.CashierFragment; import com.dspread.pos.ui.home.HomeFragment; import com.dspread.pos.ui.mulberry.MulberryFragment; +import com.dspread.pos.ui.pinpad.PinpadFragment; import com.dspread.pos.ui.printer.PrinterHelperFragment; import com.dspread.pos.ui.scan.ScanFragment; import com.dspread.pos.ui.setting.connection_settings.ConnectionSettingsFragment; @@ -151,6 +152,8 @@ public class MainViewModel extends BaseViewModel { return new ScanFragment(); case R.id.nav_cashier: return new CashierFragment(); + case R.id.nav_pinpad: + return new PinpadFragment(); case R.id.nav_mulberry: return new MulberryFragment(); } diff --git a/pos_android_app/src/main/java/com/dspread/pos/ui/payment/PaymentViewModel.java b/pos_android_app/src/main/java/com/dspread/pos/ui/payment/PaymentViewModel.java index 6c5de8d..3e337c6 100644 --- a/pos_android_app/src/main/java/com/dspread/pos/ui/payment/PaymentViewModel.java +++ b/pos_android_app/src/main/java/com/dspread/pos/ui/payment/PaymentViewModel.java @@ -317,7 +317,7 @@ public class PaymentViewModel extends BaseAppViewModel { amountDouble, currencySymbol, paymentMethod, - "String printingReciptQRcode", + "www.mulberrypos.ru", "www.mulberrypos.ru" ); } catch (RemoteException e) { diff --git a/pos_android_app/src/main/java/com/dspread/pos/ui/pinpad/PinpadFragment.java b/pos_android_app/src/main/java/com/dspread/pos/ui/pinpad/PinpadFragment.java new file mode 100644 index 0000000..e459d73 --- /dev/null +++ b/pos_android_app/src/main/java/com/dspread/pos/ui/pinpad/PinpadFragment.java @@ -0,0 +1,150 @@ +package com.dspread.pos.ui.pinpad; + +import android.content.Intent; +import android.os.Bundle; +import android.os.CountDownTimer; +import android.util.Log; +import android.view.LayoutInflater; +import android.view.ViewGroup; +import com.dspread.pos.common.base.BaseFragment; +import com.dspread.pos.TitleProviderListener; +import com.dspread.pos.posAPI.POS; +import com.dspread.pos.ui.payment.PaymentActivity; +import com.dspread.pos_android_app.BR; +import com.dspread.pos_android_app.R; +import com.dspread.pos_android_app.databinding.FragmentPinpadBinding; +import me.goldze.mvvmhabit.utils.ToastUtils; + +public class PinpadFragment extends BaseFragment implements TitleProviderListener { + + private static final String TAG = "PinpadFragment"; + private boolean canShow = true; + private CountDownTimer showTimer; + private String amountToPass; + private CountDownTimer autoNavigationTimer; + + @Override + public int initContentView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { + return R.layout.fragment_pinpad; + } + + @Override + public int initVariableId() { + return BR.viewModel; + } + + @Override + public void initData() { + super.initData(); + initTimer(); + + // Get amount from arguments if passed + if (getArguments() != null) { + amountToPass = getArguments().getString("amount", "100"); + Log.d(TAG, "Received amount: " + amountToPass); + viewModel.setInitialAmount(amountToPass); + } else { + amountToPass = "1000"; // default value + Log.d(TAG, "No arguments, using default amount: " + amountToPass); + } + + // Set up auto-navigation after 10 seconds + startAutoNavigationTimer(); + } + + private void initTimer() { + showTimer = new CountDownTimer(500, 500) { + @Override + public void onTick(long millisUntilFinished) {} + + @Override + public void onFinish() { + canShow = true; + } + }; + } + + private void startAutoNavigationTimer() { + autoNavigationTimer = new CountDownTimer(10000, 1000) { // 10 seconds + @Override + public void onTick(long millisUntilFinished) { + // Update UI with countdown + long secondsRemaining = millisUntilFinished / 1000; + viewModel.updateCountdown(secondsRemaining); + Log.d(TAG, "Countdown: " + secondsRemaining + " seconds"); + } + + @Override + public void onFinish() { + Log.d(TAG, "Auto-navigation timer finished, navigating to payment"); + navigateToPayment(); + } + }.start(); + } + + private void navigateToPayment() { + if (!canShow) { + Log.d(TAG, "Navigation blocked - canShow is false"); + return; + } + + if (getActivity() == null || getActivity().isFinishing()) { + Log.d(TAG, "Navigation blocked - activity is null or finishing"); + return; + } + + if (!POS.getInstance().isPOSReady()) { + ToastUtils.showShort("Please connect your device first"); + Log.d(TAG, "POS not ready, cannot navigate to payment"); + return; + } + + canShow = false; + showTimer.start(); + + Log.d(TAG, "Navigating to PaymentActivity with amount: " + amountToPass); + + Intent intent = new Intent(getActivity(), PaymentActivity.class); + intent.putExtra("amount", amountToPass); + startActivity(intent); + + // Optional: add animation + getActivity().overridePendingTransition(android.R.anim.fade_in, android.R.anim.fade_out); + } + + @Override + public void initViewObservable() { + // Remove this observer if you don't need manual payment start + // viewModel.paymentStartEvent.observe(this, amount -> navigateToPayment()); + } + + @Override + public void onDestroy() { + super.onDestroy(); + Log.d(TAG, "onDestroy called"); + + if (showTimer != null) { + showTimer.cancel(); + } + + if (autoNavigationTimer != null) { + autoNavigationTimer.cancel(); + } + } + + @Override + public void onPause() { + super.onPause(); + Log.d(TAG, "onPause called"); + + // Cancel timers to prevent memory leaks + if (autoNavigationTimer != null) { + autoNavigationTimer.cancel(); + } + } + + @Override + public String getTitle() { + return "Pin Pad"; + } +} \ No newline at end of file diff --git a/pos_android_app/src/main/java/com/dspread/pos/ui/pinpad/PinpadViewModel.java b/pos_android_app/src/main/java/com/dspread/pos/ui/pinpad/PinpadViewModel.java new file mode 100644 index 0000000..fc8e50b --- /dev/null +++ b/pos_android_app/src/main/java/com/dspread/pos/ui/pinpad/PinpadViewModel.java @@ -0,0 +1,36 @@ +package com.dspread.pos.ui.pinpad; + +import android.app.Application; +import androidx.annotation.NonNull; +import androidx.databinding.ObservableField; +import com.dspread.pos.common.base.BaseAppViewModel; + +public class PinpadViewModel extends BaseAppViewModel { + + public ObservableField amount = new ObservableField<>("10.00"); + public ObservableField countdown = new ObservableField<>("10"); + + public PinpadViewModel(@NonNull Application application) { + super(application); + } + + public void setInitialAmount(String initialAmount) { + if (initialAmount != null && !initialAmount.isEmpty()) { + amount.set(formatAmount(initialAmount)); + } + } + + public void updateCountdown(long seconds) { + countdown.set(String.valueOf(seconds)); + } + + private String formatAmount(String amountStr) { + try { + // Convert cents to dollars format + double amountDouble = Double.parseDouble(amountStr) / 100.0; + return String.format("%.2f", amountDouble); + } catch (NumberFormatException e) { + return "0.00"; + } + } +} \ No newline at end of file diff --git a/pos_android_app/src/main/res/layout/fragment_pinpad.xml b/pos_android_app/src/main/res/layout/fragment_pinpad.xml new file mode 100644 index 0000000..38525d2 --- /dev/null +++ b/pos_android_app/src/main/res/layout/fragment_pinpad.xml @@ -0,0 +1,48 @@ + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/pos_android_app/src/main/res/menu/activity_main_drawer.xml b/pos_android_app/src/main/res/menu/activity_main_drawer.xml index 032ecbc..0a2bb94 100644 --- a/pos_android_app/src/main/res/menu/activity_main_drawer.xml +++ b/pos_android_app/src/main/res/menu/activity_main_drawer.xml @@ -37,6 +37,11 @@ android:icon="@drawable/scan" android:title="@string/cashier" /> +