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