• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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';
23import Logger from '../../utils/Logger';
24import { BusinessError } from '@ohos.base';
25import abilityAccessCtrl, { Permissions } from '@ohos.abilityAccessCtrl';
26import photoAccessHelper from '@ohos.file.photoAccessHelper';
27
28const TAG: string = 'QRCodeScanComponent';
29let atManager: abilityAccessCtrl.AtManager = abilityAccessCtrl.createAtManager();
30const PERMISSIONS: Permissions[] = ['ohos.permission.CAMERA', 'ohos.permission.READ_IMAGEVIDEO',
31  'ohos.permission.WRITE_IMAGEVIDEO', 'ohos.permission.MEDIA_LOCATION'];
32
33/**
34 * 二维码扫描组件
35 */
36@Component
37export default struct QRCodeScanComponent {
38  private cameraService: CameraService = new CameraService();
39  private qrCodeParser: QRCodeParser = new QRCodeParser();
40  private xComponentController: XComponentController = new XComponentController();
41  private context: common.Context = getContext(this);
42  @StorageProp('qrCodeParseResult') @Watch('showQrCodeResult') qrCodeParseResult: string = '';
43  @State qrCodeImage: image.PixelMap | undefined = undefined;
44  @State animationOrdinate: number = QRCodeScanConst.SCAN_TO_TOP_HEIGHT;
45  @State hasCameraPermission: boolean | undefined = false;
46  @State isQRCodeScanStopped: boolean = false;
47  // 每当应用切换回来都重新打开相机
48  @StorageLink('cameraStatus') @Watch('reCreateCamera') isCameraOpened: boolean = false;
49  @State surFaceId: string = '';
50
51  // 重新打开相机
52  async reCreateCamera() {
53    await this.cameraService.createCamera(this.surFaceId);
54  }
55
56  showQrCodeResult() {
57    prompt.showDialog({
58      title: $r('app.string.qrcodeResult'),
59      message: this.qrCodeParseResult
60    })
61  }
62
63  // 获取权限
64  async requestPermissions() {
65    try {
66      await atManager.requestPermissionsFromUser(this.context, PERMISSIONS)
67        .then((data) => {
68          Logger.info(TAG, `data: ${JSON.stringify(data)}`);
69          // 如果权限列表中有-1,说明用户拒绝了授权
70          if (data.authResults[0] === 0) {
71            // 控制相机是否打开
72            AppStorage.setOrCreate(QRCodeScanConst.HAS_CAMERA_PERMISSION, true);
73            Logger.info(TAG, 'permissionRequestResult success');
74          } else {
75            Logger.info(TAG, 'permissionRequestResult failed');
76          }
77          Logger.info(TAG, `context:${JSON.stringify(this.context)}`);
78          AppStorage.setOrCreate('context', this.context);
79        })
80        .catch((err: BusinessError) => {
81          Logger.error(TAG, `requestPermissionsFromUser err code:${err.code},message:${err.message}`);
82        })
83    } catch (err) {
84      Logger.info(TAG, `catch err->${JSON.stringify(err)}`);
85    }
86  }
87
88  aboutToAppear() {
89    this.requestPermissions();
90    // 监听相机权限
91    this.watchCameraPermission();
92    // 设置扫描动画
93    this.setQRCodeScanAnimation();
94    // 解析二维码图片信息
95    this.qrCodeParser.parseQRCodeImageFromCamera(this.cameraService);
96  }
97
98  build() {
99    Column() {
100      Stack() {
101        if (this.hasCameraPermission) {
102          XComponent({
103            id: 'componentId',
104            type: 'surface',
105            controller: this.xComponentController
106          })
107            .onLoad(() => {
108              // 适配可能需要获取设备信息
109              this.xComponentController.setXComponentSurfaceSize({
110                surfaceWidth: QRCodeScanConst.IMG_DEFAULT_SIZE_WIDTH,
111                surfaceHeight: QRCodeScanConst.IMG_DEFAULT_SIZE_HEIGHT
112              })
113              this.surFaceId = this.xComponentController.getXComponentSurfaceId()
114              this.cameraService.createCamera(this.surFaceId)
115            })
116            .onDestroy(() => {
117              this.cameraService.releaseCamera()
118            })
119            .height('100%')
120            .width('100%')
121        }
122        Column() {
123          Column() {
124            Image($r('app.media.scan_border'))
125              .width('100%')
126              .height('100%')
127              .margin({ top: QRCodeScanConst.SCAN_TO_TOP_HEIGHT })
128              .onAreaChange((oldValue: Area, newValue: Area) => {
129                this.animationOrdinate = (newValue.position.y as number) + 10
130              })
131
132            Divider()
133              .strokeWidth(1)
134              .height(4)
135              .width('100%')
136              .color(Color.White)
137              .width('100%')
138              .position({ x: 0, y: 0 })
139              .translate({ x: 0, y: this.animationOrdinate })
140          }
141          .width(280)
142          .height(280)
143
144          Text($r('app.string.putTheQRCodeToScan'))
145            .fontSize(18)
146            .fontColor(Color.White)
147            .margin({ top: 24 })
148        }
149        .width('100%')
150        .height('100%')
151        .margin({ right: 20, top: 20, left: 20 })
152        .alignItems(HorizontalAlign.Center)
153        .justifyContent(FlexAlign.Start)
154
155        Row() {
156          Image($r('app.media.scan_back'))
157            .width(30)
158            .height(30)
159            .onClick(() => {
160              router.back()
161            })
162
163          Row({ space: 16 }) {
164            Image($r('app.media.scan_photo'))
165              .width(30)
166              .height(30)
167              .id('scanPhoto')
168              .onClick(async () => {
169                // 打开相册获取图片
170                this.isQRCodeScanStopped = true
171                let photoSelectOptions = new photoAccessHelper.PhotoSelectOptions();
172                photoSelectOptions.MIMEType = photoAccessHelper.PhotoViewMIMETypes.IMAGE_TYPE;
173                photoSelectOptions.maxSelectNumber = 1; // 最多只允许选择一张图片
174                let photoPicker = new photoAccessHelper.PhotoViewPicker();
175                photoPicker.select(photoSelectOptions).then((data: photoAccessHelper.PhotoSelectResult) => {
176                      // 被选中的图片路径media/image/8
177                      let selectedUri = data.photoUris[0];
178                      setTimeout(async () => {
179                        if (!selectedUri) {
180                          prompt.showToast({
181                            message: $r('app.string.queryImageFailed'),
182                            duration: 1000
183                          })
184                          setInterval(async () => {
185                            // 监听相机权限
186                            this.watchCameraPermission()
187                            // 设置扫描动画
188                            this.setQRCodeScanAnimation()
189                            // 解析二维码图片信息
190                            this.qrCodeParser.parseQRCodeImageFromCamera(this.cameraService);
191                          }, 4000)
192                        }
193                        // 获取解析数据
194                        let qrCodeParseRlt: DecodeResultAttribute = await this.qrCodeParser.parseImageQRCode(selectedUri);
195                        if (qrCodeParseRlt.isSucess) {
196                          prompt.showDialog({
197                            title: $r('app.string.qrcodeResult'),
198                            message: qrCodeParseRlt.decodeResult
199                          })
200                        } else {
201                          prompt.showToast({
202                            message: $r('app.string.qrCodeNotRecognized')
203                          })
204                        }
205                      }, 50)
206                })
207              })
208
209            Image($r('app.media.scan_more'))
210              .width(30)
211              .height(30)
212              .onClick(() => {
213                prompt.showToast({
214                  message: $r('app.string.notSupportCurrent'),
215                  duration: 1000
216                })
217              })
218          }
219        }
220        .width('100%')
221        .height('100%')
222        .margin({ top: 24 })
223        .padding({ left: 24, right: 24 })
224        .alignItems(VerticalAlign.Top)
225        .justifyContent(FlexAlign.SpaceBetween)
226
227        Column() {
228          Image($r('app.media.scan_light'))
229            .width(48)
230            .height(48)
231
232          Text($r('app.string.lightByTouch'))
233            .fontSize(20)
234            .fontColor(Color.White)
235            .margin({ top: 4 })
236
237          // 扫一扫 文本翻译 字体设置
238          NotSupportComponent()
239        }.margin({ top: 350 })
240      }
241      .width('100%')
242      .height('100%')
243      .layoutWeight(1)
244      .backgroundColor(Color.Grey)
245    }
246    .height('100%')
247    .width('100%')
248    .backgroundColor(Color.White)
249  }
250
251  // 监听相机权限变化
252  watchCameraPermission() {
253    let interval = setInterval(() => {
254      this.hasCameraPermission = AppStorage.Get(QRCodeScanConst.HAS_CAMERA_PERMISSION)
255      if (this.hasCameraPermission) {
256        let qrCodeScanInterval = setInterval(() => {
257          if (this.qrCodeParseResult.length > 0 || this.isQRCodeScanStopped) {
258            clearInterval(qrCodeScanInterval)
259          }
260          // 拍照
261          this.cameraService.takePicture()
262        }, 4000)
263        clearInterval(interval)
264      }
265    }, 100)
266  }
267
268  // 扫描扫描动画
269  setQRCodeScanAnimation() {
270    setInterval(() => {
271      animateTo({
272        duration: 1000, // 动画时间
273        tempo: 0.5, // 动画速率
274        curve: Curve.EaseInOut,
275        delay: 200, // 动画延迟时间
276        iterations: -1, // 动画是否重复播放
277        playMode: PlayMode.Normal,
278      }, () => {
279        this.animationOrdinate = 390 // 扫描动画结束Y坐标
280      })
281    }, 2000)
282  }
283}
284
285@Component
286struct NotSupportComponent {
287  build() {
288    Row({ space: 32 }) {
289      Image($r('app.media.scan_trans'))
290        .width(30)
291        .height(30)
292        .onClick(() => {
293          prompt.showToast({ message: $r('app.string.notSupportCurrent'), duration: 1000 })
294        })
295      Image($r('app.media.scan_size'))
296        .width(30)
297        .height(30)
298        .onClick(() => {
299          prompt.showToast({ message: $r('app.string.notSupportCurrent'), duration: 1000 })
300        })
301    }
302    .margin({ top: 30, left: 20 })
303  }
304}