diff --git a/README.md b/README.md index 0beb30b14daba1a6b081f8969006b60a49d91534..37e3d0c991b15051d7704842df42dda7e1bc88dd 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # 锁屏沉浸实况窗 -## 简介 +## 项目简介 “Live View Kit”基于HarmonyOS实现了即时配送等多个场景下实况窗的创建、更新和结束功能。基于不同的场景,皆提供了定制的状态节点,帮助用户聚焦关键信息,提升用户体验。 锁屏沉浸实况窗则是应用进一步展示实时活动状态的通道,系统通过实况窗服务(Live View Kit),将重要信息展示在锁屏界面,让用户一览无余,无需进入应用就能获取活动最新状态,适用于实时性要求高、需要用户及时了解状态的场景。 @@ -61,7 +61,7 @@ 1. 若您需要使用本实例,请替换AppScope/app.json5文件内的bundleName为您的包名。 - ![img.png](screenshots/img.png) + ![img.png](screenshots/img.png) 2. 若您自己的包名未申请实况窗与沉浸实况窗权益,请参考[Live View Kit(实况窗服务)开发准备](https://developer.huawei.com/consumer/cn/doc/harmonyos-guides/liveview-preparations),先开通推送服务权益与实况窗权益。 diff --git a/entry/src/main/ets/liveview/LockScreenPage.ets b/entry/src/main/ets/liveview/LockScreenPage.ets index 74bae7d2c6dfcf807ac0596b77838298a29aaac7..c2f52155181b4863f1301c441c081de54d8d6d96 100644 --- a/entry/src/main/ets/liveview/LockScreenPage.ets +++ b/entry/src/main/ets/liveview/LockScreenPage.ets @@ -17,6 +17,7 @@ import { Constants } from '../constant/Constant'; import { LaneData } from '../model/RouteDataModel'; import { BreakpointType } from '../utils/BreakpointUtil'; import { RoadView } from '../view/RoadView'; +import json from '@ohos.util.json'; @Entry struct Index { @@ -26,20 +27,33 @@ struct Index { build() { Stack({ alignContent: Alignment.Top }) { - RoadView({ laneNum: this.laneData?.laneNum }) + Image(this.verticalBreakpoint === Constants.BREAK_POINT_MD ? $r('app.media.ic_lock') : $r('app.media.ic_lock_md')) + .width('100%') + .height('100%') - Image($rawfile('traffic_light.svg')) - .objectFit(ImageFit.Contain) - .width(80) - .position({ - // Layout based on vertical breakpoint. - right: new BreakpointType(25, 25, 75).getValue(this.verticalBreakpoint), - top: 25 - }) + Row() { + Stack() { + Image($r('app.media.ic_light')) + .width(this.verticalBreakpoint === Constants.BREAK_POINT_MD ? 73.5 : 106) + .height(this.verticalBreakpoint === Constants.BREAK_POINT_MD ? 36 : 52) + Text(JSON.stringify(this.laneData?.lightTime ?? 90)) + .fontColor($r('sys.color.white')) + .fontSize(this.verticalBreakpoint === Constants.BREAK_POINT_MD ? 24 : 30) + .margin({ right: this.verticalBreakpoint === Constants.BREAK_POINT_MD ? 8 : 16 }) + } + .alignContent(Alignment.End) + } + .width(this.verticalBreakpoint === Constants.BREAK_POINT_MD ? 73.5 : 106) + .height(this.verticalBreakpoint === Constants.BREAK_POINT_MD ? 36 : 52) + .position({ + // Layout based on vertical breakpoint. + right: 20, + top: this.verticalBreakpoint === Constants.BREAK_POINT_MD ? 32 : 25 + }) } .width('100%') .height('100%') + .alignContent(Alignment.Center) } - // [End layoutBasedOnVerticalBreakpoint] } \ No newline at end of file diff --git a/entry/src/main/ets/model/PreferenceModel.ets b/entry/src/main/ets/model/PreferenceModel.ets new file mode 100644 index 0000000000000000000000000000000000000000..6d34a1c89a73fa6d10c81c62ed2abaf1a0a4a20f --- /dev/null +++ b/entry/src/main/ets/model/PreferenceModel.ets @@ -0,0 +1,69 @@ +/* + * Copyright (c) Huawei Technologies Co., Ltd. 2024-2024. All rights reserved. + */ + +import { preferences } from '@kit.ArkData' +import { hilog } from '@kit.PerformanceAnalysisKit'; +import { BusinessError } from '@kit.BasicServicesKit'; + +class PreferenceModel { + private preference?: preferences.Preferences; + + /** + * Read the specified Preferences persistence file and load the data into the Preferences instance. + */ + getPreferencesFromStorage(context: Context): preferences.Preferences | undefined { + if (!this.preference) { + let options: preferences.Options = { name: 'LiveViewLockScreen' }; + try { + this.preference = preferences.getPreferencesSync(context, options); + return this.preference; + } catch (error) { + hilog.error(0x0000, 'UserAuth', `replaceUrl error ${JSON.stringify(error)}`); + } + } + return this.preference; + } + + /** + * Save the data to the Preferences. + * + * @param key Key. + * @param Data to be saved. + */ + putPreference(context: Context, key: string, value: preferences.ValueType) { + let preference = this.getPreferencesFromStorage(context); + if (!key || !value || !preference) { + return; + } + try { + preference.putSync(key, value); + } catch (error) { + hilog.error(0x0000, 'UserAuth', `putSync error ${JSON.stringify(error)}`); + } + preference.flush().catch((error: BusinessError) => { + hilog.error(0x0000, 'UserAuth', `flush error ${error.code}`); + }); + } + + /** + * Get preference data. + * + * @param key Key. + * @param value Default value. + */ + getPreference(context: Context, key: string, value: preferences.ValueType): preferences.ValueType { + let preference = this.getPreferencesFromStorage(context); + if (!preference) { + return ''; + } + try { + return preference.getSync(key, value); + } catch (error) { + hilog.error(0x0000, 'UserAuth', `getSync error ${JSON.stringify(error)}`); + } + return ''; + } +} + +export default new PreferenceModel(); \ No newline at end of file diff --git a/entry/src/main/ets/model/RouteDataModel.ets b/entry/src/main/ets/model/RouteDataModel.ets index cb4de3210e6d8ae7155db5ba9651f4106612fc79..2ef0800fed8d83fa300fe03173a7b6b4ec3d9c94 100644 --- a/entry/src/main/ets/model/RouteDataModel.ets +++ b/entry/src/main/ets/model/RouteDataModel.ets @@ -15,6 +15,7 @@ export interface LaneData { laneNum: number, + lightTime: number } export interface RouteData { diff --git a/entry/src/main/ets/pages/Index.ets b/entry/src/main/ets/pages/Index.ets index b4a95d250cf17f0c25ad72c0e72d5dbcef969b7e..36e769f3343450d71bc766f9a75e5a73b5d6874c 100644 --- a/entry/src/main/ets/pages/Index.ets +++ b/entry/src/main/ets/pages/Index.ets @@ -14,6 +14,9 @@ */ import { hilog } from '@kit.PerformanceAnalysisKit'; +import { Constants } from '../constant/Constant'; +import PreferenceModel from '../model/PreferenceModel'; +import { BreakpointType } from '../utils/BreakpointUtil'; import { LiveView } from '../utils/LiveView'; import { TrafficView } from '../view/TrafficView'; @@ -78,6 +81,10 @@ struct TipDialog { @Entry struct LiveViewLockScreenPage { + @StorageLink('lightTime') lightTime: number = 90; + @StorageLink('horizontalBreakpoint') horizontalBreakpoint: string = Constants.BREAK_POINT_SM; + @StorageLink('verticalBreakpoint') verticalBreakpoint: string = Constants.BREAK_POINT_SM; + private updateInterval: number | undefined; private customDialogController: CustomDialogController = new CustomDialogController({ builder: TipDialog({ onConfirm: () => { @@ -99,13 +106,32 @@ struct LiveViewLockScreenPage { aboutToAppear(): void { LiveView.getInstance(this.getUIContext().getHostContext() as Context).createLiveView(); this.customDialogController?.open(); + this.updateInterval = setInterval(() => { + if (this.lightTime <= 0) { + this.lightTime = 90; + } else { + this.lightTime--; + } + let context = this.getUIContext().getHostContext(); + if (context) { + PreferenceModel.putPreference(context, 'lightTime', this.lightTime); + } + }, 1000) + } + + aboutToDisappear(): void { + if (this.updateInterval !== undefined) { + clearInterval(this.updateInterval); + this.updateInterval = undefined; + } } build() { Stack() { - Image($r('app.media.traffic_background')) - .width('110%') - .height('110%') + Image(new BreakpointType($r('app.media.ic_map'), $r('app.media.ic_map_md'), $r('app.media.ic_map_lg')) + .getValue(this.horizontalBreakpoint)) + .width('100%') + .height('100%') TrafficView() } diff --git a/entry/src/main/ets/utils/LiveView.ets b/entry/src/main/ets/utils/LiveView.ets index ecf68a5e0731cbbe74cc03c392f2ca00ed17d910..6326c94cf874aabb460170b1621f0ccc095150fc 100644 --- a/entry/src/main/ets/utils/LiveView.ets +++ b/entry/src/main/ets/utils/LiveView.ets @@ -21,6 +21,7 @@ import { backgroundTaskManager } from '@kit.BackgroundTasksKit'; import { BusinessError, commonEventManager } from '@kit.BasicServicesKit'; // [StartExclude pushCommonEvent] import { hilog } from '@kit.PerformanceAnalysisKit'; +import PreferenceModel from '../model/PreferenceModel'; import { RouteData } from '../model/RouteDataModel'; import { LiveViewUtil } from '../utils/LiveViewUtil'; import { getRouteData } from '../viewmodel/RouteDataViewModel'; @@ -51,8 +52,11 @@ export class LiveView { // Main entry point to start live view functionality public createLiveView() { + let routeData = getRouteData(); + let lightTime = PreferenceModel.getPreference(this.context, 'lightTime', 0) as number; + routeData.laneData.lightTime = lightTime; // Initialize live view and start background tasks - this.liveViewController.startLiveView(getRouteData()).then(() => { + this.liveViewController.startLiveView(routeData).then(() => { this.startContinuousRunningTask(); // [Start getRunningProcessInfo] // Set up periodic state checking @@ -63,7 +67,9 @@ export class LiveView { // Handle background state if (data[0].state === appManager.ProcessState.STATE_BACKGROUND) { // [StartExclude getRunningProcessInfo] - let routeData: RouteData = getRouteData(); + let routeData = getRouteData(); + let lightTime = PreferenceModel.getPreference(this.context, 'lightTime', 0) as number; + routeData.laneData.lightTime = lightTime; this.liveViewController.updateLiveView(routeData); // [EndExclude pushCommonEvent] // Prepare common event data @@ -90,7 +96,7 @@ export class LiveView { hilog.error(0x0000, TAG, '%{public}s', `Failed to getRunningProcessInformation. code is ${error.code} message is ${error.message}`); }); - }, 2000) + }, 1000) // [End getRunningProcessInfo] }) } diff --git a/entry/src/main/ets/view/TrafficView.ets b/entry/src/main/ets/view/TrafficView.ets index 4fb2851b4d546362063eb40a3ec864c3e4cd4bfe..62f623d1cc1bd408956f1e366239b3418adb3421 100644 --- a/entry/src/main/ets/view/TrafficView.ets +++ b/entry/src/main/ets/view/TrafficView.ets @@ -22,27 +22,10 @@ export struct TrafficView { @StorageProp('bottomRectHeight') bottomRectHeight: number = 0; @StorageLink('horizontalBreakpoint') horizontalBreakpoint: string = Constants.BREAK_POINT_SM; @StorageLink('verticalBreakpoint') verticalBreakpoint: string = Constants.BREAK_POINT_SM; - @Prop lane: number = 2; + @StorageLink('lightTime') lightTime: number = 90; private uiContext: UIContext = this.getUIContext(); private updateInterval: number | undefined; - aboutToAppear(): void { - this.updateInterval = setInterval(() => { - if (this.lane === 2) { - this.lane = 1; - } else { - this.lane = 2; - } - }, 2000) - } - - aboutToDisappear(): void { - if (this.updateInterval !== undefined) { - clearInterval(this.updateInterval); - this.updateInterval = undefined; - } - } - build() { if (this.horizontalBreakpoint !== Constants.BREAK_POINT_SM || this.verticalBreakpoint !== Constants.BREAK_POINT_MD) { @@ -121,6 +104,26 @@ export struct TrafficView { top: 0, left: this.horizontalBreakpoint === Constants.BREAK_POINT_MD ? '51%' : '31%', }) + Row() { + Stack() { + Image($r('app.media.ic_light')) + .width(106) + .height(52) + Text(this.lightTime + '') + .fontColor($r('sys.color.white')) + .fontSize(30) + .margin({ right: 16 }) + } + .alignContent(Alignment.End) + } + .padding({ + right: 16, + left: 9 + }) + .position({ + top: 90, + left: this.horizontalBreakpoint === Constants.BREAK_POINT_MD ? '51%' : '31%' + }) } } @@ -132,9 +135,29 @@ export struct TrafficView { } Image($r('app.media.traffic_info_label')) .objectFit(ImageFit.Contain) - .height(new BreakpointType(70, 80, 80,).getValue(this.horizontalBreakpoint)) + .height(new BreakpointType(70, 80, 80).getValue(this.horizontalBreakpoint)) } .width(new BreakpointType('100%', '40%', '30%',).getValue(this.horizontalBreakpoint)) + + if (this.horizontalBreakpoint === Constants.BREAK_POINT_SM) { + Row() { + Stack() { + Image($r('app.media.ic_light')) + .width(106) + .height(52) + Text(this.lightTime + '') + .fontColor($r('sys.color.white')) + .fontSize(30) + .margin({ right: 16 }) + } + .alignContent(Alignment.End) + } + .width('100%') + .justifyContent(FlexAlign.End) + .padding({ + right: 16 + }) + } } .margin({ left: new BreakpointType($r('sys.float.padding_level0'), $r('sys.float.padding_level12'), @@ -148,30 +171,6 @@ export struct TrafficView { Blank() - Row() { - Stack({ alignContent: Alignment.Top }) { - if (this.lane === 2) { - Image($r('app.media.traffic_right')) - .width(new BreakpointType('70%', '50%', '30%',).getValue(this.horizontalBreakpoint)) - } else { - Image($r('app.media.traffic_left')) - .width(new BreakpointType('70%', '50%', '30%',).getValue(this.horizontalBreakpoint)) - } - - Image($rawfile('traffic_light.svg')) - .objectFit(ImageFit.Contain) - .width(new BreakpointType('20%', '15%', '10%',).getValue(this.horizontalBreakpoint)) - .position({ - left: '70%', - top: 30 - }) - } - .width('100%') - } - - Blank() - Blank() - Row() { Column() { Divider() @@ -217,7 +216,6 @@ export struct TrafficView { } .width('100%') .height('100%') - .backgroundImage($r('app.media.traffic_background')) .backgroundImageSize(ImageSize.Cover) .backgroundImagePosition(Alignment.Center) } else { @@ -231,20 +229,6 @@ export struct TrafficView { left: 16, }) - if (this.lane === 2) { - Image($r('app.media.traffic_right')) - .width('60%') - .offset({ - y: 15, - }) - } else { - Image($r('app.media.traffic_left')) - .width('60%') - .offset({ - y: 15, - }) - } - Column() { Image($r('app.media.turn_right_light_rectangle')) .objectFit(ImageFit.Contain) @@ -283,6 +267,22 @@ export struct TrafficView { top: 16, right: 16 }) + + Row() { + Stack() { + Image($r('app.media.ic_light')) + .width(106) + .height(52) + Text(this.lightTime + '') + .fontColor($r('sys.color.white')) + .fontSize(30) + .margin({ right: 16 }) + } + .alignContent(Alignment.End) + } + .padding({ + right: 16 + }) } .width('100%') diff --git a/entry/src/main/ets/viewmodel/RouteDataViewModel.ets b/entry/src/main/ets/viewmodel/RouteDataViewModel.ets index 0233db197e74baccaa15347b97b7b1ca3f9c9fd3..e38fc472d22457f6307c1af65ba04395dc25e3a4 100644 --- a/entry/src/main/ets/viewmodel/RouteDataViewModel.ets +++ b/entry/src/main/ets/viewmodel/RouteDataViewModel.ets @@ -17,7 +17,8 @@ import { RouteData } from '../model/RouteDataModel'; const LEFT_STATE: RouteData = { laneData: { - laneNum: 1 + laneNum: 1, + lightTime: 90 }, distance: 800, roadName: $r('app.string.road_name') @@ -25,7 +26,8 @@ const LEFT_STATE: RouteData = { const RIGHT_STATE: RouteData = { laneData: { - laneNum: 2 + laneNum: 2, + lightTime: 90 }, distance: 800, roadName: $r('app.string.road_name') diff --git a/entry/src/main/resources/base/media/ic_light.png b/entry/src/main/resources/base/media/ic_light.png new file mode 100644 index 0000000000000000000000000000000000000000..5b30bb24d141d15bcd70d70be5bdd133c2936155 Binary files /dev/null and b/entry/src/main/resources/base/media/ic_light.png differ diff --git a/entry/src/main/resources/base/media/ic_lock.png b/entry/src/main/resources/base/media/ic_lock.png new file mode 100644 index 0000000000000000000000000000000000000000..31185f16c4fa222c1ed2f1c8878d17c24de3ff1a Binary files /dev/null and b/entry/src/main/resources/base/media/ic_lock.png differ diff --git a/entry/src/main/resources/base/media/ic_lock_md.png b/entry/src/main/resources/base/media/ic_lock_md.png new file mode 100644 index 0000000000000000000000000000000000000000..a9bf1539681ae2a8d82362a81715c8c5e3eb3f6a Binary files /dev/null and b/entry/src/main/resources/base/media/ic_lock_md.png differ diff --git a/entry/src/main/resources/base/media/ic_map.png b/entry/src/main/resources/base/media/ic_map.png new file mode 100644 index 0000000000000000000000000000000000000000..8b4e89e6b4a6bab4ffcce2514ecd3f129a232db7 Binary files /dev/null and b/entry/src/main/resources/base/media/ic_map.png differ diff --git a/entry/src/main/resources/base/media/ic_map_lg.png b/entry/src/main/resources/base/media/ic_map_lg.png new file mode 100644 index 0000000000000000000000000000000000000000..7acaae12333136370cfd128aa99ca129149a2933 Binary files /dev/null and b/entry/src/main/resources/base/media/ic_map_lg.png differ diff --git a/entry/src/main/resources/base/media/ic_map_md.png b/entry/src/main/resources/base/media/ic_map_md.png new file mode 100644 index 0000000000000000000000000000000000000000..4d2c619b6c28d3945ae50b8d2296d9b9f465e5ea Binary files /dev/null and b/entry/src/main/resources/base/media/ic_map_md.png differ diff --git a/screenshots/phone_lock_screen.en.png b/screenshots/phone_lock_screen.en.png index f3bf08a227911ee204bf476875e262dc0e8ecd10..975009d7b8a85bdcf744d48404ed1beb502960e9 100644 Binary files a/screenshots/phone_lock_screen.en.png and b/screenshots/phone_lock_screen.en.png differ diff --git a/screenshots/phone_lock_screen.png b/screenshots/phone_lock_screen.png index 768ae85a4b6944a8d37790e32039b4acee1143eb..d0428aedef4998b0d65483776c570012de33abd0 100644 Binary files a/screenshots/phone_lock_screen.png and b/screenshots/phone_lock_screen.png differ