• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1/*
2 * Copyright (c) 2021-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 { BreadData, FilesData, FileDataSource } from '../../../databases/model/FileData';
17import { TopBar } from '../../component/common/TopBar';
18import { BreadCrumb } from '../../component/myphone/BreadCrumb';
19import { FilesList } from '../../component/myphone/FilesList';
20import { setSystemBar, setImmersion } from '../../../base/utils/Common';
21import router from '@ohos.router';
22import FileAccessExec from '../../../base/utils/FileAccessExec';
23import { SystemBarColor } from '../../../base/constants/UiConstant';
24import { getResourceString } from '../../../base/utils/Tools';
25import { TopOperateBar } from '../../component/common/TopOperateBar';
26import { FileMkdirDialog } from '../../component/dialog/FileMkdirDialog';
27import { Loading } from '../../component/common/Loading';
28import { getMediaType, getDurationByUri } from '../../../databases/model/FileAssetModel';
29import Logger from '../../../base/log/Logger';
30import multimedia_image from '@ohos.multimedia.image';
31import AbilityCommonUtil, { ResultCodePicker } from '../../../base/utils/AbilityCommonUtil';
32import ObjectUtil from '../../../base/utils/ObjectUtil';
33import StringUtil from '../../../base/utils/StringUtil';
34import { FileUtil } from '../../../base/utils/FileUtil';
35import fileAccess from '@ohos.file.fileAccess';
36import { StartModeOptions } from '../../../base/model/StartModeOptions';
37import { FilePickerUtil } from '../../../base/utils/FilePickerUtil';
38import { photoAccessHelper } from '@kit.MediaLibraryKit';
39
40const TAG = 'myPhone';
41let storage = LocalStorage.getShared();
42
43@Entry(storage)
44@Component
45struct MyPhone {
46  /**
47   * 正在加载
48   */
49  private startModeOptions: StartModeOptions = FilePickerUtil.getStartOptionsFromStorage();
50  @State isShowLoading: boolean = true;
51  /**
52   * 文件或文件夹数据
53   */
54  @State fileListSource: FileDataSource = new FileDataSource();
55  /**
56   * 面包屑
57   */
58  @State @Watch('onDireListChange') direList: BreadData[] = [];
59  @State selectFilesList: FilesData[] = [];
60  @State isMulti: boolean = false;
61  @State selectAll: boolean = false;
62  @State @Watch('checkedNumChange') checkedNum: number = 0;
63  @Provide isList: boolean = true;
64  @State selectName: string = '';
65  @State fileSize: number = 0;
66  fileMkdirDialog: CustomDialogController = new CustomDialogController({
67    builder: FileMkdirDialog({
68      fileItems: this.fileListSource.getDataArray(),
69      getCurrentDir: this.getCurrentDirUri(),
70      confirm: this.refreshData.bind(this)
71    }),
72    autoCancel: true,
73    alignment: DialogAlignment.Bottom,
74    offset: { dx: 0, dy: -80 }
75  });
76
77  checkedNumChange(): void {
78    this.selectAll = this.checkedNum === this.fileListSource.totalCount();
79    this.selectFilesList = this.fileListSource.getSelectedFileList();
80    this.fileSize = 0;
81    this.selectFilesList.forEach(item => {
82      this.fileSize += item.size;
83    });
84  }
85
86  getCurrentDirUri(): string {
87    if (this.direList.length) {
88      const lastBread = this.direList[this.direList.length-1];
89      return lastBread.url;
90    } else if (globalThis.documentInfo) {
91      return globalThis.documentInfo.uri;
92    } else {
93      Logger.e(TAG, 'currentDir uri is null');
94      return '';
95    }
96  }
97
98  async getBreadCrumb(data: string): Promise<void> {
99    if (!data) {
100      data = '';
101    }
102    if (FileUtil.isUriPath(data)) {
103      let fileHelper = await FileUtil.getFileAccessHelperAsync(globalThis.abilityContext);
104      // 将uri转换成相对路径
105      let curFileInfo: fileAccess.FileInfo = await FileUtil.getFileInfoByUri(data, fileHelper);
106      if (!ObjectUtil.isNullOrUndefined(curFileInfo)) {
107        data = FileUtil.getCurrentDir(curFileInfo.relativePath, FileUtil.isFolder(curFileInfo.mode));
108      }
109    }
110    data = FileUtil.getPathWithFileSplit(data);
111
112    let fileIterator: fileAccess.FileInfo | fileAccess.RootInfo | undefined = undefined;
113    let fileData: FilesData[];
114    let isContinue: boolean = true;
115    let isRoot: boolean = true;
116    while (isContinue) {
117      isContinue = false;
118      if (!fileIterator) {
119        fileData = FileAccessExec.getRootFolder();
120        isRoot = true;
121      } else {
122        fileData = FileAccessExec.getFileByCurIterator(fileIterator);
123        isRoot = false;
124      }
125      if (Array.isArray(fileData)) {
126        isContinue = true;
127        for (let i = 0; i < fileData.length; i++) {
128          let fileName: string = fileData[i].fileName;
129          let currentDir: string = FileUtil.getPathWithFileSplit(fileData[i].currentDir);
130          if (data.startsWith(currentDir)) {
131            if (fileData[i].isFolder) {
132              let bread: BreadData = { title: fileName, url: fileData[i].uri, fileIterator: fileData[i].fileIterator }
133              this.direList.push(bread);
134              fileIterator = fileData[i].fileIterator;
135              if (data === currentDir) {
136                isContinue = false;
137              } else {
138                break;
139              }
140            }
141          }
142        }
143      }
144      if (isRoot && !fileIterator) {
145        isContinue = false;
146      }
147    }
148    if (fileIterator) {
149      fileData = FileAccessExec.getFileByCurIterator(fileIterator);
150      this.fileListSource.setData(fileData);
151    } else {
152      this.getRootListFile();
153    }
154  }
155
156  onPageShow() {
157    // 文件选择器并且是多选模式下详情返回不更新,避免原有多选被重置
158    if (this.isMulti) {
159      return;
160    }
161    setImmersion(false);
162    setSystemBar(SystemBarColor.WHITE, SystemBarColor.WHITE, SystemBarColor.BLACK, SystemBarColor.BLACK);
163  }
164
165  getRootListFile() {
166    let fileList = FileAccessExec.getRootFolder();
167    this.fileListSource.setData(fileList);
168    this.getVideoAudioDuration(fileList);
169  }
170
171  getListFile(fileInfo: fileAccess.FileInfo | fileAccess.RootInfo) {
172    let fileList = FileAccessExec.getFileByCurIterator(fileInfo);
173    this.fileListSource.setData(fileList);
174    this.getVideoAudioDuration(fileList);
175  }
176
177  async getVideoAudioDuration(fileList: FilesData[]) {
178    const videoAudioList = fileList.filter(item => item.mimeTypeObj?.isVideo() || item.mimeTypeObj?.isAudio());
179    for (let item of videoAudioList) {
180      const mediaType: photoAccessHelper.PhotoType = getMediaType(item.fileName);
181      await getDurationByUri(mediaType, item.uri).then((duration) => {
182        item.duration = duration;
183        let index = this.fileListSource.getIndex(item.uri);
184        if (index >= 0) {
185          this.fileListSource.replaceData(index, item);
186        }
187      })
188    }
189  }
190
191  addCallBack() {
192    this.fileMkdirDialog.open();
193  }
194
195  initData(): void {
196    this.selectFilesList = [];
197    this.selectAll = false;
198    this.isMulti = false;
199    this.checkedNum = 0;
200    // 全部数据列表的isChecked置为false
201    this.fileListSource.selectAll(false);
202  }
203
204  backCallback(): void {
205    if (!this.isMulti) {
206      AbilityCommonUtil.terminateFilePicker([], ResultCodePicker.SUCCESS, this.startModeOptions);
207    } else {
208      this.initData();
209    }
210  }
211
212  menuCallback(): void {
213    this.selectAll = !this.selectAll;
214    this.fileListSource.selectAll(this.selectAll);
215    if (this.selectAll) {
216      this.checkedNum = this.fileListSource.totalCount();
217    } else {
218      this.checkedNum = 0;
219      this.selectFilesList = [];
220    }
221  }
222
223  refreshData() {
224    if (this.direList.length) {
225      const lastBread = this.direList[this.direList.length-1];
226      if (lastBread.fileIterator !== undefined) {
227        this.getListFile(lastBread.fileIterator);
228      }
229    } else {
230      this.getRootListFile();
231    }
232  }
233
234  aboutToAppear(): void {
235    // 激活image媒体库,能够读取缩略图pixelMap
236    multimedia_image.createPixelMap(new ArrayBuffer(4096), { size: { height: 1, width: 2 } }).then((pixelMap) => {
237    })
238    this.setShowLoading(true);
239    let pickPath = this.getParams(this.startModeOptions);
240    if (StringUtil.isEmpty(pickPath)) {
241      this.getRootListFile();
242    } else {
243      this.getBreadCrumb(pickPath);
244    }
245    this.setShowLoading(false);
246  }
247
248  aboutToDisappear() {
249  }
250
251  getParams(startModeOptions: StartModeOptions): string {
252    let defaultPickPath = startModeOptions.defaultFilePathUri;
253    if (!ObjectUtil.isNullOrUndefined(defaultPickPath)) {
254      return defaultPickPath;
255    }
256    let params = router.getParams() as Record<string, string>;
257    if (!ObjectUtil.isNullOrUndefined(params)) {
258      defaultPickPath = params.path as string;
259      if (!ObjectUtil.isNullOrUndefined(defaultPickPath)) {
260        return defaultPickPath;
261      }
262    }
263    return '';
264  }
265
266  onDireListChange(): void {
267    if (this.isMulti) {
268      this.backCallback();
269    }
270    this.setShowLoading(true);
271    if (this.direList.length) {
272      const lastBreadCrumb = this.direList[this.direList.length-1];
273      if (lastBreadCrumb.fileIterator !== undefined) {
274        this.getListFile(lastBreadCrumb.fileIterator);
275      }
276    } else {
277      this.getRootListFile();
278    }
279    this.setShowLoading(false);
280    Logger.i(TAG, 'onDireListChange BreadCrumb length:' + this.direList.length);
281  }
282
283  setShowLoading(isShow: boolean) {
284    this.isShowLoading = isShow;
285  }
286
287  onBackPress() {
288    const direList = this.direList;
289    const dirLen = this.direList.length;
290    if (this.isMulti) {
291      this.initData();
292      return true;
293    } else {
294      if (router.getParams()) {
295        router.back();
296      } else {
297        if (dirLen) {
298          direList.splice(-1, 1);
299          if (direList.length) {
300            const lastDir = direList[direList.length-1];
301            if (lastDir.fileIterator !== undefined) {
302              this.getListFile(lastDir.fileIterator);
303            }
304          } else {
305            this.getRootListFile();
306          }
307        } else {
308          AbilityCommonUtil.terminateFilePicker([], ResultCodePicker.CANCEL, this.startModeOptions);
309        }
310      }
311      return true;
312    }
313  }
314
315  build() {
316    if (this.startModeOptions.isUxt()) {
317      Column() {
318      }.bindSheet(true, this.mainContent(), {
319        height: '95%',
320        dragBar: false,
321        showClose: false,
322        preferType: SheetType.CENTER,
323        onAppear: () => {
324        },
325        shouldDismiss: () => {
326          this.startModeOptions.session.terminateSelf();
327        }
328      })
329    } else {
330      this.mainContent()
331    }
332  }
333
334  @Builder
335  mainContent() {
336    Column() {
337      // 头部导航
338      TopBar({
339        startModeOptions: this.startModeOptions,
340        title: getResourceString($r('app.string.myPhone')),
341        isMulti: this.isMulti,
342        selectAll: this.selectAll,
343        checkedNum: $checkedNum,
344        checkedList: $selectFilesList,
345        backCallback: this.backCallback.bind(this),
346        menuCallback: this.menuCallback.bind(this)
347      })
348      // 面包屑
349      BreadCrumb({
350        direList: $direList
351      })
352
353      TopOperateBar({
354        isDisabled: this.isMulti,
355        addFolder: this.addCallBack.bind(this)
356      })
357
358      Column() {
359        Loading({ isLoading: !!this.isShowLoading })
360        if (!this.isShowLoading) {
361          // 文件列表
362          FilesList({
363            startModeOptions: this.startModeOptions,
364            fileListSource: $fileListSource,
365            direList: $direList,
366            isMulti: $isMulti,
367            checkedNum: $checkedNum
368          })
369        }
370      }.layoutWeight(1)
371    }
372    .width('100%')
373  }
374}