Compare commits
3 Commits
fbf7b99501
...
8b892e68df
Author | SHA1 | Date |
---|---|---|
|
8b892e68df | |
|
c6cb612c99 | |
|
50796a6951 |
|
@ -139,4 +139,12 @@ dependencies {
|
|||
implementation 'org.eclipse.paho:org.eclipse.paho.android.service:1.1.1'
|
||||
|
||||
implementation "org.java-websocket:Java-WebSocket:1.5.2"
|
||||
|
||||
// ML Kit Face Detection
|
||||
implementation 'com.google.mlkit:face-detection:16.1.6'
|
||||
|
||||
// CameraX (optional but recommended for better camera handling)
|
||||
// implementation "androidx.camera:camera-camera2:1.3.2"
|
||||
// implementation "androidx.camera:camera-lifecycle:1.3.2"
|
||||
// implementation "androidx.camera:camera-view:1.3.2"
|
||||
}
|
|
@ -1,6 +1,11 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools">
|
||||
|
||||
<uses-permission android:name="android.permission.CAMERA" />
|
||||
<uses-feature android:name="android.hardware.camera" />
|
||||
<uses-feature android:name="android.hardware.camera.front" />
|
||||
|
||||
<uses-permission android:name="android.permission.WAKE_LOCK" />
|
||||
<uses-permission android:name="android.permission.READ_PHONE_STATE" />
|
||||
<uses-permission android:name="android.permission.INTERNET" />
|
||||
|
|
|
@ -0,0 +1,263 @@
|
|||
package com.dspread.pos.faceID;
|
||||
|
||||
import android.Manifest;
|
||||
import android.content.Context;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.SurfaceTexture;
|
||||
import android.hardware.camera2.CameraAccessException;
|
||||
import android.hardware.camera2.CameraCaptureSession;
|
||||
import android.hardware.camera2.CameraCharacteristics;
|
||||
import android.hardware.camera2.CameraDevice;
|
||||
import android.hardware.camera2.CameraManager;
|
||||
import android.hardware.camera2.CaptureRequest;
|
||||
import android.os.Handler;
|
||||
import android.os.HandlerThread;
|
||||
import android.util.Log;
|
||||
import android.view.Surface;
|
||||
import android.view.TextureView;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.core.app.ActivityCompat;
|
||||
import java.util.Arrays;
|
||||
|
||||
public class FaceIDHelper implements TextureView.SurfaceTextureListener {
|
||||
|
||||
private static final int REQUEST_CAMERA_PERMISSION = 200;
|
||||
private static final int DETECTION_INTERVAL_MS = 1000; // Check every second
|
||||
|
||||
private Context context;
|
||||
private TextureView textureView;
|
||||
private FaceIDCallback callback;
|
||||
|
||||
private CameraManager cameraManager;
|
||||
private CameraDevice cameraDevice;
|
||||
private CameraCaptureSession cameraCaptureSession;
|
||||
private CaptureRequest.Builder captureRequestBuilder;
|
||||
|
||||
private Handler backgroundHandler;
|
||||
private HandlerThread backgroundThread;
|
||||
private String frontCameraId;
|
||||
private RealFaceDetector realFaceDetector;
|
||||
private Handler detectionHandler;
|
||||
private Runnable detectionRunnable;
|
||||
|
||||
public interface FaceIDCallback {
|
||||
void onFaceDetected();
|
||||
void onCameraError(String error);
|
||||
}
|
||||
|
||||
public FaceIDHelper(Context context, TextureView textureView, FaceIDCallback callback) {
|
||||
this.context = context;
|
||||
this.textureView = textureView;
|
||||
this.callback = callback;
|
||||
this.cameraManager = (CameraManager) context.getSystemService(Context.CAMERA_SERVICE);
|
||||
|
||||
// Initialize real face detector
|
||||
realFaceDetector = new RealFaceDetector(context, new RealFaceDetector.FaceDetectionCallback() {
|
||||
@Override
|
||||
public void onFaceDetected() {
|
||||
if (callback != null) {
|
||||
callback.onFaceDetected();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFaceDetectionError(String error) {
|
||||
Log.d("FaceIDHelper", "Face detection error: " + error);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onNoFaceDetected() {
|
||||
// Continue detection
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public void startCamera() {
|
||||
if (ActivityCompat.checkSelfPermission(context, Manifest.permission.CAMERA)
|
||||
!= PackageManager.PERMISSION_GRANTED) {
|
||||
callback.onCameraError("Camera permission required");
|
||||
return;
|
||||
}
|
||||
|
||||
setupCamera();
|
||||
}
|
||||
|
||||
private void setupCamera() {
|
||||
try {
|
||||
frontCameraId = getFrontCameraId();
|
||||
if (frontCameraId == null) {
|
||||
callback.onCameraError("Front camera not found");
|
||||
return;
|
||||
}
|
||||
|
||||
textureView.setSurfaceTextureListener(this);
|
||||
|
||||
} catch (CameraAccessException e) {
|
||||
e.printStackTrace();
|
||||
callback.onCameraError("Camera access error");
|
||||
}
|
||||
}
|
||||
|
||||
private String getFrontCameraId() throws CameraAccessException {
|
||||
String[] cameraIds = cameraManager.getCameraIdList();
|
||||
for (String cameraId : cameraIds) {
|
||||
CameraCharacteristics characteristics = cameraManager.getCameraCharacteristics(cameraId);
|
||||
Integer facing = characteristics.get(CameraCharacteristics.LENS_FACING);
|
||||
if (facing != null && facing == CameraCharacteristics.LENS_FACING_FRONT) {
|
||||
return cameraId;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSurfaceTextureAvailable(SurfaceTexture surface, int width, int height) {
|
||||
openCamera();
|
||||
startFaceDetection();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSurfaceTextureSizeChanged(SurfaceTexture surface, int width, int height) {}
|
||||
|
||||
@Override
|
||||
public boolean onSurfaceTextureDestroyed(SurfaceTexture surface) {
|
||||
stopFaceDetection();
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSurfaceTextureUpdated(SurfaceTexture surface) {}
|
||||
|
||||
private void openCamera() {
|
||||
try {
|
||||
if (ActivityCompat.checkSelfPermission(context, Manifest.permission.CAMERA)
|
||||
!= PackageManager.PERMISSION_GRANTED) {
|
||||
return;
|
||||
}
|
||||
|
||||
cameraManager.openCamera(frontCameraId, stateCallback, backgroundHandler);
|
||||
|
||||
} catch (CameraAccessException e) {
|
||||
e.printStackTrace();
|
||||
callback.onCameraError("Failed to open camera");
|
||||
}
|
||||
}
|
||||
|
||||
private final CameraDevice.StateCallback stateCallback = new CameraDevice.StateCallback() {
|
||||
@Override
|
||||
public void onOpened(@NonNull CameraDevice camera) {
|
||||
cameraDevice = camera;
|
||||
createCameraPreview();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDisconnected(@NonNull CameraDevice camera) {
|
||||
cameraDevice.close();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onError(@NonNull CameraDevice camera, int error) {
|
||||
cameraDevice.close();
|
||||
cameraDevice = null;
|
||||
callback.onCameraError("Camera error: " + error);
|
||||
}
|
||||
};
|
||||
|
||||
private void createCameraPreview() {
|
||||
try {
|
||||
SurfaceTexture texture = textureView.getSurfaceTexture();
|
||||
Surface surface = new Surface(texture);
|
||||
|
||||
captureRequestBuilder = cameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);
|
||||
captureRequestBuilder.addTarget(surface);
|
||||
|
||||
cameraDevice.createCaptureSession(Arrays.asList(surface),
|
||||
new CameraCaptureSession.StateCallback() {
|
||||
@Override
|
||||
public void onConfigured(@NonNull CameraCaptureSession session) {
|
||||
if (cameraDevice == null) return;
|
||||
|
||||
cameraCaptureSession = session;
|
||||
try {
|
||||
captureRequestBuilder.set(CaptureRequest.CONTROL_AF_MODE,
|
||||
CaptureRequest.CONTROL_AF_MODE_CONTINUOUS_PICTURE);
|
||||
captureRequestBuilder.set(CaptureRequest.CONTROL_AE_MODE,
|
||||
CaptureRequest.CONTROL_AE_MODE_ON_AUTO_FLASH);
|
||||
|
||||
session.setRepeatingRequest(captureRequestBuilder.build(),
|
||||
null, backgroundHandler);
|
||||
|
||||
} catch (CameraAccessException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onConfigureFailed(@NonNull CameraCaptureSession session) {
|
||||
callback.onCameraError("Camera configuration failed");
|
||||
}
|
||||
}, null);
|
||||
|
||||
} catch (CameraAccessException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
private void startFaceDetection() {
|
||||
detectionHandler = new Handler();
|
||||
detectionRunnable = new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
if (textureView.isAvailable()) {
|
||||
Bitmap frameBitmap = textureView.getBitmap();
|
||||
if (frameBitmap != null) {
|
||||
realFaceDetector.detectFaces(frameBitmap);
|
||||
}
|
||||
}
|
||||
detectionHandler.postDelayed(this, DETECTION_INTERVAL_MS);
|
||||
}
|
||||
};
|
||||
detectionHandler.postDelayed(detectionRunnable, DETECTION_INTERVAL_MS);
|
||||
}
|
||||
|
||||
private void stopFaceDetection() {
|
||||
if (detectionHandler != null && detectionRunnable != null) {
|
||||
detectionHandler.removeCallbacks(detectionRunnable);
|
||||
}
|
||||
realFaceDetector.stop();
|
||||
}
|
||||
|
||||
public void startBackgroundThread() {
|
||||
backgroundThread = new HandlerThread("CameraBackground");
|
||||
backgroundThread.start();
|
||||
backgroundHandler = new Handler(backgroundThread.getLooper());
|
||||
}
|
||||
|
||||
public void stopBackgroundThread() {
|
||||
stopFaceDetection();
|
||||
if (backgroundThread != null) {
|
||||
backgroundThread.quitSafely();
|
||||
try {
|
||||
backgroundThread.join();
|
||||
backgroundThread = null;
|
||||
backgroundHandler = null;
|
||||
} catch (InterruptedException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void closeCamera() {
|
||||
stopFaceDetection();
|
||||
if (cameraCaptureSession != null) {
|
||||
cameraCaptureSession.close();
|
||||
cameraCaptureSession = null;
|
||||
}
|
||||
if (cameraDevice != null) {
|
||||
cameraDevice.close();
|
||||
cameraDevice = null;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,132 @@
|
|||
package com.dspread.pos.faceID;
|
||||
|
||||
import android.content.Context;
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.Rect;
|
||||
|
||||
import com.google.mlkit.vision.common.InputImage;
|
||||
import com.google.mlkit.vision.face.Face;
|
||||
import com.google.mlkit.vision.face.FaceDetection;
|
||||
import com.google.mlkit.vision.face.FaceDetector;
|
||||
import com.google.mlkit.vision.face.FaceDetectorOptions;
|
||||
import java.util.List;
|
||||
|
||||
public class RealFaceDetector {
|
||||
|
||||
private FaceDetector faceDetector;
|
||||
private FaceDetectionCallback callback;
|
||||
private boolean isDetecting = false;
|
||||
|
||||
public interface FaceDetectionCallback {
|
||||
void onFaceDetected();
|
||||
void onFaceDetectionError(String error);
|
||||
void onNoFaceDetected();
|
||||
}
|
||||
|
||||
public RealFaceDetector(Context context, FaceDetectionCallback callback) {
|
||||
this.callback = callback;
|
||||
initializeFaceDetector();
|
||||
}
|
||||
|
||||
private void initializeFaceDetector() {
|
||||
// High-accuracy landmark detection and face classification
|
||||
FaceDetectorOptions options = new FaceDetectorOptions.Builder()
|
||||
.setPerformanceMode(FaceDetectorOptions.PERFORMANCE_MODE_ACCURATE)
|
||||
.setContourMode(FaceDetectorOptions.CONTOUR_MODE_ALL)
|
||||
.setLandmarkMode(FaceDetectorOptions.LANDMARK_MODE_ALL)
|
||||
.setClassificationMode(FaceDetectorOptions.CLASSIFICATION_MODE_ALL)
|
||||
.setMinFaceSize(0.15f) // Minimum face size (15% of image width)
|
||||
.enableTracking() // Enable face tracking for better performance
|
||||
.build();
|
||||
//// Use a different face detection approach (offline-only)
|
||||
//// the bundled version:
|
||||
// .setPerformanceMode(FaceDetectorOptions.PERFORMANCE_MODE_FAST)
|
||||
// .setContourMode(FaceDetectorOptions.CONTOUR_MODE_NONE)
|
||||
// .setLandmarkMode(FaceDetectorOptions.LANDMARK_MODE_NONE)
|
||||
// .setClassificationMode(FaceDetectorOptions.CLASSIFICATION_MODE_NONE)
|
||||
// .build();
|
||||
|
||||
faceDetector = FaceDetection.getClient(options);
|
||||
}
|
||||
|
||||
public void detectFaces(Bitmap bitmap) {
|
||||
if (isDetecting) return;
|
||||
|
||||
isDetecting = true;
|
||||
InputImage image = InputImage.fromBitmap(bitmap, 0); // 0 rotation
|
||||
|
||||
faceDetector.process(image)
|
||||
.addOnSuccessListener(faces -> {
|
||||
isDetecting = false;
|
||||
handleDetectionResult(faces);
|
||||
})
|
||||
.addOnFailureListener(e -> {
|
||||
isDetecting = false;
|
||||
if (callback != null) {
|
||||
callback.onFaceDetectionError("Face detection failed: " + e.getMessage());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void handleDetectionResult(List<Face> faces) {
|
||||
if (faces == null || faces.isEmpty()) {
|
||||
if (callback != null) {
|
||||
callback.onNoFaceDetected();
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// Check if we have at least one good quality face
|
||||
for (Face face : faces) {
|
||||
if (isGoodQualityFace(face)) {
|
||||
if (callback != null) {
|
||||
callback.onFaceDetected();
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// If we reach here, faces were detected but not good quality
|
||||
if (callback != null) {
|
||||
callback.onNoFaceDetected();
|
||||
}
|
||||
}
|
||||
|
||||
private boolean isGoodQualityFace(Face face) {
|
||||
// Check if face has good confidence (not occluded, good lighting)
|
||||
// You can adjust these thresholds based on your requirements
|
||||
|
||||
// Check if eyes are open (if classification is available)
|
||||
if (face.getLeftEyeOpenProbability() != null &&
|
||||
face.getLeftEyeOpenProbability() < 0.3f) {
|
||||
return false; // Eye probably closed
|
||||
}
|
||||
|
||||
if (face.getRightEyeOpenProbability() != null &&
|
||||
face.getRightEyeOpenProbability() < 0.3f) {
|
||||
return false; // Eye probably closed
|
||||
}
|
||||
|
||||
// Check if smiling (optional, depends on your use case)
|
||||
if (face.getSmilingProbability() != null &&
|
||||
face.getSmilingProbability() < 0.1f) {
|
||||
// Not smiling, but this might not be necessary for payment
|
||||
}
|
||||
|
||||
// Check face bounding box size (should be reasonably large)
|
||||
Rect bounds = face.getBoundingBox();
|
||||
float sizeRatio = (float) bounds.width() * bounds.height() / (1000 * 1000); // Example ratio
|
||||
if (sizeRatio < 0.1f) {
|
||||
return false; // Face too small
|
||||
}
|
||||
|
||||
return true; // Good quality face
|
||||
}
|
||||
|
||||
public void stop() {
|
||||
if (faceDetector != null) {
|
||||
faceDetector.close();
|
||||
}
|
||||
isDetecting = false;
|
||||
}
|
||||
}
|
|
@ -6,6 +6,8 @@ import android.os.Bundle;
|
|||
import android.view.LayoutInflater;
|
||||
import android.view.ViewGroup;
|
||||
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
|
||||
import com.dspread.pos.common.base.BaseFragment;
|
||||
import com.dspread.pos.TitleProviderListener;
|
||||
import com.dspread.pos.ui.main.MainActivity;
|
||||
|
@ -19,7 +21,17 @@ public class MulberryFragment extends BaseFragment<FragmentMulberryBinding, Mulb
|
|||
|
||||
@Override
|
||||
public void initViewObservable() {
|
||||
// Observe item click events
|
||||
// Call this after view is created
|
||||
RecyclerView recyclerView = binding.getRoot().findViewById(R.id.mulberryCards_list);
|
||||
|
||||
viewModel.adjustMulberryView(binding.ivLogo,
|
||||
binding.topLogoContainer,
|
||||
binding.footerContainer,
|
||||
binding.centerContainer,
|
||||
binding.getRoot()
|
||||
|
||||
);
|
||||
|
||||
// Handle fragment navigation
|
||||
viewModel.navigateToFragment.observe(this, fragmentId -> {
|
||||
Activity activity = getActivity();
|
||||
|
|
|
@ -7,18 +7,24 @@ import android.graphics.Bitmap;
|
|||
import android.graphics.BitmapFactory;
|
||||
import android.os.Build;
|
||||
import android.util.Log;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.GridLayout;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.LinearLayout;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.cardview.widget.CardView;
|
||||
import androidx.databinding.Observable;
|
||||
import androidx.databinding.ObservableArrayList;
|
||||
import androidx.databinding.ObservableField;
|
||||
import androidx.databinding.ObservableList;
|
||||
import androidx.lifecycle.LiveData;
|
||||
import androidx.lifecycle.MutableLiveData;
|
||||
import androidx.recyclerview.widget.GridLayoutManager;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
|
||||
import com.dspread.pos.common.base.BaseAppViewModel;
|
||||
import com.dspread.pos.data.local.PreferencesManager;
|
||||
import com.dspread.pos.utils.ImageUtils;
|
||||
import com.dspread.pos.utils.PosParameters;
|
||||
import com.dspread.pos.utils.TRACE;
|
||||
import com.dspread.pos_android_app.BR;
|
||||
|
@ -35,11 +41,23 @@ import java.util.Map;
|
|||
|
||||
import me.tatarka.bindingcollectionadapter2.ItemBinding;
|
||||
import me.goldze.mvvmhabit.bus.event.SingleLiveEvent;
|
||||
import me.goldze.mvvmhabit.base.ItemViewModel;
|
||||
|
||||
public class MulberryViewModel extends BaseAppViewModel {
|
||||
private static final String TAG = "MulberryVM";
|
||||
|
||||
// Device type constants
|
||||
private static final int DEVICE_TYPE_SMALL = 1; // D20, D30, D30M
|
||||
private static final int DEVICE_TYPE_COMPACT = 2; // D70 (wide but small)
|
||||
private static final int DEVICE_TYPE_LARGE = 3; // D80, D80K, others
|
||||
|
||||
private int currentDeviceType;
|
||||
|
||||
// Device-specific observables
|
||||
public final ObservableField<Integer> logoHeight = new ObservableField<>();
|
||||
public final ObservableField<Integer> gridSpanCount = new ObservableField<>();
|
||||
public final ObservableField<Integer> maxNavItems = new ObservableField<>();
|
||||
public final ObservableField<Integer> cardImageSize = new ObservableField<>();
|
||||
|
||||
// UI Observables
|
||||
public final ObservableField<Integer> mainLogo = new ObservableField<>();
|
||||
public final ObservableField<Integer> footerLogo = new ObservableField<>();
|
||||
|
@ -63,23 +81,26 @@ public class MulberryViewModel extends BaseAppViewModel {
|
|||
private final Map<String, Bitmap> imageCache = new HashMap<>();
|
||||
private final MutableLiveData<Boolean> imagesLoaded = new MutableLiveData<>(false);
|
||||
|
||||
// Provide height in pixels, not dp
|
||||
|
||||
|
||||
|
||||
public MulberryViewModel(@NonNull Application application) {
|
||||
super(application);
|
||||
TRACE.i("Mulberry fragment init");
|
||||
Log.d(TAG, "MulberryViewModel constructor called");
|
||||
|
||||
|
||||
// Add this to track initialization
|
||||
Log.d(TAG, "ViewModel hash: " + this.hashCode());
|
||||
if (Build.MODEL.equalsIgnoreCase("D70") ||
|
||||
Build.MODEL.equalsIgnoreCase("D30") ||
|
||||
Build.MODEL.equalsIgnoreCase("D30M") ||
|
||||
Build.MODEL.equalsIgnoreCase("D80")||
|
||||
Build.MODEL.equalsIgnoreCase("D80K")) {
|
||||
Log.d("Build.MODEL", "MainViewModel: " + Build.MODEL);;
|
||||
// To-Do set small size of logo ,and grid size 3 by 1
|
||||
}
|
||||
|
||||
|
||||
// if (Build.MODEL.equalsIgnoreCase("D70") ||
|
||||
// Build.MODEL.equalsIgnoreCase("D30") ||
|
||||
// Build.MODEL.equalsIgnoreCase("D30M") ||
|
||||
// Build.MODEL.equalsIgnoreCase("D80")||
|
||||
// Build.MODEL.equalsIgnoreCase("D80K")) {
|
||||
// Log.d("Build.MODEL", "MainViewModel: " + Build.MODEL);;
|
||||
// // To-Do set small size of logo ,and grid size 3 by 1
|
||||
// }
|
||||
|
||||
// Observe WebSocket connection status
|
||||
getWebSocketConnectionStatus().observeForever(isConnected -> {
|
||||
|
@ -99,6 +120,82 @@ public class MulberryViewModel extends BaseAppViewModel {
|
|||
initialize();
|
||||
}
|
||||
|
||||
public void adjustMulberryView(ImageView logoImageView,
|
||||
LinearLayout containerLayout,
|
||||
LinearLayout footerContainer,
|
||||
LinearLayout centerContainer,
|
||||
|
||||
View rootView
|
||||
) {
|
||||
// Find RecyclerView from root view
|
||||
|
||||
String model = Build.MODEL.toUpperCase();
|
||||
int heightPx = 150; // default (~100dp)
|
||||
int paddingPx = 10 ; // default 10dp
|
||||
|
||||
if (model.contains("D70")) {
|
||||
heightPx = 20; // 20dp for D70
|
||||
paddingPx = 2 ; // 2dp for D70
|
||||
|
||||
containerLayout.setVisibility(View.GONE);
|
||||
footerContainer.setVisibility(View.GONE);
|
||||
centerContainer.setVisibility(View.GONE);
|
||||
|
||||
// Adjust GridLayout columnCount
|
||||
RecyclerView recyclerView = rootView.findViewById(R.id.mulberryCards_list);
|
||||
if (recyclerView != null && recyclerView.getLayoutManager() instanceof GridLayoutManager) {
|
||||
GridLayoutManager layoutManager = (GridLayoutManager) recyclerView.getLayoutManager();
|
||||
layoutManager.setSpanCount(3);
|
||||
// Set padding on the RecyclerView (not LayoutManager)
|
||||
recyclerView.setPadding(0, 1, 0, 1);
|
||||
recyclerView.setLayoutManager(layoutManager);
|
||||
}
|
||||
// Adjust CardView size
|
||||
CardView cardView = rootView.findViewById(R.id.widget_card);
|
||||
if (cardView != null) {
|
||||
int sizeInPx = 80; // Convert 120dp to pixels
|
||||
ViewGroup.LayoutParams params = cardView.getLayoutParams();
|
||||
params.width = sizeInPx;
|
||||
params.height = sizeInPx;
|
||||
cardView.setLayoutParams(params);
|
||||
}
|
||||
|
||||
for (int i = 0; i < recyclerView.getChildCount(); i++) {
|
||||
View cardViews = recyclerView.getChildAt(i);
|
||||
if (cardViews instanceof CardView) {
|
||||
// Adjust card padding
|
||||
cardViews.setPadding( 0,0,0,0
|
||||
);
|
||||
|
||||
// Find and resize the ImageView
|
||||
ImageView imageView = cardView.findViewById(R.id.navImage);
|
||||
if (imageView != null) {
|
||||
ViewGroup.LayoutParams params = imageView.getLayoutParams();
|
||||
params.width = 80;
|
||||
params.height = 80;
|
||||
imageView.setLayoutParams(params);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} else if ( model.contains("D20")) {
|
||||
heightPx = 100; // ~80dp for D30/D20
|
||||
paddingPx = 8 ;
|
||||
} else if (model.contains("D30")) {
|
||||
heightPx = 80;
|
||||
// containerLayout.setVisibility(View.GONE);
|
||||
footerContainer.setVisibility(View.GONE);
|
||||
centerContainer.setVisibility(View.GONE);
|
||||
paddingPx = 0 ;
|
||||
}
|
||||
|
||||
ViewGroup.LayoutParams params = logoImageView.getLayoutParams();
|
||||
params.height = heightPx;
|
||||
logoImageView.setLayoutParams(params);
|
||||
containerLayout.setPadding(0, paddingPx, 0, paddingPx);
|
||||
|
||||
}
|
||||
|
||||
private void initialize() {
|
||||
Log.d(TAG, "Initializing ViewModel");
|
||||
|
||||
|
@ -424,61 +521,74 @@ public class MulberryViewModel extends BaseAppViewModel {
|
|||
JSONArray navItemsArray = new JSONArray(json);
|
||||
List<NavItemViewModel> enabledItems = new ArrayList<>();
|
||||
|
||||
// First pass: collect all enabled items with their original JSON data
|
||||
for (int i = 0; i < navItemsArray.length(); i++) {
|
||||
JSONObject itemJson = navItemsArray.getJSONObject(i);
|
||||
|
||||
// Use your exact existing model structure
|
||||
NavItemViewModel item = new NavItemViewModel(
|
||||
itemJson.getString("navName"),
|
||||
parseIconSource(itemJson.opt("icon")),
|
||||
itemJson.optInt("position", 0),
|
||||
itemJson.optBoolean("enabled", true) // Keep enabled status
|
||||
itemJson.optBoolean("enabled", true),
|
||||
itemJson // Store the original JSON to preserve page info
|
||||
);
|
||||
|
||||
// Only add enabled items (matching your working version's behavior)
|
||||
if (item.enabled) {
|
||||
enabledItems.add(item);
|
||||
Log.d(TAG, "Loaded item: " + item.name +
|
||||
" | Pos: " + item.position +
|
||||
" | Enabled: " + item.enabled);
|
||||
Log.d(TAG, "Loaded item: " + item.name + " | Pos: " + item.position + " | Enabled: " + item.enabled);
|
||||
}
|
||||
}
|
||||
|
||||
// 2. Sort with position logging (like your working version)
|
||||
// Sort by position (same as working version)
|
||||
Collections.sort(enabledItems, (i1, i2) -> {
|
||||
Log.v(TAG, "Sorting: " + i1.position + " vs " + i2.position);
|
||||
return Integer.compare(i1.position, i2.position);
|
||||
});
|
||||
|
||||
// 3. Create ViewModels - optimized page ID handling
|
||||
for (int i = 0; i < enabledItems.size(); i++) {
|
||||
JSONObject originalJson = navItemsArray.getJSONObject(i);
|
||||
NavItemViewModel item = enabledItems.get(i);
|
||||
// Create ViewModels using the original JSON data for page mapping
|
||||
for (NavItemViewModel item : enabledItems) {
|
||||
try {
|
||||
String pageName = item.originalJson.optString("navPage");
|
||||
int pageId = PAGE_MAP.containsKey(pageName) ? PAGE_MAP.get(pageName) : NAV_HOME;
|
||||
|
||||
// Directly get page ID from the original JSON
|
||||
String pageName = originalJson.optString("navPage");
|
||||
int pageId = PAGE_MAP.containsKey(pageName)
|
||||
? PAGE_MAP.get(pageName)
|
||||
: NAV_HOME;
|
||||
navItems.add(new MulberryItemViewModel(
|
||||
this,
|
||||
item.name,
|
||||
item.iconSource,
|
||||
pageId,
|
||||
null
|
||||
));
|
||||
|
||||
Log.d(TAG, "Added item: " + item.name + " | Page: " + pageName + " | PageID: " + pageId);
|
||||
|
||||
|
||||
navItems.add(new MulberryItemViewModel(
|
||||
this,
|
||||
item.name,
|
||||
item.iconSource,
|
||||
pageId, // Maintain page structure
|
||||
// null,
|
||||
null
|
||||
));
|
||||
} catch (Exception e) {
|
||||
Log.e(TAG, "Error creating ViewModel for item: " + item.name, e);
|
||||
}
|
||||
}
|
||||
|
||||
Log.d(TAG, "Loaded " + navItems.size() + " items from prefs");
|
||||
|
||||
} catch (Exception e) {
|
||||
Log.e(TAG, "Error parsing nav items JSON", e);
|
||||
}
|
||||
}
|
||||
|
||||
// Updated model class to store original JSON
|
||||
private static class NavItemViewModel {
|
||||
String name;
|
||||
Object iconSource;
|
||||
int position;
|
||||
boolean enabled;
|
||||
JSONObject originalJson; // Store original JSON for page mapping
|
||||
|
||||
NavItemViewModel(String name, Object iconSource, int position, boolean enabled, JSONObject originalJson) {
|
||||
this.name = name;
|
||||
this.iconSource = iconSource;
|
||||
this.position = position;
|
||||
this.enabled = enabled;
|
||||
this.originalJson = originalJson;
|
||||
}
|
||||
}
|
||||
private Object parseIconSource(Object icon) {
|
||||
if (icon instanceof String) {
|
||||
try {
|
||||
|
@ -492,20 +602,6 @@ public class MulberryViewModel extends BaseAppViewModel {
|
|||
return icon; // Return as-is if not string
|
||||
}
|
||||
|
||||
// Simple model for navigation items
|
||||
private static class NavItemViewModel {
|
||||
String name;
|
||||
Object iconSource;
|
||||
int position;
|
||||
boolean enabled;
|
||||
|
||||
NavItemViewModel(String name, Object iconSource, int position, boolean enabled) {
|
||||
this.name = name;
|
||||
this.iconSource = iconSource;
|
||||
this.position = position;
|
||||
this.enabled = enabled;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onCleared() {
|
||||
|
|
|
@ -4,24 +4,20 @@ import android.app.Dialog;
|
|||
import android.content.Intent;
|
||||
import android.graphics.Bitmap;
|
||||
import android.os.Bundle;
|
||||
import android.os.Handler;
|
||||
import android.text.Spanned;
|
||||
import android.text.method.LinkMovementMethod;
|
||||
import android.util.Base64;
|
||||
import android.util.Log;
|
||||
import android.view.TextureView;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.view.ViewTreeObserver;
|
||||
import android.widget.AdapterView;
|
||||
import android.widget.ArrayAdapter;
|
||||
import android.widget.ImageButton;
|
||||
import android.widget.ListView;
|
||||
|
||||
import androidx.lifecycle.Observer;
|
||||
|
||||
import com.alibaba.fastjson.JSONException;
|
||||
import com.alibaba.fastjson.JSONObject;
|
||||
import com.dspread.pos.TerminalApplication;
|
||||
import com.dspread.pos.common.enums.TransCardMode;
|
||||
import com.dspread.pos.common.manager.QPOSCallbackManager;
|
||||
import com.dspread.pos.posAPI.POS;
|
||||
import com.dspread.pos.posAPI.PaymentServiceCallback;
|
||||
|
@ -31,7 +27,6 @@ import com.dspread.pos.ui.payment.pinkeyboard.MyKeyboardView;
|
|||
import com.dspread.pos.ui.payment.pinkeyboard.PinPadDialog;
|
||||
import com.dspread.pos.ui.payment.pinkeyboard.PinPadView;
|
||||
import com.dspread.pos.utils.BitmapReadyListener;
|
||||
import com.dspread.pos.utils.DevUtils;
|
||||
import com.dspread.pos.utils.DeviceUtils;
|
||||
import com.dspread.pos.utils.HandleTxnsResultUtils;
|
||||
import com.dspread.pos.utils.LogFileConfig;
|
||||
|
@ -56,10 +51,11 @@ import me.goldze.mvvmhabit.utils.SPUtils;
|
|||
import me.goldze.mvvmhabit.utils.ToastUtils;
|
||||
|
||||
// Add these imports at the top
|
||||
import androidx.databinding.DataBindingUtil;
|
||||
import android.widget.FrameLayout;
|
||||
import com.dspread.pos_android_app.databinding.WaitingForCardBinding; // Generated binding class
|
||||
|
||||
import com.dspread.pos.faceID.FaceIDHelper;
|
||||
|
||||
|
||||
public class PaymentActivity extends BaseActivity<ActivityPaymentBinding, PaymentViewModel> implements PaymentServiceCallback {
|
||||
|
||||
|
@ -91,6 +87,9 @@ public class PaymentActivity extends BaseActivity<ActivityPaymentBinding, Paymen
|
|||
private boolean QRMood = false;
|
||||
private boolean FaceIDMood = false;
|
||||
|
||||
private FaceIDHelper faceIDHelper;
|
||||
private boolean isFaceIDActive = false;
|
||||
|
||||
@Override
|
||||
public void initData() {
|
||||
// Debug current locale
|
||||
|
@ -154,11 +153,108 @@ public class PaymentActivity extends BaseActivity<ActivityPaymentBinding, Paymen
|
|||
waitingContainer.addView(waitingBinding.getRoot());
|
||||
|
||||
// Generate QR code when needed
|
||||
// TO-DO fix M50F long time QR generated !!!
|
||||
|
||||
// waitingViewModel.generateQRCode(generatePaymentData());
|
||||
|
||||
// Observe Face ID events
|
||||
waitingViewModel.startFaceID.observe(this, start -> {
|
||||
if (start != null && start) {
|
||||
startFaceIDCamera();
|
||||
}
|
||||
});
|
||||
|
||||
waitingViewModel.faceIDSuccess.observe(this, success -> {
|
||||
if (success != null && success) {
|
||||
// simulatePaymentAfterDelay();
|
||||
}
|
||||
});
|
||||
|
||||
// Set click listener for Face ID container
|
||||
waitingBinding.faceIDContainer.setOnClickListener(v -> {
|
||||
waitingViewModel.onFaceIDClicked();
|
||||
});
|
||||
|
||||
startTransaction();
|
||||
}
|
||||
|
||||
private void startFaceIDCamera() {
|
||||
if (isFaceIDActive) return;
|
||||
|
||||
isFaceIDActive = true;
|
||||
|
||||
// Show camera container - now it's inside the FrameLayout
|
||||
FrameLayout cameraContainer = waitingBinding.getRoot().findViewById(R.id.camera_container);
|
||||
if (cameraContainer != null) {
|
||||
cameraContainer.setVisibility(View.VISIBLE);
|
||||
}
|
||||
|
||||
// Create TextureView for camera preview
|
||||
TextureView textureView = new TextureView(this);
|
||||
textureView.setLayoutParams(new FrameLayout.LayoutParams(
|
||||
FrameLayout.LayoutParams.MATCH_PARENT,
|
||||
FrameLayout.LayoutParams.MATCH_PARENT));
|
||||
|
||||
// Add TextureView to camera container
|
||||
if (cameraContainer != null) {
|
||||
cameraContainer.addView(textureView, 0); // Add at index 0 (behind buttons)
|
||||
}
|
||||
|
||||
// Set up close button
|
||||
ImageButton btnCloseCamera = waitingBinding.getRoot().findViewById(R.id.btn_close_camera);
|
||||
if (btnCloseCamera != null) {
|
||||
btnCloseCamera.setOnClickListener(v -> {
|
||||
closeFaceIDCamera();
|
||||
});
|
||||
}
|
||||
|
||||
// Initialize Face ID helper
|
||||
faceIDHelper = new FaceIDHelper(this, textureView, new FaceIDHelper.FaceIDCallback() {
|
||||
@Override
|
||||
public void onFaceDetected() {
|
||||
Log.d("FaceDetection", "✅ Face detected successfully!");
|
||||
runOnUiThread(() -> {
|
||||
// Face detected - proceed with payment
|
||||
closeFaceIDCamera();
|
||||
// waitingViewModel.simulateFaceIDSuccess();
|
||||
|
||||
FaceIDMood = true;
|
||||
POS.getInstance().cancelTrade();
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCameraError(String error) {
|
||||
Log.e("FaceDetection", "❌ Face detection error: " + error);
|
||||
runOnUiThread(() -> {
|
||||
ToastUtils.showShort(error);
|
||||
closeFaceIDCamera();
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
faceIDHelper.startBackgroundThread();
|
||||
faceIDHelper.startCamera();
|
||||
}
|
||||
|
||||
private void closeFaceIDCamera() {
|
||||
if (!isFaceIDActive) return;
|
||||
|
||||
isFaceIDActive = false;
|
||||
|
||||
// Hide camera container
|
||||
FrameLayout cameraContainer = waitingBinding.getRoot().findViewById(R.id.camera_container);
|
||||
if (cameraContainer != null) {
|
||||
cameraContainer.setVisibility(View.GONE);
|
||||
cameraContainer.removeAllViews();
|
||||
}
|
||||
|
||||
// Clean up camera resources
|
||||
if (faceIDHelper != null) {
|
||||
faceIDHelper.closeCamera();
|
||||
faceIDHelper.stopBackgroundThread();
|
||||
}
|
||||
}
|
||||
private void simulatePaymentAfterDelay() {
|
||||
boolean isTestMode = true; // Set this to false when not testing
|
||||
if (isTestMode) {
|
||||
|
@ -187,7 +283,7 @@ public class PaymentActivity extends BaseActivity<ActivityPaymentBinding, Paymen
|
|||
binding.btnSendReceipt.setVisibility(View.VISIBLE);
|
||||
}
|
||||
},
|
||||
200 // 10 second delay
|
||||
50 // 10 second delay
|
||||
);
|
||||
}
|
||||
});
|
||||
|
@ -671,6 +767,10 @@ public class PaymentActivity extends BaseActivity<ActivityPaymentBinding, Paymen
|
|||
LogFileConfig.getInstance(this).readLog();
|
||||
QPOSCallbackManager.getInstance().unregisterPaymentCallback();
|
||||
PrinterHelper.getInstance().close();
|
||||
if (faceIDHelper != null) {
|
||||
faceIDHelper.closeCamera();
|
||||
faceIDHelper.stopBackgroundThread();
|
||||
}
|
||||
}
|
||||
|
||||
private void convertReceiptToBitmap(final BitmapReadyListener listener) {
|
||||
|
|
|
@ -7,6 +7,7 @@ import android.util.Log;
|
|||
import androidx.annotation.NonNull;
|
||||
import androidx.databinding.ObservableBoolean;
|
||||
import androidx.databinding.ObservableField;
|
||||
import androidx.lifecycle.MutableLiveData;
|
||||
|
||||
import com.dspread.pos.common.manager.QPOSCallbackManager;
|
||||
import com.dspread.pos.printerAPI.PrinterHelper;
|
||||
|
@ -22,7 +23,9 @@ public class WaitingForCardViewModel extends BaseViewModel {
|
|||
|
||||
public ObservableField<String> amount = new ObservableField<>("0.00");
|
||||
public ObservableField<String> currencySymbol = new ObservableField<>("$");
|
||||
|
||||
// Add Face ID events
|
||||
public MutableLiveData<Boolean> startFaceID = new MutableLiveData<>();
|
||||
public MutableLiveData<Boolean> faceIDSuccess = new MutableLiveData<>();
|
||||
public WaitingForCardViewModel(@NonNull Application application) {
|
||||
super(application);
|
||||
}
|
||||
|
@ -48,7 +51,7 @@ public class WaitingForCardViewModel extends BaseViewModel {
|
|||
}
|
||||
public void generateQRCode(String paymentData) {
|
||||
try {
|
||||
Bitmap qrCode = QRCodeGenerator.generateQRCode(paymentData, 500, 500);
|
||||
Bitmap qrCode = QRCodeGenerator.generateQRCode(paymentData, 300, 300);
|
||||
if (qrCode != null) {
|
||||
qrCodeBitmap.set(qrCode);
|
||||
Log.d("QRCode", "QR code generated successfully");
|
||||
|
@ -60,6 +63,16 @@ public class WaitingForCardViewModel extends BaseViewModel {
|
|||
}
|
||||
}
|
||||
|
||||
// Add Face ID methods
|
||||
public void onFaceIDClicked() {
|
||||
startFaceID.setValue(true);
|
||||
}
|
||||
|
||||
public void simulateFaceIDSuccess() {
|
||||
// This will be called when face is detected
|
||||
faceIDSuccess.setValue(true);
|
||||
}
|
||||
|
||||
public void onCancel() {
|
||||
// Handle cancel action
|
||||
Log.d("WaitingForCardViewModel", "onCancel: ");
|
||||
|
|
|
@ -276,7 +276,7 @@ public class PosParameters {
|
|||
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("pinpad", R.drawable.ax_pinpad_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);
|
||||
|
|
|
@ -0,0 +1,179 @@
|
|||
<vector xmlns:aapt="http://schemas.android.com/aapt" xmlns:android="http://schemas.android.com/apk/res/android" android:height="250dp" android:viewportHeight="1024" android:viewportWidth="1024" android:width="250dp">
|
||||
|
||||
<path android:pathData="M263.9,96.4c-9.8,2.6 -17.1,8.9 -21.7,18.6 -1.6,3.2 -1.7,28 -1.7,340.5l0,337 3.4,6.3c3.7,6.8 8.3,11 15.8,14.5 4.5,2.1 6.3,2.2 32.1,2.5l27.2,0.3 0,35.3c0,37.9 0.3,40.2 5.6,50.8 3.8,7.4 12.5,16.1 20.4,20.2 13,6.9 4.8,6.6 167,6.6 159.5,-0 153.5,0.2 165.7,-5.7 7.2,-3.4 18.1,-14 21.5,-20.8 5.6,-10.9 5.8,-13 5.8,-51.1l0,-35.3 27.3,-0.3c26.9,-0.3 27.3,-0.3 32.8,-2.9 6.8,-3.2 12,-8.2 15.7,-15.2l2.7,-5.2 0,-337 0,-337 -3.4,-6.3c-3.7,-6.8 -8.3,-11 -15.8,-14.5l-4.8,-2.2 -245.5,-0.2c-192.4,-0.1 -246.5,0.1 -250.1,1.1zM753,455.5l0,329.5 -241,-0 -241,-0 0,-329.5 0,-329.5 241,-0 241,-0 0,329.5zM674.8,849.7c-0.3,38.4 -0.4,38.9 -8.7,44.8l-4.3,3 -149.8,-0 -149.8,-0 -4.3,-3c-8.3,-5.9 -8.4,-6.4 -8.7,-44.8l-0.3,-33.7 163.1,-0 163.1,-0 -0.3,33.7z" android:strokeColor="#00000000">
|
||||
|
||||
<aapt:attr name="android:fillColor">
|
||||
|
||||
<gradient android:endX="240.5" android:endY="929" android:startX="240.5" android:startY="95.3" android:type="linear">
|
||||
|
||||
<item android:color="#FFACAFCA" android:offset="0"/>
|
||||
|
||||
<item android:color="#FF44454E" android:offset="1"/>
|
||||
|
||||
</gradient>
|
||||
|
||||
</aapt:attr>
|
||||
|
||||
</path>
|
||||
|
||||
<path android:pathData="M327.3,175c-2.5,1.1 -5.1,3.3 -6.3,5.2 -2,3.2 -2,4.8 -2,89.3 0,83.6 0.1,86.1 2,89.2 1,1.8 3.7,4.1 5.9,5.3 4,2 4.8,2 185.1,2 180.3,-0 181.1,-0 185.1,-2 2.2,-1.2 4.9,-3.5 5.9,-5.3 1.9,-3.1 2,-5.6 2,-89.2 0,-84.5 0,-86.1 -2,-89.3 -1.2,-1.9 -3.8,-4.1 -6.3,-5.2 -4.1,-1.9 -8.8,-2 -184.7,-2 -175.9,-0 -180.6,0.1 -184.7,2zM675,269.5l0,65.5 -163,-0 -163,-0 0,-65.5 0,-65.5 163,-0 163,-0 0,65.5z" android:strokeColor="#00000000">
|
||||
|
||||
<aapt:attr name="android:fillColor">
|
||||
|
||||
<gradient android:endX="319" android:endY="366" android:startX="319" android:startY="173" 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,429.5c-29.6,8.3 -44.1,40 -30.7,67.3 6.9,14.2 20.4,24 36.3,26.5 18.3,2.8 38.7,-7.3 48,-23.7 14.3,-25.2 2.5,-58.3 -24.6,-68.6 -7.5,-2.9 -21.6,-3.6 -29,-1.5zM366.9,460.5c7,3.5 11,12.3 9.1,19.5 -3.7,13.7 -20.2,17.5 -29.7,6.7 -7,-8 -4.5,-21.4 4.9,-26 4.1,-2.1 12,-2.2 15.7,-0.2z" android:strokeColor="#00000000">
|
||||
|
||||
<aapt:attr name="android:fillColor">
|
||||
|
||||
<gradient android:endX="311.6" android:endY="523.8" android:startX="311.6" android:startY="428.3" android:type="linear">
|
||||
|
||||
<item android:color="#FFACAFCA" android:offset="0"/>
|
||||
|
||||
<item android:color="#FF44454E" android:offset="1"/>
|
||||
|
||||
</gradient>
|
||||
|
||||
</aapt:attr>
|
||||
|
||||
</path>
|
||||
|
||||
<path android:pathData="M500,429.4c-18.1,5.1 -32.1,20.6 -35.3,38.9 -5.3,31.3 23.2,60 54.5,54.8 18.9,-3.1 34.2,-16.9 39,-35 4.5,-17.4 -0.5,-34.6 -13.7,-47 -6.7,-6.2 -12.4,-9.5 -20.8,-11.7 -6.3,-1.7 -17.6,-1.7 -23.7,-0zM521,461.2c5.4,3.7 7.3,7.1 7.7,13.5 0.3,4.7 -0.1,6.6 -1.9,9.8 -2.7,4.7 -9.4,8.5 -14.8,8.5 -5.4,-0 -12.1,-3.8 -14.8,-8.5 -1.8,-3.2 -2.2,-5.1 -1.9,-9.8 0.6,-9.8 6.9,-15.7 16.7,-15.7 4.3,-0 6.5,0.6 9,2.2z" android:strokeColor="#00000000">
|
||||
|
||||
<aapt:attr name="android:fillColor">
|
||||
|
||||
<gradient android:endX="464.1" android:endY="523.7" android:startX="464.1" android:startY="428.1" android:type="linear">
|
||||
|
||||
<item android:color="#FFACAFCA" android:offset="0"/>
|
||||
|
||||
<item android:color="#FF44454E" android:offset="1"/>
|
||||
|
||||
</gradient>
|
||||
|
||||
</aapt:attr>
|
||||
|
||||
</path>
|
||||
|
||||
<path android:pathData="M654.3,429.5c-25.4,6.9 -40.5,33.3 -33.7,59 5.5,21.3 27.8,37.1 49.6,35.1 41.9,-3.8 59.5,-54.3 28.9,-82.9 -11.5,-10.8 -29.5,-15.3 -44.8,-11.2zM675,460.9c9.6,5.1 11.8,17.9 4.4,26.1 -6.2,6.8 -15.9,7.9 -22.9,2.6 -10,-7.7 -9.1,-22 1.9,-28.7 4.1,-2.5 12,-2.5 16.6,-0z" android:strokeColor="#00000000">
|
||||
|
||||
<aapt:attr name="android:fillColor">
|
||||
|
||||
<gradient android:endX="619" android:endY="523.8" android:startX="619" android:startY="428" android:type="linear">
|
||||
|
||||
<item android:color="#FFACAFCA" android:offset="0"/>
|
||||
|
||||
<item android:color="#FF44454E" android:offset="1"/>
|
||||
|
||||
</gradient>
|
||||
|
||||
</aapt:attr>
|
||||
|
||||
</path>
|
||||
|
||||
<path android:pathData="M350.1,537.1c-7.9,1.5 -18.4,7.3 -24.4,13.2 -19.4,19.5 -18.7,51 1.6,69.3 21.9,19.7 56.1,15.4 71.9,-9 10.1,-15.7 10.5,-36.5 0.9,-51.5 -10.9,-16.9 -31,-25.8 -50,-22zM366.6,568.6c13,6.2 13.1,24.3 0.2,31 -3.6,1.9 -11.2,1.8 -15.4,-0.2 -9.5,-4.5 -12.2,-18 -5.1,-26.1 5.4,-6.2 13.3,-8 20.3,-4.7z" android:strokeColor="#00000000">
|
||||
|
||||
<aapt:attr name="android:fillColor">
|
||||
|
||||
<gradient android:endX="311.6" android:endY="632" android:startX="311.6" android:startY="536.2" android:type="linear">
|
||||
|
||||
<item android:color="#FFACAFCA" android:offset="0"/>
|
||||
|
||||
<item android:color="#FF44454E" android:offset="1"/>
|
||||
|
||||
</gradient>
|
||||
|
||||
</aapt:attr>
|
||||
|
||||
</path>
|
||||
|
||||
<path android:pathData="M502.5,537.2c-13.4,2.5 -27.2,13.2 -33.4,26 -12.3,25.1 -0.4,55.4 26,65.9 6.8,2.7 19.7,3.6 27,1.9 21.8,-5 38.1,-25.3 37.8,-47 -0.3,-29.9 -27.9,-52.5 -57.4,-46.8zM520.1,569.3c6.3,3.3 9.1,8.5 8.6,16 -0.6,9.8 -6.9,15.7 -16.7,15.7 -9.8,-0 -16.1,-5.9 -16.7,-15.7 -0.3,-4.7 0.1,-6.6 1.9,-9.7 2.7,-4.7 9.5,-8.6 14.8,-8.6 2,-0 5.7,1 8.1,2.3z" android:strokeColor="#00000000">
|
||||
|
||||
<aapt:attr name="android:fillColor">
|
||||
|
||||
<gradient android:endX="464.2" android:endY="631.9" android:startX="464.2" android:startY="536.3" android:type="linear">
|
||||
|
||||
<item android:color="#FFACAFCA" android:offset="0"/>
|
||||
|
||||
<item android:color="#FF44454E" android:offset="1"/>
|
||||
|
||||
</gradient>
|
||||
|
||||
</aapt:attr>
|
||||
|
||||
</path>
|
||||
|
||||
<path android:pathData="M656.3,537.5c-17.2,3.9 -32.4,18.8 -36.2,35.6 -1.7,7.3 -1.3,18.1 0.9,25.3 4.2,13.3 15.7,25.7 28.7,30.6 7.3,2.8 20.1,3.7 27.4,2 24.4,-5.8 40.7,-29.4 36.9,-53.7 -1.3,-8.1 -6.3,-19.2 -11.4,-24.9 -10.8,-12.5 -30,-18.7 -46.3,-14.9zM673.3,568.4c4.4,1.8 9.5,7.8 10.3,12.1 1.4,7.5 -2.2,15.2 -8.6,18.6 -4.2,2.3 -12,2.5 -15.9,0.4 -4,-2 -7.9,-6.9 -9,-11.3 -3.5,-13.2 10.5,-25.2 23.2,-19.8z" android:strokeColor="#00000000">
|
||||
|
||||
<aapt:attr name="android:fillColor">
|
||||
|
||||
<gradient android:endX="619" android:endY="631.9" android:startX="619" android:startY="536.4" android:type="linear">
|
||||
|
||||
<item android:color="#FFACAFCA" android:offset="0"/>
|
||||
|
||||
<item android:color="#FF44454E" android:offset="1"/>
|
||||
|
||||
</gradient>
|
||||
|
||||
</aapt:attr>
|
||||
|
||||
</path>
|
||||
|
||||
<path android:pathData="M344.8,646.9c-16.7,5.4 -30.3,21.4 -32.7,38.7 -4.5,31.3 22.4,58.6 53.4,54.3 12.1,-1.6 21.1,-6.4 29.4,-15.5 9.1,-9.9 12.6,-20.4 11.9,-34.9 -0.4,-8.1 -1,-10.4 -4,-16.7 -6.2,-12.8 -15.7,-21.4 -28.6,-25.7 -8,-2.7 -21.3,-2.8 -29.4,-0.2zM368.7,678.1c7.9,4.9 10.2,15.9 4.9,23.9 -3,4.5 -9,7.9 -14.1,8 -4.4,-0 -11.6,-4 -14.2,-7.9 -10.3,-15.4 7.6,-33.8 23.4,-24z" android:strokeColor="#00000000">
|
||||
|
||||
<aapt:attr name="android:fillColor">
|
||||
|
||||
<gradient android:endX="311.6" android:endY="740.3" android:startX="311.6" android:startY="645" android:type="linear">
|
||||
|
||||
<item android:color="#FFACAFCA" android:offset="0"/>
|
||||
|
||||
<item android:color="#FF44454E" android:offset="1"/>
|
||||
|
||||
</gradient>
|
||||
|
||||
</aapt:attr>
|
||||
|
||||
</path>
|
||||
|
||||
<path android:pathData="M496.8,647.3c-23.9,8.2 -37,32 -31.3,56.6 4.4,18.6 20.7,33.3 39.8,36 37.4,5.2 66.1,-33.4 50.1,-67.6 -10.2,-21.7 -35.9,-32.7 -58.6,-25zM516.2,676c7.7,2.2 12.8,8.9 12.8,17 0,13.9 -17.2,21.9 -27.6,12.7 -4.5,-3.9 -6.4,-7.6 -6.4,-12.7 0,-7.8 4.3,-13.9 11.7,-16.5 4.7,-1.7 4.9,-1.7 9.5,-0.5z" android:strokeColor="#00000000">
|
||||
|
||||
<aapt:attr name="android:fillColor">
|
||||
|
||||
<gradient android:endX="464.2" android:endY="740.4" android:startX="464.2" android:startY="644.8" android:type="linear">
|
||||
|
||||
<item android:color="#FFACAFCA" android:offset="0"/>
|
||||
|
||||
<item android:color="#FF44454E" android:offset="1"/>
|
||||
|
||||
</gradient>
|
||||
|
||||
</aapt:attr>
|
||||
|
||||
</path>
|
||||
|
||||
<path android:pathData="M653.5,646.4c-20.7,6.6 -34.5,25 -34.5,46 0.1,29.5 24.9,51.1 54.5,47.5 25.4,-3.1 44.6,-28.8 40.5,-54.3 -2.9,-18.4 -16.4,-33.9 -33.9,-39 -6.5,-1.9 -20.9,-2 -26.6,-0.2zM671.2,676c15.3,4.3 16.9,25.5 2.4,32.1 -5.2,2.3 -9,2.4 -14.2,-0 -10.6,-4.8 -13.1,-19.3 -4.7,-27.7 4.8,-4.8 10,-6.2 16.5,-4.4z" android:strokeColor="#00000000">
|
||||
|
||||
<aapt:attr name="android:fillColor">
|
||||
|
||||
<gradient android:endX="619" android:endY="740.3" android:startX="619" android:startY="645.1" android:type="linear">
|
||||
|
||||
<item android:color="#FFACAFCA" android:offset="0"/>
|
||||
|
||||
<item android:color="#FF44454E" android:offset="1"/>
|
||||
|
||||
</gradient>
|
||||
|
||||
</aapt:attr>
|
||||
|
||||
</path>
|
||||
|
||||
</vector>
|
|
@ -0,0 +1,9 @@
|
|||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24">
|
||||
<path
|
||||
android:fillColor="@android:color/white"
|
||||
android:pathData="M19,6.41L17.59,5 12,10.59 6.41,5 5,6.41 10.59,12 5,17.59 6.41,19 12,13.41 17.59,19 19,17.59 13.41,12z" />
|
||||
</vector>
|
|
@ -0,0 +1,5 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<shape xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<solid android:color="#80000000" />
|
||||
<corners android:radius="24dp" />
|
||||
</shape>
|
|
@ -5,6 +5,10 @@
|
|||
xmlns:binding="http://schemas.android.com/apk/res-auto">
|
||||
|
||||
<data>
|
||||
<variable
|
||||
name="view"
|
||||
type="com.dspread.pos.ui.mulberry.MulberryFragment" />
|
||||
|
||||
<variable
|
||||
name="viewModel"
|
||||
type="com.dspread.pos.ui.mulberry.MulberryViewModel" />
|
||||
|
@ -33,11 +37,18 @@
|
|||
android:layout_height="wrap_content"
|
||||
android:gravity="center"
|
||||
android:orientation="vertical"
|
||||
android:paddingTop="10dp"
|
||||
android:paddingBottom="10dp"
|
||||
|
||||
|
||||
|
||||
app:layout_constraintBottom_toTopOf="@id/gridLayout"
|
||||
|
||||
|
||||
app:layout_constraintVertical_bias="0.5"
|
||||
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent">
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
>
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/ivLogo"
|
||||
|
@ -55,12 +66,18 @@
|
|||
android:id="@+id/gridLayout"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
|
||||
android:alignmentMode="alignMargins"
|
||||
android:columnCount="2"
|
||||
android:columnOrderPreserved="false"
|
||||
android:rowCount="2"
|
||||
android:layout_marginTop="5dp"
|
||||
|
||||
|
||||
app:layout_constraintBottom_toTopOf="@id/centerContainer"
|
||||
|
||||
app:layout_constraintVertical_bias="0.5"
|
||||
|
||||
app:layout_constraintTop_toBottomOf="@id/topLogoContainer"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
|
@ -102,7 +119,7 @@
|
|||
android:id="@+id/card"
|
||||
android:layout_width="140dp"
|
||||
android:layout_height="@dimen/main_grid_image_height_d30"
|
||||
android:scaleType="centerCrop"
|
||||
android:scaleType="fitCenter"
|
||||
android:src="@drawable/homo_card_black_box"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:layout_constraintBottom_toTopOf="@+id/cardText"
|
||||
|
|
|
@ -22,10 +22,11 @@
|
|||
android:layout_height="wrap_content"
|
||||
android:gravity="center"
|
||||
android:orientation="vertical"
|
||||
android:padding="16dp">
|
||||
android:padding="14dp">
|
||||
|
||||
<ImageView
|
||||
android:layout_width="100dp"
|
||||
android:id="@+id/navImage"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="100dp"
|
||||
android:src="@{viewModel.icon}"
|
||||
android:scaleType="fitCenter"
|
||||
|
|
|
@ -5,200 +5,255 @@
|
|||
xmlns:tools="http://schemas.android.com/tools">
|
||||
|
||||
<data>
|
||||
<variable
|
||||
name="vm"
|
||||
type="com.dspread.pos.ui.payment.WaitingForCardViewModel" />
|
||||
<variable name="vm" type="com.dspread.pos.ui.payment.WaitingForCardViewModel" />
|
||||
</data>
|
||||
|
||||
<LinearLayout
|
||||
<!-- Use FrameLayout as root to contain both camera overlay and main content -->
|
||||
<FrameLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:gravity="center"
|
||||
android:orientation="vertical"
|
||||
android:padding="16dp">
|
||||
android:layout_height="match_parent">
|
||||
|
||||
<!-- Card animation GIF -->
|
||||
<!-- Main Content -->
|
||||
<LinearLayout
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:gravity="center">
|
||||
<ImageView
|
||||
android:paddingTop="10dp"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="300dp"
|
||||
android:src="@drawable/melberry_char_purple"
|
||||
android:scaleType="fitCenter">
|
||||
|
||||
</ImageView>
|
||||
|
||||
<!-- <pl.droidsonroids.gif.GifImageView-->
|
||||
<!-- android:layout_width="200dp"-->
|
||||
<!-- android:layout_height="200dp"-->
|
||||
<!-- android:src="@drawable/checkcard" />-->
|
||||
</LinearLayout>
|
||||
|
||||
<!-- Instruction text -->
|
||||
<!-- <TextView-->
|
||||
<!-- android:layout_width="wrap_content"-->
|
||||
<!-- android:layout_height="wrap_content"-->
|
||||
<!-- android:layout_marginTop="16dp"-->
|
||||
<!-- android:text="@string/scan_or_tap_instruction"-->
|
||||
<!-- android:textAlignment="center"-->
|
||||
<!-- android:textSize="16sp" />-->
|
||||
|
||||
<!-- Amount and currency section -->
|
||||
<LinearLayout
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="16dp"
|
||||
android:orientation="horizontal"
|
||||
android:gravity="center"
|
||||
android:layout_weight="1">
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@{vm.amount}"
|
||||
android:textSize="36sp"
|
||||
android:textColor="#2C2929"
|
||||
tools:text="100.00" />
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@{vm.currencySymbol}"
|
||||
android:textSize="36sp"
|
||||
android:layout_marginLeft="8dp"
|
||||
android:textColor="#2C2929"
|
||||
tools:text="$" />
|
||||
</LinearLayout>
|
||||
|
||||
<!-- Divided section for QR code and Face ID -->
|
||||
<LinearLayout
|
||||
android:id="@+id/twoCardsContainer"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="20dp"
|
||||
android:orientation="horizontal"
|
||||
android:weightSum="2"
|
||||
android:paddingHorizontal="1dp">
|
||||
android:layout_height="match_parent"
|
||||
android:gravity="center"
|
||||
android:orientation="vertical"
|
||||
android:padding="16dp">
|
||||
|
||||
<!-- Left side - Face ID Card -->
|
||||
<androidx.cardview.widget.CardView
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="260dp"
|
||||
android:layout_marginEnd="2dp"
|
||||
<!-- Card animation GIF -->
|
||||
<LinearLayout
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
app:cardCornerRadius="18dp"
|
||||
app:cardElevation="2dp"
|
||||
app:cardUseCompatPadding="true">
|
||||
|
||||
<!-- Outer Card (White Background) -->
|
||||
<LinearLayout
|
||||
android:gravity="center">
|
||||
<ImageView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:orientation="vertical"
|
||||
android:background="#8c1084"
|
||||
android:padding="5dp">
|
||||
android:layout_height="@dimen/dp_300"
|
||||
android:paddingTop="10dp"
|
||||
android:scaleType="fitCenter"
|
||||
android:src="@drawable/melberry_char_purple" />
|
||||
</LinearLayout>
|
||||
|
||||
<!-- Inner Card (Purple Background) -->
|
||||
<androidx.cardview.widget.CardView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="170dp"
|
||||
app:cardCornerRadius="12dp"
|
||||
android:layout_gravity="center"
|
||||
android:background="@android:color/holo_purple">
|
||||
<!-- Amount and currency section -->
|
||||
<LinearLayout
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="16dp"
|
||||
android:orientation="horizontal"
|
||||
android:gravity="center"
|
||||
android:layout_weight="1">
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@{vm.amount}"
|
||||
android:textSize="36sp"
|
||||
android:textColor="#2C2929"
|
||||
tools:text="100.00" />
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@{vm.currencySymbol}"
|
||||
android:textSize="36sp"
|
||||
android:layout_marginLeft="8dp"
|
||||
android:textColor="#2C2929"
|
||||
tools:text="$" />
|
||||
</LinearLayout>
|
||||
|
||||
<!-- Center the GIF Image -->
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:gravity="center"
|
||||
android:orientation="vertical">
|
||||
<!-- Divided section for QR code and Face ID -->
|
||||
<LinearLayout
|
||||
android:id="@+id/twoCardsContainer"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="20dp"
|
||||
android:orientation="horizontal"
|
||||
android:weightSum="2"
|
||||
android:paddingHorizontal="1dp">
|
||||
|
||||
<pl.droidsonroids.gif.GifImageView
|
||||
android:layout_width="98dp"
|
||||
android:layout_height="124dp"
|
||||
android:src="@drawable/faceid_180px" />
|
||||
</LinearLayout>
|
||||
</androidx.cardview.widget.CardView>
|
||||
<!-- Left side - Face ID Card -->
|
||||
<androidx.cardview.widget.CardView
|
||||
android:id="@+id/faceIDContainer"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="260dp"
|
||||
android:layout_marginEnd="2dp"
|
||||
android:layout_weight="1"
|
||||
app:cardCornerRadius="18dp"
|
||||
app:cardElevation="2dp"
|
||||
app:cardUseCompatPadding="true">
|
||||
|
||||
<!-- Text in Outer Card -->
|
||||
<!-- Outer Card (White Background) -->
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="10dp"
|
||||
android:orientation="horizontal"
|
||||
android:gravity="center_vertical"
|
||||
android:layout_gravity="center">
|
||||
android:layout_height="match_parent"
|
||||
android:orientation="vertical"
|
||||
android:background="#8c1084"
|
||||
android:padding="5dp">
|
||||
|
||||
<!-- Left Text: "Оплатите лицом" -->
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="Оплатить\nлицом"
|
||||
android:textSize="18sp"
|
||||
android:paddingLeft="10dp"
|
||||
android:textStyle="normal"
|
||||
android:textColor="@color/white"
|
||||
android:layout_weight="1" />
|
||||
<!-- Inner Card (Purple Background) -->
|
||||
<androidx.cardview.widget.CardView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="170dp"
|
||||
app:cardCornerRadius="12dp"
|
||||
android:layout_gravity="center"
|
||||
android:background="@android:color/holo_purple">
|
||||
|
||||
<!-- Right Text: ">" -->
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
<!-- Center the GIF Image -->
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:gravity="center"
|
||||
android:orientation="vertical">
|
||||
<pl.droidsonroids.gif.GifImageView
|
||||
android:layout_width="98dp"
|
||||
android:layout_height="124dp"
|
||||
android:src="@drawable/faceid" />
|
||||
</LinearLayout>
|
||||
</androidx.cardview.widget.CardView>
|
||||
|
||||
<!-- Text in Outer Card -->
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:textSize="20sp"
|
||||
android:textStyle="bold"
|
||||
android:paddingRight="10dp"
|
||||
android:text=">"
|
||||
android:textColor="@color/white" />
|
||||
android:layout_marginTop="10dp"
|
||||
android:orientation="horizontal"
|
||||
android:gravity="center_vertical"
|
||||
android:layout_gravity="center">
|
||||
|
||||
<!-- Left Text: "Оплатите лицом" -->
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="Оплатить\nлицом"
|
||||
android:textSize="18sp"
|
||||
android:paddingLeft="10dp"
|
||||
android:textStyle="normal"
|
||||
android:textColor="@color/white"
|
||||
android:layout_weight="1" />
|
||||
|
||||
<!-- Right Text: ">" -->
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:textSize="20sp"
|
||||
android:textStyle="bold"
|
||||
android:paddingRight="10dp"
|
||||
android:text=">"
|
||||
android:textColor="@color/white" />
|
||||
</LinearLayout>
|
||||
</LinearLayout>
|
||||
</LinearLayout>
|
||||
</androidx.cardview.widget.CardView>
|
||||
</androidx.cardview.widget.CardView>
|
||||
|
||||
<!-- Right side - QR Code Card -->
|
||||
<androidx.cardview.widget.CardView
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="260dp"
|
||||
android:layout_marginStart="2dp"
|
||||
android:layout_weight="1"
|
||||
app:cardCornerRadius="18dp"
|
||||
app:cardElevation="2dp"
|
||||
app:cardUseCompatPadding="true">
|
||||
<!-- Right side - QR Code Card -->
|
||||
<androidx.cardview.widget.CardView
|
||||
android:id="@+id/qrCodeContainer"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="260dp"
|
||||
android:layout_marginStart="2dp"
|
||||
android:layout_weight="1"
|
||||
app:cardCornerRadius="18dp"
|
||||
app:cardElevation="2dp"
|
||||
app:cardUseCompatPadding="true">
|
||||
|
||||
<!-- Single Card with Purple Background -->
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:orientation="vertical"
|
||||
android:background="#8c1084"
|
||||
android:padding="3dp">
|
||||
|
||||
<!-- QR Code Image -->
|
||||
<ImageView
|
||||
android:id="@+id/qr_code_image"
|
||||
<!-- Single Card with Purple Background -->
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="170dp"
|
||||
android:scaleType="fitCenter"
|
||||
android:src="@drawable/am_qr_code_mag_black"
|
||||
android:contentDescription="@string/qr_code_description"
|
||||
app:imageBitmap="@{vm.qrCodeBitmap}" />
|
||||
android:layout_height="match_parent"
|
||||
android:orientation="vertical"
|
||||
android:background="#8c1084"
|
||||
android:padding="3dp">
|
||||
|
||||
<!-- Text Under QR Code -->
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="10dp"
|
||||
android:text="Отсканируйте\nQR-код"
|
||||
android:textColor="@color/white"
|
||||
android:textSize="18sp"
|
||||
android:textStyle="normal"
|
||||
android:paddingBottom="5dp"
|
||||
android:gravity="left"
|
||||
android:layout_gravity="center" />
|
||||
</LinearLayout>
|
||||
</androidx.cardview.widget.CardView>
|
||||
<!-- QR Code Image -->
|
||||
<ImageView
|
||||
android:id="@+id/qr_code_image"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="170dp"
|
||||
android:scaleType="fitCenter"
|
||||
android:src="@drawable/am_qr_code_mag_black"
|
||||
android:contentDescription="@string/qr_code_description"
|
||||
app:imageBitmap="@{vm.qrCodeBitmap}" />
|
||||
|
||||
<!-- Text Under QR Code -->
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="10dp"
|
||||
android:text="Отсканируйте\nQR-код"
|
||||
android:textColor="@color/white"
|
||||
android:textSize="18sp"
|
||||
android:textStyle="normal"
|
||||
android:paddingBottom="5dp"
|
||||
android:gravity="left"
|
||||
android:layout_gravity="center" />
|
||||
</LinearLayout>
|
||||
</androidx.cardview.widget.CardView>
|
||||
</LinearLayout>
|
||||
</LinearLayout>
|
||||
</LinearLayout>
|
||||
|
||||
<!-- Camera Container (Full Screen Overlay) -->
|
||||
<FrameLayout
|
||||
android:id="@+id/camera_container"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:gravity="center"
|
||||
android:visibility="visible"
|
||||
android:background="@android:color/black">
|
||||
|
||||
<!-- Camera Preview will be added here programmatically -->
|
||||
|
||||
<FrameLayout
|
||||
android:id="@+id/camera_preview_holder"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="0dp"
|
||||
android:layout_weight="1"
|
||||
android:scaleType="centerCrop"
|
||||
android:layout_gravity="center">
|
||||
|
||||
<!-- Camera Preview will be added here -->
|
||||
|
||||
</FrameLayout>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/amountOnCamera"
|
||||
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_margin="24dp"
|
||||
android:padding="10dp"
|
||||
|
||||
android:text="@{vm.amount + ` `+vm.currencySymbol}"
|
||||
android:textSize="36sp"
|
||||
android:textStyle="bold"
|
||||
android:background="@drawable/rounded_bg_black_50"
|
||||
android:textColor="@color/white"
|
||||
tools:text="100.00" />
|
||||
|
||||
|
||||
<!-- Close button for camera -->
|
||||
<ImageButton
|
||||
android:id="@+id/btn_close_camera"
|
||||
android:layout_width="48dp"
|
||||
android:layout_height="48dp"
|
||||
android:layout_margin="37dp"
|
||||
android:src="@drawable/ic_close_white"
|
||||
android:background="@drawable/rounded_bg_black_50"
|
||||
android:scaleType="center"
|
||||
android:contentDescription="Close_camera"
|
||||
android:layout_gravity="top|end" />
|
||||
|
||||
<!-- Face detection guidance -->
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="Смотрите в камеру\nЛицо должно быть в рамке"
|
||||
android:textColor="@color/white"
|
||||
android:textSize="18sp"
|
||||
android:textAlignment="center"
|
||||
android:gravity="center"
|
||||
android:layout_gravity="center"
|
||||
android:padding="16dp"
|
||||
android:background="@drawable/rounded_bg_black_50"
|
||||
android:visibility="gone" />
|
||||
|
||||
</FrameLayout>
|
||||
</FrameLayout>
|
||||
</layout>
|
Loading…
Reference in New Issue