Compare commits

...

7 Commits

33 changed files with 1736 additions and 83 deletions

View File

@ -38,7 +38,7 @@
android:name="BUGLY_APPID"
android:value="b2d80aa171" />
<!-- <service android:name="org.eclipse.paho.android.service.MqttService" />-->
<service android:name="org.eclipse.paho.android.service.MqttService" />
<!-- <service android:name="org.eclipse.paho.android.service.MqttService" />-->
<activity
android:name="com.dspread.pos.ui.main.MainActivity"
android:configChanges="orientation|keyboardHidden|screenSize"

View File

@ -54,28 +54,31 @@ public class TerminalApplication extends BaseApplication {
private void initBugly() {
Context context = getApplicationContext();
CrashReport.enableBugly(false);
return;
// Get the current package name
String packageName = context.getPackageName();
// Get the current process name
String processName = DevUtils.getProcessName(android.os.Process.myPid());
// Set whether it is a reporting process
CrashReport.UserStrategy strategy = new CrashReport.UserStrategy(context);
strategy.setUploadProcess(processName == null || processName.equals(packageName));
strategy.setAppVersion(DevUtils.getPackageVersionName(this, packageName));
strategy.setAppPackageName(packageName);
// Initialize Bugly
CrashReport.initCrashReport(context, "b2d80aa171", BuildConfig.DEBUG, strategy);
BuglyLog.setCache(1024 * 10); // 设置本地缓存大小(10KB)
// Set user data
CrashReport.setUserId(DevUtils.getDeviceId(this));
CrashReport.setDeviceModel(this,Build.MODEL);
// Add custom logs
CrashReport.setUserSceneTag(context, 9527); // Set label
CrashReport.putUserData(context, "deviceModel", Build.MODEL);
CrashReport.putUserData(context, "deviceManufacturer", Build.MANUFACTURER);
// String packageName = context.getPackageName();
// // Get the current process name
// String processName = DevUtils.getProcessName(android.os.Process.myPid());
// // Set whether it is a reporting process
// CrashReport.UserStrategy strategy = new CrashReport.UserStrategy(context);
// strategy.setUploadProcess(processName == null || processName.equals(packageName));
// strategy.setAppVersion(DevUtils.getPackageVersionName(this, packageName));
// strategy.setAppPackageName(packageName);
//
// // Initialize Bugly
// CrashReport.initCrashReport(context, "b2d80aa171", BuildConfig.DEBUG, strategy);
// BuglyLog.setCache(1024 * 10); // 设置本地缓存大小(10KB)
//
// // Set user data
// CrashReport.setUserId(DevUtils.getDeviceId(this));
// CrashReport.setDeviceModel(this,Build.MODEL);
//
// // Add custom logs
// CrashReport.setUserSceneTag(context, 9527); // Set label
// CrashReport.putUserData(context, "deviceModel", Build.MODEL);
// CrashReport.putUserData(context, "deviceManufacturer", Build.MANUFACTURER);
}
private void initShiply(){

View File

@ -190,7 +190,20 @@ public class BaseAppViewModel extends BaseViewModel implements WebSocketManager.
}).start();
break;
case "payment":
onPaymentUpdated(json.toString());
new Thread(() -> {
new Handler(Looper.getMainLooper()).post(() ->
onPaymentUpdated(json.toString()));
}).start();
break;
case "redirect":
new Thread(() -> {
new Handler(Looper.getMainLooper()).post(() ->
onPaymentRedirected(json.toString()));
}).start();
break;
case "ping":
// Handle ping requests
@ -206,6 +219,9 @@ public class BaseAppViewModel extends BaseViewModel implements WebSocketManager.
}
}
/**
* Handle ping requests from server
*/
@ -278,6 +294,11 @@ public class BaseAppViewModel extends BaseViewModel implements WebSocketManager.
// Override in child classes for specific update handling
}
protected void onPaymentRedirected(String paymentRedirectData) {
Log.d("BaseAppViewModel", "onPaymentUpdated: " + paymentRedirectData);
// Override in child classes for specific update handling
}
// Add a public ViewModel method
protected void showLoading() {
showDialog();

View File

@ -9,6 +9,7 @@ import android.os.RemoteException;
import com.action.printerservice.PrintStyle;
import com.action.printerservice.barcode.Barcode1D;
import com.action.printerservice.barcode.Barcode2D;
import com.dspread.pos.data.local.PreferencesManager;
import com.dspread.pos.utils.DeviceUtils;
import com.dspread.pos.utils.QRCodeUtil;
import com.dspread.pos.utils.TRACE;
@ -18,6 +19,10 @@ import com.dspread.print.device.PrinterInitListener;
import com.dspread.print.device.bean.PrintLineStyle;
import com.dspread.print.widget.PrintLine;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Locale;
public class PrinterHelper {
protected PrinterDevice mPrinter;
public static PrinterHelper printerCommand;
@ -210,6 +215,142 @@ public class PrinterHelper {
mPrinter.print(context);
}
public void printReceipt(Context context,
PreferencesManager prefs,
double amount,
String currencySymbol,
String paymentMethod,
String printingReciptQRcode,
String printingReciptURL) throws RemoteException {
mPrinter.setPrinterSpeed(5);
mPrinter.setPrinterDensity(2);
// Handle logos - supports both resource IDs and file paths
int mainLogoRes = prefs.getInt("main_logo", -1);
if (mainLogoRes == -1) {
// Load from file path
String path = prefs.getString("main_logo_path", null);
if (path != null) {
Bitmap bitmap = BitmapFactory.decodeFile(path);
if (bitmap != null) {
mPrinter.addBitmap(bitmap);
}
}
} else {
// Regular resource ID - convert resource ID to Bitmap
Bitmap bitmap = BitmapFactory.decodeResource(context.getResources(), mainLogoRes);
if (bitmap != null) {
mPrinter.addBitmap(bitmap);
}
}
// mPrinter.addPrintLintStyle(new PrintLineStyle(PrintStyle.FontStyle.BOLD, PrintLine.CENTER, 20));
// if (bitmap == null) {
// throw new IllegalArgumentException("Bitmap cannot be null");
// }
// mPrinter.printBitmap(context, bitmap);
// mPrinter.addBitmap(bitmap);
// mPrinter.addText("АО Альфа-Банк");
// mPrinter.setLineSpace(8);
mPrinter.addPrintLintStyle(new PrintLineStyle(PrintStyle.FontStyle.NORMAL, PrintLine.CENTER, 14));
// mPrinter.addText("ИНН 1234567890");
// mPrinter.setLineSpace(8);
// mPrinter.addText("8 800 550-91-23");
// mPrinter.setLineSpace(8);
// ================== ADDRESS ==================
// mPrinter.addText("г. Волгоград, пр. Ленина, д. 92");
// ================== RECEIPT DETAILS ==================
mPrinter.addText("ЧЕК №" + 123465);
mPrinter.addText("Кассир: Иванов И.И.");
SimpleDateFormat dateFormat = new SimpleDateFormat("dd.MM.yyyy", Locale.getDefault());
SimpleDateFormat timeFormat = new SimpleDateFormat("HH:mm:ss", Locale.getDefault());
Date currentDate = new Date();
// Get separated date and time strings
String dateString = dateFormat.format(currentDate);
String timeString = timeFormat.format(currentDate);
mPrinter.addTexts(
new String[]{dateString, timeString}, // Actual date and time values
new int[]{1, 1}, // Equal column width
new int[]{PrintStyle.Alignment.NORMAL, PrintStyle.Alignment.ALIGN_OPPOSITE},
new int[]{PrintStyle.FontStyle.BOLD, PrintStyle.FontStyle.BOLD},
14 // Font size
);
mPrinter.addText("------------------------------");
// ================== ITEMS ==================
mPrinter.addTexts(
new String[]{"1. Тест Документ", String.format("%.2f %s", amount, currencySymbol)},
new int[]{2, 1}, // Left column wider than right
new int[]{PrintStyle.Alignment.CENTER, PrintStyle.Alignment.ALIGN_OPPOSITE}
);
mPrinter.addTexts(
new String[]{" НДС 20%", "= " + String.format("%.2f %s", amount * 0.2, currencySymbol)},
new int[]{2, 1},
new int[]{PrintStyle.Alignment.NORMAL, PrintStyle.Alignment.ALIGN_OPPOSITE}
);
// ================== TOTALS ==================
mPrinter.addText("--------------------------------");
mPrinter.addPrintLintStyle(new PrintLineStyle(PrintStyle.FontStyle.BOLD, PrintLine.CENTER, 28));
mPrinter.addTexts(
new String[]{"ИТОГО:", String.format("%.2f %s", amount, currencySymbol)},
new int[]{1, 1}, // Equal column width
new int[]{PrintStyle.Alignment.NORMAL, PrintStyle.Alignment.ALIGN_OPPOSITE},
new int[]{PrintStyle.FontStyle.NORMAL, PrintStyle.FontStyle.BOLD}, 28
);
mPrinter.addPrintLintStyle(new PrintLineStyle(PrintStyle.FontStyle.NORMAL, PrintLine.CENTER, 14));
mPrinter.addTexts(
new String[]{"Форма расчета:", paymentMethod },
new int[]{1, 1}, // Equal column width
new int[]{PrintStyle.Alignment.NORMAL, PrintStyle.Alignment.ALIGN_OPPOSITE}
);
// ================== FISCAL DATA ==================
mPrinter.addText("--------------------------------");
// ================== QR CODE AND CONTACTS ==================
// mPrinter.addBarCode(context, Barcode1D.CODE_128.name(), 400, 100, "datexpay.ru", PrintLine.CENTER);
mPrinter.addQRCode(280, Barcode2D.QR_CODE.name(), printingReciptQRcode, PrintLine.CENTER);
mPrinter.addPrintLintStyle(new PrintLineStyle(PrintStyle.FontStyle.NORMAL, PrintLine.CENTER, 14));
mPrinter.addText(printingReciptURL);
// mPrinter.addBitmap(mulberry);
// Handle logos - supports both resource IDs and file paths
int footerLogoRes = prefs.getInt("footer_logo", -1);
if (footerLogoRes == -1) {
// Load from file path
String path = prefs.getString("footer_logo_path", null);
if (path != null) {
Bitmap bitmap = BitmapFactory.decodeFile(path);
if (bitmap != null) {
mPrinter.addBitmap(bitmap);
}
}
} else {
// Regular resource ID - convert resource ID to Bitmap
Bitmap bitmap = BitmapFactory.decodeResource(context.getResources(), footerLogoRes);
if (bitmap != null) {
mPrinter.addBitmap(bitmap);
}
}
mPrinter.setFooter(80);
//Print the document
mPrinter.print(context);
}
public void getPrinterStatus() throws RemoteException {
mPrinter.getPrinterStatus();
}

View File

@ -0,0 +1,188 @@
package com.dspread.pos.ui.cashier;
import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
import android.text.Editable;
import android.text.TextWatcher;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.ViewGroup;
import androidx.annotation.NonNull;
import androidx.lifecycle.viewmodel.CreationExtras;
import com.dspread.pos.common.base.BaseFragment;
import com.dspread.pos.TitleProviderListener;
import com.dspread.pos_android_app.R;
import com.dspread.pos_android_app.databinding.FragmentCashierBinding;
import com.dspread.pos_android_app.BR;
import android.view.KeyEvent;
import android.view.View;
import android.view.inputmethod.EditorInfo;
import android.widget.EditText;
import android.widget.TextView;
import org.json.JSONObject;
public class CashierFragment extends BaseFragment<FragmentCashierBinding, CashierViewModel >
implements TitleProviderListener{
private EditText etAmountInput;
@Override
public void initViewObservable() {
// Observe payment info LiveData
viewModel.getPaymentInfo().observe(getViewLifecycleOwner(), paymentInfo -> {
if (paymentInfo != null) {
Log.d("CashierFragment", "Payment Info Updated: " + paymentInfo);
}
});
// Observe payment status LiveData
viewModel.getPaymentStatus().observe(getViewLifecycleOwner(), paymentStatus -> {
Log.d("CashierFragment", "Payment Status Updated: " + paymentStatus);
});
}
@Override
public String getTitle() {
return "Cashier";
}
@Override
public int initContentView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
return R.layout.fragment_cashier;
}
@Override
public int initVariableId() {
return BR.viewModel;
}
@Override
public void initData() {
super.initData();
// Setup amount input field
setupAmountInput();
}
private void setupAmountInput() {
// Find the EditText in layout
etAmountInput = binding.getRoot().findViewById(R.id.etAmountInput);
if (etAmountInput != null) {
// Request focus
etAmountInput.requestFocus();
// Add text watcher to update amount in real-time
etAmountInput.addTextChangedListener(new TextWatcher() {
@Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {}
@Override
public void onTextChanged(CharSequence s, int start, int before, int count) {}
@Override
public void afterTextChanged(Editable s) {
String input = s.toString().trim();
if (!input.isEmpty()) {
try {
// Convert cents to dollars and update ViewModel
double dollars = Double.parseDouble(input) / 100.0;
viewModel.amount.set(String.format("%.2f", dollars));
} catch (NumberFormatException e) {
// Handle invalid input
viewModel.amount.set("0.00");
}
} else {
viewModel.amount.set("0.00");
}
}
});
// Set up Enter key listener
etAmountInput.setOnEditorActionListener(new TextView.OnEditorActionListener() {
@Override
public boolean onEditorAction(TextView v, int actionId, KeyEvent event) {
if (actionId == EditorInfo.IME_ACTION_DONE) {
handleEnterPressed();
return true;
}
return false;
}
});
// Handle physical keyboard Enter key
etAmountInput.setOnKeyListener(new View.OnKeyListener() {
@Override
public boolean onKey(View v, int keyCode, KeyEvent event) {
if (event.getAction() == KeyEvent.ACTION_DOWN && keyCode == KeyEvent.KEYCODE_ENTER) {
handleEnterPressed();
return true;
}
return false;
}
});
}
}
private void handleEnterPressed() {
if (etAmountInput != null) {
String amount = etAmountInput.getText().toString().trim();
String deviceIdM50 = "01534090202502210065";
String deviceIdM20 = "01220110202305150514";
if (!amount.isEmpty()) {
try {
// Create JSON signal
JSONObject paymentData = new JSONObject();
paymentData.put("type", "redirect");
paymentData.put("amount", amount);
paymentData.put("currencySymbol", "$");
paymentData.put("id", deviceIdM50);
paymentData.put("status", "signal");
JSONObject paymentDataM20 = new JSONObject();
paymentDataM20.put("type", "redirect");
paymentDataM20.put("amount", amount);
paymentDataM20.put("currencySymbol", "$");
paymentDataM20.put("id", deviceIdM20);
paymentDataM20.put("status", "signal");
// Send via WebSocket
viewModel.sendMessage(paymentData.toString());
viewModel.sendMessage(paymentDataM20.toString());
// Reset input field
etAmountInput.setText("");
} catch (Exception e) {
Log.e("CashierFragment", "Error creating JSON", e);
}
}
}
}
@Override
public void onResume() {
super.onResume();
// Request focus when fragment resumes
if (etAmountInput != null) {
etAmountInput.requestFocus();
}
}
@NonNull
@Override
public CreationExtras getDefaultViewModelCreationExtras() {
return super.getDefaultViewModelCreationExtras();
}
}

View File

@ -0,0 +1,215 @@
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;
public class CashierViewModel extends BaseAppViewModel {
// Enum to represent payment status
public enum PaymentStatus {
WAITING, SUCCESS, FAILED, PRINTER_NOT_AVAILABLE
}
private static final String TAG = "CashierVM";
private final MutableLiveData<String> paymentInfo = new MutableLiveData<>();
public LiveData<String> getPaymentInfo() {
return paymentInfo;
}
public ObservableField<String> amount = new ObservableField<>("0.00");
public ObservableField<String> currencyCode = new ObservableField<>("RUB");
private final MutableLiveData<PaymentStatus> paymentStatus = new MutableLiveData<>(PaymentStatus.WAITING);
public LiveData<PaymentStatus> getPaymentStatus() {
return paymentStatus;
}
public final ObservableField<Integer> mainLogo = 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) {
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, "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) {
Log.d(TAG, "onPaymentRedirected - processing redirect payment");
Log.d(TAG, "jsonString" + jsonString);
try {
JSONObject results = new JSONObject(jsonString);
PreferencesManager prefs = PreferencesManager.getInstance(getApplication());
prefs.saveString("lastPaymentRedirectResults", jsonString);
// Update amount and currency
amount.set(results.optString("amount", "0.00"));
currencyCode.set(results.optString("currencySymbol", "RUB"));;
String status = results.optString("status", "");
switch (status) {
case "success":
Log.d(TAG, "Redirect Payment Success");
paymentStatus.setValue(PaymentStatus.SUCCESS);
printReceipt();
break;
case "failed":
Log.d(TAG, "Redirect Payment Failed");
paymentStatus.setValue(PaymentStatus.FAILED);
break;
default:
Log.w(TAG, "Unknown JSON type: " + status);
paymentStatus.setValue(PaymentStatus.WAITING);
}
} catch (JSONException e) {
Log.e(TAG, "Error parsing JSON", e);
// paymentStatus.setValue(PaymentStatus.FAILED);
}
}
// Add this method to your existing CashierViewModel class:
public void sendMessage(String message) {
try {
sendWebSocketMessage(message);
} catch (Exception e) {
Log.e(TAG, "Error sending WebSocket message", e);
}
}
}

