Preyea Regmi
4 years ago
14 changed files with 2264 additions and 10 deletions
-
95app/src/main/java/com/swifttech/remit/android/common/view/qrscanner/BarcodeGraphic.java
-
92app/src/main/java/com/swifttech/remit/android/common/view/qrscanner/BarcodeGraphicTracker.java
-
46app/src/main/java/com/swifttech/remit/android/common/view/qrscanner/BarcodeTrackerFactory.java
-
409app/src/main/java/com/swifttech/remit/android/common/view/qrscanner/QRScannerFragment.java
-
1216app/src/main/java/com/swifttech/remit/android/common/view/qrscanner/camera/MCameraSource.java
-
190app/src/main/java/com/swifttech/remit/android/common/view/qrscanner/camera/MCameraSourcePreview.java
-
212app/src/main/java/com/swifttech/remit/android/common/view/qrscanner/camera/MGraphicOverlay.java
-
4app/src/main/java/com/swifttech/remit/android/features/balancesend/BalanceSendViewModelFactory.java
-
2app/src/main/java/com/swifttech/remit/android/features/home/view/HomeFragmentV2.java
-
1app/src/main/java/com/swifttech/remit/android/features/kycV3/KYCV3ViewModelFactory.java
-
2app/src/main/java/com/swifttech/remit/android/features/kycV3/gateway/KYCV3Gateway.java
-
1app/src/main/java/com/swifttech/remit/android/features/kycV3/presenter/KYCV3GatewayInterface.java
-
2app/src/main/java/com/swifttech/remit/android/features/kycV3/view/KYCV3Activity.java
-
2app/src/main/java/com/swifttech/remit/android/features/register/view/RegisterV2Activity.java
@ -0,0 +1,95 @@ |
|||||
|
/* |
||||
|
* Copyright (C) The Android Open Source Project |
||||
|
* |
||||
|
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||
|
* you may not use this file except in compliance with the License. |
||||
|
* You may obtain a copy of the License at |
||||
|
* |
||||
|
* http://www.apache.org/licenses/LICENSE-2.0 |
||||
|
* |
||||
|
* Unless required by applicable law or agreed to in writing, software |
||||
|
* distributed under the License is distributed on an "AS IS" BASIS, |
||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
|
* See the License for the specific language governing permissions and |
||||
|
* limitations under the License. |
||||
|
*/ |
||||
|
package com.swifttech.remit.android.common.view.qrscanner; |
||||
|
|
||||
|
import android.graphics.Canvas; |
||||
|
import android.graphics.Color; |
||||
|
import android.graphics.Paint; |
||||
|
import android.graphics.RectF; |
||||
|
|
||||
|
import com.google.android.gms.vision.barcode.Barcode; |
||||
|
import com.swifttech.remit.android.common.view.qrscanner.camera.MGraphicOverlay; |
||||
|
|
||||
|
/** |
||||
|
* Graphic instance for rendering barcode position, size, and ID within an associated graphic |
||||
|
* overlay view. |
||||
|
*/ |
||||
|
public class BarcodeGraphic extends MGraphicOverlay.Graphic { |
||||
|
|
||||
|
private int mId; |
||||
|
|
||||
|
|
||||
|
|
||||
|
private static int mCurrentColorIndex = 0; |
||||
|
|
||||
|
private Paint mRectPaint; |
||||
|
private volatile Barcode mBarcode; |
||||
|
|
||||
|
BarcodeGraphic(MGraphicOverlay overlay) { |
||||
|
super(overlay); |
||||
|
|
||||
|
|
||||
|
mRectPaint = new Paint(); |
||||
|
mRectPaint.setColor(Color.RED); |
||||
|
mRectPaint.setStyle(Paint.Style.STROKE); |
||||
|
mRectPaint.setStrokeWidth(8.0f); |
||||
|
|
||||
|
|
||||
|
} |
||||
|
|
||||
|
public int getId() { |
||||
|
return mId; |
||||
|
} |
||||
|
|
||||
|
public void setId(int id) { |
||||
|
this.mId = id; |
||||
|
} |
||||
|
|
||||
|
public Barcode getBarcode() { |
||||
|
return mBarcode; |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* Updates the barcode instance from the detection of the most recent frame. Invalidates the |
||||
|
* relevant portions of the overlay to trigger a redraw. |
||||
|
*/ |
||||
|
void updateItem(Barcode barcode) { |
||||
|
mBarcode = barcode; |
||||
|
postInvalidate(); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* Draws the barcode annotations for position, size, and raw value on the supplied canvas. |
||||
|
*/ |
||||
|
@Override |
||||
|
public void draw(Canvas canvas) { |
||||
|
Barcode barcode = mBarcode; |
||||
|
if (barcode == null) { |
||||
|
return; |
||||
|
} |
||||
|
|
||||
|
// Draws the bounding box around the barcode. |
||||
|
RectF rect = new RectF(barcode.getBoundingBox()); |
||||
|
rect.left = translateX(rect.left); |
||||
|
rect.top = translateY(rect.top); |
||||
|
rect.right = translateX(rect.right); |
||||
|
rect.bottom = translateY(rect.bottom); |
||||
|
canvas.drawRect(rect, mRectPaint); |
||||
|
|
||||
|
// Draws a label at the bottom of the barcode indicate the barcode value that was detected. |
||||
|
|
||||
|
} |
||||
|
} |
@ -0,0 +1,92 @@ |
|||||
|
/* |
||||
|
* Copyright (C) The Android Open Source Project |
||||
|
* |
||||
|
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||
|
* you may not use this file except in compliance with the License. |
||||
|
* You may obtain a copy of the License at |
||||
|
* |
||||
|
* http://www.apache.org/licenses/LICENSE-2.0 |
||||
|
* |
||||
|
* Unless required by applicable law or agreed to in writing, software |
||||
|
* distributed under the License is distributed on an "AS IS" BASIS, |
||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
|
* See the License for the specific language governing permissions and |
||||
|
* limitations under the License. |
||||
|
*/ |
||||
|
package com.swifttech.remit.android.common.view.qrscanner; |
||||
|
|
||||
|
import android.content.Context; |
||||
|
|
||||
|
import androidx.annotation.UiThread; |
||||
|
|
||||
|
import com.google.android.gms.vision.Detector; |
||||
|
import com.google.android.gms.vision.Tracker; |
||||
|
import com.google.android.gms.vision.barcode.Barcode; |
||||
|
import com.swifttech.remit.android.common.view.qrscanner.camera.MGraphicOverlay; |
||||
|
|
||||
|
/** |
||||
|
* Generic tracker which is used for tracking or reading a barcode (and can really be used for |
||||
|
* any type of item). This is used to receive newly detected items, add a graphical representation |
||||
|
* to an overlay, update the graphics as the item changes, and remove the graphics when the item |
||||
|
* goes away. |
||||
|
*/ |
||||
|
public class BarcodeGraphicTracker extends Tracker<Barcode> { |
||||
|
private MGraphicOverlay<BarcodeGraphic> mOverlay; |
||||
|
private BarcodeGraphic mGraphic; |
||||
|
|
||||
|
private BarcodeUpdateListener mBarcodeUpdateListener; |
||||
|
|
||||
|
/** |
||||
|
* Consume the item instance detected from an Activity or Fragment level by implementing the |
||||
|
* BarcodeUpdateListener interface method onBarcodeDetected. |
||||
|
*/ |
||||
|
public interface BarcodeUpdateListener { |
||||
|
@UiThread |
||||
|
void onBarcodeDetected(Barcode barcode); |
||||
|
} |
||||
|
|
||||
|
BarcodeGraphicTracker(MGraphicOverlay<BarcodeGraphic> mOverlay, BarcodeGraphic mGraphic, |
||||
|
BarcodeUpdateListener listener) { |
||||
|
this.mOverlay = mOverlay; |
||||
|
this.mGraphic = mGraphic; |
||||
|
this.mBarcodeUpdateListener=listener; |
||||
|
|
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* Start tracking the detected item instance within the item overlay. |
||||
|
*/ |
||||
|
@Override |
||||
|
public void onNewItem(int id, Barcode item) { |
||||
|
mGraphic.setId(id); |
||||
|
mBarcodeUpdateListener.onBarcodeDetected(item); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* Update the position/characteristics of the item within the overlay. |
||||
|
*/ |
||||
|
@Override |
||||
|
public void onUpdate(Detector.Detections<Barcode> detectionResults, Barcode item) { |
||||
|
mOverlay.add(mGraphic); |
||||
|
mGraphic.updateItem(item); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* Hide the graphic when the corresponding object was not detected. This can happen for |
||||
|
* intermediate frames temporarily, for example if the object was momentarily blocked from |
||||
|
* view. |
||||
|
*/ |
||||
|
@Override |
||||
|
public void onMissing(Detector.Detections<Barcode> detectionResults) { |
||||
|
mOverlay.remove(mGraphic); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* Called when the item is assumed to be gone for good. Remove the graphic annotation from |
||||
|
* the overlay. |
||||
|
*/ |
||||
|
@Override |
||||
|
public void onDone() { |
||||
|
mOverlay.remove(mGraphic); |
||||
|
} |
||||
|
} |
@ -0,0 +1,46 @@ |
|||||
|
/* |
||||
|
* Copyright (C) The Android Open Source Project |
||||
|
* |
||||
|
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||
|
* you may not use this file except in compliance with the License. |
||||
|
* You may obtain a copy of the License at |
||||
|
* |
||||
|
* http://www.apache.org/licenses/LICENSE-2.0 |
||||
|
* |
||||
|
* Unless required by applicable law or agreed to in writing, software |
||||
|
* distributed under the License is distributed on an "AS IS" BASIS, |
||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
|
* See the License for the specific language governing permissions and |
||||
|
* limitations under the License. |
||||
|
*/ |
||||
|
package com.swifttech.remit.android.common.view.qrscanner; |
||||
|
|
||||
|
import android.content.Context; |
||||
|
|
||||
|
import com.google.android.gms.vision.MultiProcessor; |
||||
|
import com.google.android.gms.vision.Tracker; |
||||
|
import com.google.android.gms.vision.barcode.Barcode; |
||||
|
import com.swifttech.remit.android.common.view.qrscanner.camera.MGraphicOverlay; |
||||
|
|
||||
|
/** |
||||
|
* Factory for creating a tracker and associated graphic to be associated with a new barcode. The |
||||
|
* multi-processor uses this factory to create barcode trackers as needed -- one for each barcode. |
||||
|
*/ |
||||
|
class BarcodeTrackerFactory implements MultiProcessor.Factory<Barcode> { |
||||
|
private MGraphicOverlay<BarcodeGraphic> mGraphicOverlay; |
||||
|
private BarcodeGraphicTracker.BarcodeUpdateListener listener; |
||||
|
|
||||
|
public BarcodeTrackerFactory(MGraphicOverlay<BarcodeGraphic> mGraphicOverlay, |
||||
|
BarcodeGraphicTracker.BarcodeUpdateListener listener) { |
||||
|
this.mGraphicOverlay = mGraphicOverlay; |
||||
|
this.listener = listener; |
||||
|
} |
||||
|
|
||||
|
@Override |
||||
|
public Tracker<Barcode> create(Barcode barcode) { |
||||
|
BarcodeGraphic graphic = new BarcodeGraphic(mGraphicOverlay); |
||||
|
return new BarcodeGraphicTracker(mGraphicOverlay, graphic,listener); |
||||
|
} |
||||
|
|
||||
|
} |
||||
|
|
@ -0,0 +1,409 @@ |
|||||
|
package com.swifttech.remit.android.common.view.qrscanner; |
||||
|
|
||||
|
import android.Manifest; |
||||
|
import android.annotation.SuppressLint; |
||||
|
import android.app.Dialog; |
||||
|
import android.content.DialogInterface; |
||||
|
import android.content.pm.PackageManager; |
||||
|
import android.hardware.Camera; |
||||
|
import android.os.Bundle; |
||||
|
import android.os.Handler; |
||||
|
import android.os.Looper; |
||||
|
import android.util.Log; |
||||
|
import android.view.GestureDetector; |
||||
|
import android.view.LayoutInflater; |
||||
|
import android.view.MotionEvent; |
||||
|
import android.view.ScaleGestureDetector; |
||||
|
import android.view.View; |
||||
|
import android.view.ViewGroup; |
||||
|
import android.widget.Toast; |
||||
|
|
||||
|
import androidx.annotation.NonNull; |
||||
|
import androidx.annotation.Nullable; |
||||
|
import androidx.core.app.ActivityCompat; |
||||
|
|
||||
|
import com.google.android.gms.common.ConnectionResult; |
||||
|
import com.google.android.gms.common.GoogleApiAvailability; |
||||
|
import com.google.android.gms.vision.CameraSource; |
||||
|
import com.google.android.gms.vision.MultiProcessor; |
||||
|
import com.google.android.gms.vision.barcode.Barcode; |
||||
|
import com.google.android.gms.vision.barcode.BarcodeDetector; |
||||
|
import com.google.android.material.dialog.MaterialAlertDialogBuilder; |
||||
|
import com.google.android.material.snackbar.Snackbar; |
||||
|
import com.swifttech.remit.android.R; |
||||
|
import com.swifttech.remit.android.base.BaseFragment; |
||||
|
import com.swifttech.remit.android.common.view.qrscanner.camera.MCameraSource; |
||||
|
import com.swifttech.remit.android.common.view.qrscanner.camera.MCameraSourcePreview; |
||||
|
import com.swifttech.remit.android.common.view.qrscanner.camera.MGraphicOverlay; |
||||
|
|
||||
|
import java.io.IOException; |
||||
|
|
||||
|
|
||||
|
public abstract class QRScannerFragment extends BaseFragment { |
||||
|
|
||||
|
MCameraSourcePreview mPreview; |
||||
|
|
||||
|
MGraphicOverlay<BarcodeGraphic> mGraphicOverlay; |
||||
|
|
||||
|
private static final String TAG = "Barcode-reader"; |
||||
|
|
||||
|
// intent request code to handle updating play services if needed. |
||||
|
private static final int RC_HANDLE_GMS = 9001; |
||||
|
|
||||
|
// permission request codes need to be < 256 |
||||
|
private static final int RC_HANDLE_CAMERA_PERM = 2; |
||||
|
|
||||
|
|
||||
|
|
||||
|
private MCameraSource mCameraSource; |
||||
|
|
||||
|
BarcodeGraphicTracker.BarcodeUpdateListener listener; |
||||
|
|
||||
|
// helper objects for detecting taps and pinches. |
||||
|
private ScaleGestureDetector scaleGestureDetector; |
||||
|
private GestureDetector gestureDetector; |
||||
|
|
||||
|
|
||||
|
|
||||
|
|
||||
|
protected abstract @NonNull |
||||
|
BarcodeGraphicTracker.BarcodeUpdateListener provideListener(); |
||||
|
|
||||
|
private class CaptureGestureListener extends GestureDetector.SimpleOnGestureListener { |
||||
|
@Override |
||||
|
public boolean onSingleTapConfirmed(MotionEvent e) { |
||||
|
return onTap(e.getRawX(), e.getRawY()) || super.onSingleTapConfirmed(e); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
private class ScaleListener implements ScaleGestureDetector.OnScaleGestureListener { |
||||
|
|
||||
|
/** |
||||
|
* Responds to scaling events for a gesture in progress. |
||||
|
* Reported by pointer motion. |
||||
|
* |
||||
|
* @param detector The detector reporting the event - use this to |
||||
|
* retrieve extended info about event state. |
||||
|
* @return Whether or not the detector should consider this event |
||||
|
* as handled. If an event was not handled, the detector |
||||
|
* will continue to accumulate movement until an event is |
||||
|
* handled. This can be useful if an application, for example, |
||||
|
* only wants to update scaling factors if the change is |
||||
|
* greater than 0.01. |
||||
|
*/ |
||||
|
@Override |
||||
|
public boolean onScale(ScaleGestureDetector detector) { |
||||
|
return false; |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* Responds to the beginning of a scaling gesture. Reported by |
||||
|
* new pointers going down. |
||||
|
* |
||||
|
* @param detector The detector reporting the event - use this to |
||||
|
* retrieve extended info about event state. |
||||
|
* @return Whether or not the detector should continue recognizing |
||||
|
* this gesture. For example, if a gesture is beginning |
||||
|
* with a focal point outside of a region where it makes |
||||
|
* sense, onScaleBegin() may return false to ignore the |
||||
|
* rest of the gesture. |
||||
|
*/ |
||||
|
@Override |
||||
|
public boolean onScaleBegin(ScaleGestureDetector detector) { |
||||
|
return true; |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* Responds to the end of a scale gesture. Reported by existing |
||||
|
* pointers going up. |
||||
|
* <p/> |
||||
|
* Once a scale has ended, {@link ScaleGestureDetector#getFocusX()} |
||||
|
* and {@link ScaleGestureDetector#getFocusY()} will return focal point |
||||
|
* of the pointers remaining on the screen. |
||||
|
* |
||||
|
* @param detector The detector reporting the event - use this to |
||||
|
* retrieve extended info about event state. |
||||
|
*/ |
||||
|
@Override |
||||
|
public void onScaleEnd(ScaleGestureDetector detector) { |
||||
|
mCameraSource.doZoom(detector.getScaleFactor()); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
|
||||
|
@Override |
||||
|
public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { |
||||
|
View view= inflater.inflate(R.layout.fragment_qr_scanner_view, container, false); |
||||
|
mPreview = view.findViewById(R.id.preview); |
||||
|
mGraphicOverlay = view.findViewById(R.id.graphicOverlay); |
||||
|
gestureDetector = new GestureDetector(getActivity(), new CaptureGestureListener()); |
||||
|
scaleGestureDetector = new ScaleGestureDetector(getActivity(), new ScaleListener()); |
||||
|
view.setOnTouchListener(new View.OnTouchListener() { |
||||
|
@Override |
||||
|
public boolean onTouch(View v, MotionEvent e) { |
||||
|
boolean b = scaleGestureDetector.onTouchEvent(e); |
||||
|
|
||||
|
boolean c = gestureDetector.onTouchEvent(e); |
||||
|
|
||||
|
return b || c ; |
||||
|
} |
||||
|
}); |
||||
|
|
||||
|
return view; |
||||
|
} |
||||
|
|
||||
|
@Override |
||||
|
public void onViewCreated(View view, @Nullable Bundle savedInstanceState) { |
||||
|
super.onViewCreated(view, savedInstanceState); |
||||
|
init(); |
||||
|
performDefaultAction(savedInstanceState); |
||||
|
} |
||||
|
|
||||
|
protected void init() { |
||||
|
listener=provideListener(); |
||||
|
// read parameters from the intent used to launch the activity. |
||||
|
|
||||
|
// Check for the camera permission before accessing the camera. If the |
||||
|
// permission is not granted yet, request permission. |
||||
|
int rc = ActivityCompat.checkSelfPermission(getActivity(), Manifest.permission.CAMERA); |
||||
|
if (rc == PackageManager.PERMISSION_GRANTED) { |
||||
|
createCameraSource(true, false); |
||||
|
} else { |
||||
|
requestCameraPermission(); |
||||
|
} |
||||
|
|
||||
|
|
||||
|
|
||||
|
Snackbar.make(mGraphicOverlay, "Scan to read QR. Pinch/Stretch to zoom", |
||||
|
Snackbar.LENGTH_LONG) |
||||
|
.show(); |
||||
|
|
||||
|
|
||||
|
} |
||||
|
|
||||
|
|
||||
|
protected void performDefaultAction(Bundle savedInstanceState) { |
||||
|
|
||||
|
|
||||
|
|
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* onTap returns the tapped barcode result to the calling Activity. |
||||
|
* |
||||
|
* @param rawX - the raw position of the tap |
||||
|
* @param rawY - the raw position of the tap. |
||||
|
* @return true if the activity is ending. |
||||
|
*/ |
||||
|
private boolean onTap(float rawX, float rawY) { |
||||
|
// Find tap point in preview frame coordinates. |
||||
|
int[] location = new int[2]; |
||||
|
mGraphicOverlay.getLocationOnScreen(location); |
||||
|
float x = (rawX - location[0]) / mGraphicOverlay.getWidthScaleFactor(); |
||||
|
float y = (rawY - location[1]) / mGraphicOverlay.getHeightScaleFactor(); |
||||
|
|
||||
|
// Find the barcode whose center is closest to the tapped point. |
||||
|
Barcode best = null; |
||||
|
float bestDistance = Float.MAX_VALUE; |
||||
|
for (BarcodeGraphic graphic : mGraphicOverlay.getGraphics()) { |
||||
|
Barcode barcode = graphic.getBarcode(); |
||||
|
if (barcode.getBoundingBox().contains((int) x, (int) y)) { |
||||
|
// Exact hit, no need to keep looking. |
||||
|
best = barcode; |
||||
|
break; |
||||
|
} |
||||
|
float dx = x - barcode.getBoundingBox().centerX(); |
||||
|
float dy = y - barcode.getBoundingBox().centerY(); |
||||
|
float distance = (dx * dx) + (dy * dy); // actually squared distance |
||||
|
if (distance < bestDistance) { |
||||
|
best = barcode; |
||||
|
bestDistance = distance; |
||||
|
} |
||||
|
} |
||||
|
if(best!=null) |
||||
|
listener.onBarcodeDetected(best); |
||||
|
return false; |
||||
|
} |
||||
|
|
||||
|
|
||||
|
|
||||
|
private void requestCameraPermission() { |
||||
|
|
||||
|
final String[] permissions = new String[]{Manifest.permission.CAMERA}; |
||||
|
|
||||
|
if (!ActivityCompat.shouldShowRequestPermissionRationale(getActivity(), |
||||
|
Manifest.permission.CAMERA)) { |
||||
|
ActivityCompat.requestPermissions(getActivity(), permissions, RC_HANDLE_CAMERA_PERM); |
||||
|
return; |
||||
|
} |
||||
|
|
||||
|
|
||||
|
View.OnClickListener listener = new View.OnClickListener() { |
||||
|
@Override |
||||
|
public void onClick(View view) { |
||||
|
ActivityCompat.requestPermissions(getActivity(), permissions, |
||||
|
RC_HANDLE_CAMERA_PERM); |
||||
|
} |
||||
|
}; |
||||
|
|
||||
|
Toast.makeText(getActivity(),"Permission not resolved",Toast.LENGTH_SHORT).show(); |
||||
|
} |
||||
|
|
||||
|
@Override |
||||
|
public void onRequestPermissionsResult(int requestCode, |
||||
|
@NonNull String[] permissions, |
||||
|
@NonNull int[] grantResults) { |
||||
|
if (requestCode != RC_HANDLE_CAMERA_PERM) { |
||||
|
super.onRequestPermissionsResult(requestCode, permissions, grantResults); |
||||
|
return; |
||||
|
} |
||||
|
|
||||
|
if (grantResults.length != 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) { |
||||
|
Log.d(TAG, "Camera permission granted - initialize the camera source"); |
||||
|
// we have permission, so create the camerasource |
||||
|
|
||||
|
createCameraSource(true, false); |
||||
|
return; |
||||
|
} |
||||
|
|
||||
|
Log.e(TAG, "Permission not granted: results len = " + grantResults.length + |
||||
|
" Result code = " + (grantResults.length > 0 ? grantResults[0] : "(empty)")); |
||||
|
|
||||
|
DialogInterface.OnClickListener listener = new DialogInterface.OnClickListener() { |
||||
|
public void onClick(DialogInterface dialog, int id) { |
||||
|
|
||||
|
} |
||||
|
}; |
||||
|
|
||||
|
MaterialAlertDialogBuilder builder = new MaterialAlertDialogBuilder(getActivity()); |
||||
|
builder.setTitle("Multitracker sample") |
||||
|
.setMessage("No camera permission") |
||||
|
.setPositiveButton("Ok", listener) |
||||
|
.show(); |
||||
|
} |
||||
|
|
||||
|
|
||||
|
|
||||
|
/** |
||||
|
* Creates and starts the camera. Note that this uses a higher resolution in comparison |
||||
|
* to other detection examples to enable the barcode detector to detect small barcodes |
||||
|
* at long distances. |
||||
|
* |
||||
|
* Suppressing InlinedApi since there is a check that the minimum version is met before using |
||||
|
* the constant. |
||||
|
*/ |
||||
|
@SuppressLint("InlinedApi") |
||||
|
private void createCameraSource(boolean autoFocus, boolean useFlash) { |
||||
|
|
||||
|
|
||||
|
// A barcode detector is created to track barcodes. An associated multi-processor instance |
||||
|
// is set to receive the barcode detection results, track the barcodes, and maintain |
||||
|
// graphics for each barcode on screen. The factory is used by the multi-processor to |
||||
|
// create a separate tracker instance for each barcode. |
||||
|
BarcodeDetector barcodeDetector = new BarcodeDetector.Builder(getActivity()).setBarcodeFormats(Barcode.QR_CODE).build(); |
||||
|
BarcodeTrackerFactory barcodeFactory = new BarcodeTrackerFactory(mGraphicOverlay, listener); |
||||
|
barcodeDetector.setProcessor( |
||||
|
new MultiProcessor.Builder<>(barcodeFactory).build()); |
||||
|
|
||||
|
if (!barcodeDetector.isOperational()) { |
||||
|
// Note: The first time that an app using the barcode or face API is installed on a |
||||
|
// device, GMS will download a native libraries to the device in order to do detection. |
||||
|
// Usually this completes before the app is run for the first time. But if that |
||||
|
// download has not yet completed, then the above call will not detect any barcodes |
||||
|
// and/or faces. |
||||
|
// |
||||
|
// isOperational() can be used to check if the required native libraries are currently |
||||
|
// available. The detectors will automatically become operational once the library |
||||
|
// downloads complete on device. |
||||
|
|
||||
|
} |
||||
|
|
||||
|
// Creates and starts the camera. Note that this uses a higher resolution in comparison |
||||
|
// to other detection examples to enable the barcode detector to detect small barcodes |
||||
|
// at long distances. |
||||
|
MCameraSource.Builder builder = new MCameraSource.Builder(getActivity(), barcodeDetector) |
||||
|
.setFacing(CameraSource.CAMERA_FACING_BACK) |
||||
|
.setRequestedPreviewSize(1600, 1024) |
||||
|
.setRequestedFps(15.0f); |
||||
|
|
||||
|
// make sure that auto focus is an available option |
||||
|
builder = builder.setFocusMode( |
||||
|
autoFocus ? Camera.Parameters.FOCUS_MODE_CONTINUOUS_PICTURE : null); |
||||
|
|
||||
|
mCameraSource = builder |
||||
|
.build(); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* Starts or restarts the camera source, if it exists. If the camera source doesn't exist yet |
||||
|
* (e.g., because onResume was called before the camera source was created), this will be called |
||||
|
* again when the camera source is created. |
||||
|
*/ |
||||
|
private void startCameraSource() throws SecurityException { |
||||
|
// check that the device has play services available. |
||||
|
int code = GoogleApiAvailability.getInstance().isGooglePlayServicesAvailable( |
||||
|
getActivity()); |
||||
|
if (code != ConnectionResult.SUCCESS) { |
||||
|
Dialog dlg = |
||||
|
GoogleApiAvailability.getInstance().getErrorDialog(getActivity(), code, RC_HANDLE_GMS); |
||||
|
dlg.show(); |
||||
|
} |
||||
|
|
||||
|
if (mCameraSource != null) { |
||||
|
try { |
||||
|
mPreview.start(mCameraSource, mGraphicOverlay); |
||||
|
} catch (IOException e) { |
||||
|
Log.e(TAG, "Unable to start camera source.", e); |
||||
|
mCameraSource.release(); |
||||
|
mCameraSource = null; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* Restarts the camera. |
||||
|
*/ |
||||
|
@Override |
||||
|
public void onResume() { |
||||
|
super.onResume(); |
||||
|
startQrScanning(); |
||||
|
Log.d("BALANCEQR"," Parent onResume"); |
||||
|
|
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* Stops the camera. |
||||
|
*/ |
||||
|
@Override |
||||
|
public void onPause() { |
||||
|
stopQrScanning(); |
||||
|
super.onPause(); |
||||
|
|
||||
|
Log.d("BALANCEQR"," Parent onPause"); |
||||
|
} |
||||
|
|
||||
|
public void stopQrScanning() { |
||||
|
if (mPreview != null) { |
||||
|
mPreview.stop(); |
||||
|
} |
||||
|
} |
||||
|
public void startQrScanning() { |
||||
|
startCameraSource(); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* Releases the resources associated with the camera source, the associated detectors, and the |
||||
|
* rest of the processing pipeline. |
||||
|
*/ |
||||
|
@Override |
||||
|
public void onDestroy() { |
||||
|
super.onDestroy(); |
||||
|
if (mPreview != null) { |
||||
|
mPreview.release(); |
||||
|
} |
||||
|
|
||||
|
} |
||||
|
|
||||
|
|
||||
|
|
||||
|
} |
1216
app/src/main/java/com/swifttech/remit/android/common/view/qrscanner/camera/MCameraSource.java
File diff suppressed because it is too large
View File
File diff suppressed because it is too large
View File
@ -0,0 +1,190 @@ |
|||||
|
/* |
||||
|
* Copyright (C) The Android Open Source Project |
||||
|
* |
||||
|
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||
|
* you may not use this file except in compliance with the License. |
||||
|
* You may obtain a copy of the License at |
||||
|
* |
||||
|
* http://www.apache.org/licenses/LICENSE-2.0 |
||||
|
* |
||||
|
* Unless required by applicable law or agreed to in writing, software |
||||
|
* distributed under the License is distributed on an "AS IS" BASIS, |
||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
|
* See the License for the specific language governing permissions and |
||||
|
* limitations under the License. |
||||
|
*/ |
||||
|
package com.swifttech.remit.android.common.view.qrscanner.camera; |
||||
|
|
||||
|
import android.Manifest; |
||||
|
import android.content.Context; |
||||
|
import android.content.res.Configuration; |
||||
|
import android.util.AttributeSet; |
||||
|
import android.util.Log; |
||||
|
import android.view.SurfaceHolder; |
||||
|
import android.view.SurfaceView; |
||||
|
import android.view.ViewGroup; |
||||
|
|
||||
|
import androidx.annotation.RequiresPermission; |
||||
|
|
||||
|
import com.google.android.gms.common.images.Size; |
||||
|
|
||||
|
import java.io.IOException; |
||||
|
|
||||
|
public class MCameraSourcePreview extends ViewGroup { |
||||
|
private static final String TAG = "CameraSourcePreview"; |
||||
|
|
||||
|
private Context mContext; |
||||
|
private SurfaceView mSurfaceView; |
||||
|
private boolean mStartRequested; |
||||
|
private boolean mSurfaceAvailable; |
||||
|
private MCameraSource mCameraSource; |
||||
|
|
||||
|
private MGraphicOverlay mOverlay; |
||||
|
|
||||
|
public MCameraSourcePreview(Context context, AttributeSet attrs) { |
||||
|
super(context, attrs); |
||||
|
mContext = context; |
||||
|
mStartRequested = false; |
||||
|
mSurfaceAvailable = false; |
||||
|
|
||||
|
mSurfaceView = new SurfaceView(context); |
||||
|
mSurfaceView.getHolder().addCallback(new SurfaceCallback()); |
||||
|
addView(mSurfaceView); |
||||
|
} |
||||
|
|
||||
|
@RequiresPermission(Manifest.permission.CAMERA) |
||||
|
public void start(MCameraSource cameraSource) throws IOException, SecurityException { |
||||
|
if (cameraSource == null) { |
||||
|
stop(); |
||||
|
} |
||||
|
|
||||
|
mCameraSource = cameraSource; |
||||
|
|
||||
|
if (mCameraSource != null) { |
||||
|
mStartRequested = true; |
||||
|
startIfReady(); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
@RequiresPermission(Manifest.permission.CAMERA) |
||||
|
public void start(MCameraSource cameraSource, MGraphicOverlay overlay) throws IOException, SecurityException { |
||||
|
mOverlay = overlay; |
||||
|
start(cameraSource); |
||||
|
} |
||||
|
|
||||
|
public void stop() { |
||||
|
if (mCameraSource != null) { |
||||
|
mCameraSource.stop(); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
public void release() { |
||||
|
if (mCameraSource != null) { |
||||
|
mCameraSource.release(); |
||||
|
mCameraSource = null; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
@RequiresPermission(Manifest.permission.CAMERA) |
||||
|
private void startIfReady() throws IOException, SecurityException { |
||||
|
if (mStartRequested && mSurfaceAvailable) { |
||||
|
mCameraSource.start(mSurfaceView.getHolder()); |
||||
|
if (mOverlay != null) { |
||||
|
Size size = mCameraSource.getPreviewSize(); |
||||
|
int min = Math.min(size.getWidth(), size.getHeight()); |
||||
|
int max = Math.max(size.getWidth(), size.getHeight()); |
||||
|
if (isPortraitMode()) { |
||||
|
// Swap width and height sizes when in portrait, since it will be rotated by |
||||
|
// 90 degrees |
||||
|
mOverlay.setCameraInfo(min, max, mCameraSource.getCameraFacing()); |
||||
|
} else { |
||||
|
mOverlay.setCameraInfo(max, min, mCameraSource.getCameraFacing()); |
||||
|
} |
||||
|
mOverlay.clear(); |
||||
|
} |
||||
|
mStartRequested = false; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
private class SurfaceCallback implements SurfaceHolder.Callback { |
||||
|
@Override |
||||
|
public void surfaceCreated(SurfaceHolder surface) { |
||||
|
mSurfaceAvailable = true; |
||||
|
try { |
||||
|
startIfReady(); |
||||
|
} catch (SecurityException se) { |
||||
|
Log.e(TAG,"Do not have permission to start the camera", se); |
||||
|
} catch (IOException e) { |
||||
|
Log.e(TAG, "Could not start camera source.", e); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
@Override |
||||
|
public void surfaceDestroyed(SurfaceHolder surface) { |
||||
|
mSurfaceAvailable = false; |
||||
|
} |
||||
|
|
||||
|
@Override |
||||
|
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) { |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
@Override |
||||
|
protected void onLayout(boolean changed, int left, int top, int right, int bottom) { |
||||
|
int width = 320; |
||||
|
int height = 240; |
||||
|
if (mCameraSource != null) { |
||||
|
Size size = mCameraSource.getPreviewSize(); |
||||
|
if (size != null) { |
||||
|
width = size.getWidth(); |
||||
|
height = size.getHeight(); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
// Swap width and height sizes when in portrait, since it will be rotated 90 degrees |
||||
|
if (isPortraitMode()) { |
||||
|
int tmp = width; |
||||
|
//noinspection SuspiciousNameCombination |
||||
|
width = height; |
||||
|
height = tmp; |
||||
|
} |
||||
|
|
||||
|
final int layoutWidth = right - left; |
||||
|
final int layoutHeight = bottom - top; |
||||
|
|
||||
|
// Computes height and width for potentially doing fit width. |
||||
|
int childWidth = layoutWidth; |
||||
|
int childHeight = (int)(((float) layoutWidth / (float) width) * height); |
||||
|
|
||||
|
// If height is too tall using fit width, does fit height instead. |
||||
|
if (childHeight > layoutHeight) { |
||||
|
childHeight = layoutHeight; |
||||
|
childWidth = (int)(((float) layoutHeight / (float) height) * width); |
||||
|
} |
||||
|
|
||||
|
for (int i = 0; i < getChildCount(); ++i) { |
||||
|
getChildAt(i).layout(0, 0, childWidth, childHeight); |
||||
|
} |
||||
|
|
||||
|
try { |
||||
|
startIfReady(); |
||||
|
} catch (SecurityException se) { |
||||
|
Log.e(TAG,"Do not have permission to start the camera", se); |
||||
|
} catch (IOException e) { |
||||
|
Log.e(TAG, "Could not start camera source.", e); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
private boolean isPortraitMode() { |
||||
|
int orientation = mContext.getResources().getConfiguration().orientation; |
||||
|
if (orientation == Configuration.ORIENTATION_LANDSCAPE) { |
||||
|
return false; |
||||
|
} |
||||
|
if (orientation == Configuration.ORIENTATION_PORTRAIT) { |
||||
|
return true; |
||||
|
} |
||||
|
|
||||
|
Log.d(TAG, "isPortraitMode returning false by default"); |
||||
|
return false; |
||||
|
} |
||||
|
} |
@ -0,0 +1,212 @@ |
|||||
|
/* |
||||
|
* Copyright (C) The Android Open Source Project |
||||
|
* |
||||
|
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||
|
* you may not use this file except in compliance with the License. |
||||
|
* You may obtain a copy of the License at |
||||
|
* |
||||
|
* http://www.apache.org/licenses/LICENSE-2.0 |
||||
|
* |
||||
|
* Unless required by applicable law or agreed to in writing, software |
||||
|
* distributed under the License is distributed on an "AS IS" BASIS, |
||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
|
* See the License for the specific language governing permissions and |
||||
|
* limitations under the License. |
||||
|
*/ |
||||
|
package com.swifttech.remit.android.common.view.qrscanner.camera; |
||||
|
|
||||
|
import android.content.Context; |
||||
|
import android.graphics.Canvas; |
||||
|
import android.util.AttributeSet; |
||||
|
import android.view.View; |
||||
|
|
||||
|
import com.google.android.gms.vision.CameraSource; |
||||
|
|
||||
|
import java.util.HashSet; |
||||
|
import java.util.List; |
||||
|
import java.util.Set; |
||||
|
import java.util.Vector; |
||||
|
|
||||
|
/** |
||||
|
* A view which renders a series of custom graphics to be overlayed on top of an associated preview |
||||
|
* (i.e., the camera preview). The creator can add graphics objects, update the objects, and remove |
||||
|
* them, triggering the appropriate drawing and invalidation within the view.<p> |
||||
|
* |
||||
|
* Supports scaling and mirroring of the graphics relative the camera's preview properties. The |
||||
|
* idea is that detection items are expressed in terms of a preview size, but need to be scaled up |
||||
|
* to the full view size, and also mirrored in the case of the front-facing camera.<p> |
||||
|
* |
||||
|
* Associated {@link Graphic} items should use the following methods to convert to view coordinates |
||||
|
* for the graphics that are drawn: |
||||
|
* <ol> |
||||
|
* <li>{@link Graphic#scaleX(float)} and {@link Graphic#scaleY(float)} adjust the size of the |
||||
|
* supplied value from the preview scale to the view scale.</li> |
||||
|
* <li>{@link Graphic#translateX(float)} and {@link Graphic#translateY(float)} adjust the coordinate |
||||
|
* from the preview's coordinate system to the view coordinate system.</li> |
||||
|
* </ol> |
||||
|
*/ |
||||
|
public class MGraphicOverlay<T extends MGraphicOverlay.Graphic> extends View { |
||||
|
private final Object mLock = new Object(); |
||||
|
private int mPreviewWidth; |
||||
|
private float mWidthScaleFactor = 1.0f; |
||||
|
private int mPreviewHeight; |
||||
|
private float mHeightScaleFactor = 1.0f; |
||||
|
private int mFacing = CameraSource.CAMERA_FACING_BACK; |
||||
|
private Set<T> mGraphics = new HashSet<>(); |
||||
|
|
||||
|
/** |
||||
|
* Base class for a custom graphics object to be rendered within the graphic overlay. Subclass |
||||
|
* this and implement the {@link Graphic#draw(Canvas)} method to define the |
||||
|
* graphics element. Add instances to the overlay using {@link MGraphicOverlay#add(Graphic)}. |
||||
|
*/ |
||||
|
public static abstract class Graphic { |
||||
|
private MGraphicOverlay mOverlay; |
||||
|
|
||||
|
public Graphic(MGraphicOverlay overlay) { |
||||
|
mOverlay = overlay; |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* Draw the graphic on the supplied canvas. Drawing should use the following methods to |
||||
|
* convert to view coordinates for the graphics that are drawn: |
||||
|
* <ol> |
||||
|
* <li>{@link Graphic#scaleX(float)} and {@link Graphic#scaleY(float)} adjust the size of |
||||
|
* the supplied value from the preview scale to the view scale.</li> |
||||
|
* <li>{@link Graphic#translateX(float)} and {@link Graphic#translateY(float)} adjust the |
||||
|
* coordinate from the preview's coordinate system to the view coordinate system.</li> |
||||
|
* </ol> |
||||
|
* |
||||
|
* @param canvas drawing canvas |
||||
|
*/ |
||||
|
public abstract void draw(Canvas canvas); |
||||
|
|
||||
|
/** |
||||
|
* Adjusts a horizontal value of the supplied value from the preview scale to the view |
||||
|
* scale. |
||||
|
*/ |
||||
|
public float scaleX(float horizontal) { |
||||
|
return horizontal * mOverlay.mWidthScaleFactor; |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* Adjusts a vertical value of the supplied value from the preview scale to the view scale. |
||||
|
*/ |
||||
|
public float scaleY(float vertical) { |
||||
|
return vertical * mOverlay.mHeightScaleFactor; |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* Adjusts the x coordinate from the preview's coordinate system to the view coordinate |
||||
|
* system. |
||||
|
*/ |
||||
|
public float translateX(float x) { |
||||
|
if (mOverlay.mFacing == CameraSource.CAMERA_FACING_FRONT) { |
||||
|
return mOverlay.getWidth() - scaleX(x); |
||||
|
} else { |
||||
|
return scaleX(x); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* Adjusts the y coordinate from the preview's coordinate system to the view coordinate |
||||
|
* system. |
||||
|
*/ |
||||
|
public float translateY(float y) { |
||||
|
return scaleY(y); |
||||
|
} |
||||
|
|
||||
|
public void postInvalidate() { |
||||
|
mOverlay.postInvalidate(); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
public MGraphicOverlay(Context context, AttributeSet attrs) { |
||||
|
super(context, attrs); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* Removes all graphics from the overlay. |
||||
|
*/ |
||||
|
public void clear() { |
||||
|
synchronized (mLock) { |
||||
|
mGraphics.clear(); |
||||
|
} |
||||
|
postInvalidate(); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* Adds a graphic to the overlay. |
||||
|
*/ |
||||
|
public void add(T graphic) { |
||||
|
synchronized (mLock) { |
||||
|
mGraphics.add(graphic); |
||||
|
} |
||||
|
postInvalidate(); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* Removes a graphic from the overlay. |
||||
|
*/ |
||||
|
public void remove(T graphic) { |
||||
|
synchronized (mLock) { |
||||
|
mGraphics.remove(graphic); |
||||
|
} |
||||
|
postInvalidate(); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* Returns a copy (as a list) of the set of all active graphics. |
||||
|
* @return list of all active graphics. |
||||
|
*/ |
||||
|
public List<T> getGraphics() { |
||||
|
synchronized (mLock) { |
||||
|
return new Vector(mGraphics); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* Returns the horizontal scale factor. |
||||
|
*/ |
||||
|
public float getWidthScaleFactor() { |
||||
|
return mWidthScaleFactor; |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* Returns the vertical scale factor. |
||||
|
*/ |
||||
|
public float getHeightScaleFactor() { |
||||
|
return mHeightScaleFactor; |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* Sets the camera attributes for size and facing direction, which informs how to transform |
||||
|
* image coordinates later. |
||||
|
*/ |
||||
|
public void setCameraInfo(int previewWidth, int previewHeight, int facing) { |
||||
|
synchronized (mLock) { |
||||
|
mPreviewWidth = previewWidth; |
||||
|
mPreviewHeight = previewHeight; |
||||
|
mFacing = facing; |
||||
|
} |
||||
|
postInvalidate(); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* Draws the overlay with its associated graphic objects. |
||||
|
*/ |
||||
|
@Override |
||||
|
protected void onDraw(Canvas canvas) { |
||||
|
super.onDraw(canvas); |
||||
|
|
||||
|
synchronized (mLock) { |
||||
|
if ((mPreviewWidth != 0) && (mPreviewHeight != 0)) { |
||||
|
mWidthScaleFactor = (float) canvas.getWidth() / (float) mPreviewWidth; |
||||
|
mHeightScaleFactor = (float) canvas.getHeight() / (float) mPreviewHeight; |
||||
|
} |
||||
|
|
||||
|
for (Graphic graphic : mGraphics) { |
||||
|
graphic.draw(canvas); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
Write
Preview
Loading…
Cancel
Save
Reference in new issue