已开启
新增自动下载和夜间升级开关、UX页面重写及多项问题修复 #88
若水随风创建于 22 天前
新增自动下载和夜间升级开关、UX页面重写及多项问题修复 #88
已开启
共 42 个文件变更+1784-176
| @@ -0,0 +1,12 @@ | |||
| 1 | +node_modules/ | ||
| 2 | +oh_modules/ | ||
| 3 | +local.properties | ||
| 4 | +.idea/ | ||
| 5 | +build/ | ||
| 6 | +.hvigor/ | ||
| 7 | +.cxx | ||
| 8 | +/.clangd | ||
| 9 | +/.clang-format | ||
| 10 | +/.clang-tidy | ||
| 11 | +**/.test | ||
| 12 | +/.appanalyzer | ||
| @@ -4,8 +4,9 @@ | |||
| 4 | { | 4 | { |
| 5 | "name": "default", | 5 | "name": "default", |
| 6 | "signingConfig": "release", | 6 | "signingConfig": "release", |
| 7 | - "compileSdkVersion": 20, | 7 | + "targetSdkVersion": 23, |
| 8 | - "compatibleSdkVersion": 20, | 8 | + "compatibleSdkVersion": 23, |
| 9 | + "compileSdkVersion": "26.0.0", | ||
| 9 | "runtimeOS": "OpenHarmony", | 10 | "runtimeOS": "OpenHarmony", |
| 10 | "buildOption": { | 11 | "buildOption": { |
| 11 | "strictMode": { | 12 | "strictMode": { |
| @@ -14,6 +15,20 @@ | |||
| 14 | } | 15 | } |
| 15 | } | 16 | } |
| 16 | } | 17 | } |
| 18 | + ], | ||
| 19 | + "signingConfigs": [ | ||
| 20 | + { | ||
| 21 | + "name": "release", | ||
| 22 | + "material": { | ||
| 23 | + "storeFile": "signature/OpenHarmony.p12", | ||
| 24 | + "storePassword": "00000016F657C3513233C0691A4CB761368F8674780E0FA1E5115D2CD38E1A378203F9A232CD", | ||
| 25 | + "keyAlias": "OpenHarmony Application Release", | ||
| 26 | + "keyPassword": "0000001618FE1FC8EE919CBDD32E6D48440BB900C030258644853134C4EE40B4665C6BB37BB3", | ||
| 27 | + "signAlg": "SHA256withECDSA", | ||
| 28 | + "profile": "signature/updateapp.p7b", | ||
| 29 | + "certpath": "signature/OpenHarmony.cer" | ||
| 30 | + } | ||
| 31 | + } | ||
| 17 | ] | 32 | ] |
| 18 | }, | 33 | }, |
| 19 | "modules": [ | 34 | "modules": [ |
| @@ -0,0 +1,93 @@ | |||
| 1 | +/* | ||
| 2 | + * Copyright (c) 2026 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 | + | ||
| 16 | +const TAG = 'SettingOptions' | ||
| 17 | + | ||
| 18 | +/** | ||
| 19 | + * 设置选项组件 | ||
| 20 | + * | ||
| 21 | + * @since 2026-04-10 | ||
| 22 | + */ | ||
| 23 | +@Component | ||
| 24 | +export struct SettingOptions { | ||
| 25 | + private title?: string | Resource; | ||
| 26 | + private description?: string | Resource; | ||
| 27 | + @Prop private isEnabled: boolean = true; | ||
| 28 | + private toggleChange?: (isOn: boolean) => void; | ||
| 29 | + @Prop private isOn: boolean = false; | ||
| 30 | + private themeColor?: ResourceColor; | ||
| 31 | + | ||
| 32 | + @Builder | ||
| 33 | + private optionToggle(isEnabled: boolean) { | ||
| 34 | + Toggle({ type: ToggleType.Switch, isOn: this.isOn }) | ||
| 35 | + .width('36vp') | ||
| 36 | + .height('20vp') | ||
| 37 | + .flexShrink(0) | ||
| 38 | + .enabled(isEnabled) | ||
| 39 | + .onChange((isOn) => { | ||
| 40 | + this.toggleChange?.(isOn) | ||
| 41 | + }) | ||
| 42 | + .hoverEffect(HoverEffect.None) | ||
| 43 | + .selectedColor(this.themeColor) | ||
| 44 | + .id(`${TAG}_Toggle_01`) | ||
| 45 | + } | ||
| 46 | + | ||
| 47 | + build() { | ||
| 48 | + Column() { | ||
| 49 | + Flex({ direction: FlexDirection.Row, alignItems: ItemAlign.Center }) { | ||
| 50 | + Text(this.title) | ||
| 51 | + .fontSize($r('sys.float.ohos_id_text_size_body1')) | ||
| 52 | + .width('100%') | ||
| 53 | + .fontColor($r('sys.color.ohos_id_color_text_primary')) | ||
| 54 | + .fontWeight(FontWeight.Medium) | ||
| 55 | + .draggable(false) | ||
| 56 | + .id(`${TAG}_Text_02`) | ||
| 57 | + | ||
| 58 | + if ((this.isEnabled)) { | ||
| 59 | + this.optionToggle(true); | ||
| 60 | + } else { | ||
| 61 | + this.optionToggle(false); | ||
| 62 | + } | ||
| 63 | + } | ||
| 64 | + .backgroundColor($r('sys.color.comp_background_list_card')) | ||
| 65 | + .padding({ | ||
| 66 | + top: $r('app.float.setting_options_padding_vertical'), | ||
| 67 | + right: $r('app.float.setting_options_padding_horizontal'), | ||
| 68 | + bottom: $r('app.float.setting_options_padding_vertical'), | ||
| 69 | + left: $r('app.float.setting_options_padding_horizontal') | ||
| 70 | + }) | ||
| 71 | + .id(`${TAG}_Flex_01`) | ||
| 72 | + .constraintSize({ minHeight: $r('app.float.setting_options_height') }) | ||
| 73 | + .borderRadius($r('app.float.setting_border_radius')) | ||
| 74 | + | ||
| 75 | + Text(this.description) | ||
| 76 | + .fontSize($r('app.float.text_size_body')) | ||
| 77 | + .width('100%') | ||
| 78 | + .fontColor($r('sys.color.ohos_id_color_text_secondary')) | ||
| 79 | + .padding({ | ||
| 80 | + top: $r('app.float.setting_options_margin_top'), | ||
| 81 | + right: $r('app.float.setting_options_padding_horizontal'), | ||
| 82 | + left: $r('app.float.setting_options_padding_horizontal') | ||
| 83 | + }) | ||
| 84 | + .draggable(false) | ||
| 85 | + .id(`${TAG}_Text_01`) | ||
| 86 | + } | ||
| 87 | + .margin({ | ||
| 88 | + right: $r('app.float.page_left_or_right_margin'), | ||
| 89 | + left: $r('app.float.page_left_or_right_margin') | ||
| 90 | + }) | ||
| 91 | + .id(`${TAG}_Column_01`) | ||
| 92 | + } | ||
| 93 | +} | ||
| @@ -26,15 +26,21 @@ export struct TitleBar { | |||
| 26 | private static readonly TAG = 'TitleBar'; | 26 | private static readonly TAG = 'TitleBar'; |
| 27 | private title: string | Resource = ''; | 27 | private title: string | Resource = ''; |
| 28 | private onBack?: () => boolean; | 28 | private onBack?: () => boolean; |
| 29 | + @Prop private showMenu: boolean = false; | ||
| 29 | 30 | ||
| 30 | build() { | 31 | build() { |
| 31 | Flex({ justifyContent: FlexAlign.SpaceBetween }) { | 32 | Flex({ justifyContent: FlexAlign.SpaceBetween }) { |
| 32 | Row() { | 33 | Row() { |
| 33 | Row() { | 34 | Row() { |
| 34 | - Image($r('app.media.back')) | 35 | + Button({ type: ButtonType.Circle, stateEffect: true }) { |
| 35 | - .width($r('app.float.title_bar_icon_width')) | 36 | + SymbolGlyph($r('sys.symbol.chevron_backward')) |
| 36 | - .height($r('app.float.title_bar_icon_height')) | 37 | + .fontSize($r('app.float.title_bar_symbol_font_size')) |
| 37 | - .objectFit(ImageFit.Contain) | 38 | + .fontColor([$r('sys.color.ohos_id_color_text_primary')]) |
| 39 | + } | ||
| 40 | + .width($r('app.float.title_bar_icon_width')) | ||
| 41 | + .height($r('app.float.title_bar_icon_height')) | ||
| 42 | + .backgroundColor($r('sys.color.ohos_id_color_button_normal')) | ||
| 43 | + .borderRadius($r('sys.float.ohos_id_corner_radius_icon')) | ||
| 38 | }.height($r('app.float.title_bar_height')) | 44 | }.height($r('app.float.title_bar_height')) |
| 39 | .margin({ left: $r('app.float.title_bar_icon_margin_left') }) | 45 | .margin({ left: $r('app.float.title_bar_icon_margin_left') }) |
| 40 | .onClick(() => { | 46 | .onClick(() => { |
| @@ -54,6 +60,25 @@ export struct TitleBar { | |||
| 54 | .fontWeight(FontWeight.Bold) | 60 | .fontWeight(FontWeight.Bold) |
| 55 | }.flexGrow(1) | 61 | }.flexGrow(1) |
| 56 | .height($r('app.float.title_bar_height')) | 62 | .height($r('app.float.title_bar_height')) |
| 63 | + | ||
| 64 | + if (this.showMenu) { | ||
| 65 | + this.showMenuBuilder(); | ||
| 66 | + } | ||
| 57 | } | 67 | } |
| 58 | } | 68 | } |
| 69 | + | ||
| 70 | + @Builder | ||
| 71 | + showMenuBuilder() { | ||
| 72 | + Button({ type: ButtonType.Circle, stateEffect: true }) { | ||
| 73 | + SymbolGlyph($r('sys.symbol.dot_grid_2x2')) | ||
| 74 | + } | ||
| 75 | + .width('28vp') | ||
| 76 | + .backgroundColor('#0C000000') | ||
| 77 | + .margin({ right: $r('app.float.title_bar_text_margin_left'), top: $r('app.float.title_bar_text_margin_left') }) | ||
| 78 | + .height('28vp') | ||
| 79 | + .onClick(() => { | ||
| 80 | + LogUtils.info(TitleBar.TAG, 'show menu click.'); | ||
| 81 | + router.push({ url: 'pages/settingsView' }) | ||
| 82 | + }) | ||
| 83 | + } | ||
| 59 | } | 84 | } |
| @@ -376,7 +376,12 @@ export enum Action { | |||
| 376 | /** | 376 | /** |
| 377 | * 动作--升级失败跳转主页面搜包 | 377 | * 动作--升级失败跳转主页面搜包 |
| 378 | */ | 378 | */ |
| 379 | - NOTIFICATION_HOT_UPGRADE_FAILED = 'com.ohos.updateapp.hot_upgrade_failed' | 379 | + NOTIFICATION_HOT_UPGRADE_FAILED = 'com.ohos.updateapp.hot_upgrade_failed', |
| 380 | + | ||
| 381 | + /** | ||
| 382 | + * 动作--跳转夜间升级设置页 | ||
| 383 | + */ | ||
| 384 | + NOTIFICATION_SETTINGS = 'com.ohos.updateapp.settings' | ||
| 380 | } | 385 | } |
| 381 | 386 | ||
| 382 | /** | 387 | /** |
| @@ -490,4 +495,14 @@ export interface CustomEventInfo { | |||
| 490 | * Task body info | 495 | * Task body info |
| 491 | */ | 496 | */ |
| 492 | taskBody: update.TaskBody; | 497 | taskBody: update.TaskBody; |
| 493 | -} | 498 | +} |
| 499 | + | ||
| 500 | +/** | ||
| 501 | + * 夜间升级开始时间 | ||
| 502 | + */ | ||
| 503 | +export const NIGHT_UPGRADE_START = 2; | ||
| 504 | + | ||
| 505 | +/** | ||
| 506 | + * 夜间升级结束时间 | ||
| 507 | + */ | ||
| 508 | +export const NIGHT_UPGRADE_END = 4; | ||
| @@ -415,6 +415,33 @@ export class UpdateManager implements IUpdate { | |||
| 415 | }); | 415 | }); |
| 416 | } | 416 | } |
| 417 | 417 | ||
| 418 | + /** | ||
| 419 | + * 获取升级策略 | ||
| 420 | + * | ||
| 421 | + * resolve 升级策略 | ||
| 422 | + */ | ||
| 423 | + async getUpdatePolicy(): Promise<update.UpgradePolicy> { | ||
| 424 | + return this.otaUpdater.getUpgradePolicy(); | ||
| 425 | + } | ||
| 426 | + | ||
| 427 | + /** | ||
| 428 | + * 设置升级策略 | ||
| 429 | + * | ||
| 430 | + * policy 策略 | ||
| 431 | + * resolve 设置结果/reject 错误信息 | ||
| 432 | + */ | ||
| 433 | + async setUpdatePolicy(policy: update.UpgradePolicy): Promise<void> { | ||
| 434 | + this.log(`setUpdatePolicy download:${policy.downloadStrategy}, autoUpgrade:${policy.autoUpgradeStrategy}`); | ||
| 435 | + return new Promise((resolve, reject) => { | ||
| 436 | + this.otaUpdater?.setUpgradePolicy(policy).then(() => { | ||
| 437 | + resolve(); | ||
| 438 | + }).catch((err: BusinessError) => { | ||
| 439 | + this.logError('setUpdatePolicy err: ' + JSON.stringify(err)); | ||
| 440 | + reject(err); | ||
| 441 | + }); | ||
| 442 | + }); | ||
| 443 | + } | ||
| 444 | + | ||
| 418 | private log(message: string): void { | 445 | private log(message: string): void { |
| 419 | LogUtils.log('UpdateManager', message); | 446 | LogUtils.log('UpdateManager', message); |
| 420 | } | 447 | } |
| @@ -161,6 +161,14 @@ export interface IPage { | |||
| 161 | * @since 2022-12-01 | 161 | * @since 2022-12-01 |
| 162 | */ | 162 | */ |
| 163 | export interface INotify { | 163 | export interface INotify { |
| 164 | + /** | ||
| 165 | + * 下载提醒通知 | ||
| 166 | + * | ||
| 167 | + * @param versionName 版本号 | ||
| 168 | + * @param context 上下文 | ||
| 169 | + */ | ||
| 170 | + showDownloadReminder(versionName: string, context: common.Context): Promise<void>; | ||
| 171 | + | ||
| 164 | /** | 172 | /** |
| 165 | * 下载进度通知 | 173 | * 下载进度通知 |
| 166 | * | 174 | * |
| @@ -170,6 +178,22 @@ export interface INotify { | |||
| 170 | */ | 178 | */ |
| 171 | showDownloading(version: string, progress: number, context: common.Context): Promise<void>; | 179 | showDownloading(version: string, progress: number, context: common.Context): Promise<void>; |
| 172 | 180 | ||
| 181 | + /** | ||
| 182 | + * 下载暂停通知 | ||
| 183 | + * | ||
| 184 | + * @param version 版本号 | ||
| 185 | + * @param progress 进度 | ||
| 186 | + * @param context 上下文 | ||
| 187 | + */ | ||
| 188 | + showDownloadPaused(version: string, progress: number, context: common.Context): Promise<void>; | ||
| 189 | + | ||
| 190 | + /** | ||
| 191 | + * 升级包校验失败通知 | ||
| 192 | + * | ||
| 193 | + * @param context 上下文 | ||
| 194 | + */ | ||
| 195 | + showVerifyFailed(context: common.Context): Promise<void>; | ||
| 196 | + | ||
| 173 | /** | 197 | /** |
| 174 | * 升级失败通知 | 198 | * 升级失败通知 |
| 175 | * | 199 | * |
| @@ -186,6 +210,13 @@ export interface INotify { | |||
| 186 | */ | 210 | */ |
| 187 | showUpgradeSuccess(versionName: string, context: common.Context): Promise<void>; | 211 | showUpgradeSuccess(versionName: string, context: common.Context): Promise<void>; |
| 188 | 212 | ||
| 213 | + /** | ||
| 214 | + * 夜间升级设置通知 | ||
| 215 | + * | ||
| 216 | + * @param context 上下文 | ||
| 217 | + */ | ||
| 218 | + showNightUpgradeNotice(context: common.Context): Promise<void>; | ||
| 219 | + | ||
| 189 | /** | 220 | /** |
| 190 | * 安装中通知 | 221 | * 安装中通知 |
| 191 | * | 222 | * |
| @@ -61,7 +61,7 @@ export namespace DeviceUtils { | |||
| 61 | * @return dialog位置 | 61 | * @return dialog位置 |
| 62 | */ | 62 | */ |
| 63 | export function getDialogLocation(): DialogAlignment{ | 63 | export function getDialogLocation(): DialogAlignment{ |
| 64 | - return DialogAlignment.Bottom; | 64 | + return DialogAlignment.Center; |
| 65 | } | 65 | } |
| 66 | 66 | ||
| 67 | /** | 67 | /** |
| @@ -70,6 +70,6 @@ export namespace DeviceUtils { | |||
| 70 | * @return dialog偏移 | 70 | * @return dialog偏移 |
| 71 | */ | 71 | */ |
| 72 | export function getDialogOffsetY(): string | Resource { | 72 | export function getDialogOffsetY(): string | Resource { |
| 73 | - return $r('app.float.dialog_location_offset_y'); | 73 | + return '0vp'; |
| 74 | } | 74 | } |
| 75 | } | 75 | } |
| @@ -16,6 +16,7 @@ | |||
| 16 | import type common from '@ohos.app.ability.common'; | 16 | import type common from '@ohos.app.ability.common'; |
| 17 | import { DeviceUtils } from '../util/DeviceUtils'; | 17 | import { DeviceUtils } from '../util/DeviceUtils'; |
| 18 | import { LogUtils } from './LogUtils'; | 18 | import { LogUtils } from './LogUtils'; |
| 19 | +import i18n from '@ohos.i18n'; | ||
| 19 | 20 | ||
| 20 | const DECIMAL_POINT = 2; | 21 | const DECIMAL_POINT = 2; |
| 21 | 22 | ||
| @@ -102,4 +103,21 @@ export class FormatUtils { | |||
| 102 | let numfmt: Intl.NumberFormat = new Intl.NumberFormat(language, {style:'percent', notation:'standard'}); | 103 | let numfmt: Intl.NumberFormat = new Intl.NumberFormat(language, {style:'percent', notation:'standard'}); |
| 103 | return numfmt.format(num); | 104 | return numfmt.format(num); |
| 104 | } | 105 | } |
| 106 | + | ||
| 107 | + /** | ||
| 108 | + * 获取小时范围格式化 | ||
| 109 | + * | ||
| 110 | + * @param start 开始时间 | ||
| 111 | + * @param end 结束时间 | ||
| 112 | + * @returns string 格式化之后的小时范围 | ||
| 113 | + */ | ||
| 114 | + static getDateRangeFormat(start: number, end: number): string { | ||
| 115 | + let current = new Date(); | ||
| 116 | + let startDate = new Date(current.getFullYear(), current.getMonth(), current.getDate(), start, 0, 0); | ||
| 117 | + let endDate = new Date(current.getFullYear(), current.getMonth(), current.getDate(), end, 0, 0); | ||
| 118 | + let language: string = DeviceUtils.getSystemLanguage(); | ||
| 119 | + let datefmt: Intl.DateTimeFormat = | ||
| 120 | + new Intl.DateTimeFormat(language, { hour12: !i18n.System.is24HourClock(), hour: 'numeric', minute: 'numeric' }); | ||
| 121 | + return datefmt.formatRange(startDate, endDate) as string; | ||
| 122 | + } | ||
| 105 | } | 123 | } |
| @@ -68,14 +68,26 @@ | |||
| 68 | "name": "title_bar_icon_height", | 68 | "name": "title_bar_icon_height", |
| 69 | "value": "24vp" | 69 | "value": "24vp" |
| 70 | }, | 70 | }, |
| 71 | + { | ||
| 72 | + "name": "title_bar_icon_background_height", | ||
| 73 | + "value": "40vp" | ||
| 74 | + }, | ||
| 75 | + { | ||
| 76 | + "name": "title_bar_icon_background_width", | ||
| 77 | + "value": "40vp" | ||
| 78 | + }, | ||
| 71 | { | 79 | { |
| 72 | "name": "title_bar_icon_margin_left", | 80 | "name": "title_bar_icon_margin_left", |
| 73 | - "value": "24vp" | 81 | + "value": "16vp" |
| 74 | }, | 82 | }, |
| 75 | { | 83 | { |
| 76 | "name": "title_bar_icon_margin_right", | 84 | "name": "title_bar_icon_margin_right", |
| 77 | "value": "24vp" | 85 | "value": "24vp" |
| 78 | }, | 86 | }, |
| 87 | + { | ||
| 88 | + "name": "title_bar_symbol_font_size", | ||
| 89 | + "value": "24vp" | ||
| 90 | + }, | ||
| 79 | { | 91 | { |
| 80 | "name": "title_bar_text_margin_left", | 92 | "name": "title_bar_text_margin_left", |
| 81 | "value": "16vp" | 93 | "value": "16vp" |
| @@ -96,6 +108,10 @@ | |||
| 96 | "name": "index_dot_width", | 108 | "name": "index_dot_width", |
| 97 | "value": "18vp" | 109 | "value": "18vp" |
| 98 | }, | 110 | }, |
| 111 | + { | ||
| 112 | + "name": "page_left_or_right_margin", | ||
| 113 | + "value": "16vp" | ||
| 114 | + }, | ||
| 99 | { | 115 | { |
| 100 | "name": "progress_stroke_width", | 116 | "name": "progress_stroke_width", |
| 101 | "value": "18vp" | 117 | "value": "18vp" |
| @@ -124,7 +140,14 @@ | |||
| 124 | "name": "progress_logo_other_text_margin_top", | 140 | "name": "progress_logo_other_text_margin_top", |
| 125 | "value": "16vp" | 141 | "value": "16vp" |
| 126 | }, | 142 | }, |
| 127 | - | 143 | + { |
| 144 | + "name": "setting_border_radius", | ||
| 145 | + "value": "16vp" | ||
| 146 | + }, | ||
| 147 | + { | ||
| 148 | + "name": "setting_options_height", | ||
| 149 | + "value": "56vp" | ||
| 150 | + }, | ||
| 128 | { | 151 | { |
| 129 | "name": "setting_options_margin_top", | 152 | "name": "setting_options_margin_top", |
| 130 | "value": "8vp" | 153 | "value": "8vp" |
| @@ -80,6 +80,19 @@ class RetryUpgradeAction implements DialogOperator { | |||
| 80 | 80 | ||
| 81 | const retryUpgradeAction: DialogOperator = new RetryUpgradeAction(); | 81 | const retryUpgradeAction: DialogOperator = new RetryUpgradeAction(); |
| 82 | 82 | ||
| 83 | +/** | ||
| 84 | + * 更新包变更后重新搜包动作 | ||
| 85 | + */ | ||
| 86 | +class VersionInfoChangedAction implements DialogOperator { | ||
| 87 | + onConfirm(): void { | ||
| 88 | + RouterUtils.singletonHomePage(); | ||
| 89 | + } | ||
| 90 | + onCancel(): void { | ||
| 91 | + } | ||
| 92 | +} | ||
| 93 | + | ||
| 94 | +const versionInfoChangedAction: DialogOperator = new VersionInfoChangedAction(); | ||
| 95 | + | ||
| 83 | /** | 96 | /** |
| 84 | * 弹框辅助者 | 97 | * 弹框辅助者 |
| 85 | * | 98 | * |
| @@ -143,7 +156,18 @@ export namespace DialogHelper { | |||
| 143 | * @param operator 回调 | 156 | * @param operator 回调 |
| 144 | */ | 157 | */ |
| 145 | export function displayDownloadFailDialog(): void { | 158 | export function displayDownloadFailDialog(): void { |
| 146 | - defaultNoTitleDialog($r('app.string.download_fail'), retryCheckAction); | 159 | + showDialog($r('app.string.download_status_download_failed'), $r('app.string.download_fail'), |
| 160 | + $r('app.string.ok'), retryCheckAction); | ||
| 161 | + } | ||
| 162 | + | ||
| 163 | + /** | ||
| 164 | + * 更新包变更弹框 | ||
| 165 | + * | ||
| 166 | + * @param operator 回调 | ||
| 167 | + */ | ||
| 168 | + export function displayVersionInfoChangedDialog(): void { | ||
| 169 | + showDialog($r('app.string.package_changed_title'), $r('app.string.package_changed_content'), | ||
| 170 | + $r('app.string.ok'), versionInfoChangedAction); | ||
| 147 | } | 171 | } |
| 148 | 172 | ||
| 149 | /** | 173 | /** |
| @@ -151,6 +175,39 @@ export namespace DialogHelper { | |||
| 151 | * | 175 | * |
| 152 | * @param operator 回调 | 176 | * @param operator 回调 |
| 153 | */ | 177 | */ |
| 178 | + export function displayCancelDownloadDialog(): void { | ||
| 179 | + AlertDialog.show( | ||
| 180 | + { | ||
| 181 | + title: $r('app.string.cancel_download_confirm_title'), | ||
| 182 | + message: $r('app.string.cancel_download_confirm_message'), | ||
| 183 | + primaryButton: { | ||
| 184 | + value: $r('app.string.continue_download'), | ||
| 185 | + action: () => { | ||
| 186 | + logInfo('Continue download button clicked'); | ||
| 187 | + }, | ||
| 188 | + backgroundColor: $r('sys.float.ohos_id_corner_radius_button') | ||
| 189 | + }, | ||
| 190 | + secondaryButton: { | ||
| 191 | + value: $r('app.string.cancel_download'), | ||
| 192 | + action: () => { | ||
| 193 | + logInfo('Cancel download button clicked'); | ||
| 194 | + OtaUpdateManager.getInstance().cancel(); | ||
| 195 | + }, | ||
| 196 | + backgroundColor: $r('sys.float.ohos_id_corner_radius_button') | ||
| 197 | + }, | ||
| 198 | + cancel: () => { | ||
| 199 | + logInfo('Closed callbacks'); | ||
| 200 | + }, | ||
| 201 | + alignment: DeviceUtils.getDialogLocation(), | ||
| 202 | + offset: ({ | ||
| 203 | + dx: '0vp', | ||
| 204 | + dy: DeviceUtils.getDialogOffsetY() | ||
| 205 | + }), | ||
| 206 | + autoCancel: false | ||
| 207 | + } | ||
| 208 | + ) | ||
| 209 | + } | ||
| 210 | + | ||
| 154 | export function displayNoNetworkDialog(): void { | 211 | export function displayNoNetworkDialog(): void { |
| 155 | defaultKnowDialog($r('app.string.net_error_title'), $r('app.string.net_error_content')); | 212 | defaultKnowDialog($r('app.string.net_error_title'), $r('app.string.net_error_content')); |
| 156 | } | 213 | } |
| @@ -201,7 +258,7 @@ export namespace DialogHelper { | |||
| 201 | * @param message 内容 | 258 | * @param message 内容 |
| 202 | * @param operator 回调 | 259 | * @param operator 回调 |
| 203 | */ | 260 | */ |
| 204 | - function defaultKnowDialog(title: string | Resource, message: string | Resource, operator ?: DialogOperator): void { | 261 | + function defaultKnowDialog(title: ResourceStr, message: ResourceStr, operator ?: DialogOperator): void { |
| 205 | showDialog(title, message, $r('app.string.button_know'), operator); | 262 | showDialog(title, message, $r('app.string.button_know'), operator); |
| 206 | } | 263 | } |
| 207 | 264 | ||
| @@ -211,8 +268,8 @@ export namespace DialogHelper { | |||
| 211 | * @param message 内容 | 268 | * @param message 内容 |
| 212 | * @param operator 回调 | 269 | * @param operator 回调 |
| 213 | */ | 270 | */ |
| 214 | - function defaultNoTitleDialog(message: string | Resource, operator ?: DialogOperator): void { | 271 | + function defaultNoTitleDialog(message: ResourceStr, operator ?: DialogOperator): void { |
| 215 | - showDialog(null, message, $r('app.string.button_know'), operator); | 272 | + showDialog(undefined, message, $r('app.string.button_know'), operator); |
| 216 | } | 273 | } |
| 217 | 274 | ||
| 218 | /** | 275 | /** |
| @@ -223,7 +280,7 @@ export namespace DialogHelper { | |||
| 223 | * @param confirmText 确认按钮显示内容 | 280 | * @param confirmText 确认按钮显示内容 |
| 224 | * @param operator 回调 | 281 | * @param operator 回调 |
| 225 | */ | 282 | */ |
| 226 | - function showDialog(title: string | Resource | null, message: string | Resource, confirmText?: string | Resource, | 283 | + function showDialog(title: ResourceStr | undefined, message: ResourceStr, confirmText: ResourceStr, |
| 227 | operator ?: DialogOperator): void { | 284 | operator ?: DialogOperator): void { |
| 228 | AlertDialog.show( | 285 | AlertDialog.show( |
| 229 | { | 286 | { |
| @@ -263,4 +320,4 @@ export namespace DialogHelper { | |||
| 263 | function logInfo(message: string): void { | 320 | function logInfo(message: string): void { |
| 264 | LogUtils.info('DialogHelper', message); | 321 | LogUtils.info('DialogHelper', message); |
| 265 | } | 322 | } |
| 266 | -} | 323 | +} |
| @@ -81,6 +81,9 @@ class RetryCheckAction implements DialogOperator { | |||
| 81 | }; | 81 | }; |
| 82 | 82 | ||
| 83 | export const retryCheckAction: DialogOperator = new RetryCheckAction(); | 83 | export const retryCheckAction: DialogOperator = new RetryCheckAction(); |
| 84 | +const DOWNLOAD_NO_NETWORK_DIALOG_DELAY_MS = 10 * 1000; | ||
| 85 | +let downloadNoNetworkDialogTimer: number | undefined = undefined; | ||
| 86 | +const DOWNLOAD_NO_NETWORK_DIALOG_VISIBLE_KEY = 'downloadNoNetworkDialogVisible'; | ||
| 84 | 87 | ||
| 85 | /** | 88 | /** |
| 86 | * 弹框工具类 | 89 | * 弹框工具类 |
| @@ -105,11 +108,42 @@ export class DialogUtils { | |||
| 105 | * | 108 | * |
| 106 | * @param context 上下文 | 109 | * @param context 上下文 |
| 107 | */ | 110 | */ |
| 108 | - @foregroundCheck() | 111 | + // 新增延迟10s后弹出功能可能不在前台,所以注释装饰器 |
| 109 | static showDownloadNoNetworkDialog(context: common.Context, otaStatus: OtaStatus, | 112 | static showDownloadNoNetworkDialog(context: common.Context, otaStatus: OtaStatus, |
| 110 | eventId?: update.EventId): void { | 113 | eventId?: update.EventId): void { |
| 111 | LogUtils.log('DialogUtils', 'showDownloadNoNetworkDialog'); | 114 | LogUtils.log('DialogUtils', 'showDownloadNoNetworkDialog'); |
| 112 | - DialogHelper.displayNoNetworkDialog(); | 115 | + if (globalThis.newVersionThis?.showNoNetworkDialog) { |
| 116 | + globalThis.newVersionThis.showNoNetworkDialog(); | ||
| 117 | + } | ||
| 118 | + AppStorage.SetOrCreate(DOWNLOAD_NO_NETWORK_DIALOG_VISIBLE_KEY, true); | ||
| 119 | + } | ||
| 120 | + /** | ||
| 121 | + * 延时显示下载断网弹框 | ||
| 122 | + * | ||
| 123 | + * @param context 上下文 | ||
| 124 | + */ | ||
| 125 | + static delayShowDownloadNoNetworkDialog(context: common.Context, otaStatus: OtaStatus, | ||
| 126 | + eventId?: update.EventId): void { | ||
| 127 | + DialogUtils.clearDownloadNoNetworkDialog(); | ||
| 128 | + downloadNoNetworkDialogTimer = setTimeout(() => { | ||
| 129 | + downloadNoNetworkDialogTimer = undefined; | ||
| 130 | + DialogUtils.showDownloadNoNetworkDialog(context, otaStatus, eventId); | ||
| 131 | + }, DOWNLOAD_NO_NETWORK_DIALOG_DELAY_MS); | ||
| 132 | + } | ||
| 133 | + | ||
| 134 | + /** | ||
| 135 | + * 清除下载断网弹框 | ||
| 136 | + * | ||
| 137 | + */ | ||
| 138 | + static clearDownloadNoNetworkDialog(): void { | ||
| 139 | + if (downloadNoNetworkDialogTimer !== undefined) { | ||
| 140 | + clearTimeout(downloadNoNetworkDialogTimer); | ||
| 141 | + downloadNoNetworkDialogTimer = undefined; | ||
| 142 | + } | ||
| 143 | + if (globalThis.newVersionThis?.hideNoNetworkDialog) { | ||
| 144 | + globalThis.newVersionThis.hideNoNetworkDialog(); | ||
| 145 | + } | ||
| 146 | + AppStorage.SetOrCreate(DOWNLOAD_NO_NETWORK_DIALOG_VISIBLE_KEY, false); | ||
| 113 | } | 147 | } |
| 114 | 148 | ||
| 115 | /** | 149 | /** |
| @@ -134,6 +168,16 @@ export class DialogUtils { | |||
| 134 | DialogHelper.displayDownloadFailDialog(); | 168 | DialogHelper.displayDownloadFailDialog(); |
| 135 | } | 169 | } |
| 136 | 170 | ||
| 171 | + /** | ||
| 172 | + * 更新包变更弹框 | ||
| 173 | + * | ||
| 174 | + * @param context 上下文 | ||
| 175 | + */ | ||
| 176 | + static showVersionInfoChangedDialog(context: common.Context, otaStatus?: OtaStatus, eventId?: update.EventId): void { | ||
| 177 | + LogUtils.log('DialogUtils', 'showVersionInfoChangedDialog'); | ||
| 178 | + DialogHelper.displayVersionInfoChangedDialog(); | ||
| 179 | + } | ||
| 180 | + | ||
| 137 | /** | 181 | /** |
| 138 | * 安装空间不足弹框 | 182 | * 安装空间不足弹框 |
| 139 | * | 183 | * |
| @@ -35,6 +35,7 @@ import VersionUtils from '../util/VersionUtils'; | |||
| 35 | import { CommonUtils } from '@ohos/common/src/main/ets/util/CommonUtils'; | 35 | import { CommonUtils } from '@ohos/common/src/main/ets/util/CommonUtils'; |
| 36 | import { UpgradeAdapter } from '../UpgradeAdapter'; | 36 | import { UpgradeAdapter } from '../UpgradeAdapter'; |
| 37 | import { FormatUtils } from '@ohos/common/src/main/ets/util/FormatUtils'; | 37 | import { FormatUtils } from '@ohos/common/src/main/ets/util/FormatUtils'; |
| 38 | +import { SettingsSyncUtils } from '../util/SettingsSyncUtils'; | ||
| 38 | 39 | ||
| 39 | /** | 40 | /** |
| 40 | * 升级接口管理类 | 41 | * 升级接口管理类 |
| @@ -145,12 +146,15 @@ export class OtaUpdateManager { | |||
| 145 | async checkNewVersion(): Promise<UpgradeData<update.CheckResult>> { | 146 | async checkNewVersion(): Promise<UpgradeData<update.CheckResult>> { |
| 146 | return new Promise((resolve, reject) => { | 147 | return new Promise((resolve, reject) => { |
| 147 | this.updateManager.checkNewVersion().then((result: UpgradeData<update.CheckResult>) => { | 148 | this.updateManager.checkNewVersion().then((result: UpgradeData<update.CheckResult>) => { |
| 148 | - if (result?.callResult === UpgradeCallResult.OK) { | 149 | + if (result?.data) { |
| 149 | - globalThis.cachedNewVersionInfo = result?.data?.newVersionInfo; | 150 | + const hasNewVersion: boolean = Boolean(result.data.isExistNewVersion); |
| 150 | - resolve(result); | 151 | + globalThis.cachedNewVersionInfo = hasNewVersion ? result.data.newVersionInfo : undefined; |
| 151 | - } else { | 152 | + SettingsSyncUtils.syncCheckResult(hasNewVersion, globalThis.abilityContext); |
| 152 | - resolve(result); | 153 | + } else if (result?.callResult === UpgradeCallResult.OK) { |
| 154 | + globalThis.cachedNewVersionInfo = undefined; | ||
| 155 | + SettingsSyncUtils.syncReset(globalThis.abilityContext); | ||
| 153 | } | 156 | } |
| 157 | + resolve(result); | ||
| 154 | }); | 158 | }); |
| 155 | }); | 159 | }); |
| 156 | } | 160 | } |
| @@ -222,6 +226,7 @@ export class OtaUpdateManager { | |||
| 222 | async cancel(): Promise<void> { | 226 | async cancel(): Promise<void> { |
| 223 | this.setUpdateState(UpdateState.CHECK_SUCCESS); | 227 | this.setUpdateState(UpdateState.CHECK_SUCCESS); |
| 224 | this.setDownloadProgress(0); | 228 | this.setDownloadProgress(0); |
| 229 | + SettingsSyncUtils.syncCancel(globalThis.abilityContext); | ||
| 225 | await UpgradeAdapter.getInstance().getNotifyInstance()?.cancelAll(); | 230 | await UpgradeAdapter.getInstance().getNotifyInstance()?.cancelAll(); |
| 226 | this.updateManager.cancel(); | 231 | this.updateManager.cancel(); |
| 227 | } | 232 | } |
| @@ -235,6 +240,25 @@ export class OtaUpdateManager { | |||
| 235 | return this.updateManager.getCurrentVersionInfo(); | 240 | return this.updateManager.getCurrentVersionInfo(); |
| 236 | } | 241 | } |
| 237 | 242 | ||
| 243 | + /** | ||
| 244 | + * 获取升级策略 | ||
| 245 | + * | ||
| 246 | + * @returns resolve 升级策略/reject 错误信息 | ||
| 247 | + */ | ||
| 248 | + async getUpdatePolicy(): Promise<update.UpgradePolicy> { | ||
| 249 | + return this.updateManager.getUpdatePolicy(); | ||
| 250 | + } | ||
| 251 | + | ||
| 252 | + /** | ||
| 253 | + * 设置升级策略 | ||
| 254 | + * | ||
| 255 | + * @param policy 升级策略 | ||
| 256 | + * @returns resolve 设置结果/reject 错误信息 | ||
| 257 | + */ | ||
| 258 | + async setUpdatePolicy(policy: update.UpgradePolicy): Promise<void> { | ||
| 259 | + await this.updateManager.setUpdatePolicy(policy); | ||
| 260 | + } | ||
| 261 | + | ||
| 238 | /** | 262 | /** |
| 239 | * 取升级状态缓存数据 | 263 | * 取升级状态缓存数据 |
| 240 | * | 264 | * |
| @@ -295,6 +319,12 @@ export class OtaUpdateManager { | |||
| 295 | this.stateObj = StateManager.createInstance(otaStatus); | 319 | this.stateObj = StateManager.createInstance(otaStatus); |
| 296 | } | 320 | } |
| 297 | this.stateObj.refresh(otaStatus); | 321 | this.stateObj.refresh(otaStatus); |
| 322 | + if (otaStatus.status === UpdateState.DOWNLOADING || otaStatus.status === UpdateState.DOWNLOAD_PAUSE) { | ||
| 323 | + let currentProgress: number = this.getDownloadProgress() ?? 0; | ||
| 324 | + if (currentProgress > this.stateObj.percent) { | ||
| 325 | + this.stateObj.percent = currentProgress; | ||
| 326 | + } | ||
| 327 | + } | ||
| 298 | this.lastStatus = otaStatus.status; | 328 | this.lastStatus = otaStatus.status; |
| 299 | this.setUpdateState(this.stateObj.state); | 329 | this.setUpdateState(this.stateObj.state); |
| 300 | this.setDownloadProgress(this.stateObj.percent); | 330 | this.setDownloadProgress(this.stateObj.percent); |
| @@ -318,6 +348,12 @@ export class OtaUpdateManager { | |||
| 318 | private async handleMessage(message: Message): Promise<void> { | 348 | private async handleMessage(message: Message): Promise<void> { |
| 319 | let eventInfo: CustomEventInfo = message.eventInfo as CustomEventInfo; | 349 | let eventInfo: CustomEventInfo = message.eventInfo as CustomEventInfo; |
| 320 | let otaStatus: OtaStatus = this.getFormattedOtaStatus(eventInfo); | 350 | let otaStatus: OtaStatus = this.getFormattedOtaStatus(eventInfo); |
| 351 | + const previousEndReason = this.stateObj?.otaStatus?.endReason; | ||
| 352 | + if (eventInfo.eventId === update.EventId.EVENT_DOWNLOAD_FAIL && !otaStatus.endReason && | ||
| 353 | + Number(previousEndReason) === ErrorCode.VERIFY_PACKAGE_FAIL) { | ||
| 354 | + this.log('Ignore empty duplicate download failure after verify failure.'); | ||
| 355 | + return; | ||
| 356 | + } | ||
| 321 | if (this.isTerminalState(otaStatus)) { | 357 | if (this.isTerminalState(otaStatus)) { |
| 322 | globalThis.lastVersionName = await VersionUtils.obtainNewVersionName(eventInfo?.taskBody); | 358 | globalThis.lastVersionName = await VersionUtils.obtainNewVersionName(eventInfo?.taskBody); |
| 323 | } | 359 | } |
| @@ -334,6 +370,7 @@ export class OtaUpdateManager { | |||
| 334 | LogUtils.warn('UpdateManager', 'notifyUpdateStatus is repeating, abandon.'); | 370 | LogUtils.warn('UpdateManager', 'notifyUpdateStatus is repeating, abandon.'); |
| 335 | return; | 371 | return; |
| 336 | } | 372 | } |
| 373 | + SettingsSyncUtils.syncFromOtaStatus(otaStatus, context); | ||
| 337 | if (!globalThis.cachedNewVersionInfo && !this.isTerminalState(otaStatus)) { | 374 | if (!globalThis.cachedNewVersionInfo && !this.isTerminalState(otaStatus)) { |
| 338 | await this.getNewVersion(); | 375 | await this.getNewVersion(); |
| 339 | } | 376 | } |
| @@ -454,8 +491,12 @@ export class OtaUpdateManager { | |||
| 454 | status = UpdateState.INIT; | 491 | status = UpdateState.INIT; |
| 455 | break; | 492 | break; |
| 456 | case update.EventId.EVENT_DOWNLOAD_START: | 493 | case update.EventId.EVENT_DOWNLOAD_START: |
| 494 | + case update.EventId.EVENT_DOWNLOAD_RESUME: | ||
| 457 | status = UpdateState.DOWNLOADING; | 495 | status = UpdateState.DOWNLOADING; |
| 458 | break; | 496 | break; |
| 497 | + case update.EventId.EVENT_DOWNLOAD_PAUSE: | ||
| 498 | + status = UpdateState.DOWNLOAD_PAUSE; | ||
| 499 | + break; | ||
| 459 | case update.EventId.EVENT_DOWNLOAD_SUCCESS: | 500 | case update.EventId.EVENT_DOWNLOAD_SUCCESS: |
| 460 | status = UpdateState.DOWNLOAD_SUCCESS; | 501 | status = UpdateState.DOWNLOAD_SUCCESS; |
| 461 | break; | 502 | break; |
| @@ -473,4 +514,4 @@ export class OtaUpdateManager { | |||
| 473 | } | 514 | } |
| 474 | return status; | 515 | return status; |
| 475 | } | 516 | } |
| 476 | -} | 517 | +} |
| @@ -14,6 +14,7 @@ | |||
| 14 | */ | 14 | */ |
| 15 | 15 | ||
| 16 | import type common from '@ohos.app.ability.common'; | 16 | import type common from '@ohos.app.ability.common'; |
| 17 | +import Settings from '@ohos.settings'; | ||
| 17 | import update from '@ohos.update'; | 18 | import update from '@ohos.update'; |
| 18 | import { ErrorCode, OtaStatus, UpdateState } from '@ohos/common/src/main/ets/const/update_const'; | 19 | import { ErrorCode, OtaStatus, UpdateState } from '@ohos/common/src/main/ets/const/update_const'; |
| 19 | import { LogUtils } from '@ohos/common/src/main/ets/util/LogUtils'; | 20 | import { LogUtils } from '@ohos/common/src/main/ets/util/LogUtils'; |
| @@ -22,7 +23,28 @@ import { OtaUpdateManager } from '../manager/OtaUpdateManager'; | |||
| 22 | import VersionUtils from '../util/VersionUtils'; | 23 | import VersionUtils from '../util/VersionUtils'; |
| 23 | import ToastUtils from '../util/ToastUtils'; | 24 | import ToastUtils from '../util/ToastUtils'; |
| 24 | import { UpgradeAdapter } from '../UpgradeAdapter'; | 25 | import { UpgradeAdapter } from '../UpgradeAdapter'; |
| 25 | -import { NotificationManager } from '../notify/NotificationManager'; | 26 | +import { SceneBoardDialogManager, SceneBoardDialogType } from '../notify/SceneBoardDialogManager'; |
| 27 | + | ||
| 28 | +const SETTINGS_DEVICE_PROVISIONED = 'device_provisioned'; | ||
| 29 | +const SETTINGS_USER_SETUP_COMPLETE = 'user_setup_complete'; | ||
| 30 | +const SETTINGS_IS_OTA_FINISHED = 'is_ota_finished'; | ||
| 31 | + | ||
| 32 | +function isInOobe(context: common.Context): boolean { | ||
| 33 | + try { | ||
| 34 | + const deviceProvisioned = Settings.getValueSync(context, SETTINGS_DEVICE_PROVISIONED, '0', | ||
| 35 | + Settings.domainName.DEVICE_SHARED); | ||
| 36 | + const userSetupComplete = Settings.getValueSync(context, SETTINGS_USER_SETUP_COMPLETE, '0', | ||
| 37 | + Settings.domainName.USER_SECURITY); | ||
| 38 | + const isOtaFinished = Settings.getValueSync(context, SETTINGS_IS_OTA_FINISHED, '1', | ||
| 39 | + Settings.domainName.USER_SECURITY); | ||
| 40 | + const result = !(deviceProvisioned === '1' && userSetupComplete === '1' && isOtaFinished === '1'); | ||
| 41 | + LogUtils.info('StateManager', `isInOobe=${result}`); | ||
| 42 | + return result; | ||
| 43 | + } catch (err) { | ||
| 44 | + LogUtils.error('StateManager', `isInOobe query failed: ${JSON.stringify(err)}`); | ||
| 45 | + return true; | ||
| 46 | + } | ||
| 47 | +} | ||
| 26 | 48 | ||
| 27 | /** | 49 | /** |
| 28 | * 状态工厂 | 50 | * 状态工厂 |
| @@ -263,8 +285,12 @@ export class Init extends BaseState { | |||
| 263 | this.actionSet.push(UpdateAction.CHECK_NEW_VERSION); | 285 | this.actionSet.push(UpdateAction.CHECK_NEW_VERSION); |
| 264 | } | 286 | } |
| 265 | 287 | ||
| 266 | - async notify(): Promise<void> { | 288 | + async notify(context?: common.Context, eventId?: update.EventId): Promise<void> { |
| 267 | await UpgradeAdapter.getInstance().getNotifyInstance()?.cancelAll(); | 289 | await UpgradeAdapter.getInstance().getNotifyInstance()?.cancelAll(); |
| 290 | + // 升级包失效时清空通知栏 | ||
| 291 | + if (context && eventId == update.EventId.EVENT_TASK_CANCEL) { | ||
| 292 | + DialogUtils.showVersionInfoChangedDialog(context, this.otaStatus, eventId); | ||
| 293 | + } | ||
| 268 | } | 294 | } |
| 269 | } | 295 | } |
| 270 | 296 | ||
| @@ -298,6 +324,25 @@ export class CheckSuccess extends BaseState { | |||
| 298 | DialogUtils.showDownloadFailDialog(context, this.otaStatus, eventId); | 324 | DialogUtils.showDownloadFailDialog(context, this.otaStatus, eventId); |
| 299 | break; | 325 | break; |
| 300 | } | 326 | } |
| 327 | + return; | ||
| 328 | + } | ||
| 329 | + const isBackgroundReminder = globalThis.AbilityStatus !== 'ON_FOREGROUND' && | ||
| 330 | + (eventId === update.EventId.EVENT_DOWNLOAD_WAIT || eventId === update.EventId.EVENT_TASK_RECEIVE); | ||
| 331 | + if (isBackgroundReminder && isInOobe(context)) { | ||
| 332 | + LogUtils.info('StateManager', 'OOBE is not complete, skip download reminder.'); | ||
| 333 | + return; | ||
| 334 | + } | ||
| 335 | + if (eventId === update.EventId.EVENT_DOWNLOAD_WAIT && globalThis.AbilityStatus !== 'ON_FOREGROUND') { | ||
| 336 | + LogUtils.info('StateManager', 'Check success in background, show download reminder dialog.'); | ||
| 337 | + await SceneBoardDialogManager.showDialog(context, { | ||
| 338 | + dialogType: SceneBoardDialogType.DOWNLOAD_REMINDER | ||
| 339 | + }); | ||
| 340 | + return; | ||
| 341 | + } | ||
| 342 | + if (eventId === update.EventId.EVENT_TASK_RECEIVE && globalThis.AbilityStatus !== 'ON_FOREGROUND') { | ||
| 343 | + LogUtils.info('StateManager', 'Download reminder reached notification threshold, show notification.'); | ||
| 344 | + const versionName = await VersionUtils.obtainNewVersionName(globalThis.cachedNewVersionInfo); | ||
| 345 | + await UpgradeAdapter.getInstance().getNotifyInstance()?.showDownloadReminder(versionName, context); | ||
| 301 | } | 346 | } |
| 302 | } | 347 | } |
| 303 | } | 348 | } |
| @@ -318,7 +363,10 @@ export class Downloading extends BaseState { | |||
| 318 | this.buttonClickAction = UpdateAction.CANCEL; | 363 | this.buttonClickAction = UpdateAction.CANCEL; |
| 319 | } | 364 | } |
| 320 | 365 | ||
| 321 | - async notify(context: common.Context): Promise<void> { | 366 | + async notify(context: common.Context, eventId?: update.EventId): Promise<void> { |
| 367 | + if (eventId == update.EventId.EVENT_DOWNLOAD_RESUME) { | ||
| 368 | + DialogUtils.clearDownloadNoNetworkDialog(); | ||
| 369 | + } | ||
| 322 | if (this.percent == 100) { | 370 | if (this.percent == 100) { |
| 323 | await UpgradeAdapter.getInstance().getNotifyInstance()?.cancelAll(); | 371 | await UpgradeAdapter.getInstance().getNotifyInstance()?.cancelAll(); |
| 324 | return; | 372 | return; |
| @@ -376,30 +424,51 @@ export class DownloadPause extends BaseState { | |||
| 376 | } | 424 | } |
| 377 | 425 | ||
| 378 | async notify(context: common.Context, eventId?: update.EventId): Promise<void> { | 426 | async notify(context: common.Context, eventId?: update.EventId): Promise<void> { |
| 379 | - if (!VersionUtils.isInNewVersionPage()) { | 427 | + const otaStatus = this.otaStatus; |
| 428 | + if (!otaStatus) { | ||
| 380 | return; | 429 | return; |
| 381 | } | 430 | } |
| 382 | - if (this.otaStatus?.endReason) { | 431 | + const isDownloadPauseEvent = eventId == update.EventId.EVENT_DOWNLOAD_PAUSE; |
| 383 | - await UpgradeAdapter.getInstance().getNotifyInstance()?.cancelAll(); | 432 | + if (isDownloadPauseEvent) { |
| 384 | - switch (Number(this.otaStatus?.endReason)) { | 433 | + if (VersionUtils.isInNewVersionPage()) { |
| 385 | - case ErrorCode.NETWORK_ERROR: | 434 | + await UpgradeAdapter.getInstance().getNotifyInstance()?.cancelAll(); |
| 386 | - if (eventId == update.EventId.EVENT_DOWNLOAD_PAUSE) { | 435 | + } else { |
| 387 | - DialogUtils.showDownloadNoNetworkDialog(context, this.otaStatus, eventId); | 436 | + let versionName = await VersionUtils.obtainNewVersionName(globalThis.cachedNewVersionInfo); |
| 388 | - } else { | 437 | + await UpgradeAdapter.getInstance().getNotifyInstance()?.showDownloadPaused(versionName, this.percent, context); |
| 389 | - let message = await context.resourceManager.getString($r('app.string.network_err_toast').id); | ||
| 390 | - ToastUtils.showToast(message); | ||
| 391 | - } | ||
| 392 | - break; | ||
| 393 | - case ErrorCode.NETWORK_NOT_ALLOW: | ||
| 394 | - if (eventId == update.EventId.EVENT_DOWNLOAD_PAUSE) { | ||
| 395 | - DialogUtils.showDownloadNoNetworkDialog(context, this.otaStatus, eventId); | ||
| 396 | - } | ||
| 397 | - break; | ||
| 398 | - default: | ||
| 399 | - DialogUtils.showDownloadFailDialog(context, this.otaStatus, eventId); | ||
| 400 | - break; | ||
| 401 | } | 438 | } |
| 402 | } | 439 | } |
| 440 | + if (!VersionUtils.isInNewVersionPage()) { | ||
| 441 | + if (globalThis.AbilityStatus !== 'ON_FOREGROUND' && isDownloadPauseEvent) { | ||
| 442 | + DialogUtils.delayShowDownloadNoNetworkDialog(context, otaStatus, eventId); | ||
| 443 | + } | ||
| 444 | + return; | ||
| 445 | + } | ||
| 446 | + if (!otaStatus.endReason) { | ||
| 447 | + if (isDownloadPauseEvent) { | ||
| 448 | + DialogUtils.delayShowDownloadNoNetworkDialog(context, otaStatus, eventId); | ||
| 449 | + } | ||
| 450 | + return; | ||
| 451 | + } | ||
| 452 | + switch (Number(otaStatus.endReason)) { | ||
| 453 | + case ErrorCode.NETWORK_ERROR: | ||
| 454 | + if (isDownloadPauseEvent) { | ||
| 455 | + DialogUtils.delayShowDownloadNoNetworkDialog(context, otaStatus, eventId); | ||
| 456 | + } else { | ||
| 457 | + await UpgradeAdapter.getInstance().getNotifyInstance()?.cancelAll(); | ||
| 458 | + let message = await context.resourceManager.getString($r('app.string.network_err_toast').id); | ||
| 459 | + ToastUtils.showToast(message); | ||
| 460 | + } | ||
| 461 | + break; | ||
| 462 | + case ErrorCode.NETWORK_NOT_ALLOW: | ||
| 463 | + if (isDownloadPauseEvent) { | ||
| 464 | + DialogUtils.delayShowDownloadNoNetworkDialog(context, otaStatus, eventId); | ||
| 465 | + } | ||
| 466 | + break; | ||
| 467 | + default: | ||
| 468 | + await UpgradeAdapter.getInstance().getNotifyInstance()?.cancelAll(); | ||
| 469 | + DialogUtils.showDownloadFailDialog(context, otaStatus, eventId); | ||
| 470 | + break; | ||
| 471 | + } | ||
| 403 | } | 472 | } |
| 404 | } | 473 | } |
| 405 | 474 | ||
| @@ -424,6 +493,10 @@ export class DownloadFailed extends BaseState { | |||
| 424 | await UpgradeAdapter.getInstance().getNotifyInstance()?.cancelAll(); | 493 | await UpgradeAdapter.getInstance().getNotifyInstance()?.cancelAll(); |
| 425 | switch (Number(this.otaStatus?.endReason)) { | 494 | switch (Number(this.otaStatus?.endReason)) { |
| 426 | case ErrorCode.VERIFY_PACKAGE_FAIL: | 495 | case ErrorCode.VERIFY_PACKAGE_FAIL: |
| 496 | + await UpgradeAdapter.getInstance().getNotifyInstance()?.showVerifyFailed(context); | ||
| 497 | + if (globalThis.AbilityStatus !== 'ON_FOREGROUND') { | ||
| 498 | + break; | ||
| 499 | + } | ||
| 427 | DialogUtils.showVerifyFailDialog(context, this.otaStatus, eventId); | 500 | DialogUtils.showVerifyFailDialog(context, this.otaStatus, eventId); |
| 428 | break; | 501 | break; |
| 429 | default: | 502 | default: |
| @@ -454,17 +527,20 @@ export class DownloadSuccess extends BaseState { | |||
| 454 | async notify(context: common.Context, eventId?: update.EventId): Promise<void> { | 527 | async notify(context: common.Context, eventId?: update.EventId): Promise<void> { |
| 455 | let isABInstall = await VersionUtils.isABInstall(); | 528 | let isABInstall = await VersionUtils.isABInstall(); |
| 456 | LogUtils.info('StateManager:', 'notify ab flag ' + isABInstall + ',eventId:' + eventId); | 529 | LogUtils.info('StateManager:', 'notify ab flag ' + isABInstall + ',eventId:' + eventId); |
| 457 | - if (eventId == update.EventId.EVENT_DOWNLOAD_SUCCESS && isABInstall) { | 530 | + if (eventId == update.EventId.EVENT_DOWNLOAD_SUCCESS) { |
| 458 | - OtaUpdateManager.getInstance().upgrade(update.Order.INSTALL); | 531 | + if (isABInstall) { |
| 532 | + OtaUpdateManager.getInstance().upgrade(update.Order.INSTALL); | ||
| 533 | + return; | ||
| 534 | + } | ||
| 535 | + if (VersionUtils.isInNewVersionPage()) { | ||
| 536 | + AppStorage.Set('isClickInstall', 1); | ||
| 537 | + } | ||
| 459 | return; | 538 | return; |
| 460 | } | 539 | } |
| 461 | 540 | ||
| 462 | if (eventId == update.EventId.EVENT_UPGRADE_WAIT && !isABInstall) { | 541 | if (eventId == update.EventId.EVENT_UPGRADE_WAIT && !isABInstall) { |
| 463 | - LogUtils.info('StateManager', 'manual download complete to count down'); | 542 | + LogUtils.info('StateManager', 'download complete, show night upgrade notice'); |
| 464 | - if (!VersionUtils.isInNewVersionPage()) { | 543 | + await UpgradeAdapter.getInstance().getNotifyInstance()?.showNightUpgradeNotice(context); |
| 465 | - NotificationManager.startToNewVersion(context); | ||
| 466 | - } | ||
| 467 | - AppStorage.Set('isClickInstall', 1); | ||
| 468 | return; | 544 | return; |
| 469 | } | 545 | } |
| 470 | 546 | ||
| @@ -541,13 +617,12 @@ export class InstallSuccess extends BaseState { | |||
| 541 | this.buttonClickAction = UpdateAction.REBOOT; | 617 | this.buttonClickAction = UpdateAction.REBOOT; |
| 542 | } | 618 | } |
| 543 | 619 | ||
| 544 | - async notify(context: common.Context, eventId?: update.EventId): Promise<void> { | 620 | + async notify(_context: common.Context, eventId?: update.EventId): Promise<void> { |
| 545 | if (eventId == update.EventId.EVENT_APPLY_WAIT) { | 621 | if (eventId == update.EventId.EVENT_APPLY_WAIT) { |
| 546 | - LogUtils.info('StateManager', 'ab install complete to count down'); | 622 | + LogUtils.info('StateManager', 'AB install complete, handle countdown only in new version page.'); |
| 547 | - if (!VersionUtils.isInNewVersionPage()) { | 623 | + if (VersionUtils.isInNewVersionPage()) { |
| 548 | - NotificationManager.startToNewVersion(context); | 624 | + AppStorage.Set('isClickInstall', 1); |
| 549 | } | 625 | } |
| 550 | - AppStorage.Set('isClickInstall', 1); | ||
| 551 | } | 626 | } |
| 552 | } | 627 | } |
| 553 | } | 628 | } |
| @@ -652,4 +727,4 @@ export class UpgradeFailed extends InstallFailed { | |||
| 652 | super(); | 727 | super(); |
| 653 | this.state = UpdateState.UPGRADE_FAILED; | 728 | this.state = UpdateState.UPGRADE_FAILED; |
| 654 | } | 729 | } |
| 655 | -} | 730 | +} |
| @@ -16,7 +16,14 @@ | |||
| 16 | import notification from '@ohos.notificationManager'; | 16 | import notification from '@ohos.notificationManager'; |
| 17 | import wantAgent from '@ohos.app.ability.wantAgent'; | 17 | import wantAgent from '@ohos.app.ability.wantAgent'; |
| 18 | import type common from '@ohos.app.ability.common'; | 18 | import type common from '@ohos.app.ability.common'; |
| 19 | -import { BusinessError, Action, PACKAGE_NAME } from '@ohos/common/src/main/ets/const/update_const'; | 19 | +import { |
| 20 | + BusinessError, | ||
| 21 | + Action, | ||
| 22 | + PACKAGE_NAME, | ||
| 23 | + NIGHT_UPGRADE_START, | ||
| 24 | + NIGHT_UPGRADE_END | ||
| 25 | +} from '@ohos/common/src/main/ets/const/update_const'; | ||
| 26 | +import { FormatUtils } from '@ohos/common/src/main/ets/util/FormatUtils'; | ||
| 20 | import { LogUtils } from '@ohos/common/src/main/ets/util/LogUtils'; | 27 | import { LogUtils } from '@ohos/common/src/main/ets/util/LogUtils'; |
| 21 | import { INotify } from '@ohos/common/src/main/ets/manager/UpgradeInterface'; | 28 | import { INotify } from '@ohos/common/src/main/ets/manager/UpgradeInterface'; |
| 22 | 29 | ||
| @@ -26,6 +33,35 @@ import { INotify } from '@ohos/common/src/main/ets/manager/UpgradeInterface'; | |||
| 26 | * @since 2022-06-05 | 33 | * @since 2022-06-05 |
| 27 | */ | 34 | */ |
| 28 | export class NotificationHelper implements INotify { | 35 | export class NotificationHelper implements INotify { |
| 36 | + /** | ||
| 37 | + * 下载提醒通知 | ||
| 38 | + * | ||
| 39 | + * @param versionName 版本号 | ||
| 40 | + * @param context 上下文 | ||
| 41 | + */ | ||
| 42 | + async showDownloadReminder(versionName: string, context: common.Context): Promise<void> { | ||
| 43 | + let request: notification.NotificationRequest = { | ||
| 44 | + content: { | ||
| 45 | + notificationContentType: notification.ContentType.NOTIFICATION_CONTENT_BASIC_TEXT, | ||
| 46 | + normal: { | ||
| 47 | + title: await context.resourceManager.getString($r('app.string.software_update').id), | ||
| 48 | + text: await context.resourceManager.getString($r('app.string.desktop_download_dialog_content').id), | ||
| 49 | + additionalText: versionName | ||
| 50 | + } | ||
| 51 | + }, | ||
| 52 | + wantAgent: await wantAgent.getWantAgent(this.downloadingWantAgentInfo), | ||
| 53 | + id: 5, | ||
| 54 | + isAlertOnce: true, | ||
| 55 | + notificationSlotType: notification.SlotType.SERVICE_INFORMATION, | ||
| 56 | + deliveryTime: new Date().getTime() | ||
| 57 | + }; | ||
| 58 | + await notification.publish(request).then(() => { | ||
| 59 | + this.logInfo('showDownloadReminder publish promise success.'); | ||
| 60 | + }).catch((err: BusinessError) => { | ||
| 61 | + this.logError('showDownloadReminder publish promise failed because ' + JSON.stringify(err)); | ||
| 62 | + }); | ||
| 63 | + } | ||
| 64 | + | ||
| 29 | /** | 65 | /** |
| 30 | * 跳转信息--跳转到搜包页面 | 66 | * 跳转信息--跳转到搜包页面 |
| 31 | */ | 67 | */ |
| @@ -54,6 +90,34 @@ export class NotificationHelper implements INotify { | |||
| 54 | wantAgentFlags: [wantAgent.WantAgentFlags.CONSTANT_FLAG], | 90 | wantAgentFlags: [wantAgent.WantAgentFlags.CONSTANT_FLAG], |
| 55 | }; | 91 | }; |
| 56 | 92 | ||
| 93 | + /** | ||
| 94 | + * 跳转信息--夜间升级直接安装 | ||
| 95 | + */ | ||
| 96 | + private installWantAgentInfo: wantAgent.WantAgentInfo = { | ||
| 97 | + wants: [{ | ||
| 98 | + bundleName: PACKAGE_NAME, | ||
| 99 | + abilityName: 'ServiceExtAbility', | ||
| 100 | + action: Action.NOTIFICATION_INSTALL | ||
| 101 | + }], | ||
| 102 | + actionType: wantAgent.OperationType.START_ABILITY, | ||
| 103 | + requestCode: 0, | ||
| 104 | + wantAgentFlags: [wantAgent.WantAgentFlags.CONSTANT_FLAG], | ||
| 105 | + }; | ||
| 106 | + | ||
| 107 | + /** | ||
| 108 | + * 跳转信息--夜间升级设置页 | ||
| 109 | + */ | ||
| 110 | + private settingsWantAgentInfo: wantAgent.WantAgentInfo = { | ||
| 111 | + wants: [{ | ||
| 112 | + bundleName: PACKAGE_NAME, | ||
| 113 | + abilityName: 'ServiceExtAbility', | ||
| 114 | + action: Action.NOTIFICATION_SETTINGS | ||
| 115 | + }], | ||
| 116 | + actionType: wantAgent.OperationType.START_ABILITY, | ||
| 117 | + requestCode: 0, | ||
| 118 | + wantAgentFlags: [wantAgent.WantAgentFlags.CONSTANT_FLAG], | ||
| 119 | + }; | ||
| 120 | + | ||
| 57 | /** | 121 | /** |
| 58 | * 下载进度通知 | 122 | * 下载进度通知 |
| 59 | * | 123 | * |
| @@ -63,6 +127,9 @@ export class NotificationHelper implements INotify { | |||
| 63 | */ | 127 | */ |
| 64 | async showDownloading(version: string, progress: number, context: common.Context): Promise<void> { | 128 | async showDownloading(version: string, progress: number, context: common.Context): Promise<void> { |
| 65 | let templateName: string = 'downloadTemplate'; | 129 | let templateName: string = 'downloadTemplate'; |
| 130 | + let downloadText: string = await context.resourceManager.getString($r('app.string.software_download_progress').id); | ||
| 131 | + let downloadTitle: string = downloadText + ':' + progress + '%'; | ||
| 132 | + this.logInfo('showDownloading start, version=' + version + ', progress=' + progress); | ||
| 66 | if (!globalThis.isSupportTemplate) { | 133 | if (!globalThis.isSupportTemplate) { |
| 67 | globalThis.isSupportTemplate = await notification.isSupportTemplate(templateName).catch((err: BusinessError) => { | 134 | globalThis.isSupportTemplate = await notification.isSupportTemplate(templateName).catch((err: BusinessError) => { |
| 68 | this.logError('showDownloading isSupportTemplate failed because ' + JSON.stringify(err)); | 135 | this.logError('showDownloading isSupportTemplate failed because ' + JSON.stringify(err)); |
| @@ -77,14 +144,14 @@ export class NotificationHelper implements INotify { | |||
| 77 | content: { | 144 | content: { |
| 78 | notificationContentType: notification.ContentType.NOTIFICATION_CONTENT_BASIC_TEXT, | 145 | notificationContentType: notification.ContentType.NOTIFICATION_CONTENT_BASIC_TEXT, |
| 79 | normal: { | 146 | normal: { |
| 80 | - title: await context.resourceManager.getString($r('app.string.software_update').id), | 147 | + title: downloadTitle, |
| 81 | - text: await context.resourceManager.getString($r('app.string.software_download_progress').id) | 148 | + text: version |
| 82 | }, | 149 | }, |
| 83 | }, | 150 | }, |
| 84 | template: { | 151 | template: { |
| 85 | name: 'downloadTemplate', | 152 | name: 'downloadTemplate', |
| 86 | data: { | 153 | data: { |
| 87 | - title: await context.resourceManager.getString($r('app.string.software_download_progress').id), | 154 | + title: downloadTitle, |
| 88 | fileName: version, | 155 | fileName: version, |
| 89 | progressValue: progress | 156 | progressValue: progress |
| 90 | } | 157 | } |
| @@ -92,6 +159,7 @@ export class NotificationHelper implements INotify { | |||
| 92 | wantAgent: await wantAgent.getWantAgent(this.downloadingWantAgentInfo), | 159 | wantAgent: await wantAgent.getWantAgent(this.downloadingWantAgentInfo), |
| 93 | id: 5, | 160 | id: 5, |
| 94 | label: '111', | 161 | label: '111', |
| 162 | + isAlertOnce: true, | ||
| 95 | notificationSlotType: notification.SlotType.SERVICE_INFORMATION, | 163 | notificationSlotType: notification.SlotType.SERVICE_INFORMATION, |
| 96 | deliveryTime: new Date().getTime() | 164 | deliveryTime: new Date().getTime() |
| 97 | } | 165 | } |
| @@ -102,6 +170,65 @@ export class NotificationHelper implements INotify { | |||
| 102 | }); | 170 | }); |
| 103 | } | 171 | } |
| 104 | 172 | ||
| 173 | + /** | ||
| 174 | + * 下载暂停通知 | ||
| 175 | + * | ||
| 176 | + * @param version 版本号 | ||
| 177 | + * @param progress 进度 | ||
| 178 | + * @param context 上下文 | ||
| 179 | + */ | ||
| 180 | + async showDownloadPaused(version: string, progress: number, context: common.Context): Promise<void> { | ||
| 181 | + let pausedText: string = | ||
| 182 | + await context.resourceManager.getString($r('app.string.download_status_download_pause').id); | ||
| 183 | + let request: notification.NotificationRequest = { | ||
| 184 | + content: { | ||
| 185 | + notificationContentType: notification.ContentType.NOTIFICATION_CONTENT_BASIC_TEXT, | ||
| 186 | + normal: { | ||
| 187 | + title: pausedText, | ||
| 188 | + text: version | ||
| 189 | + } | ||
| 190 | + }, | ||
| 191 | + wantAgent: await wantAgent.getWantAgent(this.downloadingWantAgentInfo), | ||
| 192 | + id: 5, | ||
| 193 | + label: '111', | ||
| 194 | + isAlertOnce: true, | ||
| 195 | + notificationSlotType: notification.SlotType.SERVICE_INFORMATION, | ||
| 196 | + deliveryTime: new Date().getTime() | ||
| 197 | + }; | ||
| 198 | + await notification.publish(request).then(() => { | ||
| 199 | + this.logInfo('showDownloadPaused publish promise success.'); | ||
| 200 | + }).catch((err: BusinessError) => { | ||
| 201 | + this.logError('showDownloadPaused publish promise failed because ' + JSON.stringify(err)); | ||
| 202 | + }); | ||
| 203 | + } | ||
| 204 | + | ||
| 205 | + /** | ||
| 206 | + * 升级包校验失败通知 | ||
| 207 | + * | ||
| 208 | + * @param context 上下文 | ||
| 209 | + */ | ||
| 210 | + async showVerifyFailed(context: common.Context): Promise<void> { | ||
| 211 | + let request: notification.NotificationRequest = { | ||
| 212 | + content: { | ||
| 213 | + notificationContentType: notification.ContentType.NOTIFICATION_CONTENT_BASIC_TEXT, | ||
| 214 | + normal: { | ||
| 215 | + title: await context.resourceManager.getString($r('app.string.download_verify_fail_notification_title').id), | ||
| 216 | + text: await context.resourceManager.getString($r('app.string.download_verify_fail_notification_content').id) | ||
| 217 | + } | ||
| 218 | + }, | ||
| 219 | + wantAgent: await wantAgent.getWantAgent(this.checkWantAgentInfo), | ||
| 220 | + id: 5, | ||
| 221 | + isAlertOnce: true, | ||
| 222 | + notificationSlotType: notification.SlotType.SERVICE_INFORMATION, | ||
| 223 | + deliveryTime: new Date().getTime() | ||
| 224 | + }; | ||
| 225 | + await notification.publish(request).then(() => { | ||
| 226 | + this.logInfo('showVerifyFailed publish promise success.'); | ||
| 227 | + }).catch((err: BusinessError) => { | ||
| 228 | + this.logError('showVerifyFailed publish promise failed because ' + JSON.stringify(err)); | ||
| 229 | + }); | ||
| 230 | + } | ||
| 231 | + | ||
| 105 | /** | 232 | /** |
| 106 | * 下载进度通知 | 233 | * 下载进度通知 |
| 107 | * | 234 | * |
| @@ -111,6 +238,8 @@ export class NotificationHelper implements INotify { | |||
| 111 | */ | 238 | */ |
| 112 | async showInstalling(version: string, progress: number, context: common.Context): Promise<void> { | 239 | async showInstalling(version: string, progress: number, context: common.Context): Promise<void> { |
| 113 | let templateName: string = 'installTemplate'; | 240 | let templateName: string = 'installTemplate'; |
| 241 | + let installText: string = await context.resourceManager.getString($r('app.string.software_install_progress').id); | ||
| 242 | + let installProgressText: string = installText + ' ' + progress + '%'; | ||
| 114 | if (!globalThis.isSupportTemplate) { | 243 | if (!globalThis.isSupportTemplate) { |
| 115 | globalThis.isSupportTemplate = await notification.isSupportTemplate(templateName).catch((err: BusinessError) => { | 244 | globalThis.isSupportTemplate = await notification.isSupportTemplate(templateName).catch((err: BusinessError) => { |
| 116 | this.logError('showInstalling isSupportTemplate failed because ' + JSON.stringify(err)); | 245 | this.logError('showInstalling isSupportTemplate failed because ' + JSON.stringify(err)); |
| @@ -126,20 +255,21 @@ export class NotificationHelper implements INotify { | |||
| 126 | notificationContentType: notification.ContentType.NOTIFICATION_CONTENT_BASIC_TEXT, | 255 | notificationContentType: notification.ContentType.NOTIFICATION_CONTENT_BASIC_TEXT, |
| 127 | normal: { | 256 | normal: { |
| 128 | title: await context.resourceManager.getString($r('app.string.software_update').id), | 257 | title: await context.resourceManager.getString($r('app.string.software_update').id), |
| 129 | - text: await context.resourceManager.getString($r('app.string.software_install_progress').id) | 258 | + text: installProgressText |
| 130 | }, | 259 | }, |
| 131 | }, | 260 | }, |
| 132 | template: { | 261 | template: { |
| 133 | name: 'installTemplate', | 262 | name: 'installTemplate', |
| 134 | data: { | 263 | data: { |
| 135 | - title: await context.resourceManager.getString($r('app.string.software_install_progress').id), | 264 | + title: installText, |
| 136 | - fileName: version, | 265 | + fileName: installProgressText, |
| 137 | progressValue: progress | 266 | progressValue: progress |
| 138 | } | 267 | } |
| 139 | }, | 268 | }, |
| 140 | wantAgent: await wantAgent.getWantAgent(this.downloadingWantAgentInfo), | 269 | wantAgent: await wantAgent.getWantAgent(this.downloadingWantAgentInfo), |
| 141 | id: 5, | 270 | id: 5, |
| 142 | label: '111', | 271 | label: '111', |
| 272 | + isAlertOnce: true, | ||
| 143 | notificationSlotType: notification.SlotType.SERVICE_INFORMATION, | 273 | notificationSlotType: notification.SlotType.SERVICE_INFORMATION, |
| 144 | deliveryTime: new Date().getTime() | 274 | deliveryTime: new Date().getTime() |
| 145 | } | 275 | } |
| @@ -198,6 +328,46 @@ export class NotificationHelper implements INotify { | |||
| 198 | }); | 328 | }); |
| 199 | } | 329 | } |
| 200 | 330 | ||
| 331 | + /** | ||
| 332 | + * 夜间升级设置通知 | ||
| 333 | + * | ||
| 334 | + * @param context 实际上下文 | ||
| 335 | + */ | ||
| 336 | + async showNightUpgradeNotice(context: common.Context): Promise<void> { | ||
| 337 | + let description: string = | ||
| 338 | + await context.resourceManager.getString($r('app.string.setting_auto_update_description').id); | ||
| 339 | + let detailText: string = await context.resourceManager.getString($r('app.string.detail_info').id); | ||
| 340 | + let installText: string = await context.resourceManager.getString($r('app.string.btn_install_now').id); | ||
| 341 | + let request: notification.NotificationRequest = { | ||
| 342 | + content: { | ||
| 343 | + notificationContentType: notification.ContentType.NOTIFICATION_CONTENT_BASIC_TEXT, | ||
| 344 | + normal: { | ||
| 345 | + title: await context.resourceManager.getString($r('app.string.setting_auto_update_title').id), | ||
| 346 | + text: FormatUtils.formatStr(description, | ||
| 347 | + FormatUtils.getDateRangeFormat(NIGHT_UPGRADE_START, NIGHT_UPGRADE_END)) | ||
| 348 | + } | ||
| 349 | + }, | ||
| 350 | + wantAgent: await wantAgent.getWantAgent(this.downloadingWantAgentInfo), | ||
| 351 | + actionButtons: [ | ||
| 352 | + { | ||
| 353 | + title: detailText, | ||
| 354 | + wantAgent: await wantAgent.getWantAgent(this.downloadingWantAgentInfo) | ||
| 355 | + }, | ||
| 356 | + { | ||
| 357 | + title: installText, | ||
| 358 | + wantAgent: await wantAgent.getWantAgent(this.installWantAgentInfo) | ||
| 359 | + } | ||
| 360 | + ], | ||
| 361 | + id: 6, | ||
| 362 | + notificationSlotType: notification.SlotType.SERVICE_INFORMATION | ||
| 363 | + }; | ||
| 364 | + await notification.publish(request).then(() => { | ||
| 365 | + this.logInfo('showNightUpgradeNotice publish promise success.'); | ||
| 366 | + }).catch((err: BusinessError) => { | ||
| 367 | + this.logError('showNightUpgradeNotice publish promise failed because ' + JSON.stringify(err)); | ||
| 368 | + }); | ||
| 369 | + } | ||
| 370 | + | ||
| 201 | /** | 371 | /** |
| 202 | * 取消所有通知 | 372 | * 取消所有通知 |
| 203 | */ | 373 | */ |
| @@ -251,4 +421,4 @@ export class NotificationHelper implements INotify { | |||
| 251 | logError(message: string): void { | 421 | logError(message: string): void { |
| 252 | LogUtils.error('NotificationHelper', message); | 422 | LogUtils.error('NotificationHelper', message); |
| 253 | } | 423 | } |
| 254 | -} | 424 | +} |
| @@ -14,7 +14,7 @@ | |||
| 14 | */ | 14 | */ |
| 15 | 15 | ||
| 16 | import type common from '@ohos.app.ability.common'; | 16 | import type common from '@ohos.app.ability.common'; |
| 17 | -import { StartOptions, Want } from '@kit.AbilityKit'; | 17 | +import { Want } from '@kit.AbilityKit'; |
| 18 | import { | 18 | import { |
| 19 | Action, | 19 | Action, |
| 20 | PACKAGE_NAME, | 20 | PACKAGE_NAME, |
| @@ -48,6 +48,12 @@ export class NotificationManager { | |||
| 48 | case Action.NOTIFICATION_DETAIL: | 48 | case Action.NOTIFICATION_DETAIL: |
| 49 | await NotificationManager.handleDetailAction(context); | 49 | await NotificationManager.handleDetailAction(context); |
| 50 | return true; | 50 | return true; |
| 51 | + case Action.NOTIFICATION_INSTALL: | ||
| 52 | + NotificationManager.startAbility('pages/newVersion', context, true); | ||
| 53 | + return true; | ||
| 54 | + case Action.NOTIFICATION_SETTINGS: | ||
| 55 | + NotificationManager.handleSettingsAction(context); | ||
| 56 | + return true; | ||
| 51 | default: | 57 | default: |
| 52 | return false; | 58 | return false; |
| 53 | } | 59 | } |
| @@ -60,11 +66,12 @@ export class NotificationManager { | |||
| 60 | 66 | ||
| 61 | private static async handleDetailAction(context: common.Context): Promise<void> { | 67 | private static async handleDetailAction(context: common.Context): Promise<void> { |
| 62 | LogUtils.log(TAG, 'handleDetailAction'); | 68 | LogUtils.log(TAG, 'handleDetailAction'); |
| 63 | - if (await RouterUtils.isCanToNewVersion()) { | 69 | + NotificationManager.startAbility('pages/newVersion', context); |
| 64 | - NotificationManager.startAbility('pages/newVersion', context); | 70 | + } |
| 65 | - } else { | 71 | + |
| 66 | - NotificationManager.startAbility('pages/index', context); | 72 | + private static handleSettingsAction(context: common.Context): void { |
| 67 | - } | 73 | + LogUtils.log(TAG, 'handleSettingsAction'); |
| 74 | + NotificationManager.startAbility('pages/settingsView', context); | ||
| 68 | } | 75 | } |
| 69 | 76 | ||
| 70 | public static async startToNewVersion(context: common.Context): Promise<void> { | 77 | public static async startToNewVersion(context: common.Context): Promise<void> { |
| @@ -73,16 +80,16 @@ export class NotificationManager { | |||
| 73 | } | 80 | } |
| 74 | } | 81 | } |
| 75 | 82 | ||
| 76 | - private static startAbility(uri: string, context: common.Context): void { | 83 | + private static startAbility(uri: string, context: common.Context, autoInstall?: boolean): void { |
| 77 | let want: Want = { | 84 | let want: Want = { |
| 78 | bundleName: PACKAGE_NAME, | 85 | bundleName: PACKAGE_NAME, |
| 79 | abilityName: MAIN_ABILITY_NAME, | 86 | abilityName: MAIN_ABILITY_NAME, |
| 80 | uri: uri | 87 | uri: uri |
| 81 | }; | 88 | }; |
| 82 | - let options: StartOptions = { | 89 | + // 新增直接直接安装逻辑 |
| 83 | - windowMode: 0, | 90 | + if (autoInstall !== undefined) { |
| 84 | - displayId: 2 | 91 | + want.parameters = { autoInstall: autoInstall }; |
| 85 | - }; | 92 | + } |
| 86 | - UpdateUtils.startAbility(context, want, options); | 93 | + UpdateUtils.startAbility(context, want); |
| 87 | } | 94 | } |
| 88 | } | 95 | } |
| @@ -0,0 +1,140 @@ | |||
| 1 | +/* | ||
| 2 | + * Copyright (c) 2026 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 | + | ||
| 16 | +import type common from '@ohos.app.ability.common'; | ||
| 17 | +import type Want from '@ohos.app.ability.Want'; | ||
| 18 | +import rpc from '@ohos.rpc'; | ||
| 19 | +import { PACKAGE_NAME } from '@ohos/common/src/main/ets/const/update_const'; | ||
| 20 | +import type { BusinessError } from '@ohos/common/src/main/ets/const/update_const'; | ||
| 21 | +import { LogUtils } from '@ohos/common/src/main/ets/util/LogUtils'; | ||
| 22 | + | ||
| 23 | +const TAG = 'SceneBoardDialogMgr'; | ||
| 24 | +const SYSTEM_DIALOG_BUNDLE = 'com.ohos.sceneboard'; | ||
| 25 | +const SYSTEM_DIALOG_ABILITY = 'com.ohos.sceneboard.systemdialog'; | ||
| 26 | +const UPDATE_DIALOG_ABILITY = 'UpdateSystemDialogAbility'; | ||
| 27 | +const START_DIALOG_CMD = 1; | ||
| 28 | +const REQUEST_PARAM_NUM = 3; | ||
| 29 | +const SUCCESS_CODE = 0; | ||
| 30 | + | ||
| 31 | +type DialogConnectContext = common.UIAbilityContext | common.ServiceExtensionContext; | ||
| 32 | + | ||
| 33 | +export enum SceneBoardDialogType { | ||
| 34 | + DOWNLOAD_REMINDER = 'downloadReminder' | ||
| 35 | +} | ||
| 36 | + | ||
| 37 | +export interface SceneBoardDialogOptions { | ||
| 38 | + dialogType: SceneBoardDialogType; | ||
| 39 | +} | ||
| 40 | + | ||
| 41 | +/** | ||
| 42 | + * 使用SceneBoard的系统弹窗服务来显示 updateapp UI扩展弹窗 | ||
| 43 | + */ | ||
| 44 | +export class SceneBoardDialogManager { | ||
| 45 | + private static requestingType?: SceneBoardDialogType; | ||
| 46 | + | ||
| 47 | + static async showDialog(context: common.Context, options: SceneBoardDialogOptions): Promise<boolean> { | ||
| 48 | + const dialogContext = context as DialogConnectContext; | ||
| 49 | + if (!dialogContext?.connectServiceExtensionAbility) { | ||
| 50 | + LogUtils.warn(TAG, 'Context does not support connectServiceExtensionAbility.'); | ||
| 51 | + return false; | ||
| 52 | + } | ||
| 53 | + if (SceneBoardDialogManager.requestingType !== undefined) { | ||
| 54 | + LogUtils.info(TAG, `Dialog request is in progress: ${SceneBoardDialogManager.requestingType}`); | ||
| 55 | + return false; | ||
| 56 | + } | ||
| 57 | + | ||
| 58 | + SceneBoardDialogManager.requestingType = options.dialogType; | ||
| 59 | + const want: Want = { | ||
| 60 | + bundleName: SYSTEM_DIALOG_BUNDLE, | ||
| 61 | + abilityName: SYSTEM_DIALOG_ABILITY | ||
| 62 | + }; | ||
| 63 | + | ||
| 64 | + return new Promise<boolean>((resolve) => { | ||
| 65 | + let connectionId: number = -1; | ||
| 66 | + let settled: boolean = false; | ||
| 67 | + const finish = (success: boolean): void => { | ||
| 68 | + if (settled) { | ||
| 69 | + return; | ||
| 70 | + } | ||
| 71 | + settled = true; | ||
| 72 | + SceneBoardDialogManager.requestingType = undefined; | ||
| 73 | + if (connectionId >= 0) { | ||
| 74 | + try { | ||
| 75 | + dialogContext.disconnectServiceExtensionAbility(connectionId).catch((error: BusinessError) => { | ||
| 76 | + LogUtils.error(TAG, `Disconnect system dialog failed: ${JSON.stringify(error)}`); | ||
| 77 | + }); | ||
| 78 | + } catch (error) { | ||
| 79 | + LogUtils.error(TAG, `Disconnect system dialog exception: ${JSON.stringify(error)}`); | ||
| 80 | + } | ||
| 81 | + } | ||
| 82 | + resolve(success); | ||
| 83 | + }; | ||
| 84 | + | ||
| 85 | + const connectOption: common.ConnectOptions = { | ||
| 86 | + onConnect(elementName, remote) { | ||
| 87 | + const data = rpc.MessageSequence.create(); | ||
| 88 | + const reply = rpc.MessageSequence.create(); | ||
| 89 | + const option = new rpc.MessageOption(); | ||
| 90 | + const parameters: Record<string, Object> = { | ||
| 91 | + 'ability.want.params.uiExtensionType': 'sysDialog/common', | ||
| 92 | + 'sysDialogZOrder': 1, | ||
| 93 | + 'dialogType': options.dialogType | ||
| 94 | + }; | ||
| 95 | + try { | ||
| 96 | + data.writeInt(REQUEST_PARAM_NUM); | ||
| 97 | + data.writeString('bundleName'); | ||
| 98 | + data.writeString(PACKAGE_NAME); | ||
| 99 | + data.writeString('abilityName'); | ||
| 100 | + data.writeString(UPDATE_DIALOG_ABILITY); | ||
| 101 | + data.writeString('parameters'); | ||
| 102 | + data.writeString(JSON.stringify(parameters)); | ||
| 103 | + remote.sendMessageRequest(START_DIALOG_CMD, data, reply, option).then(() => { | ||
| 104 | + const result = reply.readInt(); | ||
| 105 | + LogUtils.info(TAG, `Show ${options.dialogType} reply=${result}`); | ||
| 106 | + finish(result === SUCCESS_CODE); | ||
| 107 | + }).catch((error: BusinessError) => { | ||
| 108 | + LogUtils.error(TAG, `Show ${options.dialogType} failed: ${JSON.stringify(error)}`); | ||
| 109 | + finish(false); | ||
| 110 | + }).finally(() => { | ||
| 111 | + data.reclaim(); | ||
| 112 | + reply.reclaim(); | ||
| 113 | + }); | ||
| 114 | + } catch (error) { | ||
| 115 | + data.reclaim(); | ||
| 116 | + reply.reclaim(); | ||
| 117 | + LogUtils.error(TAG, `Build ${options.dialogType} request failed: ${JSON.stringify(error)}`); | ||
| 118 | + finish(false); | ||
| 119 | + } | ||
| 120 | + LogUtils.info(TAG, `Connected to system dialog: ${elementName?.bundleName}`); | ||
| 121 | + }, | ||
| 122 | + onDisconnect(elementName) { | ||
| 123 | + LogUtils.info(TAG, `Disconnected from system dialog: ${elementName?.bundleName}`); | ||
| 124 | + }, | ||
| 125 | + onFailed(code) { | ||
| 126 | + LogUtils.error(TAG, `Connect system dialog failed: ${code}`); | ||
| 127 | + finish(false); | ||
| 128 | + } | ||
| 129 | + }; | ||
| 130 | + | ||
| 131 | + try { | ||
| 132 | + connectionId = dialogContext.connectServiceExtensionAbility(want, connectOption) ?? -1; | ||
| 133 | + LogUtils.info(TAG, `Connect system dialog start, connectionId=${connectionId}`); | ||
| 134 | + } catch (error) { | ||
| 135 | + LogUtils.error(TAG, `Connect system dialog exception: ${JSON.stringify(error)}`); | ||
| 136 | + finish(false); | ||
| 137 | + } | ||
| 138 | + }); | ||
| 139 | + } | ||
| 140 | +} | ||
| @@ -0,0 +1,127 @@ | |||
| 1 | +/* | ||
| 2 | + * Copyright (c) 2026 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 | + | ||
| 16 | +import Settings from '@ohos.settings'; | ||
| 17 | +import type common from '@ohos.app.ability.common'; | ||
| 18 | +import { LogUtils } from '@ohos/common/src/main/ets/util/LogUtils'; | ||
| 19 | +import type { OtaStatus } from '@ohos/common/src/main/ets/const/update_const'; | ||
| 20 | +import { UpdateState } from '@ohos/common/src/main/ets/const/update_const'; | ||
| 21 | + | ||
| 22 | +const TAG = 'SettingsSyncUtils'; | ||
| 23 | +const HW_NEW_SYSTEM_UPDATE = 'hw_new_system_update'; | ||
| 24 | +const HW_NEW_SYSTEM_UPDATE_STATUS = 'hw_new_system_update_status'; | ||
| 25 | + | ||
| 26 | +enum NewVersionViewStatus { | ||
| 27 | + VIEW_INIT = 0, | ||
| 28 | + VIEW_CHECK_SUCCESS, | ||
| 29 | + VIEW_DOWNLOADING, | ||
| 30 | + VIEW_DOWNLOAD_PAUSE, | ||
| 31 | + VIEW_DOWNLOAD_VERIFYING, | ||
| 32 | + VIEW_DOWNLOAD_SUCCESS, | ||
| 33 | + VIEW_UPGRADE_VERIFYING, | ||
| 34 | + VIEW_UPGRADE_REBOOTING, | ||
| 35 | + VIEW_UPGRADE_HOT_INSTALLING, | ||
| 36 | + VIEW_PRE_INSTALLING, | ||
| 37 | + VIEW_INSTALLING, | ||
| 38 | + VIEW_INSTALL_SUCCESS, | ||
| 39 | + VIEW_INSTALL_WAIT, | ||
| 40 | +} | ||
| 41 | + | ||
| 42 | +interface SettingsUpdateView { | ||
| 43 | + badge: number; | ||
| 44 | + status: NewVersionViewStatus; | ||
| 45 | +} | ||
| 46 | + | ||
| 47 | +export class SettingsSyncUtils { | ||
| 48 | + static syncCheckResult(hasNewVersion: boolean, context?: common.Context): void { | ||
| 49 | + if (hasNewVersion) { | ||
| 50 | + SettingsSyncUtils.writeView({ badge: 1, status: NewVersionViewStatus.VIEW_CHECK_SUCCESS }, context); | ||
| 51 | + return; | ||
| 52 | + } | ||
| 53 | + SettingsSyncUtils.syncReset(context); | ||
| 54 | + } | ||
| 55 | + | ||
| 56 | + static syncCancel(context?: common.Context): void { | ||
| 57 | + SettingsSyncUtils.writeView({ badge: 1, status: NewVersionViewStatus.VIEW_CHECK_SUCCESS }, context); | ||
| 58 | + } | ||
| 59 | + | ||
| 60 | + static syncReset(context?: common.Context): void { | ||
| 61 | + SettingsSyncUtils.writeView({ badge: 0, status: NewVersionViewStatus.VIEW_INIT }, context); | ||
| 62 | + } | ||
| 63 | + | ||
| 64 | + static syncFromOtaStatus(otaStatus: OtaStatus, context?: common.Context): void { | ||
| 65 | + const view = SettingsSyncUtils.mapStatus(otaStatus?.status); | ||
| 66 | + if (!view) { | ||
| 67 | + return; | ||
| 68 | + } | ||
| 69 | + SettingsSyncUtils.writeView(view, context); | ||
| 70 | + } | ||
| 71 | + | ||
| 72 | + private static mapStatus(status?: number): SettingsUpdateView | undefined { | ||
| 73 | + switch (status) { | ||
| 74 | + case UpdateState.INIT: | ||
| 75 | + return { badge: 0, status: NewVersionViewStatus.VIEW_INIT }; | ||
| 76 | + case UpdateState.CHECK_SUCCESS: | ||
| 77 | + return { badge: 1, status: NewVersionViewStatus.VIEW_CHECK_SUCCESS }; | ||
| 78 | + case UpdateState.DOWNLOADING: | ||
| 79 | + return { badge: 1, status: NewVersionViewStatus.VIEW_DOWNLOADING }; | ||
| 80 | + case UpdateState.DOWNLOAD_PAUSE: | ||
| 81 | + return { badge: 1, status: NewVersionViewStatus.VIEW_DOWNLOAD_PAUSE }; | ||
| 82 | + case UpdateState.DOWNLOAD_SUCCESS: | ||
| 83 | + return { badge: 1, status: NewVersionViewStatus.VIEW_DOWNLOAD_SUCCESS }; | ||
| 84 | + case UpdateState.INSTALLING: | ||
| 85 | + case UpdateState.UPGRADING: | ||
| 86 | + return { badge: 1, status: NewVersionViewStatus.VIEW_INSTALLING }; | ||
| 87 | + case UpdateState.INSTALL_SUCCESS: | ||
| 88 | + return { badge: 1, status: NewVersionViewStatus.VIEW_INSTALL_SUCCESS }; | ||
| 89 | + case UpdateState.DOWNLOAD_CANCEL: | ||
| 90 | + case UpdateState.DOWNLOAD_FAILED: | ||
| 91 | + case UpdateState.INSTALL_FAILED: | ||
| 92 | + case UpdateState.UPGRADE_FAILED: | ||
| 93 | + return { badge: 1, status: NewVersionViewStatus.VIEW_CHECK_SUCCESS }; | ||
| 94 | + case UpdateState.UPGRADE_SUCCESS: | ||
| 95 | + return { badge: 0, status: NewVersionViewStatus.VIEW_INIT }; | ||
| 96 | + default: | ||
| 97 | + return undefined; | ||
| 98 | + } | ||
| 99 | + } | ||
| 100 | + | ||
| 101 | + private static writeView(view: SettingsUpdateView, context?: common.Context): void { | ||
| 102 | + const resolvedContext = SettingsSyncUtils.getContext(context); | ||
| 103 | + if (!resolvedContext) { | ||
| 104 | + LogUtils.warn(TAG, 'writeView skip because context is empty'); | ||
| 105 | + return; | ||
| 106 | + } | ||
| 107 | + | ||
| 108 | + SettingsSyncUtils.setSettingsValue(resolvedContext, HW_NEW_SYSTEM_UPDATE, String(view.badge)); | ||
| 109 | + SettingsSyncUtils.setSettingsValue(resolvedContext, HW_NEW_SYSTEM_UPDATE_STATUS, String(view.status)); | ||
| 110 | + } | ||
| 111 | + | ||
| 112 | + private static getContext(context?: common.Context): common.Context | undefined { | ||
| 113 | + return globalThis.abilityContext || context || globalThis.stageContext || globalThis.extensionContext; | ||
| 114 | + } | ||
| 115 | + | ||
| 116 | + private static setSettingsValue(context: common.Context, key: string, value: string): void { | ||
| 117 | + try { | ||
| 118 | + const result = Settings.setValueSync(context, key, value); | ||
| 119 | + LogUtils.info(TAG, `setSettingsValue key=${key}, value=${value}, result=${JSON.stringify(result)}`); | ||
| 120 | + if (!result) { | ||
| 121 | + LogUtils.warn(TAG, `setSettingsValue returned false, key=${key}`); | ||
| 122 | + } | ||
| 123 | + } catch (err) { | ||
| 124 | + LogUtils.error(TAG, `setSettingsValue failed, key=${key}, err=${JSON.stringify(err)}`); | ||
| 125 | + } | ||
| 126 | + } | ||
| 127 | +} | ||
| @@ -4,6 +4,10 @@ | |||
| 4 | "name":"btn_check_new_version", | 4 | "name":"btn_check_new_version", |
| 5 | "value":"Check for updates" | 5 | "value":"Check for updates" |
| 6 | }, | 6 | }, |
| 7 | + { | ||
| 8 | + "name":"btn_checking_new_version", | ||
| 9 | + "value":"Checking for updates" | ||
| 10 | + }, | ||
| 7 | { | 11 | { |
| 8 | "name":"btn_download", | 12 | "name":"btn_download", |
| 9 | "value":"Download & install" | 13 | "value":"Download & install" |
| @@ -68,6 +72,22 @@ | |||
| 68 | "name":"package_verify_fail", | 72 | "name":"package_verify_fail", |
| 69 | "value":"Update verification failed." | 73 | "value":"Update verification failed." |
| 70 | }, | 74 | }, |
| 75 | + { | ||
| 76 | + "name":"download_verify_fail_notification_title", | ||
| 77 | + "value":"New version download failed" | ||
| 78 | + }, | ||
| 79 | + { | ||
| 80 | + "name":"download_verify_fail_notification_content", | ||
| 81 | + "value":"Update package verification failed" | ||
| 82 | + }, | ||
| 83 | + { | ||
| 84 | + "name":"package_changed_title", | ||
| 85 | + "value":"Update package changed" | ||
| 86 | + }, | ||
| 87 | + { | ||
| 88 | + "name":"package_changed_content", | ||
| 89 | + "value":"The update package has changed. Please wait for a later push." | ||
| 90 | + }, | ||
| 71 | { | 91 | { |
| 72 | "name":"later", | 92 | "name":"later", |
| 73 | "value":"Later" | 93 | "value":"Later" |
| @@ -76,6 +96,10 @@ | |||
| 76 | "name":"install_now", | 96 | "name":"install_now", |
| 77 | "value":"Install now (%d)" | 97 | "value":"Install now (%d)" |
| 78 | }, | 98 | }, |
| 99 | + { | ||
| 100 | + "name":"btn_install_now", | ||
| 101 | + "value":"Install now" | ||
| 102 | + }, | ||
| 79 | { | 103 | { |
| 80 | "name":"software_update", | 104 | "name":"software_update", |
| 81 | "value":"Software update" | 105 | "value":"Software update" |
| @@ -98,7 +122,7 @@ | |||
| 98 | }, | 122 | }, |
| 99 | { | 123 | { |
| 100 | "name":"download_fail", | 124 | "name":"download_fail", |
| 101 | - "value":"Download failed. Please check for updates again." | 125 | + "value":"Tap \"OK\" to check for updates again." |
| 102 | }, | 126 | }, |
| 103 | { | 127 | { |
| 104 | "name":"update_fail", | 128 | "name":"update_fail", |
| @@ -182,6 +206,50 @@ | |||
| 182 | { | 206 | { |
| 183 | "name":"reboot_now", | 207 | "name":"reboot_now", |
| 184 | "value":"Reboot Now (%d)" | 208 | "value":"Reboot Now (%d)" |
| 209 | + }, | ||
| 210 | + { | ||
| 211 | + "name": "setting_auto_download_title", | ||
| 212 | + "value": "Automatically download updates" | ||
| 213 | + }, | ||
| 214 | + { | ||
| 215 | + "name": "setting_auto_download_description", | ||
| 216 | + "value": "Updates for this device will be downloaded over WLAN." | ||
| 217 | + }, | ||
| 218 | + { | ||
| 219 | + "name": "setting_auto_update_title", | ||
| 220 | + "value": "Update overnight" | ||
| 221 | + }, | ||
| 222 | + { | ||
| 223 | + "name": "setting_auto_update_description", | ||
| 224 | + "value": "When enabled, your device will automatically install updates and restart between %s if not in use. Some battery power will be used." | ||
| 225 | + }, | ||
| 226 | + { | ||
| 227 | + "name": "local_settings", | ||
| 228 | + "value": "Update options" | ||
| 229 | + }, | ||
| 230 | + { | ||
| 231 | + "name":"detail_info", | ||
| 232 | + "value":"Details" | ||
| 233 | + }, | ||
| 234 | + { | ||
| 235 | + "name":"cancel_download", | ||
| 236 | + "value":"Cancel download" | ||
| 237 | + }, | ||
| 238 | + { | ||
| 239 | + "name":"continue_download", | ||
| 240 | + "value":"Continue download" | ||
| 241 | + }, | ||
| 242 | + { | ||
| 243 | + "name":"cancel_download_confirm_title", | ||
| 244 | + "value":"Cancel download?" | ||
| 245 | + }, | ||
| 246 | + { | ||
| 247 | + "name":"cancel_download_confirm_message", | ||
| 248 | + "value":"Tap \"Cancel download\" to cancel this update package download." | ||
| 249 | + }, | ||
| 250 | + { | ||
| 251 | + "name":"desktop_download_dialog_content", | ||
| 252 | + "value":"A new software update is available. Download and install it now?" | ||
| 185 | } | 253 | } |
| 186 | ] | 254 | ] |
| 187 | -} | 255 | +} |
| @@ -4,6 +4,10 @@ | |||
| 4 | "name":"btn_check_new_version", | 4 | "name":"btn_check_new_version", |
| 5 | "value":"检查更新" | 5 | "value":"检查更新" |
| 6 | }, | 6 | }, |
| 7 | + { | ||
| 8 | + "name":"btn_checking_new_version", | ||
| 9 | + "value":"检查更新中" | ||
| 10 | + }, | ||
| 7 | { | 11 | { |
| 8 | "name":"btn_download", | 12 | "name":"btn_download", |
| 9 | "value":"下载并安装" | 13 | "value":"下载并安装" |
| @@ -38,7 +42,7 @@ | |||
| 38 | }, | 42 | }, |
| 39 | { | 43 | { |
| 40 | "name":"ok", | 44 | "name":"ok", |
| 41 | - "value":"确认" | 45 | + "value":"确定" |
| 42 | }, | 46 | }, |
| 43 | { | 47 | { |
| 44 | "name":"cancel", | 48 | "name":"cancel", |
| @@ -68,6 +72,22 @@ | |||
| 68 | "name":"package_verify_fail", | 72 | "name":"package_verify_fail", |
| 69 | "value":"升级包校验失败。" | 73 | "value":"升级包校验失败。" |
| 70 | }, | 74 | }, |
| 75 | + { | ||
| 76 | + "name":"download_verify_fail_notification_title", | ||
| 77 | + "value":"新版本下载失败" | ||
| 78 | + }, | ||
| 79 | + { | ||
| 80 | + "name":"download_verify_fail_notification_content", | ||
| 81 | + "value":"升级包校验失败" | ||
| 82 | + }, | ||
| 83 | + { | ||
| 84 | + "name":"package_changed_title", | ||
| 85 | + "value":"更新包有变更" | ||
| 86 | + }, | ||
| 87 | + { | ||
| 88 | + "name":"package_changed_content", | ||
| 89 | + "value":"更新包有变更,请等待后续推送。" | ||
| 90 | + }, | ||
| 71 | { | 91 | { |
| 72 | "name":"later", | 92 | "name":"later", |
| 73 | "value":"稍后" | 93 | "value":"稍后" |
| @@ -76,6 +96,10 @@ | |||
| 76 | "name":"install_now", | 96 | "name":"install_now", |
| 77 | "value":"现在安装 (%d)" | 97 | "value":"现在安装 (%d)" |
| 78 | }, | 98 | }, |
| 99 | + { | ||
| 100 | + "name":"btn_install_now", | ||
| 101 | + "value":"现在安装" | ||
| 102 | + }, | ||
| 79 | { | 103 | { |
| 80 | "name":"software_update", | 104 | "name":"software_update", |
| 81 | "value":"软件更新" | 105 | "value":"软件更新" |
| @@ -98,7 +122,7 @@ | |||
| 98 | }, | 122 | }, |
| 99 | { | 123 | { |
| 100 | "name":"download_fail", | 124 | "name":"download_fail", |
| 101 | - "value":"下载失败,请重新搜索更新包。" | 125 | + "value":"点击“确定”将重新搜索新版本。" |
| 102 | }, | 126 | }, |
| 103 | { | 127 | { |
| 104 | "name":"update_fail", | 128 | "name":"update_fail", |
| @@ -182,6 +206,50 @@ | |||
| 182 | { | 206 | { |
| 183 | "name":"reboot_now", | 207 | "name":"reboot_now", |
| 184 | "value":"现在重启 (%d)" | 208 | "value":"现在重启 (%d)" |
| 209 | + }, | ||
| 210 | + { | ||
| 211 | + "name": "setting_auto_download_title", | ||
| 212 | + "value": "WLAN 下自动下载" | ||
| 213 | + }, | ||
| 214 | + { | ||
| 215 | + "name": "setting_auto_download_description", | ||
| 216 | + "value": "本机仅 WLAN 环境自动下载升级包或者更新应用。" | ||
| 217 | + }, | ||
| 218 | + { | ||
| 219 | + "name": "setting_auto_update_title", | ||
| 220 | + "value": "夜间安装" | ||
| 221 | + }, | ||
| 222 | + { | ||
| 223 | + "name": "setting_auto_update_description", | ||
| 224 | + "value": "开启后,本机将在 %s 且处于空闲状态时自动安装并重启。安装过程中将少量耗电。" | ||
| 225 | + }, | ||
| 226 | + { | ||
| 227 | + "name": "local_settings", | ||
| 228 | + "value": "更新选项" | ||
| 229 | + }, | ||
| 230 | + { | ||
| 231 | + "name":"detail_info", | ||
| 232 | + "value":"详细信息" | ||
| 233 | + }, | ||
| 234 | + { | ||
| 235 | + "name":"cancel_download", | ||
| 236 | + "value":"取消下载" | ||
| 237 | + }, | ||
| 238 | + { | ||
| 239 | + "name":"continue_download", | ||
| 240 | + "value":"继续下载" | ||
| 241 | + }, | ||
| 242 | + { | ||
| 243 | + "name":"cancel_download_confirm_title", | ||
| 244 | + "value":"确认取消下载?" | ||
| 245 | + }, | ||
| 246 | + { | ||
| 247 | + "name":"cancel_download_confirm_message", | ||
| 248 | + "value":"点击“取消下载”将取消本次更新包的下载任务" | ||
| 249 | + }, | ||
| 250 | + { | ||
| 251 | + "name":"desktop_download_dialog_content", | ||
| 252 | + "value":"发现新的软件版本,是否立即下载并安装?" | ||
| 185 | } | 253 | } |
| 186 | ] | 254 | ] |
| 187 | -} | 255 | +} |
| @@ -1,5 +1,5 @@ | |||
| 1 | { | 1 | { |
| 2 | - "modelVersion": "5.0.2", | 2 | + "modelVersion": "6.0.0", |
| 3 | "dependencies": {}, | 3 | "dependencies": {}, |
| 4 | "execution": { | 4 | "execution": { |
| 5 | // "analyze": "default", /* Define the build analyze mode. Value: [ "default" | "verbose" | false ]. Default: "default" */ | 5 | // "analyze": "default", /* Define the build analyze mode. Value: [ "default" | "verbose" | false ]. Default: "default" */ |
| @@ -1,5 +1,5 @@ | |||
| 1 | { | 1 | { |
| 2 | - "modelVersion": "5.0.2", | 2 | + "modelVersion": "6.0.0", |
| 3 | "license": "ISC", | 3 | "license": "ISC", |
| 4 | "devDependencies": {}, | 4 | "devDependencies": {}, |
| 5 | "author": "", | 5 | "author": "", |
| @@ -32,5 +32,6 @@ export default class MyAbilityStage extends AbilityStage { | |||
| 32 | AppStorage.SetOrCreate('isClickInstall', 0); | 32 | AppStorage.SetOrCreate('isClickInstall', 0); |
| 33 | AppStorage.SetOrCreate('configLanguage', ''); | 33 | AppStorage.SetOrCreate('configLanguage', ''); |
| 34 | AppStorage.SetOrCreate('installStatusRefresh', ''); | 34 | AppStorage.SetOrCreate('installStatusRefresh', ''); |
| 35 | + AppStorage.SetOrCreate('downloadNoNetworkDialogVisible', false); | ||
| 35 | } | 36 | } |
| 36 | } | 37 | } |
| @@ -16,6 +16,7 @@ | |||
| 16 | import Ability from '@ohos.app.ability.UIAbility'; | 16 | import Ability from '@ohos.app.ability.UIAbility'; |
| 17 | import type Want from '@ohos.app.ability.Want'; | 17 | import type Want from '@ohos.app.ability.Want'; |
| 18 | import type AbilityConstant from '@ohos.app.ability.AbilityConstant'; | 18 | import type AbilityConstant from '@ohos.app.ability.AbilityConstant'; |
| 19 | +import notification from '@ohos.notificationManager'; | ||
| 19 | import router from '@ohos.router'; | 20 | import router from '@ohos.router'; |
| 20 | import type update from '@ohos.update'; | 21 | import type update from '@ohos.update'; |
| 21 | import type window from '@ohos.window'; | 22 | import type window from '@ohos.window'; |
| @@ -54,8 +55,8 @@ export default class MainAbility extends Ability { | |||
| 54 | globalThis.AbilityStatus = null; | 55 | globalThis.AbilityStatus = null; |
| 55 | if (globalThis.abilityWant?.uri === 'pages/newVersion') { | 56 | if (globalThis.abilityWant?.uri === 'pages/newVersion') { |
| 56 | windowStage.loadContent('pages/newVersion', null); | 57 | windowStage.loadContent('pages/newVersion', null); |
| 57 | - } else if (globalThis.abilityWant?.uri === 'pages/setting') { | 58 | + } else if (globalThis.abilityWant?.uri === 'pages/settingsView') { |
| 58 | - windowStage.loadContent('pages/setting', null); | 59 | + windowStage.loadContent('pages/settingsView', null); |
| 59 | } else { | 60 | } else { |
| 60 | windowStage.loadContent('pages/index', null); | 61 | windowStage.loadContent('pages/index', null); |
| 61 | } | 62 | } |
| @@ -63,6 +64,7 @@ export default class MainAbility extends Ability { | |||
| 63 | 64 | ||
| 64 | onNewWant(want: Want): void { | 65 | onNewWant(want: Want): void { |
| 65 | this.log('BaseAbility onNewWant:' + JSON.stringify(want)); | 66 | this.log('BaseAbility onNewWant:' + JSON.stringify(want)); |
| 67 | + globalThis.abilityWant = want; | ||
| 66 | globalThis.newPage = want.uri; | 68 | globalThis.newPage = want.uri; |
| 67 | if (globalThis.AbilityStatus === 'ON_FOREGROUND') { | 69 | if (globalThis.AbilityStatus === 'ON_FOREGROUND') { |
| 68 | this.routePage(); | 70 | this.routePage(); |
| @@ -85,6 +87,7 @@ export default class MainAbility extends Ability { | |||
| 85 | 87 | ||
| 86 | onForeground(): void { | 88 | onForeground(): void { |
| 87 | this.log('BaseAbility onForeground'); | 89 | this.log('BaseAbility onForeground'); |
| 90 | + this.requestEnableNotificationIfNeeded(); | ||
| 88 | new NotificationHelper().cancelAll(); | 91 | new NotificationHelper().cancelAll(); |
| 89 | globalThis.AbilityStatus = 'ON_FOREGROUND'; | 92 | globalThis.AbilityStatus = 'ON_FOREGROUND'; |
| 90 | setTimeout(() => { | 93 | setTimeout(() => { |
| @@ -122,6 +125,18 @@ export default class MainAbility extends Ability { | |||
| 122 | } | 125 | } |
| 123 | } | 126 | } |
| 124 | 127 | ||
| 128 | + private async requestEnableNotificationIfNeeded(): Promise<void> { | ||
| 129 | + try { | ||
| 130 | + if (await notification.isNotificationEnabled()) { | ||
| 131 | + return; | ||
| 132 | + } | ||
| 133 | + await notification.requestEnableNotification(this.context); | ||
| 134 | + this.log('requestEnableNotification success'); | ||
| 135 | + } catch (err) { | ||
| 136 | + this.log('requestEnableNotificationIfNeeded failed: ' + JSON.stringify(err)); | ||
| 137 | + } | ||
| 138 | + } | ||
| 139 | + | ||
| 125 | protected log(message: string): void { | 140 | protected log(message: string): void { |
| 126 | LogUtils.log('BaseAbility', message); | 141 | LogUtils.log('BaseAbility', message); |
| 127 | } | 142 | } |
| @@ -0,0 +1,46 @@ | |||
| 1 | +/* | ||
| 2 | + * Copyright (c) 2026 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 | + | ||
| 16 | +import UIExtensionAbility from '@ohos.app.ability.UIExtensionAbility'; | ||
| 17 | +import type Want from '@ohos.app.ability.Want'; | ||
| 18 | +import type UIExtensionContentSession from '@ohos.app.ability.UIExtensionContentSession'; | ||
| 19 | +import { LogUtils } from '@ohos/common/src/main/ets/util/LogUtils'; | ||
| 20 | + | ||
| 21 | +const TAG = 'UpdateSystemDialogAbility'; | ||
| 22 | + | ||
| 23 | +export default class UpdateSystemDialogAbility extends UIExtensionAbility { | ||
| 24 | + onSessionCreate(want: Want, session: UIExtensionContentSession): void { | ||
| 25 | + LogUtils.info(TAG, `onSessionCreate: ${JSON.stringify(want?.parameters)}`); | ||
| 26 | + const storage = new LocalStorage(); | ||
| 27 | + storage.setOrCreate('session', session); | ||
| 28 | + storage.setOrCreate('dialogType', (want?.parameters?.dialogType as string) ?? ''); | ||
| 29 | + try { | ||
| 30 | + session.loadContent('UpdateSystemDialog/pages/UpdateSystemDialogPage', storage); | ||
| 31 | + } catch (error) { | ||
| 32 | + LogUtils.error(TAG, `Load system dialog content failed: ${JSON.stringify(error)}`); | ||
| 33 | + try { | ||
| 34 | + session.terminateSelf(); | ||
| 35 | + } catch (terminateError) { | ||
| 36 | + LogUtils.error(TAG, `Terminate invalid session failed: ${JSON.stringify(terminateError)}`); | ||
| 37 | + } | ||
| 38 | + return; | ||
| 39 | + } | ||
| 40 | + try { | ||
| 41 | + session.setWindowBackgroundColor('#00000000'); | ||
| 42 | + } catch (error) { | ||
| 43 | + LogUtils.warn(TAG, `Set transparent background failed: ${JSON.stringify(error)}`); | ||
| 44 | + } | ||
| 45 | + } | ||
| 46 | +} | ||
| @@ -0,0 +1,104 @@ | |||
| 1 | +/* | ||
| 2 | + * Copyright (c) 2026 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 | + | ||
| 16 | +import type common from '@ohos.app.ability.common'; | ||
| 17 | +import type UIExtensionContentSession from '@ohos.app.ability.UIExtensionContentSession'; | ||
| 18 | +import { AlertDialog } from '@kit.ArkUI'; | ||
| 19 | +import { MAIN_ABILITY_NAME, PACKAGE_NAME } from '@ohos/common/src/main/ets/const/update_const'; | ||
| 20 | +import { DeviceUtils } from '@ohos/common/src/main/ets/util/DeviceUtils'; | ||
| 21 | +import { LogUtils } from '@ohos/common/src/main/ets/util/LogUtils'; | ||
| 22 | +import { UpdateUtils } from '@ohos/common/src/main/ets/util/UpdateUtils'; | ||
| 23 | +import { SceneBoardDialogType } from 'ota/src/main/ets/notify/SceneBoardDialogManager'; | ||
| 24 | + | ||
| 25 | +const TAG = 'UpdateSystemDialogPage'; | ||
| 26 | +const storage = LocalStorage.getShared(); | ||
| 27 | + | ||
| 28 | +@Entry(storage) | ||
| 29 | +@Component | ||
| 30 | +struct UpdateSystemDialogPage { | ||
| 31 | + private session?: UIExtensionContentSession = storage?.get('session') as UIExtensionContentSession; | ||
| 32 | + private context = getContext(this) as common.UIExtensionContext; | ||
| 33 | + private dialogType: string = (storage?.get('dialogType') as string) ?? ''; | ||
| 34 | + private actionHandled: boolean = false; | ||
| 35 | + | ||
| 36 | + private downloadDialogController = new CustomDialogController({ | ||
| 37 | + builder: AlertDialog({ | ||
| 38 | + primaryTitle: $r('app.string.software_update'), | ||
| 39 | + content: $r('app.string.desktop_download_dialog_content'), | ||
| 40 | + primaryButton: { | ||
| 41 | + value: $r('app.string.later'), | ||
| 42 | + action: () => { | ||
| 43 | + this.closeSession(); | ||
| 44 | + } | ||
| 45 | + }, | ||
| 46 | + secondaryButton: { | ||
| 47 | + value: $r('app.string.btn_download'), | ||
| 48 | + action: () => { | ||
| 49 | + this.startDownload(); | ||
| 50 | + } | ||
| 51 | + } | ||
| 52 | + }), | ||
| 53 | + alignment: DeviceUtils.getDialogLocation(), | ||
| 54 | + offset: { | ||
| 55 | + dx: '0vp', | ||
| 56 | + dy: DeviceUtils.getDialogOffsetY() | ||
| 57 | + }, | ||
| 58 | + autoCancel: false, | ||
| 59 | + cancel: () => { | ||
| 60 | + this.closeSession(); | ||
| 61 | + } | ||
| 62 | + }); | ||
| 63 | + | ||
| 64 | + aboutToAppear(): void { | ||
| 65 | + LogUtils.info(TAG, `aboutToAppear, dialogType=${this.dialogType}`); | ||
| 66 | + if (this.dialogType === SceneBoardDialogType.DOWNLOAD_REMINDER) { | ||
| 67 | + this.downloadDialogController.open(); | ||
| 68 | + return; | ||
| 69 | + } | ||
| 70 | + LogUtils.error(TAG, `Unsupported dialog type: ${this.dialogType}`); | ||
| 71 | + this.closeSession(); | ||
| 72 | + } | ||
| 73 | + | ||
| 74 | + private startDownload(): void { | ||
| 75 | + if (this.actionHandled) { | ||
| 76 | + return; | ||
| 77 | + } | ||
| 78 | + this.actionHandled = true; | ||
| 79 | + UpdateUtils.startAbility(this.context, { | ||
| 80 | + bundleName: PACKAGE_NAME, | ||
| 81 | + abilityName: MAIN_ABILITY_NAME, | ||
| 82 | + uri: 'pages/newVersion', | ||
| 83 | + parameters: { | ||
| 84 | + autoDownload: true | ||
| 85 | + } | ||
| 86 | + }); | ||
| 87 | + this.closeSession(); | ||
| 88 | + } | ||
| 89 | + | ||
| 90 | + private closeSession(): void { | ||
| 91 | + try { | ||
| 92 | + this.session?.terminateSelf(); | ||
| 93 | + } catch (error) { | ||
| 94 | + LogUtils.error(TAG, `Terminate system dialog session failed: ${JSON.stringify(error)}`); | ||
| 95 | + } | ||
| 96 | + } | ||
| 97 | + | ||
| 98 | + build() { | ||
| 99 | + Column() | ||
| 100 | + .width('100%') | ||
| 101 | + .height('100%') | ||
| 102 | + .backgroundColor('#00000000') | ||
| 103 | + } | ||
| 104 | +} | ||
| @@ -79,7 +79,7 @@ struct CurrentVersion { | |||
| 79 | build() { | 79 | build() { |
| 80 | Flex({ direction: FlexDirection.Column, alignItems: ItemAlign.Center, justifyContent: FlexAlign.Start }) { | 80 | Flex({ direction: FlexDirection.Column, alignItems: ItemAlign.Center, justifyContent: FlexAlign.Start }) { |
| 81 | Column() { | 81 | Column() { |
| 82 | - TitleBar({ title: $r('app.string.title_current_version'), onBack: this.onBackPress.bind(this) }) | 82 | + TitleBar({ title: $r('app.string.title_current_version'), onBack: this.onBackPress.bind(this), showMenu: true}) |
| 83 | }.flexShrink(0) | 83 | }.flexShrink(0) |
| 84 | 84 | ||
| 85 | Scroll() { | 85 | Scroll() { |
| @@ -14,6 +14,7 @@ | |||
| 14 | */ | 14 | */ |
| 15 | 15 | ||
| 16 | import update from '@ohos.update'; | 16 | import update from '@ohos.update'; |
| 17 | +import window from '@ohos.window'; | ||
| 17 | import { | 18 | import { |
| 18 | ErrorCode, | 19 | ErrorCode, |
| 19 | OtaStatus, | 20 | OtaStatus, |
| @@ -24,7 +25,6 @@ import { | |||
| 24 | } from '@ohos/common/src/main/ets/const/update_const'; | 25 | } from '@ohos/common/src/main/ets/const/update_const'; |
| 25 | import { TitleBar } from '@ohos/common/src/main/ets/component/TitleBar'; | 26 | import { TitleBar } from '@ohos/common/src/main/ets/component/TitleBar'; |
| 26 | import { HomeCardView } from '@ohos/common/src/main/ets/component/HomeCardView'; | 27 | import { HomeCardView } from '@ohos/common/src/main/ets/component/HomeCardView'; |
| 27 | -import { CheckingDots } from '@ohos/common/src/main/ets/component/CheckingDots'; | ||
| 28 | import { LogUtils } from '@ohos/common/src/main/ets/util/LogUtils'; | 28 | import { LogUtils } from '@ohos/common/src/main/ets/util/LogUtils'; |
| 29 | import { FormatUtils } from '@ohos/common/src/main/ets/util/FormatUtils'; | 29 | import { FormatUtils } from '@ohos/common/src/main/ets/util/FormatUtils'; |
| 30 | import RouterUtils from 'ota/src/main/ets/util/RouterUtils'; | 30 | import RouterUtils from 'ota/src/main/ets/util/RouterUtils'; |
| @@ -44,6 +44,9 @@ enum NewVersionStatus { | |||
| 44 | HAS_NEW_VERSION = 'HAS_NEW_VERSION', | 44 | HAS_NEW_VERSION = 'HAS_NEW_VERSION', |
| 45 | } | 45 | } |
| 46 | 46 | ||
| 47 | +const CHECK_ANIMATION_DURATION: number = 450; | ||
| 48 | +const CHECK_ANIMATION_SCALE: number = 1.2; | ||
| 49 | + | ||
| 47 | /** | 50 | /** |
| 48 | * 主页index | 51 | * 主页index |
| 49 | * | 52 | * |
| @@ -53,7 +56,6 @@ enum NewVersionStatus { | |||
| 53 | @Component | 56 | @Component |
| 54 | struct Index { | 57 | struct Index { |
| 55 | @State newVersionActionText: Resource = $r('app.string.check_version_status_no_new'); | 58 | @State newVersionActionText: Resource = $r('app.string.check_version_status_no_new'); |
| 56 | - @State dotTextPlay: boolean = false; | ||
| 57 | @State newVersionStatus: string = NewVersionStatus.NO_NEW_VERSION; | 59 | @State newVersionStatus: string = NewVersionStatus.NO_NEW_VERSION; |
| 58 | @StorageProp('updateStatus') | 60 | @StorageProp('updateStatus') |
| 59 | private updateStatus: number = AppStorage.Get('updateStatus') as number; | 61 | private updateStatus: number = AppStorage.Get('updateStatus') as number; |
| @@ -65,6 +67,7 @@ struct Index { | |||
| 65 | private checkIntervalId: number | null = null; | 67 | private checkIntervalId: number | null = null; |
| 66 | private checkLoopTimes: number = 0; | 68 | private checkLoopTimes: number = 0; |
| 67 | @State private buttonText: string = ''; | 69 | @State private buttonText: string = ''; |
| 70 | + @State private statusBarHeight: number = 0; | ||
| 68 | 71 | ||
| 69 | /** | 72 | /** |
| 70 | * 是否已经有搜包结果 | 73 | * 是否已经有搜包结果 |
| @@ -92,7 +95,7 @@ struct Index { | |||
| 92 | this.log('afterNewVersionFound'); | 95 | this.log('afterNewVersionFound'); |
| 93 | setTimeout(() => { | 96 | setTimeout(() => { |
| 94 | RouterUtils.openNewVersionPage(); | 97 | RouterUtils.openNewVersionPage(); |
| 95 | - }, 50); | 98 | + }, CHECK_ANIMATION_DURATION); |
| 96 | } else if (this.checkEndStatus == NewVersionStatus.NO_NEW_VERSION) { | 99 | } else if (this.checkEndStatus == NewVersionStatus.NO_NEW_VERSION) { |
| 97 | this.log('afterNewVersionFound no new version'); | 100 | this.log('afterNewVersionFound no new version'); |
| 98 | this.newVersionStatus = NewVersionStatus.NO_NEW_VERSION; | 101 | this.newVersionStatus = NewVersionStatus.NO_NEW_VERSION; |
| @@ -107,7 +110,6 @@ struct Index { | |||
| 107 | this.checkEndStatus = newVersionStatus; // 延时更新 | 110 | this.checkEndStatus = newVersionStatus; // 延时更新 |
| 108 | if (newVersionStatus == NewVersionStatus.CHECKING) { | 111 | if (newVersionStatus == NewVersionStatus.CHECKING) { |
| 109 | this.newVersionStatus = newVersionStatus; | 112 | this.newVersionStatus = newVersionStatus; |
| 110 | - this.newVersionActionText = $r('app.string.check_version_status_checking'); | ||
| 111 | } | 113 | } |
| 112 | } | 114 | } |
| 113 | 115 | ||
| @@ -186,7 +188,6 @@ struct Index { | |||
| 186 | private uiCheckLoop(running: boolean): void { | 188 | private uiCheckLoop(running: boolean): void { |
| 187 | this.log(`uiCheckLoop, running ${running}`) | 189 | this.log(`uiCheckLoop, running ${running}`) |
| 188 | this.displayVideo = running; | 190 | this.displayVideo = running; |
| 189 | - this.dotTextPlay = running; | ||
| 190 | if (this.checkIntervalId != null) { | 191 | if (this.checkIntervalId != null) { |
| 191 | clearInterval(this.checkIntervalId); | 192 | clearInterval(this.checkIntervalId); |
| 192 | this.checkIntervalId = null; | 193 | this.checkIntervalId = null; |
| @@ -260,53 +261,60 @@ struct Index { | |||
| 260 | 261 | ||
| 261 | build() { | 262 | build() { |
| 262 | Flex({ direction: FlexDirection.Column, alignItems: ItemAlign.Center, justifyContent: FlexAlign.Start }) { | 263 | Flex({ direction: FlexDirection.Column, alignItems: ItemAlign.Center, justifyContent: FlexAlign.Start }) { |
| 263 | - TitleBar({ | 264 | + Column() { |
| 264 | - title: $r('app.string.software_update'), | 265 | + TitleBar({ |
| 265 | - onBack: this.onBackPress.bind(this) | 266 | + title: $r('app.string.software_update'), |
| 266 | - }) | 267 | + onBack: this.onBackPress.bind(this), |
| 268 | + showMenu: true | ||
| 269 | + }) | ||
| 270 | + } | ||
| 271 | + .width('100%') | ||
| 272 | + .padding({ top: this.statusBarHeight }) | ||
| 273 | + | ||
| 267 | Column() { | 274 | Column() { |
| 268 | Scroll() { | 275 | Scroll() { |
| 269 | - Flex({ direction: FlexDirection.Column, alignItems: ItemAlign.Center, justifyContent: FlexAlign.Start }) { | 276 | + Flex({ direction: FlexDirection.Column, alignItems: ItemAlign.Center, justifyContent: FlexAlign.Center }) { |
| 270 | - HomeCardView({ playVideo: this.displayVideo, videoController: this.videoController, onVideoFinished: | 277 | + Column() { |
| 271 | - (): boolean => { | 278 | + Image($r('app.media.center')) |
| 272 | - this.checkTimes++; | 279 | + .width($r('app.float.index_center_image_width')) |
| 273 | - return this.onCheckAction.bind(this)(); | 280 | + .objectFit(ImageFit.Contain) |
| 274 | - } }) | 281 | + .margin({bottom: '10vp'}) |
| 275 | - Text(this.displayVersion) | 282 | + Row() { |
| 276 | - .fontSize($r('app.float.home_text_size_version_name')) | 283 | + Text(this.newVersionActionText) |
| 277 | - .fontWeight(FontWeight.Medium) | 284 | + .fontSize($r('app.float.text_size_body')) |
| 278 | - .margin({ | 285 | + .fontColor(this.newVersionStatus !== NewVersionStatus.CHECKING ? |
| 279 | - top: $r('app.float.index_version_name_margin_top'), | 286 | + $r('app.color.deep_blue') : Color.Black) |
| 280 | - bottom: $r('app.float.index_version_name_margin_bottom') | 287 | + .opacity(this.newVersionStatus !== NewVersionStatus.CHECKING ? 1 : 0.6) |
| 281 | - }) | 288 | + .fontWeight(FontWeight.Regular) |
| 282 | - Row() { | 289 | + .align(Alignment.Start) |
| 283 | - Text(this.newVersionActionText) | 290 | + .onClick(() => this.handleStateClicked()) |
| 284 | - .fontSize($r('app.float.text_size_body')) | 291 | + if (this.newVersionStatus == NewVersionStatus.HAS_NEW_VERSION) { |
| 285 | - .fontColor(this.newVersionStatus !== NewVersionStatus.CHECKING ? | 292 | + Circle({ width: '8vp', height: '8vp' }) |
| 286 | - $r('app.color.blue') : Color.Black) | 293 | + .margin({ left: $r('app.float.index_has_new_version_margin_left') }) |
| 287 | - .opacity(this.newVersionStatus !== NewVersionStatus.CHECKING ? 1 : 0.6) | 294 | + .fill($r('app.color.has_new_version_circle_fill')) |
| 288 | - .fontWeight(FontWeight.Regular) | 295 | + } |
| 289 | - .align(Alignment.Start) | ||
| 290 | - .onClick(() => this.handleStateClicked()) | ||
| 291 | - CheckingDots({ dotTextPlay: this.dotTextPlay }) | ||
| 292 | - if (this.newVersionStatus == NewVersionStatus.HAS_NEW_VERSION) { | ||
| 293 | - Circle({ width: '8vp', height: '8vp' }) | ||
| 294 | - .margin({ left: $r('app.float.index_has_new_version_margin_left') }) | ||
| 295 | - .fill($r('app.color.has_new_version_circle_fill')) | ||
| 296 | } | 296 | } |
| 297 | } | 297 | } |
| 298 | + .alignItems(HorizontalAlign.Center) | ||
| 299 | + .scale({ | ||
| 300 | + x: this.newVersionStatus === NewVersionStatus.CHECKING ? CHECK_ANIMATION_SCALE : 1, | ||
| 301 | + y: this.newVersionStatus === NewVersionStatus.CHECKING ? CHECK_ANIMATION_SCALE : 1 | ||
| 302 | + }) | ||
| 303 | + .opacity(this.newVersionStatus === NewVersionStatus.CHECKING ? 0 : 1) | ||
| 304 | + .animation({ duration: CHECK_ANIMATION_DURATION, curve: Curve.EaseInOut }) | ||
| 298 | }.padding({ | 305 | }.padding({ |
| 299 | left: $r('app.float.index_content_padding_horizontal'), | 306 | left: $r('app.float.index_content_padding_horizontal'), |
| 300 | right: $r('app.float.index_content_padding_horizontal'), | 307 | right: $r('app.float.index_content_padding_horizontal'), |
| 301 | bottom: $r('app.float.index_content_padding_bottom') | 308 | bottom: $r('app.float.index_content_padding_bottom') |
| 302 | }).width('100%') | 309 | }).width('100%') |
| 303 | - }.width('100%') | 310 | + }.width('100%').height('100%') |
| 304 | .scrollable(ScrollDirection.Vertical) | 311 | .scrollable(ScrollDirection.Vertical) |
| 305 | .scrollBar(BarState.On) | 312 | .scrollBar(BarState.On) |
| 306 | }.flexGrow(1) | 313 | }.flexGrow(1) |
| 307 | Column() { | 314 | Column() { |
| 308 | Button() { | 315 | Button() { |
| 309 | - Text(this.buttonText) | 316 | + Text(this.newVersionStatus === NewVersionStatus.CHECKING ? |
| 317 | + $r('app.string.btn_checking_new_version') : this.buttonText) | ||
| 310 | .fontSize($r('app.float.text_size_btn')).fontColor(Color.White).fontWeight(FontWeight.Medium) | 318 | .fontSize($r('app.float.text_size_btn')).fontColor(Color.White).fontWeight(FontWeight.Medium) |
| 311 | .margin({ left: $r('app.float.custom_button_text_margin_left'), | 319 | .margin({ left: $r('app.float.custom_button_text_margin_left'), |
| 312 | right: $r('app.float.custom_button_text_margin_right') }) | 320 | right: $r('app.float.custom_button_text_margin_right') }) |
| @@ -328,6 +336,9 @@ struct Index { | |||
| 328 | bottom: $r('app.float.index_content_padding_bottom') | 336 | bottom: $r('app.float.index_content_padding_bottom') |
| 329 | }) | 337 | }) |
| 330 | } | 338 | } |
| 339 | + .backgroundImage($r('app.media.background')) | ||
| 340 | + .backgroundImageSize(ImageSize.Cover) | ||
| 341 | + .expandSafeArea([SafeAreaType.SYSTEM], [SafeAreaEdge.TOP, SafeAreaEdge.BOTTOM]) | ||
| 331 | .backgroundColor($r('app.color.page_background')) | 342 | .backgroundColor($r('app.color.page_background')) |
| 332 | .width('100%') | 343 | .width('100%') |
| 333 | .height('100%') | 344 | .height('100%') |
| @@ -39,6 +39,7 @@ import { StateManager, UpdateAction } from 'ota/src/main/ets/manager/StateManage | |||
| 39 | import { UpgradeAdapter } from 'ota/src/main/ets/UpgradeAdapter'; | 39 | import { UpgradeAdapter } from 'ota/src/main/ets/UpgradeAdapter'; |
| 40 | import { VersionUtils } from 'ota/src/main/ets/util/VersionUtils'; | 40 | import { VersionUtils } from 'ota/src/main/ets/util/VersionUtils'; |
| 41 | import { NotificationHelper } from 'ota/src/main/ets/notify/NotificationHelper'; | 41 | import { NotificationHelper } from 'ota/src/main/ets/notify/NotificationHelper'; |
| 42 | +import { AlertDialog } from '@kit.ArkUI'; | ||
| 42 | import prompt from '@ohos.prompt'; | 43 | import prompt from '@ohos.prompt'; |
| 43 | import RouterUtils from 'ota/src/main/ets/util/RouterUtils'; | 44 | import RouterUtils from 'ota/src/main/ets/util/RouterUtils'; |
| 44 | 45 | ||
| @@ -56,6 +57,8 @@ struct NewVersion { | |||
| 56 | @State private displayNewVersionName: string = ''; | 57 | @State private displayNewVersionName: string = ''; |
| 57 | @State private isButtonEnable: boolean = true; | 58 | @State private isButtonEnable: boolean = true; |
| 58 | @State private isButtonVisible: boolean = true; | 59 | @State private isButtonVisible: boolean = true; |
| 60 | + @StorageProp('downloadProgress') | ||
| 61 | + private downloadProgress: number = AppStorage.Get('downloadProgress') as number; | ||
| 59 | @StorageProp('updateStatus') | 62 | @StorageProp('updateStatus') |
| 60 | @Watch('initDataByStatus') private updateStatus: number = AppStorage.Get('updateStatus') as number; | 63 | @Watch('initDataByStatus') private updateStatus: number = AppStorage.Get('updateStatus') as number; |
| 61 | @StorageProp('isClickInstall') @Watch('onInstallClick') | 64 | @StorageProp('isClickInstall') @Watch('onInstallClick') |
| @@ -68,6 +71,10 @@ struct NewVersion { | |||
| 68 | @State private buttonText: string = ''; | 71 | @State private buttonText: string = ''; |
| 69 | @StorageProp('installStatusRefresh') @Watch('refresh') | 72 | @StorageProp('installStatusRefresh') @Watch('refresh') |
| 70 | private installStatusRefresh: string = AppStorage.Get('installStatusRefresh') as string; | 73 | private installStatusRefresh: string = AppStorage.Get('installStatusRefresh') as string; |
| 74 | + @StorageProp('downloadNoNetworkDialogVisible') @Watch('syncDownloadNoNetworkDialog') | ||
| 75 | + private downloadNoNetworkDialogVisible: boolean = | ||
| 76 | + AppStorage.Get('downloadNoNetworkDialogVisible') as boolean; | ||
| 77 | + @State private isNoNetworkDialogOpen: boolean = false; | ||
| 71 | 78 | ||
| 72 | private countdownDialogController = new CustomDialogController({ | 79 | private countdownDialogController = new CustomDialogController({ |
| 73 | builder: CountDownInstallDialogBuilder({ | 80 | builder: CountDownInstallDialogBuilder({ |
| @@ -103,6 +110,26 @@ struct NewVersion { | |||
| 103 | }) | 110 | }) |
| 104 | }); | 111 | }); |
| 105 | 112 | ||
| 113 | + private noNetworkDialogController: CustomDialogController = new CustomDialogController({ | ||
| 114 | + builder: AlertDialog({ | ||
| 115 | + primaryTitle: $r('app.string.net_error_title'), | ||
| 116 | + content: $r('app.string.net_error_content'), | ||
| 117 | + primaryButton: { | ||
| 118 | + value: $r('app.string.button_know'), | ||
| 119 | + action: () => { | ||
| 120 | + this.isNoNetworkDialogOpen = false; | ||
| 121 | + AppStorage.Set('downloadNoNetworkDialogVisible', false); | ||
| 122 | + } | ||
| 123 | + } | ||
| 124 | + }), | ||
| 125 | + autoCancel: false, | ||
| 126 | + alignment: DeviceUtils.getDialogLocation(), | ||
| 127 | + offset: ({ | ||
| 128 | + dx: '0vp', | ||
| 129 | + dy: DeviceUtils.getDialogOffsetY() | ||
| 130 | + }) | ||
| 131 | + }); | ||
| 132 | + | ||
| 106 | private refreshDescription(): void { | 133 | private refreshDescription(): void { |
| 107 | this.log('refreshDescription'); | 134 | this.log('refreshDescription'); |
| 108 | this.description = JSON.stringify(this.changelogArray); | 135 | this.description = JSON.stringify(this.changelogArray); |
| @@ -119,6 +146,8 @@ struct NewVersion { | |||
| 119 | this.initDataByStatus(); | 146 | this.initDataByStatus(); |
| 120 | new NotificationHelper().cancelAll(); | 147 | new NotificationHelper().cancelAll(); |
| 121 | this.handleAbnormalState(); | 148 | this.handleAbnormalState(); |
| 149 | + this.handleAbilityWantAction(); | ||
| 150 | + this.syncDownloadNoNetworkDialog(); | ||
| 122 | } | 151 | } |
| 123 | 152 | ||
| 124 | private async handleAbnormalState(): Promise<void> { | 153 | private async handleAbnormalState(): Promise<void> { |
| @@ -135,6 +164,10 @@ struct NewVersion { | |||
| 135 | 164 | ||
| 136 | onPageHide() { | 165 | onPageHide() { |
| 137 | this.log('onPageHide NewVersionPage'); | 166 | this.log('onPageHide NewVersionPage'); |
| 167 | + if (this.isNoNetworkDialogOpen) { | ||
| 168 | + this.noNetworkDialogController.close(); | ||
| 169 | + this.isNoNetworkDialogOpen = false; | ||
| 170 | + } | ||
| 138 | } | 171 | } |
| 139 | 172 | ||
| 140 | onBackPress(): boolean { | 173 | onBackPress(): boolean { |
| @@ -165,6 +198,28 @@ struct NewVersion { | |||
| 165 | } | 198 | } |
| 166 | } | 199 | } |
| 167 | 200 | ||
| 201 | + private syncDownloadNoNetworkDialog(): void { | ||
| 202 | + if (this.downloadNoNetworkDialogVisible) { | ||
| 203 | + this.showNoNetworkDialog(); | ||
| 204 | + return; | ||
| 205 | + } | ||
| 206 | + this.hideNoNetworkDialog(); | ||
| 207 | + } | ||
| 208 | + | ||
| 209 | + showNoNetworkDialog(): void { | ||
| 210 | + if (!this.isNoNetworkDialogOpen) { | ||
| 211 | + this.isNoNetworkDialogOpen = true; | ||
| 212 | + this.noNetworkDialogController.open(); | ||
| 213 | + } | ||
| 214 | + } | ||
| 215 | + | ||
| 216 | + hideNoNetworkDialog(): void { | ||
| 217 | + if (this.isNoNetworkDialogOpen) { | ||
| 218 | + this.noNetworkDialogController.close(); | ||
| 219 | + this.isNoNetworkDialogOpen = false; | ||
| 220 | + } | ||
| 221 | + } | ||
| 222 | + | ||
| 168 | public onLanguageChange(): void { | 223 | public onLanguageChange(): void { |
| 169 | this.initDataByStatus(); | 224 | this.initDataByStatus(); |
| 170 | } | 225 | } |
| @@ -241,19 +296,48 @@ struct NewVersion { | |||
| 241 | } | 296 | } |
| 242 | } | 297 | } |
| 243 | 298 | ||
| 299 | + private async handleAbilityWantAction(): Promise<void> { | ||
| 300 | + const autoDownload = Boolean(globalThis.abilityWant?.parameters?.autoDownload); | ||
| 301 | + const autoInstall = Boolean(globalThis.abilityWant?.parameters?.autoInstall); | ||
| 302 | + if (!autoDownload && !autoInstall) { | ||
| 303 | + return; | ||
| 304 | + } | ||
| 305 | + globalThis.abilityWant = null; | ||
| 306 | + const statusResult = await OtaUpdateManager.getInstance().getOtaStatus(); | ||
| 307 | + if (statusResult?.callResult !== UpgradeCallResult.OK || statusResult?.data?.status === undefined) { | ||
| 308 | + this.log('Ignore ability want action because the current update status is unavailable.'); | ||
| 309 | + return; | ||
| 310 | + } | ||
| 311 | + const currentStatus = statusResult.data.status; | ||
| 312 | + await this.initDataByStatus(); | ||
| 313 | + if (autoDownload) { | ||
| 314 | + if (StateManager.getButtonClickAction(currentStatus) === UpdateAction.DOWNLOAD) { | ||
| 315 | + await this.startDownload(); | ||
| 316 | + } | ||
| 317 | + return; | ||
| 318 | + } | ||
| 319 | + if (StateManager.getButtonClickAction(currentStatus) === UpdateAction.INSTALL) { | ||
| 320 | + await this.upgrade(); | ||
| 321 | + } | ||
| 322 | + } | ||
| 323 | + | ||
| 324 | + private async startDownload(): Promise<void> { | ||
| 325 | + if (await NetUtils.isCellularNetwork()) { | ||
| 326 | + DialogHelper.displayNetworkDialog(); | ||
| 327 | + return; | ||
| 328 | + } | ||
| 329 | + if (this.isABStreamInstall()) { | ||
| 330 | + await this.upgrade(); | ||
| 331 | + return; | ||
| 332 | + } | ||
| 333 | + await OtaUpdateManager.getInstance().download(); | ||
| 334 | + } | ||
| 335 | + | ||
| 244 | private async handleButtonClick(): Promise<void> { | 336 | private async handleButtonClick(): Promise<void> { |
| 245 | this.log('handleButtonClick'); | 337 | this.log('handleButtonClick'); |
| 246 | switch (StateManager.getButtonClickAction(this.updateStatus)) { | 338 | switch (StateManager.getButtonClickAction(this.updateStatus)) { |
| 247 | case UpdateAction.DOWNLOAD: | 339 | case UpdateAction.DOWNLOAD: |
| 248 | - if (await NetUtils.isCellularNetwork()) { | 340 | + await this.startDownload(); |
| 249 | - DialogHelper.displayNetworkDialog(); | ||
| 250 | - } else { | ||
| 251 | - if (this.isABStreamInstall()) { | ||
| 252 | - this.upgrade(); | ||
| 253 | - } else { | ||
| 254 | - OtaUpdateManager.getInstance().download(); | ||
| 255 | - } | ||
| 256 | - } | ||
| 257 | break; | 341 | break; |
| 258 | case UpdateAction.INSTALL: | 342 | case UpdateAction.INSTALL: |
| 259 | this.upgrade(); | 343 | this.upgrade(); |
| @@ -262,7 +346,11 @@ struct NewVersion { | |||
| 262 | this.reboot(); | 346 | this.reboot(); |
| 263 | break; | 347 | break; |
| 264 | case UpdateAction.CANCEL: | 348 | case UpdateAction.CANCEL: |
| 265 | - OtaUpdateManager.getInstance().cancel(); | 349 | + if (this.updateStatus === UpdateState.DOWNLOADING) { |
| 350 | + DialogHelper.displayCancelDownloadDialog(); | ||
| 351 | + } else { | ||
| 352 | + OtaUpdateManager.getInstance().cancel(); | ||
| 353 | + } | ||
| 266 | break; | 354 | break; |
| 267 | case UpdateAction.RESUME: | 355 | case UpdateAction.RESUME: |
| 268 | if (this.isABStreamInstall()) { | 356 | if (this.isABStreamInstall()) { |
| @@ -279,28 +367,21 @@ struct NewVersion { | |||
| 279 | build() { | 367 | build() { |
| 280 | Flex({ direction: FlexDirection.Column, alignItems: ItemAlign.Center, justifyContent: FlexAlign.Start }) { | 368 | Flex({ direction: FlexDirection.Column, alignItems: ItemAlign.Center, justifyContent: FlexAlign.Start }) { |
| 281 | Column() { | 369 | Column() { |
| 282 | - TitleBar({ title: $r('app.string.title_new_version'), onBack: this.onBackPress.bind(this)}); | 370 | + TitleBar({ title: $r('app.string.title_new_version'), onBack: this.onBackPress.bind(this), showMenu: true}); |
| 283 | }.flexShrink(0); | 371 | }.flexShrink(0); |
| 284 | Column() { | 372 | Column() { |
| 285 | Scroll() { | 373 | Scroll() { |
| 286 | Column() { | 374 | Column() { |
| 287 | - ProgressContent(); | 375 | + Row() { |
| 376 | + Image($r('app.media.home_bg')) | ||
| 377 | + .width('100%') | ||
| 378 | + .constraintSize({ maxWidth: 600 }) | ||
| 379 | + .objectFit(ImageFit.Contain) | ||
| 380 | + } | ||
| 381 | + .width('100%') | ||
| 382 | + .padding({ left: 20, right: 20, bottom: 20 }) | ||
| 383 | + .justifyContent(FlexAlign.Center) | ||
| 288 | if (this.isInitComplete) { | 384 | if (this.isInitComplete) { |
| 289 | - Text(this.displayNewVersionName) | ||
| 290 | - .fontSize($r('app.float.text_size_version_name')) | ||
| 291 | - .maxLines(5) | ||
| 292 | - .textOverflow({ overflow: TextOverflow.Ellipsis }) | ||
| 293 | - .padding({ | ||
| 294 | - right: $r('app.float.changelog_detail_content_padding_horizontal'), | ||
| 295 | - left: $r('app.float.changelog_detail_content_padding_horizontal'), | ||
| 296 | - }) | ||
| 297 | - .fontWeight(FontWeight.Medium); | ||
| 298 | - Text(this.displayFileSize) | ||
| 299 | - .fontSize($r('app.float.text_size_version_size')).fontWeight(FontWeight.Regular).opacity(0.6) | ||
| 300 | - .margin({ | ||
| 301 | - top: $r('app.float.new_version_size_margin_top'), | ||
| 302 | - bottom: $r('app.float.new_version_size_margin_bottom') | ||
| 303 | - }) | ||
| 304 | ChangelogContent({ | 385 | ChangelogContent({ |
| 305 | isCurrentPage: false, | 386 | isCurrentPage: false, |
| 306 | isNeedFold: this.changelogArray?.length > 1, | 387 | isNeedFold: this.changelogArray?.length > 1, |
| @@ -315,23 +396,49 @@ struct NewVersion { | |||
| 315 | }.flexGrow(1) | 396 | }.flexGrow(1) |
| 316 | 397 | ||
| 317 | Column() { | 398 | Column() { |
| 318 | - Button() { | 399 | + if (this.updateStatus === UpdateState.DOWNLOADING) { |
| 319 | - Text(this.buttonText) | 400 | + Stack({ alignContent: Alignment.Center }) { |
| 320 | - .fontSize($r('app.float.text_size_btn')).fontColor(Color.White).fontWeight(FontWeight.Medium) | 401 | + Progress({ |
| 321 | - .margin({ left: $r('app.float.custom_button_text_margin_left'), | 402 | + value: this.downloadProgress, |
| 322 | - right: $r('app.float.custom_button_text_margin_right') }) | 403 | + total: 100, |
| 323 | - } | 404 | + type: ProgressType.Capsule |
| 324 | - .type(ButtonType.Capsule) | 405 | + }) |
| 325 | - .constraintSize({ minWidth: $r('app.float.custom_button_width') }) | 406 | + .width($r('app.float.custom_button_width')) |
| 326 | - .height($r('app.float.custom_button_height')) | 407 | + .height($r('app.float.custom_button_height')) |
| 327 | - .backgroundColor($r('app.color.blue')) | 408 | + .color($r('app.color.blue')) |
| 328 | - .opacity(this.isButtonEnable ? 1 : 0.4) | 409 | + .backgroundColor($r('app.color.white')) |
| 329 | - .visibility(this.isButtonVisible ? Visibility.Visible : Visibility.None) | 410 | + Text(`下载中 ${this.getDisplayProgress(this.downloadProgress)} %`) |
| 330 | - .onClick(() => { | 411 | + .fontSize($r('app.float.text_size_btn')) |
| 331 | - if(this.isButtonEnable) { | 412 | + .fontColor($r('app.color.black')) |
| 332 | - this.handleButtonClick(); | 413 | + .fontWeight(FontWeight.Medium) |
| 333 | } | 414 | } |
| 334 | - }) | 415 | + .width($r('app.float.custom_button_width')) |
| 416 | + .height($r('app.float.custom_button_height')) | ||
| 417 | + .visibility(this.isButtonVisible ? Visibility.Visible : Visibility.None) | ||
| 418 | + .onClick(() => { | ||
| 419 | + if (this.isButtonEnable) { | ||
| 420 | + this.handleButtonClick(); | ||
| 421 | + } | ||
| 422 | + }) | ||
| 423 | + } else { | ||
| 424 | + Button() { | ||
| 425 | + Text(this.buttonText) | ||
| 426 | + .fontSize($r('app.float.text_size_btn')).fontColor(Color.White).fontWeight(FontWeight.Medium) | ||
| 427 | + .margin({ left: $r('app.float.custom_button_text_margin_left'), | ||
| 428 | + right: $r('app.float.custom_button_text_margin_right') }) | ||
| 429 | + } | ||
| 430 | + .type(ButtonType.Capsule) | ||
| 431 | + .constraintSize({ minWidth: $r('app.float.custom_button_width') }) | ||
| 432 | + .height($r('app.float.custom_button_height')) | ||
| 433 | + .backgroundColor($r('app.color.blue')) | ||
| 434 | + .opacity(this.isButtonEnable ? 1 : 0.4) | ||
| 435 | + .visibility(this.isButtonVisible ? Visibility.Visible : Visibility.None) | ||
| 436 | + .onClick(() => { | ||
| 437 | + if(this.isButtonEnable) { | ||
| 438 | + this.handleButtonClick(); | ||
| 439 | + } | ||
| 440 | + }) | ||
| 441 | + } | ||
| 335 | } | 442 | } |
| 336 | .flexShrink(0) | 443 | .flexShrink(0) |
| 337 | .padding({ | 444 | .padding({ |
| @@ -341,9 +448,17 @@ struct NewVersion { | |||
| 341 | } | 448 | } |
| 342 | .width('100%') | 449 | .width('100%') |
| 343 | .height('100%') | 450 | .height('100%') |
| 451 | + .expandSafeArea([SafeAreaType.SYSTEM], [SafeAreaEdge.TOP, SafeAreaEdge.BOTTOM]) | ||
| 344 | .backgroundColor($r('app.color.page_background')) | 452 | .backgroundColor($r('app.color.page_background')) |
| 345 | } | 453 | } |
| 346 | 454 | ||
| 455 | + private getDisplayProgress(progress: number): number { | ||
| 456 | + if (isNaN(progress)) { | ||
| 457 | + return 0; | ||
| 458 | + } | ||
| 459 | + return Number((progress).toFixed(0)); | ||
| 460 | + } | ||
| 461 | + | ||
| 347 | private log(message: string): void { | 462 | private log(message: string): void { |
| 348 | LogUtils.log('NewVersion', message); | 463 | LogUtils.log('NewVersion', message); |
| 349 | } | 464 | } |
| @@ -373,4 +488,4 @@ struct NewVersion { | |||
| 373 | this.restartDialogController.open(); | 488 | this.restartDialogController.open(); |
| 374 | } | 489 | } |
| 375 | } | 490 | } |
| 376 | -} | 491 | +} |
| @@ -0,0 +1,100 @@ | |||
| 1 | +/* | ||
| 2 | + * Copyright (c) 2026 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 | + | ||
| 16 | +import { SettingOptions } from '@ohos/common/src/main/ets/component/SettingOptions' | ||
| 17 | +import { TitleBar } from '@ohos/common/src/main/ets/component/TitleBar'; | ||
| 18 | +import { NIGHT_UPGRADE_START, NIGHT_UPGRADE_END } from '@ohos/common/src/main/ets/const/update_const'; | ||
| 19 | +import { FormatUtils } from '@ohos/common/src/main/ets/util/FormatUtils'; | ||
| 20 | +import { LogUtils } from '@ohos/common/src/main/ets/util/LogUtils'; | ||
| 21 | +import { SettingsViewModel } from '../viewmodel/settingsViewModel'; | ||
| 22 | + | ||
| 23 | +const TAG = 'SettingsView' | ||
| 24 | + | ||
| 25 | +/** | ||
| 26 | + * 更新选项 | ||
| 27 | + * | ||
| 28 | + * @since 2026-04-13 | ||
| 29 | + */ | ||
| 30 | +@Entry | ||
| 31 | +@Component | ||
| 32 | +struct SettingsView { | ||
| 33 | + @State private viewModel: SettingsViewModel = new SettingsViewModel(); | ||
| 34 | + | ||
| 35 | + aboutToAppear(): void { | ||
| 36 | + LogUtils.info(TAG, 'aboutToAppear SettingsView'); | ||
| 37 | + globalThis.newVersionThis = this; | ||
| 38 | + } | ||
| 39 | + | ||
| 40 | + onPageShow(): void { | ||
| 41 | + LogUtils.info(TAG, 'onPageShow SettingsView'); | ||
| 42 | + globalThis.currentPage = 'pages/settingsView'; | ||
| 43 | + this.viewModel.initUpdatePolicy(); | ||
| 44 | + } | ||
| 45 | + | ||
| 46 | + onPageHide(): void { | ||
| 47 | + LogUtils.info(TAG, 'onPageHide SettingsView'); | ||
| 48 | + } | ||
| 49 | + | ||
| 50 | + onBackPress(): boolean | void { | ||
| 51 | + LogUtils.info(TAG, 'onBackPress SettingsView'); | ||
| 52 | + } | ||
| 53 | + | ||
| 54 | + build() { | ||
| 55 | + NavDestination() { | ||
| 56 | + this.pageContent(); | ||
| 57 | + } | ||
| 58 | + .hideTitleBar(true) | ||
| 59 | + .onShown((): void => this.onPageShow()) | ||
| 60 | + .id(`${TAG}_NavDestination`) | ||
| 61 | + } | ||
| 62 | + | ||
| 63 | + @Builder | ||
| 64 | + pageContent() { | ||
| 65 | + Flex({ direction: FlexDirection.Column, alignItems: ItemAlign.Center, justifyContent: FlexAlign.Start }) { | ||
| 66 | + TitleBar({ | ||
| 67 | + title: $r('app.string.local_settings') | ||
| 68 | + }) | ||
| 69 | + | ||
| 70 | + Scroll() { | ||
| 71 | + Column({ space: '24vp' }) { | ||
| 72 | + SettingOptions({ | ||
| 73 | + title: $r('app.string.setting_auto_download_title'), | ||
| 74 | + description: $r('app.string.setting_auto_download_description'), | ||
| 75 | + toggleChange: (isOn: boolean) => { | ||
| 76 | + this.viewModel.autoDownloadToggleChange(isOn) | ||
| 77 | + }, | ||
| 78 | + isOn: this.viewModel.isAutoDownloadOpen() | ||
| 79 | + }) | ||
| 80 | + | ||
| 81 | + SettingOptions({ | ||
| 82 | + title: $r('app.string.setting_auto_update_title'), | ||
| 83 | + description: $r('app.string.setting_auto_update_description', | ||
| 84 | + FormatUtils.getDateRangeFormat(NIGHT_UPGRADE_START, NIGHT_UPGRADE_END)), | ||
| 85 | + toggleChange: (isOn: boolean) => { | ||
| 86 | + this.viewModel.nightUpgradeToggleChange(isOn); | ||
| 87 | + }, | ||
| 88 | + isOn: this.viewModel.isAutoUpgradeOpen() | ||
| 89 | + }) | ||
| 90 | + } | ||
| 91 | + .padding({ | ||
| 92 | + top: '16vp' | ||
| 93 | + }) | ||
| 94 | + .id(`${TAG}_pageContent_Column`) | ||
| 95 | + } | ||
| 96 | + } | ||
| 97 | + .expandSafeArea([SafeAreaType.SYSTEM], [SafeAreaEdge.TOP, SafeAreaEdge.BOTTOM]) | ||
| 98 | + .backgroundColor($r('app.color.page_background')) | ||
| 99 | + } | ||
| 100 | +} | ||
| @@ -0,0 +1,94 @@ | |||
| 1 | +/* | ||
| 2 | + * Copyright (c) 2026 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 | + | ||
| 16 | +import { LogUtils } from '@ohos/common/src/main/ets/util/LogUtils'; | ||
| 17 | +import { OtaUpdateManager } from 'ota/src/main/ets/manager/OtaUpdateManager'; | ||
| 18 | +import update from '@ohos.update'; | ||
| 19 | +import { JSON } from '@kit.ArkTS'; | ||
| 20 | + | ||
| 21 | +const TAG = 'SettingsViewModel'; | ||
| 22 | + | ||
| 23 | +/** | ||
| 24 | + * 设置页面视图控制 | ||
| 25 | + * | ||
| 26 | + * @since 2026-04-13 | ||
| 27 | + */ | ||
| 28 | +export class SettingsViewModel { | ||
| 29 | + private isAutoDownload: boolean = false; | ||
| 30 | + private isAutoUpgrade: boolean = false; | ||
| 31 | + | ||
| 32 | + constructor() { | ||
| 33 | + } | ||
| 34 | + | ||
| 35 | + isAutoDownloadOpen(): boolean { | ||
| 36 | + LogUtils.info(TAG, `isAutoDownloadOpen: ${this.isAutoDownload}`); | ||
| 37 | + return this.isAutoDownload; | ||
| 38 | + } | ||
| 39 | + | ||
| 40 | + isAutoUpgradeOpen(): boolean { | ||
| 41 | + LogUtils.info(TAG, `isAutoUpgradeOpen: ${this.isAutoUpgrade}`); | ||
| 42 | + return this.isAutoUpgrade; | ||
| 43 | + } | ||
| 44 | + | ||
| 45 | + async initUpdatePolicy(): Promise<void> { | ||
| 46 | + OtaUpdateManager.getInstance().getUpdatePolicy().then((upgradePolicy: update.UpgradePolicy) => { | ||
| 47 | + LogUtils.info(TAG, `initUpdatePolicy: ${JSON.stringify(upgradePolicy)}`); | ||
| 48 | + if (!upgradePolicy.downloadStrategy && upgradePolicy.autoUpgradeStrategy) { | ||
| 49 | + // 不能出现夜间安装打开,自动下载关闭的情况 | ||
| 50 | + upgradePolicy.downloadStrategy = false; | ||
| 51 | + upgradePolicy.autoUpgradeStrategy = false; | ||
| 52 | + OtaUpdateManager.getInstance().setUpdatePolicy(upgradePolicy); | ||
| 53 | + } | ||
| 54 | + this.refreshSwitches(upgradePolicy); | ||
| 55 | + }); | ||
| 56 | + } | ||
| 57 | + | ||
| 58 | + async autoDownloadToggleChange(isOn: boolean): Promise<void> { | ||
| 59 | + LogUtils.info(TAG, `autoDownloadToggleChange isOn: ${isOn}`); | ||
| 60 | + let upgradePolicy = await OtaUpdateManager.getInstance().getUpdatePolicy(); | ||
| 61 | + if (isOn) { | ||
| 62 | + this.isAutoDownload = true; | ||
| 63 | + upgradePolicy.downloadStrategy = true; | ||
| 64 | + } else { | ||
| 65 | + this.isAutoDownload = false; | ||
| 66 | + this.isAutoUpgrade = false; | ||
| 67 | + upgradePolicy.downloadStrategy = false; | ||
| 68 | + // 关闭自动下载,夜间安装也要随之关闭 | ||
| 69 | + upgradePolicy.autoUpgradeStrategy = false; | ||
| 70 | + } | ||
| 71 | + OtaUpdateManager.getInstance().setUpdatePolicy(upgradePolicy); | ||
| 72 | + } | ||
| 73 | + | ||
| 74 | + async nightUpgradeToggleChange(isOn: boolean): Promise<void> { | ||
| 75 | + LogUtils.info(TAG, `nightUpgradeToggleChange isOn: ${isOn}`); | ||
| 76 | + let upgradePolicy = await OtaUpdateManager.getInstance().getUpdatePolicy(); | ||
| 77 | + if (isOn) { | ||
| 78 | + this.isAutoUpgrade = true; | ||
| 79 | + this.isAutoDownload = true; | ||
| 80 | + upgradePolicy.autoUpgradeStrategy = true; | ||
| 81 | + // 打开夜间升级,自动下载也要随之打开 | ||
| 82 | + upgradePolicy.downloadStrategy = true; | ||
| 83 | + } else { | ||
| 84 | + this.isAutoUpgrade = false; | ||
| 85 | + upgradePolicy.autoUpgradeStrategy = false; | ||
| 86 | + } | ||
| 87 | + OtaUpdateManager.getInstance().setUpdatePolicy(upgradePolicy); | ||
| 88 | + } | ||
| 89 | + | ||
| 90 | + refreshSwitches(upgradePolicy: update.UpgradePolicy): void { | ||
| 91 | + this.isAutoDownload = upgradePolicy.downloadStrategy; | ||
| 92 | + this.isAutoUpgrade = upgradePolicy.autoUpgradeStrategy; | ||
| 93 | + } | ||
| 94 | +} | ||
| @@ -68,6 +68,13 @@ | |||
| 68 | "type": "service", | 68 | "type": "service", |
| 69 | "visible": true, | 69 | "visible": true, |
| 70 | "srcEntrance": "./ets/ServiceExtAbility/service.ets" | 70 | "srcEntrance": "./ets/ServiceExtAbility/service.ets" |
| 71 | + }, | ||
| 72 | + { | ||
| 73 | + "name": "UpdateSystemDialogAbility", | ||
| 74 | + "srcEntry": "./ets/UpdateSystemDialog/UpdateSystemDialogAbility.ets", | ||
| 75 | + "description": "Software update system dialog", | ||
| 76 | + "type": "sysDialog/common", | ||
| 77 | + "exported": true | ||
| 71 | } | 78 | } |
| 72 | ], | 79 | ], |
| 73 | "requestPermissions": [ | 80 | "requestPermissions": [ |
| @@ -79,7 +86,13 @@ | |||
| 79 | }, | 86 | }, |
| 80 | { | 87 | { |
| 81 | "name": "ohos.permission.START_ABILITIES_FROM_BACKGROUND" | 88 | "name": "ohos.permission.START_ABILITIES_FROM_BACKGROUND" |
| 89 | + }, | ||
| 90 | + { | ||
| 91 | + "name": "ohos.permission.MANAGE_SECURE_SETTINGS" | ||
| 92 | + }, | ||
| 93 | + { | ||
| 94 | + "name": "ohos.permission.START_SYSTEM_DIALOG" | ||
| 82 | } | 95 | } |
| 83 | ] | 96 | ] |
| 84 | } | 97 | } |
| 85 | -} | 98 | +} |
| @@ -7,6 +7,14 @@ | |||
| 7 | { | 7 | { |
| 8 | "name": "white", | 8 | "name": "white", |
| 9 | "value": "#FFFFFF" | 9 | "value": "#FFFFFF" |
| 10 | + }, | ||
| 11 | + { | ||
| 12 | + "name": "black", | ||
| 13 | + "value": "#000000" | ||
| 14 | + }, | ||
| 15 | + { | ||
| 16 | + "name": "deep_blue", | ||
| 17 | + "value": "#0A59F7" | ||
| 10 | } | 18 | } |
| 11 | ] | 19 | ] |
| 12 | } | 20 | } |
| @@ -3,6 +3,10 @@ | |||
| 3 | { | 3 | { |
| 4 | "name": "card_border_radius", | 4 | "name": "card_border_radius", |
| 5 | "value": "24vp" | 5 | "value": "24vp" |
| 6 | + }, | ||
| 7 | + { | ||
| 8 | + "name": "index_center_image_width", | ||
| 9 | + "value": "150vp" | ||
| 6 | } | 10 | } |
| 7 | ] | 11 | ] |
| 8 | } | 12 | } |
| @@ -2,6 +2,8 @@ | |||
| 2 | "src": [ | 2 | "src": [ |
| 3 | "pages/index", | 3 | "pages/index", |
| 4 | "pages/newVersion", | 4 | "pages/newVersion", |
| 5 | - "pages/currentVersion" | 5 | + "pages/currentVersion", |
| 6 | + "pages/settingsView", | ||
| 7 | + "UpdateSystemDialog/pages/UpdateSystemDialogPage" | ||
| 6 | ] | 8 | ] |
| 7 | } | 9 | } |
| @@ -0,0 +1,29 @@ | |||
| 1 | +-----BEGIN CERTIFICATE----- | ||
| 2 | +MIICNDCCAbegAwIBAgIEPkDWrTAMBggqhkjOPQQDAwUAMGMxCzAJBgNVBAYTAkNO | ||
| 3 | +MRQwEgYDVQQKEwtPcGVuSGFybW9ueTEZMBcGA1UECxMQT3Blbkhhcm1vbnkgVGVh | ||
| 4 | +bTEjMCEGA1UEAxMaT3Blbkhhcm1vbnkgQXBwbGljYXRpb24gQ0EwHhcNMjIxMDA5 | ||
| 5 | +MDEzMTA0WhcNMzIxMDA2MDEzMTA0WjBoMQswCQYDVQQGEwJDTjEUMBIGA1UEChML | ||
| 6 | +T3Blbkhhcm1vbnkxGTAXBgNVBAsTEE9wZW5IYXJtb255IFRlYW0xKDAmBgNVBAMT | ||
| 7 | +H09wZW5IYXJtb255IEFwcGxpY2F0aW9uIFJlbGVhc2UwWTATBgcqhkjOPQIBBggq | ||
| 8 | +hkjOPQMBBwNCAATbYOCQQpW5fdkYHN45v0X3AHax12jPBdEDosFRIZ1eXmxOYzSG | ||
| 9 | +JwMfsHhUU90E8lI0TXYZnNmgM1sovubeQqATo1IwUDAdBgNVHQ4EFgQU+3Gu6GVx | ||
| 10 | +EFCwl3Bxmou72g1RWBowDgYDVR0PAQH/BAQDAgeAMB8GA1UdIwQYMBaAFNuGtyIW | ||
| 11 | +1QuhS7fdJXu58QV9oi1HMAwGCCqGSM49BAMDBQADaQAwZgIxAI6oQLYENgt6sA9G | ||
| 12 | +IVf4eRg3feITJxZX2QvVv2YONy99gX+WW2ZDLtNzgx3ks5HrwQIxANewK7wVFlJx | ||
| 13 | +5Xdn8Ao0pOkdyBRpRnPL6FUSANqY+pbdUvEo88R0rXc/cR84WkQYBw== | ||
| 14 | +-----END CERTIFICATE----- | ||
| 15 | +-----BEGIN CERTIFICATE----- | ||
| 16 | +MIICYTCCAeWgAwIBAgIEHmXAPTAMBggqhkjOPQQDAwUAMGgxCzAJBgNVBAYTAkNO | ||
| 17 | +MRQwEgYDVQQKEwtPcGVuSGFybW9ueTEZMBcGA1UECxMQT3Blbkhhcm1vbnkgVGVh | ||
| 18 | +bTEoMCYGA1UEAxMfT3Blbkhhcm1vbnkgQXBwbGljYXRpb24gUm9vdCBDQTAeFw0y | ||
| 19 | +MTAyMDIxMjE1MzJaFw00OTEyMzExMjE1MzJaMGMxCzAJBgNVBAYTAkNOMRQwEgYD | ||
| 20 | +VQQKEwtPcGVuSGFybW9ueTEZMBcGA1UECxMQT3Blbkhhcm1vbnkgVGVhbTEjMCEG | ||
| 21 | +A1UEAxMaT3Blbkhhcm1vbnkgQXBwbGljYXRpb24gQ0EwdjAQBgcqhkjOPQIBBgUr | ||
| 22 | +gQQAIgNiAAQhnu7Hna8XNa2KyqRf5+lBJScE4xqf89N0g0OuqAb2re8nGsvWkw26 | ||
| 23 | +uDekfnBYicd+G3Cydqa2zFIwV7Talyg2ULW3r8KbGpyl84mJEPPRmCGJ+H9gtCsf | ||
| 24 | ++OrJ4Y76LVWjYzBhMB8GA1UdIwQYMBaAFBc6EKGrGXzlAE+s0Zgnsphadw7NMA8G | ||
| 25 | +A1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgEGMB0GA1UdDgQWBBTbhrciFtUL | ||
| 26 | +oUu33SV7ufEFfaItRzAMBggqhkjOPQQDAwUAA2gAMGUCMG3cXjiDmXTvf7D4Omhf | ||
| 27 | +qcc2nuO+EMfWE+N9ZhBP5UhV34mAGWi3SfLU6rcV0urWEQIxAMYIb3epOnKhUrcm | ||
| 28 | +Lfu1WKzFlpYQwmw73RaCHP2I3k6NcuWOYeNwWXSNZ8o0nzvaLg== | ||
| 29 | +-----END CERTIFICATE----- | ||
| @@ -0,0 +1,10 @@ | |||
| 1 | +-----BEGIN NEW CERTIFICATE REQUEST----- | ||
| 2 | +MIIBVTCB+gIBADBoMQswCQYDVQQGEwJDTjEUMBIGA1UEChMLT3Blbkhhcm1vbnkx | ||
| 3 | +GTAXBgNVBAsTEE9wZW5IYXJtb255IFRlYW0xKDAmBgNVBAMTH09wZW5IYXJtb255 | ||
| 4 | +IEFwcGxpY2F0aW9uIFJlbGVhc2UwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAATb | ||
| 5 | +YOCQQpW5fdkYHN45v0X3AHax12jPBdEDosFRIZ1eXmxOYzSGJwMfsHhUU90E8lI0 | ||
| 6 | +TXYZnNmgM1sovubeQqAToDAwLgYJKoZIhvcNAQkOMSEwHzAdBgNVHQ4EFgQU+3Gu | ||
| 7 | +6GVxEFCwl3Bxmou72g1RWBowDAYIKoZIzj0EAwIFAANIADBFAiEApsgx1bKHVCIU | ||
| 8 | +c9aKaImMUWk7oYSDbBDSYTlWbIZrOvMCIFtvD32iKSc5/abTA3u8PdWpL3k7hQab | ||
| 9 | +ChPtge/7IHDb | ||
| 10 | +-----END NEW CERTIFICATE REQUEST----- | ||
Binary files do not support preview
Binary files do not support preview