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