1/* 2 * Copyright (c) 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 type common from '@ohos.app.ability.common'; 17import update from '@ohos.update'; 18import { ErrorCode, OtaStatus, UpdateState } from '@ohos/common/src/main/ets/const/update_const'; 19import { LogUtils } from '@ohos/common/src/main/ets/util/LogUtils'; 20import { DialogUtils } from '../dialog/DialogUtils'; 21import { OtaUpdateManager } from '../manager/OtaUpdateManager'; 22import VersionUtils from '../util/VersionUtils'; 23import ToastUtils from '../util/ToastUtils'; 24import { UpgradeAdapter } from '../UpgradeAdapter'; 25import { NotificationManager } from '../notify/NotificationManager'; 26 27/** 28 * 状态工厂 29 * 30 * @since 2022-06-10 31 */ 32export namespace StateManager { 33 /** 34 * 是否允许执行该升级行为 35 * 36 * @param status 状态 37 * @param action 行为 38 * @return 是否允许 39 */ 40 export function isAllowExecute(status: number, action: UpdateAction): boolean { 41 let stateObj: BaseState = OtaUpdateManager.getInstance().getStateObj(status); 42 return stateObj.actionSet.indexOf(action) != -1; 43 } 44 45 /** 46 * 取下载状态描述 47 * 48 * @param status 状态 49 * @return 下载状态描述 50 */ 51 export function getDownloadStateText(status: number): string | Resource { 52 return OtaUpdateManager.getInstance().getStateObj(status).downloadStateText; 53 } 54 55 /** 56 * 取按钮文字 57 * 58 * @param status 状态 59 * @return 按钮文字 60 */ 61 export function getButtonText(status: number): string | Resource { 62 return OtaUpdateManager.getInstance().getStateObj(status).buttonText; 63 } 64 65 /** 66 * 按钮点击是否可点击 67 * 68 * @param status 状态 69 * @return 是否可点击 70 */ 71 export function isButtonEnable(status: number): boolean { 72 return OtaUpdateManager.getInstance().getStateObj(status).isButtonClickable; 73 } 74 75 /** 76 * 取按钮点击行为 77 * 78 * @param status 状态 79 * @return 按钮点击行为 80 */ 81 export function getButtonClickAction(status: number): UpdateAction { 82 return OtaUpdateManager.getInstance().getStateObj(status).buttonClickAction; 83 } 84 85 /** 86 * 创造状态实例 87 * 88 * @param status 状态 89 * @return 实例对象 90 */ 91 export function createInstance(status: OtaStatus | number): BaseState { 92 let state: number = (typeof status === 'number') ? status : status?.status; 93 let stateObject: BaseState = null; 94 switch (state) { 95 case UpdateState.DOWNLOAD_CANCEL: // fall through 96 case UpdateState.CHECK_SUCCESS: 97 stateObject = new CheckSuccess(); 98 break; 99 case UpdateState.DOWNLOADING: 100 stateObject = new Downloading(); 101 break; 102 case UpdateState.DOWNLOAD_PAUSE: 103 stateObject = new DownloadPause(); 104 break; 105 case UpdateState.DOWNLOAD_SUCCESS: 106 stateObject = new DownloadSuccess(); 107 break; 108 case UpdateState.INSTALLING: 109 stateObject = new Installing(); 110 break; 111 case UpdateState.INSTALL_FAILED: 112 stateObject = new InstallFailed(); 113 break; 114 case UpdateState.DOWNLOAD_FAILED: 115 stateObject = new DownloadFailed(); 116 break; 117 case UpdateState.INSTALL_SUCCESS: // fall through 118 stateObject = new InstallSuccess(); 119 break; 120 case UpdateState.UPGRADING: 121 stateObject = new Upgrading(); 122 break; 123 case UpdateState.UPGRADE_SUCCESS: 124 stateObject = new UpgradeSuccess(); 125 break; 126 case UpdateState.UPGRADE_FAILED: 127 stateObject = new UpgradeFailed(); 128 break; 129 default: 130 stateObject = new Init(); 131 break; 132 } 133 if (typeof status !== 'number' && status != null) { 134 stateObject.refresh(status); 135 } 136 return stateObject; 137 } 138} 139 140/** 141 * 升级行为 142 * 143 * @since 2022-06-10 144 */ 145export enum UpdateAction { 146 /** 147 * 搜索新版本 148 */ 149 CHECK_NEW_VERSION, 150 151 /** 152 * 下载 153 */ 154 DOWNLOAD, 155 156 /** 157 * 取消升级 158 */ 159 CANCEL, 160 161 /** 162 * 继续下载 163 */ 164 RESUME, 165 166 /** 167 * 安装 168 */ 169 INSTALL, 170 171 /** 172 * 重启 173 */ 174 REBOOT, 175 176 /** 177 * 显示新版本页面 178 */ 179 SHOW_NEW_VERSION, 180 181 /** 182 * 显示进度圆圈 183 */ 184 SHOW_PROCESS_VIEW 185} 186 187/** 188 * 状态基类 189 * 190 * @since 2022-06-10 191 */ 192export class BaseState { 193 /** 194 * 状态对象 195 */ 196 otaStatus: OtaStatus; 197 198 /** 199 * 进度 200 */ 201 percent: number = 0; 202 203 /** 204 * 状态 205 */ 206 state: number = UpdateState.INIT; 207 208 /** 209 * 升级行为 210 */ 211 actionSet: Array<UpdateAction> = []; 212 213 /** 214 * 下载状态描述 215 */ 216 downloadStateText: string | Resource = ""; 217 218 /** 219 * 下载安装文字描述 220 */ 221 buttonText: string | Resource = $r('app.string.btn_download'); 222 223 /** 224 * 按钮是否可点击 225 */ 226 isButtonClickable: boolean = true; 227 228 /** 229 * 按钮对应的升级行为 230 */ 231 buttonClickAction: UpdateAction; 232 233 /** 234 * 数据刷新 235 * 236 * @param otaStatus 状态 237 */ 238 refresh(otaStatus: OtaStatus): void { 239 this.otaStatus = otaStatus; 240 if (this.otaStatus?.percent) { 241 this.percent = otaStatus?.percent; 242 } 243 } 244 245 /** 246 * 提醒 247 */ 248 async notify(context?: common.Context, eventId?: update.EventId): Promise<void> { 249 } 250} 251 252/** 253 * 状态--初始状态 254 * 255 * @since 2022-06-10 256 */ 257export class Init extends BaseState { 258 constructor() { 259 super(); 260 this.actionSet.push(UpdateAction.CHECK_NEW_VERSION); 261 } 262 263 async notify(): Promise<void> { 264 await UpgradeAdapter.getInstance().getNotifyInstance()?.cancelAll(); 265 } 266} 267 268/** 269 * 状态--搜包成功 270 * 271 * @since 2022-06-10 272 */ 273export class CheckSuccess extends BaseState { 274 constructor() { 275 super(); 276 this.actionSet.push(UpdateAction.SHOW_NEW_VERSION); 277 this.actionSet.push(UpdateAction.CHECK_NEW_VERSION); 278 this.actionSet.push(UpdateAction.DOWNLOAD); 279 this.state = UpdateState.CHECK_SUCCESS; 280 this.buttonClickAction = UpdateAction.DOWNLOAD; 281 } 282 283 async notify(context?: common.Context, eventId?: update.EventId): Promise<void> { 284 if (this.otaStatus?.endReason) { 285 await UpgradeAdapter.getInstance().getNotifyInstance()?.cancelAll(); 286 switch (this.otaStatus.endReason) { 287 case ErrorCode.NETWORK_ERROR: 288 let message = await context.resourceManager.getString($r("app.string.network_err_toast").id); 289 ToastUtils.showToast(message); 290 break; 291 case ErrorCode.NO_ENOUGH_MEMORY: 292 DialogUtils.showDownloadNotEnoughSpaceDialog(context, this.otaStatus, eventId); 293 break; 294 default: 295 DialogUtils.showDownloadFailDialog(context, this.otaStatus, eventId); 296 break; 297 } 298 } 299 } 300} 301 302/** 303 * 状态--下载中 304 * 305 * @since 2022-06-10 306 */ 307export class Downloading extends BaseState { 308 constructor() { 309 super(); 310 this.actionSet.push(UpdateAction.SHOW_NEW_VERSION); 311 this.actionSet.push(UpdateAction.SHOW_PROCESS_VIEW); 312 this.state = UpdateState.DOWNLOADING; 313 this.downloadStateText = $r('app.string.download_status_downloading'); 314 this.buttonText = $r('app.string.cancel'); 315 this.buttonClickAction = UpdateAction.CANCEL; 316 } 317 318 async notify(context?: common.Context): Promise<void> { 319 if (this.percent == 100) { 320 await UpgradeAdapter.getInstance().getNotifyInstance()?.cancelAll(); 321 return; 322 } 323 if (!VersionUtils.isInNewVersionPage()) { 324 let versionName = await VersionUtils.obtainNewVersionName(globalThis.cachedNewVersionInfo); 325 await UpgradeAdapter.getInstance().getNotifyInstance()?.showDownloading(versionName, this.percent, context); 326 } 327 } 328} 329 330/** 331 * 状态--下载暂停 332 * 333 * @since 2022-06-10 334 */ 335export class DownloadPause extends BaseState { 336 constructor() { 337 super(); 338 this.actionSet.push(UpdateAction.SHOW_NEW_VERSION); 339 this.actionSet.push(UpdateAction.SHOW_PROCESS_VIEW); 340 this.actionSet.push(UpdateAction.RESUME); 341 this.state = UpdateState.DOWNLOAD_PAUSE; 342 this.downloadStateText = $r('app.string.download_status_download_pause'); 343 this.buttonText = $r('app.string.continue'); 344 this.buttonClickAction = UpdateAction.RESUME; 345 } 346 347 async notify(context?: common.Context, eventId?: update.EventId): Promise<void> { 348 if (!VersionUtils.isInNewVersionPage()) { 349 return; 350 } 351 if (this.otaStatus?.endReason) { 352 await UpgradeAdapter.getInstance().getNotifyInstance()?.cancelAll(); 353 switch (this.otaStatus?.endReason) { 354 case ErrorCode.NETWORK_ERROR: 355 if (eventId == update.EventId.EVENT_DOWNLOAD_PAUSE) { 356 DialogUtils.showDownloadNoNetworkDialog(context, this.otaStatus, eventId); 357 } else { 358 let message = await context.resourceManager.getString($r("app.string.network_err_toast").id); 359 ToastUtils.showToast(message); 360 } 361 break; 362 case ErrorCode.NETWORK_NOT_ALLOW: 363 if (eventId == update.EventId.EVENT_DOWNLOAD_PAUSE) { 364 DialogUtils.showDownloadNoNetworkDialog(context, this.otaStatus, eventId); 365 } 366 break; 367 default: 368 DialogUtils.showDownloadFailDialog(context, this.otaStatus, eventId); 369 break; 370 } 371 } 372 } 373} 374 375/** 376 * 状态--下载失败 377 * 378 * @since 2022-06-10 379 */ 380export class DownloadFailed extends BaseState { 381 constructor() { 382 super(); 383 this.actionSet.push(UpdateAction.CHECK_NEW_VERSION); 384 this.actionSet.push(UpdateAction.SHOW_PROCESS_VIEW); 385 this.state = UpdateState.DOWNLOAD_FAILED; 386 this.downloadStateText = $r('app.string.download_status_download_failed'); 387 this.isButtonClickable = false; 388 this.buttonText = $r('app.string.cancel'); 389 this.buttonClickAction = UpdateAction.CANCEL; 390 } 391 392 async notify(context?: common.Context, eventId?: update.EventId): Promise<void> { 393 await UpgradeAdapter.getInstance().getNotifyInstance()?.cancelAll(); 394 switch (this.otaStatus.endReason) { 395 case ErrorCode.VERIFY_PACKAGE_FAIL: 396 DialogUtils.showVerifyFailDialog(context, this.otaStatus, eventId); 397 break; 398 default: 399 DialogUtils.showDownloadFailDialog(context, this.otaStatus, eventId); 400 break; 401 } 402 } 403} 404 405/** 406 * 状态--下载成功 407 * 408 * @since 2022-06-10 409 */ 410export class DownloadSuccess extends BaseState { 411 constructor() { 412 super(); 413 this.actionSet.push(UpdateAction.INSTALL); 414 this.actionSet.push(UpdateAction.SHOW_NEW_VERSION); 415 this.actionSet.push(UpdateAction.SHOW_PROCESS_VIEW); 416 this.state = UpdateState.DOWNLOAD_SUCCESS; 417 this.percent = 100; 418 this.downloadStateText = $r('app.string.download_status_download_success'); 419 this.buttonText = $r('app.string.btn_upgrade'); 420 this.buttonClickAction = UpdateAction.INSTALL; 421 } 422 423 async notify(context?: common.Context, eventId?: update.EventId): Promise<void> { 424 let isABInstall = await VersionUtils.isABInstall(); 425 if (eventId == update.EventId.EVENT_DOWNLOAD_SUCCESS && isABInstall) { 426 OtaUpdateManager.getInstance().upgrade(update.Order.INSTALL); 427 return; 428 } 429 430 if (eventId == update.EventId.EVENT_UPGRADE_WAIT && !isABInstall) { 431 LogUtils.info('StateManager', 'manual download complete to count down'); 432 if (!VersionUtils.isInNewVersionPage()) { 433 NotificationManager.startToNewVersion(context); 434 } 435 AppStorage.Set('isClickInstall', 1); 436 return; 437 } 438 439 if (this.otaStatus?.endReason) { 440 switch (this.otaStatus.endReason) { 441 case ErrorCode.NO_ENOUGH_MEMORY: 442 DialogUtils.showUpgradeNotEnoughSpaceDialog(context, this.otaStatus, eventId); 443 break; 444 case ErrorCode.NO_ENOUGH_BATTERY: 445 DialogUtils.showUpgradeNotEnoughBatteryDialog(context, this.otaStatus, eventId); 446 break; 447 default: 448 DialogUtils.showUpgradeFailDialog(context, this.otaStatus, eventId); 449 break; 450 } 451 } 452 } 453} 454 455/** 456 * 状态--解压中 457 * 458 * @since 2022-06-10 459 */ 460export class Installing extends BaseState { 461 constructor() { 462 super(); 463 this.actionSet.push(UpdateAction.SHOW_NEW_VERSION); 464 this.actionSet.push(UpdateAction.SHOW_PROCESS_VIEW); 465 this.state = UpdateState.INSTALLING; 466 this.buttonText = $r('app.string.btn_upgrade') 467 this.isButtonClickable = false; 468 this.downloadStateText = $r('app.string.new_version_status_installing'); 469 } 470 471 async notify(context?: common.Context): Promise<void> { 472 if (this.percent == 100) { 473 await UpgradeAdapter.getInstance().getNotifyInstance()?.cancelAll(); 474 return; 475 } 476 if (!VersionUtils.isInNewVersionPage()) { 477 let versionName = await VersionUtils.obtainNewVersionName(globalThis.cachedNewVersionInfo); 478 await UpgradeAdapter.getInstance().getNotifyInstance()?.showInstalling(versionName, this.percent, context); 479 } 480 } 481} 482 483/** 484 * 状态--下载成功 485 * 486 * @since 2022-06-10 487 */ 488export class InstallSuccess extends BaseState { 489 constructor() { 490 super(); 491 this.actionSet.push(UpdateAction.REBOOT); 492 this.actionSet.push(UpdateAction.SHOW_NEW_VERSION); 493 this.actionSet.push(UpdateAction.SHOW_PROCESS_VIEW); 494 this.state = UpdateState.INSTALL_SUCCESS; 495 this.percent = 100; 496 this.downloadStateText = $r('app.string.new_version_status_install_success'); 497 this.buttonText = $r('app.string.btn_reboot'); 498 this.buttonClickAction = UpdateAction.REBOOT; 499 } 500 501 async notify(context?: common.Context, eventId?: update.EventId): Promise<void> { 502 if (eventId == update.EventId.EVENT_APPLY_WAIT) { 503 LogUtils.info('StateManager', 'ab install complete to count down'); 504 if (!VersionUtils.isInNewVersionPage()) { 505 NotificationManager.startToNewVersion(context); 506 } 507 AppStorage.Set('isClickInstall', 1); 508 } 509 } 510} 511 512/** 513 * 状态--升级失败 514 * 515 * @since 2022-06-10 516 */ 517export class InstallFailed extends BaseState { 518 constructor() { 519 super(); 520 this.actionSet.push(UpdateAction.CHECK_NEW_VERSION); 521 this.actionSet.push(UpdateAction.SHOW_PROCESS_VIEW); 522 this.state = UpdateState.INSTALL_FAILED; 523 this.percent = 100; 524 this.buttonText = $r('app.string.btn_upgrade'); 525 this.isButtonClickable = false; 526 } 527 528 async notify(context?: common.Context): Promise<void> { 529 AppStorage.Set('installStatusRefresh', JSON.stringify(this.otaStatus)); 530 await UpgradeAdapter.getInstance().getNotifyInstance()?.cancelAll(); 531 if (VersionUtils.isInNewVersionPage()) { 532 DialogUtils.showUpgradeFailDialog(context, this.otaStatus); 533 } else { 534 let versionName = globalThis.lastVersionName; 535 LogUtils.log('StateManager', "InstallFailed versionName is " + versionName); 536 await UpgradeAdapter.getInstance().getNotifyInstance()?.showUpgradeFailed(versionName, context); 537 } 538 539 } 540} 541 542/** 543 * 状态--升级中 544 * 545 * @since 2022-06-10 546 */ 547export class Upgrading extends Installing { 548 constructor() { 549 super(); 550 this.actionSet.push(UpdateAction.SHOW_NEW_VERSION); 551 this.actionSet.push(UpdateAction.SHOW_PROCESS_VIEW); 552 this.state = UpdateState.UPGRADING; 553 this.percent = 100; 554 this.isButtonClickable = false; 555 this.downloadStateText = $r('app.string.new_version_status_installing'); 556 } 557 558 async notify(context?: common.Context): Promise<void> { 559 AppStorage.Set('installStatusRefresh', JSON.stringify(this.otaStatus)); 560 } 561} 562 563/** 564 * 状态--升级成功 565 * 566 * @since 2022-06-10 567 */ 568export class UpgradeSuccess extends BaseState { 569 constructor() { 570 super(); 571 this.actionSet.push(UpdateAction.CHECK_NEW_VERSION); 572 this.actionSet.push(UpdateAction.SHOW_PROCESS_VIEW); 573 this.state = UpdateState.UPGRADE_SUCCESS; 574 this.percent = 100; 575 this.buttonText = $r('app.string.btn_upgrade'); 576 this.isButtonClickable = false; 577 } 578 579 async notify(context?: common.Context): Promise<void> { 580 AppStorage.Set('installStatusRefresh', JSON.stringify(this.otaStatus)); 581 await UpgradeAdapter.getInstance().getNotifyInstance()?.cancelAll(); 582 let versionName = globalThis.lastVersionName; 583 LogUtils.log('StateManager', "UpgradeSuccess versionName is " + versionName); 584 await UpgradeAdapter.getInstance().getNotifyInstance()?.showUpgradeSuccess(versionName, context); 585 } 586} 587 588/** 589 * 状态--升级失败 590 * 591 * @since 2022-06-10 592 */ 593export class UpgradeFailed extends InstallFailed { 594 constructor() { 595 super(); 596 this.state = UpdateState.UPGRADE_FAILED; 597 } 598}