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 public otaStatus: OtaStatus; 197 198 /** 199 * 进度 200 */ 201 public percent: number = 0; 202 203 /** 204 * 状态 205 */ 206 public state: number = UpdateState.INIT; 207 208 /** 209 * 升级行为 210 */ 211 public actionSet: Array<UpdateAction> = []; 212 213 /** 214 * 下载状态描述 215 */ 216 public downloadStateText: string | Resource = ''; 217 218 /** 219 * 下载安装文字描述 220 */ 221 public buttonText: string | Resource = $r('app.string.btn_download'); 222 223 /** 224 * 按钮是否可点击 225 */ 226 public isButtonClickable: boolean = true; 227 228 /** 229 * 按钮对应的升级行为 230 */ 231 public 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 LogUtils.info('StateManager:', 'notify ab flag ' + isABInstall + ',eventId:' + eventId); 426 if (eventId == update.EventId.EVENT_DOWNLOAD_SUCCESS && isABInstall) { 427 OtaUpdateManager.getInstance().upgrade(update.Order.INSTALL); 428 return; 429 } 430 431 if (eventId == update.EventId.EVENT_UPGRADE_WAIT && !isABInstall) { 432 LogUtils.info('StateManager', 'manual download complete to count down'); 433 if (!VersionUtils.isInNewVersionPage()) { 434 NotificationManager.startToNewVersion(context); 435 } 436 AppStorage.Set('isClickInstall', 1); 437 return; 438 } 439 440 if (this.otaStatus?.endReason) { 441 switch (this.otaStatus.endReason) { 442 case ErrorCode.NO_ENOUGH_MEMORY: 443 DialogUtils.showUpgradeNotEnoughSpaceDialog(context, this.otaStatus, eventId); 444 break; 445 case ErrorCode.NO_ENOUGH_BATTERY: 446 DialogUtils.showUpgradeNotEnoughBatteryDialog(context, this.otaStatus, eventId); 447 break; 448 default: 449 DialogUtils.showUpgradeFailDialog(context, this.otaStatus, eventId); 450 break; 451 } 452 } 453 } 454} 455 456/** 457 * 状态--解压中 458 * 459 * @since 2022-06-10 460 */ 461export class Installing extends BaseState { 462 constructor() { 463 super(); 464 this.actionSet.push(UpdateAction.SHOW_NEW_VERSION); 465 this.actionSet.push(UpdateAction.SHOW_PROCESS_VIEW); 466 this.state = UpdateState.INSTALLING; 467 this.buttonText = $r('app.string.btn_upgrade') 468 this.isButtonClickable = false; 469 this.downloadStateText = $r('app.string.new_version_status_installing'); 470 } 471 472 async notify(context?: common.Context): Promise<void> { 473 if (this.percent == 100) { 474 await UpgradeAdapter.getInstance().getNotifyInstance()?.cancelAll(); 475 return; 476 } 477 if (!VersionUtils.isInNewVersionPage()) { 478 let versionName = await VersionUtils.obtainNewVersionName(globalThis.cachedNewVersionInfo); 479 await UpgradeAdapter.getInstance().getNotifyInstance()?.showInstalling(versionName, this.percent, context); 480 } 481 } 482} 483 484/** 485 * 状态--下载成功 486 * 487 * @since 2022-06-10 488 */ 489export class InstallSuccess extends BaseState { 490 constructor() { 491 super(); 492 this.actionSet.push(UpdateAction.REBOOT); 493 this.actionSet.push(UpdateAction.SHOW_NEW_VERSION); 494 this.actionSet.push(UpdateAction.SHOW_PROCESS_VIEW); 495 this.state = UpdateState.INSTALL_SUCCESS; 496 this.percent = 100; 497 this.downloadStateText = $r('app.string.new_version_status_install_success'); 498 this.buttonText = $r('app.string.btn_reboot'); 499 this.buttonClickAction = UpdateAction.REBOOT; 500 } 501 502 async notify(context?: common.Context, eventId?: update.EventId): Promise<void> { 503 if (eventId == update.EventId.EVENT_APPLY_WAIT) { 504 LogUtils.info('StateManager', 'ab install complete to count down'); 505 if (!VersionUtils.isInNewVersionPage()) { 506 NotificationManager.startToNewVersion(context); 507 } 508 AppStorage.Set('isClickInstall', 1); 509 } 510 } 511} 512 513/** 514 * 状态--升级失败 515 * 516 * @since 2022-06-10 517 */ 518export class InstallFailed extends BaseState { 519 constructor() { 520 super(); 521 this.actionSet.push(UpdateAction.CHECK_NEW_VERSION); 522 this.actionSet.push(UpdateAction.SHOW_PROCESS_VIEW); 523 this.state = UpdateState.INSTALL_FAILED; 524 this.percent = 100; 525 this.buttonText = $r('app.string.btn_upgrade'); 526 this.isButtonClickable = false; 527 } 528 529 async notify(context?: common.Context): Promise<void> { 530 AppStorage.Set('installStatusRefresh', JSON.stringify(this.otaStatus)); 531 await UpgradeAdapter.getInstance().getNotifyInstance()?.isServiceReady(); 532 await UpgradeAdapter.getInstance().getNotifyInstance()?.cancelAll(); 533 if (VersionUtils.isInNewVersionPage()) { 534 DialogUtils.showUpgradeFailDialog(context, this.otaStatus); 535 } else { 536 let versionName = globalThis.lastVersionName; 537 LogUtils.log('StateManager', 'InstallFailed versionName is ' + versionName); 538 await UpgradeAdapter.getInstance().getNotifyInstance()?.showUpgradeFailed(versionName, context); 539 } 540 541 } 542} 543 544/** 545 * 状态--升级中 546 * 547 * @since 2022-06-10 548 */ 549export class Upgrading extends Installing { 550 constructor() { 551 super(); 552 this.actionSet.push(UpdateAction.SHOW_NEW_VERSION); 553 this.actionSet.push(UpdateAction.SHOW_PROCESS_VIEW); 554 this.state = UpdateState.UPGRADING; 555 this.percent = 100; 556 this.isButtonClickable = false; 557 this.downloadStateText = $r('app.string.new_version_status_installing'); 558 } 559 560 async notify(context?: common.Context): Promise<void> { 561 AppStorage.Set('installStatusRefresh', JSON.stringify(this.otaStatus)); 562 } 563} 564 565/** 566 * 状态--升级成功 567 * 568 * @since 2022-06-10 569 */ 570export class UpgradeSuccess extends BaseState { 571 constructor() { 572 super(); 573 this.actionSet.push(UpdateAction.CHECK_NEW_VERSION); 574 this.actionSet.push(UpdateAction.SHOW_PROCESS_VIEW); 575 this.state = UpdateState.UPGRADE_SUCCESS; 576 this.percent = 100; 577 this.buttonText = $r('app.string.btn_upgrade'); 578 this.isButtonClickable = false; 579 } 580 581 async notify(context?: common.Context, eventId?: update.EventId): Promise<void> { 582 if (eventId == update.EventId.EVENT_UPGRADE_SUCCESS) { 583 LogUtils.info('StateManager', 'Upgrade success'); 584 AppStorage.Set('installStatusRefresh', JSON.stringify(this.otaStatus)); 585 await UpgradeAdapter.getInstance().getNotifyInstance()?.isServiceReady(); 586 await UpgradeAdapter.getInstance().getNotifyInstance()?.cancelAll(); 587 let versionName = globalThis.lastVersionName; 588 LogUtils.info('StateManager', 'UpgradeSuccess versionName is ' + versionName); 589 await UpgradeAdapter.getInstance().getNotifyInstance()?.showUpgradeSuccess(versionName, context); 590 } else { 591 LogUtils.error('StateManager', 'Upgrade EventId error'); 592 } 593 } 594} 595 596/** 597 * 状态--升级失败 598 * 599 * @since 2022-06-10 600 */ 601export class UpgradeFailed extends InstallFailed { 602 constructor() { 603 super(); 604 this.state = UpdateState.UPGRADE_FAILED; 605 } 606}