1/* 2 * Copyright (c) 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 fs from '@ohos.file.fs'; 18import common from '@ohos.app.ability.common'; 19import picker from '@ohos.file.picker'; 20import photoAccessHelper from '@ohos.file.photoAccessHelper'; 21import image from '@ohos.multimedia.image'; 22import Logger from '../utils/Logger'; 23import CommonConstants from '../common/constants/CommonConstants'; 24import { BusinessError } from '@ohos.base'; 25import { LooperUtil } from '../utils/LooperUtil'; 26 27const TAG = 'testTag_Notes: '; 28let context = getContext(this) as common.UIAbilityContext; //获取设备A&B UIAbilityContext信息 29let disPath: string = context.distributedFilesDir; 30//获取分布式目录的文件路径 31let filePath: string = disPath + '/picture.jpg'; 32 33@Entry 34@Component 35struct Notes { 36 @State text: string = ''; 37 @State img: string = 'app.media.pic_image'; 38 @StorageLink('isContinuation') isContinuation: boolean = false; 39 @StorageLink('isSelectImg') isSelectImg: boolean = false; 40 @StorageLink('inputText') inputText: string = ''; 41 @StorageLink('inputTextArea') inputTextArea: string = ''; 42 @StorageLink('imgStr') imgStr: string = 'app.media.pic_image'; 43 @StorageLink('ability_isON') stoLink_ability_isON: boolean = true; 44 controller: TextAreaController = new TextAreaController(); 45 @State num: number = 0; 46 @State image1: PixelMap | null = null; // 图一展示照片(选择或流转) 47 @State image2: Object = new Object; // 图二展示照片(选择或流转) 48 @StorageLink('imgIsVisible1') imgIsVisible1: boolean = true; 49 @StorageLink('imgIsVisible2') imgIsVisible2: boolean = true; 50 @State showSubMenus: boolean = false; 51 @State jpgStr: string = ''; 52 @State uris: Array<string> = []; // 用于接受 PhotoViewPicker选择图片1的返回的uri(分布式文件方式) 53 @State uris2: Array<string> = []; // 用于接受 PhotoViewPicker选择图片2的返回的uri(分布式文件方式) 54 @State fileName: string = ''; 55 @State readLenAtSink: number = 0; 56 private imgArray: Resource[] = [ 57 $r('app.media.pic_image'), 58 $r('app.media.img_08'), 59 $r('app.media.img_07') 60 ] 61 62 // 从图库中选择图片 63 async choosePictureFile() { 64 try { 65 let photoSelectOptions = new picker.PhotoSelectOptions(); 66 photoSelectOptions.MIMEType = picker.PhotoViewMIMETypes.IMAGE_TYPE; 67 photoSelectOptions.maxSelectNumber = 1; 68 let photoPicker = new picker.PhotoViewPicker(); 69 photoPicker.select(photoSelectOptions).then((photoSelectResult) => { 70 if (!photoSelectResult) { 71 Logger.error(TAG, 'photoSelectResult = null'); 72 return; 73 } 74 this.uris = photoSelectResult.photoUris; 75 76 if (this.writeSrcToDistributedFile(this.uris)) { 77 Logger.info(TAG, 'copy file success'); 78 // 成功写入图片后将标志设为true 79 AppStorage.set('isSelectImg', true); 80 this.isSelectImg = true; 81 Logger.info(TAG, "AppStorage.get<boolean>('isSelectImg') :" + AppStorage.get<boolean>('isSelectImg')); 82 } else { 83 Logger.error(TAG, 'copy file failed'); 84 } 85 86 // 获取图库中清晰图片PixelMap 87 // 1.通过uri打开文件得到fd 88 let file = fs.openSync(this.uris[0], fs.OpenMode.READ_ONLY); 89 Logger.info(TAG, 'file.fd:' + file.fd); 90 // 2.通过imageSourceApi来创建pixelMap并直接显示 91 let imageSourceApi = image.createImageSource(file.fd); 92 if (imageSourceApi) { 93 imageSourceApi.createPixelMap().then((pixelMap) => { 94 this.image1 = pixelMap; 95 this.imgIsVisible1 = false; 96 }); 97 } else { 98 Logger.info(TAG, 'imageSourceApi is undefined'); 99 } 100 }) 101 } catch (error) { 102 Logger.error(TAG, 'photoPicker failed with error: ' + JSON.stringify(error)); 103 } 104 } 105 106 writeSrcToDistributedFile(uris: string[]): boolean { 107 if (!uris.length) { 108 Logger.error(TAG, 'uri length is empty'); 109 return false; 110 } 111 Logger.info(TAG, 'uri length = ' + uris.length); 112 // 选中后先删除之前的文件 113 try { 114 let res = fs.accessSync(filePath); 115 if (res) { 116 fs.unlink(filePath); 117 Logger.info(TAG, ' delete file successfully'); 118 } else { 119 Logger.info(TAG, 'delete file failed'); 120 } 121 // 重新写文件 122 let srcFile = fs.openSync(uris[0], fs.OpenMode.READ_ONLY); 123 let dstFile = fs.openSync(filePath, fs.OpenMode.READ_WRITE | fs.OpenMode.CREATE); 124 Logger.info(TAG, 'open srcFile dstFile success!'); 125 const bufSize = 4096; 126 let readSize = 0; 127 let buf = new ArrayBuffer(bufSize); 128 let readLen = fs.readSync(srcFile.fd as number, buf, { offset: readSize }); 129 while (readLen > 0) { 130 readSize += readLen; 131 fs.writeSync(dstFile.fd as number, buf, { length: readLen }); 132 readLen = fs.readSync(srcFile.fd as number, buf, { offset: readSize }); 133 } 134 Logger.info(TAG, 'copy file success size: ' + readSize); 135 fs.close(srcFile); 136 fs.close(dstFile); 137 return true; 138 } catch (error) { 139 Logger.error(TAG, 'open file fail with err: ' + JSON.stringify(error)); 140 return false; 141 } 142 } 143 144 readFromDistributedFile(): void { 145 Logger.info(TAG, 'readFromDistributedFile: filePath: ' + filePath); 146 try { 147 Logger.info(TAG, 'readFromDistributedFile try to read'); 148 // 查看并打开分布式目录下的文件 149 if (fs.accessSync(filePath)) { 150 Logger.info(TAG, filePath + ' exists'); 151 } else { 152 Logger.error(TAG, filePath + 'not exists'); 153 return; 154 } 155 // 通过imageSourceApi来创建pixelMap 并负责流转后展示 156 let imageSourceApi = image.createImageSource(filePath); 157 imageSourceApi.createPixelMap().then((pixelMap) => { 158 this.image1 = pixelMap; 159 this.imgIsVisible1 = false; 160 }); 161 Logger.info(TAG, 'readFromDistributedFile is ready'); 162 } catch (error) { 163 Logger.error(TAG, 'readFromDistributedFile failed with error :' + JSON.stringify(error)); 164 } 165 } 166 167 choosePictureFileForImage2(): void { 168 try { 169 let photoSelectOptions = new picker.PhotoSelectOptions(); 170 photoSelectOptions.MIMEType = picker.PhotoViewMIMETypes.IMAGE_TYPE; 171 photoSelectOptions.maxSelectNumber = 1; 172 let photoPicker = new picker.PhotoViewPicker(); 173 photoPicker.select(photoSelectOptions).then((photoSelectResult) => { 174 if (!photoSelectResult) { 175 Logger.error(TAG, 'choosePicture2 photoSelectResult = null'); 176 return; 177 } 178 this.uris2 = photoSelectResult.photoUris; 179 180 if (this.writeDistributedAsset()) { 181 Logger.info(TAG, "choosePicture2 success"); 182 } else { 183 Logger.error(TAG, 'choosePicture2 failed'); 184 } 185 186 // 获取图库中清晰图片PixelMap 187 // 1.通过uri打开文件得到fd 188 let file = fs.openSync(this.uris2[0], fs.OpenMode.READ_ONLY); 189 // 2.通过imageSourceApi来创建pixelMap并直接显示 190 let imageSourceApi = image.createImageSource(file.fd); 191 if (imageSourceApi) { 192 imageSourceApi.createPixelMap().then((pixelMap) => { 193 this.image2 = pixelMap; 194 this.imgIsVisible2 = false; 195 }); 196 AppStorage.setOrCreate<string>('image2Path', CommonConstants.DISTRIBUTED_ASSET_SAVE_PATH); 197 } else { 198 Logger.info(TAG, 'choosePicture2 imageSourceApi is undefined'); 199 } 200 }) 201 } catch (error) { 202 Logger.error(TAG, 'choosePicture2 photoPicker failed with error: ' + JSON.stringify(error)); 203 } 204 } 205 206 writeDistributedAsset(): boolean { 207 Logger.info(TAG, 'writeDistributedAsset in'); 208 // 删除目录 209 try { 210 fs.rmdirSync(CommonConstants.DISTRIBUTED_ASSET_DIR); 211 Logger.info(TAG, 'remove dir successful'); 212 } catch (error) { 213 Logger.info(TAG, `remove dir failed, cause: ${JSON.stringify(error)}`); 214 } 215 try { 216 fs.mkdirSync(CommonConstants.DISTRIBUTED_ASSET_DIR, true); 217 let srcFile = fs.openSync(this.uris2[0], fs.OpenMode.READ_ONLY); 218 let dstFile = fs.openSync(CommonConstants.DISTRIBUTED_ASSET_SAVE_PATH, fs.OpenMode.READ_WRITE | fs.OpenMode.CREATE); 219 Logger.info(TAG, 'writeDistributedAsset open srcFile dstFile success!'); 220 const bufSize = 4096; 221 let readSize = 0; 222 let buf = new ArrayBuffer(bufSize); 223 let readLen = fs.readSync(srcFile.fd as number, buf, { offset: readSize }); 224 while (readLen > 0) { 225 readSize += readLen; 226 fs.writeSync(dstFile.fd as number, buf, { length: readLen }); 227 readLen = fs.readSync(srcFile.fd as number, buf, { offset: readSize }); 228 } 229 Logger.info(TAG, 'copy file success size: ' + readSize); 230 fs.close(srcFile); 231 fs.close(dstFile); 232 return true; 233 } catch (error) { 234 Logger.error(TAG, `writeDistributedAsset error, cause: ${JSON.stringify(error)}`); 235 } 236 return false; 237 } 238 239 readDistributedAsset(): void { 240 Logger.info(TAG, 'readDistributedAsset in'); 241 try { 242 let image2Path = AppStorage.get<string>('image2Path'); 243 if (image2Path == undefined || image2Path == '') { 244 Logger.info(TAG, `image2Path error, cause: $JSON.stringify(image2Path)`); 245 return; 246 } 247 if (fs.accessSync(image2Path)) { 248 Logger.info(TAG, 'open distributed asset successfully'); 249 } else { 250 Logger.error(TAG, 'distributed asset does not exists'); 251 return; 252 } 253 let imageSource = image.createImageSource(image2Path); 254 imageSource.createPixelMap().then((pixelMap) => { 255 Logger.info(TAG, 'createPixelMap successfully') 256 this.image2 = pixelMap; 257 this.imgIsVisible2 = false; 258 }) 259 Logger.info(TAG, 'readDistributedAsset successfully'); 260 } catch (error) { 261 Logger.error(TAG, `readDistributedAsset error, cause: ${JSON.stringify(error)}`); 262 } 263 } 264 265 aboutToAppear() { 266 Logger.info(TAG, 'aboutToAppear in'); 267 LooperUtil.on(CommonConstants.EVENT_DATA_RESTORE, () => { 268 this.readDistributedAsset(); 269 }); 270 if (this.isContinuation) { 271 if (this.isSelectImg) { 272 this.readFromDistributedFile(); 273 } 274 } 275 } 276 277 aboutToDisappear(): void { 278 Logger.info(TAG, 'aboutToDisappear in'); 279 LooperUtil.off(CommonConstants.EVENT_DATA_RESTORE); 280 AppStorage.set<string>('image2Path', ''); 281 this.isSelectImg = false; 282 this.imgIsVisible1 = true; 283 this.imgIsVisible2 = true; 284 } 285 286 build() { 287 Row() { 288 Column() { 289 Column() { 290 291 // 顶部导航 292 Flex({ direction: FlexDirection.Row }) { 293 Row() { 294 Image($r('app.media.ic_back')) 295 .width(24) 296 .height(24) 297 .margin({ right: 16 }) 298 .id('back_arrow_notes') 299 300 } 301 .width(30) 302 .height(30) 303 .onClick(() => { 304 router.back() 305 }) 306 307 Text(CommonConstants.NOTES_TITLE) 308 .fontSize(20) 309 .fontWeight(500) 310 .margin({ top: 5, left: 16 }) 311 312 } 313 .margin({ top: 36, left: 24, bottom: 14 }) 314 .width(360) 315 316 Column() { 317 318 TextInput({ text: this.inputText, 319 placeholder: CommonConstants.PLEASE_INPUT_NOTE_TITLE 320 }) 321 .fontWeight(500) 322 .fontSize(30) 323 .textAlign(TextAlign.JUSTIFY) 324 .align(Alignment.Center) 325 .height(51) 326 .onChange((value: string) => { 327 this.inputText = value; 328 AppStorage.set('inputText', value); 329 Logger.info(TAG, 'this.inputText: ' + this.inputText); 330 }) 331 .type(InputType.Normal) 332 .backgroundColor(Color.Transparent) 333 .margin({ left: -14 }) 334 .restoreId(2) 335 .id('textInput') 336 337 338 Text(CommonConstants.NOTES_TIME) 339 .fontWeight(400) 340 .fontSize(14) 341 .textAlign(TextAlign.JUSTIFY) 342 .fontColor('#182431') 343 .lineHeight(19) 344 345 TextArea({ 346 text: this.inputTextArea, 347 placeholder: CommonConstants.PLEASE_INPUT_NOTE_CONTENT, 348 controller: this.controller 349 }) 350 .placeholderFont({ size: 16, weight: 400 }) 351 .placeholderColor('#182431') 352 .width('100%') 353 .height(126) 354 .margin({ top: 16 }) 355 .fontSize(16) 356 .fontColor('#182431') 357 .backgroundColor('#FFFFFF') 358 .onChange((value: string) => { 359 this.inputTextArea = value; 360 AppStorage.set('inputTextArea', value); 361 }) 362 .restoreId(3) 363 .id('textArea') 364 365 366 // 分布式文件: 迁移图片1 367 Row() { 368 // 初始占位小图 369 Image(this.imgArray[0]) 370 .width(48) 371 .height(48) 372 .margin({ left: 144 }) 373 .visibility(this.imgIsVisible1 ? Visibility.Visible : Visibility.None) 374 //大图 375 Image(this.image1) 376 .width('100%') 377 .height('100%') 378 .borderRadius(24) 379 .syncLoad(true) 380 .onComplete((msg) => { 381 Logger.info(TAG, 'load image success 01'); 382 })// 图片获取失败打印结果 383 .onError(() => { 384 Logger.info(TAG, 'load image fail 01'); 385 }) 386 .visibility(this.imgIsVisible1 ? Visibility.None : Visibility.Visible) 387 } 388 .width('100%') 389 .height(154) 390 .backgroundColor('#FFFFFF') 391 .borderRadius(24) 392 .margin({ top: 12 }) 393 394 Text($r('app.string.PICTURE_ONE')) 395 .fontSize(12) 396 .fontColor('#182431') 397 .height(16) 398 .fontWeight(500) 399 .margin({ top: 8, left: 152 }) 400 // 分布式对象携带附件: 迁移图片2 401 Row() { 402 // 初始站位小图 403 Image(this.imgArray[0]) 404 .width(48) 405 .height(48) 406 .margin({ left: 144 }) 407 .visibility(this.imgIsVisible2 ? Visibility.Visible : Visibility.None) 408 // 大图 409 Image(this.image2 as PixelMap) 410 .width('100%') 411 .height('100%') 412 .borderRadius(24) 413 .syncLoad(true) 414 .onComplete((msg) => { 415 Logger.info(TAG, 'load image success 03'); 416 })// 图片获取失败打印结果 417 .onError(() => { 418 Logger.info(TAG, 'load image fail 03') 419 }) 420 .visibility(this.imgIsVisible2 ? Visibility.None : Visibility.Visible) 421 } 422 .width('100%') 423 .height(154) 424 .backgroundColor('#FFFFFF') 425 .borderRadius(24) 426 .margin({ top: 12 }) 427 428 Text($r('app.string.PICTURE_TWO')) 429 .fontSize(12) 430 .fontColor('#182431') 431 .height(16) 432 .fontWeight(500) 433 .margin({ top: 8, left: 152, bottom: 32 }) 434 435 Flex({ alignItems: ItemAlign.Center, justifyContent: FlexAlign.Center }) { 436 Button($r('app.string.pick_photo_one'), { type: ButtonType.Capsule, stateEffect: true }) 437 .backgroundColor('rgba(24,36,49,0.05)') 438 .width(150) 439 .height(40) 440 .fontColor('#007DFF') 441 .margin({ right: 12 }) 442 .onClick(() => { 443 this.choosePictureFile(); 444 }) 445 .id('button_select_picture_One') 446 447 448 Button($r('app.string.pick_photo_two'), { type: ButtonType.Capsule, stateEffect: true }) 449 .backgroundColor('rgba(24,36,49,0.05)') 450 .width(150) 451 .height(40) 452 .fontColor('#007DFF') 453 .onClick(() => { 454 this.choosePictureFileForImage2(); 455 }) 456 .id('button_select_picture_two') 457 458 } 459 } 460 .width(336) 461 .alignItems(HorizontalAlign.Start) 462 } 463 .width(360) 464 .alignItems(HorizontalAlign.Center) 465 } 466 .width('100%') 467 .height('100%') 468 } 469 .width('100%') 470 .height('100%') 471 .alignItems(VerticalAlign.Top) 472 .backgroundColor('#f1f3f5') 473 } 474}