diff --git a/core/src/cz/nic/tablexia/menu/user/UserMenu.java b/core/src/cz/nic/tablexia/menu/user/UserMenu.java index acf839fff72a612f22b6deec11a64623a9c244ff..eeb054ddaec965af9d94162c227fe5f93e4a42f0 100644 --- a/core/src/cz/nic/tablexia/menu/user/UserMenu.java +++ b/core/src/cz/nic/tablexia/menu/user/UserMenu.java @@ -188,16 +188,15 @@ public class UserMenu extends AbstractMenu { final UserMenuQrCodeSelectBoxItemGroup qrCodeSync = new UserMenuQrCodeSelectBoxItemGroup() { @Override public void performAction() { + //Close if qr code scanner is active + if(Tablexia.getQRCodeScanner().isCameraPreviewActive()) return; + //TODO Draw the magnifier and open the camera //showSyncQrDialog() //probably ? - Tablexia.getQRCodeScanner().setQRCodeListener(new QRCodeScanner.QRCodeListener() { @Override - public void onCodeScanned(String data) { + public void onCodeScanned(final String data) { Tablexia.getQRCodeScanner().disableScanning(); - Tablexia.getQRCodeScanner().stopCameraPreview(); - Tablexia.getQRCodeScanner().removeQRCodeListener(); - Pattern pattern = Pattern.compile(CODE_REGEX); Matcher matcher = pattern.matcher(data); @@ -205,6 +204,9 @@ public class UserMenu extends AbstractMenu { String uuid = matcher.group(1); runSyncWork(uuid); } + + Tablexia.getQRCodeScanner().stopCameraPreview(); + Tablexia.getQRCodeScanner().removeQRCodeListener(); } }); diff --git a/core/src/cz/nic/tablexia/util/ui/QRCodeScanner.java b/core/src/cz/nic/tablexia/util/ui/QRCodeScanner.java index 86d15b5a4787e3b183f525dfff4400f449297b61..5b620e128a1df14d146f410b6907f195cc7888b3 100644 --- a/core/src/cz/nic/tablexia/util/ui/QRCodeScanner.java +++ b/core/src/cz/nic/tablexia/util/ui/QRCodeScanner.java @@ -1,5 +1,9 @@ package cz.nic.tablexia.util.ui; +import com.badlogic.gdx.Gdx; + +import cz.nic.tablexia.util.Log; + /** * Created by drahomir on 10/5/16. */ @@ -10,6 +14,7 @@ public abstract class QRCodeScanner { private QRCodeListener listener; private boolean scanningEnabled = false; + private boolean cameraPreviewActive = false; public void enableScanning() { scanningEnabled = true; @@ -23,6 +28,10 @@ public abstract class QRCodeScanner { return scanningEnabled; } + public boolean isCameraPreviewActive() { + return cameraPreviewActive; + } + public void setQRCodeListener(QRCodeListener listener) { this.listener = listener; } @@ -31,15 +40,44 @@ public abstract class QRCodeScanner { this.listener = null; } - public synchronized void onCodeScanned(String data) { + public synchronized void onCodeScanned(final String data) { + if(!isScanningEnabled()) return; + if(listener != null) { - listener.onCodeScanned(data); + Gdx.app.postRunnable(new Runnable() { + @Override + public void run() { listener.onCodeScanned(data); } + }); + } + } + + public void startCameraPreview() { + if(isCameraPreviewActive()) { + Log.info(getClass(), "Camera preview is already active. Cannot start it again!"); + return; } + + Log.info(getClass(), "Starting Camera Preview!"); + + cameraPreviewActive = true; + onCameraPreviewStarted(); + } + + public void stopCameraPreview() { + if(!isCameraPreviewActive()) { + Log.info(getClass(), "Camera preview is not running. Cannot stop it!"); + return; + } + + Log.info(getClass(), "Stopping Camera Preview"); + + onCameraPreviewStopped(); + cameraPreviewActive = false; } /* ABSTRACT METHODS */ public abstract boolean isCameraAccessible(); - public abstract void startCameraPreview(); - public abstract void stopCameraPreview(); + public abstract void onCameraPreviewStarted(); + public abstract void onCameraPreviewStopped(); } diff --git a/ios/src/cz/nic/tablexia/IOSQRCodeScanner.java b/ios/src/cz/nic/tablexia/IOSQRCodeScanner.java index 7ff4853d208404e5554ce61a240435731ea9d943..57f8876ec3ace89ccaa40a2a4760696cc757f3d4 100644 --- a/ios/src/cz/nic/tablexia/IOSQRCodeScanner.java +++ b/ios/src/cz/nic/tablexia/IOSQRCodeScanner.java @@ -13,22 +13,25 @@ import org.robovm.apple.avfoundation.AVCaptureOutput; import org.robovm.apple.avfoundation.AVCaptureSession; import org.robovm.apple.avfoundation.AVCaptureVideoOrientation; import org.robovm.apple.avfoundation.AVCaptureVideoPreviewLayer; +import org.robovm.apple.avfoundation.AVLayerVideoGravity; import org.robovm.apple.avfoundation.AVMediaType; import org.robovm.apple.avfoundation.AVMetadataObject; import org.robovm.apple.avfoundation.AVMetadataObjectType; import org.robovm.apple.dispatch.DispatchQueue; +import org.robovm.apple.dispatch.DispatchQueueAttr; import org.robovm.apple.foundation.NSArray; import org.robovm.apple.foundation.NSErrorException; +import org.robovm.apple.uikit.UIApplication; +import org.robovm.apple.uikit.UIInterfaceOrientation; import org.robovm.apple.uikit.UIView; +import org.robovm.apple.uikit.UIViewController; import java.util.Arrays; import cz.nic.tablexia.util.ui.QRCodeScanner; -/** - * Created by drahomir on 10/5/16. - */ public class IOSQRCodeScanner extends QRCodeScanner { + private static final String DISPATCH_QUEUE_NAME = "qrCodeQueue"; private class CaptureMetadataOutputDelegate extends AVCaptureMetadataOutputObjectsDelegateAdapter{ private IOSQRCodeScanner qrCodeScanner; @@ -39,14 +42,14 @@ public class IOSQRCodeScanner extends QRCodeScanner { @Override public void didOutputMetadataObjects(AVCaptureOutput captureOutput, NSArray<AVMetadataObject> metadataObjects, AVCaptureConnection connection) { - if(!isScanningEnabled()) return; - if(metadataObjects != null && metadataObjects.size() > 0) { AVMetadataObject data = metadataObjects.get(0); if(data.getType() == AVMetadataObjectType.QRCode) { //Found QR Code TODO - change data.toString() qrCodeScanner.onCodeScanned(data.toString()); } + + metadataObjects.dispose(); } } } @@ -59,13 +62,12 @@ public class IOSQRCodeScanner extends QRCodeScanner { private CaptureMetadataOutputDelegate captureDelegate; + private UIViewController cameraPreviewViewController; private UIView cameraPreviewView; private AVCaptureVideoPreviewLayer videoPreviewLayer; private DispatchQueue dispatchQueue; - private boolean cameraPreviewRunning = false; - private void initializeCameraDevice() { if(cameraDevice != null) return; @@ -86,12 +88,22 @@ public class IOSQRCodeScanner extends QRCodeScanner { return cameraDevice != null; } - @Override - public void startCameraPreview() { - if(cameraPreviewRunning) return; + private AVCaptureVideoOrientation getOrientationForCameraPreviewLayer() { + switch (UIApplication.getSharedApplication().getStatusBarOrientation()) { + case LandscapeLeft: return AVCaptureVideoOrientation.LandscapeLeft; + case LandscapeRight: return AVCaptureVideoOrientation.LandscapeRight; + case Portrait: return AVCaptureVideoOrientation.Portrait; + case PortraitUpsideDown: return AVCaptureVideoOrientation.PortraitUpsideDown; + default: return AVCaptureVideoOrientation.LandscapeLeft; + } + } - cameraPreviewRunning = true; + private void updateViewPreviewLayerOrientation() { + if(videoPreviewLayer != null ) videoPreviewLayer.getConnection().setVideoOrientation(getOrientationForCameraPreviewLayer()); + } + @Override + public void onCameraPreviewStarted() { captureSession = new AVCaptureSession(); initializeCameraDevice(); @@ -104,8 +116,9 @@ public class IOSQRCodeScanner extends QRCodeScanner { e.printStackTrace(); } + dispatchQueue = DispatchQueue.create(DISPATCH_QUEUE_NAME, DispatchQueueAttr.Serial()); + captureMetadataOutput = new AVCaptureMetadataOutput(); - dispatchQueue = DispatchQueue.getMainQueue(); captureDelegate = new CaptureMetadataOutputDelegate(); captureMetadataOutput.setMetadataObjectsDelegate(captureDelegate, dispatchQueue); @@ -114,61 +127,73 @@ public class IOSQRCodeScanner extends QRCodeScanner { IOSApplication iosApplication = (IOSApplication) Gdx.app; + cameraPreviewViewController = new UIViewController() { + @Override + public void viewWillLayoutSubviews() { + super.viewWillLayoutSubviews(); + updateViewPreviewLayerOrientation(); + } + + @Override + public void willAnimateRotation(UIInterfaceOrientation uiInterfaceOrientation, double v) { + //Unforunately this method is deprecated and there isnt binding in RoboVM for these replacements... + // - willTransitionToTraitCollection:withTransitionCoordinator: + // - viewWillTransitionToSize:withTransitionCoordinator: + super.willAnimateRotation(uiInterfaceOrientation, v); + updateViewPreviewLayerOrientation(); + } + }; + cameraPreviewView = new UIView(); cameraPreviewView.setBounds(iosApplication.getUIViewController().getView().getBounds()); cameraPreviewView.setCenter(iosApplication.getUIViewController().getView().getCenter()); + cameraPreviewViewController.setView(cameraPreviewView); + videoPreviewLayer = new AVCaptureVideoPreviewLayer(captureSession); - //TODO - Change orientation on device rotate - videoPreviewLayer.getConnection().setVideoOrientation(AVCaptureVideoOrientation.LandscapeLeft); + videoPreviewLayer.setVideoGravity(AVLayerVideoGravity.ResizeAspectFill); + videoPreviewLayer.getConnection().setVideoOrientation(getOrientationForCameraPreviewLayer()); videoPreviewLayer.setFrame(cameraPreviewView.getBounds()); - //Prevents java objects to get GCed unless captureSession ObjC object gets deallocated! - cameraDevice.addStrongRef(captureDeviceInput); - captureDeviceInput.addStrongRef(captureDelegate); - captureMetadataOutput.addStrongRef(captureDelegate); - - captureDelegate.addStrongRef(captureSession); - videoPreviewLayer.addStrongRef(captureSession); - cameraPreviewView.getLayer().addSublayer(videoPreviewLayer); + + iosApplication.getUIViewController().addChildViewController(cameraPreviewViewController); iosApplication.getUIViewController().getView().addSubview(cameraPreviewView); if(!captureSession.isRunning()) captureSession.startRunning(); } @Override - public void stopCameraPreview() { - if(!cameraPreviewRunning) return; - - cameraPreviewRunning = false; - + public void onCameraPreviewStopped() { captureSession.stopRunning(); captureSession.removeInput(captureDeviceInput); captureSession.removeOutput(captureMetadataOutput); - captureDeviceInput.removeStrongRef(captureDelegate); - captureDeviceInput.release(); + captureDeviceInput.dispose(); captureDeviceInput = null; - captureMetadataOutput.removeStrongRef(captureDelegate); - captureMetadataOutput.release(); + captureMetadataOutput.dispose(); captureMetadataOutput = null; - captureDelegate.removeStrongRef(captureSession); - captureDelegate.release(); + captureDelegate.dispose(); captureDelegate = null; - videoPreviewLayer.removeStrongRef(captureSession); videoPreviewLayer.removeFromSuperlayer(); + videoPreviewLayer.dispose(); videoPreviewLayer = null; + cameraPreviewViewController.removeFromParentViewController(); + cameraPreviewViewController.dispose(); + cameraPreviewViewController = null; + cameraPreviewView.removeFromSuperview(); - cameraPreviewView.release(); + cameraPreviewView.dispose(); cameraPreviewView = null; - captureSession.release(); + captureSession.dispose(); captureSession = null; + + dispatchQueue = null; } }