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_PAUSE: 112 stateObject = new InstallPaused(); 113 break; 114 case UpdateState.INSTALL_FAILED: 115 stateObject = new InstallFailed(); 116 break; 117 case UpdateState.DOWNLOAD_FAILED: 118 stateObject = new DownloadFailed(); 119 break; 120 case UpdateState.INSTALL_SUCCESS: // fall through 121 stateObject = new InstallSuccess(); 122 break; 123 case UpdateState.UPGRADING: 124 stateObject = new Upgrading(); 125 break; 126 case UpdateState.UPGRADE_SUCCESS: 127 stateObject = new UpgradeSuccess(); 128 break; 129 case UpdateState.UPGRADE_FAILED: 130 stateObject = new UpgradeFailed(); 131 break; 132 default: 133 stateObject = new Init(); 134 break; 135 } 136 if (typeof status !== 'number' && status != null) { 137 stateObject.refresh(status); 138 } 139 return stateObject; 140 } 141} 142 143/** 144 * 升级行为 145 * 146 * @since 2022-06-10 147 */ 148export enum UpdateAction { 149 /** 150 * 搜索新版本 151 */ 152 CHECK_NEW_VERSION, 153 154 /** 155 * 下载 156 */ 157 DOWNLOAD, 158 159 /** 160 * 取消升级 161 */ 162 CANCEL, 163 164 /** 165 * 继续下载 166 */ 167 RESUME, 168 169 /** 170 * 安装 171 */ 172 INSTALL, 173 174 /** 175 * 重启 176 */ 177 REBOOT, 178 179 /** 180 * 显示新版本页面 181 */ 182 SHOW_NEW_VERSION, 183 184 /** 185 * 显示进度圆圈 186 */ 187 SHOW_PROCESS_VIEW 188} 189 190/** 191 * 状态基类 192 * 193 * @since 2022-06-10 194 */ 195export class BaseState { 196 /** 197 * 状态对象 198 */ 199 public otaStatus: OtaStatus; 200 201 /** 202 * 进度 203 */ 204 public percent: number = 0; 205 206 /** 207 * 状态 208 */ 209 public state: number = UpdateState.INIT; 210 211 /** 212 * 升级行为 213 */ 214 public actionSet: Array<UpdateAction> = []; 215 216 /** 217 * 下载状态描述 218 */ 219 public downloadStateText: string | Resource = ''; 220 221 /** 222 * 下载安装文字描述 223 */ 224 public buttonText: string | Resource = $r('app.string.btn_download'); 225 226 /** 227 * 按钮是否可点击 228 */ 229 public isButtonClickable: boolean = true; 230 231 /** 232 * 按钮对应的升级行为 233 */ 234 public buttonClickAction: UpdateAction; 235 236 /** 237 * 数据刷新 238 * 239 * @param otaStatus 状态 240 */ 241 refresh(otaStatus: OtaStatus): void { 242 this.otaStatus = otaStatus; 243 if (this.otaStatus?.percent) { 244 this.percent = otaStatus?.percent; 245 } 246 } 247 248 /** 249 * 提醒 250 */ 251 async notify(context?: common.Context, eventId?: update.EventId): Promise<void> { 252 } 253} 254 255/** 256 * 状态--初始状态 257 * 258 * @since 2022-06-10 259 */ 260export class Init extends BaseState { 261 constructor() { 262 super(); 263 this.actionSet.push(UpdateAction.CHECK_NEW_VERSION); 264 } 265 266 async notify(): Promise<void> { 267 await UpgradeAdapter.getInstance().getNotifyInstance()?.cancelAll(); 268 } 269} 270 271/** 272 * 状态--搜包成功 273 * 274 * @since 2022-06-10 275 */ 276export class CheckSuccess extends BaseState { 277 constructor() { 278 super(); 279 this.actionSet.push(UpdateAction.SHOW_NEW_VERSION); 280 this.actionSet.push(UpdateAction.CHECK_NEW_VERSION); 281 this.actionSet.push(UpdateAction.DOWNLOAD); 282 this.state = UpdateState.CHECK_SUCCESS; 283 this.buttonClickAction = UpdateAction.DOWNLOAD; 284 } 285 286 async notify(context?: common.Context, eventId?: update.EventId): Promise<void> { 287 if (this.otaStatus?.endReason) { 288 await UpgradeAdapter.getInstance().getNotifyInstance()?.cancelAll(); 289 switch (this.otaStatus.endReason) { 290 case ErrorCode.NETWORK_ERROR: 291 let message = await context.resourceManager.getString($r('app.string.network_err_toast').id); 292 ToastUtils.showToast(message); 293 break; 294 case ErrorCode.NO_ENOUGH_MEMORY: 295 DialogUtils.showDownloadNotEnoughSpaceDialog(context, this.otaStatus, eventId); 296 break; 297 default: 298 DialogUtils.showDownloadFailDialog(context, this.otaStatus, eventId); 299 break; 300 } 301 } 302 } 303} 304 305/** 306 * 状态--下载中 307 * 308 * @since 2022-06-10 309 */ 310export class Downloading extends BaseState { 311 constructor() { 312 super(); 313 this.actionSet.push(UpdateAction.SHOW_NEW_VERSION); 314 this.actionSet.push(UpdateAction.SHOW_PROCESS_VIEW); 315 this.state = UpdateState.DOWNLOADING; 316 this.downloadStateText = $r('app.string.download_status_downloading'); 317 this.buttonText = $r('app.string.cancel'); 318 this.buttonClickAction = UpdateAction.CANCEL; 319 } 320 321 async notify(context?: common.Context): Promise<void> { 322 if (this.percent == 100) { 323 await UpgradeAdapter.getInstance().getNotifyInstance()?.cancelAll(); 324 return; 325 } 326 if (!VersionUtils.isInNewVersionPage()) { 327 let versionName = await VersionUtils.obtainNewVersionName(globalThis.cachedNewVersionInfo); 328 await UpgradeAdapter.getInstance().getNotifyInstance()?.showDownloading(versionName, this.percent, context); 329 } 330 } 331} 332 333export class InstallPaused extends BaseState { 334 constructor() { 335 super(); 336 this.actionSet.push(UpdateAction.SHOW_NEW_VERSION); 337 this.actionSet.push(UpdateAction.SHOW_PROCESS_VIEW); 338 339 this.actionSet.push(UpdateAction.INSTALL); 340 341 this.state = UpdateState.INSTALL_PAUSE; 342 this.downloadStateText = $r('app.string.download_status_download_pause'); 343 this.buttonText = $r('app.string.continue'); 344 this.buttonClickAction = UpdateAction.INSTALL; 345 346 this.isButtonClickable = true; 347 } 348 349 async notify(context?: common.Context, eventId?: update.EventId): Promise<void> { 350 if (!VersionUtils.isInNewVersionPage()) { 351 return; 352 } 353 if (this.otaStatus?.endReason) { 354 await UpgradeAdapter.getInstance().getNotifyInstance()?.cancelAll(); 355 let message = await context.resourceManager.getString($r('app.string.install_pause_message').id); 356 ToastUtils.showToast(message); 357 } 358 359 } 360} 361 362/** 363 * 状态--下载暂停 364 * 365 * @since 2022-06-10 366 */ 367export class DownloadPause extends BaseState { 368 constructor() { 369 super(); 370 this.actionSet.push(UpdateAction.SHOW_NEW_VERSION); 371 this.actionSet.push(UpdateAction.SHOW_PROCESS_VIEW); 372 this.actionSet.push(UpdateAction.RESUME); 373 this.state = UpdateState.DOWNLOAD_PAUSE; 374 this.downloadStateText = $r('app.string.download_status_download_pause'); 375 this.buttonText = $r('app.string.continue'); 376 this.buttonClickAction = UpdateAction.RESUME; 377 } 378 379 async notify(context?: common.Context, eventId?: update.EventId): Promise<void> { 380 if (!VersionUtils.isInNewVersionPage()) { 381 return; 382 } 383 if (this.otaStatus?.endReason) { 384 await UpgradeAdapter.getInstance().getNotifyInstance()?.cancelAll(); 385 switch (this.otaStatus?.endReason) { 386 case ErrorCode.NETWORK_ERROR: 387 if (eventId == update.EventId.EVENT_DOWNLOAD_PAUSE) { 388 DialogUtils.showDownloadNoNetworkDialog(context, this.otaStatus, eventId); 389 } else { 390 let message = await context.resourceManager.getString($r('app.string.network_err_toast').id); 391 ToastUtils.showToast(message); 392 } 393 break; 394 case ErrorCode.NETWORK_NOT_ALLOW: 395 if (eventId == update.EventId.EVENT_DOWNLOAD_PAUSE) { 396 DialogUtils.showDownloadNoNetworkDialog(context, this.otaStatus, eventId); 397 } 398 break; 399 default: 400 DialogUtils.showDownloadFailDialog(context, this.otaStatus, eventId); 401 break; 402 } 403 } 404 } 405} 406 407/** 408 * 状态--下载失败 409 * 410 * @since 2022-06-10 411 */ 412export class DownloadFailed extends BaseState { 413 constructor() { 414 super(); 415 this.actionSet.push(UpdateAction.CHECK_NEW_VERSION); 416 this.actionSet.push(UpdateAction.SHOW_PROCESS_VIEW); 417 this.state = UpdateState.DOWNLOAD_FAILED; 418 this.downloadStateText = $r('app.string.download_status_download_failed'); 419 this.isButtonClickable = false; 420 this.buttonText = $r('app.string.cancel'); 421 this.buttonClickAction = UpdateAction.CANCEL; 422 } 423 424 async notify(context?: common.Context, eventId?: update.EventId): Promise<void> { 425 await UpgradeAdapter.getInstance().getNotifyInstance()?.cancelAll(); 426 switch (this.otaStatus.endReason) { 427 case ErrorCode.VERIFY_PACKAGE_FAIL: 428 DialogUtils.showVerifyFailDialog(context, this.otaStatus, eventId); 429 break; 430 default: 431 DialogUtils.showDownloadFailDialog(context, this.otaStatus, eventId); 432 break; 433 } 434 } 435} 436 437/** 438 * 状态--下载成功 439 * 440 * @since 2022-06-10 441 */ 442export class DownloadSuccess extends BaseState { 443 constructor() { 444 super(); 445 this.actionSet.push(UpdateAction.INSTALL); 446 this.actionSet.push(UpdateAction.SHOW_NEW_VERSION); 447 this.actionSet.push(UpdateAction.SHOW_PROCESS_VIEW); 448 this.state = UpdateState.DOWNLOAD_SUCCESS; 449 this.percent = 100; 450 this.downloadStateText = $r('app.string.download_status_download_success'); 451 this.buttonText = $r('app.string.btn_upgrade'); 452 this.buttonClickAction = UpdateAction.INSTALL; 453 } 454 455 async notify(context?: common.Context, eventId?: update.EventId): Promise<void> { 456 let isABInstall = await VersionUtils.isABInstall(); 457 LogUtils.info('StateManager:', 'notify ab flag ' + isABInstall + ',eventId:' + eventId); 458 if (eventId == update.EventId.EVENT_DOWNLOAD_SUCCESS && isABInstall) { 459 OtaUpdateManager.getInstance().upgrade(update.Order.INSTALL); 460 return; 461 } 462 463 if (eventId == update.EventId.EVENT_UPGRADE_WAIT && !isABInstall) { 464 LogUtils.info('StateManager', 'manual download complete to count down'); 465 if (!VersionUtils.isInNewVersionPage()) { 466 NotificationManager.startToNewVersion(context); 467 } 468 AppStorage.Set('isClickInstall', 1); 469 return; 470 } 471 472 if (this.otaStatus?.endReason) { 473 switch (this.otaStatus.endReason) { 474 case ErrorCode.NO_ENOUGH_MEMORY: 475 DialogUtils.showUpgradeNotEnoughSpaceDialog(context, this.otaStatus, eventId); 476 break; 477 case ErrorCode.NO_ENOUGH_BATTERY: 478 DialogUtils.showUpgradeNotEnoughBatteryDialog(context, this.otaStatus, eventId); 479 break; 480 default: 481 DialogUtils.showUpgradeFailDialog(context, this.otaStatus, eventId); 482 break; 483 } 484 } 485 } 486} 487 488/** 489 * 状态--解压中 490 * 491 * @since 2022-06-10 492 */ 493export class Installing extends BaseState { 494 constructor() { 495 super(); 496 this.actionSet.push(UpdateAction.SHOW_NEW_VERSION); 497 this.actionSet.push(UpdateAction.SHOW_PROCESS_VIEW); 498 this.checkAbStreamInstall(); 499 } 500 501 async checkAbStreamInstall() { 502 let isABStreamInstall = await VersionUtils.isABStreamInstall(); 503 this.state = UpdateState.INSTALLING; 504 this.downloadStateText = $r('app.string.new_version_status_installing'); 505 if (isABStreamInstall) { 506 this.buttonText = $r('app.string.cancel'); 507 this.isButtonClickable = true; 508 this.buttonClickAction = UpdateAction.CANCEL; 509 } else { 510 this.buttonText = $r('app.string.btn_upgrade'); 511 this.isButtonClickable = false; 512 } 513 } 514 515 async notify(context?: common.Context): Promise<void> { 516 if (this.percent == 100) { 517 await UpgradeAdapter.getInstance().getNotifyInstance()?.cancelAll(); 518 return; 519 } 520 if (!VersionUtils.isInNewVersionPage()) { 521 let versionName = await VersionUtils.obtainNewVersionName(globalThis.cachedNewVersionInfo); 522 await UpgradeAdapter.getInstance().getNotifyInstance()?.showInstalling(versionName, this.percent, context); 523 } 524 } 525} 526 527/** 528 * 状态--下载成功 529 * 530 * @since 2022-06-10 531 */ 532export class InstallSuccess extends BaseState { 533 constructor() { 534 super(); 535 this.actionSet.push(UpdateAction.REBOOT); 536 this.actionSet.push(UpdateAction.SHOW_NEW_VERSION); 537 this.actionSet.push(UpdateAction.SHOW_PROCESS_VIEW); 538 this.state = UpdateState.INSTALL_SUCCESS; 539 this.percent = 100; 540 this.downloadStateText = $r('app.string.new_version_status_install_success'); 541 this.buttonText = $r('app.string.btn_reboot'); 542 this.buttonClickAction = UpdateAction.REBOOT; 543 } 544 545 async notify(context?: common.Context, eventId?: update.EventId): Promise<void> { 546 if (eventId == update.EventId.EVENT_APPLY_WAIT) { 547 LogUtils.info('StateManager', 'ab install complete to count down'); 548 if (!VersionUtils.isInNewVersionPage()) { 549 NotificationManager.startToNewVersion(context); 550 } 551 AppStorage.Set('isClickInstall', 1); 552 } 553 } 554} 555 556/** 557 * 状态--升级失败 558 * 559 * @since 2022-06-10 560 */ 561export class InstallFailed extends BaseState { 562 constructor() { 563 super(); 564 this.actionSet.push(UpdateAction.CHECK_NEW_VERSION); 565 this.actionSet.push(UpdateAction.SHOW_PROCESS_VIEW); 566 this.state = UpdateState.INSTALL_FAILED; 567 this.percent = 100; 568 this.buttonText = $r('app.string.btn_upgrade'); 569 this.isButtonClickable = false; 570 } 571 572 async notify(context?: common.Context): Promise<void> { 573 AppStorage.Set('installStatusRefresh', JSON.stringify(this.otaStatus)); 574 await UpgradeAdapter.getInstance().getNotifyInstance()?.isServiceReady(); 575 await UpgradeAdapter.getInstance().getNotifyInstance()?.cancelAll(); 576 if (VersionUtils.isInNewVersionPage()) { 577 DialogUtils.showUpgradeFailDialog(context, this.otaStatus); 578 } else { 579 let versionName = globalThis.lastVersionName; 580 LogUtils.log('StateManager', 'InstallFailed versionName is ' + versionName); 581 await UpgradeAdapter.getInstance().getNotifyInstance()?.showUpgradeFailed(versionName, context); 582 } 583 584 } 585} 586 587/** 588 * 状态--升级中 589 * 590 * @since 2022-06-10 591 */ 592export class Upgrading extends Installing { 593 constructor() { 594 super(); 595 this.actionSet.push(UpdateAction.SHOW_NEW_VERSION); 596 this.actionSet.push(UpdateAction.SHOW_PROCESS_VIEW); 597 this.state = UpdateState.UPGRADING; 598 this.percent = 100; 599 this.isButtonClickable = false; 600 this.downloadStateText = $r('app.string.new_version_status_installing'); 601 } 602 603 async notify(context?: common.Context): Promise<void> { 604 AppStorage.Set('installStatusRefresh', JSON.stringify(this.otaStatus)); 605 } 606} 607 608/** 609 * 状态--升级成功 610 * 611 * @since 2022-06-10 612 */ 613export class UpgradeSuccess extends BaseState { 614 constructor() { 615 super(); 616 this.actionSet.push(UpdateAction.CHECK_NEW_VERSION); 617 this.actionSet.push(UpdateAction.SHOW_PROCESS_VIEW); 618 this.state = UpdateState.UPGRADE_SUCCESS; 619 this.percent = 100; 620 this.buttonText = $r('app.string.btn_upgrade'); 621 this.isButtonClickable = false; 622 } 623 624 async notify(context?: common.Context, eventId?: update.EventId): Promise<void> { 625 if (eventId == update.EventId.EVENT_UPGRADE_SUCCESS) { 626 LogUtils.info('StateManager', 'Upgrade success'); 627 AppStorage.Set('installStatusRefresh', JSON.stringify(this.otaStatus)); 628 await UpgradeAdapter.getInstance().getNotifyInstance()?.isServiceReady(); 629 await UpgradeAdapter.getInstance().getNotifyInstance()?.cancelAll(); 630 let versionName = globalThis.lastVersionName; 631 LogUtils.info('StateManager', 'UpgradeSuccess versionName is ' + versionName); 632 await UpgradeAdapter.getInstance().getNotifyInstance()?.showUpgradeSuccess(versionName, context); 633 } else { 634 LogUtils.error('StateManager', 'Upgrade EventId error'); 635 } 636 } 637} 638 639/** 640 * 状态--升级失败 641 * 642 * @since 2022-06-10 643 */ 644export class UpgradeFailed extends InstallFailed { 645 constructor() { 646 super(); 647 this.state = UpdateState.UPGRADE_FAILED; 648 } 649}