wudebin 1 miesiąc temu
rodzic
commit
6f5b57d1c8

+ 5 - 5
Strides-SPAPP/android/build.gradle

@@ -1,11 +1,11 @@
 // Top-level build file where you can add configuration options common to all sub-projects/modules.
 buildscript {
     ext {
-        versionName = "4.2.0" //★★★★★版本号★★★★★
-        buildToolsVersion = "35.0.1"
+        versionName = "4.2.1" //★★★★★版本号★★★★★
+        buildToolsVersion = "36.1.0"
         minSdkVersion = 24
-        compileSdkVersion = 35
-        targetSdkVersion = 35
+        compileSdkVersion = 36
+        targetSdkVersion = 36
         kotlinVersion = "2.0.21"
         playServicesVersion = "17.0.0"
         firebaseMessagingVersion = "21.1.0"
@@ -21,7 +21,7 @@ buildscript {
         mavenCentral()
     }
     dependencies {
-        classpath("com.android.tools.build:gradle")
+        classpath("com.android.tools.build:gradle:8.9.1")
         classpath("com.facebook.react:react-native-gradle-plugin")
         classpath("com.google.gms:google-services:4.3.15")
         classpath("com.google.firebase:firebase-crashlytics-gradle:2.9.9")

+ 2 - 1
Strides-SPAPP/android/gradle.properties

@@ -10,7 +10,8 @@
 # Specifies the JVM arguments used for the daemon process.
 # The setting is particularly useful for tweaking memory settings.
 # Default value: -Xmx512m -XX:MaxMetaspaceSize=256m
-org.gradle.jvmargs=-Xmx5120m -XX:MaxMetaspaceSize=512m
+# org.gradle.jvmargs=-Xmx5120m -XX:MaxMetaspaceSize=512m
+org.gradle.jvmargs=-Xmx4g -XX:MaxMetaspaceSize=1g -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8
 
 # When configured, Gradle will run in incubating parallel mode.
 # This option should only be used with decoupled projects. More details, visit

+ 2 - 2
Strides-SPAPP/app/pages/charge/QRResult.js

@@ -36,9 +36,9 @@ export default {
           chargeBoxId: bid,
           connectorId: cid
         }
-        console.log('====================================');
+        console.log('================QR==================');
         console.log(qr);
-        console.log('====================================');
+        console.log('================QR==================');
         Dialog.showProgressDialog();
         apiCharge.checkQRStatus(qr).then(res => {
           Dialog.dismissLoading();

+ 57 - 94
Strides-SPAPP/app/pages/charge/QRScan.js

@@ -4,82 +4,21 @@
  * 升级到 react-native-vision-camera
  */
 /* eslint-disable no-undef */
-import React, { Component, useEffect, useState } from 'react'
-import { Pressable, StyleSheet, View, Vibration } from 'react-native'
-import { Camera, useCameraDevice, useCodeScanner } from 'react-native-vision-camera';
+import React, { Component } from 'react'
+import { StyleSheet, View, Vibration, AppState } from 'react-native'
 import apiCharge from '../../api/apiCharge';
 import { PageList } from '../Router';
 import Dialog from '../../components/Dialog';
 import app from '../../../app.json';
 import Button from '../../components/Button';
-import TextView from '../../components/TextView';
 import { EnterStationDialog } from '../chargeV2/Charging';
 import QRResult from './QRResult';
 import utils from '../../utils/utils';
+import QRScanner from './QRScanner';
 
-// 函数组件:QR码扫描器
-const QRScanner = ({ onCodeScanned, isActive }) => {
-  const [flashOn, switchFlash] = useState(false);
-  const [hasPermission, setHasPermission] = useState(false);
-  const device = useCameraDevice('back');
-  useEffect(() => {
-    console.log("相机设备", device);
-    utils.logEventTracking("scan_camera_devices", (utils.isNotEmpty(device) ? "OK" : "NULL"))
-    Camera.requestCameraPermission().then(res => {
-      console.log("相机权限请求", res);
-      utils.logEventTracking("scan_camera_permission", res)
-      setHasPermission(res == 'granted');
-    }).catch(err => {
-      console.warn("相机权限请求错误", err);
-      utils.logEventTracking("scan_camera_permission_error", err)
-    });
-  }, []);
-  const codeScanner = useCodeScanner({
-    codeTypes: ['qr'],
-    onCodeScanned: (codes) => {
-      if (codes?.length > 0 && isActive) {
-        const code = codes[0];
-        onCodeScanned(code.value || "");
-      }
-    },
-  });
-
-  return (
-    (device && hasPermission)
-    ? <>
-        <Camera
-          style={{width: $width, height: $vh(110)}}
-          device={device}
-          isActive={isActive}
-          codeScanner={codeScanner}
-          enableZoomGesture={true}
-          photo={false}
-          video={false}
-          audio={false}
-          resizeMode="cover"
-          torch={flashOn ? 'on' : 'off'}
-        />
-        { isActive &&
-          <Pressable
-            style={styles.flashLight}
-            onPress={() => switchFlash(!flashOn)}>
-            <MaterialIcons
-              name={flashOn ? "flashlight-on" : "flashlight-off"}
-              size={36}
-              color="#fff"/>
-          </Pressable>
-        }
-      </>
-    : <View style={styles.tipsScreen}>
-        <TextView style={styles.tipsText}>
-          Camera access has been denied. Please enable it in your device settings.
-        </TextView>
-      </View>
-  );
-};
+const showInputStationView = true;
 
 export default class QRScan extends Component {
-
   constructor(props) {
     super(props);
     this.state={
@@ -88,6 +27,8 @@ export default class QRScan extends Component {
       params: this.props.route.params,
       showInputStation: false
     }
+    this.scaning = false;
+    this.stateListener;
   }
 
   componentDidMount() {
@@ -99,7 +40,7 @@ export default class QRScan extends Component {
       }, 200);
     });
     this.props.navigation.addListener('beforeRemove', (e) => {
-      if (!this.state.isResult) {
+      if (!isIOS && !this.state.isResult) {
         e.preventDefault();
         this.setState({
           isResult: true
@@ -110,14 +51,47 @@ export default class QRScan extends Component {
         });
       }
     });
+    this.stateListener = AppState.addEventListener("change", state => {
+      console.log("state change", state);
+      if (state == 'active') {
+        if (this.state.isResult) {
+          this.setState({
+            isResult: false
+          });
+        }
+      } else {
+        this.setState({
+          isResult: true
+        });
+      }
+    });
     utils.logEventTracking("scan_qr_click")
   }
 
-  scanResult = (msg) => {
+  componentWillUnmount() {
+    if (this.stateListener) {
+      this.stateListener.remove();
+    }
+  }
+
+  scanResult = (codes) => {
+    if (this.scaning) {
+      return;
+    }
+    this.scaning = true;
+    Vibration.vibrate(100);
     this.setState({
       isResult: true
+    }, () => {
+      setTimeout(() => {
+        this.scaning = false;
+      }, 300);
+      const msg = codes[0]
+      this.getQrMessage(msg);
     });
-    Vibration.vibrate(100);
+  }
+
+  getQrMessage(msg) {
     utils.logEventTracking("scan_qr_result", msg)
     if (msg.indexOf('::') > 0) {
       const arr = msg.split('::');
@@ -150,6 +124,12 @@ export default class QRScan extends Component {
         this.setState({
           isResult: false
         });
+      },
+      onBackPress: btn => {
+        Dialog.dismissDialog();
+        this.setState({
+          isResult: false
+        });
       }
     });
   }
@@ -225,14 +205,16 @@ export default class QRScan extends Component {
     return (
       <View style={styles.container}>
         <QRScanner
-          onCodeScanned={this.scanResult}
+          onResult={this.scanResult}
           isActive={!this.state.isResult}
         />
-        <Button
-          style={styles.btnStationInput}
-          text={$t('charging.enterStationId')}
-          textColor={"rgba(255,255,255,.8)"}
-          onClick={() => this.onEnterStation(true)}/>
+        { showInputStationView &&
+          <Button
+            style={styles.btnStationInput}
+            text={$t('charging.enterStationId')}
+            textColor={"rgba(255,255,255,.8)"}
+            onClick={() => this.onEnterStation(true)}/>
+        }
         <EnterStationDialog
           visible={this.state.showInputStation}
           stationId={this.state.params?.id}
@@ -251,25 +233,6 @@ const styles = StyleSheet.create({
     backgroundColor: '#000',
     ...StyleSheet.absoluteFillObject
   },
-  tipsScreen: {
-    flex: 1,
-    padding: 48,
-    alignItems: "center",
-    justifyContent: "center",
-    backgroundColor: '#444',
-  },
-  tipsText: {
-    color: textLight,
-    fontSize: 14,
-    textAlign: "center"
-  },
-  flashLight: {
-    bottom: 120,
-    zIndex: 2,
-    opacity: 0.7,
-    padding: 8,
-    position: 'absolute'
-  },
   btnStationInput: {
     left: 16,
     right: 16,
@@ -277,5 +240,5 @@ const styles = StyleSheet.create({
     zIndex: 2,
     position: 'absolute',
     backgroundColor: "rgba(0,0,0,.4)"
-  },
+  }
 })

+ 135 - 0
Strides-SPAPP/app/pages/charge/QRScanner.js

@@ -0,0 +1,135 @@
+/**
+ * QR码扫描器
+ * @邠心vbe on 2026/04/21
+ */
+import React, { useEffect, useState } from 'react';
+import { Pressable, StyleSheet, View } from 'react-native';
+import TextView from '../../components/TextView';
+import { Camera, useCameraDevice, useCodeScanner } from 'react-native-vision-camera';
+import { check, PERMISSIONS, RESULTS } from 'react-native-permissions';
+import utils from '../../utils/utils';
+
+const checkPermission = (back) => {
+  if (isIOS) {
+    back(true);
+  } else {
+    check(PERMISSIONS.ANDROID.CAMERA).then(res => {
+      console.log('checkPermission', res);
+      switch (res) {
+        case RESULTS.DENIED://权限未被请求/被拒绝,但可以请求
+          back(false);
+          break;
+        case RESULTS.LIMITED://权限是有限的:有些操作是可能的
+          back(true);
+          break;
+        case RESULTS.GRANTED://许可被授予
+          back(true);
+          break;
+        case RESULTS.BLOCKED://权限被拒绝,不再可请求
+          back(false);
+          break;
+        default:
+          back(false);
+          break;
+      }
+    }).catch(err => {
+      back(false);
+    })
+  }
+}
+
+const QRScanner = ({ onResult, isActive }) => {
+  const [flashOn, switchFlash] = useState(false);
+  const [hasPermission, setHasPermission] = useState(false);
+  const [permissionStr, setPermissionStr] = useState("");
+  const device = useCameraDevice('back');
+  useEffect(() => {
+    console.log("相机设备", device);
+    checkPermission(result => {
+      setHasPermission(result);
+    })
+    Camera.requestCameraPermission().then(res => {
+      setPermissionStr(res);
+      utils.logEventTracking("scan_camera_permission", res)
+      if (!hasPermission) {
+        setHasPermission(res == 'granted');
+      }
+    }).catch(err => {
+      console.warn("相机权限请求错误", err);
+      utils.logEventTracking("scan_camera_permission_error", err)
+      setHasPermission(false);
+    });
+  }, []);
+  const codeScanner = useCodeScanner({
+    codeTypes: ['qr'],
+    onCodeScanned: (codes) => {
+      if (codes && codes.length > 0) {
+        onResult(codes)
+      }
+    },
+  });
+
+  if (!device || !hasPermission) {
+    return (
+      <View style={styles.tipsScreen}>
+        <TextView style={styles.tipsText}>
+          {!hasPermission ? "Camera access has been denied. Please enable it in your device settings.(E0)" : "Can not find camera device.(E1)"}
+        </TextView>
+      </View>
+    )
+  }
+  return (
+    <>
+      <Camera
+        style={{width: $width, height: $vh(110)}}
+        device={device}
+        isActive={isActive}
+        codeScanner={codeScanner}
+        enableZoomGesture={true}
+        photo={false}
+        video={false}
+        audio={false}
+        resizeMode="cover"
+        torch={flashOn ? 'on' : 'off'}
+      />
+      { isActive &&
+        <Pressable
+          style={styles.flashLight}
+          onPress={() => switchFlash(!flashOn)}>
+          <MaterialIcons
+            name={flashOn ? "flashlight-on" : "flashlight-off"}
+            size={36}
+            color="#fff"/>
+          <TextView style={{color: "#fff", fontSize: 12}}>{permissionStr}</TextView>
+        </Pressable>
+      }
+    </>
+  );
+};
+
+export default QRScanner;
+
+const styles = StyleSheet.create({
+  tipsScreen: {
+    top: 0,
+    left: 0,
+    right: 0,
+    bottom: 0,
+    position: 'absolute',
+    alignItems: "center",
+    justifyContent: "center",
+    backgroundColor: '#111',
+  },
+  tipsText: {
+    color: textLight,
+    fontSize: 14,
+    textAlign: "center"
+  },
+  flashLight: {
+    bottom: 120,
+    zIndex: 2,
+    opacity: 0.7,
+    padding: 8,
+    position: 'absolute'
+  }
+})

+ 1 - 1
Strides-SPAPP/package.json

@@ -79,7 +79,7 @@
     "react-native-swiper": "1.6.0",
     "react-native-tab-view": "4.0.5",
     "react-native-view-shot": "4.0.0",
-    "react-native-vision-camera": "4.7.3",
+    "react-native-vision-camera": "https://gitee.com/vbes/react-native-vision-camera.git",
     "react-native-webview": "13.15.0",
     "vbe-cluster-map": "https://gitee.com/vbes/vbe-cluster-map2.git"
   },