1/* 2* Copyright (c) 2023 Hunan OpenValley Digital Industry Development 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 common from '@ohos.app.ability.common' 16import request from '@ohos.request'; 17import fs from '@ohos.file.fs'; 18import emitter from '@ohos.events.emitter' 19import CostTimeCompute from '../feature/CostTimeCompute' 20import Logger from '../util/Logger' 21 22@Preview 23@Component 24export default struct DownloadController { 25 @StorageProp('currentBreakpoint') curBp: string = 'sm' 26 fileName: string = "" 27 downloadUrl: string = ""; 28 downloadTask: request.DownloadTask = null; 29 downloadPath: string = null; 30 @State isDownloading: boolean = false; 31 @State isCompleted: boolean = false; 32 @State isPausing: boolean = false; 33 @State progress: number = 0; 34 @State total: number = 1; 35 @State speed: string = "0B/s"; 36 // 剩余时间计算 37 @State lastTimeStr: string = ""; 38 @State isError: boolean = false; 39 costTimeCompute: CostTimeCompute = null; 40 41 aboutToAppear() { 42 Logger.debug("register fileName = " + this.fileName); 43 //定义计算器 44 this.costTimeCompute = new CostTimeCompute( 45 (str) => { 46 this.lastTimeStr = str; 47 }, 48 () => { 49 return this.progress; 50 }, 51 () => { 52 return this.total; 53 }, 54 null, 55 false, 56 (str) => { 57 this.speed = str; 58 } 59 ); 60 this.getDownloadPath(); 61 fs.access(this.downloadPath) 62 .then(isExist => { 63 if (isExist) { 64 this.isCompleted = true; 65 } 66 //下载监听,在文件检测之后,再注册 67 this.addEventListener(); 68 }).catch(err => { 69 Logger.error(err) 70 }) 71 } 72 73 addEventListener() { 74 emitter.on({ eventId: 2 }, () => { 75 this.startDownload(); 76 }) 77 } 78 79 aboutToDisappear() { 80 this.costTimeCompute.cancelCostTime(); 81 this.cancel(); 82 } 83 84 getDownloadPath() { 85 if (this.downloadPath == null) { 86 let context: common.Context = getContext(this) as common.Context; 87 this.downloadPath = context.cacheDir + "/" + this.fileName; 88 Logger.debug("download file path =" + this.downloadPath) 89 } 90 } 91 92 restart() { 93 this.cancel(true); 94 } 95 96 async startDownload() { 97 if (this.isDownloading || this.isCompleted) { 98 return; 99 } 100 this.isError = false; 101 let context: common.Context = getContext(this) as common.Context; 102 this.getDownloadPath(); 103 try { 104 this.downloadTask = await request.downloadFile(context, { url: this.downloadUrl, filePath: this.downloadPath }) 105 this.isDownloading = true; 106 this.addListener(); 107 108 this.costTimeCompute.startCostTime(); 109 } catch (err) { 110 Logger.debug("start download failed,err = " + err) 111 this.onFail(); 112 } 113 } 114 115 pauseOrPlay() { 116 if (this.downloadTask == null || !this.isDownloading) { 117 return; 118 } 119 if (this.isPausing) { 120 this.downloadTask.restore(() => { 121 this.isPausing = false; 122 }); 123 this.costTimeCompute.startCostTime(); 124 } else { 125 this.downloadTask.suspend(() => { 126 this.isPausing = true; 127 }) 128 this.costTimeCompute.pauseCostTime(); 129 } 130 } 131 132 cancel(restart: boolean = false) { 133 if (this.downloadTask == null || this.isCompleted) { 134 return; 135 } 136 this.isPausing = false; 137 this.isError = false; 138 this.downloadTask.delete(() => { 139 this.costTimeCompute.cancelCostTime(); 140 let isExist = fs.accessSync(this.downloadPath); 141 if (isExist) { 142 fs.unlink(this.downloadPath).then(() => { 143 this.reset(restart); 144 }) 145 } else { 146 this.reset(restart); 147 } 148 }) 149 } 150 151 reset(restart: boolean) { 152 this.isDownloading = false; 153 this.progress = 0; 154 this.total = 1; 155 if (restart) { 156 setTimeout(() => this.startDownload(), 600); 157 } 158 } 159 160 addListener() { 161 if (this.downloadTask == null) { 162 return; 163 } 164 this.downloadTask.on("progress", (receivedSize, totalSize) => { 165 this.progress = receivedSize; 166 this.total = totalSize; 167 }) 168 this.downloadTask.on('complete', () => { 169 this.isDownloading = false; 170 this.isCompleted = true; 171 this.costTimeCompute.cancelCostTime(); 172 }) 173 this.downloadTask.on('fail', err => { 174 Logger.error("下载失败:" + err) 175 this.onFail(); 176 }) 177 } 178 179 private onFail() { 180 this.cancel(); 181 this.isError = true; 182 } 183 184 getTimeLastTip() { 185 let context: common.Context = getContext(this) as common.Context; 186 let lastTime = context.resourceManager.getStringSync($r('app.string.tip_last_time')); 187 let computingStr = context.resourceManager.getStringSync($r('app.string.tip_computing')); 188 return lastTime + ((this.lastTimeStr != null && this.lastTimeStr.length > 0) ? this.lastTimeStr : computingStr); 189 } 190 191 getProgressPercent(): string { 192 switch (this.curBp) { 193 case 'lg': 194 return "80%"; 195 case 'md': 196 case 'xs': 197 case 'sm': 198 default: 199 return "65%" 200 } 201 } 202 203 build() { 204 if (this.isDownloading || this.isError || this.isCompleted) { 205 Row() { 206 Column({ space: 10 }) { 207 Text(this.fileName) 208 .fontSize($r('app.float.download_file_name_font_size')) 209 if (!this.isCompleted) { 210 Progress({ value: this.progress, total: this.total, type: ProgressType.Capsule }) 211 .height($r('app.float.download_progress_height')) 212 .width('100%') 213 .align(Alignment.Center) 214 } 215 if (this.isPausing) { 216 Text($r("app.string.tip_paused")) 217 .fontSize($r('app.float.download_tip_font_size')) 218 .fontColor($r('app.color.download_font_color')) 219 } else { 220 if (this.isError || this.isCompleted) { 221 Text(this.isCompleted ? $r('app.string.download_success') : $r('app.string.download_fail')) 222 .fontColor(this.isCompleted ? $r('app.color.download_font_color') : $r('app.color.upload_error_color')) 223 .fontSize($r('app.float.download_tip_font_size')) 224 } else { 225 Row() { 226 Text(this.speed) 227 .fontColor($r('app.color.download_font_color')) 228 .fontSize($r('app.float.download_tip_font_size')) 229 Blank() 230 Text(this.getTimeLastTip()) 231 .fontColor($r('app.color.download_font_color')) 232 .fontSize($r('app.float.download_tip_font_size')) 233 }.width('100%') 234 } 235 } 236 } 237 .width(this.getProgressPercent()) 238 .alignItems(HorizontalAlign.Start) 239 240 Blank() 241 if (!this.isCompleted) { 242 Row({ space: 15 }) { 243 if (this.isError) { 244 Image($r("app.media.ic_public_reset")) 245 .width($r('app.float.download_icon_size')) 246 .aspectRatio(1) 247 .onClick(() => { 248 this.restart(); 249 }) 250 } else { 251 Image(this.isPausing ? $r("app.media.ic_public_play_norm") : $r("app.media.ic_public_pause_norm")) 252 .width($r('app.float.download_icon_size')) 253 .aspectRatio(1) 254 .onClick(() => { 255 this.pauseOrPlay(); 256 }) 257 } 258 Image($r("app.media.ic_public_close")) 259 .width($r('app.float.download_icon_size')) 260 .aspectRatio(1) 261 .onClick(() => { 262 this.cancel(); 263 }) 264 } 265 } 266 }.width("100%") 267 .height('100%') 268 .alignItems(VerticalAlign.Center) 269 } else { 270 Row() { 271 Text(this.fileName) 272 .fontSize($r('app.float.download_file_name_font_size')) 273 Blank() 274 Image($r("app.media.ic_public_download")) 275 .onClick(() => { 276 this.startDownload() 277 }) 278 .width($r('app.float.download_icon_size')) 279 .aspectRatio(1) 280 } 281 .width('100%') 282 .height('100%') 283 .alignItems(VerticalAlign.Center) 284 } 285 } 286}