View File

@ -1,6 +1,8 @@
package com.dspread.pos.ui.home;
import android.app.Application;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.util.Log;
import androidx.annotation.NonNull;
@ -9,6 +11,7 @@ import androidx.lifecycle.LiveData;
import com.dspread.pos.common.base.BaseAppViewModel;
import com.dspread.pos.utils.CurrencySymbolUtils;
import com.dspread.pos.utils.PosParameters;
import com.dspread.pos.utils.TRACE;
import com.dspread.pos_android_app.R;
@ -21,7 +24,8 @@ public class HomeViewModel extends BaseAppViewModel {
public ObservableField<String> amount = new ObservableField<>("0.00");
public ObservableField<String> currencyCode = new ObservableField<>("RUB");
public SingleLiveEvent<Long> paymentStartEvent = new SingleLiveEvent<>();
public final ObservableField<Integer> mainLogo = new ObservableField<>();
public final ObservableField<Bitmap> mainLogoBitmap = new ObservableField<>();
public StringBuilder amountBuilder = new StringBuilder();
private static final int MAX_DIGITS = 12; // Maximum amount digits
@ -32,9 +36,44 @@ public class HomeViewModel extends BaseAppViewModel {
currencyCode.set(symbolForCurrency); // Set symbol
// currencyCode.set(prefs.getString("CurrencyCode", "RUB"));
Log.d("HomeViewModel", "symbolForCurrency: " + symbolForCurrency );
// Handle logos - supports both resource IDs and file paths
int mainLogoRes = prefs.getInt("main_logo", -1);
if (mainLogoRes == -1) {
// Load from file path
String path = prefs.getString("main_logo_path", null);
if (path != null) {
Bitmap bitmap = BitmapFactory.decodeFile(path);
if (bitmap != null) {
// Use a custom binding adapter to set bitmap
mainLogoBitmap.set(bitmap);
}
}
} else {
// Regular resource ID
mainLogo.set(mainLogoRes);
}
}
@Override
protected void onConfigurationUpdated() {
int mainLogoRes = prefs.getInt("main_logo", -1);
if (mainLogoRes == -1) {
// Load from file path
String path = prefs.getString("main_logo_path", null);
if (path != null) {
Bitmap bitmap = BitmapFactory.decodeFile(path);
if (bitmap != null) {
// Use a custom binding adapter to set bitmap
mainLogoBitmap.set(bitmap);
}
}
} else {
// Regular resource ID
mainLogo.set(mainLogoRes);
}
}
// Update amount display
private void updateAmountDisplay() {
if (amountBuilder.length() == 0) {
@ -42,7 +81,7 @@ public class HomeViewModel extends BaseAppViewModel {
return;
}
String amountStr = amountBuilder.toString();
// Convert to display an amount with two decimal places
if (amountStr.length() == 1) {

View File

@ -17,8 +17,10 @@ import com.dspread.pos.common.manager.FragmentCacheManager;
import com.dspread.pos.TitleProviderListener;
import com.dspread.pos.common.manager.ScreensaverManager;
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;
@ -148,6 +150,10 @@ public class MainViewModel extends BaseViewModel {
return new PrinterHelperFragment();
case R.id.nav_scan:
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();
}

View File

@ -341,7 +341,7 @@ public class MulberryViewModel extends BaseAppViewModel {
public void onItemClick(MulberryItemViewModel item) {
Log.d(TAG, "Navigation item clicked: " + item.title.get());
sendWebSocketMessage("Navigation item clicked: " + item.title.get());
// sendWebSocketMessage("{\"type\":\"message\", \"content\": \"Navigation item clicked\" }");
if (item.fragmentId != null) {
navigateToFragment.setValue(item.fragmentId);
@ -416,6 +416,8 @@ public class MulberryViewModel extends BaseAppViewModel {
put("NAV_SCAN", R.id.nav_scan);
put("NAV_PRINTER", R.id.nav_printer);
put("NAV_SETTINGS", R.id.nav_setting);
put("NAV_PINPAD", R.id.nav_pinpad);
put("NAV_CASHIER", R.id.nav_cashier);
}};
try {

View File

@ -49,6 +49,7 @@ import java.util.ArrayList;
import java.util.Calendar;
import java.util.Hashtable;
import java.util.List;
import java.util.Locale;
import me.goldze.mvvmhabit.base.BaseActivity;
import me.goldze.mvvmhabit.utils.SPUtils;
@ -61,7 +62,7 @@ import com.dspread.pos_android_app.databinding.WaitingForCardBinding; // Generat
public class PaymentActivity extends BaseActivity<ActivityPaymentBinding, PaymentViewModel> implements PaymentServiceCallback {
private String amount;
private String transactionTypeString;
private String cashbackAmounts;
@ -78,7 +79,7 @@ public class PaymentActivity extends BaseActivity<ActivityPaymentBinding, Paymen
public int initContentView(Bundle savedInstanceState) {
return R.layout.activity_payment;
}
@Override
public int initVariableId() {
return BR.viewModel;
@ -87,14 +88,33 @@ public class PaymentActivity extends BaseActivity<ActivityPaymentBinding, Paymen
private WaitingForCardBinding waitingBinding; // Changed from ActivityWaitingForCardBinding
private WaitingForCardViewModel waitingViewModel;
private boolean QRMood = false;
private boolean FaceIDMood = false;
@Override
public void initData() {
// Debug current locale
Locale currentLocale = getResources().getConfiguration().locale;
Log.d("LanguageDebug", "Current locale: " + currentLocale.getLanguage());
Log.d("LanguageDebug", "Current country: " + currentLocale.getCountry());
// Check available locales
Locale[] availableLocales = Locale.getAvailableLocales();
for (Locale locale : availableLocales) {
if (locale.getLanguage().equals("ru")) {
Log.d("LanguageDebug", "Russian locale available: " + locale);
}
}
// Test string retrieval
String testString = getString(R.string.menu_payment);
Log.d("LanguageDebug", "Retrieved string: " + testString);
logFileConfig = LogFileConfig.getInstance(this);
QPOSCallbackManager.getInstance().registerPaymentCallback(this);
binding.setVariable(BR.viewModel, viewModel);
viewModel.setmContext(this);
binding.pinpadEditText.setText("");
viewModel.titleText.set("Оплата");
viewModel.titleText.set(getString(R.string.menu_payment));
Intent intent = getIntent();
if (intent != null) {
amount = intent.getStringExtra("amount");
@ -139,6 +159,41 @@ public class PaymentActivity extends BaseActivity<ActivityPaymentBinding, Paymen
startTransaction();
}
private void simulatePaymentAfterDelay() {
boolean isTestMode = true; // Set this to false when not testing
if (isTestMode) {
// Use runOnUiThread to ensure we're on the main thread
runOnUiThread(new Runnable() {
@Override
public void run() {
new android.os.Handler().postDelayed(
new Runnable() {
@Override
public void run() {
TRACE.d("SIMULATION: Simulating successful payment");
String mockTlv = "9A03240515" + // Transaction date: 2024-05-15
"9C00" + // Transaction type
"5F2A0156" + // Currency code: 156 (CNY)
"9F0206000000001000" + // Amount: 10.00
"95050000000000" + // TVR
"9F3403000000" + // CVM results
"9F270100"; // CID data
// Simulate the onRequestBatchData callback
onRequestBatchData(mockTlv);
// Enable print button
binding.btnSendReceipt.setEnabled(true);
binding.btnSendReceipt.setVisibility(View.VISIBLE);
}
},
200 // 10 second delay
);
}
});
}
}
private String generatePaymentData() {
// Generate payment data for QR code
// Create JSON structure with relevant payment info
@ -190,8 +245,9 @@ public class PaymentActivity extends BaseActivity<ActivityPaymentBinding, Paymen
POS.getInstance().sendOnlineProcessResult("8A023030");
} else {
if (DeviceUtils.isPrinterDevices()) {
handleSendReceipt();
// handleSendReceipt();
}
handleSendReceipt();
viewModel.setTransactionSuccess();
}
} else {
@ -207,12 +263,26 @@ public class PaymentActivity extends BaseActivity<ActivityPaymentBinding, Paymen
startTransaction();
}
});
// Observe QR mode
viewModel.getQrMode().observe(this, qrEnabled -> {
QRMood = Boolean.TRUE.equals(qrEnabled);
});
// Observe FaceID mode
viewModel.getFaceIDMode().observe(this, faceIDEnabled -> {
FaceIDMood = Boolean.TRUE.equals(faceIDEnabled);
});
//Stop DoTrade
viewModel.StopReadingCard.observe(this, aBoolean -> {
Log.d("PaymentAct", "StopReadingCard: " + aBoolean);
if (aBoolean) {
// Unregister the callback
QPOSCallbackManager.getInstance().unregisterPaymentCallback();
// QPOSCallbackManager.getInstance().unregisterPaymentCallback();
POS.getInstance().cancelTrade();
}
});
// Add this observer for receipt content
@ -456,12 +526,20 @@ public class PaymentActivity extends BaseActivity<ActivityPaymentBinding, Paymen
public void onRequestTransactionResult(QPOSService.TransactionResult transactionResult) {
TRACE.d("onRequestTransactionResult()" + transactionResult.toString());
isChangePin = false;
runOnUiThread(() -> {
String msg = HandleTxnsResultUtils.getTransactionResultMessage(transactionResult, PaymentActivity.this);
if (!msg.isEmpty()) {
if (QRMood || FaceIDMood){
simulatePaymentAfterDelay();
}
else {
runOnUiThread(() -> {
String msg = HandleTxnsResultUtils.getTransactionResultMessage(transactionResult, PaymentActivity.this);
if (!msg.isEmpty()) {
viewModel.setTransactionFailed(msg);
}
});
}
});
}
}
@Override
@ -556,6 +634,7 @@ public class PaymentActivity extends BaseActivity<ActivityPaymentBinding, Paymen
TRACE.d("onTradeCancelled");
runOnUiThread(() -> {
// viewModel.setTransactionFailed("Transaction is canceled!");
finish();
});
}

View File

@ -15,6 +15,9 @@ import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.databinding.ObservableBoolean;
import androidx.databinding.ObservableField;
import androidx.lifecycle.LiveData;
import androidx.lifecycle.MutableLiveData;
import androidx.lifecycle.ViewModel;
import com.dspread.pos.common.base.BaseAppViewModel;
import com.dspread.pos.common.http.RetrofitClient;
@ -37,6 +40,7 @@ import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import io.reactivex.android.schedulers.AndroidSchedulers;
import io.reactivex.functions.Consumer;
@ -70,6 +74,10 @@ public class PaymentViewModel extends BaseAppViewModel {
public SingleLiveEvent<Boolean> isOnlineSuccess = new SingleLiveEvent();
public SingleLiveEvent<Boolean> isContinueTrx = new SingleLiveEvent();
public SingleLiveEvent<Boolean> StopReadingCard = new SingleLiveEvent();
// Add these for QR and FaceID modes
private final MutableLiveData<Boolean> qrMode = new MutableLiveData<>(false);
private final MutableLiveData<Boolean> faceIDMode = new MutableLiveData<>(false);
public ObservableBoolean showPinpad = new ObservableBoolean(false);
public ObservableBoolean showResultStatus = new ObservableBoolean(false);
public ObservableField<String> receiptContent = new ObservableField<>();
@ -81,6 +89,30 @@ public class PaymentViewModel extends BaseAppViewModel {
this.mContext = mContext;
}
// Public getters for the LiveData
public LiveData<Boolean> getQrMode() {
return qrMode;
}
public LiveData<Boolean> getFaceIDMode() {
return faceIDMode;
}
// Methods to set the modes
public void setQrMode(boolean enabled) {
qrMode.setValue(enabled);
}
public void setFaceIDMode(boolean enabled) {
faceIDMode.setValue(enabled);
}
// Reset both modes to false (call this when activity starts)
public void resetModes() {
qrMode.setValue(false);
faceIDMode.setValue(false);
}
public void simulateSuccessfulPayment() {
Log.d("PaymentVM", "simulateSuccessfulPayment ... ");
setTransactionSuccess("Simulated successful payment");
@ -117,8 +149,9 @@ public class PaymentViewModel extends BaseAppViewModel {
case "success":
Log.d(TAG, "QR Payment Success");
// Unregister callback through the activity
StopReadingCard.setValue(true);// This will trigger the activity to unregister
simulateSuccessfulPayment();
setQrMode(true);
StopReadingCard.setValue(true);
// simulateSuccessfulPayment();
break;
case "failed":
Log.d(TAG, "QR Payment Failed");
@ -127,11 +160,11 @@ public class PaymentViewModel extends BaseAppViewModel {
break;
default:
Log.w(TAG, "Unknown JSON type: " + status);
StopReadingCard.setValue(true);// This will trigger the activity to unregister
// StopReadingCard.setValue(true);// This will trigger the activity to unregister
}
} catch (JSONException e) {
Log.e("PaymentVM", "Error parsing JSON", e);
StopReadingCard.setValue(true); // This will trigger the activity to unregister
// StopReadingCard.setValue(true); // This will trigger the activity to unregister
}
}
@ -174,7 +207,7 @@ public class PaymentViewModel extends BaseAppViewModel {
}
public void setAmount(String newAmount) {
amount.set("¥" + newAmount);
amount.set( newAmount);
}
public void setWaitingStatus(boolean isWaitings){
@ -202,9 +235,25 @@ public class PaymentViewModel extends BaseAppViewModel {
loadingText.set("");
}
public static Double convertCentsToDollars(String amountValue) {
if (amountValue == null || amountValue.isEmpty()) {
return 0.0; // Default value if the input is null or empty
}
try {
return Double.parseDouble(amountValue) / 100.0; // Convert cents to dollars
} catch (NumberFormatException e) {
e.printStackTrace();
return 0.0; // Default value if parsing fails
}
}
public BindingCommand continueTxnsCommand = new BindingCommand(new BindingAction() {
@Override
public void call() {
// paymentData.put("status", "failed");
// sendWebSocketMessage(paymentData.toString());
// titleText.set("Payment");
// stopLoading();
// showPinpad.set(false);
@ -219,31 +268,85 @@ public class PaymentViewModel extends BaseAppViewModel {
public BindingCommand sendReceiptCommand = new BindingCommand(new BindingAction() {
@Override
public void call() {
isPrinting.set(true);
PrinterManager instance = PrinterManager.getInstance();
PrinterDevice mPrinter = instance.getPrinter();
PrinterHelper.getInstance().setPrinter(mPrinter);
PrinterHelper.getInstance().initPrinter(mContext);
TRACE.i("bitmap = "+receiptBitmap);
new Handler().postDelayed(() -> {
try {
PrinterHelper.getInstance().printBitmap(getApplication(),receiptBitmap);
} catch (RemoteException e) {
throw new RuntimeException(e);
String CurrentID = SPUtils.getInstance().getString("posID");
// Extract the String value from the ObservableField
String amountValue = amount != null ? amount.get() : "0";
String currencySymbol = SPUtils.getInstance().getString("symbolForCurrency", "$");
// Convert the amount from cents to dollars using the utility function
Double amountDouble = convertCentsToDollars(amountValue);
// Pinpads device list
String deviceIdM50 = "01534090202502210065";
String deviceIdM20 = "01220110202305150514";
// Cashier devices
String deviceIdM70 = "01723060202412010160";
if (Objects.equals(CurrentID, deviceIdM50)|| Objects.equals(CurrentID, deviceIdM20)){
com.alibaba.fastjson.JSONObject paymentData = new com.alibaba.fastjson.JSONObject();
paymentData.put("type", "redirect");
paymentData.put("amount", amountDouble);
paymentData.put("currencySymbol", currencySymbol);
paymentData.put("id", deviceIdM70);
paymentData.put("status", "success");
Log.d(TAG, "call: this is M50 maybe");
sendWebSocketMessage(paymentData.toString());
}else {
isPrinting.set(true);
PrinterManager instance = PrinterManager.getInstance();
PrinterDevice mPrinter = instance.getPrinter();
PrinterHelper.getInstance().setPrinter(mPrinter);
PrinterHelper.getInstance().initPrinter(mContext);
Boolean isQR = getQrMode().getValue();
Boolean isFaceID = getFaceIDMode().getValue();
String paymentMethod;
if (isQR != null && isQR) {
paymentMethod = "QR Code";
} else if (isFaceID != null && isFaceID) {
paymentMethod = "Face ID";
} else {
paymentMethod = "Крата";
}
PrinterHelper.getInstance().getmPrinter().setPrintListener(new PrintListener() {
@Override
public void printResult(boolean b, String s, PrinterDevice.ResultType resultType) {
if(b){
ToastUtils.showShort("Print Finished!");
}else {
ToastUtils.showShort("Print Result: "+s);
}
isPrinting.set(false);
finish();
TRACE.i("bitmap = " + receiptBitmap);
new Handler().postDelayed(() -> {
try {
// PrinterHelper.getInstance().printBitmap(getApplication(),receiptBitmap);
assert amountValue != null;
PrinterHelper.getInstance().printReceipt(
getApplication(), prefs,
amountDouble,
currencySymbol,
paymentMethod,
"www.mulberrypos.ru",
"www.mulberrypos.ru"
);
} catch (RemoteException e) {
throw new RuntimeException(e);
}
});
},100);
PrinterHelper.getInstance().getmPrinter().setPrintListener(new PrintListener() {
@Override
public void printResult(boolean b, String s, PrinterDevice.ResultType resultType) {
if (b) {
// ToastUtils.showShort("Print Finished!");
Log.d(TAG, "printResult: Finished");
} else {
// ToastUtils.showShort("Print Result: "+s);
Log.d(TAG, "printResult: Result" + s);
}
isPrinting.set(false);
// finish();
}
});
}, 100);
} // else if not M50
}
});

View File

@ -8,6 +8,9 @@ import androidx.annotation.NonNull;
import androidx.databinding.ObservableBoolean;
import androidx.databinding.ObservableField;
import com.dspread.pos.common.manager.QPOSCallbackManager;
import com.dspread.pos.printerAPI.PrinterHelper;
import com.dspread.pos.utils.LogFileConfig;
import com.dspread.pos.utils.QRCodeGenerator;
import me.goldze.mvvmhabit.base.BaseViewModel;
@ -26,11 +29,23 @@ public class WaitingForCardViewModel extends BaseViewModel {
public void setAmount(String amount) {
this.amount.set(amount);
Double amountDouble = convertCentsToDollars(amount);
this.amount.set(String.valueOf(amountDouble));
}
public void setCurrencySymbol(String symbol) {
this.currencySymbol.set(symbol);
}
public static Double convertCentsToDollars(String amountValue) {
if (amountValue == null || amountValue.isEmpty()) {
return 0.0; // Default value if the input is null or empty
}
try {
return Double.parseDouble(amountValue) / 100.0; // Convert cents to dollars
} catch (NumberFormatException e) {
e.printStackTrace();
return 0.0; // Default value if parsing fails
}
}
public void generateQRCode(String paymentData) {
try {
Bitmap qrCode = QRCodeGenerator.generateQRCode(paymentData, 500, 500);
@ -49,4 +64,14 @@ public class WaitingForCardViewModel extends BaseViewModel {
// Handle cancel action
Log.d("WaitingForCardViewModel", "onCancel: ");
}
@Override
public void onDestroy() {
Log.d("WaitingCardVM", "onDestroy: ");
// super.onDestroy();
// LogFileConfig.getInstance(this).readLog();
// QPOSCallbackManager.getInstance().unregisterPaymentCallback();
// PrinterHelper.getInstance().close();
}
}

View File

@ -0,0 +1,117 @@
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;
@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);
}
// REMOVED: Auto-navigation timer - we'll use signal instead
}
private void initTimer() {
showTimer = new CountDownTimer(500, 500) {
@Override
public void onTick(long millisUntilFinished) {}
@Override
public void onFinish() {
canShow = true;
}
};
}
private void navigateToPayment(String amount) {
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: " + amount);
Intent intent = new Intent(getActivity(), PaymentActivity.class);
intent.putExtra("amount", amount);
startActivity(intent);
// Optional: add animation
getActivity().overridePendingTransition(android.R.anim.fade_in, android.R.anim.fade_out);
}
@Override
public void initViewObservable() {
// Observe the payment start event (signal-based)
viewModel.paymentStartEvent.observe(this, amount -> {
Log.d(TAG, "Payment start signal received with amount: " + amount);
navigateToPayment(amount);
});
}
@Override
public void onDestroy() {
super.onDestroy();
Log.d(TAG, "onDestroy called");
if (showTimer != null) {
showTimer.cancel();
}
}
@Override
public String getTitle() {
return "Pin Pad";
}
}

View File

@ -0,0 +1,97 @@
package com.dspread.pos.ui.pinpad;
import android.app.Application;
import android.util.Log;
import androidx.annotation.NonNull;
import androidx.databinding.ObservableField;
import com.dspread.pos.common.base.BaseAppViewModel;
import me.goldze.mvvmhabit.bus.event.SingleLiveEvent;
import org.json.JSONException;
import org.json.JSONObject;
public class PinpadViewModel extends BaseAppViewModel {
private static final String TAG = "PinpadVM";
public ObservableField<String> currencyCode = new ObservableField<>("RUB");
public ObservableField<String> amount = new ObservableField<>("10.00");
public ObservableField<String> countdown = new ObservableField<>("10");
// Add this SingleLiveEvent for signal-based navigation
public SingleLiveEvent<String> paymentStartEvent = new SingleLiveEvent<>();
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";
}
}
@Override
protected void onPaymentRedirected(String jsonString) {
Log.d(TAG, "onPaymentRedirected - processing redirect payment");
Log.d(TAG, "jsonString" + jsonString);
try {
JSONObject results = new JSONObject(jsonString);
String status = results.optString("status", "");
switch (status) {
case "signal":
Log.d(TAG, "Signal Redirect Payment received");
// Get the amount from the signal (or use the current amount)
String signalAmount = results.optString("amount", null);
if (signalAmount != null) {
amount.set(formatAmount(signalAmount));
}
// Trigger navigation via the event
String amountToPass = results.optString("rawAmount", getRawAmount());
paymentStartEvent.postValue(amountToPass);
break;
default:
Log.w(TAG, "Unknown JSON type: " + status);
}
} catch (JSONException e) {
Log.e(TAG, "Error parsing JSON", e);
}
}
// Helper method to get raw amount (without formatting)
private String getRawAmount() {
try {
// Convert formatted amount back to raw cents
String formatted = amount.get();
if (formatted != null) {
double amountDouble = Double.parseDouble(formatted);
return String.valueOf((int)(amountDouble * 100));
}
} catch (NumberFormatException e) {
Log.e(TAG, "Error converting amount back to raw", e);
}
return "1000"; // default fallback
}
// Method to manually trigger payment (if needed)
public void triggerPayment() {
paymentStartEvent.postValue(getRawAmount());
}
}

View File

@ -169,13 +169,13 @@ public class ConnectionSettingsFragment extends BaseFragment<FragmentConnectionS
// 1. Update app configuration
// 2. Restart activity to apply changes
Log.d("RESTART", "updateAppLanguage: " + languageCode);
// Locale locale = new Locale(languageCode);
// Locale.setDefault(locale);
// Configuration config = new Configuration();
// config.locale = locale;
// getResources().updateConfiguration(config, getResources().getDisplayMetrics());
//
// // Restart activity
Locale locale = new Locale(languageCode);
Locale.setDefault(locale);
Configuration config = new Configuration();
config.locale = locale;
getResources().updateConfiguration(config, getResources().getDisplayMetrics());
// Restart activity
// Intent refresh = new Intent(getActivity(), getActivity().getClass());
// startActivity(refresh);
// getActivity().finish();
@ -183,6 +183,7 @@ public class ConnectionSettingsFragment extends BaseFragment<FragmentConnectionS
@Override
public String getTitle() {
return "Settings";
return "Settings" ;
}
}

View File

@ -0,0 +1,45 @@
package com.dspread.pos.utils;
import android.content.Context;
import android.content.res.Configuration;
import android.content.res.Resources;
import android.os.Build;
import android.util.Log;
import java.util.Locale;
public class LocaleHelper {
private static final String TAG = "LocaleHelper";
public static Context setLocale(Context context, String languageCode) {
Log.d(TAG, "Setting locale to: " + languageCode);
Locale locale = new Locale(languageCode);
Locale.setDefault(locale);
Resources resources = context.getResources();
Configuration configuration = new Configuration(resources.getConfiguration());
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
configuration.setLocale(locale);
} else {
configuration.locale = locale;
}
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
return context.createConfigurationContext(configuration);
} else {
resources.updateConfiguration(configuration, resources.getDisplayMetrics());
return context;
}
}
public static String getLanguage(Context context) {
Configuration config = context.getResources().getConfiguration();
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
return config.getLocales().get(0).getLanguage();
} else {
return config.locale.getLanguage();
}
}
}

View File

@ -37,6 +37,8 @@ public class PosParameters {
public static final int NAV_SCAN = R.id.nav_scan;
public static final int NAV_PRINTER = R.id.nav_printer;
public static final int NAV_SETTINGS = R.id.nav_setting;
public static final int NAV_PINPAD = R.id.nav_pinpad;
public static final int NAV_CASHIER = R.id.nav_cashier;
// Default values
private static final int DEFAULT_MAIN_LOGO = R.drawable.am_mulberry_logo_wide_color;
@ -105,6 +107,8 @@ public class PosParameters {
NAV_ITEMS.put(NAV_SCAN, new ArrayList<>());
NAV_ITEMS.put(NAV_PRINTER, new ArrayList<>());
NAV_ITEMS.put(NAV_SETTINGS, new ArrayList<>());
NAV_ITEMS.put(NAV_PINPAD, new ArrayList<>());
NAV_ITEMS.put(NAV_CASHIER, new ArrayList<>());
Log.d(TAG, "Initialized empty NAV_ITEMS");
}
@ -261,15 +265,19 @@ public class PosParameters {
map.put("NAV_SCAN", NAV_SCAN);
map.put("NAV_PRINTER", NAV_PRINTER);
map.put("NAV_SETTINGS", NAV_SETTINGS);
map.put("NAV_PINPAD", NAV_PINPAD);
map.put("NAV_CASHIER", NAV_CASHIER);
return map;
}
private static Map<String, Integer> createIconResourceMap() {
Map<String, Integer> map = new HashMap<>();
map.put("home", R.drawable.am_card_pay_black);
map.put("scan", R.drawable.am_barcode_black);
map.put("printer", R.drawable.am_receipt_cart_black);
map.put("settings", R.drawable.am_settings_black);
map.put("home", R.drawable.ax_payment_grey);
map.put("scan", R.drawable.ax_scaner_grey);
map.put("printer", R.drawable.ax_receipt_grey);
map.put("settings", R.drawable.ax_settings_grey);
map.put("pinpad", R.drawable.ax_menu_grey);
map.put("cashier", R.drawable.ax_cashier_grey);
map.put("mulberry", R.drawable.am_mulberry_logo_wide_color);
map.put("overtec", R.drawable.am_overtec_logo_wide_color);
map.put("datexpay", R.drawable.am_datexpay_logo_wide_color);

View File

@ -0,0 +1,25 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:aapt="http://schemas.android.com/aapt"
android:width="237dp"
android:height="237dp"
android:viewportWidth="237"
android:viewportHeight="237">
<path
android:pathData="M80.75,156.13L156.13,80.75M156.25,156.13L80.75,80.75M231.75,118.5C231.75,181.05 181.05,231.75 118.5,231.75C55.95,231.75 5.25,181.05 5.25,118.5C5.25,55.95 55.95,5.25 118.5,5.25C181.05,5.25 231.75,55.95 231.75,118.5Z"
android:strokeLineJoin="round"
android:strokeWidth="10"
android:fillColor="#00000000"
android:strokeLineCap="round">
<aapt:attr name="android:strokeColor">
<gradient
android:startX="118.5"
android:startY="5.25"
android:endX="118.5"
android:endY="231.75"
android:type="linear">
<item android:offset="0" android:color="#FFACAFCA"/>
<item android:offset="1" android:color="#FF44454E"/>
</gradient>
</aapt:attr>
</path>
</vector>

View File

@ -0,0 +1,25 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:aapt="http://schemas.android.com/aapt"
android:width="244dp"
android:height="176dp"
android:viewportWidth="244"
android:viewportHeight="176">
<path
android:pathData="M5.75,60.55H238.13M5.75,45.55C5.75,31.55 5.75,24.55 8.5,19.18C10.88,14.8 14.5,11.05 19,8.68C24.25,5.93 31.38,5.93 45.38,5.93H198.5C212.5,5.93 219.5,5.93 224.88,8.68C229.25,11.05 233,14.68 235.38,19.18C238.13,24.43 238.13,31.55 238.13,45.55V130.43C238.13,144.43 238.13,151.43 235.38,156.8C233,161.18 229.38,164.93 224.88,167.3C219.63,170.05 212.5,170.05 198.5,170.05H45.38C31.38,170.05 24.38,170.05 19,167.3C14.63,164.93 10.88,161.3 8.5,156.8C5.75,151.55 5.75,144.43 5.75,130.43V45.55ZM53.5,129.05C53.5,132.78 50.48,135.8 46.75,135.8C43.02,135.8 40,132.78 40,129.05C40,125.33 43.02,122.3 46.75,122.3C50.48,122.3 53.5,125.33 53.5,129.05Z"
android:strokeLineJoin="round"
android:strokeWidth="10"
android:fillColor="#00000000"
android:strokeLineCap="round">
<aapt:attr name="android:strokeColor">
<gradient
android:startX="121.94"
android:startY="5.93"
android:endX="121.94"
android:endY="170.05"
android:type="linear">
<item android:offset="0" android:color="#FFACAFCA"/>
<item android:offset="1" android:color="#FF44454E"/>
</gradient>
</aapt:attr>
</path>
</vector>

View File

@ -0,0 +1,147 @@
<vector xmlns:aapt="http://schemas.android.com/aapt" xmlns:android="http://schemas.android.com/apk/res/android" android:height="240dp" android:viewportHeight="512" android:viewportWidth="512" android:width="240dp">
<path android:pathData="M287,44.8c-2.8,1 -6.8,4 -10.5,7.7 -9.2,9.2 -9.5,10.5 -9.5,43.1 0,31.5 0.5,34.4 7.8,42.6 6.9,7.6 12.7,10.1 24.5,10.6l9.7,0.4 0,5.4 0,5.4 -26.5,-0 -26.5,-0 0,-39.1 0,-39.1 -3.4,-3.4c-2.8,-2.8 -4.2,-3.4 -7.6,-3.4 -4,-0 -4.8,0.6 -12.6,8.2l-8.4,8.2 -8.4,-8.2c-8,-7.9 -8.5,-8.2 -13,-8.2 -4.5,-0 -5,0.3 -13.1,8.2l-8.5,8.1 -8.4,-8.1c-7.6,-7.4 -8.7,-8.2 -12.3,-8.2 -2.1,-0 -4.9,0.7 -6.2,1.6 -4.9,3.4 -5.1,5.6 -5.1,45.9l0,37.5 -5.2,-0c-15.7,-0 -31.9,10.3 -38.3,24.3 -1.7,3.7 -8,32.9 -21.1,97.7 -10.2,50.9 -18.6,92.5 -18.7,92.5 -0.1,-0 -2.8,1.3 -6,2.9 -6.8,3.4 -12,9.2 -14.8,16.6 -1.7,4.6 -1.9,7.9 -1.9,37.1l0,32.1 2.9,2.9 2.9,2.9 207.2,-0 207.2,-0 2.9,-2.9 2.9,-2.9 0,-32.1c0,-30 -0.1,-32.4 -2.1,-37.6 -2.5,-6.8 -9.8,-14.4 -16.2,-17.1l-4.5,-1.8 -4.1,-20.1c-2.2,-11 -10.6,-52.6 -18.6,-92.4 -8,-39.8 -15.4,-74.4 -16.5,-76.8 -5.2,-11.7 -15.3,-20.5 -27.5,-23.8l-6.6,-1.8 0.3,-5.1 0.3,-5.1 10,-0.6c7.6,-0.4 11,-1.1 14.3,-2.8 6.5,-3.3 11.8,-8.7 14.9,-14.9l2.8,-5.7 0,-29.5 0,-29.5 -2.8,-5.7c-3,-6.2 -8.3,-11.5 -14.9,-15l-4.3,-2.3 -51,-0.2c-45.8,-0.2 -51.5,-0 -55.5,1.5zM389.1,65.6c5.3,2.7 5.9,5.9 5.9,30.4 0,24.6 -0.6,27.7 -5.9,30.5 -2.7,1.4 -8.6,1.5 -49.1,1.3l-46,-0.3 -2.7,-2.8 -2.8,-2.7 0,-25.9c0,-24.5 0.1,-26 2,-28.1 1.1,-1.2 2.9,-2.6 4,-3.1 1.1,-0.4 22.2,-0.8 46.8,-0.8 38.8,-0.1 45.2,0.1 47.8,1.5zM211.6,109c7.5,7.3 8.5,8 12.2,8 2.9,-0 4.9,-0.7 7,-2.5 1.7,-1.4 3.3,-2.5 3.6,-2.5 0.3,-0 0.5,20.4 0.4,45.2l-0.3,45.3 -31.7,0.3 -31.8,0.2 0,-45.6 0,-45.6 3.1,2.6c2.3,1.9 4.2,2.6 7.5,2.6 4.2,-0 4.9,-0.4 12.7,-8 4.5,-4.4 8.4,-8 8.6,-8 0.3,-0 4.2,3.6 8.7,8zM352,154.5l0,5.5 -10.5,-0 -10.5,-0 0,-5.5 0,-5.5 10.5,-0 10.5,-0 0,5.5zM149,192c0,10.5 -0.1,11 -2.1,11 -1.1,-0 -3.4,1.3 -5,2.9 -4.7,4.8 -3.7,13 2.1,16.4 2.5,1.5 8.3,1.7 58.5,1.7 61.9,-0 60.3,0.2 63.1,-6.7 1.2,-2.7 1.3,-4.2 0.4,-6.9 -1.3,-3.7 -5.3,-7.4 -8.1,-7.4 -1.7,-0 -1.9,-0.9 -1.9,-11l0,-11 56.6,-0c37.9,-0 58,0.4 60.9,1.1 5.5,1.4 11.6,6.5 13.8,11.6 0.9,2.1 9.3,42.3 18.7,89.3 9.4,47 17.4,86.5 17.7,87.7l0.5,2.3 -168.2,-0 -168.2,-0 0.5,-2.3c0.3,-1.2 8.3,-40.7 17.7,-87.7 9.4,-47 17.5,-87 18.1,-88.7 0.6,-1.8 3.1,-5 5.5,-7.2 4.6,-4.1 9.2,-5.8 15.7,-6l3.7,-0.1 0,11zM442.3,396.1c3.9,1.8 5.7,6 5.7,13.3l0,6.6 -192,-0 -192,-0 0,-6.6c0,-7.1 1.8,-11.5 5.4,-13.3 3,-1.4 369.7,-1.5 372.9,-0zM448,442.5l0,5.5 -192,-0 -192,-0 0,-5.5 0,-5.5 192,-0 192,-0 0,5.5z" android:strokeColor="#00000000">
<aapt:attr name="android:fillColor">
<gradient android:endX="43" android:endY="469" android:startX="43" android:startY="43.24" android:type="linear">
<item android:color="#FFACAFCA" android:offset="0"/>
<item android:color="#FF44454E" android:offset="1"/>
</gradient>
</aapt:attr>
</path>
<path android:pathData="M315.3,86.6c-2.9,1.5 -6.3,6.5 -6.3,9.4 0,3 3.4,8 6.5,9.4 4.6,2.2 45.8,2.3 50.9,0.2 4.3,-1.8 6.6,-5.1 6.6,-9.6 0,-4.5 -2.3,-7.8 -6.6,-9.6 -5.1,-2.1 -46.7,-2 -51.1,0.2z" android:strokeColor="#00000000">
<aapt:attr name="android:fillColor">
<gradient android:endX="309" android:endY="107.11" android:startX="309" android:startY="84.89" android:type="linear">
<item android:color="#FFACAFCA" android:offset="0"/>
<item android:color="#FF44454E" android:offset="1"/>
</gradient>
</aapt:attr>
</path>
<path android:pathData="M152.4,246.4c-6.4,1.6 -11.1,4.4 -16.1,9.9 -7.4,8 -7.8,10.3 -7.8,43.2 0,28.1 0.1,29.1 2.3,33.5 3.5,7.2 7.5,11.4 14.4,15.1l6.3,3.4 51,-0 51,-0 5.7,-2.8c6.2,-3 11.5,-8.3 15,-14.9 2.2,-4.2 2.3,-5.3 2.6,-33.4 0.3,-27.5 0.2,-29.3 -1.8,-34.7 -2.7,-7.1 -9.2,-14 -16.8,-17.5l-5.7,-2.7 -47.5,-0.2c-35.4,-0.1 -48.8,0.1 -52.6,1.1zM250.3,268.1c5.3,2.4 5.7,4.7 5.7,30.4 0,19.5 -0.3,24.1 -1.6,26.6 -2.9,5.8 -4,5.9 -52,5.9 -48.5,-0 -48.8,-0 -51.8,-6.5 -2.2,-4.6 -2.3,-45.8 -0.2,-50.9 2.8,-6.8 1.1,-6.6 51.8,-6.6 30.7,-0 46.5,0.4 48.1,1.1z" android:strokeColor="#00000000">
<aapt:attr name="android:fillColor">
<gradient android:endX="128.5" android:endY="351.5" android:startX="128.5" android:startY="245.28" android:type="linear">
<item android:color="#FFACAFCA" android:offset="0"/>
<item android:color="#FF44454E" android:offset="1"/>
</gradient>
</aapt:attr>
</path>
<path android:pathData="M294.3,246.5c-3.3,1.4 -6.3,5.9 -6.3,9.5 0,3.7 3,8.1 6.5,9.6 5,2 25.6,1.9 30,-0.2 3.1,-1.4 6.5,-6.4 6.5,-9.4 0,-3 -3.4,-8 -6.5,-9.4 -4.4,-2.1 -25.5,-2.2 -30.2,-0.1z" android:strokeColor="#00000000">
<aapt:attr name="android:fillColor">
<gradient android:endX="288" android:endY="267.04" android:startX="288" android:startY="244.97" android:type="linear">
<item android:color="#FFACAFCA" android:offset="0"/>
<item android:color="#FF44454E" android:offset="1"/>
</gradient>
</aapt:attr>
</path>
<path android:pathData="M347.3,246.6c-2.9,1.5 -6.3,6.5 -6.3,9.4 0,3 3.4,8 6.5,9.4 4.4,2.1 25,2.2 30,0.2 3.5,-1.5 6.5,-5.9 6.5,-9.6 0,-3.7 -3,-8.1 -6.5,-9.6 -4.9,-2 -25.9,-1.9 -30.2,0.2z" android:strokeColor="#00000000">
<aapt:attr name="android:fillColor">
<gradient android:endX="341" android:endY="267.04" android:startX="341" android:startY="244.96" android:type="linear">
<item android:color="#FFACAFCA" android:offset="0"/>
<item android:color="#FF44454E" android:offset="1"/>
</gradient>
</aapt:attr>
</path>
<path android:pathData="M295,288.7c-5.7,2.1 -8.5,9.4 -5.5,14.8 2.5,4.8 5,5.5 20.4,5.5 12.4,-0 14.1,-0.2 16.4,-2 3.8,-3 5.2,-8 3.3,-12.3 -2.3,-5.7 -5.5,-6.7 -20.3,-6.6 -7,-0 -13.5,0.3 -14.3,0.6z" android:strokeColor="#00000000">
<aapt:attr name="android:fillColor">
<gradient android:endX="288.28" android:endY="309" android:startX="288.28" android:startY="288.09" android:type="linear">
<item android:color="#FFACAFCA" android:offset="0"/>
<item android:color="#FF44454E" android:offset="1"/>
</gradient>
</aapt:attr>
</path>
<path android:pathData="M346.4,289.9c-4.4,2.7 -6,7.7 -4.1,12.4 2.6,6.1 4.5,6.7 19.9,6.7 8,-0 14.7,-0.5 16.2,-1.1 6,-2.8 7.1,-12.2 2,-17 -2.4,-2.3 -3.3,-2.4 -16.7,-2.7 -12.7,-0.2 -14.5,-0.1 -17.3,1.7z" android:strokeColor="#00000000">
<aapt:attr name="android:fillColor">
<gradient android:endX="341.51" android:endY="309" android:startX="341.51" android:startY="288.13" android:type="linear">
<item android:color="#FFACAFCA" android:offset="0"/>
<item android:color="#FF44454E" android:offset="1"/>
</gradient>
</aapt:attr>
</path>
<path android:pathData="M293.3,332.2c-5.7,2.8 -6.7,12.2 -1.7,16.9 2.4,2.3 3.4,2.4 16.4,2.7 16,0.4 19.2,-0.6 21.6,-6.5 1.9,-4.3 0.5,-9.3 -3.3,-12.3 -2.3,-1.8 -4,-2 -16.7,-1.9 -7.7,-0 -15.1,0.5 -16.3,1.1z" android:strokeColor="#00000000">
<aapt:attr name="android:fillColor">
<gradient android:endX="288.37" android:endY="351.88" android:startX="288.37" android:startY="331.08" android:type="linear">
<item android:color="#FFACAFCA" android:offset="0"/>
<item android:color="#FF44454E" android:offset="1"/>
</gradient>
</aapt:attr>
</path>
<path android:pathData="M345.7,333c-3.8,3 -5.2,8 -3.3,12.3 2.4,5.9 5.6,6.9 21.6,6.5 13,-0.3 14,-0.4 16.4,-2.7 5.1,-4.8 4,-14.2 -2,-17 -1.5,-0.6 -8.3,-1.1 -16.3,-1.1 -12.4,-0 -14.1,0.2 -16.4,2z" android:strokeColor="#00000000">
<aapt:attr name="android:fillColor">
<gradient android:endX="341.56" android:endY="351.88" android:startX="341.56" android:startY="331" android:type="linear">
<item android:color="#FFACAFCA" android:offset="0"/>
<item android:color="#FF44454E" android:offset="1"/>
</gradient>
</aapt:attr>
</path>
</vector>

View File

@ -0,0 +1,79 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:aapt="http://schemas.android.com/aapt"
android:width="194dp"
android:height="194dp"
android:viewportWidth="194"
android:viewportHeight="194">
<path
android:pathData="M64.38,83.3H25C14.38,83.3 5.75,74.68 5.75,64.05V24.68C5.75,14.05 14.38,5.43 25,5.43H64.25C74.88,5.43 83.5,14.05 83.5,24.68V63.93C83.63,74.68 75,83.3 64.38,83.3Z"
android:strokeLineJoin="round"
android:strokeWidth="10"
android:fillColor="#00000000"
android:strokeLineCap="round">
<aapt:attr name="android:strokeColor">
<gradient
android:startX="97.13"
android:startY="5.43"
android:endX="97.13"
android:endY="188.43"
android:type="linear">
<item android:offset="0" android:color="#FFACAFCA"/>
<item android:offset="1" android:color="#FF44454E"/>
</gradient>
</aapt:attr>
</path>
<path
android:pathData="M169.25,83.3H130C119.38,83.3 110.75,74.68 110.75,64.05V24.68C110.75,14.05 119.38,5.43 130,5.43H169.25C179.88,5.43 188.5,14.05 188.5,24.68V63.93C188.63,74.68 179.88,83.3 169.25,83.3Z"
android:strokeLineJoin="round"
android:strokeWidth="10"
android:fillColor="#00000000"
android:strokeLineCap="round">
<aapt:attr name="android:strokeColor">
<gradient
android:startX="97.13"
android:startY="5.43"
android:endX="97.13"
android:endY="188.43"
android:type="linear">
<item android:offset="0" android:color="#FFACAFCA"/>
<item android:offset="1" android:color="#FF44454E"/>
</gradient>
</aapt:attr>
</path>
<path
android:pathData="M64.38,188.43H25C14.38,188.43 5.75,179.8 5.75,169.18V129.93C5.75,119.3 14.38,110.68 25,110.68H64.25C74.88,110.68 83.5,119.3 83.5,129.93V169.18C83.63,179.8 75,188.43 64.38,188.43Z"
android:strokeLineJoin="round"
android:strokeWidth="10"
android:fillColor="#00000000"
android:strokeLineCap="round">
<aapt:attr name="android:strokeColor">
<gradient
android:startX="97.13"
android:startY="5.43"
android:endX="97.13"
android:endY="188.43"
android:type="linear">
<item android:offset="0" android:color="#FFACAFCA"/>
<item android:offset="1" android:color="#FF44454E"/>
</gradient>
</aapt:attr>
</path>
<path
android:pathData="M169.25,188.43H130C119.38,188.43 110.75,179.8 110.75,169.18V129.93C110.75,119.3 119.38,110.68 130,110.68H169.25C179.88,110.68 188.5,119.3 188.5,129.93V169.18C188.63,179.8 179.88,188.43 169.25,188.43Z"
android:strokeLineJoin="round"
android:strokeWidth="10"
android:fillColor="#00000000"
android:strokeLineCap="round">
<aapt:attr name="android:strokeColor">
<gradient
android:startX="97.13"
android:startY="5.43"
android:endX="97.13"
android:endY="188.43"
android:type="linear">
<item android:offset="0" android:color="#FFACAFCA"/>
<item android:offset="1" android:color="#FF44454E"/>
</gradient>
</aapt:attr>
</path>
</vector>

View File

@ -0,0 +1,25 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:aapt="http://schemas.android.com/aapt"
android:width="236dp"
android:height="240dp"
android:viewportWidth="236"
android:viewportHeight="240">
<path
android:pathData="M190.25,70.97C191.13,70.97 192.13,70.97 193,71.1L195.63,56.22C196.88,48.72 192,41.6 184.5,40.35L123.88,29.72M216.63,177.48C224.13,177.48 230.38,171.23 230.38,163.73V141.98C230.38,134.48 224.13,128.23 216.63,128.23H214.75M100.25,6.85L26.63,34.47C19.5,37.22 16,45.1 18.63,52.22L26,71.47C27.25,71.35 28.75,71.1 30.25,71.1H95.38L100.88,39.72C102.13,32.22 109.25,27.22 116.75,28.6L123.75,29.85L118.13,14.85C115.25,7.72 107.38,4.1 100.25,6.85ZM154.75,152.98C154.75,139.48 165.75,128.48 179.25,128.48H214.63V95.85C214.63,82.35 203.63,71.35 190.13,71.35H30C16.5,71.35 5.5,82.35 5.5,95.85V210.48C5.5,223.98 16.5,234.98 30,234.98H190.13C203.63,234.98 214.63,223.98 214.63,210.48V177.85H179.25C165.88,177.48 154.75,166.6 154.75,152.98ZM181.25,152.98C181.25,154.77 179.79,156.23 178,156.23C176.21,156.23 174.75,154.77 174.75,152.98C174.75,151.18 176.21,149.73 178,149.73C179.79,149.73 181.25,151.18 181.25,152.98Z"
android:strokeLineJoin="round"
android:strokeWidth="10"
android:fillColor="#00000000"
android:strokeLineCap="round">
<aapt:attr name="android:strokeColor">
<gradient
android:startX="117.94"
android:startY="5.93"
android:endX="117.94"
android:endY="234.97"
android:type="linear">
<item android:offset="0" android:color="#FFACAFCA"/>
<item android:offset="1" android:color="#FF44454E"/>
</gradient>
</aapt:attr>
</path>
</vector>

View File

@ -0,0 +1,25 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:aapt="http://schemas.android.com/aapt"
android:width="194dp"
android:height="224dp"
android:viewportWidth="194"
android:viewportHeight="224">
<path
android:pathData="M5.75,48.88V26.38C5.75,14.88 15,5.63 26.5,5.63H167.5C179,5.63 188.25,14.88 188.25,26.38V197.75C188.25,209.25 179,218.5 167.5,218.5L165.38,217.13C159,213.25 151,213.13 144.5,216.88L141.88,218.38L134,214.5C128.75,211.88 122.5,211.88 117.25,214.38L108.88,218.25L106.38,216.75C100.13,213 92.38,213 86.25,216.75L83.75,218.25C72.25,218.25 63,209 63,197.5V26.25C63,14.88 53.75,5.63 42.38,5.63H34M82.75,61.25H158.13M83.13,91.88H130.25M82.75,124.38H147M82.75,156.88H165"
android:strokeLineJoin="round"
android:strokeWidth="10"
android:fillColor="#00000000"
android:strokeLineCap="round">
<aapt:attr name="android:strokeColor">
<gradient
android:startX="97"
android:startY="5.63"
android:endX="97"
android:endY="218.5"
android:type="linear">
<item android:offset="0" android:color="#FFACAFCA"/>
<item android:offset="1" android:color="#FF44454E"/>
</gradient>
</aapt:attr>
</path>
</vector>

View File

@ -0,0 +1,25 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:aapt="http://schemas.android.com/aapt"
android:width="242dp"
android:height="195dp"
android:viewportWidth="242"
android:viewportHeight="195">
<path
android:pathData="M132.78,156.55H88.88C87.12,156.55 85.67,157.99 85.67,159.75V187.18C85.67,189.56 83.3,190.69 82.16,189.56L6.24,129.63C4.59,128.29 4.59,125.81 6.24,124.57L82.16,65.78C83.3,64.65 85.67,64.65 85.67,66.92V94.35C85.67,96.11 87.12,97.55 88.88,97.55H156.43M109.22,38.45H153.23C154.98,38.45 156.43,37.01 156.43,35.25V7.82C156.43,5.44 158.81,4.31 159.94,5.44L235.76,65.37C237.41,66.71 237.41,69.19 235.76,70.42L159.84,129.22C158.7,130.35 156.33,129.22 156.33,126.85V100.65C156.33,98.89 154.88,97.45 153.13,97.45H85.67"
android:strokeLineJoin="round"
android:strokeWidth="10"
android:fillColor="#00000000"
android:strokeLineCap="round">
<aapt:attr name="android:strokeColor">
<gradient
android:startX="121"
android:startY="5"
android:endX="121"
android:endY="190"
android:type="linear">
<item android:offset="0" android:color="#FFACAFCA"/>
<item android:offset="1" android:color="#FF44454E"/>
</gradient>
</aapt:attr>
</path>
</vector>

View File

@ -0,0 +1,25 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:aapt="http://schemas.android.com/aapt"
android:width="243dp"
android:height="244dp"
android:viewportWidth="243"
android:viewportHeight="244">
<path
android:pathData="M65.63,5.93H23.25C13.5,5.93 5.5,13.93 5.5,23.68V64.68M238,65.3V23.05C238,13.3 230,5.3 220.25,5.3H179.25M178,237.68H220.25C230,237.68 238,229.68 238,219.93V178.93M5.5,178.3V220.68C5.5,230.43 13.5,238.43 23.25,238.43H64.25M24.13,122.55H220M43.25,68.8V176.18M56.25,68.8V176.18M82.38,68.8V176.18M98.88,68.8V176.18M123.5,68.8V176.18M111.13,68.8V176.18M148.63,68.8V176.18M173.5,68.8V176.18M186.75,68.8V176.18M201,68.8V176.18"
android:strokeLineJoin="round"
android:strokeWidth="10"
android:fillColor="#00000000"
android:strokeLineCap="round">
<aapt:attr name="android:strokeColor">
<gradient
android:startX="121.75"
android:startY="5.3"
android:endX="121.75"
android:endY="238.43"
android:type="linear">
<item android:offset="0" android:color="#FFACAFCA"/>
<item android:offset="1" android:color="#FF44454E"/>
</gradient>
</aapt:attr>
</path>
</vector>

View File

@ -0,0 +1,25 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:aapt="http://schemas.android.com/aapt"
android:width="246dp"
android:height="246dp"
android:viewportWidth="246"
android:viewportHeight="246">
<path
android:pathData="M44.83,34.22C48.45,30.6 50.22,28.83 52.43,28.04C54.99,26.89 57.82,26.71 60.65,27.59C62.68,28.21 64.72,30.07 68.78,33.25C76.38,39.44 80.01,42.53 83.46,42.97C87.61,43.42 91.76,41.74 94.42,38.55C96.27,35.81 96.98,31.04 97.77,21.41C98.31,16.28 98.66,13.81 99.72,11.68C100.87,9.12 103.25,7.09 105.82,6.12C108.03,5.32 110.41,5.23 115.63,5.32L130.48,5.32C135.6,5.32 138.08,5.32 140.29,6.12C142.94,7.18 144.97,9.21 146.39,11.68C147.36,13.72 147.8,16.28 148.33,21.41C149.3,31.04 149.75,35.9 151.69,38.55C154.26,42 158.32,43.59 162.65,42.97C166.01,42.62 169.72,39.44 177.32,33.25C181.3,30.16 183.25,28.39 185.46,27.59C188.28,26.71 191.2,26.8 193.68,28.04C195.89,28.83 197.65,30.6 201.28,34.22L211.88,44.83C215.51,48.45 217.28,50.22 218.07,52.43C219.22,54.99 219.4,57.82 218.51,60.65C217.9,62.68 216.04,64.72 212.86,68.78C206.67,76.38 203.66,79.92 203.13,83.46C202.69,87.61 204.37,91.76 207.55,94.42C210.29,96.27 215.07,96.98 224.7,97.77C229.83,98.31 232.3,98.66 234.42,99.72C236.99,100.87 239.02,103.25 239.99,105.82C240.79,108.03 240.88,110.41 240.79,115.63V130.48C240.79,135.6 240.79,138.08 239.99,140.29C238.93,142.94 236.9,144.97 234.42,146.39C232.39,147.36 229.83,147.8 224.7,148.33C215.07,149.3 210.21,149.75 207.55,151.69C204.11,154.26 202.51,158.32 203.13,162.65C203.49,166.01 206.67,169.72 212.86,177.32C215.95,181.3 217.72,183.25 218.51,185.46C219.4,188.28 219.31,191.2 218.07,193.68C217.28,195.89 215.51,197.65 211.88,201.28L201.28,211.88C197.65,215.51 195.89,217.28 193.68,218.07C191.11,219.22 188.28,219.4 185.46,218.51C183.42,217.89 181.39,216.04 177.32,212.86C169.72,206.67 166.1,203.58 162.65,203.13C158.5,202.69 154.34,204.37 151.69,207.55C149.84,210.29 149.13,215.07 148.33,224.7C147.8,229.83 147.45,232.3 146.39,234.42C145.24,236.99 143.03,238.84 140.29,239.99C138.08,240.79 135.69,240.88 130.48,240.79H115.63C110.5,240.79 108.03,240.79 105.82,239.99C103.17,238.93 101.13,236.9 99.72,234.42C98.75,232.39 98.31,229.83 97.77,224.7C96.8,215.07 96.36,210.21 94.42,207.55C91.85,204.11 87.79,202.51 83.46,203.13C80.1,203.49 76.38,206.67 68.78,212.86C64.81,215.95 62.86,217.72 60.65,218.51C57.82,219.4 54.91,219.31 52.43,218.07C50.22,217.28 48.45,215.51 44.83,211.88L34.22,201.28C30.6,197.65 28.83,195.89 28.04,193.68C26.89,191.11 26.71,188.28 27.59,185.46C28.21,183.42 30.07,181.39 33.25,177.32C39.44,169.72 42.53,166.1 42.97,162.65C43.42,158.5 41.74,154.34 38.55,151.69C35.81,149.84 31.04,149.13 21.41,148.33C16.28,147.8 13.81,147.45 11.68,146.39C9.12,145.24 7.27,143.03 6.12,140.29C5.32,138.08 5.23,135.69 5.32,130.48L5.32,115.63C5.32,110.5 5.32,108.03 6.12,105.82C7.18,103.17 9.21,101.13 11.68,99.72C13.72,98.75 16.28,98.31 21.41,97.77C31.04,96.8 35.9,96.36 38.55,94.42C42,91.85 43.59,87.79 42.97,83.46C42.62,80.1 39.44,76.38 33.25,68.78C30.16,64.81 28.39,62.86 27.59,60.65C26.71,57.82 26.8,54.91 28.04,52.43C28.83,50.22 30.6,48.45 34.22,44.83L44.83,34.22ZM89.47,89.47C71.08,107.85 71.17,137.64 89.47,155.93C107.85,174.32 137.64,174.23 155.93,155.93C174.23,137.64 174.23,107.76 155.93,89.47C137.55,70.9 107.85,71.08 89.47,89.47Z"
android:strokeLineJoin="round"
android:strokeWidth="10"
android:fillColor="#00000000"
android:strokeLineCap="round">
<aapt:attr name="android:strokeColor">
<gradient
android:startX="123.05"
android:startY="5.29"
android:endX="123.05"
android:endY="240.81"
android:type="linear">
<item android:offset="0" android:color="#FFACAFCA"/>
<item android:offset="1" android:color="#FF44454E"/>
</gradient>
</aapt:attr>
</path>
</vector>

View File

@ -0,0 +1,102 @@
<layout xmlns:android="http://schemas.android.com/apk/res/android">
<data>
<import type="android.view.View" />
<!-- Import the PaymentStatus enum -->
<import type="com.dspread.pos.ui.cashier.CashierViewModel.PaymentStatus" />
<variable
name="viewModel"
type="com.dspread.pos.ui.cashier.CashierViewModel" />
</data>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:gravity="center"
android:padding="16dp">
<!-- Real-time amount display -->
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@{viewModel.amount + ` ` + viewModel.currencyCode}"
android:textSize="24sp"
android:textStyle="bold"
android:textColor="@color/black"
android:layout_marginTop="16dp"
android:gravity="center"/>
<EditText
android:id="@+id/etAmountInput"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:inputType="numberDecimal"
android:hint="Enter amount"
android:imeOptions="actionDone"
android:maxLines="1"/>
<!-- Waiting State -->
<LinearLayout
android:id="@+id/waitingLayout"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical"
android:gravity="center"
android:visibility="@{viewModel.paymentStatus == PaymentStatus.WAITING ? View.VISIBLE : View.GONE}">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Enter amount for pinpad M50 to pay"
android:textSize="18sp"
android:textStyle="bold"
android:gravity="center" />
</LinearLayout>
<!-- Success State -->
<LinearLayout
android:id="@+id/successLayout"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical"
android:gravity="center"
android:visibility="@{viewModel.paymentStatus == PaymentStatus.SUCCESS ? View.VISIBLE : View.GONE}">
<!-- <TextView-->
<!-- android:layout_width="wrap_content"-->
<!-- android:layout_height="wrap_content"-->
<!-- android:text="@{viewModel.amount + ` ` + viewModel.currencyCode}"-->
<!-- android:textSize="24sp"-->
<!-- android:textStyle="bold"-->
<!-- android:gravity="center" />-->
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Print Receipt"
android:onClick="@{() -> viewModel.printReceipt()}"
android:layout_marginTop="16dp" />
</LinearLayout>
<!-- Failed State -->
<LinearLayout
android:id="@+id/failedLayout"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical"
android:gravity="center"
android:visibility="@{viewModel.paymentStatus == PaymentStatus.FAILED ? View.VISIBLE : View.GONE}">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Payment Failed. Please try again."
android:textSize="18sp"
android:textStyle="bold"
android:gravity="center" />
</LinearLayout>
</LinearLayout>
</layout>

View File

@ -6,6 +6,7 @@
<variable
name="viewModel"
type="com.dspread.pos.ui.home.HomeViewModel" />
</data>
<androidx.constraintlayout.widget.ConstraintLayout
@ -17,13 +18,13 @@
<ImageView
android:id="@+id/logoImage"
android:layout_width="0dp"
android:layout_height="110dp"
android:layout_height="100dp"
android:paddingLeft="20dp"
android:paddingRight="20dp"
android:scaleType="fitCenter"
android:src="@drawable/am_mulberry_logo_wide_color"
android:adjustViewBounds="true"
android:src="@{viewModel.mainLogoBitmap}"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent" />

View File

@ -0,0 +1,47 @@
<?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 for Payment Signal..."
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="Currency:"
android:textSize="16sp"
android:layout_marginBottom="5dp"/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@{viewModel.currencyCode}"
android:textSize="18sp"
android:textStyle="bold"/>
</LinearLayout>
</layout>

View File

@ -28,8 +28,8 @@
android:layout_width="100dp"
android:layout_height="100dp"
android:src="@{viewModel.icon}"
android:scaleType="centerCrop"
tools:src="@drawable/am_mulberry_logo_wide_color" /> <!-- Use your actual default drawable -->
android:scaleType="fitCenter"
tools:src="@drawable/ax_card_grey" /> <!-- Use your actual default drawable -->
<TextView
android:layout_width="wrap_content"

View File

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

View File

@ -545,4 +545,5 @@
<string name="send_receipt">Print Receipt</string>
<string name="connect_warnning">Pls connect your devices first!</string>
<string name="no_device">No device</string>
<string name="cashier">Cashier</string>
</resources>

View File

@ -545,5 +545,6 @@
<string name="send_receipt">Print Receipt</string>
<string name="connect_warnning">Pls connect your devices first!</string>
<string name="no_device">No device</string>
<string name="cashier">Cashier</string>
</resources>