mirror of
https://github.com/BlueWallet/BlueWallet.git
synced 2025-01-18 05:12:47 +01:00
Merge pull request #7472 from BlueWallet/cameraki
REF: Upgrade Camera kit
This commit is contained in:
commit
0ee3da9dc0
218
components/CameraScreen.tsx
Normal file
218
components/CameraScreen.tsx
Normal file
@ -0,0 +1,218 @@
|
||||
import React, { useState, useRef } from 'react';
|
||||
import { Animated, ImageURISource, SafeAreaView, StatusBar, StyleSheet, TouchableOpacity, View } from 'react-native';
|
||||
import { Camera, CameraApi, CameraType, Orientation } from 'react-native-camera-kit';
|
||||
import loc from '../loc';
|
||||
import { Icon } from '@rneui/base';
|
||||
|
||||
interface CameraScreenProps {
|
||||
onCancelButtonPress: () => void;
|
||||
showImagePickerButton?: boolean;
|
||||
showFilePickerButton?: boolean;
|
||||
onImagePickerButtonPress?: () => void;
|
||||
onFilePickerButtonPress?: () => void;
|
||||
torchOnImage?: ImageURISource;
|
||||
torchOffImage?: ImageURISource;
|
||||
onReadCode?: (event: any) => void;
|
||||
cameraFlipImage?: ImageURISource;
|
||||
}
|
||||
|
||||
const CameraScreen: React.FC<CameraScreenProps> = ({
|
||||
onCancelButtonPress,
|
||||
showImagePickerButton,
|
||||
showFilePickerButton,
|
||||
onImagePickerButtonPress,
|
||||
onFilePickerButtonPress,
|
||||
torchOnImage,
|
||||
torchOffImage,
|
||||
onReadCode,
|
||||
cameraFlipImage,
|
||||
}) => {
|
||||
const cameraRef = useRef<CameraApi>(null);
|
||||
const [torchMode, setTorchMode] = useState(false);
|
||||
const [cameraType, setCameraType] = useState(CameraType.Back);
|
||||
const [zoom, setZoom] = useState<number | undefined>();
|
||||
const [orientationAnim] = useState(new Animated.Value(3));
|
||||
|
||||
const onSwitchCameraPressed = () => {
|
||||
const direction = cameraType === CameraType.Back ? CameraType.Front : CameraType.Back;
|
||||
setCameraType(direction);
|
||||
setZoom(1); // When changing camera type, reset to default zoom for that camera
|
||||
};
|
||||
|
||||
const onSetTorch = () => {
|
||||
setTorchMode(!torchMode);
|
||||
};
|
||||
|
||||
// Counter-rotate the icons to indicate the actual orientation of the captured photo.
|
||||
// For this example, it'll behave incorrectly since UI orientation is allowed (and already-counter rotates the entire screen)
|
||||
// For real phone apps, lock your UI orientation using a library like 'react-native-orientation-locker'
|
||||
const rotateUi = true;
|
||||
const uiRotation = orientationAnim.interpolate({
|
||||
inputRange: [1, 4],
|
||||
outputRange: ['180deg', '-90deg'],
|
||||
});
|
||||
const uiRotationStyle = rotateUi ? { transform: [{ rotate: uiRotation }] } : undefined;
|
||||
|
||||
function rotateUiTo(rotationValue: number) {
|
||||
Animated.timing(orientationAnim, {
|
||||
toValue: rotationValue,
|
||||
useNativeDriver: true,
|
||||
duration: 200,
|
||||
isInteraction: false,
|
||||
}).start();
|
||||
}
|
||||
|
||||
return (
|
||||
<View style={styles.screen}>
|
||||
<StatusBar hidden />
|
||||
<SafeAreaView style={styles.topButtons}>
|
||||
<TouchableOpacity style={styles.topButton} onPress={onSetTorch}>
|
||||
<Animated.Image
|
||||
source={torchMode ? torchOnImage : torchOffImage}
|
||||
resizeMode="contain"
|
||||
style={[styles.topButtonImg, uiRotationStyle]}
|
||||
/>
|
||||
</TouchableOpacity>
|
||||
<View style={styles.rightButtonsContainer}>
|
||||
{showImagePickerButton && (
|
||||
<TouchableOpacity
|
||||
accessibilityRole="button"
|
||||
accessibilityLabel={loc._.pick_image}
|
||||
style={[styles.topButton, styles.spacing, uiRotationStyle]}
|
||||
onPress={onImagePickerButtonPress}
|
||||
>
|
||||
<Icon name="image" type="font-awesome" color="#ffffff" />
|
||||
</TouchableOpacity>
|
||||
)}
|
||||
{showFilePickerButton && (
|
||||
<TouchableOpacity
|
||||
accessibilityRole="button"
|
||||
accessibilityLabel={loc._.pick_file}
|
||||
style={[styles.topButton, styles.spacing, uiRotationStyle]}
|
||||
onPress={onFilePickerButtonPress}
|
||||
>
|
||||
<Icon name="file-import" type="font-awesome-5" color="#ffffff" />
|
||||
</TouchableOpacity>
|
||||
)}
|
||||
</View>
|
||||
</SafeAreaView>
|
||||
|
||||
<View style={styles.cameraContainer}>
|
||||
<Camera
|
||||
ref={cameraRef}
|
||||
style={styles.cameraPreview}
|
||||
cameraType={cameraType}
|
||||
resetFocusWhenMotionDetected
|
||||
zoom={zoom}
|
||||
maxZoom={10}
|
||||
onZoom={e => {
|
||||
console.debug('zoom', e.nativeEvent.zoom);
|
||||
setZoom(e.nativeEvent.zoom);
|
||||
}}
|
||||
onReadCode={onReadCode}
|
||||
torchMode={torchMode ? 'on' : 'off'}
|
||||
shutterPhotoSound
|
||||
maxPhotoQualityPrioritization="quality"
|
||||
onOrientationChange={e => {
|
||||
// We recommend locking the camera UI to portrait (using a different library)
|
||||
// and rotating the UI elements counter to the orientation
|
||||
// However, we include onOrientationChange so you can match your UI to what the camera does
|
||||
switch (e.nativeEvent.orientation) {
|
||||
case Orientation.PORTRAIT_UPSIDE_DOWN:
|
||||
console.debug('orientationChange', 'PORTRAIT_UPSIDE_DOWN');
|
||||
rotateUiTo(1);
|
||||
break;
|
||||
case Orientation.LANDSCAPE_LEFT:
|
||||
console.debug('orientationChange', 'LANDSCAPE_LEFT');
|
||||
rotateUiTo(2);
|
||||
break;
|
||||
case Orientation.PORTRAIT:
|
||||
console.debug('orientationChange', 'PORTRAIT');
|
||||
rotateUiTo(3);
|
||||
break;
|
||||
case Orientation.LANDSCAPE_RIGHT:
|
||||
console.debug('orientationChange', 'LANDSCAPE_RIGHT');
|
||||
rotateUiTo(4);
|
||||
break;
|
||||
default:
|
||||
console.debug('orientationChange', e.nativeEvent);
|
||||
break;
|
||||
}
|
||||
}}
|
||||
/>
|
||||
</View>
|
||||
|
||||
<SafeAreaView style={styles.bottomButtons}>
|
||||
<TouchableOpacity onPress={onCancelButtonPress}>
|
||||
<Animated.Text style={[styles.backTextStyle, uiRotationStyle]}>{loc._.cancel}</Animated.Text>
|
||||
</TouchableOpacity>
|
||||
<TouchableOpacity style={styles.bottomButton} onPress={onSwitchCameraPressed}>
|
||||
<Animated.Image source={cameraFlipImage as ImageURISource} resizeMode="contain" style={[styles.topButtonImg, uiRotationStyle]} />
|
||||
</TouchableOpacity>
|
||||
</SafeAreaView>
|
||||
</View>
|
||||
);
|
||||
};
|
||||
|
||||
export default CameraScreen;
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
screen: {
|
||||
height: '100%',
|
||||
backgroundColor: '#000000',
|
||||
},
|
||||
topButtons: {
|
||||
margin: 10,
|
||||
zIndex: 10,
|
||||
flexDirection: 'row',
|
||||
justifyContent: 'space-between',
|
||||
},
|
||||
topButton: {
|
||||
backgroundColor: '#222',
|
||||
width: 44,
|
||||
height: 44,
|
||||
borderRadius: 22,
|
||||
justifyContent: 'center',
|
||||
alignItems: 'center',
|
||||
},
|
||||
topButtonImg: {
|
||||
margin: 10,
|
||||
width: 24,
|
||||
height: 24,
|
||||
},
|
||||
cameraContainer: {
|
||||
justifyContent: 'center',
|
||||
flex: 1,
|
||||
},
|
||||
cameraPreview: {
|
||||
width: '100%',
|
||||
height: '100%',
|
||||
},
|
||||
bottomButtons: {
|
||||
margin: 10,
|
||||
flexDirection: 'row',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'space-between',
|
||||
},
|
||||
backTextStyle: {
|
||||
padding: 10,
|
||||
color: 'white',
|
||||
fontSize: 20,
|
||||
},
|
||||
rightButtonsContainer: {
|
||||
flexDirection: 'row',
|
||||
alignItems: 'center',
|
||||
},
|
||||
bottomButton: {
|
||||
backgroundColor: '#222',
|
||||
width: 44,
|
||||
height: 44,
|
||||
borderRadius: 22,
|
||||
justifyContent: 'center',
|
||||
alignItems: 'center',
|
||||
marginLeft: 10,
|
||||
},
|
||||
spacing: {
|
||||
marginLeft: 20,
|
||||
},
|
||||
});
|
@ -1588,8 +1588,27 @@ PODS:
|
||||
- React-logger (= 0.75.4)
|
||||
- React-perflogger (= 0.75.4)
|
||||
- React-utils (= 0.75.4)
|
||||
- ReactNativeCameraKit (13.0.0):
|
||||
- ReactNativeCameraKit (14.1.0):
|
||||
- DoubleConversion
|
||||
- glog
|
||||
- hermes-engine
|
||||
- RCT-Folly (= 2024.01.01.00)
|
||||
- RCTRequired
|
||||
- RCTTypeSafety
|
||||
- React-Core
|
||||
- React-debug
|
||||
- React-Fabric
|
||||
- React-featureflags
|
||||
- React-graphics
|
||||
- React-ImageManager
|
||||
- React-NativeModulesApple
|
||||
- React-RCTFabric
|
||||
- React-rendererdebug
|
||||
- React-utils
|
||||
- ReactCodegen
|
||||
- ReactCommon/turbomodule/bridging
|
||||
- ReactCommon/turbomodule/core
|
||||
- Yoga
|
||||
- RealmJS (20.1.0):
|
||||
- React
|
||||
- RNCAsyncStorage (2.1.0):
|
||||
@ -2258,7 +2277,7 @@ SPEC CHECKSUMS:
|
||||
React-utils: 02526ea15628a768b8db9517b6017a1785c734d2
|
||||
ReactCodegen: 8b5341ecb61898b8bd40a73ebc443c6bf2d14423
|
||||
ReactCommon: 36d48f542b4010786d6b2bcee615fe5f906b7105
|
||||
ReactNativeCameraKit: f058d47e0b1e55fd819bb55ee16505a2e0ca53db
|
||||
ReactNativeCameraKit: e72b838dac4ea2da19b7eb5d00b23125072790fd
|
||||
RealmJS: 9fd51c849eb552ade9f7b11db42a319b4f6cab4c
|
||||
RNCAsyncStorage: c91d753ede6dc21862c4922cd13f98f7cfde578e
|
||||
RNCClipboard: dbcf25b8f666b4685c02eeb65be981d30198e505
|
||||
|
12
package-lock.json
generated
12
package-lock.json
generated
@ -63,7 +63,7 @@
|
||||
"react-native": "0.75.4",
|
||||
"react-native-biometrics": "3.0.1",
|
||||
"react-native-blue-crypto": "github:BlueWallet/react-native-blue-crypto#3cb5442",
|
||||
"react-native-camera-kit": "13.0.0",
|
||||
"react-native-camera-kit": "14.1.0",
|
||||
"react-native-crypto": "2.2.0",
|
||||
"react-native-default-preference": "https://github.com/BlueWallet/react-native-default-preference.git#6338a1f1235e4130b8cfc2dd3b53015eeff2870c",
|
||||
"react-native-device-info": "13.2.0",
|
||||
@ -20530,12 +20530,12 @@
|
||||
}
|
||||
},
|
||||
"node_modules/react-native-camera-kit": {
|
||||
"version": "13.0.0",
|
||||
"resolved": "https://registry.npmjs.org/react-native-camera-kit/-/react-native-camera-kit-13.0.0.tgz",
|
||||
"integrity": "sha512-fnkyivCG2xzS+14/doP8pCAYNafYaTyg5J0t+JJltJdgKSHf328OG44Rd+fnbbEOydZxgy/bcuLB24R0kCbynw==",
|
||||
"version": "14.1.0",
|
||||
"resolved": "https://registry.npmjs.org/react-native-camera-kit/-/react-native-camera-kit-14.1.0.tgz",
|
||||
"integrity": "sha512-idkg+Sa2KbGvF6SUqmuAr2U12qBELdiuUJ6fxgB4whUC2AyYHi5jBxiGv6whY/eTB3is7nW1S+TjyM9pEBzNzw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"lodash": "^4.14.2"
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react": "*",
|
||||
|
@ -59,7 +59,7 @@
|
||||
"android:clean": "cd android; ./gradlew clean ; cd .. ; npm run android",
|
||||
"ios": "react-native run-ios",
|
||||
"postinstall": "rn-nodeify --install buffer,events,process,stream,inherits,path,assert,crypto --hack; npm run releasenotes2json; npm run branch2json; npm run patches",
|
||||
"patches": "patch -p1 < scripts/react-native-camera-kit.patch;",
|
||||
"patches": "",
|
||||
"test": "npm run tslint && npm run lint && npm run unit && npm run jest",
|
||||
"jest": "jest tests/integration/*",
|
||||
"e2e:debug-build": "detox build -c android.debug",
|
||||
@ -127,7 +127,7 @@
|
||||
"react-native": "0.75.4",
|
||||
"react-native-biometrics": "3.0.1",
|
||||
"react-native-blue-crypto": "github:BlueWallet/react-native-blue-crypto#3cb5442",
|
||||
"react-native-camera-kit": "13.0.0",
|
||||
"react-native-camera-kit": "14.1.0",
|
||||
"react-native-crypto": "2.2.0",
|
||||
"react-native-default-preference": "https://github.com/BlueWallet/react-native-default-preference.git#6338a1f1235e4130b8cfc2dd3b53015eeff2870c",
|
||||
"react-native-device-info": "13.2.0",
|
||||
|
@ -2,9 +2,7 @@ import { useFocusEffect, useIsFocused, useNavigation, useRoute } from '@react-na
|
||||
import * as bitcoin from 'bitcoinjs-lib';
|
||||
import createHash from 'create-hash';
|
||||
import React, { useCallback, useEffect, useState } from 'react';
|
||||
import { Alert, Image, Platform, StyleSheet, TextInput, TouchableOpacity, View } from 'react-native';
|
||||
import { CameraScreen } from 'react-native-camera-kit';
|
||||
import { Icon } from '@rneui/themed';
|
||||
import { Alert, Platform, StyleSheet, TextInput, TouchableOpacity, View } from 'react-native';
|
||||
import Base43 from '../../blue_modules/base43';
|
||||
import * as fs from '../../blue_modules/fs';
|
||||
import { BlueURDecoder, decodeUR, extractSingleWorkload } from '../../blue_modules/ur';
|
||||
@ -15,6 +13,7 @@ import { useTheme } from '../../components/themes';
|
||||
import { isCameraAuthorizationStatusGranted } from '../../helpers/scan-qr';
|
||||
import loc from '../../loc';
|
||||
import { useSettings } from '../../hooks/context/useSettings';
|
||||
import CameraScreen from '../../components/CameraScreen';
|
||||
|
||||
let decoder = false;
|
||||
|
||||
@ -23,39 +22,6 @@ const styles = StyleSheet.create({
|
||||
flex: 1,
|
||||
backgroundColor: '#000000',
|
||||
},
|
||||
closeTouch: {
|
||||
width: 40,
|
||||
height: 40,
|
||||
backgroundColor: 'rgba(0,0,0,0.4)',
|
||||
justifyContent: 'center',
|
||||
borderRadius: 20,
|
||||
position: 'absolute',
|
||||
left: 16,
|
||||
top: 55,
|
||||
},
|
||||
closeImage: {
|
||||
alignSelf: 'center',
|
||||
},
|
||||
imagePickerTouch: {
|
||||
width: 40,
|
||||
height: 40,
|
||||
backgroundColor: 'rgba(0,0,0,0.4)',
|
||||
justifyContent: 'center',
|
||||
borderRadius: 20,
|
||||
position: 'absolute',
|
||||
left: 24,
|
||||
bottom: 48,
|
||||
},
|
||||
filePickerTouch: {
|
||||
width: 40,
|
||||
height: 40,
|
||||
backgroundColor: 'rgba(0,0,0,0.4)',
|
||||
justifyContent: 'center',
|
||||
borderRadius: 20,
|
||||
position: 'absolute',
|
||||
left: 96,
|
||||
bottom: 48,
|
||||
},
|
||||
openSettingsContainer: {
|
||||
flex: 1,
|
||||
justifyContent: 'center',
|
||||
@ -67,6 +33,9 @@ const styles = StyleSheet.create({
|
||||
height: 60,
|
||||
backgroundColor: 'rgba(0,0,0,0.01)',
|
||||
position: 'absolute',
|
||||
top: 10,
|
||||
left: '50%',
|
||||
transform: [{ translateX: -30 }],
|
||||
},
|
||||
backdoorInputWrapper: { position: 'absolute', left: '5%', top: '0%', width: '90%', height: '70%', backgroundColor: 'white' },
|
||||
progressWrapper: { position: 'absolute', alignSelf: 'center', alignItems: 'center', top: '50%', padding: 8, borderRadius: 8 },
|
||||
@ -288,7 +257,7 @@ const ScanQRCode = () => {
|
||||
setIsLoading(false);
|
||||
};
|
||||
|
||||
const showImagePicker = () => {
|
||||
const onShowImagePickerButtonPress = () => {
|
||||
if (!isLoading) {
|
||||
setIsLoading(true);
|
||||
fs.showImagePickerAndReadImage()
|
||||
@ -321,29 +290,13 @@ const ScanQRCode = () => {
|
||||
cameraFlipImage={require('../../img/camera-rotate-solid.png')}
|
||||
onReadCode={event => onBarCodeRead({ data: event?.nativeEvent?.codeStringValue })}
|
||||
showFrame={false}
|
||||
showFilePickerButton={showFileImportButton}
|
||||
showImagePickerButton={true}
|
||||
onFilePickerButtonPress={showFilePicker}
|
||||
onImagePickerButtonPress={onShowImagePickerButtonPress}
|
||||
onCancelButtonPress={dismiss}
|
||||
/>
|
||||
) : null}
|
||||
<TouchableOpacity accessibilityRole="button" accessibilityLabel={loc._.close} style={styles.closeTouch} onPress={dismiss}>
|
||||
<Image style={styles.closeImage} source={require('../../img/close-white.png')} />
|
||||
</TouchableOpacity>
|
||||
<TouchableOpacity
|
||||
accessibilityRole="button"
|
||||
accessibilityLabel={loc._.pick_image}
|
||||
style={styles.imagePickerTouch}
|
||||
onPress={showImagePicker}
|
||||
>
|
||||
<Icon name="image" type="font-awesome" color="#ffffff" />
|
||||
</TouchableOpacity>
|
||||
{showFileImportButton && (
|
||||
<TouchableOpacity
|
||||
accessibilityRole="button"
|
||||
accessibilityLabel={loc._.pick_file}
|
||||
style={styles.filePickerTouch}
|
||||
onPress={showFilePicker}
|
||||
>
|
||||
<Icon name="file-import" type="font-awesome-5" color="#ffffff" />
|
||||
</TouchableOpacity>
|
||||
)}
|
||||
{urTotal > 0 && (
|
||||
<View style={[styles.progressWrapper, stylesHook.progressWrapper]} testID="UrProgressBar">
|
||||
<BlueText>{loc.wallets.please_continue_scanning}</BlueText>
|
||||
|
@ -1,46 +0,0 @@
|
||||
--- ../node_modules/react-native-camera-kit/android/src/main/java/com/rncamerakit/CKCamera.kt 2023-11-10 11:25:36
|
||||
+++ ../node_modules/react-native-camera-kit/android/src/main/java/com/rncamerakit/CKCamera.kt 2023-11-10 11:25:42
|
||||
@@ -180,7 +180,7 @@
|
||||
orientationListener!!.enable()
|
||||
|
||||
val scaleDetector = ScaleGestureDetector(context, object: ScaleGestureDetector.SimpleOnScaleGestureListener() {
|
||||
- override fun onScale(detector: ScaleGestureDetector?): Boolean {
|
||||
+ override fun onScale(detector: ScaleGestureDetector): Boolean {
|
||||
if (zoomMode == "off") return true
|
||||
val cameraControl = camera?.cameraControl ?: return true
|
||||
val zoom = camera?.cameraInfo?.zoomState?.value?.zoomRatio ?: return true
|
||||
|
||||
--- ../node_modules/react-native-camera-kit/dist/CameraScreen.js 2024-09-01 13:00:57
|
||||
+++ ../node_modules/react-native-camera-kit/dist/CameraScreen.js 2024-09-01 13:00:46
|
||||
@@ -61,14 +61,14 @@
|
||||
</TouchableOpacity>));
|
||||
}
|
||||
renderTorchButton() {
|
||||
- return (!this.isCaptureRetakeMode() && (<TouchableOpacity style={{ paddingHorizontal: 15 }} onPress={() => this.onSetTorch()}>
|
||||
- <Image style={[{ flex: 1, justifyContent: 'center' }, this.props.torchImageStyle]} source={this.state.torchMode ? this.props.torchOnImage : this.props.torchOffImage} resizeMode="contain"/>
|
||||
+ return (!this.isCaptureRetakeMode() && (<TouchableOpacity style={{ backgroundColor: '#FFFFFF', borderRadius: 20, height: 40, marginHorizontal: 15 }} onPress={() => this.onSetTorch()}>
|
||||
+ <Image style={[{ width: 40, height: 40, justifyContent: 'center' }, this.props.torchImageStyle]} source={this.state.torchMode ? this.props.torchOnImage : this.props.torchOffImage} resizeMode="contain"/>
|
||||
</TouchableOpacity>));
|
||||
}
|
||||
renderSwitchCameraButton() {
|
||||
return (this.props.cameraFlipImage &&
|
||||
- !this.isCaptureRetakeMode() && (<TouchableOpacity style={{ paddingHorizontal: 15 }} onPress={() => this.onSwitchCameraPressed()}>
|
||||
- <Image style={{ flex: 1, justifyContent: 'center' }} source={this.props.cameraFlipImage} resizeMode="contain"/>
|
||||
+ !this.isCaptureRetakeMode() && (<TouchableOpacity style={{ }} onPress={() => this.onSwitchCameraPressed()}>
|
||||
+ <Image style={{ width: 40, height: 40, justifyContent: 'center' }} source={this.props.cameraFlipImage} resizeMode="contain"/>
|
||||
</TouchableOpacity>));
|
||||
}
|
||||
renderTopButtons() {
|
||||
\ No newline at end of file
|
||||
@@ -228,8 +228,8 @@
|
||||
flex: 1,
|
||||
flexDirection: 'row',
|
||||
justifyContent: 'space-between',
|
||||
- paddingTop: 8,
|
||||
- paddingBottom: 0,
|
||||
+ paddingTop:44,
|
||||
+ paddingHorizontal: 16,
|
||||
},
|
||||
cameraContainer: Object.assign({}, Platform.select({
|
||||
android: {
|
||||
\ No newline at end of file
|
Loading…
Reference in New Issue
Block a user