Face ID first attemp, on waiting_for-card layout
This commit is contained in:
parent
50796a6951
commit
c6cb612c99
|
@ -1,6 +1,11 @@
|
||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
xmlns:tools="http://schemas.android.com/tools">
|
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.WAKE_LOCK" />
|
||||||
<uses-permission android:name="android.permission.READ_PHONE_STATE" />
|
<uses-permission android:name="android.permission.READ_PHONE_STATE" />
|
||||||
<uses-permission android:name="android.permission.INTERNET" />
|
<uses-permission android:name="android.permission.INTERNET" />
|
||||||
|
|
|
@ -9,11 +9,13 @@ import android.text.Spanned;
|
||||||
import android.text.method.LinkMovementMethod;
|
import android.text.method.LinkMovementMethod;
|
||||||
import android.util.Base64;
|
import android.util.Base64;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
|
import android.view.TextureView;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.view.ViewGroup;
|
import android.view.ViewGroup;
|
||||||
import android.view.ViewTreeObserver;
|
import android.view.ViewTreeObserver;
|
||||||
import android.widget.AdapterView;
|
import android.widget.AdapterView;
|
||||||
import android.widget.ArrayAdapter;
|
import android.widget.ArrayAdapter;
|
||||||
|
import android.widget.ImageButton;
|
||||||
import android.widget.ListView;
|
import android.widget.ListView;
|
||||||
|
|
||||||
import androidx.lifecycle.Observer;
|
import androidx.lifecycle.Observer;
|
||||||
|
@ -60,6 +62,9 @@ import androidx.databinding.DataBindingUtil;
|
||||||
import android.widget.FrameLayout;
|
import android.widget.FrameLayout;
|
||||||
import com.dspread.pos_android_app.databinding.WaitingForCardBinding; // Generated binding class
|
import com.dspread.pos_android_app.databinding.WaitingForCardBinding; // Generated binding class
|
||||||
|
|
||||||
|
import android.widget.FrameLayout;
|
||||||
|
import com.dspread.pos.utils.FaceIDHelper;
|
||||||
|
|
||||||
|
|
||||||
public class PaymentActivity extends BaseActivity<ActivityPaymentBinding, PaymentViewModel> implements PaymentServiceCallback {
|
public class PaymentActivity extends BaseActivity<ActivityPaymentBinding, PaymentViewModel> implements PaymentServiceCallback {
|
||||||
|
|
||||||
|
@ -91,6 +96,9 @@ public class PaymentActivity extends BaseActivity<ActivityPaymentBinding, Paymen
|
||||||
private boolean QRMood = false;
|
private boolean QRMood = false;
|
||||||
private boolean FaceIDMood = false;
|
private boolean FaceIDMood = false;
|
||||||
|
|
||||||
|
private FaceIDHelper faceIDHelper;
|
||||||
|
private boolean isFaceIDActive = false;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void initData() {
|
public void initData() {
|
||||||
// Debug current locale
|
// Debug current locale
|
||||||
|
@ -154,11 +162,103 @@ public class PaymentActivity extends BaseActivity<ActivityPaymentBinding, Paymen
|
||||||
waitingContainer.addView(waitingBinding.getRoot());
|
waitingContainer.addView(waitingBinding.getRoot());
|
||||||
|
|
||||||
// Generate QR code when needed
|
// Generate QR code when needed
|
||||||
|
// TO-DO fix M50F long time QR generated !!!
|
||||||
|
|
||||||
// waitingViewModel.generateQRCode(generatePaymentData());
|
// 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();
|
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() {
|
||||||
|
runOnUiThread(() -> {
|
||||||
|
// Face detected - proceed with payment
|
||||||
|
closeFaceIDCamera();
|
||||||
|
waitingViewModel.simulateFaceIDSuccess();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onCameraError(String 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() {
|
private void simulatePaymentAfterDelay() {
|
||||||
boolean isTestMode = true; // Set this to false when not testing
|
boolean isTestMode = true; // Set this to false when not testing
|
||||||
if (isTestMode) {
|
if (isTestMode) {
|
||||||
|
@ -671,6 +771,10 @@ public class PaymentActivity extends BaseActivity<ActivityPaymentBinding, Paymen
|
||||||
LogFileConfig.getInstance(this).readLog();
|
LogFileConfig.getInstance(this).readLog();
|
||||||
QPOSCallbackManager.getInstance().unregisterPaymentCallback();
|
QPOSCallbackManager.getInstance().unregisterPaymentCallback();
|
||||||
PrinterHelper.getInstance().close();
|
PrinterHelper.getInstance().close();
|
||||||
|
if (faceIDHelper != null) {
|
||||||
|
faceIDHelper.closeCamera();
|
||||||
|
faceIDHelper.stopBackgroundThread();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void convertReceiptToBitmap(final BitmapReadyListener listener) {
|
private void convertReceiptToBitmap(final BitmapReadyListener listener) {
|
||||||
|
|
|
@ -7,6 +7,7 @@ import android.util.Log;
|
||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
import androidx.databinding.ObservableBoolean;
|
import androidx.databinding.ObservableBoolean;
|
||||||
import androidx.databinding.ObservableField;
|
import androidx.databinding.ObservableField;
|
||||||
|
import androidx.lifecycle.MutableLiveData;
|
||||||
|
|
||||||
import com.dspread.pos.common.manager.QPOSCallbackManager;
|
import com.dspread.pos.common.manager.QPOSCallbackManager;
|
||||||
import com.dspread.pos.printerAPI.PrinterHelper;
|
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> amount = new ObservableField<>("0.00");
|
||||||
public ObservableField<String> currencySymbol = new ObservableField<>("$");
|
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) {
|
public WaitingForCardViewModel(@NonNull Application application) {
|
||||||
super(application);
|
super(application);
|
||||||
}
|
}
|
||||||
|
@ -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() {
|
public void onCancel() {
|
||||||
// Handle cancel action
|
// Handle cancel action
|
||||||
Log.d("WaitingForCardViewModel", "onCancel: ");
|
Log.d("WaitingForCardViewModel", "onCancel: ");
|
||||||
|
|
|
@ -0,0 +1,222 @@
|
||||||
|
package com.dspread.pos.utils;
|
||||||
|
|
||||||
|
import android.Manifest;
|
||||||
|
import android.content.Context;
|
||||||
|
import android.content.pm.PackageManager;
|
||||||
|
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.Size;
|
||||||
|
import android.view.Surface;
|
||||||
|
import android.view.TextureView;
|
||||||
|
import android.widget.Toast;
|
||||||
|
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 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;
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
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();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onSurfaceTextureSizeChanged(SurfaceTexture surface, int width, int height) {}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean onSurfaceTextureDestroyed(SurfaceTexture surface) {
|
||||||
|
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();
|
||||||
|
// Simulate face detection after camera opens
|
||||||
|
simulateFaceDetection();
|
||||||
|
}
|
||||||
|
|
||||||
|
@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 simulateFaceDetection() {
|
||||||
|
// Simulate face detection after 3 seconds
|
||||||
|
new Handler().postDelayed(() -> {
|
||||||
|
if (callback != null) {
|
||||||
|
callback.onFaceDetected();
|
||||||
|
}
|
||||||
|
}, 18000);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void startBackgroundThread() {
|
||||||
|
backgroundThread = new HandlerThread("CameraBackground");
|
||||||
|
backgroundThread.start();
|
||||||
|
backgroundHandler = new Handler(backgroundThread.getLooper());
|
||||||
|
}
|
||||||
|
|
||||||
|
public void stopBackgroundThread() {
|
||||||
|
if (backgroundThread != null) {
|
||||||
|
backgroundThread.quitSafely();
|
||||||
|
try {
|
||||||
|
backgroundThread.join();
|
||||||
|
backgroundThread = null;
|
||||||
|
backgroundHandler = null;
|
||||||
|
} catch (InterruptedException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void closeCamera() {
|
||||||
|
if (cameraCaptureSession != null) {
|
||||||
|
cameraCaptureSession.close();
|
||||||
|
cameraCaptureSession = null;
|
||||||
|
}
|
||||||
|
if (cameraDevice != null) {
|
||||||
|
cameraDevice.close();
|
||||||
|
cameraDevice = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -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,200 +5,226 @@
|
||||||
xmlns:tools="http://schemas.android.com/tools">
|
xmlns:tools="http://schemas.android.com/tools">
|
||||||
|
|
||||||
<data>
|
<data>
|
||||||
<variable
|
<variable name="vm" type="com.dspread.pos.ui.payment.WaitingForCardViewModel" />
|
||||||
name="vm"
|
|
||||||
type="com.dspread.pos.ui.payment.WaitingForCardViewModel" />
|
|
||||||
</data>
|
</data>
|
||||||
|
|
||||||
<LinearLayout
|
<!-- Use FrameLayout as root to contain both camera overlay and main content -->
|
||||||
|
<FrameLayout
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent">
|
||||||
android:gravity="center"
|
|
||||||
android:orientation="vertical"
|
|
||||||
android:padding="16dp">
|
|
||||||
|
|
||||||
<!-- Card animation GIF -->
|
<!-- Main Content -->
|
||||||
<LinearLayout
|
<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_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="match_parent"
|
||||||
android:layout_marginTop="20dp"
|
android:gravity="center"
|
||||||
android:orientation="horizontal"
|
android:orientation="vertical"
|
||||||
android:weightSum="2"
|
android:padding="16dp">
|
||||||
android:paddingHorizontal="1dp">
|
|
||||||
|
|
||||||
<!-- Left side - Face ID Card -->
|
<!-- Card animation GIF -->
|
||||||
<androidx.cardview.widget.CardView
|
<LinearLayout
|
||||||
android:layout_width="0dp"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="260dp"
|
android:layout_height="wrap_content"
|
||||||
android:layout_marginEnd="2dp"
|
|
||||||
android:layout_weight="1"
|
android:layout_weight="1"
|
||||||
app:cardCornerRadius="18dp"
|
android:gravity="center">
|
||||||
app:cardElevation="2dp"
|
<ImageView
|
||||||
app:cardUseCompatPadding="true">
|
|
||||||
|
|
||||||
<!-- Outer Card (White Background) -->
|
|
||||||
<LinearLayout
|
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="@dimen/dp_300"
|
||||||
android:orientation="vertical"
|
android:paddingTop="10dp"
|
||||||
android:background="#8c1084"
|
android:scaleType="fitCenter"
|
||||||
android:padding="5dp">
|
android:src="@drawable/melberry_char_purple" />
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
<!-- Inner Card (Purple Background) -->
|
<!-- Amount and currency section -->
|
||||||
<androidx.cardview.widget.CardView
|
<LinearLayout
|
||||||
android:layout_width="match_parent"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="170dp"
|
android:layout_height="wrap_content"
|
||||||
app:cardCornerRadius="12dp"
|
android:layout_marginTop="16dp"
|
||||||
android:layout_gravity="center"
|
android:orientation="horizontal"
|
||||||
android:background="@android:color/holo_purple">
|
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 -->
|
<!-- Divided section for QR code and Face ID -->
|
||||||
<LinearLayout
|
<LinearLayout
|
||||||
android:layout_width="match_parent"
|
android:id="@+id/twoCardsContainer"
|
||||||
android:layout_height="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:gravity="center"
|
android:layout_height="wrap_content"
|
||||||
android:orientation="vertical">
|
android:layout_marginTop="20dp"
|
||||||
|
android:orientation="horizontal"
|
||||||
|
android:weightSum="2"
|
||||||
|
android:paddingHorizontal="1dp">
|
||||||
|
|
||||||
<pl.droidsonroids.gif.GifImageView
|
<!-- Left side - Face ID Card -->
|
||||||
android:layout_width="98dp"
|
<androidx.cardview.widget.CardView
|
||||||
android:layout_height="124dp"
|
android:id="@+id/faceIDContainer"
|
||||||
android:src="@drawable/faceid_180px" />
|
android:layout_width="0dp"
|
||||||
</LinearLayout>
|
android:layout_height="260dp"
|
||||||
</androidx.cardview.widget.CardView>
|
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
|
<LinearLayout
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="match_parent"
|
||||||
android:layout_marginTop="10dp"
|
android:orientation="vertical"
|
||||||
android:orientation="horizontal"
|
android:background="#8c1084"
|
||||||
android:gravity="center_vertical"
|
android:padding="5dp">
|
||||||
android:layout_gravity="center">
|
|
||||||
|
|
||||||
<!-- Left Text: "Оплатите лицом" -->
|
<!-- Inner Card (Purple Background) -->
|
||||||
<TextView
|
<androidx.cardview.widget.CardView
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="170dp"
|
||||||
android:text="Оплатить\nлицом"
|
app:cardCornerRadius="12dp"
|
||||||
android:textSize="18sp"
|
android:layout_gravity="center"
|
||||||
android:paddingLeft="10dp"
|
android:background="@android:color/holo_purple">
|
||||||
android:textStyle="normal"
|
|
||||||
android:textColor="@color/white"
|
|
||||||
android:layout_weight="1" />
|
|
||||||
|
|
||||||
<!-- Right Text: ">" -->
|
<!-- Center the GIF Image -->
|
||||||
<TextView
|
<LinearLayout
|
||||||
android:layout_width="wrap_content"
|
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_180px" />
|
||||||
|
</LinearLayout>
|
||||||
|
</androidx.cardview.widget.CardView>
|
||||||
|
|
||||||
|
<!-- Text in Outer Card -->
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:textSize="20sp"
|
android:layout_marginTop="10dp"
|
||||||
android:textStyle="bold"
|
android:orientation="horizontal"
|
||||||
android:paddingRight="10dp"
|
android:gravity="center_vertical"
|
||||||
android:text=">"
|
android:layout_gravity="center">
|
||||||
android:textColor="@color/white" />
|
|
||||||
|
<!-- 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>
|
||||||
</LinearLayout>
|
</androidx.cardview.widget.CardView>
|
||||||
</androidx.cardview.widget.CardView>
|
|
||||||
|
|
||||||
<!-- Right side - QR Code Card -->
|
<!-- Right side - QR Code Card -->
|
||||||
<androidx.cardview.widget.CardView
|
<androidx.cardview.widget.CardView
|
||||||
android:layout_width="0dp"
|
android:id="@+id/qrCodeContainer"
|
||||||
android:layout_height="260dp"
|
android:layout_width="0dp"
|
||||||
android:layout_marginStart="2dp"
|
android:layout_height="260dp"
|
||||||
android:layout_weight="1"
|
android:layout_marginStart="2dp"
|
||||||
app:cardCornerRadius="18dp"
|
android:layout_weight="1"
|
||||||
app:cardElevation="2dp"
|
app:cardCornerRadius="18dp"
|
||||||
app:cardUseCompatPadding="true">
|
app:cardElevation="2dp"
|
||||||
|
app:cardUseCompatPadding="true">
|
||||||
|
|
||||||
<!-- Single Card with Purple Background -->
|
<!-- Single Card with Purple Background -->
|
||||||
<LinearLayout
|
<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"
|
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="170dp"
|
android:layout_height="match_parent"
|
||||||
android:scaleType="fitCenter"
|
android:orientation="vertical"
|
||||||
android:src="@drawable/am_qr_code_mag_black"
|
android:background="#8c1084"
|
||||||
android:contentDescription="@string/qr_code_description"
|
android:padding="3dp">
|
||||||
app:imageBitmap="@{vm.qrCodeBitmap}" />
|
|
||||||
|
|
||||||
<!-- Text Under QR Code -->
|
<!-- QR Code Image -->
|
||||||
<TextView
|
<ImageView
|
||||||
android:layout_width="wrap_content"
|
android:id="@+id/qr_code_image"
|
||||||
android:layout_height="wrap_content"
|
android:layout_width="match_parent"
|
||||||
android:layout_marginTop="10dp"
|
android:layout_height="170dp"
|
||||||
android:text="Отсканируйте\nQR-код"
|
android:scaleType="fitCenter"
|
||||||
android:textColor="@color/white"
|
android:src="@drawable/am_qr_code_mag_black"
|
||||||
android:textSize="18sp"
|
android:contentDescription="@string/qr_code_description"
|
||||||
android:textStyle="normal"
|
app:imageBitmap="@{vm.qrCodeBitmap}" />
|
||||||
android:paddingBottom="5dp"
|
|
||||||
android:gravity="left"
|
<!-- Text Under QR Code -->
|
||||||
android:layout_gravity="center" />
|
<TextView
|
||||||
</LinearLayout>
|
android:layout_width="wrap_content"
|
||||||
</androidx.cardview.widget.CardView>
|
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>
|
||||||
</LinearLayout>
|
|
||||||
|
<!-- Camera Container (Full Screen Overlay) -->
|
||||||
|
<FrameLayout
|
||||||
|
android:id="@+id/camera_container"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:visibility="gone"
|
||||||
|
android:background="@android:color/black">
|
||||||
|
|
||||||
|
<!-- Camera Preview will be added here programmatically -->
|
||||||
|
|
||||||
|
<!-- Close button for camera -->
|
||||||
|
<ImageButton
|
||||||
|
android:id="@+id/btn_close_camera"
|
||||||
|
android:layout_width="48dp"
|
||||||
|
android:layout_height="48dp"
|
||||||
|
android:layout_margin="24dp"
|
||||||
|
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="visible" />
|
||||||
|
|
||||||
|
</FrameLayout>
|
||||||
|
</FrameLayout>
|
||||||
</layout>
|
</layout>
|
Loading…
Reference in New Issue