已合并
检视意见修改 #2
检视意见修改 #2
已合并
sfchu创建于 6月24日
19 个文件变更+303-388
MREADME.md+20-21
@@ -44,32 +44,31 @@
44## 工程目录44## 工程目录
45 45 
46```46```
47-├──entry/src/main/ets // 代码区47+├──entry/src/main/ets // 代码区
48│ ├──constants48│ ├──constants
49-│ │ └──CommonConstants.ets // 公共常量类49+│ │ └──CommonConstants.ets // 公共常量类
50│ ├──entryability50│ ├──entryability
51-│ │ └──EntryAbility.ets // 应用入口,处理接续逻辑51+│ │ └──EntryAbility.ets // 应用入口,处理接续逻辑
52│ ├──entrybackupability52│ ├──entrybackupability
53-│ │ └──EntryBackupAbility.ets // 应用备份能力53+│ │ └──EntryBackupAbility.ets // 应用备份能力
54│ ├──model54│ ├──model
55-│ │ └──CommModel.ets // 公共实体类55+│ │ └──CommModel.ets // 公共实体类
56│ ├──pages56│ ├──pages
57-│ │ └──Index.ets // 主页面57+│ │ └──Index.ets // 主页面
58│ ├──utils58│ ├──utils
59-│ │ ├──BreakpointSystem.ets // 断点适配工具类59+│ │ ├──DebounceUtil.ets // 防抖工具类
60-│ │ ├──DebounceUtil.ets // 防抖工具类60+│ │ ├──DistributedFileUtil.ets // 分布式文件处理工具类
61-│ │ ──FileUtil.ets // 文件选择工具类61+│ │ ──FileUtil.ets // 文件选择工具类
62-──viewModel62+│ └──WidthBreakpointType.ets // 断点宽度类型工具类
63-├──AddPicViewModel.ets // 添加图片业务逻辑63+│ ├──view
64-│ │ ├──ContinuationViewModel.ets // 应用接续业务逻辑64+│ │ ├──AddPicView.ets // 添加图片组件
65-│ │ ──ImageViewModel.ets // 图片处理业务逻辑65+│ │ ──MigrateDataWantParamView.ets // want.param迁移页面
66-──view66+│ ├──MigrateDataDistributedBaseView.ets // 分布式对象基础数据迁移页面
67- ├──AddPic.ets // 添加图片组67+├──MigrateDataDistributedMonoFileView.ets // 分布式对象单文迁移页面
68-──MigrateDataWantParamView.ets // want.param迁移页面68+ │ └──MigrateDataDistributedMultiFileView.ets // 分布式对象多文件迁移页面
69-──MigrateDataDistributedBaseView.ets // 分布式对象基础数据迁移页面69+──viewModel
70-──MigrateDataDistributedMonoFileView.ets // 分布式对象单文件迁移页面70+──AddPicViewModel.ets // 图片添加与处理业务逻辑
71-└──MigrateDataDistributedMultiFileView.ets // 分布式对象多文件迁移页面71+└──entry/src/main/resources // 应用资源目录
72-└──entry/src/main/resources // 应用资源目录
73```72```
74 73 
75## 相关权限74## 相关权限
@@ -82,7 +81,7 @@
82 81 
83## 约束与限制82## 约束与限制
84 83 
85-1. 本示例仅支持标准系统上运行,支持设备:机。84+1. 本示例仅支持标准系统上运行,支持设备:直板机。
86 85 
872. HarmonyOS系统:HarmonyOS 6.1.0 Release及以上。862. HarmonyOS系统:HarmonyOS 6.1.0 Release及以上。
88 87 
@@ -33,4 +33,5 @@ export default class CommonConstants {
33 'migrate_data_distributed_multi_file'33 'migrate_data_distributed_multi_file'
34 ];34 ];
35 public static readonly FULL_PERCENT: string = '100%';35 public static readonly FULL_PERCENT: string = '100%';
36+ public static readonly IMAGE_WIDTH_HEIGHT: number = 113;
36}37}
@@ -19,20 +19,24 @@ import { BusinessError } from '@kit.BasicServicesKit';
19import { hilog } from '@kit.PerformanceAnalysisKit';19import { hilog } from '@kit.PerformanceAnalysisKit';
20import { window } from '@kit.ArkUI';20import { window } from '@kit.ArkUI';
21import { ContinuationData, ImageInfo } from '../model/CommModel';21import { ContinuationData, ImageInfo } from '../model/CommModel';
22-import { ContinuationViewModel } from '../viewModel/ContinuationViewModel';22+import { DistributedFileUtil } from '../utils/DistributedFileUtil';
23 23 
24const DOMAIN = 0x0000;24const DOMAIN = 0x0000;
25const TAG = 'EntryAbility';25const TAG = 'EntryAbility';
26const FORMAT = '%{public}s';26const FORMAT = '%{public}s';
27 27 
28+// [Start WantParamSource]
29+// [Start WantParamFace]
30+// [Start DistributedBaseContinue]
28export default class EntryAbility extends UIAbility {31export default class EntryAbility extends UIAbility {
32+ // [StartExclude WantParamSource]
29 private distributedObject: distributedDataObject.DataObject | undefined = undefined;33 private distributedObject: distributedDataObject.DataObject | undefined = undefined;
30 private storage: LocalStorage = new LocalStorage();34 private storage: LocalStorage = new LocalStorage();
31- private continuationViewModel: ContinuationViewModel | undefined = undefined;35+ private distributedFileUtil: DistributedFileUtil | undefined = undefined;
32- 36+ // [StartExclude DistributedBaseContinue]
33 onCreate(want: Want, launchParam: AbilityConstant.LaunchParam): void {37 onCreate(want: Want, launchParam: AbilityConstant.LaunchParam): void {
34 hilog.info(DOMAIN, TAG, FORMAT, `Ability onCreate`);38 hilog.info(DOMAIN, TAG, FORMAT, `Ability onCreate`);
35- this.continuationViewModel = new ContinuationViewModel(this.context);39+ this.distributedFileUtil = new DistributedFileUtil(this.context);
36 this.appContinuity(want, launchParam);40 this.appContinuity(want, launchParam);
37 }41 }
38 42 
@@ -44,11 +48,10 @@ export default class EntryAbility extends UIAbility {
44 appContinuity(want: Want, launchParam: AbilityConstant.LaunchParam) {48 appContinuity(want: Want, launchParam: AbilityConstant.LaunchParam) {
45 if (launchParam.launchReason === AbilityConstant.LaunchReason.CONTINUATION) {49 if (launchParam.launchReason === AbilityConstant.LaunchReason.CONTINUATION) {
46 if (want.parameters !== undefined) {50 if (want.parameters !== undefined) {
47- // Single data51+ // Use want.param to migrate the simple data page data
48- AppStorage.setOrCreate('basicDataStr', want.parameters.basicDataStr);52+ AppStorage.setOrCreate('wantBasicData', want.parameters.wantBasicData);
49- AppStorage.setOrCreate('basicDataNum', want.parameters.basicDataNum);
50- AppStorage.setOrCreate('wantBasicData', want.parameters.WantBasic);
51 }53 }
54+ // [StartExclude WantParamFace]
52 let mailInfo: ContinuationData = new ContinuationData(undefined, undefined, undefined, undefined, undefined,55 let mailInfo: ContinuationData = new ContinuationData(undefined, undefined, undefined, undefined, undefined,
53 undefined, undefined, undefined, undefined, undefined);56 undefined, undefined, undefined, undefined, undefined);
54 this.distributedObject = distributedDataObject.create(this.context, mailInfo);57 this.distributedObject = distributedDataObject.create(this.context, mailInfo);
@@ -56,35 +59,33 @@ export default class EntryAbility extends UIAbility {
56 try {59 try {
57 this.distributedObject.on('status',60 this.distributedObject.on('status',
58 (_sessionId: string, _networkId: string, status: 'online' | 'offline' | 'restored') => {61 (_sessionId: string, _networkId: string, status: 'online' | 'offline' | 'restored') => {
59- hilog.info(DOMAIN, TAG, FORMAT, `status changed, sessionId: ****`);
60- hilog.info(DOMAIN, TAG, FORMAT, `status changed, status: ${status}`);
61- hilog.info(DOMAIN, TAG, FORMAT, `status changed, networkId: ****`);
62 if (status === 'restored') {62 if (status === 'restored') {
63- if (!this.distributedObject || !this.continuationViewModel) {63+ if (!this.distributedObject || !this.distributedFileUtil) {
64 return;64 return;
65 }65 }
66+ // Use the distributed object migration to transfer the basic page data
66 AppStorage.setOrCreate('basicDataStr', this.distributedObject['basicDataStr']);67 AppStorage.setOrCreate('basicDataStr', this.distributedObject['basicDataStr']);
67 AppStorage.setOrCreate('basicDataNum', this.distributedObject['basicDataNum']);68 AppStorage.setOrCreate('basicDataNum', this.distributedObject['basicDataNum']);
68 AppStorage.setOrCreate('distributedData', this.distributedObject['distributedData']);69 AppStorage.setOrCreate('distributedData', this.distributedObject['distributedData']);
69- 70+ // Migrate page data using distributed object single-file migration
70 AppStorage.setOrCreate('monoFileStr', this.distributedObject['monoFileStr']);71 AppStorage.setOrCreate('monoFileStr', this.distributedObject['monoFileStr']);
71 AppStorage.setOrCreate('monoFileNum', this.distributedObject['monoFileNum']);72 AppStorage.setOrCreate('monoFileNum', this.distributedObject['monoFileNum']);
72 AppStorage.setOrCreate('attachment', this.distributedObject['attachment']);73 AppStorage.setOrCreate('attachment', this.distributedObject['attachment']);
73 let attachment = this.distributedObject['attachment'] as commonType.Assets;74 let attachment = this.distributedObject['attachment'] as commonType.Assets;
74 for (const attachments of attachment) {75 for (const attachments of attachment) {
75- this.continuationViewModel.fileCopy(attachments, true);76+ this.distributedFileUtil.fileCopy(attachments, true);
76 }77 }
77- AppStorage.setOrCreate<ImageInfo>('imageUri', this.continuationViewModel.imageUri);78+ AppStorage.setOrCreate<ImageInfo>('imageUri', this.distributedFileUtil.imageUri);
78- 79+ // Use the distributed object multi-file migration page data function
79 AppStorage.setOrCreate('multiFileStr', this.distributedObject['multiFileStr']);80 AppStorage.setOrCreate('multiFileStr', this.distributedObject['multiFileStr']);
80 AppStorage.setOrCreate('multiFileNum', this.distributedObject['multiFileNum']);81 AppStorage.setOrCreate('multiFileNum', this.distributedObject['multiFileNum']);
81 AppStorage.setOrCreate('attachments', this.distributedObject['attachments']);82 AppStorage.setOrCreate('attachments', this.distributedObject['attachments']);
82 let attachments = this.distributedObject['attachments'] as commonType.Assets;83 let attachments = this.distributedObject['attachments'] as commonType.Assets;
83 for (const attachment of attachments) {84 for (const attachment of attachments) {
84- this.continuationViewModel.fileCopy(attachment);85+ this.distributedFileUtil.fileCopy(attachment);
85 }86 }
86- AppStorage.setOrCreate<Array<ImageInfo>>('imageUriArray', this.continuationViewModel.imageUriArray);87+ AppStorage.setOrCreate<Array<ImageInfo>>('imageUriArray', this.distributedFileUtil.imageUriArray);
87- 88+ // The name of the page routing for migration
88 AppStorage.setOrCreate('curPageInfos', this.distributedObject['curPageInfos']);89 AppStorage.setOrCreate('curPageInfos', this.distributedObject['curPageInfos']);
89 }90 }
90 });91 });
@@ -95,48 +96,58 @@ export default class EntryAbility extends UIAbility {
95 this.distributedObject.setSessionId(sessionId).catch((err: BusinessError) => {96 this.distributedObject.setSessionId(sessionId).catch((err: BusinessError) => {
96 hilog.info(DOMAIN, TAG, FORMAT, `SetSessionId failed. Cause code: ${err.code}`);97 hilog.info(DOMAIN, TAG, FORMAT, `SetSessionId failed. Cause code: ${err.code}`);
97 });98 });
99+ // [EndExclude WantParamFace]
98 try {100 try {
99 // Restore window stage with new LocalStorage instance101 // Restore window stage with new LocalStorage instance
100 this.context.restoreWindowStage(this.storage);102 this.context.restoreWindowStage(this.storage);
101 } catch (err) {103 } catch (err) {
102- hilog.error(DOMAIN, TAG, FORMAT, `RestoreWindowStage failed. Cause: ${(err as BusinessError).code}`);104+ hilog.error(DOMAIN, TAG, FORMAT, `RestoreWindowStage failed. Cause : ${err}`);
103 }105 }
104 }106 }
105 }107 }
106- 108+ // [EndExclude DistributedBaseContinue]
109+ // [EndExclude WantParamSource]
110+ // [StartExclude WantParamFace]
107 async onContinue(wantParam: Record<string, Object | undefined>): Promise<AbilityConstant.OnContinueResult> {111 async onContinue(wantParam: Record<string, Object | undefined>): Promise<AbilityConstant.OnContinueResult> {
108- if (!this.continuationViewModel) {112+ // [StartExclude WantParamSource]
113+ if (!this.distributedFileUtil) {
109 return AbilityConstant.OnContinueResult.REJECT;114 return AbilityConstant.OnContinueResult.REJECT;
110 }115 }
116+ // [StartExclude DistributedBaseContinue]
117+ // [EndExclude WantParamSource]
111 // Migrate simple data using wantParam.param118 // Migrate simple data using wantParam.param
112- wantParam.WantBasic = AppStorage.get('wantBasicData');119+ wantParam.wantBasicData = AppStorage.get('wantBasicData');
113- // Migrate basic data using distributed object120+ // [StartExclude WantParamSource]
114- wantParam.distributedData = AppStorage.get('distributedData');121+ // [EndExclude DistributedBaseContinue]
115- // Migrate single file using distributed object
116- wantParam.imageUri = AppStorage.get('imageUri');
117- // Migrate multiple files using distributed object
118- wantParam.imageUriArray = AppStorage.get('imageUriArray');
119 let sessionId: string = distributedDataObject.genSessionId();122 let sessionId: string = distributedDataObject.genSessionId();
120 wantParam.distributedSessionId = sessionId;123 wantParam.distributedSessionId = sessionId;
121- let imageUri = AppStorage.get<ImageInfo>('imageUri');124+ // [StartExclude DistributedBaseContinue]
122- let asset = this.continuationViewModel.buildAssetsFromImageInfo(imageUri);125+ let imageUri: ImageInfo | undefined = AppStorage.get<ImageInfo>('imageUri');
123- 126+ if (imageUri !== undefined) {
124- let imageUriArray = AppStorage.get<Array<ImageInfo>>('imageUriArray');127+ this.distributedFileUtil.pixelMapToBuffer(imageUri);
125- let assets = this.continuationViewModel.buildAssetsFromImageArray(imageUriArray);128+ }
126- 129+ let asset = this.distributedFileUtil.buildAssetsFromImageInfo(imageUri);
130+ let imageUriArray: Array<ImageInfo> | undefined = AppStorage.get<Array<ImageInfo>>('imageUriArray');
131+ if (imageUriArray !== undefined ) {
132+ imageUriArray.forEach((item: ImageInfo) => {
133+ this.distributedFileUtil?.pixelMapToBuffer(item);
134+ });
135+ }
136+ let assets =
137+ this.distributedFileUtil.buildAssetsFromImageArray(AppStorage.get<Array<ImageInfo>>('imageUriArray'));
138+ // [EndExclude DistributedBaseContinue]
127 let contentInfo: ContinuationData = new ContinuationData(139 let contentInfo: ContinuationData = new ContinuationData(
128 AppStorage.get('basicDataStr'),140 AppStorage.get('basicDataStr'),
129 AppStorage.get('basicDataNum'),141 AppStorage.get('basicDataNum'),
130 AppStorage.get('distributedData'),142 AppStorage.get('distributedData'),
131- 143+ // [StartExclude DistributedBaseContinue]
132 AppStorage.get('monoFileStr'),144 AppStorage.get('monoFileStr'),
133 AppStorage.get('monoFileNum'),145 AppStorage.get('monoFileNum'),
134 asset,146 asset,
135- 
136 AppStorage.get('multiFileStr'),147 AppStorage.get('multiFileStr'),
137 AppStorage.get('multiFileNum'),148 AppStorage.get('multiFileNum'),
138 assets,149 assets,
139- 150+ // [EndExclude DistributedBaseContinue]
140 (AppStorage.get('pageInfos') as NavPathStack).getAllPathName()[0]151 (AppStorage.get('pageInfos') as NavPathStack).getAllPathName()[0]
141 );152 );
142 let source = contentInfo.flatAssets();153 let source = contentInfo.flatAssets();
@@ -147,10 +158,12 @@ export default class EntryAbility extends UIAbility {
147 await this.distributedObject.save(wantParam.targetDevice as string).catch((err: BusinessError) => {158 await this.distributedObject.save(wantParam.targetDevice as string).catch((err: BusinessError) => {
148 hilog.info(DOMAIN, TAG, FORMAT, `Failed to save. Code: ${err.code}`);159 hilog.info(DOMAIN, TAG, FORMAT, `Failed to save. Code: ${err.code}`);
149 });160 });
161+ // [EndExclude WantParamSource]
150 // Return AGREE to allow cross-device migration162 // Return AGREE to allow cross-device migration
151 return AbilityConstant.OnContinueResult.AGREE;163 return AbilityConstant.OnContinueResult.AGREE;
152 }164 }
153- 165+ // [StartExclude DistributedBaseContinue]
166+ // [StartExclude WantParamSource]
154 onDestroy(): void {167 onDestroy(): void {
155 hilog.info(DOMAIN, TAG, FORMAT, `Ability onDestroy`);168 hilog.info(DOMAIN, TAG, FORMAT, `Ability onDestroy`);
156 }169 }
@@ -182,4 +195,10 @@ export default class EntryAbility extends UIAbility {
182 // Ability has back to background195 // Ability has back to background
183 hilog.info(DOMAIN, TAG, FORMAT, `Ability onBackground`);196 hilog.info(DOMAIN, TAG, FORMAT, `Ability onBackground`);
184 }197 }
185-}198+ // [EndExclude DistributedBaseContinue]
199+ // [EndExclude WantParamFace]
200+ // [EndExclude WantParamSource]
201+}
202+// [End DistributedBaseContinue]
203+// [End WantParamFace]
204+// [End WantParamSource]
@@ -13,7 +13,7 @@
13 * limitations under the License.13 * limitations under the License.
14 */14 */
15 15 
16-import { BackupExtensionAbility, BundleVersion } from '@kit.CoreFileKit';16+import { BackupExtensionAbility } from '@kit.CoreFileKit';
17import { hilog } from '@kit.PerformanceAnalysisKit';17import { hilog } from '@kit.PerformanceAnalysisKit';
18 18 
19const DOMAIN = 0x0000;19const DOMAIN = 0x0000;
@@ -24,70 +24,48 @@ export interface ImageInfo {
24 imagePixelMap: PixelMap;24 imagePixelMap: PixelMap;
25 imageName: string;25 imageName: string;
26}26}
27- 27+// [Start DistributedBaseData]
28-export interface DistributedBaseData {
29- basicDataStr: string | undefined;
30- basicDataNum: number | undefined;
31- distributedData: DistributedData | undefined;
32-}
33- 
34-export interface DistributedUniFileData {
35- monoFileStr: string | undefined;
36- monoFileNum: number | undefined;
37- attachment: commonType.Assets | undefined;
38-}
39- 
40-export interface DistributedMultiFileData {
41- multiFileStr: string | undefined;
42- multiFileNum: number | undefined;
43- attachments: commonType.Assets | undefined;
44-}
45- 
46- 
47export class ContinuationData {28export class ContinuationData {
48- basicDataStr: string | undefined;29+ public basicDataStr: string | undefined;
49- basicDataNum: number | undefined;30+ public basicDataNum: number | undefined;
50- distributedData: DistributedData | undefined;31+ public distributedData: DistributedData | undefined;
51- 32+ // [StartExclude DistributedBaseData]
52- monoFileStr: string | undefined;33+ public monoFileStr: string | undefined;
53- monoFileNum: number | undefined;34+ public monoFileNum: number | undefined;
54- attachment: commonType.Assets | undefined;35+ public attachment: commonType.Assets | undefined;
55- 36+ public multiFileStr: string | undefined;
56- multiFileStr: string | undefined;37+ public multiFileNum: number | undefined;
57- multiFileNum: number | undefined;38+ public attachments: commonType.Assets | undefined;
58- attachments: commonType.Assets | undefined;39+ public curPageInfos: string | undefined;
59- 40+ // [EndExclude DistributedBaseData]
60- curPageInfos: string | undefined;
61 constructor(41 constructor(
62 basicDataStr: string | undefined,42 basicDataStr: string | undefined,
63 basicDataNum: number | undefined,43 basicDataNum: number | undefined,
64 distributedData: DistributedData | undefined,44 distributedData: DistributedData | undefined,
65- 45+ // [StartExclude DistributedBaseData]
66 monoFileStr: string | undefined,46 monoFileStr: string | undefined,
67 monoFileNum: number | undefined,47 monoFileNum: number | undefined,
68 attachment: commonType.Assets | undefined,48 attachment: commonType.Assets | undefined,
69- 
70 multiFileStr: string | undefined,49 multiFileStr: string | undefined,
71 multiFileNum: number | undefined,50 multiFileNum: number | undefined,
72 attachments: commonType.Assets | undefined,51 attachments: commonType.Assets | undefined,
73- 
74 curPageInfos: string | undefined52 curPageInfos: string | undefined
53+ // [EndExclude DistributedBaseData]
75 ) {54 ) {
76 this.basicDataStr = basicDataStr;55 this.basicDataStr = basicDataStr;
77 this.basicDataNum = basicDataNum;56 this.basicDataNum = basicDataNum;
78 this.distributedData = distributedData;57 this.distributedData = distributedData;
79- 58+ // [StartExclude DistributedBaseData]
80 this.monoFileStr = monoFileStr;59 this.monoFileStr = monoFileStr;
81 this.monoFileNum = monoFileNum;60 this.monoFileNum = monoFileNum;
82 this.attachment = attachment;61 this.attachment = attachment;
83- 
84 this.multiFileStr = multiFileStr;62 this.multiFileStr = multiFileStr;
85 this.multiFileNum = multiFileNum;63 this.multiFileNum = multiFileNum;
86 this.attachments = attachments;64 this.attachments = attachments;
87- 
88 this.curPageInfos = curPageInfos;65 this.curPageInfos = curPageInfos;
66+ // [EndExclude DistributedBaseData]
89 }67 }
90- 68+ // [EndExclude DistributedBaseData]
91 flatAssets(): object {69 flatAssets(): object {
92 let obj: object = this;70 let obj: object = this;
93 if (!this.attachments) {71 if (!this.attachments) {
@@ -99,4 +77,6 @@ export class ContinuationData {
99 }77 }
100 return obj;78 return obj;
101 }79 }
102-}80+ // [EndExclude DistributedBaseData]
81+}
82+// [End DistributedBaseData]
@@ -13,12 +13,15 @@
13* limitations under the License.13* limitations under the License.
14*/14*/
15 15 
16+import { uiObserver } from '@kit.ArkUI';
17+import { WidthBreakpointType } from '../utils/WidthBreakpointType';
16import CommonConstants from '../constants/CommonConstants';18import CommonConstants from '../constants/CommonConstants';
17 19 
18@Entry20@Entry
19@Component21@Component
20struct Index {22struct Index {
21 @StorageLink('pageInfos') pageInfos: NavPathStack = new NavPathStack();23 @StorageLink('pageInfos') pageInfos: NavPathStack = new NavPathStack();
24+ @Env(SystemProperties.BREAK_POINT) breakpoint: uiObserver.WindowSizeLayoutBreakpointInfo;
22 25 
23 aboutToAppear(): void {26 aboutToAppear(): void {
24 if (AppStorage.get('curPageInfos')) {27 if (AppStorage.get('curPageInfos')) {
@@ -47,6 +50,8 @@ struct Index {
47 .padding(16)50 .padding(16)
48 .justifyContent(FlexAlign.End)51 .justifyContent(FlexAlign.End)
49 }52 }
53+ .mode(new WidthBreakpointType(NavigationMode.Stack, NavigationMode.Stack, NavigationMode.Auto,
54+ NavigationMode.Auto).getValue(this.breakpoint.widthBreakpoint))
50 .title($r('app.string.EntryAbility_label'))55 .title($r('app.string.EntryAbility_label'))
51 .height(CommonConstants.FULL_PERCENT)56 .height(CommonConstants.FULL_PERCENT)
52 .width(CommonConstants.FULL_PERCENT)57 .width(CommonConstants.FULL_PERCENT)
@@ -1,92 +0,0 @@
1-/*
2- * Copyright (c) 2026 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- 
16-import { hilog } from '@kit.PerformanceAnalysisKit';
17-import { display, window } from '@kit.ArkUI';
18- 
19-const DOMAIN = 0x0000;
20-const TAG = 'BreakpointSystem';
21-const FORMAT = '%{public}s';
22- 
23-/**
24- * Breakpoint type generic class
25- * Used to store and return values of corresponding types based on different screen breakpoint sizes
26- * Supports four breakpoint types: xs (ultra-small screen), sm (small screen), md (medium screen), lg (large screen)
27- */
28-export class BreakpointType<T> {
29- private xs: T;
30- private sm: T;
31- private md: T;
32- private lg: T;
33- 
34- constructor(xs: T, sm: T, md: T, lg: T) {
35- this.xs = xs;
36- this.sm = sm;
37- this.md = md;
38- this.lg = lg;
39- }
40- 
41- /**
42- * Obtain the corresponding value based on the current breakpoint type
43- */
44- getValue(currentBreakpoint: string): T {
45- const validBreakpoints = ['xs', 'sm', 'md', 'lg'];
46- // Check whether the incoming breakpoint type is valid
47- if (!validBreakpoints.includes(currentBreakpoint)) {
48- return this.xs;
49- }
50- // Return the corresponding value based on the breakpoint type
51- switch (currentBreakpoint) {
52- case 'xs':
53- return this.xs;
54- case 'sm':
55- return this.sm;
56- case 'md':
57- return this.md;
58- case 'lg':
59- return this.lg;
60- default:
61- return this.lg;
62- }
63- }
64-}
65- 
66-/**
67- * Update breakpoints based on current window size
68- * Analyze window width to determine the breakpoint type for the current device and update global state
69- */
70-export function updateBreakpoint(windowWidth: number, windowObj?: window.Window, curBp?: string): string {
71- try {
72- let displayId = windowObj?.getWindowProperties().displayId;
73- let windowWidthVp = windowWidth / display.getDisplayByIdSync(displayId).densityPixels;
74- let newBp: string = '';
75- if (windowWidthVp < 320) {
76- newBp = 'xs';
77- } else if (windowWidthVp < 600) {
78- newBp = 'sm';
79- } else if (windowWidthVp < 840) {
80- newBp = 'md';
81- } else {
82- newBp = 'lg';
83- }
84- if (curBp !== newBp) {
85- AppStorage.setOrCreate('currentBreakpoint', newBp);
86- }
87- return newBp;
88- } catch (err) {
89- hilog.error(DOMAIN, TAG, FORMAT, `GetDisplayByIdSync or getWindowProperties failed.`);
90- return curBp ?? '';
91- }
92-}
Rentry/src/main/ets/viewModel/ContinuationViewModel.etsentry/src/main/ets/utils/DistributedFileUtil.ets+45-4
@@ -22,10 +22,10 @@ import { common } from '@kit.AbilityKit';
22import { ImageInfo } from '../model/CommModel';22import { ImageInfo } from '../model/CommModel';
23 23 
24const DOMAIN = 0x0000;24const DOMAIN = 0x0000;
25-const TAG = 'ContinuationViewModel';25+const TAG = 'DistributedFileUtil';
26const FORMAT = '%{public}s';26const FORMAT = '%{public}s';
27 27 
28-export class ContinuationViewModel {28+export class DistributedFileUtil {
29 private context: common.UIAbilityContext;29 private context: common.UIAbilityContext;
30 public imageUriArray: Array<ImageInfo> = [];30 public imageUriArray: Array<ImageInfo> = [];
31 public imageUri: ImageInfo | undefined = undefined;31 public imageUri: ImageInfo | undefined = undefined;
@@ -108,10 +108,18 @@ export class ContinuationViewModel {
108 hilog.error(DOMAIN, TAG, FORMAT, `fileCopy failed with err code: ${err.code}`);108 hilog.error(DOMAIN, TAG, FORMAT, `fileCopy failed with err code: ${err.code}`);
109 } finally {109 } finally {
110 if (file) {110 if (file) {
111- fileIo.closeSync(file);111+ try {
112+ fileIo.closeSync(file);
113+ } catch (err) {
114+ hilog.error(DOMAIN, TAG, FORMAT, `Failed to closeSync file. Cause : ${err}`);
115+ }
112 }116 }
113 if (saveFile) {117 if (saveFile) {
114- fileIo.closeSync(saveFile);118+ try {
119+ fileIo.closeSync(saveFile);
120+ } catch (err) {
121+ hilog.error(DOMAIN, TAG, FORMAT, `Failed to closeSync saveFile. Cause : ${err}`);
122+ }
115 }123 }
116 }124 }
117 }125 }
@@ -139,4 +147,37 @@ export class ContinuationViewModel {
139 }147 }
140 return assets;148 return assets;
141 }149 }
150+ 
151+ pixelMapToBuffer(imageInfo:ImageInfo): void {
152+ const imagePackerApi: image.ImagePacker = image.createImagePacker();
153+ let packOpts: image.PackingOption = { format: 'image/jpeg', quality: 100 };
154+ imagePackerApi.packToData(imageInfo.imagePixelMap, packOpts).then((data: ArrayBuffer) => {
155+ this.writeDistributedFile(data, imageInfo.imageName);
156+ }).catch((err: BusinessError) => {
157+ hilog.error(DOMAIN, TAG, FORMAT, `PackToData failed. Cause code: ${err.code}`);
158+ });
159+ }
160+ 
161+ writeDistributedFile(buf: ArrayBuffer, displayName: string): void {
162+ let distributedDir: string = this.context.distributedFilesDir;
163+ let fileName: string = '/' + displayName;
164+ let filePath: string = distributedDir + fileName;
165+ let file: fileIo.File | undefined = undefined;
166+ try {
167+ file = fileIo.openSync(filePath, fileIo.OpenMode.READ_WRITE | fileIo.OpenMode.CREATE);
168+ hilog.info(DOMAIN, TAG, FORMAT, 'Create file success.');
169+ fileIo.writeSync(file.fd, buf);
170+ } catch (error) {
171+ let err: BusinessError = error as BusinessError;
172+ hilog.error(DOMAIN, TAG, FORMAT, `Failed to openSync / writeSync. Code: ${err.code}`);
173+ } finally {
174+ if (file) {
175+ try {
176+ fileIo.closeSync(file.fd);
177+ } catch (err) {
178+ hilog.error(DOMAIN, TAG, FORMAT, `Failed to closeSync. Cause : ${err}`);
179+ }
180+ }
181+ }
182+ }
142}183}
@@ -0,0 +1,50 @@
1+/*
2+ * Copyright (c) 2026 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+ 
16+export class WidthBreakpointType<T> {
17+ public sm: T;
18+ public md: T;
19+ public lg: T;
20+ public xl: T;
21+ 
22+ public constructor(sm: T, md: T, lg: T, xl: T) {
23+ this.sm = sm;
24+ this.md = md;
25+ this.lg = lg;
26+ this.xl = xl;
27+ }
28+ 
29+ public getValue(widthBp: WidthBreakpoint): T {
30+ let value: T;
31+ switch (widthBp) {
32+ case WidthBreakpoint.WIDTH_XS:
33+ case WidthBreakpoint.WIDTH_SM:
34+ value = this.sm;
35+ break;
36+ case WidthBreakpoint.WIDTH_MD:
37+ value = this.md;
38+ break;
39+ case WidthBreakpoint.WIDTH_LG:
40+ value = this.lg;
41+ break;
42+ case WidthBreakpoint.WIDTH_XL:
43+ value = this.xl;
44+ break;
45+ default:
46+ value = this.sm;
47+ }
48+ return value;
49+ }
50+}
Rentry/src/main/ets/view/AddPic.etsentry/src/main/ets/view/AddPicView.ets+10-19
@@ -18,15 +18,14 @@ import { SymbolGlyphModifier } from '@kit.ArkUI';
18import { common } from '@kit.AbilityKit';18import { common } from '@kit.AbilityKit';
19import { ImageInfo } from '../model/CommModel';19import { ImageInfo } from '../model/CommModel';
20import { AddPicViewModel } from '../viewModel/AddPicViewModel';20import { AddPicViewModel } from '../viewModel/AddPicViewModel';
21-import { BreakpointType } from '../utils/BreakpointSystem';
22import CommonConstants from '../constants/CommonConstants';21import CommonConstants from '../constants/CommonConstants';
23 22 
24const DOMAIN = 0x0000;23const DOMAIN = 0x0000;
25-const TAG = 'AddPic';24+const TAG = 'AddPicView';
26const FORMAT = '%{public}s';25const FORMAT = '%{public}s';
27 26 
28@Component27@Component
29-export struct AddPic {28+export struct AddPicView {
30 @StorageLink('imageUri') imageUri: ImageInfo | null = null;29 @StorageLink('imageUri') imageUri: ImageInfo | null = null;
31 @StorageLink('imageUriArray') imageUriArray: Array<ImageInfo> = [];30 @StorageLink('imageUriArray') imageUriArray: Array<ImageInfo> = [];
32 @StorageLink('currentBreakpoint') currentBreakpoint: string = 'sm';31 @StorageLink('currentBreakpoint') currentBreakpoint: string = 'sm';
@@ -54,33 +53,25 @@ export struct AddPic {
54 if (this.isSingleImage) {53 if (this.isSingleImage) {
55 if (this.imageUri) {54 if (this.imageUri) {
56 Image(this.imageUri.imagePixelMap)55 Image(this.imageUri.imagePixelMap)
57- .width(96)56+ .width(CommonConstants.IMAGE_WIDTH_HEIGHT)
58- .height(96)57+ .height(CommonConstants.IMAGE_WIDTH_HEIGHT)
59 .borderRadius(10)58 .borderRadius(10)
60 .visibility(Visibility.Visible)59 .visibility(Visibility.Visible)
61 .draggable(false)60 .draggable(false)
62- .margin({
63- left: new BreakpointType($r('sys.float.padding_level8'), $r('sys.float.padding_level8'),
64- $r('sys.float.padding_level12'), $r('sys.float.padding_level16')).getValue(this.currentBreakpoint)
65- })
66 } else {61 } else {
67 this.addDefaultPic()62 this.addDefaultPic()
68 }63 }
69 } else {64 } else {
70 List({ space: 8 }) {65 List({ space: 8 }) {
71- ForEach(this.imageUriArray, (item: ImageInfo, index: number) => {66+ ForEach(this.imageUriArray, (item: ImageInfo) => {
72 ListItem() {67 ListItem() {
73 Image(item.imagePixelMap)68 Image(item.imagePixelMap)
74- .width(96)69+ .width(CommonConstants.IMAGE_WIDTH_HEIGHT)
75- .height(96)70+ .height(CommonConstants.IMAGE_WIDTH_HEIGHT)
76 .borderRadius(10)71 .borderRadius(10)
77 .visibility(item ? Visibility.Visible : Visibility.None)72 .visibility(item ? Visibility.Visible : Visibility.None)
78 .draggable(false)73 .draggable(false)
79 }74 }
80- .margin({
81- left: index === 0 ? new BreakpointType($r('sys.float.padding_level8'), $r('sys.float.padding_level8'),
82- $r('sys.float.padding_level12'), $r('sys.float.padding_level16')).getValue(this.currentBreakpoint) : 0
83- })
84 }, (item: ImageInfo, index: number) => JSON.stringify(item) + index)75 }, (item: ImageInfo, index: number) => JSON.stringify(item) + index)
85 if ((this.imageUriArray.length === 0 && this.isSingleImage) ||76 if ((this.imageUriArray.length === 0 && this.isSingleImage) ||
86 (this.imageUriArray.length < 9 && !this.isSingleImage)) {77 (this.imageUriArray.length < 9 && !this.isSingleImage)) {
@@ -91,7 +82,7 @@ export struct AddPic {
91 .scrollBar(BarState.Off)82 .scrollBar(BarState.Off)
92 }83 }
93 }84 }
94- .height(96)85+ .height(CommonConstants.IMAGE_WIDTH_HEIGHT)
95 .width(CommonConstants.FULL_PERCENT)86 .width(CommonConstants.FULL_PERCENT)
96 .alignItems(VerticalAlign.Center)87 .alignItems(VerticalAlign.Center)
97 }88 }
@@ -129,8 +120,8 @@ export struct AddPic {
129 .borderWidth(1)120 .borderWidth(1)
130 .borderColor($r('app.color.border_color'))121 .borderColor($r('app.color.border_color'))
131 .borderRadius(10)122 .borderRadius(10)
132- .width(96)123+ .width(CommonConstants.IMAGE_WIDTH_HEIGHT)
133- .height(96)124+ .height(CommonConstants.IMAGE_WIDTH_HEIGHT)
134 .bindMenu(this.MyTestMenu())125 .bindMenu(this.MyTestMenu())
135 }126 }
136}127}
@@ -37,7 +37,7 @@ export struct MigrateDataDistributedBaseView {
37 .fontWeight(FontWeight.Medium)37 .fontWeight(FontWeight.Medium)
38 .margin({ top: 12 })38 .margin({ top: 12 })
39 TextInput({ text: this.basicDataStr, placeholder: $r('app.string.input_text_data_to_migrate') })39 TextInput({ text: this.basicDataStr, placeholder: $r('app.string.input_text_data_to_migrate') })
40- .width(328)40+ .width(CommonConstants.FULL_PERCENT)
41 .height(40)41 .height(40)
42 .onChange(((value: string) => {42 .onChange(((value: string) => {
43 DebounceUtil.debounce('basicDataStr', () => {43 DebounceUtil.debounce('basicDataStr', () => {
@@ -46,7 +46,7 @@ export struct MigrateDataDistributedBaseView {
46 }))46 }))
47 TextInput({ text: `${this.basicDataNum}`, placeholder: $r('app.string.input_number_data_to_migrate') })47 TextInput({ text: `${this.basicDataNum}`, placeholder: $r('app.string.input_number_data_to_migrate') })
48 .type(InputType.Number)48 .type(InputType.Number)
49- .width(328)49+ .width(CommonConstants.FULL_PERCENT)
50 .height(40)50 .height(40)
51 .onChange(((value: string) => {51 .onChange(((value: string) => {
52 DebounceUtil.debounce('basicDataNum', () => {52 DebounceUtil.debounce('basicDataNum', () => {
@@ -59,7 +59,7 @@ export struct MigrateDataDistributedBaseView {
59 .fontWeight(FontWeight.Medium)59 .fontWeight(FontWeight.Medium)
60 .margin({ top: 12 })60 .margin({ top: 12 })
61 TextInput({ text: this.distributedData.str, placeholder: $r('app.string.input_text_data_to_migrate') })61 TextInput({ text: this.distributedData.str, placeholder: $r('app.string.input_text_data_to_migrate') })
62- .width(328)62+ .width(CommonConstants.FULL_PERCENT)
63 .height(40)63 .height(40)
64 .onChange(((value: string) => {64 .onChange(((value: string) => {
65 DebounceUtil.debounce('distributedDataStr', () => {65 DebounceUtil.debounce('distributedDataStr', () => {
@@ -68,7 +68,7 @@ export struct MigrateDataDistributedBaseView {
68 }))68 }))
69 TextInput({ text: `${this.distributedData.num}`, placeholder: $r('app.string.input_number_data_to_migrate') })69 TextInput({ text: `${this.distributedData.num}`, placeholder: $r('app.string.input_number_data_to_migrate') })
70 .type(InputType.Number)70 .type(InputType.Number)
71- .width(328)71+ .width(CommonConstants.FULL_PERCENT)
72 .height(40)72 .height(40)
73 .onChange(((value: string) => {73 .onChange(((value: string) => {
74 DebounceUtil.debounce('distributedDataNum', () => {74 DebounceUtil.debounce('distributedDataNum', () => {
@@ -78,7 +78,10 @@ export struct MigrateDataDistributedBaseView {
78 }))78 }))
79 }79 }
80 .width(CommonConstants.FULL_PERCENT)80 .width(CommonConstants.FULL_PERCENT)
81- .padding({ left: 16, right: 16 })81+ .padding({
82+ left: 16,
83+ right: 16
84+ })
82 }85 }
83 .title($r('app.string.migrate_data_distributed_base'))86 .title($r('app.string.migrate_data_distributed_base'))
84 .width(CommonConstants.FULL_PERCENT)87 .width(CommonConstants.FULL_PERCENT)
@@ -15,7 +15,7 @@
15 15 
16import CommonConstants from '../constants/CommonConstants';16import CommonConstants from '../constants/CommonConstants';
17import { DebounceUtil } from '../utils/DebounceUtil';17import { DebounceUtil } from '../utils/DebounceUtil';
18-import { AddPic } from './AddPic';18+import { AddPicView } from './AddPicView';
19 19 
20@Builder20@Builder
21export function migrateDataDistributedMonoFileViewBuilder() {21export function migrateDataDistributedMonoFileViewBuilder() {
@@ -37,7 +37,7 @@ export struct MigrateDataDistributedMonoFileView {
37 .fontWeight(FontWeight.Medium)37 .fontWeight(FontWeight.Medium)
38 .margin({ top: 12 })38 .margin({ top: 12 })
39 TextInput({ text: this.monoFileStr, placeholder: $r('app.string.input_text_data_to_migrate') })39 TextInput({ text: this.monoFileStr, placeholder: $r('app.string.input_text_data_to_migrate') })
40- .width(328)40+ .width(CommonConstants.FULL_PERCENT)
41 .height(40)41 .height(40)
42 .onChange(((value: string) => {42 .onChange(((value: string) => {
43 DebounceUtil.debounce('monoFileStr', () => {43 DebounceUtil.debounce('monoFileStr', () => {
@@ -46,7 +46,7 @@ export struct MigrateDataDistributedMonoFileView {
46 }))46 }))
47 TextInput({ text: `${this.monoFileNum}`, placeholder: $r('app.string.input_number_data_to_migrate') })47 TextInput({ text: `${this.monoFileNum}`, placeholder: $r('app.string.input_number_data_to_migrate') })
48 .type(InputType.Number)48 .type(InputType.Number)
49- .width(328)49+ .width(CommonConstants.FULL_PERCENT)
50 .height(40)50 .height(40)
51 .onChange(((value: string) => {51 .onChange(((value: string) => {
52 DebounceUtil.debounce('monoFileNum', () => {52 DebounceUtil.debounce('monoFileNum', () => {
@@ -57,10 +57,13 @@ export struct MigrateDataDistributedMonoFileView {
57 Text($r('app.string.image_file'))57 Text($r('app.string.image_file'))
58 .alignSelf(ItemAlign.Start)58 .alignSelf(ItemAlign.Start)
59 .fontWeight(FontWeight.Medium)59 .fontWeight(FontWeight.Medium)
60- AddPic({ isSingleImage: this.isSingleImage })60+ AddPicView({ isSingleImage: this.isSingleImage })
61 }61 }
62 .width(CommonConstants.FULL_PERCENT)62 .width(CommonConstants.FULL_PERCENT)
63- .padding({ left: 16, right: 16 })63+ .padding({
64+ left: 16,
65+ right: 16
66+ })
64 }67 }
65 .title($r('app.string.migrate_data_distributed_mono_file'))68 .title($r('app.string.migrate_data_distributed_mono_file'))
66 .width(CommonConstants.FULL_PERCENT)69 .width(CommonConstants.FULL_PERCENT)
@@ -15,7 +15,7 @@
15 15 
16import CommonConstants from '../constants/CommonConstants';16import CommonConstants from '../constants/CommonConstants';
17import { DebounceUtil } from '../utils/DebounceUtil';17import { DebounceUtil } from '../utils/DebounceUtil';
18-import { AddPic } from './AddPic';18+import { AddPicView } from './AddPicView';
19 19 
20@Builder20@Builder
21export function migrateDataDistributedMultiFileViewBuilder() {21export function migrateDataDistributedMultiFileViewBuilder() {
@@ -37,7 +37,7 @@ export struct MigrateDataDistributedMultiFileView {
37 .fontWeight(FontWeight.Medium)37 .fontWeight(FontWeight.Medium)
38 .margin({ top: 12 })38 .margin({ top: 12 })
39 TextInput({ text: this.multiFileStr, placeholder: $r('app.string.input_text_data_to_migrate') })39 TextInput({ text: this.multiFileStr, placeholder: $r('app.string.input_text_data_to_migrate') })
40- .width(328)40+ .width(CommonConstants.FULL_PERCENT)
41 .height(40)41 .height(40)
42 .onChange(((value: string) => {42 .onChange(((value: string) => {
43 DebounceUtil.debounce('multiFileStr', () => {43 DebounceUtil.debounce('multiFileStr', () => {
@@ -46,7 +46,7 @@ export struct MigrateDataDistributedMultiFileView {
46 }))46 }))
47 TextInput({ text: `${this.multiFileNum}`, placeholder: $r('app.string.input_number_data_to_migrate') })47 TextInput({ text: `${this.multiFileNum}`, placeholder: $r('app.string.input_number_data_to_migrate') })
48 .type(InputType.Number)48 .type(InputType.Number)
49- .width(328)49+ .width(CommonConstants.FULL_PERCENT)
50 .height(40)50 .height(40)
51 .onChange(((value: string) => {51 .onChange(((value: string) => {
52 DebounceUtil.debounce('multiFileNum', () => {52 DebounceUtil.debounce('multiFileNum', () => {
@@ -57,10 +57,13 @@ export struct MigrateDataDistributedMultiFileView {
57 Text($r('app.string.image_file'))57 Text($r('app.string.image_file'))
58 .alignSelf(ItemAlign.Start)58 .alignSelf(ItemAlign.Start)
59 .fontWeight(FontWeight.Medium)59 .fontWeight(FontWeight.Medium)
60- AddPic({ isSingleImage: this.isSingleImage })60+ AddPicView({ isSingleImage: this.isSingleImage })
61 }61 }
62 .width(CommonConstants.FULL_PERCENT)62 .width(CommonConstants.FULL_PERCENT)
63- .padding({ left: 16, right: 16 })63+ .padding({
64+ left: 16,
65+ right: 16
66+ })
64 }67 }
65 .title($r('app.string.migrate_data_distributed_multi_file'))68 .title($r('app.string.migrate_data_distributed_multi_file'))
66 .width(CommonConstants.FULL_PERCENT)69 .width(CommonConstants.FULL_PERCENT)
@@ -33,7 +33,7 @@ export struct MigrateDataWantParamView {
33 .fontWeight(FontWeight.Medium)33 .fontWeight(FontWeight.Medium)
34 .margin({ top: 12 })34 .margin({ top: 12 })
35 TextInput({ text: this.wantBasicData, placeholder: $r('app.string.input_data_to_migrate') })35 TextInput({ text: this.wantBasicData, placeholder: $r('app.string.input_data_to_migrate') })
36- .width(328)36+ .width(CommonConstants.FULL_PERCENT)
37 .height(40)37 .height(40)
38 .onChange(((value: string) => {38 .onChange(((value: string) => {
39 DebounceUtil.debounce('wantBasicData', () => {39 DebounceUtil.debounce('wantBasicData', () => {
@@ -42,7 +42,10 @@ export struct MigrateDataWantParamView {
42 }))42 }))
43 }43 }
44 .width(CommonConstants.FULL_PERCENT)44 .width(CommonConstants.FULL_PERCENT)
45- .padding({ left: 16, right: 16 })45+ .padding({
46+ left: 16,
47+ right: 16
48+ })
46 }49 }
47 .title($r('app.string.migrate_data_want_param'))50 .title($r('app.string.migrate_data_want_param'))
48 .width(CommonConstants.FULL_PERCENT)51 .width(CommonConstants.FULL_PERCENT)
@@ -13,10 +13,11 @@
13 * limitations under the License.13 * limitations under the License.
14 */14 */
15 15 
16-import { BusinessError } from '@kit.BasicServicesKit';
17import { hilog } from '@kit.PerformanceAnalysisKit';16import { hilog } from '@kit.PerformanceAnalysisKit';
18import { common } from '@kit.AbilityKit';17import { common } from '@kit.AbilityKit';
19-import { ImageViewModel } from './ImageViewModel';18+import { dataSharePredicates } from '@kit.ArkData';
19+import { photoAccessHelper } from '@kit.MediaLibraryKit';
20+import { fileSelect } from '../utils/FileUtil';
20import { ImageInfo } from '../model/CommModel';21import { ImageInfo } from '../model/CommModel';
21 22 
22const DOMAIN = 0x0000;23const DOMAIN = 0x0000;
@@ -26,13 +27,11 @@ const MAX_IMAGE_COUNT = 9;
26 27 
27export class AddPicViewModel {28export class AddPicViewModel {
28 private context: common.UIAbilityContext;29 private context: common.UIAbilityContext;
29- private imageViewModel: ImageViewModel;
30 private showToastCallback: (message: ResourceStr) => void;30 private showToastCallback: (message: ResourceStr) => void;
31 public imageUriArray: Array<ImageInfo> = [];31 public imageUriArray: Array<ImageInfo> = [];
32 32 
33 constructor(context: common.UIAbilityContext, showToastCallback: (message: ResourceStr) => void) {33 constructor(context: common.UIAbilityContext, showToastCallback: (message: ResourceStr) => void) {
34 this.context = context;34 this.context = context;
35- this.imageViewModel = new ImageViewModel(this.context);
36 this.showToastCallback = showToastCallback;35 this.showToastCallback = showToastCallback;
37 }36 }
38 37 
@@ -40,7 +39,7 @@ export class AddPicViewModel {
40 try {39 try {
41 this.showToastCallback(message);40 this.showToastCallback(message);
42 } catch (err) {41 } catch (err) {
43- hilog.error(DOMAIN, TAG, FORMAT, `ShowToast failed. Cause code: ${(err as BusinessError).code}`);42+ hilog.error(DOMAIN, TAG, FORMAT, `ShowToast failed. Cause : ${err}`);
44 }43 }
45 }44 }
46 45 
@@ -63,16 +62,58 @@ export class AddPicViewModel {
63 62 
64 selectImage(isSingleImage: boolean): void {63 selectImage(isSingleImage: boolean): void {
65 if (!this.canAddMoreImages(isSingleImage)) {64 if (!this.canAddMoreImages(isSingleImage)) {
65+ this.showToast($r('app.string.add_picture_prompt'));
66 return;66 return;
67 }67 }
68- this.imageViewModel.imageUriArray = this.imageUriArray;68+ fileSelect(isSingleImage).then((uri: Array<ResourceStr>) => {
69- this.imageViewModel.selectImage(isSingleImage, () => {69+ if (!uri || uri.length === 0) {
70- this.showToast($r('app.string.add_picture_prompt'));70+ return;
71+ }
72+ if ((this.imageUriArray.length + uri.length) > (isSingleImage ? 1 : 9)) {
73+ uri.length = (isSingleImage ? 1 : 9) - this.imageUriArray.length;
74+ this.showToast($r('app.string.add_picture_prompt'));
75+ }
76+ uri.forEach((item: ResourceStr) => {
77+ this.getThumbnail(item as string, isSingleImage);
78+ });
79+ }).catch(() => {
80+ this.showToast($r('app.string.select_image_failed'));
71 });81 });
72 }82 }
73 83 
84+ private async getThumbnail(uri: string, isSingleImage: boolean): Promise<void> {
85+ let phAccessHelper = photoAccessHelper.getPhotoAccessHelper(this.context);
86+ let predicates: dataSharePredicates.DataSharePredicates = new dataSharePredicates.DataSharePredicates();
87+ predicates.equalTo('uri', uri);
88+ let fetchOption: photoAccessHelper.FetchOptions = {
89+ fetchColumns: [],
90+ predicates: predicates
91+ };
92+ try {
93+ let fetchResult: photoAccessHelper.FetchResult<photoAccessHelper.PhotoAsset> =
94+ await phAccessHelper.getAssets(fetchOption);
95+ let asset: photoAccessHelper.PhotoAsset = await fetchResult.getFirstObject();
96+ let pixelMap = await asset.getThumbnail();
97+ hilog.info(DOMAIN, TAG, FORMAT, 'getThumbnail successful');
98+ let displayName = asset.displayName || '';
99+ if (!displayName) {
100+ hilog.error(DOMAIN, TAG, FORMAT, 'displayName is empty');
101+ return;
102+ }
103+ let dotIndex = displayName.indexOf('.');
104+ let imageName = dotIndex > 0 ? displayName.substring(0, dotIndex) : displayName;
105+ if (isSingleImage) {
106+ AppStorage.setOrCreate('imageUri', { imagePixelMap: pixelMap, imageName: imageName });
107+ } else {
108+ this.imageUriArray.push({ imagePixelMap: pixelMap, imageName: imageName });
109+ AppStorage.setOrCreate('imageUriArray', this.imageUriArray);
110+ }
111+ } catch (err) {
112+ hilog.error(DOMAIN, TAG, FORMAT, `GetThumbnail failed. Cause : ${err}`);
113+ }
114+ }
115+ 
74 updateImageArray(imageUriArray: Array<ImageInfo>): void {116 updateImageArray(imageUriArray: Array<ImageInfo>): void {
75 this.imageUriArray = imageUriArray;117 this.imageUriArray = imageUriArray;
76- this.imageViewModel.imageUriArray = imageUriArray;
77 }118 }
78}119}
@@ -1,144 +0,0 @@
1-/*
2- * Copyright (c) 2026 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- 
16-import { dataSharePredicates, unifiedDataChannel } from '@kit.ArkData';
17-import { photoAccessHelper } from '@kit.MediaLibraryKit';
18-import { BusinessError } from '@kit.BasicServicesKit';
19-import { hilog } from '@kit.PerformanceAnalysisKit';
20-import { fileIo } from '@kit.CoreFileKit';
21-import { common } from '@kit.AbilityKit';
22-import { image } from '@kit.ImageKit';
23-import { fileSelect } from '../utils/FileUtil';
24-import { ImageInfo } from '../model/CommModel';
25- 
26-const DOMAIN = 0x0000;
27-const TAG = 'ImageViewModel';
28-const FORMAT = '%{public}s';
29- 
30-export class ImageViewModel {
31- public context: common.UIAbilityContext;
32- public imageUriArray: Array<ImageInfo> = [];
33- 
34- constructor(context: common.UIAbilityContext) {
35- this.context = context;
36- }
37- 
38- selectImage(isSingleImage: boolean, onLimitReached?: () => void): void {
39- fileSelect(isSingleImage).then((uri: Array<ResourceStr>) => {
40- if ((this.imageUriArray.length + uri.length) > (isSingleImage ? 1 : 9)) {
41- uri.length = (isSingleImage ? 1 : 9) - this.imageUriArray.length;
42- if (onLimitReached) {
43- onLimitReached();
44- }
45- }
46- uri.forEach((item: ResourceStr) => {
47- this.getThumbnail(item as string, isSingleImage);
48- });
49- });
50- }
51- 
52- private async getThumbnail(uri: string, isSingleImage: boolean): Promise<void> {
53- let phAccessHelper = photoAccessHelper.getPhotoAccessHelper(this.context);
54- let predicates: dataSharePredicates.DataSharePredicates = new dataSharePredicates.DataSharePredicates();
55- predicates.equalTo('uri', uri);
56- let fetchOption: photoAccessHelper.FetchOptions = {
57- fetchColumns: [],
58- predicates: predicates
59- };
60- try {
61- let fetchResult: photoAccessHelper.FetchResult<photoAccessHelper.PhotoAsset> =
62- await phAccessHelper.getAssets(fetchOption);
63- let asset: photoAccessHelper.PhotoAsset = await fetchResult.getFirstObject();
64- let pixelMap = await asset.getThumbnail();
65- hilog.info(DOMAIN, TAG, FORMAT, 'getThumbnail successful');
66- let displayName = asset.displayName || '';
67- if (!displayName) {
68- hilog.error(DOMAIN, TAG, FORMAT, 'displayName is empty');
69- return;
70- }
71- let dotIndex = displayName.indexOf('.');
72- let imageName = dotIndex > 0 ? displayName.substring(0, dotIndex) : displayName;
73- this.pixelMapToBuffer(pixelMap, imageName);
74- if (isSingleImage) {
75- AppStorage.setOrCreate('imageUri', { imagePixelMap: pixelMap, imageName: imageName });
76- } else {
77- this.imageUriArray.push({ imagePixelMap: pixelMap, imageName: imageName });
78- AppStorage.setOrCreate('imageUriArray', this.imageUriArray);
79- }
80- } catch (err) {
81- hilog.error(DOMAIN, TAG, FORMAT, `GetThumbnail failed. Cause code: ${(err as BusinessError).code}`);
82- }
83- }
84- 
85- private pixelMapToBuffer(pixelMap: image.PixelMap, displayName: string): void {
86- const imagePackerApi: image.ImagePacker = image.createImagePacker();
87- let packOpts: image.PackingOption = { format: 'image/jpeg', quality: 100 };
88- imagePackerApi.packToData(pixelMap, packOpts).then((data: ArrayBuffer) => {
89- this.writeDistributedFile(data, displayName);
90- }).catch((err: BusinessError) => {
91- hilog.error(DOMAIN, TAG, FORMAT, `PackToData failed. Cause code: ${err.code}`);
92- });
93- }
94- 
95- private writeDistributedFile(buf: ArrayBuffer, displayName: string): void {
96- let distributedDir: string = this.context.distributedFilesDir;
97- let fileName: string = '/' + displayName;
98- let filePath: string = distributedDir + fileName;
99- let file: fileIo.File | undefined = undefined;
100- try {
101- file = fileIo.openSync(filePath, fileIo.OpenMode.READ_WRITE | fileIo.OpenMode.CREATE);
102- hilog.info(DOMAIN, TAG, FORMAT, 'Create file success.');
103- fileIo.writeSync(file.fd, buf);
104- } catch (error) {
105- let err: BusinessError = error as BusinessError;
106- hilog.error(DOMAIN, TAG, FORMAT, `Failed to openSync / writeSync. Code: ${err.code}`);
107- } finally {
108- if (file) {
109- try {
110- fileIo.closeSync(file.fd);
111- } catch (err) {
112- hilog.error(DOMAIN, TAG, FORMAT, `Failed to closeSync. Code: ${(err as BusinessError).code}`);
113- }
114- }
115- }
116- }
117- 
118- getDataFromUdmf(event: DragEvent, callback: (data: DragEvent) => void): void {
119- if (this.getDataFromUdmfRetry(event, callback)) {
120- return;
121- }
122- setTimeout(() => {
123- this.getDataFromUdmfRetry(event, callback);
124- }, 1500);
125- }
126- 
127- private getDataFromUdmfRetry(event: DragEvent, callback: (data: DragEvent) => void): boolean {
128- try {
129- let data: UnifiedData = event.getData();
130- if (!data) {
131- return false;
132- }
133- let records: Array<unifiedDataChannel.UnifiedRecord> = data.getRecords();
134- if (!records || records.length <= 0) {
135- return false;
136- }
137- callback(event);
138- return true;
139- } catch (e) {
140- hilog.error(DOMAIN, TAG, FORMAT, `getData failed, code = ${(e as BusinessError).code}`);
141- return false;
142- }
143- }
144-}
@@ -79,6 +79,10 @@
79 {79 {
80 "name": "permission_read_imagevideo",80 "name": "permission_read_imagevideo",
81 "value": "Allow reading of image or video files in the user's public directory"81 "value": "Allow reading of image or video files in the user's public directory"
82+ },
83+ {
84+ "name": "select_image_failed",
85+ "value": "Failed to select image"
82 }86 }
83 ]87 ]
84}88}
@@ -79,6 +79,10 @@
79 {79 {
80 "name": "permission_read_imagevideo",80 "name": "permission_read_imagevideo",
81 "value": "Allow reading of image or video files in the user's public directory"81 "value": "Allow reading of image or video files in the user's public directory"
82+ },
83+ {
84+ "name": "select_image_failed",
85+ "value": "Failed to select image"
82 }86 }
83 ]87 ]
84}88}
@@ -79,6 +79,10 @@
79 {79 {
80 "name": "permission_read_imagevideo",80 "name": "permission_read_imagevideo",
81 "value": "允许读取用户公共目录的图片或视频文件"81 "value": "允许读取用户公共目录的图片或视频文件"
82+ },
83+ {
84+ "name": "select_image_failed",
85+ "value": "选择图片失败"
82 }86 }
83 ]87 ]
84}88}