From 025349328540f8db77447bb2feffc6ae4957b700 Mon Sep 17 00:00:00 2001 From: xiaoxiao Date: Fri, 1 Aug 2025 10:18:52 +0800 Subject: [PATCH] =?UTF-8?q?bug=E4=BF=AE=E5=A4=8D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- chatkit/src/main/ets/ChatKitClient.ets | 22 +- commons/basic/Index.ets | 6 +- .../main/ets/components/ChangePhotoGrids.ets | 1 + .../pushnotification/NotificationConfig.ets | 33 ++ .../pushnotification/NotificationOptions.ets | 80 +++ commons/basic/src/main/ets/utils/AppUtil.ets | 347 +++++++++++ .../basic/src/main/ets/utils/ChangeUtil.ets | 2 +- .../basic/src/main/ets/utils/ImageUtil.ets | 258 ++++++++ .../src/main/ets/utils/NotificationUtil.ets | 549 ++++++++++++++++++ .../src/main/ets/components/HomeIconComp.ets | 27 +- .../ets/components/HomeReplayVideoComp.ets | 1 + .../Home/src/main/ets/pages/VideoGandan.ets | 12 +- .../src/main/ets/view/MyPageSectionItem.ets | 4 +- .../src/main/ets/view/PatientListComp.ets | 8 +- .../ets/components/AddAndEditRecordComp.ets | 2 +- .../main/ets/components/PatientApplyPage.ets | 8 +- .../components/PatientCommonSettingComp.ets | 1 + .../src/main/ets/components/PatientsGroup.ets | 10 +- .../main/ets/entryability/EntryAbility.ets | 1 - 19 files changed, 1340 insertions(+), 32 deletions(-) create mode 100644 commons/basic/src/main/ets/pushnotification/NotificationConfig.ets create mode 100644 commons/basic/src/main/ets/pushnotification/NotificationOptions.ets create mode 100644 commons/basic/src/main/ets/utils/AppUtil.ets create mode 100644 commons/basic/src/main/ets/utils/ImageUtil.ets create mode 100644 commons/basic/src/main/ets/utils/NotificationUtil.ets diff --git a/chatkit/src/main/ets/ChatKitClient.ets b/chatkit/src/main/ets/ChatKitClient.ets index 1d0d4dc..7113535 100644 --- a/chatkit/src/main/ets/ChatKitClient.ets +++ b/chatkit/src/main/ets/ChatKitClient.ets @@ -17,7 +17,9 @@ import { V2NIMError, V2NIMKickedOfflineDetail, V2NIMLoginStatus, - V2NIMMessageRevokeNotification + V2NIMMessage, + V2NIMMessageRevokeNotification, + V2NIMP2PMessageMuteMode } from '@nimsdk/base'; import { ContactRepo } from '../../../Index'; import { KitLogger } from './logger/AppLogger'; @@ -26,6 +28,9 @@ import { ChatRepo } from './repo/ChatRepo'; import { saveLocalRevokeMessageFormOther } from './utils/MessageUtils'; import { router } from '@kit.ArkUI'; import { HMRouterMgr } from '@hadss/hmrouter'; +import { NotificationUtil } from '@itcast/basic'; +import { BusinessError } from '@kit.BasicServicesKit'; +import { NotificationBasicOptions } from '@itcast/basic/src/main/ets/pushnotification/NotificationOptions'; export const currentConversationChanged: string = 'CurrentConversationChanged' @@ -185,6 +190,21 @@ export class ChatKitClient { ChatRepo.onRevokeMessage(ChatKitClient.onRevokeFun) ChatKitClient.hasInitListener = true + // ChatKitClient.nim.messageService?.on("onReceiveMessages", (messages: V2NIMMessage[]) => { + // for (const message of messages) { + // let basicOptions: NotificationBasicOptions = { + // id: Number(message.messageServerId), + // title: String(message['fromNick']), + // text: String(message.text), + // } + // NotificationUtil.publishBasic(basicOptions).then((id) => { + // + // }).catch((err: BusinessError) => { + // console.error('推送展示失败:', err) + // }); + // } + // }) + } private static onSyncFinishedFun = () => { diff --git a/commons/basic/Index.ets b/commons/basic/Index.ets index d1c96ab..48418af 100644 --- a/commons/basic/Index.ets +++ b/commons/basic/Index.ets @@ -1,3 +1,5 @@ +import { NotificationUtil } from './src/main/ets/utils/NotificationUtil' + export { hdHttp, HdResponse } from './src/main/ets/utils/request' export { authStore, HdUser, AUTH_STORE_KEY } from './src/main/ets/utils/auth' @@ -95,4 +97,6 @@ export { applyListCallBacl, applyListModel, applyHistoryCallBacl , historyObject export { TripleOptionDialog } from './src/main//ets/Views/TripleOptionDialog' -export { HdTwoNav } from './src/main/ets/components/HdTwoNav' \ No newline at end of file +export { HdTwoNav } from './src/main/ets/components/HdTwoNav' + +export { NotificationUtil } from './src/main/ets/utils/NotificationUtil' \ No newline at end of file diff --git a/commons/basic/src/main/ets/components/ChangePhotoGrids.ets b/commons/basic/src/main/ets/components/ChangePhotoGrids.ets index bf3ebea..c27406b 100644 --- a/commons/basic/src/main/ets/components/ChangePhotoGrids.ets +++ b/commons/basic/src/main/ets/components/ChangePhotoGrids.ets @@ -27,6 +27,7 @@ export struct ChangePhotoGrids { GridItem() { Stack() { Image(item.url ? (BasicConstant.urlHtml + item.url) : item.uri) + .alt($r('app.media.default_video')) .width(80) .height(80) .objectFit(ImageFit.Fill) diff --git a/commons/basic/src/main/ets/pushnotification/NotificationConfig.ets b/commons/basic/src/main/ets/pushnotification/NotificationConfig.ets new file mode 100644 index 0000000..d0c7783 --- /dev/null +++ b/commons/basic/src/main/ets/pushnotification/NotificationConfig.ets @@ -0,0 +1,33 @@ +import { notificationManager } from '@kit.NotificationKit'; +import { WantAgent } from '@kit.AbilityKit'; +import { image } from '@kit.ImageKit'; + +export class NotificationConfig { + notificationSlotType: notificationManager.SlotType = notificationManager.SlotType.SERVICE_INFORMATION; //通道类型,默认服务类型。 + deliveryTime?: number; //通知发送时间。 + tapDismissed: boolean = false; //通知是否自动清除。 + autoDeletedTime?: number; //自动清除的时间(大于0,否者不显示角标)。 + wantAgent?: WantAgent; //WantAgent封装了应用的行为意图,点击通知时触发该行为。 + extraInfo?: Record; //扩展参数 + isAlertOnce: boolean = false; //设置是否仅有一次此通知提醒。 + isStopwatch: boolean = false; //是否显示已用时间。 + isCountDown: boolean = false; //是否显示倒计时时间。 + isFloatingIcon: boolean = true; //是否显示状态栏图标。 + label?: string; //通知标签。 + badgeIconStyle?: number; //通知角标类型。 + showDeliveryTime: boolean = false; //是否显示分发时间。 + actionButtons?: Array; //通知按钮,最多三个按钮。 + smallIcon?: image.PixelMap; //通知小图标。可选字段,图像像素的总字节数不超过100KB。实际显示效果依赖于设备能力和通知中心UI样式。 + largeIcon?: image.PixelMap; //通知大图标。可选字段,图像像素的总字节数不超过100KB。实际显示效果依赖于设备能力和通知中心UI样式。 + groupName?: string; //组通知名称。 + template?: notificationManager.NotificationTemplate; //通知模板。 + distributedOption?: notificationManager.DistributedOptions; //分布式通知的选项。 + removalWantAgent?: WantAgent; //当移除通知时,通知将被重定向到的WantAgent实例。当前不支持跳转UIAbility,只支持发布公共事件及跳转系统Service能力(WantAgent的OperationType >= 3)。 + badgeNumber?: number = 1; //应用程序图标上显示的通知数。 + appMessageId?: string; //应用发送通知携带的唯一标识字段, 用于通知去重。如果同一应用通过本地和云端等不同途径发布携带相同appMessageId的通知,设备只展示一条消息,之后收到的重复通知会被静默去重,不展示、不提醒。去重标识仅在通知发布的24小时内有效,超过24小时或者设备重启失效。 + sound?: string; //应用通知自定义铃声,需要通过Push云侧获取自定义发送铃声权限后,该字段才会生效 + + additionalText?: string; //通知附加内容,是对通知内容的补充。 + lockscreenPicture?: image.PixelMap; //通知在锁屏界面显示的图片。 + +} \ No newline at end of file diff --git a/commons/basic/src/main/ets/pushnotification/NotificationOptions.ets b/commons/basic/src/main/ets/pushnotification/NotificationOptions.ets new file mode 100644 index 0000000..f05e174 --- /dev/null +++ b/commons/basic/src/main/ets/pushnotification/NotificationOptions.ets @@ -0,0 +1,80 @@ +import { WantAgent } from '@kit.AbilityKit'; +import { notificationManager } from '@kit.NotificationKit'; +import { image } from '@kit.ImageKit'; + +export interface NotificationOptions { + id?: number; //通知ID。 + notificationSlotType?: notificationManager.SlotType; //通道类型。 + showDeliveryTime?: boolean; //是否显示分发时间。 + deliveryTime?: number; //通知发送时间。 + tapDismissed?: boolean; //通知是否自动清除。 + autoDeletedTime?: number; //自动清除的时间。 + wantAgent?: WantAgent; //WantAgent封装了应用的行为意图,点击通知时触发该行为。 + extraInfo?: Record; //扩展参数 + isAlertOnce?: boolean; //设置是否仅有一次此通知提醒。 + isStopwatch?: boolean; //是否显示已用时间。 + isCountDown?: boolean; //是否显示倒计时时间。 + isFloatingIcon?: boolean; //是否显示状态栏图标。 + label?: string; //通知标签。 + actionButtons?: Array; //通知按钮,最多三个按钮。 + smallIcon?: image.PixelMap; //通知小图标。可选字段,图像像素的总字节数不超过100KB。实际显示效果依赖于设备能力和通知中心UI样式。 + largeIcon?: image.PixelMap; //通知大图标。可选字段,图像像素的总字节数不超过100KB。实际显示效果依赖于设备能力和通知中心UI样式。 + groupName?: string; //组通知名称。 + template?: notificationManager.NotificationTemplate; //通知模板。 + distributedOption?: notificationManager.DistributedOptions; //分布式通知的选项。 + removalWantAgent?: WantAgent; //当移除通知时,通知将被重定向到的WantAgent实例。当前不支持跳转UIAbility,只支持发布公共事件及跳转系统Service能力(WantAgent的OperationType >= 3)。 + badgeNumber?: number; //应用程序图标上显示的通知数。 + appMessageId?: string; //应用发送通知携带的唯一标识字段, 用于通知去重。如果同一应用通过本地和云端等不同途径发布携带相同appMessageId的通知,设备只展示一条消息,之后收到的重复通知会被静默去重,不展示、不提醒。去重标识仅在通知发布的24小时内有效,超过24小时或者设备重启失效。 + sound?: string; //应用通知自定义铃声,需要通过Push云侧获取自定义发送铃声权限后,该字段才会生效 +} + + +/** + * 描述普通文本通知 + */ +export interface NotificationBasicOptions extends NotificationOptions { + title: string; //通知标题(不可为空字符串)。 + text: string; //通知内容(不可为空字符串)。 + additionalText?: string; //通知附加内容,是对通知内容的补充。 + lockscreenPicture?: image.PixelMap; //通知在锁屏界面显示的图片。 +} + + +/** + * 描述长文本通知 + */ +export interface NotificationLongTextOptions extends NotificationBasicOptions { + briefText: string; //通知概要内容,是对通知内容的总结(不可为空字符串)。 + longText: string; //通知的长文本(不可为空字符串)。 + expandedTitle: string; //通知展开时的标题(不可为空字符串) +} + + +/** + * 描述多行文本通知。 + */ +export interface NotificationMultiLineOptions extends NotificationBasicOptions { + briefText: string; //通知概要内容,是对通知内容的总结(不可为空字符串)。 + longTitle: string; //通知展开时的标题(不可为空字符串)。 + lines: Array; //通知的多行文本。 +} + + +/** + * 描述附有图片的通知。 + */ +export interface NotificationPictureOptions extends NotificationBasicOptions { + briefText: string; //通知概要内容,是对通知内容的总结(不可为空字符串)。 + expandedTitle: string; //通知展开时的标题(不可为空字符串)。 + picture: image.PixelMap; //通知的图片内容(图像像素的总字节数不能超过2MB)。 +} + + +/** + * 描述模板的通知。 + */ +export interface NotificationTemplateOptions extends NotificationBasicOptions { + // title: string; //通知标题 和 下载标题,共用一个字段。 + fileName: string; //表示下载文件名。必填字段,值为字符串类型。 + progressValue: number; //表示下载进度,值为数值类型。 +} \ No newline at end of file diff --git a/commons/basic/src/main/ets/utils/AppUtil.ets b/commons/basic/src/main/ets/utils/AppUtil.ets new file mode 100644 index 0000000..222c4d7 --- /dev/null +++ b/commons/basic/src/main/ets/utils/AppUtil.ets @@ -0,0 +1,347 @@ +import bundleManager from '@ohos.bundle.bundleManager' +import { BusinessError } from '@ohos.base' +import { KeyboardAvoidMode, window } from '@kit.ArkUI' +import { resourceManager } from '@kit.LocalizationKit' +import { common } from '@kit.AbilityKit' + +export class AppUtil { + + private static context: common.UIAbilityContext; //common.UIAbilityContext,上下文 + + + /** + * 初始化方法,缓存全局变量,在UIAbility的onCreate方法中初始化该方法。 + * @param windowStage 窗口管理器 + */ + static init(context: common.UIAbilityContext) { + AppUtil.context = context; + } + + + /** + * 获取上下文,common.UIAbilityContext + * @returns + */ + static getContext(): common.UIAbilityContext { + if (!AppUtil.context) { + AppUtil.context = getContext() as common.UIAbilityContext; //兜底 + console.error("请在UIAbility的onCreate方法中调用AppUtil的init方法初始化!"); + } + return AppUtil.context; + } + + + /** + * 获取WindowStage + * @returns + */ + static getWindowStage(): window.WindowStage { + return AppUtil.getContext().windowStage; + } + + /** + * 获取主窗口 + */ + static getMainWindow(): window.Window { + return AppUtil.getContext().windowStage.getMainWindowSync(); + } + + /** + * 获取UIContext + * @returns + */ + static getUIContext(): UIContext { + return AppUtil.getMainWindow().getUIContext(); + } + + + /** + * 设置灰阶,APP一键置灰。 + * @param grayScale 该参数为浮点数,取值范围为[0.0, 1.0]。 + * @param onlyMainWindow 是否只置灰主窗口,默认false。 + * @returns + */ + static async setGrayScale(grayScale: number = 1.0, onlyMainWindow: boolean = false): Promise { + AppUtil.getMainWindow().setWindowGrayScale(grayScale); + if (!onlyMainWindow) { + let subWindows = await AppUtil.getContext().windowStage.getSubWindow(); + if (subWindows && subWindows.length > 0) { + subWindows.forEach((subWindow) => subWindow.setWindowGrayScale(grayScale)); + } + } + } + + + /** + * 获取当前窗口的属性 + * @param windowClass 不传该值,获取主窗口的属性 + * @returns + */ + static getWindowProperties(windowClass: window.Window = AppUtil.getMainWindow()): window.WindowProperties { + return windowClass.getWindowProperties(); + } + + + /** + * 获取虚拟键盘抬起时的页面避让模式(OFFSET-上抬模式、RESIZE-压缩模式)。 + */ + static getKeyboardAvoidMode(): KeyboardAvoidMode { + let mode = AppUtil.getUIContext().getKeyboardAvoidMode(); + if (typeof mode === 'string') { + if ('KeyBoardAvoidMode.RESIZE' === mode) { + return KeyboardAvoidMode.RESIZE; + } else { + return KeyboardAvoidMode.OFFSET; + } + } + return mode; + } + + + /** + * 设置虚拟键盘弹出时,页面的避让模式。 + * @param value (OFFSET-上抬模式、RESIZE-压缩模式) + */ + static setKeyboardAvoidMode(value: KeyboardAvoidMode): boolean { + try { + AppUtil.getUIContext().setKeyboardAvoidMode(value); + } catch (err) { + let error = err as BusinessError; + console.error(`AppUtil-setKeyboardAvoidMode-异常 ~ code: ${error.code} -·- message: ${error.message}`); + return false; + } + return true; + } + + /** + * 设置窗口的显示方向属性,使用Promise异步回调。 + * Orientation 窗口显示方向类型枚举: + * UNSPECIFIED 0 表示未定义方向模式,由系统判定。 + * PORTRAIT 1 表示竖屏显示模式。 + * LANDSCAPE 2 表示横屏显示模式。 + * PORTRAIT_INVERTED 3 表示反向竖屏显示模式。 + * LANDSCAPE_INVERTED 4 表示反向横屏显示模式。 + * AUTO_ROTATION 5 表示传感器自动旋转模式。 + * AUTO_ROTATION_PORTRAIT 6 表示传感器自动竖向旋转模式。 + * AUTO_ROTATION_LANDSCAPE 7 表示传感器自动横向旋转模式。 + * AUTO_ROTATION_RESTRICTED 8 表示受开关控制的自动旋转模式。 + * AUTO_ROTATION_PORTRAIT_RESTRICTED 9 表示受开关控制的自动竖向旋转模式。 + * AUTO_ROTATION_LANDSCAPE_RESTRICTED 10 表示受开关控制的自动横向旋转模式。 + * LOCKED 11 表示锁定模式。 + */ + static async setPreferredOrientation(orientation: window.Orientation, + windowClass: window.Window = AppUtil.getMainWindow()): Promise { + return windowClass.setPreferredOrientation(orientation); + } + + + /** + * 设置屏幕亮度值,使用Promise异步回调。 + * @param brightness 屏幕亮度值。该参数为浮点数,取值范围为[0.0, 1.0]或-1.0。1.0表示最亮,-1.0表示默认亮度。 + * @returns + */ + static async setWindowBrightness(brightness: number, + windowClass: window.Window = AppUtil.getMainWindow()): Promise { + return windowClass.setWindowBrightness(brightness); + } + + + /** + * 设置屏幕是否为常亮状态,使用Promise异步回调。 + * @param isKeepScreenOn true表示常亮;false表示不常亮。 + * @returns + */ + static async setWindowKeepScreenOn(isKeepScreenOn: boolean, + windowClass: window.Window = AppUtil.getMainWindow()): Promise { + return windowClass.setWindowKeepScreenOn(isKeepScreenOn); + } + + + /** + * 设置窗口是否为隐私模式。设置为隐私模式的窗口,窗口内容将无法被截屏或录屏。 + * @param isPrivacyMode 窗口是否为隐私模式。true表示模式开启;false表示模式关闭。 + * @returns + */ + static async setWindowPrivacyMode(isPrivacyMode: boolean, + windowClass: window.Window = AppUtil.getMainWindow()): Promise { + return windowClass.setWindowPrivacyMode(isPrivacyMode); + } + + + /** + * 设置窗口的背景色。Stage模型下,该接口需要在loadContent()或setUIContent()调用生效后使用。 + * @param color 需要设置的背景色,为十六进制RGB或ARGB颜色,不区分大小写,例如#00FF00或#FF00FF00。 + * @returns + */ + static setWindowBackgroundColor(color: string, windowClass: window.Window = AppUtil.getMainWindow()) { + try { + windowClass.setWindowBackgroundColor(color); + } catch (err) { + let error = err as BusinessError; + console.error(`AppUtil-setWindowBackgroundColor-异常 ~ code: ${error.code} -·- message: ${error.message}`); + } + } + + /** + * 设置点击时是否支持切换焦点窗口,使用Promise异步回调。 + * @param isFocusable 点击时是否支持切换焦点窗口。true表示支持;false表示不支持。 + * @returns + */ + static async setWindowFocusable(isFocusable: boolean, + windowClass: window.Window = AppUtil.getMainWindow()): Promise { + return windowClass.setWindowFocusable(isFocusable); + } + + /** + * 设置窗口是否为可触状态,使用Promise异步回调。 + * @param isTouchable 窗口是否为可触状态。true表示可触;false表示不可触。 + * @returns + */ + static async setWindowTouchable(isTouchable: boolean, + windowClass: window.Window = AppUtil.getMainWindow()): Promise { + return windowClass.setWindowTouchable(isTouchable); + } + + + /** + * 获取状态栏的高度,单位为px。 + * @returns + */ + static getStatusBarHeight(): number { + try { + const windowClass = AppUtil.getMainWindow(); + const avoidArea = windowClass.getWindowAvoidArea(window.AvoidAreaType.TYPE_SYSTEM); + return avoidArea.topRect.height; + } catch (err) { + let error = err as BusinessError; + console.error(`AppUtil-getStatusBarHeight-异常 ~ code: ${error.code} -·- message: ${error.message}`); + return 0; + } + } + + /** + * 获取底部导航条的高度,单位为px。 + * @returns + */ + static getNavigationIndicatorHeight(): number { + try { + const windowClass = AppUtil.getMainWindow(); + const avoidArea = windowClass.getWindowAvoidArea(window.AvoidAreaType.TYPE_NAVIGATION_INDICATOR); + return avoidArea.bottomRect.height; + } catch (err) { + let error = err as BusinessError; + console.error(`AppUtil-getNavigationIndicatorHeight-异常 ~ code: ${error.code} -·- message: ${error.message}`); + return 0; + } + } + + + /** + * 设置沉浸式状态栏 + * @param isLayoutFullScreen 窗口的布局是否为沉浸式布局(该沉浸式布局状态栏、导航栏仍然显示)。true表示沉浸式布局;false表示非沉浸式布局。 + * @param enable 设置窗口全屏模式时状态栏、导航栏或底部导航条是否显示,true表示显示 false表示隐藏。 + * @param color 设置窗口的背景颜色。 + * @param systemBarProperties 状态栏、导航栏的属性: + * statusBarColor 状态栏背景颜色,为十六进制RGB或ARGB颜色,不区分大小写,例如#00FF00或#FF00FF00。默认值:#0x66000000。 + * statusBarContentColor 状态栏文字颜色。当设置此属性后, isStatusBarLightIcon属性设置无效。默认值:#0xE5FFFFFF。 + * isStatusBarLightIcon 状态栏图标是否为高亮状态。true表示高亮;false表示不高亮。默认值:false。 + * navigationBarColor 导航栏背景颜色,为十六进制RGB或ARGB颜色,不区分大小写,例如#00FF00或#FF00FF00。默认值:#0x66000000。 + * navigationBarContentColor 导航栏文字颜色。当设置此属性后, isNavigationBarLightIcon属性设置无效。默认值:#0xE5FFFFFF。 + * isNavigationBarLightIcon 导航栏图标是否为高亮状态。true表示高亮;false表示不高亮。默认值:false。 + */ + static setStatusBar(isLayoutFullScreen: boolean = true, enable: boolean = true, color: string = '#FFFFFF', systemBarProperties?: window.SystemBarProperties) { + try { + const windowClass = AppUtil.getMainWindow(); + windowClass.setWindowLayoutFullScreen(isLayoutFullScreen).then(() => { + windowClass.setWindowBackgroundColor(color); + }).catch((error: BusinessError) => { + console.error(`AppUtil-setStatusBar-异常 ~ code: ${error.code} -·- message: ${error.message}`); + }); + windowClass.setWindowSystemBarEnable(enable ? ['status', 'navigation'] : []).then(() => { + windowClass.setSpecificSystemBarEnabled("navigationIndicator", enable); //底部导航条。 + }).catch((error: BusinessError) => { + console.error(`AppUtil-setStatusBar-异常 ~ code: ${error.code} -·- message: ${error.message}`); + }); + if (systemBarProperties) { + windowClass.setWindowSystemBarProperties(systemBarProperties).catch((error: BusinessError) => { + console.error(`AppUtil-setStatusBar-异常 ~ code: ${error.code} -·- message: ${error.message}`); + }); + } + } catch (err) { + let error = err as BusinessError; + console.error(`AppUtil-setStatusBar-异常 ~ code: ${error.code} -·- message: ${error.message}`); + } + } + + + /** + * 获取当前应用的BundleInfo + */ + static async getBundleInfo(): Promise { + return bundleManager.getBundleInfoForSelf(bundleManager.BundleFlag.GET_BUNDLE_INFO_WITH_APPLICATION); + } + + /** + * 获取当前应用的BundleInfo + */ + static getBundleInfoSync(): bundleManager.BundleInfo { + return bundleManager.getBundleInfoForSelfSync(bundleManager.BundleFlag.GET_BUNDLE_INFO_WITH_APPLICATION); + } + + + /** + * 获取应用包的名称。 + */ + static getBundleName(): string { + return AppUtil.getBundleInfoSync().name; + } + + /** + * 获取应用版本号。 + */ + static getVersionCode(): number { + return AppUtil.getBundleInfoSync().versionCode; + } + + /** + * 获取应用版本名。 + */ + static getVersionName(): string { + return AppUtil.getBundleInfoSync().versionName; + } + + /** + * 获取运行应用包所需要最高SDK版本号。 + */ + static getTargetVersion(): number { + return AppUtil.getBundleInfoSync().targetVersion; + } + + + /** + * 获取应用程序的配置信息 + * @returns + */ + static async getAppInfo(): Promise { + return (await AppUtil.getBundleInfo()).appInfo; + } + + /** + * 获取应用程序的配置信息 + * @returns + */ + static getAppInfoSync(): bundleManager.ApplicationInfo { + return AppUtil.getBundleInfoSync().appInfo; + } + + + /** + * 主动退出整个应用;调用该方法后,任务中心的任务默认不会清理,如需清理,需要配置removeMissionAfterTerminate为true。 + */ + static exit() { + AppUtil.getContext().terminateSelf(); + AppUtil.getContext().getApplicationContext().killAllProcesses(); + } + + +} \ No newline at end of file diff --git a/commons/basic/src/main/ets/utils/ChangeUtil.ets b/commons/basic/src/main/ets/utils/ChangeUtil.ets index f8f7c0c..b360b23 100644 --- a/commons/basic/src/main/ets/utils/ChangeUtil.ets +++ b/commons/basic/src/main/ets/utils/ChangeUtil.ets @@ -259,7 +259,7 @@ export class ChangeUtil { return new Promise((resolve, reject) => { const httpRequest = http.createHttp(); httpRequest.request( - BasicConstant.urlImage+url, + url, { method: http.RequestMethod.GET }, diff --git a/commons/basic/src/main/ets/utils/ImageUtil.ets b/commons/basic/src/main/ets/utils/ImageUtil.ets new file mode 100644 index 0000000..d3ba603 --- /dev/null +++ b/commons/basic/src/main/ets/utils/ImageUtil.ets @@ -0,0 +1,258 @@ +import fs from '@ohos.file.fs'; +import { image } from '@kit.ImageKit'; +import { resourceManager } from '@kit.LocalizationKit'; +import { Base64Util } from './Base64Util'; + +export class ImageUtil { + + + /** + * 图片base64字符串转PixelMap + * @param base64 图片base64字符串 + * @returns + */ + static base64ToPixelMap(base64: string): Promise { + //将原始图片base64字符串转变为通过base64字符串 + const reg = new RegExp('data:image/\\w+;base64,'); + const base64Str = base64.replace(reg, ''); + //将通用base64字符串转变为arrayBuffer + let arrayBuffer = Base64Util.decodeSync(base64Str).buffer as ArrayBuffer; + //将arrayBuffer转变为pixelMap + let imageSource = image.createImageSource(arrayBuffer); + let opts: image.DecodingOptions = { editable: false } + //注意:这里return的是Promise,因此使用时需要在业务侧拿到最终的PixelMap + return imageSource.createPixelMap(opts); + } + + /** + * 创建图片源实例 + * @param src(联合类型: string、number、ArrayBuffer、resourceManager.RawFileDescriptor) + * path string 图片路径,当前仅支持应用沙箱路径。当前支持格式有:.jpg .png .gif .bmp .webp RAW SVG10+ .ico11+。 + * fd number 文件描述符fd。 + * buf ArrayBuffer 图像缓冲区数组。 + * rawfile resourceManager.RawFileDescriptor 图像资源文件的RawFileDescriptor。 + * options SourceOptions 图片属性,包括图片像素密度、像素格式和图片尺寸。 + * sourceDensity number ImageSource的密度。 + * sourcePixelFormat PixelMapFormat 图片像素格式。 + * sourceSize Size 图像像素大小。 + * @returns + */ + static createImageSource(src: string | number | ArrayBuffer | resourceManager.RawFileDescriptor, options?: image.SourceOptions): image.ImageSource { + if (typeof src === 'string') { + if (options) { + return image.createImageSource(src, options); + } else { + return image.createImageSource(src); + } + } else if (typeof src === 'number') { + if (options) { + return image.createImageSource(src, options); + } else { + return image.createImageSource(src); + } + } else if (src instanceof ArrayBuffer) { + if (options) { + return image.createImageSource(src, options); + } else { + return image.createImageSource(src); + } + } else { + if (options) { + return image.createImageSource(src, options); + } else { + return image.createImageSource(src); + } + } + } + + + /** + * 以增量的方式创建图片源实例 + * @param buf ArrayBuffer 增量数据 + * @param options SourceOptions 图片属性,包括图片像素密度、像素格式和图片尺寸。 + * sourceDensity number ImageSource的密度。 + * sourcePixelFormat PixelMapFormat 图片像素格式。 + * sourceSize Size 图像像素大小。 + * @returns + */ + static createIncrementalSource(buf: ArrayBuffer, options?: image.SourceOptions): image.ImageSource { + if (options) { + return image.CreateIncrementalSource(buf, options); + } else { + return image.CreateIncrementalSource(buf); + } + } + + + /** + * 图片压缩或重新打包,使用Promise形式返回结果。 + * @param source PixelMap-打包的PixelMap资源。 + * @param options 设置打包参数: + * format 目标格式。当前只支持"image/jpeg"、"image/webp"、"image/png"和"image/heif"12+(不同硬件设备支持情况不同)。 + * quality JPEG编码中设定输出图片质量的参数,取值范围为0-100。 + * bufferSize 接收编码数据的缓冲区大小,单位为Byte。默认为10MB。bufferSize需大于编码后图片大小。 + * @returns + */ + static packingFromPixelMap(source: image.PixelMap, options: image.PackingOption): Promise { + const imagePacker: image.ImagePacker = image.createImagePacker(); + return imagePacker.packing(source, options).finally(() => { + imagePacker.release(); //释放 + }); + } + + + /** + * 图片压缩或重新打包,使用Promise形式返回结果。 + * @param source ImageSource-打包的图片源。 + * @param options 设置打包参数: + * format 目标格式。当前只支持"image/jpeg"、"image/webp"、"image/png"和"image/heif"12+(不同硬件设备支持情况不同)。 + * quality JPEG编码中设定输出图片质量的参数,取值范围为0-100。 + * bufferSize 接收编码数据的缓冲区大小,单位为Byte。默认为10MB。bufferSize需大于编码后图片大小。 + * @returns + */ + static packingFromImageSource(source: image.ImageSource, options: image.PackingOption): Promise { + const imagePacker: image.ImagePacker = image.createImagePacker(); + return imagePacker.packing(source, options).finally(() => { + imagePacker.release(); //释放 + }); + } + + + /** + * 将PixelMap图片源编码后直接打包进文件。 + * @param source PixelMap-打包的PixelMap资源。 + * @param fd 文件描述符。 + * @param option 设置打包参数: + * format 目标格式。当前只支持"image/jpeg"、"image/webp"、"image/png"和"image/heif"12+(不同硬件设备支持情况不同)。 + * quality JPEG编码中设定输出图片质量的参数,取值范围为0-100。 + * bufferSize 接收编码数据的缓冲区大小,单位为Byte。默认为10MB。bufferSize需大于编码后图片大小。 + * @returns + */ + static packToFileFromPixelMap(source: image.PixelMap, fd: number, options: image.PackingOption): Promise { + const imagePacker: image.ImagePacker = image.createImagePacker(); + return imagePacker.packToFile(source, fd, options).finally(() => { + imagePacker.release(); //释放 + }); + } + + + /** + * 将ImageSource图片源编码后直接打包进文件。 + * @param source ImageSource-打包的图片源。 + * @param fd 文件描述符。 + * @param option 设置打包参数: + * format 目标格式。当前只支持"image/jpeg"、"image/webp"、"image/png"和"image/heif"12+(不同硬件设备支持情况不同)。 + * quality JPEG编码中设定输出图片质量的参数,取值范围为0-100。 + * bufferSize 接收编码数据的缓冲区大小,单位为Byte。默认为10MB。bufferSize需大于编码后图片大小。 + * @returns + */ + static packToFileFromImageSource(source: image.ImageSource, fd: number, options: image.PackingOption): Promise { + const imagePacker: image.ImagePacker = image.createImagePacker(); + return imagePacker.packToFile(source, fd, options).finally(() => { + imagePacker.release(); //释放 + }); + } + + /** + * 图片压缩 + * @param sourcePixelMap:原始待压缩图片的PixelMap对象 + * @param maxSize:指定图片的压缩目标大小,单位kb + * @param imageFormat:当前只支持"image/jpeg"、"image/webp"、"image/png"和"image/heif"12+(不同硬件设备支持情况不同)。 + * @returns compressedImageData:返回压缩后的图片数据 + */ + static async compressedImage(sourcePixelMap: image.PixelMap, maxImgSize: number, imageFormat: string = "image/jpeg"): Promise { + const imagePackerApi = image.createImagePacker(); //创建图像编码ImagePacker对象 + const IMAGE_QUALITY = 0; + const packOpts: image.PackingOption = { format: imageFormat, quality: IMAGE_QUALITY }; + //通过PixelMap进行编码。compressedImageData为打包获取到的图片文件流。 + let compressedImageData: ArrayBuffer = await imagePackerApi.packing(sourcePixelMap, packOpts); + const maxCompressedImageByte = maxImgSize * 1024; //压缩目标图像字节长度 + //图片压缩。先判断设置图片质量参数quality为0时,packing能压缩到的图片最小字节大小是否满足指定的图片压缩大小。如果满足,则使用packing方式二分查找最接近指定图片压缩目标大小的quality来压缩图片。如果不满足,则使用scale对图片先进行缩放,采用while循环每次递减0.4倍缩放图片,再用packing(图片质量参数quality设置0)获取压缩图片大小,最终查找到最接近指定图片压缩目标大小的缩放倍数的图片压缩数据。 + if (maxCompressedImageByte > compressedImageData.byteLength) { + //使用packing二分压缩获取图片文件流 + compressedImageData = await ImageUtil.packingImage(compressedImageData, sourcePixelMap, maxCompressedImageByte, IMAGE_QUALITY, imageFormat); + } else { + //使用scale对图片先进行缩放,采用while循环每次递减0.4倍缩放图片,再用packing(图片质量参数quality设置0)获取压缩图片大小,最终查找到最接近指定图片压缩目标大小的缩放倍数的图片压缩数据 + let imageScale = 1; + const REDUCE_SCALE = 0.4; + //判断压缩后的图片大小是否大于指定图片的压缩目标大小,如果大于,继续降低缩放倍数压缩。 + while (compressedImageData.byteLength > maxCompressedImageByte) { + if (imageScale > 0) { + //性能知识点: 由于scale会直接修改图片PixelMap数据,所以不适用二分查找scale缩放倍数。这里采用循环递减0.4倍缩放图片,来查找确定最适合的缩放倍数。如果对图片压缩质量要求不高,建议调高每次递减的缩放倍数reduceScale,减少循环,提升scale压缩性能。 + imageScale = imageScale - REDUCE_SCALE; + await sourcePixelMap.scale(imageScale, imageScale); + compressedImageData = await ImageUtil.packing(sourcePixelMap, IMAGE_QUALITY, imageFormat); + } else { + //imageScale缩放小于等于0时,没有意义,结束压缩。这里不考虑图片缩放倍数小于reduceScale的情况。 + break; + } + } + } + imagePackerApi.release(); + return compressedImageData; + } + + + /** + * packing压缩 + * @param sourcePixelMap:原始待压缩图片的PixelMap + * @param imageQuality:图片质量参数 + * @param imageFormat:当前只支持"image/jpeg"、"image/webp"、"image/png"和"image/heif"12+(不同硬件设备支持情况不同)。 + * @returns data:返回压缩后的图片数据 + */ + static async packing(sourcePixelMap: image.PixelMap, imageQuality: number, imageFormat: string = "image/jpeg"): Promise { + const imagePackerApi = image.createImagePacker(); + const packOpts: image.PackingOption = { format: imageFormat, quality: imageQuality }; + const data: ArrayBuffer = await imagePackerApi.packing(sourcePixelMap, packOpts); + imagePackerApi.release() + return data; + } + + + /** + * packing二分方式循环压缩 + * @param compressedImageData:图片压缩的ArrayBuffer + * @param sourcePixelMap:原始待压缩图片的PixelMap + * @param maxCompressedImageByte:压缩目标图像字节长度 + * @param imageQuality:图片质量参数 + * @param imageFormat:当前只支持"image/jpeg"、"image/webp"、"image/png"和"image/heif"12+(不同硬件设备支持情况不同)。 + * @returns compressedImageData:返回二分packing压缩后的图片数据 + */ + static async packingImage(compressedImageData: ArrayBuffer, sourcePixelMap: image.PixelMap, maxCompressedImageByte: number, + imageQuality: number, imageFormat: string = "image/jpeg"): Promise { + //图片质量参数范围为0-100,这里以10为最小二分单位创建用于packing二分图片质量参数的数组。 + const packingArray: number[] = []; + const DICHOTOMY_ACCURACY = 10; + //性能知识点: 如果对图片压缩质量要求不高,建议调高最小二分单位dichotomyAccuracy,减少循环,提升packing压缩性能。 + for (let i = 0; i <= 100; i += DICHOTOMY_ACCURACY) { + packingArray.push(i); + } + let left = 0; + let right = packingArray.length - 1; + //二分压缩图片 + while (left <= right) { + const mid = Math.floor((left + right) / 2); + imageQuality = packingArray[mid]; + //根据传入的图片质量参数进行packing压缩,返回压缩后的图片文件流数据。 + compressedImageData = await ImageUtil.packing(sourcePixelMap, imageQuality, imageFormat); + //判断查找一个尽可能接近但不超过压缩目标的压缩大小 + if (compressedImageData.byteLength <= maxCompressedImageByte) { + left = mid + 1; + if (mid === packingArray.length - 1) { + break; + } + //获取下一次二分的图片质量参数(mid+1)压缩的图片文件流数据 + compressedImageData = await ImageUtil.packing(sourcePixelMap, packingArray[mid + 1], imageFormat); + //判断用下一次图片质量参数(mid+1)压缩的图片大小是否大于指定图片的压缩目标大小。如果大于,说明当前图片质量参数(mid)压缩出来的图片大小最接近指定图片的压缩目标大小。传入当前图片质量参数mid,得到最终目标图片压缩数据。 + if (compressedImageData.byteLength > maxCompressedImageByte) { + compressedImageData = await ImageUtil.packing(sourcePixelMap, packingArray[mid], imageFormat); + break; + } + } else { + //目标值不在当前范围的右半部分,将搜索范围的右边界向左移动,以缩小搜索范围并继续在下一次迭代中查找左半部分。 + right = mid - 1; + } + } + return compressedImageData; + } +} \ No newline at end of file diff --git a/commons/basic/src/main/ets/utils/NotificationUtil.ets b/commons/basic/src/main/ets/utils/NotificationUtil.ets new file mode 100644 index 0000000..f024dc0 --- /dev/null +++ b/commons/basic/src/main/ets/utils/NotificationUtil.ets @@ -0,0 +1,549 @@ +import notificationManager from '@ohos.notificationManager'; +import common from '@ohos.app.ability.common'; +import wantAgent from '@ohos.app.ability.wantAgent'; +import { WantAgent } from '@ohos.wantAgent'; +import { + NotificationBasicOptions, + NotificationLongTextOptions, + NotificationMultiLineOptions, + NotificationOptions, + NotificationPictureOptions, + NotificationTemplateOptions +} from '../pushnotification/NotificationOptions'; +import { NotificationConfig } from '../pushnotification/NotificationConfig'; +import { image } from '@kit.ImageKit'; +import { ImageUtil } from './ImageUtil'; +import { AppUtil } from './AppUtil'; +import { systemDateTime } from '@kit.BasicServicesKit'; + + +export class NotificationUtil { + + private static defaultConfig: NotificationConfig = new NotificationConfig(); //默认配置 + + + /** + * 设置通知的默认统一配置 + * @param configs + */ + static setDefaultConfig(configs: (config: NotificationConfig) => void): void { + configs(NotificationUtil.defaultConfig); + } + + + /** + * 查询通知是否授权 + */ + static async isNotificationEnabled(): Promise { + return notificationManager.isNotificationEnabled(); //查询通知是否授权。 + } + + /** + * 查询通知是否授权 + */ + static isNotificationEnabledSync(): boolean { + return notificationManager.isNotificationEnabledSync(); //查询通知是否授权。 + } + + + /** + * 请求通知授权,第一次调用会弹窗让用户选择。 + * @returns + */ + static async authorizeNotification(callBack?: (grant: boolean) => void): Promise { + let isEnabled = await NotificationUtil.isNotificationEnabled(); //查询通知是否授权 + if (!isEnabled) { //未授权,拉起授权 + try { + await notificationManager.requestEnableNotification(AppUtil.getContext()); + callBack?.(true); + return true; + } catch (e) { + callBack?.(false); + return false; + } + } else { + callBack?.(true); + return true; + } + } + + + /** + * 查询模板是否存在,目前仅支持进度条模板。 + * @param templateName 模板名称。当前仅支持'downloadTemplate'。 + * @returns + */ + static async isSupportTemplate(templateName: string = 'downloadTemplate'): Promise { + return notificationManager.isSupportTemplate(templateName); + } + + + /** + * 查询设备是否支持分布式通知 + * @returns + */ + static async isDistributedEnabled(): Promise { + return notificationManager.isDistributedEnabled() + } + + + /** + * 发布普通文本通知 + * @param options 通知实体 + * @returns + */ + static async publishBasic(options: NotificationBasicOptions): Promise { + const notificationId: number = options.id ?? NotificationUtil.generateNotificationId(); //通知ID。 + const basicContent: notificationManager.NotificationBasicContent = { + title: options.title, + text: options.text + } + if (options.additionalText || NotificationUtil.defaultConfig.additionalText) { + basicContent.additionalText = options.additionalText ?? NotificationUtil.defaultConfig.additionalText; + } + if (options.lockscreenPicture || NotificationUtil.defaultConfig.lockscreenPicture) { + basicContent.lockscreenPicture = options.lockscreenPicture ?? NotificationUtil.defaultConfig.lockscreenPicture; + } + let notificationContent: notificationManager.NotificationContent = { + notificationContentType: notificationManager.ContentType.NOTIFICATION_CONTENT_BASIC_TEXT, + normal: basicContent + } + //通知Request对象 + const notificationRequest = NotificationUtil.getNotificationRequest(notificationId, options, notificationContent); + await notificationManager.publish(notificationRequest); //发送通知 + return notificationId + } + + + /** + * 发布长文本通知 + * @param options 通知实体 + * @returns + */ + static async publishLongText(options: NotificationLongTextOptions): Promise { + const notificationId: number = options.id ?? NotificationUtil.generateNotificationId(); //通知ID。 + const longTextContent: notificationManager.NotificationLongTextContent = { + title: options.title, + text: options.text, + briefText: options.briefText, + longText: options.longText, + expandedTitle: options.expandedTitle + } + if (options.additionalText || NotificationUtil.defaultConfig.additionalText) { + longTextContent.additionalText = options.additionalText ?? NotificationUtil.defaultConfig.additionalText; + } + if (options.lockscreenPicture || NotificationUtil.defaultConfig.lockscreenPicture) { + longTextContent.lockscreenPicture = options.lockscreenPicture ?? NotificationUtil.defaultConfig.lockscreenPicture; + } + let notificationContent: notificationManager.NotificationContent = { + notificationContentType: notificationManager.ContentType.NOTIFICATION_CONTENT_LONG_TEXT, + longText: longTextContent + } + //通知Request对象 + const notificationRequest = NotificationUtil.getNotificationRequest(notificationId, options, notificationContent); + await notificationManager.publish(notificationRequest); //发送通知 + return notificationId; + } + + + /** + * 发布多文本通知 + * @param options 通知实体 + * @returns + */ + static async publishMultiLine(options: NotificationMultiLineOptions): Promise { + const notificationId: number = options.id ?? NotificationUtil.generateNotificationId(); //通知ID。 + const multiLineContent: notificationManager.NotificationMultiLineContent = { + title: options.title, + text: options.text, + briefText: options.briefText, + longTitle: options.longTitle, + lines: options.lines + } + if (options.additionalText || NotificationUtil.defaultConfig.additionalText) { + multiLineContent.additionalText = options.additionalText ?? NotificationUtil.defaultConfig.additionalText; + } + if (options.lockscreenPicture || NotificationUtil.defaultConfig.lockscreenPicture) { + multiLineContent.lockscreenPicture = options.lockscreenPicture ?? NotificationUtil.defaultConfig.lockscreenPicture; + } + let notificationContent: notificationManager.NotificationContent = { + notificationContentType: notificationManager.ContentType.NOTIFICATION_CONTENT_MULTILINE, + multiLine: multiLineContent + } + //通知Request对象 + const notificationRequest = NotificationUtil.getNotificationRequest(notificationId, options, notificationContent); + await notificationManager.publish(notificationRequest); //发送通知 + return notificationId; + } + + + /** + * 发布带有图片的通知 + * @param options 通知实体 + * @returns + */ + static async publishPicture(options: NotificationPictureOptions): Promise { + const notificationId: number = options.id ?? NotificationUtil.generateNotificationId(); //通知ID。 + const pictureContent: notificationManager.NotificationPictureContent = { + title: options.title, + text: options.text, + briefText: options.briefText, + expandedTitle: options.expandedTitle, + picture: options.picture + } + if (options.additionalText || NotificationUtil.defaultConfig.additionalText) { + pictureContent.additionalText = options.additionalText ?? NotificationUtil.defaultConfig.additionalText; + } + if (options.lockscreenPicture || NotificationUtil.defaultConfig.lockscreenPicture) { + pictureContent.lockscreenPicture = options.lockscreenPicture ?? NotificationUtil.defaultConfig.lockscreenPicture; + } + let notificationContent: notificationManager.NotificationContent = { + notificationContentType: notificationManager.ContentType.NOTIFICATION_CONTENT_PICTURE, + picture: pictureContent + } + //通知Request对象 + const notificationRequest = NotificationUtil.getNotificationRequest(notificationId, options, notificationContent); + await notificationManager.publish(notificationRequest); //发送通知 + return notificationId; + } + + + /** + * 发布模板的通知 + * @param options + */ + static async publishTemplate(options: NotificationTemplateOptions): Promise { + const notificationId: number = options.id ?? NotificationUtil.generateNotificationId(); //通知ID。 + const basicContent: notificationManager.NotificationBasicContent = { + title: options.title, + text: options.text + } + if (options.additionalText || NotificationUtil.defaultConfig.additionalText) { + basicContent.additionalText = options.additionalText ?? NotificationUtil.defaultConfig.additionalText; + } + if (options.lockscreenPicture || NotificationUtil.defaultConfig.lockscreenPicture) { + basicContent.lockscreenPicture = options.lockscreenPicture ?? NotificationUtil.defaultConfig.lockscreenPicture; + } + let notificationContent: notificationManager.NotificationContent = { + notificationContentType: notificationManager.ContentType.NOTIFICATION_CONTENT_BASIC_TEXT, + normal: basicContent + } + //模板对象 + let template: notificationManager.NotificationTemplate = { + name: 'downloadTemplate', + data: { title: options.title, fileName: options.fileName, progressValue: options.progressValue } + } + //通知Request对象 + const notificationRequest = NotificationUtil.getNotificationRequest(notificationId, options, notificationContent, template); + await notificationManager.publish(notificationRequest); //发送通知 + return notificationId; + } + + + /** + * 取消通知,通过通知ID和通知标签取消已发布的通知,若label为空表示取消与指定通知ID相匹配的已发布通知。 + * @param id 通知id + * @param label 通知标签,默认为空。 + * @returns + */ + static async cancel(id: number, label?: string): Promise { + return notificationManager.cancel(id, label); + } + + /** + * 取消本应用指定组下的通知 + * @param groupName 通知组名称,此名称需要在发布通知时通过NotificationRequest对象指定。 + * @returns + */ + static async cancelGroup(groupName: string): Promise { + return notificationManager.cancelGroup(groupName); + } + + /** + * 取消所有通知,取消当前应用所有已发布的通知。 + * @returns + */ + static async cancelAll(): Promise { + return notificationManager.cancelAll() + } + + + /** + * 设置桌面角标个数,在应用的桌面图标上呈现。 + */ + static async setBadge(badgeNumber: number): Promise { + return notificationManager.setBadgeNumber(badgeNumber); + } + + /** + * 清空桌面角标,在应用的桌面图标上呈现。 + */ + static async clearBadge(): Promise { + return notificationManager.setBadgeNumber(0); + } + + + /** + * 获取当前应用未删除的通知数量。 + */ + static async getActiveNotificationCount(): Promise { + return notificationManager.getActiveNotificationCount(); + } + + + /** + * 设置桌面角标数量,来自于通知数量。 + */ + static async setBadgeFromNotificationCount(): Promise { + let count = await NotificationUtil.getActiveNotificationCount(); + return notificationManager.setBadgeNumber(count); //设置角标 + } + + + /** + * 获取当前应用未删除的通知列表。 + */ + static async getActiveNotifications(): Promise> { + return notificationManager.getActiveNotifications(); + } + + + /** + * 创建指定类型的通知渠道。 + * @param type 要创建的通知渠道的类型 + */ + static async addSlot(type: notificationManager.SlotType) { + return notificationManager.addSlot(type); + } + + /** + * 获取一个指定类型的通知渠道。 + * @param type 要创建的通知渠道的类型 + */ + static async getSlot(type: notificationManager.SlotType): Promise { + return notificationManager.getSlot(type); + } + + /** + * 获取此应用程序的所有通知渠道 + * @returns + */ + static async getSlots(): Promise> { + return notificationManager.getSlots(); + } + + + /** + * 删除此应用程序指定类型的通知渠道 + * @param type 通知渠道类型,例如社交通信、服务提醒、内容咨询等类型。 + * @returns + */ + static async removeSlot(type: notificationManager.SlotType) { + return notificationManager.removeSlot(type); + } + + /** + * 删除此应用程序所有通知渠道 + * @returns + */ + static async removeAllSlots() { + return notificationManager.removeAllSlots(); + } + + + /** + * 生成通知id(用时间戳当id) + * @returns + */ + static generateNotificationId(): number { + return systemDateTime.getTime(true); + } + + + /** + * 获取压缩通知的图片(图像像素的总字节数不能超过2MB) + * @param pixelMap:原始待压缩图片的PixelMap对象 + * @returns 返回压缩后的图片数据 + */ + static async getCompressedPicture(src: Resource | image.PixelMap): Promise { + if (typeof (src as Resource).bundleName == 'string') { + // src = await ImageUtil.getPixelMapFromMedia((src as Resource)); + } + let pixelMap = src as image.PixelMap; + let pictureMaxSize = 2 * 1024 * 1024; //通知的图片内容(图像像素的总字节数不能超过2MB)。 + let pixelBytes = pixelMap.getPixelBytesNumber(); //图像像素的总字节数 + while (pixelBytes > pictureMaxSize) { + await pixelMap.scale(0.7, 0.7, image.AntiAliasingLevel.LOW); //缩放 + pixelBytes = pixelMap.getPixelBytesNumber(); //图像像素的总字节数 + } + return pixelMap; + } + + + /** + * 获取压缩通知图标(图标像素的总字节数不超过192KB) + * @param pixelMap:原始待压缩图片的PixelMap对象 + * @returns + */ + static async getCompressedIcon(src: Resource | image.PixelMap): Promise { + if (typeof (src as Resource).bundleName == 'string') { + // src = await ImageUtil.getPixelMapFromMedia((src as Resource)); + } + let pixelMap = src as image.PixelMap; + let pictureMaxSize = 192 * 1024; //通知图标(图标像素的总字节数不超过192KB)。 + let pixelBytes = pixelMap.getPixelBytesNumber(); //图像像素的总字节数 + while (pixelBytes > pictureMaxSize) { + await pixelMap.scale(0.7, 0.7, image.AntiAliasingLevel.LOW); //缩放 + pixelBytes = pixelMap.getPixelBytesNumber(); //图像像素的总字节数 + } + return pixelMap; + } + + + /** + * 创建一个可拉起Ability的Want + * @returns + */ + static async getDefaultWantAgent(): Promise { + const context = getContext() as common.UIAbilityContext; //获取当前上下文对象 + const wantAgentInfo: wantAgent.WantAgentInfo = { + wants: [ + { + deviceId: '', + bundleName: context.abilityInfo.bundleName, + moduleName: context.abilityInfo.moduleName, + abilityName: context.abilityInfo.name, + action: 'action_notice', + entities: [], + uri: '', + } + ], + actionType: wantAgent.OperationType.START_ABILITY | wantAgent.OperationType.SEND_COMMON_EVENT, + requestCode: 0, + actionFlags: [wantAgent.WantAgentFlags.CONSTANT_FLAG] + }; + return wantAgent.getWantAgent(wantAgentInfo); + } + + + /** + * 构建NotificationRequest对象 + * @param notificationId 通知ID + * @param options 描述通知的请求参数 + * @param content 通知内容 + * @param template 模板 + * @returns + */ + private static getNotificationRequest(notificationId: number, options: NotificationOptions, + content: notificationManager.NotificationContent, template?: notificationManager.NotificationTemplate): notificationManager.NotificationRequest { + const request: notificationManager.NotificationRequest = { content: content }; + request.id = notificationId; //通知ID。 + if (template) { + request.template = template; //模板 + } + if (options.notificationSlotType || NotificationUtil.defaultConfig.notificationSlotType) { + request.notificationSlotType = options.notificationSlotType ?? NotificationUtil.defaultConfig.notificationSlotType; //通道类型。 + } + if (options.deliveryTime || NotificationUtil.defaultConfig.deliveryTime) { + request.deliveryTime = options.deliveryTime ?? NotificationUtil.defaultConfig.deliveryTime; //通知发送时间。 + } + request.showDeliveryTime = options.showDeliveryTime ?? NotificationUtil.defaultConfig.showDeliveryTime; //是否显示分发时间。 + request.tapDismissed = options.tapDismissed ?? NotificationUtil.defaultConfig.tapDismissed; //通知是否自动清除。 + if (options.autoDeletedTime || NotificationUtil.defaultConfig.autoDeletedTime) { + request.autoDeletedTime = options.autoDeletedTime ?? NotificationUtil.defaultConfig.autoDeletedTime; //自动清除的时间。 + } + request.isAlertOnce = options.isAlertOnce ?? NotificationUtil.defaultConfig.isAlertOnce; //设置是否仅有一次此通知提醒。 + request.isStopwatch = options.isStopwatch ?? NotificationUtil.defaultConfig.isStopwatch; //是否显示已用时间。 + request.isCountDown = options.isCountDown ?? NotificationUtil.defaultConfig.isCountDown; //是否显示倒计时时间。 + request.isFloatingIcon = options.isFloatingIcon ?? NotificationUtil.defaultConfig.isFloatingIcon; //是否显示状态栏图标。 + + if (options.label || NotificationUtil.defaultConfig.label) { + request.label = options.label ?? NotificationUtil.defaultConfig.label; //通知标签。 + } + if (options.actionButtons || NotificationUtil.defaultConfig.actionButtons) { + request.actionButtons = options.actionButtons ?? NotificationUtil.defaultConfig.actionButtons; //通知按钮,最多三个按钮。 + } + if (options.smallIcon || NotificationUtil.defaultConfig.smallIcon) { + request.smallIcon = options.smallIcon ?? NotificationUtil.defaultConfig.smallIcon; //通知小图标。可选字段,图像像素的总字节数不超过100KB。实际显示效果依赖于设备能力和通知中心UI样式。 + } + if (options.largeIcon || NotificationUtil.defaultConfig.largeIcon) { + request.largeIcon = options.largeIcon ?? NotificationUtil.defaultConfig.largeIcon; //通知大图标。可选字段,图像像素的总字节数不超过100KB。实际显示效果依赖于设备能力和通知中心UI样式。 + } + if (options.groupName || NotificationUtil.defaultConfig.groupName) { + request.groupName = options.groupName ?? NotificationUtil.defaultConfig.groupName; //组通知名称。 + } + if (options.template || NotificationUtil.defaultConfig.template) { + request.template = options.template ?? NotificationUtil.defaultConfig.template; //通知模板。 + } + if (options.distributedOption || NotificationUtil.defaultConfig.distributedOption) { + request.distributedOption = options.distributedOption ?? NotificationUtil.defaultConfig.distributedOption; //分布式通知的选项。 + } + if (options.appMessageId || NotificationUtil.defaultConfig.appMessageId) { + request.appMessageId = options.appMessageId ?? NotificationUtil.defaultConfig.appMessageId; //应用发送通知携带的唯一标识字段, 用于通知去重。如果同一应用通过本地和云端等不同途径发布携带相同appMessageId的通知,设备只展示一条消息,之后收到的重复通知会被静默去重,不展示、不提醒。去重标识仅在通知发布的24小时内有效,超过24小时或者设备重启失效。。 + } + if (options.sound || NotificationUtil.defaultConfig.sound) { + request.sound = options.sound ?? NotificationUtil.defaultConfig.sound; //应用通知自定义铃声,需要通过Push云侧获取自定义发送铃声权限后,该字段才会生效。 + } + if (options.badgeNumber || NotificationUtil.defaultConfig.badgeNumber) { + request.badgeNumber = options.badgeNumber ?? NotificationUtil.defaultConfig.badgeNumber; //应用程序图标上显示的通知数。 + } + if (options.extraInfo || NotificationUtil.defaultConfig.extraInfo) { + request.extraInfo = options.extraInfo ?? NotificationUtil.defaultConfig.extraInfo; //扩展参数。 + } + if (options.wantAgent || NotificationUtil.defaultConfig.wantAgent) { + request.wantAgent = options.wantAgent ?? NotificationUtil.defaultConfig.wantAgent; //WantAgent封装了应用的行为意图,点击通知时触发该行为。 + } + if (options.removalWantAgent || NotificationUtil.defaultConfig.removalWantAgent) { + request.removalWantAgent = options.removalWantAgent ?? NotificationUtil.defaultConfig.removalWantAgent; //当移除通知时,通知将被重定向到的WantAgent实例。 + } + return request; + } + + + /** + * 获取错误msg + * @param code + * @param defaultMsg + */ + public static getErrorMsg(code: number, defaultMsg: string) { + if (201 == code) { + return '权限校验失败!' + } else if (202 == code) { + return '系统API权限校验失败!' + } else if (401 == code) { + return '参数检查失败!' + } else if (801 == code) { + return '该设备不支持此API!' + } else if (1600001 == code) { + return '应用通知,内部错误!' + } else if (1600002 == code) { + return '序列化或反序列化错误!' + } else if (1600003 == code) { + return '应用连接通知服务失败!' + } else if (1600004 == code) { + return '请开启应用通知开关!' + } else if (1600005 == code) { + return '通知渠道关闭!' + } else if (1600006 == code) { + return '通知删除失败!' + } else if (1600007 == code) { + return '通知不存在!' + } else if (1600008 == code) { + return '用户不存在!' + } else if (1600009 == code) { + return '通知发布频度超过限制!' + } else if (1600010 == code) { + return '分布式操作失败!' + } else if (1600011 == code) { + return '读模板配置文件错误!' + } else if (1600012 == code) { + return '内存空间不够!' + } else if (17700001 == code) { + return '包名不存在!' + } else { + return defaultMsg + } + } + + +} diff --git a/features/Home/src/main/ets/components/HomeIconComp.ets b/features/Home/src/main/ets/components/HomeIconComp.ets index 9ed60ca..519f48c 100644 --- a/features/Home/src/main/ets/components/HomeIconComp.ets +++ b/features/Home/src/main/ets/components/HomeIconComp.ets @@ -17,6 +17,8 @@ export struct HomeIconComp { @State patientName: string = '我的患者'; @State videoIcon: string = ''; @State videoName: string = '肝胆视频'; + @State zixunIcon: string = ''; + @State consultation: string = '公益咨询'; aboutToAppear(): void { for (const icons of this.iconList) { @@ -24,6 +26,8 @@ export struct HomeIconComp { this.patientIcon = icons.img; } else if (icons.name === '肝胆视频') { this.videoIcon = icons.img; + } else if (icons.name == '公益咨询') { + this.zixunIcon = icons.img } } @@ -38,7 +42,7 @@ export struct HomeIconComp { build() { Row() { Grid() { - ForEach(this.iconList, (item: iconsModel) => {//[{ 'img': this.patientIcon, 'name': this.patientName },{ 'img': this.videoIcon, 'name': this.videoName}] + ForEach([{ 'img': this.patientIcon, 'name': this.patientName },{ 'img': this.videoIcon, 'name': this.videoName},{'img':this.zixunIcon,'name':this.consultation}], (item: iconsModel) => {//this.iconList GridItem(){ Stack() { Column() { @@ -56,24 +60,15 @@ export struct HomeIconComp { }.width('25%').alignContent(Alignment.TopEnd) }.margin({top:20,bottom:20}) .onClick(async ()=>{ - const patients = await patientDbManager.getAllPatients(); - console.info(`添加了 ${patients.length} 个患者`); - promptAction.showToast({message:`添加了 ${patients.length} 个患者`}) if(item.name=='公益咨询') { router.pushUrl({ url: 'pages/Netease/PublicConsultationPage' }) - } - else if(item.name=='我的患者') { + } else if(item.name=='我的患者') { router.pushUrl({url:'pages/Netease/imTabPage'}) - } - - - try { - const pushToken = pushService.getToken(); - console.info("PushService", `Get push token successfully: ${pushToken}`) - } catch (err) { - // fail - let e: BusinessError = err as BusinessError; - console.error("PushService", 'Get push token catch error: ' + e.code + ' ' + e.message); + } else if (item.name == '肝胆视频') { + router.pushUrl({ + url:'pages/VideoPage/VideoGandanPage', + params:{"page":"首页"} + }) } }) diff --git a/features/Home/src/main/ets/components/HomeReplayVideoComp.ets b/features/Home/src/main/ets/components/HomeReplayVideoComp.ets index 8c0e108..f6de424 100644 --- a/features/Home/src/main/ets/components/HomeReplayVideoComp.ets +++ b/features/Home/src/main/ets/components/HomeReplayVideoComp.ets @@ -28,6 +28,7 @@ export struct HomeReplayVideoComp { .onClick(()=>{ router.pushUrl({ url:'pages/VideoPage/VideoGandanPage', + params:{"page":"首页"} }) }) }.height(50) diff --git a/features/Home/src/main/ets/pages/VideoGandan.ets b/features/Home/src/main/ets/pages/VideoGandan.ets index cfecf40..b8d2361 100644 --- a/features/Home/src/main/ets/pages/VideoGandan.ets +++ b/features/Home/src/main/ets/pages/VideoGandan.ets @@ -7,10 +7,20 @@ import { getDisplayWindowWidth } from 'media-player-common' @Component export struct VideoGandan { + @State params:Record = router.getParams() as Record + @State isShowNaviLeft:boolean = false + + aboutToAppear(): void { + if (this.params) { + if (this.params["page"] == '首页') { + this.isShowNaviLeft = true + } + } + } build() { Column() { - HdNav({ title: '肝胆视频', showRightIcon: true, showLeftIcon: false,showRightText:false,rightIcon:$r('app.media.selected_hospital_ws'),rightItemAction:()=>{ + HdNav({ title: '肝胆视频', showRightIcon: true, showLeftIcon: this.isShowNaviLeft,showRightText:false,rightIcon:$r('app.media.selected_hospital_ws'),rightItemAction:()=>{ router.pushUrl({ url:'pages/SearchPage/VideoSearchPage', params:{'pageName':'视频'} diff --git a/features/mypage/src/main/ets/view/MyPageSectionItem.ets b/features/mypage/src/main/ets/view/MyPageSectionItem.ets index be76a24..dc0261c 100644 --- a/features/mypage/src/main/ets/view/MyPageSectionItem.ets +++ b/features/mypage/src/main/ets/view/MyPageSectionItem.ets @@ -11,7 +11,9 @@ export struct MyPageSectionItem { .width(24).height(24) .objectFit(ImageFit.Auto) Text(this.sectionItem.title) - .fontSize(14) + .maxFontSize(14) + .minFontSize(6) + .maxLines(1) .fontColor('#333333') .margin({ top: 10 }) }.width('100%') diff --git a/features/netease/src/main/ets/view/PatientListComp.ets b/features/netease/src/main/ets/view/PatientListComp.ets index 2f85475..36237a8 100644 --- a/features/netease/src/main/ets/view/PatientListComp.ets +++ b/features/netease/src/main/ets/view/PatientListComp.ets @@ -241,9 +241,7 @@ export struct PatientListComp { .width('100%') .align(Alignment.Start) .onClick(() => { - router.pushUrl({ - url:'pages/PatientsPage/PatientPages' - }) + HMRouterMgr.push({pageUrl:'PatientApplyPage',param:"我的患者"}) }) ListItem() { Row() { @@ -291,9 +289,7 @@ export struct PatientListComp { .width('100%') .align(Alignment.Start) .onClick(() => { - router.pushUrl({ - url:'pages/PatientsPage/PatientsGroupPage' - }) + HMRouterMgr.push({pageUrl:'PatientsGroup',param:"我的患者"}) }) ForEach(this.regionDataGroupsList, (regionDataGroups: Groups, index) => { diff --git a/features/patient/src/main/ets/components/AddAndEditRecordComp.ets b/features/patient/src/main/ets/components/AddAndEditRecordComp.ets index facc43c..9bf96eb 100644 --- a/features/patient/src/main/ets/components/AddAndEditRecordComp.ets +++ b/features/patient/src/main/ets/components/AddAndEditRecordComp.ets @@ -258,7 +258,7 @@ export struct AddAndEditRecordComp { } this.photos.push(...selectedUris); this.maxSelectNumber = 8 - this.photos.length - ChangeUtil.convertUrisOrUrlsToBase64(selectedUris).then(base64Array => { + ChangeUtil.convertUrisOrUrlsToBase64(this.photos).then(base64Array => { console.info('转换结果:', base64Array+'转换个数:'+base64Array.length) this.base64Array = base64Array }).catch((err:BusinessError) => { diff --git a/features/patient/src/main/ets/components/PatientApplyPage.ets b/features/patient/src/main/ets/components/PatientApplyPage.ets index a6cc106..215ae7a 100644 --- a/features/patient/src/main/ets/components/PatientApplyPage.ets +++ b/features/patient/src/main/ets/components/PatientApplyPage.ets @@ -8,6 +8,7 @@ import { BusinessError } from '@kit.BasicServicesKit'; import { promptAction, router } from '@kit.ArkUI' import { patientDbManager, PatientData } from '@itcast/basic'; import { PullToRefreshLayout, RefreshController } from 'refreshlib' +import { HMRouter, HMRouterMgr } from '@hadss/hmrouter'; interface extraData { expertUuid: string, @@ -16,9 +17,11 @@ interface extraData { status: string } +@HMRouter({ pageUrl: 'PatientApplyPage' }) @Component export struct PatientApplyPage { public controller:RefreshController = new RefreshController(); + private params: ESObject = HMRouterMgr.getCurrentParam() scroller = new Scroller(); @State applyArray:applyListModel[] = []; @State historyArray:historyModel[] = []; @@ -137,7 +140,10 @@ export struct PatientApplyPage { build() { Column(){ - HdNav({ title: '新的患者', showRightIcon: false, hasBorder: true }) + HdNav({ title: '新的患者', showRightIcon: false, hasBorder: true, isLeftAction:this.params?true:false, + leftItemAction:()=>{ + HMRouterMgr.pop() + }, }) PullToRefreshLayout({ scroller:this.scroller, viewKey:"ListPage", diff --git a/features/patient/src/main/ets/components/PatientCommonSettingComp.ets b/features/patient/src/main/ets/components/PatientCommonSettingComp.ets index fe62224..0c1b8c2 100644 --- a/features/patient/src/main/ets/components/PatientCommonSettingComp.ets +++ b/features/patient/src/main/ets/components/PatientCommonSettingComp.ets @@ -146,6 +146,7 @@ export struct PatientCommonSettingComp { Column() { Row() { Image(BasicConstant.urlImage + this.itemList.photo) + .alt($r('app.media.userPhoto_default')) .width(60) .height(60) .borderRadius(6) diff --git a/features/patient/src/main/ets/components/PatientsGroup.ets b/features/patient/src/main/ets/components/PatientsGroup.ets index 9a0b4a2..f940e9f 100644 --- a/features/patient/src/main/ets/components/PatientsGroup.ets +++ b/features/patient/src/main/ets/components/PatientsGroup.ets @@ -5,8 +5,9 @@ import { BasicConstant,hdHttp, HdResponse ,logger} from '@itcast/basic/Index' import { BusinessError } from '@kit.BasicServicesKit'; import HashMap from '@ohos.util.HashMap'; import { groupRequest,groupRequestCall,groupModel,patientListModel } from '../models/PatientsGroupModel' -import { HMRouterMgr, HMRouterPathInfo, HMRouterPathCallback } from "@hadss/hmrouter" +import { HMRouterMgr, HMRouterPathInfo, HMRouterPathCallback, HMRouter } from "@hadss/hmrouter" +@HMRouter({ pageUrl: 'PatientsGroup' }) @Component export struct PatientsGroup { @State groupSort: string = '分组排序' @@ -20,7 +21,8 @@ export struct PatientsGroup { @State isMaiLanHidden:boolean = false;//麦兰项目是否显示 @State group_sort:string = '0' @State list_sort:string = '0' - @State groupListArray: groupModel[] = []; + @State groupListArray: groupModel[] = [] + private params: ESObject = HMRouterMgr.getCurrentParam() dialog: CustomDialogController = new CustomDialogController({ builder: HdLoadingDialog({ message: '加载中...' }), @@ -88,6 +90,10 @@ export struct PatientsGroup { hasBorder: true, rightText: '新建', showRightText: true, + isLeftAction:this.params?true:false, + leftItemAction:()=>{ + HMRouterMgr.pop() + }, rightItemAction: () => { HMRouterMgr.push({pageUrl:'BuildOrEditGroupPage',param:{"title":"新建分组"}}) // router.pushUrl({ diff --git a/products/expert/src/main/ets/entryability/EntryAbility.ets b/products/expert/src/main/ets/entryability/EntryAbility.ets index 41baae2..583f81d 100644 --- a/products/expert/src/main/ets/entryability/EntryAbility.ets +++ b/products/expert/src/main/ets/entryability/EntryAbility.ets @@ -7,7 +7,6 @@ import contextConstant from '@ohos.app.ability.contextConstant'; import { patientDbManager } from '@itcast/basic'; import { HMRouterMgr } from '@hadss/hmrouter' import { BusinessError } from '@kit.BasicServicesKit' -import pushService from '@hms.core.push.pushService' const DOMAIN = 0x0000;