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}