Flutter - How to scan QR code with front camera? - dart

i want to be able to scan QR code in my app with the front camera but all the available plugins supports only the main camera and dont have any option to capture from front cam, is there is any solution to this problem in the mainwhile?

You can configure it in the options of the scan function:
Future scan() async {
print("Scanning!!!");
try {
String barcode = await BarcodeScanner.scan(
options: ScanOptions(
useCamera: 1,
)
).then((value) { return value.rawContent;});
setState(() => this.barcode = barcode);
} catch (e) {
if (e.code == BarcodeScanner.cameraAccessDenied) {
setState(() {
this.barcode = 'The user did not grant the camera permission!';
});
} else {
setState(() => this.barcode = 'Unknown error: $e');
}
}
}
Using the number 1 in the useCamera property in ScanOptions means the front camera.
Hope it helps.

Related

Capacitor iOS Geolocation watchPostion kCLErrorDomain error 1 while permission granted

I am using an older version of the capacitor geolocation, v1.3.1, and recently switched to the watchPosition implementation but occasionally that created a situation where the position is null or undefined even when the device is showing the location icon being active for the app. I tried to solve that by falling back to the slower getCurrentPosition function but still persists. Has anyone run into this issue before? Here is a gist of the hook.
https://gist.github.com/billpull/8bc6e49872cfee29aa5cef193b59c835
useCurrentPosition.ts
const useCurrentPosition = (): GeoWatchPositionResult => {
const [position, setPosition] = useState<Position>();
const [watchId, setWatchId] = useState("");
const [error, setError] = useState();
const clearWatch = () => {
if (watchId) {
clearPosition({ id: watchId });
setWatchId("");
}
};
const startWatch = async () => {
if (!watchId) {
const id = await watchPosition(async (pos: Position | null, err) => {
if (err) {
setError(err);
}
if (pos) {
setPosition(pos);
} else {
const newPosition = await getCurrentPosition();
setPosition(newPosition);
}
});
setWatchId(id);
}
};
useEffect(() => {
startWatch();
return () => clearWatch();
}, []);
return { currentPosition: position, error };
};
Even though the watchPosition is still returning location data on the interval I am getting a kCLErrorDomain error 1. which online says it means the permission was denied but thats not the case the phone was just in sleep mode. Is there a way to catch this error specifically? Should I clear the watch and restart it on this error?
Edit:
One attempt I made was to use a try catch in the watch, but I still have encountered this issue.
const useCurrentPosition = (): GeoWatchPositionResult => {
const [position, setPosition] = useState<Position>();
const [watchId, setWatchId] = useState("");
const [error, setError] = useState();
const clearWatch = () => {
if (watchId) {
clearPosition({ id: watchId });
setWatchId("");
}
};
const startWatch = async () => {
if (!watchId) {
const id = await watchPosition(async (pos: Position | null, err) => {
try {
if (err) {
setError(err);
}
if (pos) {
setPosition(pos);
} else {
const newPosition = await getCurrentPosition();
setPosition(newPosition);
}
} catch (ex) {
await requestPermission();
clearWatch();
await startWatch();
}
});
setWatchId(id);
}
};
useEffect(() => {
startWatch();
return () => clearWatch();
}, []);
return { currentPosition: position, error };
};
I think you should use this code snippet to check the current position.
import { Geolocation, Geoposition } from '#ionic-native/geolocation/ngx';
constructor(private geolocation: Geolocation) { }
...
let watch = this.geolocation.watchPosition();
watch.subscribe((data) => {
// data can be a set of coordinates, or an error (if an error occurred).
// data.coords.latitude
// data.coords.longitude
});
You can also use the following code snippet to get the current position.
this.geolocation.getCurrentPosition().then((resp) => {
// resp.coords.latitude
// resp.coords.longitude
}).catch((error) => {
console.log('Error getting location', error);
});
If you want to handle the permission denied error, the best way to handle the permission denied error is to check the permission status of the app first before trying to access any location data. This can be done by using the Capacitor Permissions API. If the permission has been granted, you can then proceed to use the watchPosition or getCurrentPosition APIs. If the permission has been denied, you can present a prompt to the user to request permission again.
setState is an asynchronous function, so please pass to it an anonymous function:
if (pos) {
setPosition(pos);
} else {
const newPosition = await getCurrentPosition();
setPosition(() => newPosition);
}

Detecting ibeacon with react-native on ios device

