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, { 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; 113 let fileData; 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 this.direList.push({ title: fileName, url: fileData[i].uri, fileIterator: fileData[i].fileIterator }); 133 fileIterator = fileData[i].fileIterator; 134 if (data === currentDir) { 135 isContinue = false; 136 } else { 137 break; 138 } 139 } 140 } 141 } 142 } 143 if (isRoot && !fileIterator) { 144 isContinue = false; 145 } 146 } 147 if (fileIterator) { 148 fileData = FileAccessExec.getFileByCurIterator(fileIterator); 149 this.fileListSource.setData(fileData); 150 } else { 151 this.getRootListFile(); 152 } 153 } 154 155 onPageShow() { 156 // 文件选择器并且是多选模式下详情返回不更新,避免原有多选被重置 157 if (this.isMulti) { 158 return; 159 } 160 setImmersion(false); 161 setSystemBar(SYSTEM_BAR_COLOR.WHITE, SYSTEM_BAR_COLOR.WHITE, SYSTEM_BAR_COLOR.BLACK, SYSTEM_BAR_COLOR.BLACK); 162 } 163 164 getRootListFile() { 165 let fileList = FileAccessExec.getRootFolder(); 166 this.fileListSource.setData(fileList); 167 this.getVideoAudioDuration(fileList); 168 } 169 170 getListFile(fileInfo) { 171 let fileList = FileAccessExec.getFileByCurIterator(fileInfo); 172 this.fileListSource.setData(fileList); 173 this.getVideoAudioDuration(fileList); 174 } 175 176 async getVideoAudioDuration(fileList: FilesData[]) { 177 const videoAudioList = fileList.filter(item => item.mimeTypeObj.isVideo() || item.mimeTypeObj.isAudio()); 178 for (let item of videoAudioList) { 179 const mediaType: photoAccessHelper.PhotoType = getMediaType(item.fileName); 180 await getDurationByUri(mediaType, item.uri).then((duration) => { 181 item.duration = duration; 182 let index = this.fileListSource.getIndex(item.uri); 183 if (index >= 0) { 184 this.fileListSource.replaceData(index, item); 185 } 186 }) 187 } 188 } 189 190 addCallBack() { 191 this.fileMkdirDialog.open(); 192 } 193 194 initData(): void { 195 this.selectFilesList = []; 196 this.selectAll = false; 197 this.isMulti = false; 198 this.checkedNum = 0; 199 // 全部数据列表的isChecked置为false 200 this.fileListSource.selectAll(false); 201 } 202 203 backCallback(): void { 204 if (!this.isMulti) { 205 AbilityCommonUtil.terminateFilePicker([], ResultCodePicker.SUCCESS, this.startModeOptions); 206 } else { 207 this.initData(); 208 } 209 } 210 211 menuCallback(): void { 212 this.selectAll = !this.selectAll; 213 this.fileListSource.selectAll(this.selectAll); 214 if (this.selectAll) { 215 this.checkedNum = this.fileListSource.totalCount(); 216 } else { 217 this.checkedNum = 0; 218 this.selectFilesList = []; 219 } 220 } 221 222 refreshData() { 223 if (this.direList.length) { 224 const lastBread = this.direList[this.direList.length-1]; 225 this.getListFile(lastBread.fileIterator); 226 } else { 227 this.getRootListFile(); 228 } 229 } 230 231 aboutToAppear(): void { 232 // 激活image媒体库,能够读取缩略图pixelMap 233 multimedia_image.createPixelMap(new ArrayBuffer(4096), { size: { height: 1, width: 2 } }).then((pixelMap) => { 234 }) 235 this.setShowLoading(true); 236 let pickPath = this.getParams(this.startModeOptions); 237 if (StringUtil.isEmpty(pickPath)) { 238 this.getRootListFile(); 239 } else { 240 this.getBreadCrumb(pickPath); 241 } 242 this.setShowLoading(false); 243 } 244 245 aboutToDisappear() { 246 this.fileMkdirDialog = null; 247 } 248 249 getParams(startModeOptions: StartModeOptions): string { 250 let defaultPickPath = startModeOptions.defaultFilePathUri; 251 if (!ObjectUtil.isNullOrUndefined(defaultPickPath)) { 252 return defaultPickPath; 253 } 254 let params = router.getParams(); 255 if (!ObjectUtil.isNullOrUndefined(params)) { 256 defaultPickPath = params['path']; 257 if (!ObjectUtil.isNullOrUndefined(defaultPickPath)) { 258 return defaultPickPath; 259 } 260 } 261 return ''; 262 } 263 264 onDireListChange(): void { 265 if (this.isMulti) { 266 this.backCallback(); 267 } 268 this.setShowLoading(true); 269 if (this.direList.length) { 270 const lastBreadCrumb = this.direList[this.direList.length-1]; 271 this.getListFile(lastBreadCrumb.fileIterator); 272 } else { 273 this.getRootListFile(); 274 } 275 this.setShowLoading(false); 276 Logger.i(TAG, 'onDireListChange BreadCrumb length:' + this.direList.length); 277 } 278 279 setShowLoading(isShow: boolean) { 280 this.isShowLoading = isShow; 281 } 282 283 onBackPress() { 284 const direList = this.direList; 285 const dirLen = this.direList.length; 286 if (this.isMulti) { 287 this.initData(); 288 return true; 289 } else { 290 if (router.getParams()) { 291 router.back(); 292 } else { 293 if (dirLen) { 294 direList.splice(-1, 1); 295 if (direList.length) { 296 const lastDir = direList[direList.length-1]; 297 this.getListFile(lastDir.fileIterator); 298 } else { 299 this.getRootListFile(); 300 } 301 } else { 302 AbilityCommonUtil.terminateFilePicker([], ResultCodePicker.CANCEL, this.startModeOptions); 303 } 304 } 305 return true; 306 } 307 } 308 309 build() { 310 if (this.startModeOptions.isUxt()) { 311 Column() { 312 }.bindSheet(true, this.mainContent(), { 313 height: '95%', 314 dragBar: false, 315 showClose: false, 316 preferType: SheetType.CENTER, 317 onAppear: () => { 318 }, 319 shouldDismiss: () => { 320 this.startModeOptions.session.terminateSelf(); 321 } 322 }) 323 } else { 324 this.mainContent() 325 } 326 } 327 328 @Builder 329 mainContent() { 330 Column() { 331 // 头部导航 332 TopBar({ 333 startModeOptions: this.startModeOptions, 334 title: getResourceString($r('app.string.myPhone')), 335 isMulti: this.isMulti, 336 selectAll: this.selectAll, 337 checkedNum: $checkedNum, 338 checkedList: $selectFilesList, 339 backCallback: this.backCallback.bind(this), 340 menuCallback: this.menuCallback.bind(this) 341 }) 342 // 面包屑 343 BreadCrumb({ 344 direList: $direList 345 }) 346 347 TopOperateBar({ 348 isDisabled: this.isMulti, 349 addFolder: this.addCallBack.bind(this) 350 }) 351 352 Column() { 353 Loading({ isLoading: !!this.isShowLoading }) 354 if (!this.isShowLoading) { 355 // 文件列表 356 FilesList({ 357 startModeOptions: this.startModeOptions, 358 fileListSource: $fileListSource, 359 direList: $direList, 360 isMulti: $isMulti, 361 checkedNum: $checkedNum 362 }) 363 } 364 }.layoutWeight(1) 365 } 366 .width('100%') 367 } 368}