1/* 2 * Copyright (c) 2023 Hunan OpenValley Digital Industry Development 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 */ 15import { Permissions } from '@ohos.abilityAccessCtrl' 16import abilityAccessCtrl from '@ohos.abilityAccessCtrl' 17import router from '@ohos.router' 18import Logger from '../../utils/Logger' 19import CameraModel from '../../model/CameraModel' 20import User from '../data/User' 21import { BusinessError } from '@ohos.base' 22 23const TAG: string = '[CameraPage]' 24const PERMISSIONS: Array<Permissions> = ['ohos.permission.READ_MEDIA', 'ohos.permission.WRITE_MEDIA', 'ohos.permission.MEDIA_LOCATION', 'ohos.permission.MICROPHONE', 'ohos.permission.CAMERA'] 25 26@Entry 27@Component 28struct CameraPage { 29 // 底部特效模拟图片资源数组 30 private imageList: Array<Resource> = [$r('app.media.app_icon'), $r('app.media.app_icon'), $r('app.media.app_icon'), $r('app.media.app_icon'), $r('app.media.app_icon'), $r('app.media.app_icon')]; 31 // 文字资源数组 32 private textList: Array<Resource> = [$r('app.string.Word'), $r('app.string.Subsection'), $r('app.string.Video'), $r('app.string.Photo'), $r('app.string.Everyday'), $r('app.string.Live_streaming')]; 33 // 侧边图标资源数组 34 private sidebarList_1: Array<Resource> = [$r('app.media.app_icon'), $r('app.media.app_icon'), $r('app.media.app_icon'), $r('app.media.app_icon'), $r('app.media.app_icon'), $r('app.media.app_icon')]; 35 private sidebarList_2: Array<Resource> = [$r('app.media.app_icon'), $r('app.media.app_icon'), $r('app.media.app_icon'), $r('app.media.app_icon'), $r('app.media.app_icon'), $r('app.media.app_icon')]; 36 private xComponentController: XComponentController = new XComponentController(); 37 private textTimerController: TextTimerController = new TextTimerController(); 38 private cameraModel: CameraModel = new CameraModel(getContext(this)) 39 private scrollerHorText: Scroller = new Scroller(); 40 private scrollerHorImage: Scroller = new Scroller(); 41 private currentUser: User | null = null; // 当前用户信息 42 @State recordingStatus: number = 0; // 0:未录制 1:正在录制 2:结束录制 43 @State surfaceId: string = '-1'; 44 @State format: string = 'mm:ss'; 45 @State uploadFile: string = ''; 46 47 pageTransition() { 48 // 登录页面从底部滑入滑出 49 PageTransitionEnter({ type: RouteType.Push, duration: 200 }) 50 .slide(SlideEffect.Bottom) 51 PageTransitionExit({ type: RouteType.Pop, duration: 200 }) 52 .slide(SlideEffect.Bottom) 53 } 54 55 aboutToAppear() { 56 let atManager: abilityAccessCtrl.AtManager = abilityAccessCtrl.createAtManager() 57 try { 58 atManager.requestPermissionsFromUser(getContext(this), PERMISSIONS).then((data) => { 59 this.cameraModel.createCamera(this.surfaceId); 60 Logger.info(TAG, 'requestPermissionsFromUser success') 61 }).catch((err: BusinessError) => { 62 Logger.info(TAG, `requestPermissionsFromUser err: ${JSON.stringify(err)}`) 63 }) 64 } catch (err) { 65 Logger.info(TAG, `requestPermissionsFromUser catch err->${JSON.stringify(err)}`); 66 } 67 if (AppStorage.get("currentUser")) { 68 this.currentUser = AppStorage.get("currentUser")!; 69 } 70 } 71 72 onPageHide() { 73 Logger.info(TAG, 'page onPageHide'); 74 this.stopVideo(); 75 this.cameraModel.releaseCamera(); 76 } 77 78 onPageShow() { 79 Logger.info(TAG, 'page onPageHide'); 80 this.cameraModel.createCamera(this.surfaceId); 81 } 82 83 startVideo() { 84 Logger.info(TAG, 'page startVideo'); 85 this.recordingStatus = 1; 86 this.textTimerController.reset(); 87 this.textTimerController.start(); 88 this.cameraModel.startVideo(); 89 } 90 91 async stopVideo() { 92 Logger.info(TAG, 'page stopVideo'); 93 this.recordingStatus = 2; 94 this.textTimerController.pause(); 95 this.uploadFile = await this.cameraModel.stopVideo(); 96 Logger.info(TAG, `page stopVideo uploadFile = ${this.uploadFile}`); 97 } 98 99 build() { 100 Column() { 101 Stack() { 102 XComponent({ 103 id: 'xComponentId', 104 type: 'surface', 105 controller: this.xComponentController 106 }) 107 .onLoad(() => { 108 Logger.info(TAG, 'onLoad is called') 109 // 设置XComponent创建的曲面宽为640vp,高为480vp 110 this.xComponentController.setXComponentSurfaceSize({ surfaceWidth: 640, surfaceHeight: 480 }) 111 this.surfaceId = this.xComponentController.getXComponentSurfaceId() 112 Logger.info(TAG, `onLoad surfaceId: ${this.surfaceId}`) 113 this.cameraModel.createCamera(this.surfaceId) 114 }) 115 .height('100%') 116 .width('100%') 117 118 Column() { 119 Row() { 120 Row() { 121 Image($r('app.media.app_icon')) 122 .width(30) 123 .height(30) 124 .objectFit(ImageFit.Contain) 125 } 126 .width(50) 127 .height(50) 128 .justifyContent(FlexAlign.Center) 129 .onClick(e => { 130 router.back() 131 }) 132 .visibility(this.recordingStatus !== 1 ? Visibility.Visible : Visibility.None) // 正在录制时不显示 133 134 Row({ space: 8 }) { 135 Image($r('app.media.app_icon')) 136 .width(28) 137 .height(28) 138 .objectFit(ImageFit.Fill) 139 .borderRadius(14) 140 Text($r('app.string.Select_music')) 141 .textAlign(TextAlign.Center) 142 .fontColor($r('app.color.COLOR_FFFFFF')) 143 .fontSize(16) 144 .fontFamily($r('app.string.Font_family_regular')) 145 .borderRadius(14) 146 } 147 .width(132) 148 .height(48) 149 .borderRadius(12) 150 .backgroundColor($r('app.color.COLOR_669F9B9B')) 151 .justifyContent(FlexAlign.Center) 152 .visibility(this.recordingStatus !== 1 ? Visibility.Visible : Visibility.None) // 正在录制时不显示 153 154 Row() { 155 Image($r('app.media.app_icon')) 156 .width(26) 157 .height(26) 158 .objectFit(ImageFit.Contain) 159 .visibility(this.recordingStatus !== 2 ? Visibility.Visible : Visibility.None) // 非录制结束情况下显示 160 Image($r('app.media.app_icon')) 161 .width(26) 162 .height(26) 163 .objectFit(ImageFit.Contain) 164 .visibility(this.recordingStatus === 2 ? Visibility.Visible : Visibility.None) // 录制结束情况时显示 165 } 166 .width(42) 167 .height(42) 168 } 169 .width('100%') 170 .height('8%') 171 .justifyContent(this.recordingStatus !== 1 ? FlexAlign.SpaceBetween : FlexAlign.End) 172 173 Column({ space: 18 }) { 174 if (this.recordingStatus === 0) { 175 ForEach(this.sidebarList_1, (sidebar: Resource, index: number) => { 176 Image($r('app.media.app_icon')) 177 .width(28) 178 .height(28) 179 .objectFit(ImageFit.Contain) 180 if (index === 1) { 181 Divider() 182 .vertical(false) 183 .height(1) 184 .width(22) 185 .color($r('app.color.COLOR_FFFFFF')) 186 .margin({ right: 4 }) 187 } 188 }) 189 } else if (this.recordingStatus === 2) { 190 ForEach(this.sidebarList_2, (sidebar: Resource, index: number) => { 191 Image($r('app.media.app_icon')) 192 .width(28) 193 .height(28) 194 .objectFit(ImageFit.Contain) 195 if (index === 0) { 196 Divider() 197 .vertical(false) 198 .height(1) 199 .width(22) 200 .color($r('app.color.COLOR_FFFFFF')) 201 .margin({ right: 4 }) 202 } 203 if (index === 1) { 204 Text($r('app.string.Wen')) 205 .textAlign(TextAlign.Center) 206 .fontColor($r('app.color.COLOR_FFFFFF')) 207 .fontSize(22) 208 .fontFamily($r('app.string.Font_family_medium')) 209 .margin({ right: 4 }) 210 } 211 }) 212 } 213 } 214 .width('100%') 215 .height('50%') 216 .padding({ top: 4, right: 14 }) 217 .alignItems(HorizontalAlign.End) 218 .visibility(this.recordingStatus === 1 ? Visibility.None : Visibility.Visible) // 正在录制时不显示 219 220 Blank() 221 222 Column() { 223 Column() { 224 Column() { 225 TextTimer({ isCountDown: false, count: 60000, controller: this.textTimerController }) 226 .height('100%') 227 .fontSize(18) 228 .format(this.format) 229 .fontColor($r('app.color.COLOR_FFFFFF')) 230 } 231 .justifyContent(FlexAlign.Start) 232 .visibility(this.recordingStatus === 1 ? Visibility.Visible : Visibility.Hidden) 233 .width('100%') 234 .height('30%') 235 236 // 文字列表 237 Scroll(this.scrollerHorText) { 238 Row({ space: 42 }) { 239 ForEach(this.textList, (text: Resource, index: number) => { 240 Text(text) 241 .height('100%') 242 .textAlign(TextAlign.Center) 243 .fontColor(index === 2 ? $r('app.color.COLOR_EEC934') : $r('app.color.COLOR_FFFFFF')) 244 .fontSize(16) 245 .fontFamily($r('app.string.Font_family_medium')) 246 }) 247 } 248 .height('100%') 249 .justifyContent(FlexAlign.Start) 250 .alignItems(VerticalAlign.Bottom) 251 } 252 .width('70%') 253 .height('100%') 254 .scrollable(ScrollDirection.Horizontal) 255 .scrollBar(BarState.Off) 256 .visibility(this.recordingStatus === 0 ? Visibility.Visible : Visibility.Hidden) 257 } 258 .width('100%') 259 .height('15%') 260 .justifyContent(FlexAlign.Center) 261 262 Row() { 263 if (this.recordingStatus === 0) { 264 Column({ space: 6 }) { 265 Image($r('app.media.app_icon')) 266 .width(56) 267 .height(56) 268 .objectFit(ImageFit.Contain) 269 .borderRadius(12) 270 Text($r('app.string.Special')) 271 .textAlign(TextAlign.Center) 272 .fontColor($r('app.color.COLOR_FFFFFF')) 273 .fontSize(16) 274 .fontFamily($r('app.string.Font_family_medium')) 275 } 276 .width(64) 277 .height(64) 278 279 this.StartRecordComponent() 280 281 Column({ space: 6 }) { 282 Image($r('app.media.app_icon')) 283 .width(56) 284 .height(56) 285 .objectFit(ImageFit.Fill) 286 .borderRadius(12) 287 Text($r('app.string.Album')) 288 .textAlign(TextAlign.Center) 289 .fontColor($r('app.color.COLOR_FFFFFF')) 290 .fontSize(16) 291 .fontFamily($r('app.string.Font_family_medium')) 292 } 293 .width(64) 294 .height(64) 295 } else if (this.recordingStatus === 1) { 296 this.RecordingComponent() 297 } else { 298 this.PointComponent() 299 } 300 } 301 .width('100%') 302 .height('85%') 303 .justifyContent(FlexAlign.SpaceEvenly) 304 } 305 .width('100%') 306 .height('25%') 307 } 308 .width('100%') 309 .height('100%') 310 } 311 .width('100%') 312 .height('91%') 313 .borderRadius(12) 314 315 Row({ space: 12 }) { 316 if (this.recordingStatus === 0) { 317 Scroll(this.scrollerHorImage) { 318 Row({ space: 14 }) { 319 ForEach(this.imageList, (img: Resource) => { 320 Image($r('app.media.app_icon')) 321 .width(56) 322 .height(56) 323 .objectFit(ImageFit.Fill) 324 .borderRadius(10) 325 }) 326 } 327 .height('100%') 328 .justifyContent(FlexAlign.Start) 329 .alignItems(VerticalAlign.Bottom) 330 .margin({ bottom: 1 }) 331 } 332 .width('70%') 333 .height('100%') 334 .scrollable(ScrollDirection.Horizontal) 335 .scrollBar(BarState.Off) 336 } else if (this.recordingStatus === 1) { 337 338 } else if (this.recordingStatus === 2) { 339 Row() { 340 Image($r('app.media.app_icon')) 341 .width(28) 342 .height(28) 343 .objectFit(ImageFit.Fill) 344 } 345 .layoutWeight(1) 346 .height('80%') 347 .justifyContent(FlexAlign.Center) 348 .backgroundColor($r('app.color.COLOR_393939')) 349 .borderRadius(12) 350 351 Row({ space: 8 }) { 352 if (this.currentUser) { 353 Image($r('app.media.app_icon')) 354 .width(28) 355 .height(28) 356 .objectFit(ImageFit.Fill) 357 .borderRadius(14) 358 } 359 Text($r('app.string.Send_everyday')) 360 .textAlign(TextAlign.Center) 361 .fontColor($r('app.color.COLOR_FFFFFF')) 362 .fontSize(18) 363 .fontFamily($r('app.string.Font_family_regular')) 364 .borderRadius(14) 365 } 366 .layoutWeight(2) 367 .height('80%') 368 .justifyContent(FlexAlign.Center) 369 .backgroundColor($r('app.color.COLOR_393939')) 370 .borderRadius(12) 371 372 Row() { 373 Text($r('app.string.Next')) 374 .textAlign(TextAlign.Center) 375 .fontColor($r('app.color.COLOR_FFFFFF')) 376 .fontSize(18) 377 .fontFamily($r('app.string.Font_family_regular')) 378 .borderRadius(14) 379 } 380 .id('next') 381 .layoutWeight(2) 382 .height('80%') 383 .justifyContent(FlexAlign.Center) 384 .backgroundColor($r('app.color.COLOR_FC2B55')) 385 .borderRadius(12) 386 .onClick(e => { 387 this.recordingStatus = 0; 388 router.pushUrl({ 389 url: 'appsampled/pages/PublishPage', 390 params: { 391 uploadFile: this.uploadFile 392 } 393 }) 394 }) 395 } 396 397 } 398 .width('100%') 399 .height('9%') 400 .justifyContent(FlexAlign.Center) 401 .padding({ left: 12, right: 12 }) 402 } 403 .width('100%') 404 .height('100%') 405 .backgroundColor($r('app.color.COLOR_000000')) 406 } 407 408 @Builder 409 StartRecordComponent() { 410 Column() { 411 Column() 412 .width(80) 413 .height(80) 414 .backgroundColor(Color.Red) 415 .borderRadius(40) 416 } 417 .id('startVideo') 418 .width(100) 419 .height(100) 420 .border({ width: 5, color: $r('app.color.COLOR_FFFFFF'), radius: 50 }) 421 .alignItems(HorizontalAlign.Center) 422 .justifyContent(FlexAlign.Center) 423 .onClick(e => { 424 this.startVideo(); 425 }) 426 } 427 428 @Builder 429 RecordingComponent() { 430 Stack() { 431 Column() 432 .width(60) 433 .height(60) 434 .borderRadius(30) 435 .backgroundColor($r('app.color.COLOR_E6FFFFFF')) 436 Column() 437 .width(20) 438 .height(20) 439 .borderRadius(4) 440 .backgroundColor(Color.Red) 441 } 442 .id('stopVideo') 443 .width(120) 444 .height(120) 445 .borderRadius(60) 446 .backgroundColor($r('app.color.COLOR_80FFFFFF')) 447 .onClick(e => { 448 this.stopVideo(); 449 }) 450 } 451 452 @Builder 453 PointComponent() { 454 Row({ space: 8 }) { 455 Text() 456 .width(4) 457 .height(4) 458 .backgroundColor($r('app.color.COLOR_CCFFFFFF')) 459 .borderRadius(2) 460 Text() 461 .width(6) 462 .height(6) 463 .backgroundColor($r('app.color.COLOR_CCFFFFFF')) 464 .borderRadius(3) 465 Text() 466 .width(8) 467 .height(8) 468 .backgroundColor($r('app.color.COLOR_CCFFFFFF')) 469 .borderRadius(4) 470 471 Text() 472 .width(10) 473 .height(10) 474 .backgroundColor($r('app.color.COLOR_FFFFFF')) 475 .borderRadius(5) 476 477 Text() 478 .width(8) 479 .height(8) 480 .backgroundColor($r('app.color.COLOR_CCFFFFFF')) 481 .borderRadius(4) 482 Text() 483 .width(6) 484 .height(6) 485 .backgroundColor($r('app.color.COLOR_CCFFFFFF')) 486 .borderRadius(3) 487 Text() 488 .width(4) 489 .height(4) 490 .backgroundColor($r('app.color.COLOR_CCFFFFFF')) 491 .borderRadius(2) 492 } 493 .width(100) 494 .height(30) 495 .alignItems(VerticalAlign.Center) 496 .justifyContent(FlexAlign.Center) 497 } 498}