I have an IoT device which is sending iBeacon advertisements and want to detect these signals on my react-native application.
I found this library react-native-kontaktio. It works fine on android, but on ios it does not look like the "didDiscoverDevices" event gets triggered since the console.log is not showing up in my terminal. The app is running and i get no error message.
To configure this library for iOS i did the following:
yarn add react-native-kontaktio
cd ios
pod install
I have also included these permissions in info.plist:
NSBluetoothAlwaysUsageDescription, NSLocationAlwaysAndWhenInUseUsageDescription
This is my react-native version:
react-native-cli: 2.0.1,
react-native: 0.66.4.
This is the code which is working on android, but not on iOS.
import React, {useEffect, useState} from 'react';
import {
Alert,
DeviceEventEmitter,
NativeEventEmitter,
Platform,
PermissionsAndroid,
SafeAreaView,
Text,
StyleSheet,
Button,
} from 'react-native';
import Kontakt, {KontaktModule} from 'react-native-kontaktio';
const {connect, init, startDiscovery, startScanning} = Kontakt;
const kontaktEmitter = new NativeEventEmitter(KontaktModule);
const isAndroid = Platform.OS === 'android';
const isIOS = Platform.OS === 'ios';
const App = () => {
/**
* Android Marshmallow (6.0) and above need to ask the user to grant certain permissions.
* This function does just that.
*/
const requestLocationPermission = async () => {
try {
const granted = await PermissionsAndroid.request(
PermissionsAndroid.PERMISSIONS.ACCESS_COARSE_LOCATION,
{
title: 'Location Permission',
message:
'This example app needs to access your location in order to use bluetooth beacons.',
buttonNeutral: 'Ask Me Later',
buttonNegative: 'Cancel',
buttonPositive: 'OK',
},
);
if (granted === PermissionsAndroid.RESULTS.GRANTED) {
return true;
} else {
// permission denied
return false;
}
} catch (err) {
console.warn(err);
return false;
}
};
const beaconSetup = async () => {
if (isAndroid) {
// Android
const granted = await requestLocationPermission();
if (granted) {
await connect();
await startScanning();
} else {
Alert.alert(
'Permission error',
'Location permission not granted. Cannot scan for beacons',
[{text: 'OK', onPress: () => console.log('OK Pressed')}],
{cancelable: false},
);
}
} else {
// iOS
console.log('IOS', isIOS);
await init();
await startDiscovery();
}
// Add beacon listener
if (isAndroid) {
DeviceEventEmitter.addListener(
'beaconsDidUpdate',
({beacons, region}) => {
console.log('beaconsDidUpdate', beacons, region);
console.log('REGION: ', region);
console.log('BEACONS', beacons);
}
);
} else {
console.log('IOS ADD LISTENER');
kontaktEmitter.addListener('didDiscoverDevices', ({beacons}) => {
console.log('didDiscoverDevices', beacons);
console.log('IOS DISCOVERED BEACON');
});
}
};
useEffect(() => {
beaconSetup();
return () => {
DeviceEventEmitter.removeAllListeners();
};
}, []);
I just solved the problem.
I called startRangingBeaconsInRegion(region) instead of startDiscovery() method and then it worked. I understood this after I read this sentence in documentation: "Discovery (i.e. didDiscoverDevices) can only detect Kontakt.io beacons. Ranging and monitoring also works with beacons of other manufacturers."

SetState call blocks access to field updated by stream

I am making a barcode scanning functionality. The camera feed is setting _currentImage.
// CameraView.dart
CameraImage _currentImage;
CameraImage takePhoto() => _currentImage;
void imageStream(CameraImage image) async {
print("_currentImage is set with " + image.toString()); // keeps saying it sets 'currentImage'
this._currentImage = image;
}
Later on I call takePhoto from button pressed handler and call 'setState' empty...
void pressedShowBarcodeOnScreenButton() async {
var image = this.cameraView.takePhoto();
if (image == null) {
print(// occurs after having called empty 'setState' every time button is pressed
"image was null, maybe it is not possible to take images this fast!");
return;
}
var barcode =
await VisionService.getInstance() // is not producing any errors
.analyzeBarcode(ImageUtils.toAbstractImage(image));
if (Utils.isNullOrEmpty(barcode)) {
return;
}
print("got barcode: " + barcode); // prints all barcodes ok, but stops doing so when calling 'setState'
setState(() {
// Makes it so that 'currentImage' is null at all times, despite continuously set by imageStream given from log
//this.scannedBarcodes = [...this.scannedBarcodes, barcode]; // my intention eventual to update a list
});
}
I suspect it might be due to some underlaying dart behavour, or there is some sort of bug? Why does the setState call prevent me from accessing the _currentImage - or why is blocked from being set by the imageStream()?
The CameraView which contained the CameraController that had the stream, was part of the page to display the barcodes.
So SetState(){} triggered a reconstruction of the CameraView, and the controller was not able to be reconstructed. Probably the old stream was producing the log and was misleading.
It helped to extract the CameraController in this scenario, and have it accessed as singleton, inside CameraViewController that I made:
//...
static CameraController _cameraController;
static Future<CameraController> getCameraControllerInstance() async {
if (_cameraController == null) {
var cameras = await availableCameras();
if (cameras?.length == 0) {
print("the device did not have a camera");
return null;
}
_cameraController = CameraController(cameras[0], ResolutionPreset.max);
await _cameraController.initialize();
_cameraController.startImageStream(updateCurrentImage);
}
return _cameraController;
}
And I use it in a FutureBuilder like so, in the CameraView:
FutureBuilder futureBuilder() => FutureBuilder<CameraController>(
future: CameraViewController.getCameraControllerInstance(), // <--!!!
builder:
(BuildContext context, AsyncSnapshot<CameraController> snapshot) {
if (snapshot.connectionState == ConnectionState.waiting) {
return LoadingPage();
}
return content(snapshot.data);
});
I am tempted to have one instance of the CameraView around at all times (?), since it looks glitchy when updating the list - but as I see it would be anti pattern as the Widget's are supposed to rerender (immutable) themselves:
The content of the page that has the barcode list:
Widget content() {
return Container(
child: (Column(children: [
cameraView, // camera view part of page and recontructed on 'scannedProducts' state change
header(),
scanButton(),
Column(
children: this.scannedProducts(), /// list
)
])));
}

React Native: storage only works in simulator

I've tried AsyncStorage, react-native-store, and react-native-simple-store, and they all work in the simulator, but not on a device. I'm using redux and redux-thunk to load the stored state. I call the following function in my root component's componentDidMount method (using react-native-simple-store):
export function loadState() {
return (dispatch, getState) => {
store.get('state').then((state) => {
if (state) {
let action = {
type: LOAD_STATE,
state: fromJS(state),
};
dispatch(action);
} else {
store.save('state', initialState);
}
}).catch((error) => {
console.log(error);
});
};
}
And then in my reducer when the user triggers an update I'll update the state in storage before returning the new state like this:
case SET_UPDATED_DATE:
newState = state.set('updatedDate', action.date);
store.update('state', newState.toJS())
.catch((error) => console.log(error));
return newState;
Is the initializing/updating approach insufficient? Does something special need to be done to set it up for a device? Or is redux-thunk not supported when run on a device – main.jsbundle or with the development server – (putting a log statement at the top of the loadState function's returned function leads me to believe it may not be being called when on a device)?
Following the AsyncStorage docs' example, I've figured out a way to make it work. In my reducer file (my redux state is an Immutable.js object):
var STORAGE_KEY = '#AppName:state';
export function loadState() {
return async (dispatch, getState) => {
try {
var value = await AsyncStorage.getItem(STORAGE_KEY);
if (value !== null) {
dispatch(replaceState(value));
console.log('Recovered selection from disk:');
console.log(value);
} else {
saveState(JSON.stringify(initialState.toJS()));
console.log('No state on disk. Initialized with initialState.');
}
} catch (error) {
console.log('AsyncStorage error: ' + error.message);
}
};
}
function replaceState(newState) {
return {
type: REPLACE_STATE,
newState: fromJS(JSON.parse(newState)),
};
}
async function saveState(state) {
try {
await AsyncStorage.setItem(STORAGE_KEY, state);
console.log('Saved selection to disk:');
console.log(state);
} catch (error) {
console.log('AsyncStorage error: ' + error.message);
}
}
Then in the reducer function:
case SET_UPDATED_DATE:
newState = state.set('updatedDate', action.date);
saveState(JSON.stringify(newState.toJS()));
return newState;

How to prevent multiple instances in Electron

I do not know if this is possible but I might as well give it a chance and ask.
I'm doing an Electron app and I'd like to know if it is possible to have no more than a single instance at a time.
I have found this gist but I'm not sure hot to use it. Can someone shed some light of share a better idea ?
var preventMultipleInstances = function(window) {
var socket = (process.platform === 'win32') ? '\\\\.\\pipe\\myapp-sock' : path.join(os.tmpdir(), 'myapp.sock');
net.connect({path: socket}, function () {
var errorMessage = 'Another instance of ' + pjson.productName + ' is already running. Only one instance of the app can be open at a time.'
dialog.showMessageBox(window, {'type': 'error', message: errorMessage, buttons: ['OK']}, function() {
window.destroy()
})
}).on('error', function (err) {
if (process.platform !== 'win32') {
// try to unlink older socket if it exists, if it doesn't,
// ignore ENOENT errors
try {
fs.unlinkSync(socket);
} catch (e) {
if (e.code !== 'ENOENT') {
throw e;
}
}
}
net.createServer(function (connection) {}).listen(socket);;
});
}
There is a new API now: requestSingleInstanceLock
const { app } = require('electron')
let myWindow = null
const gotTheLock = app.requestSingleInstanceLock()
if (!gotTheLock) {
app.quit()
} else {
app.on('second-instance', (event, commandLine, workingDirectory) => {
// Someone tried to run a second instance, we should focus our window.
if (myWindow) {
if (myWindow.isMinimized()) myWindow.restore()
myWindow.focus()
}
})
// Create myWindow, load the rest of the app, etc...
app.on('ready', () => {
})
}
Use the makeSingleInstance function in the app module, there's even an example in the docs.
In Case you need the code.
let mainWindow = null;
//to make singleton instance
const isSecondInstance = app.makeSingleInstance((commandLine, workingDirectory) => {
// Someone tried to run a second instance, we should focus our window.
if (mainWindow) {
if (mainWindow.isMinimized()) mainWindow.restore()
mainWindow.focus()
}
})
if (isSecondInstance) {
app.quit()
}

Resources