add pinpad fragment, 10 second counter will navigate to payment

This commit is contained in:
Ahmed Al-Omairi 2025-08-28 21:06:46 +03:00
parent 7bc7f21c2e
commit a75f5b9a6f
7 changed files with 364 additions and 18 deletions

View File

@ -1,16 +1,26 @@
package com.dspread.pos.ui.cashier; package com.dspread.pos.ui.cashier;
import android.app.Application; import android.app.Application;
import android.content.Context;
import android.graphics.Bitmap; import android.graphics.Bitmap;
import android.os.Handler;
import android.os.Looper;
import android.os.RemoteException;
import android.util.Log; import android.util.Log;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import androidx.databinding.ObservableBoolean;
import androidx.databinding.ObservableField; import androidx.databinding.ObservableField;
import androidx.lifecycle.LiveData; import androidx.lifecycle.LiveData;
import androidx.lifecycle.MutableLiveData; import androidx.lifecycle.MutableLiveData;
import com.dspread.pos.common.base.BaseAppViewModel; import com.dspread.pos.common.base.BaseAppViewModel;
import com.dspread.pos.data.local.PreferencesManager; 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.JSONException;
import org.json.JSONObject; import org.json.JSONObject;
@ -19,45 +29,142 @@ public class CashierViewModel extends BaseAppViewModel {
// Enum to represent payment status // Enum to represent payment status
public enum PaymentStatus { public enum PaymentStatus {
WAITING, SUCCESS, FAILED WAITING, SUCCESS, FAILED, PRINTER_NOT_AVAILABLE
} }
private static final String TAG = "CashierVM"; private static final String TAG = "CashierVM";
// LiveData to hold payment information
private final MutableLiveData<String> paymentInfo = new MutableLiveData<>(); private final MutableLiveData<String> paymentInfo = new MutableLiveData<>();
public LiveData<String> getPaymentInfo() { public LiveData<String> getPaymentInfo() {
return paymentInfo; return paymentInfo;
} }
public ObservableField<String> amount = new ObservableField<>("0.00"); public ObservableField<String> amount = new ObservableField<>("0.00");
public ObservableField<String> currencyCode = new ObservableField<>("RUB"); public ObservableField<String> currencyCode = new ObservableField<>("RUB");
// LiveData to track payment status
private final MutableLiveData<PaymentStatus> paymentStatus = new MutableLiveData<>(PaymentStatus.WAITING); private final MutableLiveData<PaymentStatus> paymentStatus = new MutableLiveData<>(PaymentStatus.WAITING);
public LiveData<PaymentStatus> getPaymentStatus() { public LiveData<PaymentStatus> getPaymentStatus() {
return paymentStatus; return paymentStatus;
} }
public final ObservableField<Integer> mainLogo = new ObservableField<>(); public final ObservableField<Integer> mainLogo = new ObservableField<>();
public final ObservableField<Bitmap> mainLogoBitmap = new ObservableField<>(); public final ObservableField<Bitmap> mainLogoBitmap = new ObservableField<>();
public ObservableBoolean isPrinting = new ObservableBoolean(false);
private Context mContext;
protected PrinterDevice mPrinter;
public CashierViewModel(@NonNull Application application) { public CashierViewModel(@NonNull Application application) {
super(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() { public void printReceipt() {
String amountValue = amount.get(); String amountValue = amount.get();
String currencyCodeValue = currencyCode.get(); String currencyCodeValue = currencyCode.get();
if (amountValue != null && currencyCodeValue != null) {
Log.d(TAG, "Printing Receipt: " + amountValue + " " + currencyCodeValue); if (amountValue == null || currencyCodeValue == null) {
// Simulate printing logic here
// For example, send the payment info to a printer service
} else {
Log.d(TAG, "No payment info to print"); 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 @Override
protected void onPaymentRedirected(String jsonString) { protected void onPaymentRedirected(String jsonString) {
@ -78,6 +185,7 @@ public class CashierViewModel extends BaseAppViewModel {
case "success": case "success":
Log.d(TAG, "Redirect Payment Success"); Log.d(TAG, "Redirect Payment Success");
paymentStatus.setValue(PaymentStatus.SUCCESS); paymentStatus.setValue(PaymentStatus.SUCCESS);
printReceipt();
break; break;
case "failed": case "failed":
Log.d(TAG, "Redirect Payment Failed"); Log.d(TAG, "Redirect Payment Failed");
@ -94,8 +202,4 @@ public class CashierViewModel extends BaseAppViewModel {
} }
} }

View File

@ -20,6 +20,7 @@ import com.dspread.pos.posAPI.POS;
import com.dspread.pos.ui.cashier.CashierFragment; import com.dspread.pos.ui.cashier.CashierFragment;
import com.dspread.pos.ui.home.HomeFragment; import com.dspread.pos.ui.home.HomeFragment;
import com.dspread.pos.ui.mulberry.MulberryFragment; 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.printer.PrinterHelperFragment;
import com.dspread.pos.ui.scan.ScanFragment; import com.dspread.pos.ui.scan.ScanFragment;
import com.dspread.pos.ui.setting.connection_settings.ConnectionSettingsFragment; import com.dspread.pos.ui.setting.connection_settings.ConnectionSettingsFragment;
@ -151,6 +152,8 @@ public class MainViewModel extends BaseViewModel {
return new ScanFragment(); return new ScanFragment();
case R.id.nav_cashier: case R.id.nav_cashier:
return new CashierFragment(); return new CashierFragment();
case R.id.nav_pinpad:
return new PinpadFragment();
case R.id.nav_mulberry: case R.id.nav_mulberry:
return new MulberryFragment(); return new MulberryFragment();
} }

View File

@ -317,7 +317,7 @@ public class PaymentViewModel extends BaseAppViewModel {
amountDouble, amountDouble,
currencySymbol, currencySymbol,
paymentMethod, paymentMethod,
"String printingReciptQRcode", "www.mulberrypos.ru",
"www.mulberrypos.ru" "www.mulberrypos.ru"
); );
} catch (RemoteException e) { } catch (RemoteException e) {

View File

@ -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<FragmentPinpadBinding, PinpadViewModel> 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";
}
}

View File

@ -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<String> amount = new ObservableField<>("10.00");
public ObservableField<String> 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";
}
}
}

View File

@ -0,0 +1,48 @@
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android">
<data>
<variable
name="viewModel"
type="com.dspread.pos.ui.pinpad.PinpadViewModel" />
</data>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:gravity="center"
android:padding="16dp">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Waiting Payment..."
android:textSize="20sp"
android:layout_marginBottom="20dp"/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@{viewModel.amount}"
android:textSize="28sp"
android:textStyle="bold"
android:layout_marginBottom="30dp"/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Auto-processing in:"
android:textSize="16sp"
android:layout_marginBottom="10dp"/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@{viewModel.countdown}"
android:textSize="24sp"
android:textStyle="bold"
android:textColor="@android:color/holo_red_dark"/>
</LinearLayout>
</layout>

View File

@ -37,6 +37,11 @@
android:icon="@drawable/scan" android:icon="@drawable/scan"
android:title="@string/cashier" android:title="@string/cashier"
/> />
<item
android:id="@+id/nav_pinpad"
android:icon="@drawable/payment_icon"
android:title="Pinpad"
/>
<item <item
android:id="@+id/nav_setting" android:id="@+id/nav_setting"