• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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}