1/* 2 * Copyright (c) 2022 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 */ 15import MediaLib from '@ohos.multimedia.mediaLibrary'; 16import type { AsyncCallback } from '../interface/AsyncCallback'; 17import { Log } from '../utils/Log'; 18import type { MenuOperationCallback } from './MenuOperationCallback'; 19import type { MenuOperation } from './MenuOperation'; 20import { MenuContext } from './MenuContext'; 21import { BroadcastConstants } from '../constants/BroadcastConstants'; 22import { startTraceWithTaskId, finishTraceWithTaskId } from '../utils/TraceControllerUtils'; 23import { getFetchOptionsByItem } from '../helper/MediaDataHelper'; 24import { SimpleAlbumDataItem } from '../data/SimpleAlbumDataItem'; 25import mediaModel from '../model/MediaModel'; 26 27export enum FindSameOperation { 28 NONE, 29 REPLACE, 30 SKIP 31} 32 33const TAG = "ProcessMenuOperation" 34 35export class ProcessMenuOperation implements MenuOperation, AsyncCallback<String[]>, MenuOperationCallback { 36 // Number of data operated in a batch 37 readonly BATCH_SIZE: number = 1; 38 39 // Maximum progress 40 readonly MAX_PROGRESS: number = 100; 41 items: any[] = []; 42 menuContext: MenuContext; 43 uris: string[]; 44 count: number; 45 onOperationEnd: Function; 46 47 // Total batches operated 48 totalBatches: number; 49 50 // Currently operated batch 51 currentBatch: number = 0; 52 successBatch: number = 0; 53 isCancelled: boolean = false; 54 startTime: number; 55 isPause: boolean = false; 56 isError: boolean = false; 57 findSameOperation: number = FindSameOperation.NONE; 58 requestTime: number; 59 60 constructor(menuContext: MenuContext) { 61 this.menuContext = menuContext; 62 this.requestTime = Date.now(); 63 } 64 65 doAction(): void { 66 } 67 68 // Asynchronous callback for getSelection 69 callback(uris: string[]): void { 70 71 } 72 73 onCompleted(): void { 74 Log.info(TAG, `onCompleted ${this.isPause}`); 75 this.successBatch++; 76 if (!this.isPause) { 77 this.cyclicOperation(); 78 } 79 } 80 81 onError(): void { 82 Log.error(TAG, `Operate the ${this.currentBatch} batch data error, total ${this.totalBatches} batches`); 83 this.isError = true; 84 this.cyclicOperation(); 85 } 86 87 // Start processing operation 88 processOperation(): void { 89 Log.info(TAG, 'processOperation start'); 90 startTraceWithTaskId('ProgressOperation', this.requestTime); 91 let length = this.items.length; 92 Log.info(TAG, `selected count: ${this.count}, uris's length: ${length}`); 93 // Batch deletion 94 this.totalBatches = Math.floor(length / this.BATCH_SIZE) + ((length % this.BATCH_SIZE) ? 1 : 0); 95 Log.info(TAG, `The count to be operate is ${length}, operate in ${this.totalBatches} batches`); 96 if (this.isCancelled) { 97 this.isCancelled = false; 98 } 99 this.startTime = Date.now(); 100 101 this.requestOneBatchOperation() 102 } 103 104 // Batch circular deletion 105 cyclicOperation() { 106 Log.info(TAG, 'cyclicOperation'); 107 this.menuContext.broadCast.emit(BroadcastConstants.UPDATE_PROGRESS, [this.getExpectProgress(), this.currentBatch]); 108 109 if (this.isCancelled) { 110 this.onProcessDone(); 111 } 112 113 if (this.currentBatch >= this.totalBatches || this.isError) { 114 this.onProcessDone(); 115 } else { 116 this.requestOneBatchOperation(); 117 } 118 } 119 120 // Operate a batch of data 121 requestOneBatchOperation(): void { 122 } 123 124 onProcessDone(): void { 125 this.menuContext.broadCast.emit(BroadcastConstants.UPDATE_PROGRESS, [100]); 126 this.findSameOperation = FindSameOperation.NONE; 127 if (this.startTime != null) { 128 let operateCount = this.currentBatch >= this.totalBatches ? this.count : this.currentBatch * this.BATCH_SIZE; 129 let costTime = Date.now() - this.startTime; 130 Log.debug(TAG, `process data operate done, operate ${operateCount} items, cost time ${costTime} ms,\ 131 average ${(costTime / operateCount)} ms/item.`); 132 } 133 this.isCancelled = false; 134 finishTraceWithTaskId('ProgressOperation', this.requestTime); 135 this.onOperationEnd && this.onOperationEnd(this.isError, this.successBatch, this.count); 136 } 137 138 // Operate cancel callback 139 onOperateCancelled(): void { 140 Log.info(TAG, 'Operate Cancel'); 141 this.isCancelled = true; 142 this.onProcessDone(); 143 } 144 145 // Operate cancel callback 146 onOperatePause(): void { 147 Log.info(TAG, 'Operate Pause'); 148 this.isPause = true; 149 } 150 151 // Calculate the operation progress according to the batch 152 getExpectProgress(): number { 153 Log.info(TAG, 'getExpectProgress'); 154 let progress = Math.min( 155 Math.floor(this.MAX_PROGRESS * this.currentBatch * this.BATCH_SIZE / this.count), this.MAX_PROGRESS); 156 return progress; 157 } 158 159 setFindSameOperation(newOperation: number): void { 160 Log.info(TAG, `setFindSameOperation ${newOperation}`); 161 this.findSameOperation = newOperation; 162 } 163 164 async getFileCopyOrMoveInfo(fileAsset: MediaLib.FileAsset, albumInfo: SimpleAlbumDataItem) { 165 Log.debug(TAG, 'getFileCopyOrMoveInfo start'); 166 let item: SimpleAlbumDataItem = new SimpleAlbumDataItem("", fileAsset.displayName, albumInfo.relativePath, "", ""); 167 let fetchOptions = await getFetchOptionsByItem(item); 168 let targetAsset = (await mediaModel.getAllCommonMediaItem(fetchOptions, false)).fileAsset; 169 if (targetAsset == null || targetAsset == undefined) { 170 Log.debug(TAG, 'targetAsset not found'); 171 } 172 173 return { sourceAsset: fileAsset, targetAsset: targetAsset }; 174 } 175}