diff --git a/README.md b/README.md index 5a5524c1a67ceb59ce332d65b5f57c5127467687..1c4ef08f3ffc742fbb63dcd74c5ce9dc4a9ed96a 100644 --- a/README.md +++ b/README.md @@ -2,10 +2,13 @@ ### 介绍 -本示例实现使用router、call和message三种事件,以及定点刷新的方式实现卡片信息的刷新。 +本示例卡片一使用router、call和message三种事件,以及定点刷新的方式实现刷新卡片信息,卡片二实现了定时刷新卡片、应用侧触发刷新卡片的功能。 ### 效果预览 -![](screenshots/device/form.png) +| 第一卡片 | 第二卡片 | +|----------------------------------|-----------------------------------------| +| ![](screenshots/device/form.png) | ![](screenshots/device/form_second.png) | + ### 工程目录 @@ -22,33 +25,43 @@ │ │ └──EntryFormAbility.ets // 卡片生命周期类 │ ├──pages │ │ └──Index.ets // 首页 -│ └──widget -│ ├──pages -│ │ └──WidgetCard.ets // 卡片页面 -│ ├──view -│ │ └──CardListComponent.ets -│ └──viewmodel -│ └──CardListParameter.ets +│ ├──widget +│ │ ├──pages +│ │ │ └──WidgetCard.ets // 卡片一页面 +│ │ ├──view +│ │ │ └──CardListComponent.ets +│ │ └──viewmodel +│ │ └──CardListParameter.ets +│ └──widgetupdate +│ └──pages +│ └──WidgetCardUpdate.ets // 卡片二页面 └──entry/src/main/resources // 应用静态资源目录 ``` ### 使用说明 +**第一卡片** 1. 安装应用后,长按应用图标,添加服务卡片。 2. 点击“router事件”按钮进入应用页面,返回桌面时可见卡片信息刷新。 3. 点击“call事件”,可见卡片信息刷新。 4. 点击“message事件”可见卡片信息刷新。 5. 配置“src/main/resources/base/profile/form_config.json”文件中的“scheduledUpdateTime”字段为某个时间,卡片信息即可在相应时间刷新。 +**第二卡片** +1. 安装应用后,应用首页展示图片轮播图,右下角心形支持点击。 +2. 长按应用图片选择创建第二卡片,点击卡片拉起应用。 +3. 选择图片点击心形按钮,返回桌面,图片及心形状态信息更新至卡片。 +4. 配置“src/main/resources/base/profile/form_config.json”文件中的“updateDuration”字段。卡片定时刷新的更新周期单位为30分钟。示例配置为1,表示卡片任意更新后,30分钟后定时刷新。 + ### 实现说明 1. router事件通过应用EntryAbility的生命周期回调触发更新方法,使用formProvider的updateForm方法,将内容更新到指定卡片。 2. call事件在EntryFormAbility的onAddForm中更新卡片信息,触发卡片UI的onFormTimeChange方法,通过其中postCardAction方法携带的updateCardInfo信息,触发EntryAbility中的callee监听,在监听方法中进行刷新处理。 3. message事件在EntryFormAbility的onFormEvent回调中进行刷新处理。 +4. 第二卡片创建后使用call事件将卡片id传递至EntryAbility后保存至首选项,应用侧从首选项获取卡片id使用updateForm方法更新指定卡片。 #### 注: -1. 卡片刷新也可以通过定时刷新实现,在“form_config.json”文件中配置“updateDuration”字段即可。由于定时刷新和定点刷新同时配置时,定点刷新也会被忽略,本示例项目仅使用定点刷新。若想要实现定时刷新,可参考指南指导。 -2. 本项目的卡片中信息为代码中配置的数据,在真实场景中,可以自行封装获取信息的方法。 +1. 本项目的卡片中信息为代码中配置的数据,在真实场景中,可以自行封装获取信息的方法。 ### 相关权限 @@ -57,6 +70,6 @@ ohos.permission.KEEP_BACKGROUND_RUNNING:允许Service Ability在后台持续 ### 约束与限制 1. 本示例仅支持标准系统上运行,支持设备:华为手机。 -2. HarmonyOS系统:HarmonyOS NEXT Developer Beta5及以上。 -3. DevEco Studio版本:DevEco Studio NEXT Developer Beta5及以上。 -4. HarmonyOS SDK版本:HarmonyOS NEXT Developer Beta5 SDK及以上。 \ No newline at end of file +2. HarmonyOS系统:HarmonyOS NEXT Release及以上。 +3. DevEco Studio版本:DevEco Studio NEXT Release及以上。 +4. HarmonyOS SDK版本:HarmonyOS NEXT Release SDK及以上。 \ No newline at end of file diff --git a/README_EN.md b/README_EN.md index 3ba4e31d370a46169343529d584fed2d5333ef31..a0f34f9b0c411b1385d8c50d380d73ac03b662e4 100644 --- a/README_EN.md +++ b/README_EN.md @@ -2,10 +2,12 @@ ### Overview -This sample demonstrates how to use the router, call, and message events, as well as timed updates, to update widget information. +The first card in this example uses three events: router, call, and message, as well as a fixed-point refresh method to refresh card information. The second card implements the functions of timed refresh of cards and application triggered refresh of cards. ### Preview -![](screenshots/device/form.en.png) +| First Card | Second Card | +|-------------------------------------|--------------------------------------------| +| ![](screenshots/device/form.en.png) | ![](screenshots/device/form_second.en.png) | ### Project Directory @@ -34,21 +36,28 @@ This sample demonstrates how to use the router, call, and message events, as wel ### How to Use +**First Card** 1. Install the sample app, and touch and hold the app icon to add a widget. 2. Touch the router event button to go to the app page. When you return to the home screen, you can see that the widget information is updated. 3. Touch the call event. The widget information is updated. 4. Touch the message event. The widget information is updated. 5. Set the **scheduledUpdateTime** field in the **src/main/resources/base/profile/form_config.json** file to a time point. The widget information is updated at the specified time point. +**Second Card** +1. After installing the application, the homepage of the application displays a slideshow of images, and the heart-shaped image in the bottom right corner supports clicking. +2. Long press the application image to select create a second card, click on the card to pull up the application. +3. Select the image and click the heart-shaped button to return to the desktop. The image and heart-shaped status information will be updated to the card. +4. Configure the **updateDuration** field in the **src/main/resources/base/profile/form_config.json** file. The update cycle unit for card scheduled refresh is 30 minutes. The example configuration is set to 1, which means that after any update of the card, it will be refreshed at a scheduled time after 30 minutes. + ### How to Implement 1. The router event calls the **updateForm** method of **formProvider** in the lifecycle callback of the EntryAbility to update the widget information. 2. The call event updates the widget information in the **onAddForm** method of the EntryFormAbility and triggers the **onFormTimeChange** method of the widget UI. The **updateCardInfo** information carried in the **postCardAction** method is used to trigger the callee listening in the EntryAbility, thereby updating the widget information in the listening method. 3. The message event updates the widget information in the **onFormEvent** callback of the EntryFormAbility. +4. After creating the second card, use the call event to pass the card ID to the Entry Ability and save it to the preferences. The application side retrieves the card ID from the preferences and updates the specified card using the updateForm method. #### NOTE -1. Widgets can also be periodically updated by configuring the **updateDuration** field in the **form_config.json** file. If both scheduled update and timed update are configured, scheduled update takes precedence. This sample demonstrates the timed update only. For details about how to implement scheduled update, see the development guide. -2. The widget information in this project uses the data configured in the code. In actual scenarios, you can encapsulate a method to obtain the information. +1. The widget information in this project uses the data configured in the code. In actual scenarios, you can encapsulate a method to obtain the information. ### Required Permissions @@ -57,6 +66,6 @@ This sample demonstrates how to use the router, call, and message events, as wel ### Constraints 1. The sample app is supported only on Huawei phones running the standard system. -2. The HarmonyOS version must be HarmonyOS NEXT Developer Beta5 or later. -3. The DevEco Studio version must be DevEco Studio NEXT Developer Beta5 or later. -4. The HarmonyOS SDK version must be HarmonyOS NEXT Developer Beta5 or later. +2. The HarmonyOS version must be HarmonyOS NEXT Release or later. +3. The DevEco Studio version must be DevEco Studio NEXT Release or later. +4. The HarmonyOS SDK version must be HarmonyOS NEXT Release or later. diff --git a/entry/obfuscation-rules.txt b/entry/obfuscation-rules.txt index 69c4d6a8a5531548e4886fa766090c5c157a87d9..fdbb5b9852d7dd5f39bddaeb21ab5ee1f3346749 100644 --- a/entry/obfuscation-rules.txt +++ b/entry/obfuscation-rules.txt @@ -15,4 +15,8 @@ # Keep options: # -keep-property-name: specifies property names that you want to keep -# -keep-global-name: specifies names that you want to keep in the global scope \ No newline at end of file +# -keep-global-name: specifies names that you want to keep in the global scope +-enable-property-obfuscation +-enable-toplevel-obfuscation +-enable-filename-obfuscation +-enable-export-obfuscation \ No newline at end of file diff --git a/entry/src/main/ets/common/CommonConstants.ets b/entry/src/main/ets/common/CommonConstants.ets index e9601a05ee366338cd94a9b78e5905dd0d0caf06..63fa82a911ea99a454abf8b07cef6a03737a7676 100644 --- a/entry/src/main/ets/common/CommonConstants.ets +++ b/entry/src/main/ets/common/CommonConstants.ets @@ -13,7 +13,7 @@ * limitations under the License. */ -import { CardListItemData } from '../widget/pages/WidgetCard'; +import { CardListItemData } from './CommonData'; export class CommonConstants { static readonly CARD_LIST_DATA_FIRST: Array = [ diff --git a/entry/src/main/ets/common/CommonData.ets b/entry/src/main/ets/common/CommonData.ets index e91cd9112f4df8987e0622d0bf6c21f43e154219..e0d82376b30985101419296f7aa0a5226eb52833 100644 --- a/entry/src/main/ets/common/CommonData.ets +++ b/entry/src/main/ets/common/CommonData.ets @@ -13,7 +13,6 @@ * limitations under the License. */ -import { CardListItemData } from '../widget/pages/WidgetCard'; import { CommonConstants } from './CommonConstants'; export class CommonData { @@ -32,4 +31,39 @@ export class CommonData { static changeFlag(): void { CommonData.flag++; } +} + + +export interface CardListItemData { + id: number; + title: ResourceStr; + content: ResourceStr; + icon?: Resource; + favour?: boolean; +} + +export class FormData { + formId: string = ''; + formTime: string = ''; + imageItem?: ImageItem = undefined; + isFavor?: boolean = false; + index?: number = 0; + cardList: Array = []; + + constructor(formId: string) { + this.formId = formId; + } +} + +@Observed +export class ImageItem { + public id: number = 0; + public image: ResourceStr = ''; + public isFavor: boolean = false; + + constructor(id: number, image: ResourceStr, isFavor: boolean) { + this.id = id; + this.image = image; + this.isFavor = isFavor + } } \ No newline at end of file diff --git a/entry/src/main/ets/entryability/EntryAbility.ets b/entry/src/main/ets/entryability/EntryAbility.ets index e59cd06549fb805f0e3964fa57c4ace6ab83d542..d6c45f0e000c155cc84f5c7f993c8feaf164296c 100644 --- a/entry/src/main/ets/entryability/EntryAbility.ets +++ b/entry/src/main/ets/entryability/EntryAbility.ets @@ -14,12 +14,14 @@ */ import { AbilityConstant, UIAbility, Want } from '@kit.AbilityKit'; -import { hilog } from '@kit.PerformanceAnalysisKit'; +import { preferences } from '@kit.ArkData'; import { window } from '@kit.ArkUI'; -import { formBindingData, formInfo, formProvider } from '@kit.FormKit'; import { rpc } from '@kit.IPCKit'; -import { FormData } from '../widget/pages/WidgetCard'; -import { CommonData } from '../common/CommonData'; +import { formBindingData, formInfo, formProvider } from '@kit.FormKit'; +import { hilog } from '@kit.PerformanceAnalysisKit'; +import { CommonData, FormData, ImageItem } from '../common/CommonData'; + +const TAG: string = 'EntryAbility'; export default class EntryAbility extends UIAbility { private callFunc = (data: rpc.MessageSequence): MyParcelable => { @@ -29,20 +31,50 @@ export default class EntryAbility extends UIAbility { let formData = new FormData(formId); formData.cardList = CommonData.getData(); CommonData.changeFlag(); - let formMsg: formBindingData.FormBindingData = formBindingData.createFormBindingData(formData); - formProvider.updateForm(formId, formMsg).then((data) => { - hilog.info(0x0000, 'testTag', `updateForm success. ${JSON.stringify(data)}`); - }).catch((error: Error) => { - hilog.error(0x0000, 'testTag', `updateForm failed: ${JSON.stringify(error)}`); - }); + this.updateFormData(formId, formData); } return new MyParcelable(1); }; + private callSaveFunc = (data: rpc.MessageSequence): MyParcelable => { + let params: Record = JSON.parse(data.readString()); + if (params.formId !== undefined) { + let formId: string = params.formId; + let index: number = Number.parseInt(params.index); + let formData = new FormData(formId); + let formIdArr: Array = []; + let myPreferences = AppStorage.get('myPreferences') as preferences.Preferences; + if (myPreferences.hasSync('favorCardId')) { + formIdArr = myPreferences.getSync('favorCardId', []) as Array; + } + formIdArr.push(formId); + myPreferences.putSync('favorCardId', formIdArr); + myPreferences.flush(); + let imageArr: ImageItem[] = myPreferences.getSync('imageArr', []) as ImageItem[]; + if (imageArr.length > 0) { + formData.imageItem = imageArr[index + 1 % 3]; + formData.isFavor = imageArr[index + 1 % 3].isFavor; + formData.index = imageArr[index + 1 % 3].id; + } + this.updateFormData(formId, formData); + } + return new MyParcelable(1); + }; + + private createPreferences(): void { + let dataPreferences: preferences.Preferences | null = null; + let options: preferences.Options = { + name: 'myStore' + }; + dataPreferences = preferences.getPreferencesSync(this.context, options); + AppStorage.setOrCreate('myPreferences', dataPreferences); + } onCreate(want: Want, launchParam: AbilityConstant.LaunchParam): void { - hilog.info(0x0000, 'testTag', '%{public}s', 'Ability onCreate'); + hilog.info(0x0000, TAG, '%{public}s', 'Ability onCreate'); + this.createPreferences(); this.updateInfo(want); this.callee.on('updateCardInfo', this.callFunc); + this.callee.on('saveAndUpdateForm', this.callSaveFunc); } onNewWant(want: Want, launchParam: AbilityConstant.LaunchParam): void { @@ -64,49 +96,55 @@ export default class EntryAbility extends UIAbility { let formData = new FormData(formId); formData.cardList = CommonData.getData(); CommonData.changeFlag(); - let formMsg = formBindingData.createFormBindingData(formData); - formProvider.updateForm(formId, formMsg).then((data) => { - hilog.info(0x0000, 'testTag', 'updateForm success.', JSON.stringify(data)); - }).catch((error: Error) => { - hilog.info(0x0000, 'testTag', 'updateForm failed.', JSON.stringify(error)); - }); + this.updateFormData(formId, formData); } onDestroy(): void { - hilog.info(0x0000, 'testTag', '%{public}s', 'Ability onDestroy'); + hilog.info(0x0000, TAG, '%{public}s', 'Ability onDestroy'); try { this.callee.off('updateCardInfo'); + this.callee.off('saveAndUpdateForm'); } catch (err) { - hilog.error(0x0000, 'testTag', 'Failed to disconnect callee. Cause: %{public}s', JSON.stringify(err) ?? ''); + hilog.error(0x0000, TAG, 'Failed to disconnect callee. Cause: %{public}s', JSON.stringify(err) ?? ''); } } onWindowStageCreate(windowStage: window.WindowStage): void { // Main window is created, set main page for this ability - hilog.info(0x0000, 'testTag', '%{public}s', 'Ability onWindowStageCreate'); + hilog.info(0x0000, TAG, '%{public}s', 'Ability onWindowStageCreate'); windowStage.loadContent('pages/Index', (err) => { if (err.code) { - hilog.error(0x0000, 'testTag', 'Failed to load the content. Cause: %{public}s', JSON.stringify(err) ?? ''); + hilog.error(0x0000, TAG, 'Failed to load the content. Cause: %{public}s', JSON.stringify(err) ?? ''); return; } - hilog.info(0x0000, 'testTag', 'Succeeded in loading the content.'); + hilog.info(0x0000, TAG, 'Succeeded in loading the content.'); }); } onWindowStageDestroy(): void { // Main window is destroyed, release UI related resources - hilog.info(0x0000, 'testTag', '%{public}s', 'Ability onWindowStageDestroy'); + hilog.info(0x0000, TAG, '%{public}s', 'Ability onWindowStageDestroy'); } onForeground(): void { // Ability has brought to foreground - hilog.info(0x0000, 'testTag', '%{public}s', 'Ability onForeground'); + hilog.info(0x0000, TAG, '%{public}s', 'Ability onForeground'); } onBackground(): void { // Ability has back to background - hilog.info(0x0000, 'testTag', '%{public}s', 'Ability onBackground'); + hilog.info(0x0000, TAG, '%{public}s', 'Ability onBackground'); + } + + + private updateFormData(formId: string, formData: FormData): void { + let formMsg: formBindingData.FormBindingData = formBindingData.createFormBindingData(formData); + formProvider.updateForm(formId, formMsg).then((data) => { + hilog.info(0x0000, TAG, 'updateForm success.', JSON.stringify(data)); + }).catch((error: Error) => { + hilog.info(0x0000, TAG, 'updateForm failed.', JSON.stringify(error)); + }); } } diff --git a/entry/src/main/ets/entryformability/EntryFormAbility.ets b/entry/src/main/ets/entryformability/EntryFormAbility.ets index 3a456ad358a4207cd35273cbbdaad7843fad53a2..215b665abffdca811deade59e390cee95d11c630 100644 --- a/entry/src/main/ets/entryformability/EntryFormAbility.ets +++ b/entry/src/main/ets/entryformability/EntryFormAbility.ets @@ -17,21 +17,22 @@ import { Want } from '@kit.AbilityKit'; import { systemDateTime } from '@kit.BasicServicesKit'; import { formBindingData, FormExtensionAbility, formInfo, formProvider } from '@kit.FormKit'; import { hilog } from '@kit.PerformanceAnalysisKit'; -import { FormData } from '../widget/pages/WidgetCard'; -import { CommonData } from '../common/CommonData'; +import { CommonData, FormData } from '../common/CommonData'; export default class EntryFormAbility extends FormExtensionAbility { - onAddForm(want: Want) { + onAddForm(want: Want): formBindingData.FormBindingData { // Called to return a FormBindingData object. let formData = ''; if (want && want.parameters) { let formName: string = want.parameters['ohos.extra.param.key.form_name'] as string; let formId: string = want.parameters['ohos.extra.param.key.form_identity'] as string; - if (formName === 'card_info_refresh') { + console.info('llx_test addForm: ' + formId) + if (formName === 'card_info_refresh' || formName === 'card_info_update') { let formData = new FormData(formId); formData.formTime = systemDateTime.getTime().toString(); let formInfo: formBindingData.FormBindingData = formBindingData.createFormBindingData(formData); formProvider.updateForm(formId, formInfo); + return formInfo; } } return formBindingData.createFormBindingData(formData); @@ -39,6 +40,7 @@ export default class EntryFormAbility extends FormExtensionAbility { onUpdateForm(formId: string) { // Called to notify the form provider to update a specified form. + hilog.info(0x0000, 'testTag', `FormAbility onUpdateForm, formId = ${formId}`); let formData = new FormData(formId); formData.formTime = systemDateTime.getTime().toString(); let formMsg: formBindingData.FormBindingData = formBindingData.createFormBindingData(formData); diff --git a/entry/src/main/ets/pages/Index.ets b/entry/src/main/ets/pages/Index.ets index cda7864a53eba71837766f62d5f2dc40d321a841..dc32869276758d74911708324f91ae36b77549de 100644 --- a/entry/src/main/ets/pages/Index.ets +++ b/entry/src/main/ets/pages/Index.ets @@ -13,23 +13,109 @@ * limitations under the License. */ +import { preferences } from '@kit.ArkData'; +import { formBindingData, formProvider } from '@kit.FormKit'; +import { hilog } from '@kit.PerformanceAnalysisKit'; +import { FormData, ImageItem } from '../common/CommonData'; + +const TAG: string = 'Index'; + @Entry @Component struct Index { - @State message: string = 'Hello World'; + @StorageLink('myPreferences') myPreferences: preferences.Preferences | undefined = undefined; + @StorageLink('imageArr') imageInfoArray: ImageItem[] = [ + new ImageItem(0, $r('app.media.ic_social_circle1'), false), + new ImageItem(1, $r('app.media.ic_social_circle2'), false), + new ImageItem(2, $r('app.media.ic_social_circle3'), false) + ]; + + aboutToAppear(): void { + if (this.myPreferences) { + if (this.myPreferences.hasSync('imageArr')) { + this.imageInfoArray = this.myPreferences.getSync('imageArr', []) as ImageItem[]; + return; + } else { + this.myPreferences?.putSync('imageArr', this.imageInfoArray); + this.myPreferences?.flush(); + } + } + } build() { - RelativeContainer() { - Text(this.message) - .id('HelloWorld') - .fontSize(50) - .fontWeight(FontWeight.Bold) - .alignRules({ - center: { anchor: '__container__', align: VerticalAlign.Center }, - middle: { anchor: '__container__', align: HorizontalAlign.Center } - }) + Navigation() { + Swiper() { + ForEach(this.imageInfoArray, (item: ImageItem, index: number) => { + ImageView({ + imageItem: item, + isFavor: item.isFavor, + index: index + }) + .layoutWeight(1) + }, (item: ImageItem) => JSON.stringify(item)) + } + .width('100%') + .borderRadius(24) } + .mode(NavigationMode.Stack) + .title($r('app.string.EntryAbility_label')) .height('100%') .width('100%') + .margin({ + top: 16 + }) + .padding({ + left: 16, + right: 16 + }) + } +} + +@Component +struct ImageView { + @StorageLink('myPreferences') myPreferences: preferences.Preferences | undefined = undefined; + @ObjectLink imageItem: ImageItem; + @State isFavor: boolean = false; + index: number = 0; + + build() { + Stack() { + Image(this.imageItem.image) + .objectFit(ImageFit.Auto) + .width('100%') + .height('33%') + + Image(this.isFavor ? $r('app.media.ic_public_favor_filled') : $r('app.media.ic_public_favor')) + .height(30) + .aspectRatio(1) + .margin({ + right: 8, + bottom: 8 + }) + .onClick(() => { + this.isFavor = !this.isFavor; + this.imageItem.isFavor = this.isFavor; + let tmpArr = this.myPreferences?.getSync('imageArr', []) as ImageItem[]; + tmpArr[this.index] = this.imageItem; + this.myPreferences?.putSync('imageArr', tmpArr); + this.myPreferences?.flush(); + + let favorCardIdArr = this.myPreferences?.getSync('favorCardId', '') as Array; + if (favorCardIdArr.length > 0) { + favorCardIdArr.forEach((fromId: string) => { + let formData = new FormData(fromId); + formData.imageItem = this.imageItem; + formData.isFavor = this.isFavor; + let formMsg: formBindingData.FormBindingData = formBindingData.createFormBindingData(formData); + formProvider.updateForm(fromId, formMsg).then((data) => { + hilog.info(0x0000, TAG, `updateForm success. ${JSON.stringify(data)}`); + }).catch((error: Error) => { + hilog.error(0x0000, TAG, `updateForm failed: ${JSON.stringify(error)}`); + }); + }) + } + }) + } + .alignContent(Alignment.BottomEnd) } } \ No newline at end of file diff --git a/entry/src/main/ets/widget/pages/WidgetCard.ets b/entry/src/main/ets/widget/pages/WidgetCard.ets index 8accfcae083c6eb281afe27355a948916a54d8d0..77595e0ca5821774fa3b3b0600b2604d0f3c65ca 100644 --- a/entry/src/main/ets/widget/pages/WidgetCard.ets +++ b/entry/src/main/ets/widget/pages/WidgetCard.ets @@ -15,6 +15,7 @@ import { CardListComponent } from '../view/CardListComponent'; import { CardListParameter } from '../viewmodel/CardListParameter'; +import { CardListItemData } from '../../common/CommonData' let storageUpdate = new LocalStorage(); @@ -171,20 +172,3 @@ struct WidgetCard { }) } } - -export interface CardListItemData { - id: number; - title: ResourceStr; - content: ResourceStr; - icon?: Resource; -} - -export class FormData { - formId: string = ''; - formTime: string = ''; - cardList: Array = []; - - constructor(formId: string) { - this.formId = formId; - } -} \ No newline at end of file diff --git a/entry/src/main/ets/widget/view/CardListComponent.ets b/entry/src/main/ets/widget/view/CardListComponent.ets index 064405a0da95f06c8a1cb2a8c42a41e88f4e1e42..5d508ef3c85f2867f8c5f1a8d30f45bb7078b296 100644 --- a/entry/src/main/ets/widget/view/CardListComponent.ets +++ b/entry/src/main/ets/widget/view/CardListComponent.ets @@ -53,7 +53,7 @@ export struct CardListComponent { if (this.cardListParameter.isItemCount && this.cardListParameter.itemCount) { Text(this.cardListParameter.itemCount > this.MAX_QUANTITY ? '99+' : - this.cardListParameter.itemCount + '') + this.cardListParameter.itemCount + '') .textAlign(TextAlign.Center) .fontSize($r('app.float.item_count_font_size')) .fontWeight(FontWeight.Medium) @@ -63,7 +63,8 @@ export struct CardListComponent { .height($r('app.float.item_count_height')) .borderRadius($r('app.float.item_count_radius')) } - }.layoutWeight(1) + } + .layoutWeight(1) Image(this.cardListParameter.logo) .width($r('app.float.list_logo_size')) diff --git a/entry/src/main/ets/widgetupdate/pages/WidgetCardUpdate.ets b/entry/src/main/ets/widgetupdate/pages/WidgetCardUpdate.ets new file mode 100644 index 0000000000000000000000000000000000000000..9ed4bc562e27967e71fc52a5863571940b8c3c5e --- /dev/null +++ b/entry/src/main/ets/widgetupdate/pages/WidgetCardUpdate.ets @@ -0,0 +1,89 @@ +/* + * Copyright (c) 2024 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { ImageItem } from '../../common/CommonData'; + +let localStorage = new LocalStorage(); + +@Entry(localStorage) +@Component +struct Widget1Card { + @LocalStorageProp('formId') formId: string = ''; + @LocalStorageProp('formTime') @Watch('onFormTimeChange') formTime: string = ''; + @LocalStorageProp('imageItem') imageItem: ImageItem = new ImageItem(0, $r('app.media.ic_social_circle1'), false); + @LocalStorageProp('isFavor') isFavor: boolean = false; + @LocalStorageProp('index') index: number = -1; + /* + * The action type. + */ + readonly ACTION_TYPE: string = 'router'; + /* + * The ability name. + */ + readonly ABILITY_NAME: string = 'EntryAbility'; + /* + * The message. + */ + readonly MESSAGE: string = 'add detail'; + /* + * The width percentage setting. + */ + readonly FULL_WIDTH_PERCENT: string = '100%'; + /* + * The height percentage setting. + */ + readonly FULL_HEIGHT_PERCENT: string = '100%'; + + onFormTimeChange() { + if (this.formTime) { + postCardAction(this, { + action: 'call', + abilityName: 'EntryAbility', + params: { + formId: this.formId, + method: 'saveAndUpdateForm', + message: 'Call refresh card.', + index: this.index + } + }); + } + } + + build() { + Stack() { + Image(this.imageItem?.image) + .height(this.FULL_WIDTH_PERCENT) + .width(this.FULL_WIDTH_PERCENT) + Image(this.isFavor ? $r('app.media.ic_public_favor_filled') : $r('app.media.ic_public_favor')) + .height(30) + .aspectRatio(1) + .margin({ + right: 8, + bottom: 8 + }) + } + .alignContent(Alignment.BottomEnd) + .height(this.FULL_HEIGHT_PERCENT) + .onClick(() => { + postCardAction(this, { + action: this.ACTION_TYPE, + abilityName: this.ABILITY_NAME, + params: { + message: this.MESSAGE + } + }); + }) + } +} \ No newline at end of file diff --git a/entry/src/main/resources/base/element/color.json b/entry/src/main/resources/base/element/color.json index 2171512a7ae5aeeb8eaaeaf87994af626e51e824..0ea5268adddd4dbe371e77cf6836e8c35d1466c3 100644 --- a/entry/src/main/resources/base/element/color.json +++ b/entry/src/main/resources/base/element/color.json @@ -15,6 +15,10 @@ { "name": "refresh_color", "value": "#0A59F7" + }, + { + "name": "item_title_font", + "value": "#E6000000" } ] } \ No newline at end of file diff --git a/entry/src/main/resources/base/element/float.json b/entry/src/main/resources/base/element/float.json index 9f090b9f5538448988a196ca8da938b48d463a55..f801ba298bae7a8482ef03c26920efe600b13576 100644 --- a/entry/src/main/resources/base/element/float.json +++ b/entry/src/main/resources/base/element/float.json @@ -99,6 +99,10 @@ { "name": "border_radius", "value": "12vp" + }, + { + "name": "font_size", + "value": "14fp" } ] } \ No newline at end of file diff --git a/entry/src/main/resources/base/element/string.json b/entry/src/main/resources/base/element/string.json index 63a313619dc5f86b91c6ad30cc83844c4bdbc918..d517db9c5e9b0d6cbe005404ad82ab20bc2e90f6 100644 --- a/entry/src/main/resources/base/element/string.json +++ b/entry/src/main/resources/base/element/string.json @@ -147,6 +147,14 @@ { "name": "reason_keep_background_running", "value": "Allow applications to run in the background in card update scenarios." + }, + { + "name": "widget1_desc", + "value": "This is a service widget." + }, + { + "name": "widget1_display_name", + "value": "widget1" } ] } \ No newline at end of file diff --git a/entry/src/main/resources/base/media/ic_public_favor.svg b/entry/src/main/resources/base/media/ic_public_favor.svg new file mode 100644 index 0000000000000000000000000000000000000000..fbb98de4bbb1f2e1f9e5f7d2adeeed8dd0f8b1fb --- /dev/null +++ b/entry/src/main/resources/base/media/ic_public_favor.svg @@ -0,0 +1,13 @@ + + + Public/ic_public_favor_filled + + + + + + + + + + \ No newline at end of file diff --git a/entry/src/main/resources/base/media/ic_public_favor_filled.png b/entry/src/main/resources/base/media/ic_public_favor_filled.png new file mode 100644 index 0000000000000000000000000000000000000000..75a8d7f242d72793a2a748d8d4d707a737b9c10c Binary files /dev/null and b/entry/src/main/resources/base/media/ic_public_favor_filled.png differ diff --git a/entry/src/main/resources/base/media/ic_social_circle1.png b/entry/src/main/resources/base/media/ic_social_circle1.png new file mode 100644 index 0000000000000000000000000000000000000000..c154bf4ce1ede2b1ab76d94a388748add9621005 Binary files /dev/null and b/entry/src/main/resources/base/media/ic_social_circle1.png differ diff --git a/entry/src/main/resources/base/media/ic_social_circle2.jpg b/entry/src/main/resources/base/media/ic_social_circle2.jpg new file mode 100644 index 0000000000000000000000000000000000000000..1f48c2abb9c15499951edb75092ec61bd92042a7 Binary files /dev/null and b/entry/src/main/resources/base/media/ic_social_circle2.jpg differ diff --git a/entry/src/main/resources/base/media/ic_social_circle3.jpg b/entry/src/main/resources/base/media/ic_social_circle3.jpg new file mode 100644 index 0000000000000000000000000000000000000000..c743fffbde638107707801fb7740c530c4f6cdd2 Binary files /dev/null and b/entry/src/main/resources/base/media/ic_social_circle3.jpg differ diff --git a/entry/src/main/resources/base/profile/form_config.json b/entry/src/main/resources/base/profile/form_config.json index bdc0a6bd4c5a6cc7c67d421b62d404e3bb912408..4bff58037e40c4044603d498306ae596d580e887 100644 --- a/entry/src/main/resources/base/profile/form_config.json +++ b/entry/src/main/resources/base/profile/form_config.json @@ -20,6 +20,27 @@ "supportDimensions": [ "4*4" ] + }, + { + "name": "card_info_update", + "displayName": "$string:widget1_display_name", + "description": "$string:widget1_desc", + "src": "./ets/widgetupdate/pages/WidgetCardUpdate.ets", + "uiSyntax": "arkts", + "window": { + "designWidth": 720, + "autoDesignWidth": true + }, + "colorMode": "auto", + "isDynamic": true, + "isDefault": false, + "updateEnabled": true, + "updateDuration": 1, + "scheduledUpdateTime": "10:30", + "defaultDimension": "2*4", + "supportDimensions": [ + "2*4" + ] } ] } \ No newline at end of file diff --git a/entry/src/main/resources/en_US/element/string.json b/entry/src/main/resources/en_US/element/string.json index 5759b5e95757b30890bcf0bf2f2c2e5c315b3cd1..7f6920cc7b0489edc1ec70d16d393023331e85b2 100644 --- a/entry/src/main/resources/en_US/element/string.json +++ b/entry/src/main/resources/en_US/element/string.json @@ -147,6 +147,14 @@ { "name": "reason_keep_background_running", "value": "Allow applications to run in the background in card update scenarios." + }, + { + "name": "widget1_desc", + "value": "This is a service widget." + }, + { + "name": "widget1_display_name", + "value": "widget1" } ] } \ No newline at end of file diff --git a/entry/src/main/resources/zh_CN/element/string.json b/entry/src/main/resources/zh_CN/element/string.json index c0cd6bb3a576105accf6864b4730a7ba42eea365..2d246ccee24838fd4b72114826e2121e7eb049e9 100644 --- a/entry/src/main/resources/zh_CN/element/string.json +++ b/entry/src/main/resources/zh_CN/element/string.json @@ -147,6 +147,14 @@ { "name": "reason_keep_background_running", "value": "允许应用在卡片更新场景中后台运行" + }, + { + "name": "widget1_desc", + "value": "This is a service widget." + }, + { + "name": "widget1_display_name", + "value": "widget1" } ] } \ No newline at end of file diff --git a/screenshots/device/form.en.png b/screenshots/device/form.en.png index 802f8606c6dbcdc7ffea1f31ee0e49232fa22fb8..cd997728506888bba72b4eebb0f26f58b20bdd06 100644 Binary files a/screenshots/device/form.en.png and b/screenshots/device/form.en.png differ diff --git a/screenshots/device/form.png b/screenshots/device/form.png index 457a55783817e80c2bde80d27c8d22b53767a36f..e26372a2820ff28211c08ea728328193f7ab57ba 100644 Binary files a/screenshots/device/form.png and b/screenshots/device/form.png differ diff --git a/screenshots/device/form_second.en.png b/screenshots/device/form_second.en.png new file mode 100644 index 0000000000000000000000000000000000000000..8b83900b469cdb7ad819f33503179f67cdfd83ec Binary files /dev/null and b/screenshots/device/form_second.en.png differ diff --git a/screenshots/device/form_second.png b/screenshots/device/form_second.png new file mode 100644 index 0000000000000000000000000000000000000000..141f9d029eeab50d56b57f077762d301e18ff2af Binary files /dev/null and b/screenshots/device/form_second.png differ