1bea4f105Sopenharmony_ci/*
2bea4f105Sopenharmony_ci * Copyright (c) 2021-2023 Huawei Device Co., Ltd.
3bea4f105Sopenharmony_ci * Licensed under the Apache License, Version 2.0 (the "License");
4bea4f105Sopenharmony_ci * you may not use this file except in compliance with the License.
5bea4f105Sopenharmony_ci * You may obtain a copy of the License at
6bea4f105Sopenharmony_ci *
7bea4f105Sopenharmony_ci *     http://www.apache.org/licenses/LICENSE-2.0
8bea4f105Sopenharmony_ci *
9bea4f105Sopenharmony_ci * Unless required by applicable law or agreed to in writing, software
10bea4f105Sopenharmony_ci * distributed under the License is distributed on an "AS IS" BASIS,
11bea4f105Sopenharmony_ci * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12bea4f105Sopenharmony_ci * See the License for the specific language governing permissions and
13bea4f105Sopenharmony_ci * limitations under the License.
14bea4f105Sopenharmony_ci */
15bea4f105Sopenharmony_ci
16bea4f105Sopenharmony_ciimport { fileTree } from './component/dialog/FileMoveDialog';
17bea4f105Sopenharmony_ciimport Logger from '../base/log/Logger';
18bea4f105Sopenharmony_ciimport ErrorCodeConst from '../base//constants/ErrorCodeConst';
19bea4f105Sopenharmony_ciimport { toast } from '../base/utils/Common';
20bea4f105Sopenharmony_ciimport AbilityCommonUtil, { ResultCodePicker } from '../base/utils/AbilityCommonUtil';
21bea4f105Sopenharmony_ciimport { SYSTEM_BAR_COLOR } from '../base/constants/UiConstant';
22bea4f105Sopenharmony_ciimport StringUtil from '../base/utils/StringUtil';
23bea4f105Sopenharmony_ciimport { FileUtil } from '../base/utils/FileUtil';
24bea4f105Sopenharmony_ciimport ObjectUtil from '../base/utils/ObjectUtil';
25bea4f105Sopenharmony_ciimport fileAccess from '@ohos.file.fileAccess';
26bea4f105Sopenharmony_ciimport { ArrayUtil } from '../base/utils/ArrayUtil';
27bea4f105Sopenharmony_ciimport { UiUtil } from '../base/utils/UiUtil';
28bea4f105Sopenharmony_ciimport { StartModeOptions } from '../base/model/StartModeOptions';
29bea4f105Sopenharmony_ciimport { FilePickerUtil } from '../base/utils/FilePickerUtil';
30bea4f105Sopenharmony_ciimport { photoAccessHelper } from '@kit.MediaLibraryKit';
31bea4f105Sopenharmony_ci
32bea4f105Sopenharmony_ciconst TAG = 'PathSelector';
33bea4f105Sopenharmony_cilet storage = LocalStorage.getShared();
34bea4f105Sopenharmony_ci
35bea4f105Sopenharmony_ci@Entry(storage)
36bea4f105Sopenharmony_ci@Component
37bea4f105Sopenharmony_cistruct PathSelector {
38bea4f105Sopenharmony_ci  private startModeOptions: StartModeOptions = FilePickerUtil.getStartOptionsFromStorage();
39bea4f105Sopenharmony_ci  @State createResultType: number = ErrorCodeConst.PICKER.NORMAL;
40bea4f105Sopenharmony_ci
41bea4f105Sopenharmony_ci  aboutToAppear() {
42bea4f105Sopenharmony_ci    UiUtil.setWindowBackground(SYSTEM_BAR_COLOR.LIGHT_GRAY);
43bea4f105Sopenharmony_ci  }
44bea4f105Sopenharmony_ci
45bea4f105Sopenharmony_ci  async saveFileCallback(res, startModeOptions: StartModeOptions): Promise<void> {
46bea4f105Sopenharmony_ci    if (res?.cancel) {
47bea4f105Sopenharmony_ci      AbilityCommonUtil.terminatePathPicker([], ResultCodePicker.CANCEL, startModeOptions);
48bea4f105Sopenharmony_ci      return;
49bea4f105Sopenharmony_ci    } else {
50bea4f105Sopenharmony_ci      let fileNameList = this.startModeOptions.newFileNames;
51bea4f105Sopenharmony_ci      // 保存单个文件时文件名可修改,需使用修改后的文件名来创建文件
52bea4f105Sopenharmony_ci      if (fileNameList.length <= 1) {
53bea4f105Sopenharmony_ci        fileNameList = [res.fileName];
54bea4f105Sopenharmony_ci      }
55bea4f105Sopenharmony_ci      this.saveFiles(res.selectUri, fileNameList).then((createdFileList) => {
56bea4f105Sopenharmony_ci        AbilityCommonUtil.terminatePathPicker(createdFileList, ResultCodePicker.SUCCESS, startModeOptions);
57bea4f105Sopenharmony_ci      }).catch((err) => {
58bea4f105Sopenharmony_ci        let errorMessage = '';
59bea4f105Sopenharmony_ci        let errorCode = 0;
60bea4f105Sopenharmony_ci        Logger.e(TAG, JSON.stringify(err));
61bea4f105Sopenharmony_ci        if (err.code) {
62bea4f105Sopenharmony_ci          if (err.code === ErrorCodeConst.FILE_ACCESS.FILE_NAME_EXIST) {
63bea4f105Sopenharmony_ci            errorMessage = 'Same name file already exists';
64bea4f105Sopenharmony_ci            errorCode = ErrorCodeConst.PICKER.FILE_NAME_EXIST;
65bea4f105Sopenharmony_ci            this.createResultType = errorCode;
66bea4f105Sopenharmony_ci            const pathName = startModeOptions.newFileNames;
67bea4f105Sopenharmony_ci            let listLength: number = pathName.length;
68bea4f105Sopenharmony_ci            if (listLength == 1) {
69bea4f105Sopenharmony_ci              return;
70bea4f105Sopenharmony_ci            }
71bea4f105Sopenharmony_ci          } else if (err.code === ErrorCodeConst.FILE_ACCESS.FILE_NAME_INVALID) {
72bea4f105Sopenharmony_ci            errorMessage = 'Invalid display name';
73bea4f105Sopenharmony_ci            errorCode = ErrorCodeConst.PICKER.FILE_NAME_INVALID;
74bea4f105Sopenharmony_ci          } else {
75bea4f105Sopenharmony_ci            errorMessage = 'File create failed';
76bea4f105Sopenharmony_ci            errorCode = ErrorCodeConst.PICKER.OTHER_ERROR;
77bea4f105Sopenharmony_ci          }
78bea4f105Sopenharmony_ci        } else {
79bea4f105Sopenharmony_ci          errorMessage = err.message ? err.message : err;
80bea4f105Sopenharmony_ci          errorCode = ErrorCodeConst.PICKER.OTHER_ERROR;
81bea4f105Sopenharmony_ci        }
82bea4f105Sopenharmony_ci        AbilityCommonUtil.terminatePathPicker([], errorCode, startModeOptions);
83bea4f105Sopenharmony_ci        toast($r('app.string.save_file_fail'));
84bea4f105Sopenharmony_ci        Logger.e(TAG, `path select error, errorCode: ${errorCode}, errorMessage: ${errorMessage}`);
85bea4f105Sopenharmony_ci      })
86bea4f105Sopenharmony_ci    }
87bea4f105Sopenharmony_ci  }
88bea4f105Sopenharmony_ci
89bea4f105Sopenharmony_ci  /**
90bea4f105Sopenharmony_ci   * PathPicker保存文件
91bea4f105Sopenharmony_ci   * @param data SaveFilesParam
92bea4f105Sopenharmony_ci   */
93bea4f105Sopenharmony_ci  async saveFiles(path: string, nameList: string[]): Promise<string[]> {
94bea4f105Sopenharmony_ci    return new Promise(async (resolve, reject) => {
95bea4f105Sopenharmony_ci      let fileAccessHelper = await FileUtil.getFileAccessHelperAsync(globalThis.abilityContext);
96bea4f105Sopenharmony_ci      let dirPath = path;
97bea4f105Sopenharmony_ci      if (StringUtil.isEmpty(dirPath)) {
98bea4f105Sopenharmony_ci        dirPath = (await FileUtil.getFileInfoByRelativePath('Documents/', fileAccessHelper)).uri;
99bea4f105Sopenharmony_ci      }
100bea4f105Sopenharmony_ci      let fileNameArr = nameList;
101bea4f105Sopenharmony_ci      let successArr: string[] = [];
102bea4f105Sopenharmony_ci      let resultErr: any;
103bea4f105Sopenharmony_ci      let len: number = fileNameArr.length;
104bea4f105Sopenharmony_ci      let fileNameList: string[] = [];
105bea4f105Sopenharmony_ci      if (len > 1) {
106bea4f105Sopenharmony_ci        fileNameList = await this.getPickPathListFiles(dirPath, fileAccessHelper);
107bea4f105Sopenharmony_ci      }
108bea4f105Sopenharmony_ci      Logger.i(TAG, 'saveFiles createName: ' + JSON.stringify(fileNameArr) + ' ; ');
109bea4f105Sopenharmony_ci      Logger.i(TAG, 'saveFiles subList: ' + JSON.stringify(fileNameList) + ' ; ');
110bea4f105Sopenharmony_ci      for (let i = 0; i < len; i++) {
111bea4f105Sopenharmony_ci        const currName = fileNameArr[i];
112bea4f105Sopenharmony_ci        let result;
113bea4f105Sopenharmony_ci        if (len === 1) {
114bea4f105Sopenharmony_ci          result = await FileUtil.createFile(fileAccessHelper, dirPath, currName);
115bea4f105Sopenharmony_ci        } else {
116bea4f105Sopenharmony_ci          result = await this.tryRenameFileOperate(fileAccessHelper, currName, dirPath, 0, fileNameList);
117bea4f105Sopenharmony_ci        }
118bea4f105Sopenharmony_ci        if (ObjectUtil.isUndefined(result.err)) {
119bea4f105Sopenharmony_ci          Logger.i(TAG, 'saveFiles createOK: ' + result.uri);
120bea4f105Sopenharmony_ci          successArr.push(result.uri);
121bea4f105Sopenharmony_ci          continue;
122bea4f105Sopenharmony_ci        }
123bea4f105Sopenharmony_ci        Logger.i(TAG, 'saveFiles err: ' + result.err.code);
124bea4f105Sopenharmony_ci        // 失败
125bea4f105Sopenharmony_ci        resultErr = { code: result.err.code, message: result.err.message };
126bea4f105Sopenharmony_ci        let photoManageHelper: photoAccessHelper.PhotoAccessHelper = AbilityCommonUtil.getPhotoManageHelper();
127bea4f105Sopenharmony_ci        if (ObjectUtil.isNullOrUndefined(photoManageHelper)) {
128bea4f105Sopenharmony_ci          break;
129bea4f105Sopenharmony_ci        }
130bea4f105Sopenharmony_ci        for (let i = 0; i < successArr.length; i++) {
131bea4f105Sopenharmony_ci          await FileUtil.hardDelete(successArr[i]);
132bea4f105Sopenharmony_ci        }
133bea4f105Sopenharmony_ci        try {
134bea4f105Sopenharmony_ci          photoManageHelper.release();
135bea4f105Sopenharmony_ci        } catch (e) {
136bea4f105Sopenharmony_ci          Logger.e(TAG, 'mediaLibrary close error');
137bea4f105Sopenharmony_ci        }
138bea4f105Sopenharmony_ci        successArr = [];
139bea4f105Sopenharmony_ci        break;
140bea4f105Sopenharmony_ci      }
141bea4f105Sopenharmony_ci
142bea4f105Sopenharmony_ci      Logger.i(TAG, 'saveFiles end: ' + JSON.stringify(successArr));
143bea4f105Sopenharmony_ci      if (!ArrayUtil.isEmpty(successArr)) {
144bea4f105Sopenharmony_ci        resolve(successArr);
145bea4f105Sopenharmony_ci      } else {
146bea4f105Sopenharmony_ci        reject(resultErr);
147bea4f105Sopenharmony_ci      }
148bea4f105Sopenharmony_ci    })
149bea4f105Sopenharmony_ci  }
150bea4f105Sopenharmony_ci
151bea4f105Sopenharmony_ci  private async getPickPathListFiles(dirUri: string, fileAccessHelper: fileAccess.FileAccessHelper): Promise<string[]> {
152bea4f105Sopenharmony_ci    let fileInfo: fileAccess.FileInfo = await FileUtil.getFileInfoByUri(dirUri, fileAccessHelper);
153bea4f105Sopenharmony_ci    if (ObjectUtil.isNullOrUndefined(fileInfo) || !FileUtil.isFolder(fileInfo.mode)) {
154bea4f105Sopenharmony_ci      return [];
155bea4f105Sopenharmony_ci    }
156bea4f105Sopenharmony_ci    return this.getFilesByIterator(fileInfo.listFile());
157bea4f105Sopenharmony_ci  }
158bea4f105Sopenharmony_ci
159bea4f105Sopenharmony_ci  private getFilesByIterator(fileIterator: fileAccess.FileIterator): string[] {
160bea4f105Sopenharmony_ci    if (ObjectUtil.isNull(fileIterator)) {
161bea4f105Sopenharmony_ci      return null;
162bea4f105Sopenharmony_ci    }
163bea4f105Sopenharmony_ci    let result: string[] = [];
164bea4f105Sopenharmony_ci    let isDone = false;
165bea4f105Sopenharmony_ci    while (!isDone) {
166bea4f105Sopenharmony_ci      try {
167bea4f105Sopenharmony_ci        let nextFileInfo = fileIterator.next();
168bea4f105Sopenharmony_ci        isDone = nextFileInfo.done;
169bea4f105Sopenharmony_ci        if (isDone) {
170bea4f105Sopenharmony_ci          break;
171bea4f105Sopenharmony_ci        }
172bea4f105Sopenharmony_ci        let currFile = nextFileInfo.value;
173bea4f105Sopenharmony_ci        if (!FileUtil.isFolder(currFile.mode)) {
174bea4f105Sopenharmony_ci          result.push(currFile.fileName);
175bea4f105Sopenharmony_ci        }
176bea4f105Sopenharmony_ci      } catch (err) {
177bea4f105Sopenharmony_ci        Logger.e(TAG, 'current File err: ' + JSON.stringify(err) + ', ' + err.toString());
178bea4f105Sopenharmony_ci      }
179bea4f105Sopenharmony_ci    }
180bea4f105Sopenharmony_ci    return result;
181bea4f105Sopenharmony_ci  }
182bea4f105Sopenharmony_ci
183bea4f105Sopenharmony_ci  private async tryRenameFileOperate(fileAccessHelper: fileAccess.FileAccessHelper, fileName: string,
184bea4f105Sopenharmony_ci    dirUri: string, renameCount: number, fileNameList: string[] = []): Promise<{
185bea4f105Sopenharmony_ci    err,
186bea4f105Sopenharmony_ci    uri
187bea4f105Sopenharmony_ci  }> {
188bea4f105Sopenharmony_ci    let index = fileName.lastIndexOf('.');
189bea4f105Sopenharmony_ci    let name = fileName;
190bea4f105Sopenharmony_ci    let suffix = '';
191bea4f105Sopenharmony_ci    if (index !== -1) {
192bea4f105Sopenharmony_ci      suffix = fileName.substring(index, fileName.length);
193bea4f105Sopenharmony_ci      name = fileName.substring(0, index);
194bea4f105Sopenharmony_ci    }
195bea4f105Sopenharmony_ci    let hasReNameCount = FileUtil.getFileNameReName(name);
196bea4f105Sopenharmony_ci    if (!ObjectUtil.isNullOrUndefined(hasReNameCount)) {
197bea4f105Sopenharmony_ci      let num = Number(hasReNameCount[1]);
198bea4f105Sopenharmony_ci      if (!isNaN(num)) {
199bea4f105Sopenharmony_ci        name = hasReNameCount[0];
200bea4f105Sopenharmony_ci        renameCount = num;
201bea4f105Sopenharmony_ci      }
202bea4f105Sopenharmony_ci    }
203bea4f105Sopenharmony_ci
204bea4f105Sopenharmony_ci    let newName = fileName;
205bea4f105Sopenharmony_ci    while (true) {
206bea4f105Sopenharmony_ci      newName = FileUtil.renameFile(name, renameCount++, suffix);
207bea4f105Sopenharmony_ci      let index = this.getIndex(newName, fileNameList);
208bea4f105Sopenharmony_ci      Logger.i(TAG, 'tryRenameFileOperate : ' + newName + ' ; index = ' + index);
209bea4f105Sopenharmony_ci      if (index === -1) {
210bea4f105Sopenharmony_ci        const result = await FileUtil.createFile(fileAccessHelper, dirUri, newName);
211bea4f105Sopenharmony_ci        if (ObjectUtil.isUndefined(result.err)) {
212bea4f105Sopenharmony_ci          Logger.i(TAG, 'tryRenameFileOperate createOK: ' + result.uri);
213bea4f105Sopenharmony_ci          return result;
214bea4f105Sopenharmony_ci        } else {
215bea4f105Sopenharmony_ci          Logger.i(TAG, 'tryRenameFileOperate createFail: ' + JSON.stringify(result) + ' ; ' + newName);
216bea4f105Sopenharmony_ci          if (result.err.code === ErrorCodeConst.FILE_ACCESS.FILE_NAME_EXIST) {
217bea4f105Sopenharmony_ci            fileNameList.push(newName);
218bea4f105Sopenharmony_ci          } else {
219bea4f105Sopenharmony_ci            return result;
220bea4f105Sopenharmony_ci          }
221bea4f105Sopenharmony_ci        }
222bea4f105Sopenharmony_ci      }
223bea4f105Sopenharmony_ci    }
224bea4f105Sopenharmony_ci  }
225bea4f105Sopenharmony_ci
226bea4f105Sopenharmony_ci  private getIndex(fileName: string, fileNameList: string[] = []) {
227bea4f105Sopenharmony_ci    return fileNameList.findIndex(value => value === fileName);
228bea4f105Sopenharmony_ci  }
229bea4f105Sopenharmony_ci
230bea4f105Sopenharmony_ci  build() {
231bea4f105Sopenharmony_ci    if (this.startModeOptions.isUxt()) {
232bea4f105Sopenharmony_ci      Column() {
233bea4f105Sopenharmony_ci      }.bindSheet(true, this.mainContent(), {
234bea4f105Sopenharmony_ci        height: '95%',
235bea4f105Sopenharmony_ci        dragBar: false,
236bea4f105Sopenharmony_ci        showClose: false,
237bea4f105Sopenharmony_ci        preferType: SheetType.CENTER,
238bea4f105Sopenharmony_ci        onAppear: () => {
239bea4f105Sopenharmony_ci        },
240bea4f105Sopenharmony_ci        shouldDismiss: () => {
241bea4f105Sopenharmony_ci          this.startModeOptions.session.terminateSelf();
242bea4f105Sopenharmony_ci        }
243bea4f105Sopenharmony_ci      })
244bea4f105Sopenharmony_ci    } else {
245bea4f105Sopenharmony_ci      this.mainContent()
246bea4f105Sopenharmony_ci    }
247bea4f105Sopenharmony_ci  }
248bea4f105Sopenharmony_ci
249bea4f105Sopenharmony_ci  @Builder
250bea4f105Sopenharmony_ci  mainContent() {
251bea4f105Sopenharmony_ci    Row() {
252bea4f105Sopenharmony_ci      fileTree({
253bea4f105Sopenharmony_ci        startModeOptions: this.startModeOptions,
254bea4f105Sopenharmony_ci        createFileFailType: $createResultType,
255bea4f105Sopenharmony_ci        moveCallback: (e) => {
256bea4f105Sopenharmony_ci          this.saveFileCallback(e, this.startModeOptions);
257bea4f105Sopenharmony_ci        }
258bea4f105Sopenharmony_ci      })
259bea4f105Sopenharmony_ci    }
260bea4f105Sopenharmony_ci  }
261bea4f105Sopenharmony_ci}
262