• 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'
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}