• 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_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}