1/* 2 * Copyright (c) 2022-2023 Huawei Device Co., Ltd. 3 * Licensed under the Apache License, Version 2.0 (the "License"); 4 * you may not use this file except in compliance with the License. 5 * You may obtain a copy of the License at 6 * 7 * http://www.apache.org/licenses/LICENSE-2.0 8 * 9 * Unless required by applicable law or agreed to in writing, software 10 * distributed under the License is distributed on an "AS IS" BASIS, 11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 * See the License for the specific language governing permissions and 13 * limitations under the License. 14 */ 15 16import router from '@ohos.router' 17import image from '@ohos.multimedia.image' 18import prompt from '@ohos.promptAction' 19import common from '@ohos.app.ability.common' 20import { CameraService } from '../CameraService' 21import { QRCodeScanConst, ImageAttribute, DecodeResultAttribute } from '../QRCodeScanConst'; 22import { QRCodeParser } from '../QRCodeParser' 23 24/** 25 * 二维码扫描组件 26 */ 27@Component 28export default struct QRCodeScanComponent { 29 private cameraService: CameraService = new CameraService() 30 private qrCodeParser: QRCodeParser = new QRCodeParser() 31 private xComponentController: XComponentController = new XComponentController() 32 @StorageProp('qrCodeParseResult') @Watch('showQrCodeResult') qrCodeParseResult: string = '' 33 @State qrCodeImage: image.PixelMap | undefined = undefined 34 @State animationOrdinate: number = QRCodeScanConst.SCAN_TO_TOP_HEIGHT 35 @State hasCameraPermission: boolean | undefined = false; 36 @State isQRCodeScanStopped: boolean = false 37 // 每当应用切换回来都重新打开相机 38 @StorageLink('cameraStatus') @Watch('reCreateCamera') isCameraOpened: boolean = false 39 @State surFaceId: string = '' 40 41 // 重新打开相机 42 async reCreateCamera() { 43 await this.cameraService.createCamera(this.surFaceId) 44 } 45 46 showQrCodeResult() { 47 prompt.showDialog({ 48 title: $r('app.string.qrcodeResult'), 49 message: this.qrCodeParseResult 50 }) 51 } 52 53 aboutToAppear() { 54 // 监听相机权限 55 this.watchCameraPermission() 56 // 设置扫描动画 57 this.setQRCodeScanAnimation() 58 // 解析二维码图片信息 59 this.qrCodeParser.parseQRCodeImageFromCamera(this.cameraService); 60 } 61 62 build() { 63 Column() { 64 Stack() { 65 if (this.hasCameraPermission) { 66 XComponent({ 67 id: 'componentId', 68 type: 'surface', 69 controller: this.xComponentController 70 }) 71 .onLoad(() => { 72 // 适配可能需要获取设备信息 73 this.xComponentController.setXComponentSurfaceSize({ 74 surfaceWidth: QRCodeScanConst.IMG_DEFAULT_SIZE_WIDTH, 75 surfaceHeight: QRCodeScanConst.IMG_DEFAULT_SIZE_HEIGHT 76 }) 77 this.surFaceId = this.xComponentController.getXComponentSurfaceId() 78 this.cameraService.createCamera(this.surFaceId) 79 }) 80 .onDestroy(() => { 81 this.cameraService.releaseCamera() 82 }) 83 .height('100%') 84 .width('100%') 85 } 86 Column() { 87 Column() { 88 Image($r('app.media.scan_border')) 89 .width('100%') 90 .height('100%') 91 .margin({ top: QRCodeScanConst.SCAN_TO_TOP_HEIGHT }) 92 .onAreaChange((oldValue: Area, newValue: Area) => { 93 this.animationOrdinate = (newValue.position.y as number) + 10 94 }) 95 96 Divider() 97 .strokeWidth(1) 98 .height(4) 99 .width('100%') 100 .color(Color.White) 101 .width('100%') 102 .position({ x: 0, y: 0 }) 103 .translate({ x: 0, y: this.animationOrdinate }) 104 } 105 .width(280) 106 .height(280) 107 108 Text($r('app.string.putTheQRCodeToScan')) 109 .fontSize(18) 110 .fontColor(Color.White) 111 .margin({ top: 24 }) 112 } 113 .width('100%') 114 .height('100%') 115 .margin({ right: 20, top: 20, left: 20 }) 116 .alignItems(HorizontalAlign.Center) 117 .justifyContent(FlexAlign.Start) 118 119 Row() { 120 Image($r('app.media.scan_back')) 121 .width(30) 122 .height(30) 123 .onClick(() => { 124 router.back() 125 }) 126 127 Row({ space: 16 }) { 128 Image($r('app.media.scan_photo')) 129 .width(30) 130 .height(30) 131 .id('scanPhoto') 132 .onClick(async () => { 133 // 打开相册获取图片 134 this.isQRCodeScanStopped = true 135 let context = AppStorage.Get('context') as common.UIAbilityContext 136 await context.startAbilityForResult({ 137 parameters: { uri: 'singleselect' }, 138 bundleName: 'com.ohos.photos', 139 abilityName: 'com.ohos.photos.MainAbility', 140 }).then(data => { 141 // 获取want数据 142 let want = data['want']; 143 if (want) { 144 // param代表want参数中的paramters 145 let param = want['parameters']; 146 if (param) { 147 // 被选中的图片路径media/image/8 148 let selectedUri = param['select-item-list']; 149 setTimeout(async () => { 150 if (!selectedUri) { 151 prompt.showToast({ 152 message: $r('app.string.queryImageFailed'), 153 duration: 1000 154 }) 155 setInterval(async () => { 156 // 监听相机权限 157 this.watchCameraPermission() 158 // 设置扫描动画 159 this.setQRCodeScanAnimation() 160 // 解析二维码图片信息 161 this.qrCodeParser.parseQRCodeImageFromCamera(this.cameraService); 162 }, 4000) 163 } 164 // 获取解析数据 165 let qrCodeParseRlt: DecodeResultAttribute = await this.qrCodeParser.parseImageQRCode((selectedUri as string[])[0]); 166 if (qrCodeParseRlt.isSucess) { 167 prompt.showDialog({ 168 title: $r('app.string.qrcodeResult'), 169 message: qrCodeParseRlt.decodeResult 170 }) 171 } else { 172 prompt.showToast({ 173 message: $r('app.string.qrCodeNotRecognized') 174 }) 175 } 176 }, 50) 177 } 178 } 179 }) 180 }) 181 182 Image($r('app.media.scan_more')) 183 .width(30) 184 .height(30) 185 .onClick(() => { 186 prompt.showToast({ 187 message: $r('app.string.notSupportCurrent'), 188 duration: 1000 189 }) 190 }) 191 } 192 } 193 .width('100%') 194 .height('100%') 195 .margin({ top: 24 }) 196 .padding({ left: 24, right: 24 }) 197 .alignItems(VerticalAlign.Top) 198 .justifyContent(FlexAlign.SpaceBetween) 199 200 Column() { 201 Image($r('app.media.scan_light')) 202 .width(48) 203 .height(48) 204 205 Text($r('app.string.lightByTouch')) 206 .fontSize(20) 207 .fontColor(Color.White) 208 .margin({ top: 4 }) 209 210 // 扫一扫 文本翻译 字体设置 211 NotSupportComponent() 212 }.margin({ top: 350 }) 213 } 214 .width('100%') 215 .height('100%') 216 .layoutWeight(1) 217 .backgroundColor(Color.Grey) 218 } 219 .height('100%') 220 .width('100%') 221 .backgroundColor(Color.White) 222 } 223 224 // 监听相机权限变化 225 watchCameraPermission() { 226 let interval = setInterval(() => { 227 this.hasCameraPermission = AppStorage.Get(QRCodeScanConst.HAS_CAMERA_PERMISSION) 228 if (this.hasCameraPermission) { 229 let qrCodeScanInterval = setInterval(() => { 230 if (this.qrCodeParseResult.length > 0 || this.isQRCodeScanStopped) { 231 clearInterval(qrCodeScanInterval) 232 } 233 // 拍照 234 this.cameraService.takePicture() 235 }, 4000) 236 clearInterval(interval) 237 } 238 }, 100) 239 } 240 241 // 扫描扫描动画 242 setQRCodeScanAnimation() { 243 setInterval(() => { 244 animateTo({ 245 duration: 1000, // 动画时间 246 tempo: 0.5, // 动画速率 247 curve: Curve.EaseInOut, 248 delay: 200, // 动画延迟时间 249 iterations: -1, // 动画是否重复播放 250 playMode: PlayMode.Normal, 251 }, () => { 252 this.animationOrdinate = 390 // 扫描动画结束Y坐标 253 }) 254 }, 2000) 255 } 256} 257 258@Component 259struct NotSupportComponent { 260 build() { 261 Row({ space: 32 }) { 262 Image($r('app.media.scan_trans')) 263 .width(30) 264 .height(30) 265 .onClick(() => { 266 prompt.showToast({ message: $r('app.string.notSupportCurrent'), duration: 1000 }) 267 }) 268 Image($r('app.media.scan_size')) 269 .width(30) 270 .height(30) 271 .onClick(() => { 272 prompt.showToast({ message: $r('app.string.notSupportCurrent'), duration: 1000 }) 273 }) 274 } 275 .margin({ top: 30, left: 20 }) 276 } 277}