xugenyuan

ref |> 早晚报桌面组件开发

Signed-off-by: xugenyuan <xugenyuan@wondertek.com.cn>
Showing 23 changed files with 535 additions and 27 deletions
... ... @@ -101,3 +101,5 @@ export { viewBlogItemInsightIntentShare } from './src/main/ets/utils/InsightInte
export { CommentListDialogView } from './src/main/ets/components/comment/view/CommentListDialog';
export { CustomPullToRefresh } from './src/main/ets/components/reusable/CustomPullToRefresh';
export { MorningEveningViewModel } from './src/main/ets/viewmodel/MorningEveningViewModel'
\ No newline at end of file
... ...
... ... @@ -20,7 +20,7 @@ import { http } from '@kit.NetworkKit';
import { BusinessError } from '@kit.BasicServicesKit';
import ResponseCode from '@ohos.net.http';
const resourceMgs: resourceManager.ResourceManager = getContext(this).resourceManager;
// const resourceMgs: resourceManager.ResourceManager = getContext(this).resourceManager;
const PIXEL_MAP_SIZE_WIDTH: number = 40;
const PIXEL_MAP_SIZE_HEIGHT: number = 40;
... ... @@ -29,19 +29,19 @@ const PIXEL_MAP_SIZE_HEIGHT: number = 40;
* @param icon 图片地址,模拟数据存放于rawfile文件夹中
* @returns 图片转换的PixelMap对象
*/
export async function image2PixelMap(icon: string): Promise<image.PixelMap> {
// 读取rawfile文件夹下的文件
const rawFileDescriptor: resourceManager.RawFileDescriptor = resourceMgs.getRawFdSync(icon);
const imageSource: image.ImageSource = image.createImageSource(rawFileDescriptor);
// 通过ImageSource对象创建PixelMap对象,使用BGRA_8888格式,由4个字节表示一个像素
const pixelMap: Promise<PixelMap> = imageSource.createPixelMap({
editable: false,
desiredPixelFormat: image.PixelMapFormat.BGRA_8888,
// 高性能知识点:经测试,将图片的宽和高设置为40像素时,既可以保证提取颜色的准确性,也可以保证计算颜色的速度。
desiredSize: { width: PIXEL_MAP_SIZE_WIDTH, height: PIXEL_MAP_SIZE_HEIGHT }
})
return pixelMap;
}
// export async function image2PixelMap(icon: string): Promise<image.PixelMap> {
// // 读取rawfile文件夹下的文件
// const rawFileDescriptor: resourceManager.RawFileDescriptor = resourceMgs.getRawFdSync(icon);
// const imageSource: image.ImageSource = image.createImageSource(rawFileDescriptor);
// // 通过ImageSource对象创建PixelMap对象,使用BGRA_8888格式,由4个字节表示一个像素
// const pixelMap: Promise<PixelMap> = imageSource.createPixelMap({
// editable: false,
// desiredPixelFormat: image.PixelMapFormat.BGRA_8888,
// // 高性能知识点:经测试,将图片的宽和高设置为40像素时,既可以保证提取颜色的准确性,也可以保证计算颜色的速度。
// desiredSize: { width: PIXEL_MAP_SIZE_WIDTH, height: PIXEL_MAP_SIZE_HEIGHT }
// })
// return pixelMap;
// }
/**
* 通过http的request方法从网络下载图片资源
... ...
... ... @@ -6,6 +6,33 @@ import { PageRepository } from '../repository/PageRepository';
const TAG = 'MorningEveningViewModel'
export class MorningEveningViewModel {
static async getDailyPaperTopic(): Promise<PageInfoBean> {
return new Promise<PageInfoBean>((success, error) => {
Logger.info(TAG, `getDailyPaperTopic pageInfo start`);
PageRepository.fetchDailyPaperTopic()
.then((resDTO: ResponseDTO<PageInfoBean>) => {
if (!resDTO || !resDTO.data) {
Logger.error(TAG, 'getDailyPaperTopic then navResDTO is empty');
error('resDTO is empty');
return
}
if (resDTO.code != 0) {
Logger.error(TAG, `getDailyPaperTopic then code:${resDTO.code}, message:${resDTO.message}`);
error('resDTO Response Code is failure');
return
}
// let navResStr = JSON.stringify(navResDTO);
Logger.info(TAG, "getDailyPaperTopic then,navResDTO.timestamp:" + resDTO.timestamp);
success(resDTO.data);
})
.catch((err: Error) => {
Logger.error(TAG, `getDailyPaperTopic catch, error.name : ${err.name}, error.message:${err.message}`);
error(err);
})
})
}
static async getMorningEveningPageInfo(pageId: string): Promise<PageInfoBean> {
return new Promise<PageInfoBean>((success, error) => {
Logger.info(TAG, `getMorningEveningPageInfo pageInfo start`);
... ...
export { WDShare } from './src/main/ets/WDShare'
export { DeepLinkUtil } from "./src/main/ets/utils/DeepLinkUtil"
\ No newline at end of file
... ...
import { ContentDetailDTO, ContentDTO } from 'wdBean/Index';
import { ContentDetailDTO, ContentDTO, FrontLinkObject } from 'wdBean/Index';
import { BreakpointSystem } from 'wdKit/Index';
import { ContentType } from 'wdRouter/Index';
... ... @@ -16,6 +16,10 @@ export class DeepLinkUtil {
return DeepLinkUtil.generate(Number(content.objectType), content.objectId +'', content.relId, content.linkUrl)
}
static generateDeepLinkWithFrontObjectLink(content: FrontLinkObject) {
return DeepLinkUtil.generate(Number(content.newsType), content.newsId +'', content.newsRelId + '', content.linkUrl)
}
private static generate(contentType: number, contentId?: string, relId?: string, link?: string): string {
let deeplink = DeepLinkUtil.DEEP_LINK_PREFIX
... ...
... ... @@ -20,6 +20,7 @@
"wdJsBridge": "file:../../commons/wdJsBridge",
"wdLogin": "file:../../features/wdLogin",
"wdTracking": "file:../../features/wdTracking",
"wdPlayer": "file:../../features/wdPlayer"
"wdPlayer": "file:../../features/wdPlayer",
"wdShare": "file:../../features/wdShare",
}
}
... ...
import { formBindingData, FormExtensionAbility, formInfo, formProvider } from '@kit.FormKit';
import { Want } from '@kit.AbilityKit';
import { Logger, NetworkManager, SPHelper, StringUtils } from 'wdKit/Index';
import { BusinessError } from '@kit.BasicServicesKit';
import { FormDataType, NewspaperDataFetcher } from './NewspaperDataFetcher';
import { JSON } from '@kit.ArkTS';
import { FormNewspaperPaperType } from '../dailynewspaperwidget/common/NewspaperWidgetData';
import { HostEnum, HostManager, WDHttp } from 'wdNetwork/Index';
const TAG = "DailyNewspaperFormAbility"
export default class DailyNewspaperFormAbility extends FormExtensionAbility {
onAddForm(want: Want) {
Logger.debug(TAG, "onAddForm with " + JSON.stringify(want))
this.initApp()
if (want.parameters) {
let formId = want.parameters[formInfo.FormParam.IDENTITY_KEY] as string
let isTempCard = want.parameters[formInfo.FormParam.TEMPORARY_KEY] as boolean
if (isTempCard === false) { // 如果为常态卡片,直接进行信息持久化
Logger.debug(TAG, "开始刷新数据");
NewspaperDataFetcher.refreshDailyPaper().then((data) => {
let formData = formBindingData.createFormBindingData(data);
formProvider.updateForm(formId, formData).catch((err: BusinessError) => {
Logger.debug(TAG, ` xFailed to updateForm. Code: ${err.code}, message: ${err.message}`);
});
})
}
}
let obj: FormDataType = {}
obj.paperType = FormNewspaperPaperType.unknown
let formData = formBindingData.createFormBindingData(obj);
return formData;
}
onCastToNormalForm(formId: string) {
// 使用方将临时卡片转换为常态卡片触发,提供方需要做相应的处理
Logger.debug(TAG, `onCastToNormalForm, formId: ${formId}`);
}
onUpdateForm(formId: string) {
// 若卡片支持定时更新/定点更新/卡片使用方主动请求更新功能,则提供方需要重写该方法以支持数据更新
Logger.debug(TAG, 'onUpdateForm ' + formId);
NewspaperDataFetcher.refreshDailyPaper().then((data) => {
let formData = formBindingData.createFormBindingData(data);
formProvider.updateForm(formId, formData).catch((err: BusinessError) => {
Logger.debug(TAG, ` xFailed to updateForm. Code: ${err.code}, message: ${err.message}`);
});
})
}
onFormEvent(formId: string, message: string) {
// 若卡片支持触发事件,则需要重写该方法并实现对事件的触发
Logger.debug(TAG, 'onFormEvent:' +" formId =>" + formId + " message =>" + message);
}
onRemoveForm(formId: string) {
// 当对应的卡片删除时触发的回调,入参是被删除的卡片ID
Logger.debug(TAG, 'onRemoveForm');
}
// onConfigurationUpdate(config: Configuration) {
// // 当前formExtensionAbility存活时更新系统配置信息时触发的回调。
// // 需注意:formExtensionAbility创建后5秒内无操作将会被清理。
// Logger.debug(TAG, 'onConfigurationUpdate:' + JSON.stringify(config));
// }
onAcquireFormState(want: Want) {
// 卡片提供方接收查询卡片状态通知接口,默认返回卡片初始状态。
return formInfo.FormState.READY;
}
initApp() {
// KV存储
SPHelper.init(this.context);
// 网络模块
NetworkManager.getInstance().init()
// App环境
const spHostUrl = SPHelper.default.getSync('hostUrl', '') as string
if (StringUtils.isNotEmpty(spHostUrl)) {
HostManager.changeHost(spHostUrl as HostEnum)
}
Logger.debug(TAG, "App 网络 初始化")
WDHttp.initHttpHeader()
}
};
\ No newline at end of file
... ...
import { CompInfoBean, ContentDTO, PageInfoBean } from 'wdBean/Index';
import { MorningEveningViewModel } from 'wdComponent/Index';
import { Logger } from 'wdKit/Index';
import { HttpUrlUtils, ResponseDTO, WDHttp } from 'wdNetwork/Index';
import { DeepLinkUtil } from 'wdShare/Index'
import { FormNewspaperPaperType, FormNewspaperPaperInfo, FormNewspaperPaperContent } from "../dailynewspaperwidget/common/NewspaperWidgetData"
const TAG = "NewspaperDataFetcher"
export type FormDataType = Record<string, FormNewspaperPaperType | FormNewspaperPaperInfo | FormNewspaperPaperContent[]>
export class NewspaperDataFetcher {
public static async refreshDailyPaper(): Promise<FormDataType> {
return new Promise<FormDataType>(async (reslove, fail) => {
let data: FormDataType = { 'paperType': FormNewspaperPaperType.unknown }
data.paperInfo = { showLeftImage: false }
try {
let page: PageInfoBean = await MorningEveningViewModel.getDailyPaperTopic()
data.paperType = page.topicInfo?.frontFlag || FormNewspaperPaperType.unknown
let currentTime = new Date().getTime()
let compInfo = await MorningEveningViewModel.getMorningEveningCompInfo(
page.id,
page.groups[0]?.id,
currentTime + "",
page.topicInfo?.topicId
)
if (page.topicInfo.frontLinkObject) {
data.paperInfo.showLeftImage = true
data.paperInfo.leftImageUrl = page.topicInfo.frontLinkObject.coverUrl
data.paperInfo.leftTitle = page.topicInfo.frontLinkObject.title
data.paperInfo.leftDeepLink = DeepLinkUtil.generateDeepLinkWithFrontObjectLink(page.topicInfo.frontLinkObject)
}
let contents: ContentDTO[] = compInfo.compList[0].operDataList as ContentDTO[]
if (contents && contents.length) {
data.paperContents = contents.map((contentDTO) => {
let content : FormNewspaperPaperContent = {
title: contentDTO.newsTitle,
coverUrl: contentDTO.coverUrl,
deepLink: DeepLinkUtil.generateDeepLinkWithProgram(contentDTO)
}
return content
})
}
} catch (e) {
Logger.debug(TAG, "刷新早晚报数据失败" + JSON.stringify(e))
fail(e)
// reslove(NewspaperDataFetcher.fakeData())
return
}
Logger.debug(TAG, "refresh Data " + JSON.stringify(data))
reslove(data)
})
}
static fakeData(): FormDataType {
let data : FormNewspaperPaperContent = {
title: "标题标题标题标题标题标题标题标题",
coverUrl: "https://"
} as FormNewspaperPaperContent
return {
'paperType': FormNewspaperPaperType.unknown,
"paperInfo": {
showLeftImage: true,
leftTitle: "leftTitleleftTitleleftTitleleftTitleleftTitleleftTitle"
},
"paperContents": [data, data, data]
}
}
}
\ No newline at end of file
... ...
export namespace NewspaperWidgetCommon {
export enum JumpParam {
DeepLinkKey = "newspaper.widget.jump.deeplink",
FromNewspaperWidgetKey = "newspaper.widget.jump.fromNewspaperWidget"
}
export enum PosterCardAction {
ActionRouter = "router",
ActionCaller = "caller",
MainAbilityName = "EntryAbility"
}
}
\ No newline at end of file
... ...
export enum FormNewspaperPaperType {
unknown = 0,
morning = 1,
noon = 2,
evening = 3
}
export class FormNewspaperPaperInfo {
showLeftImage: boolean = false
leftImageUrl?: string
leftTitle?: string
leftDeepLink?: string
rightContentHasAnyImage?: boolean = false
}
export class FormNewspaperPaperContent {
title: string = ""
coverUrl?: string
deepLink: string = ""
}
... ...
import { FormNewspaperPaperType, FormNewspaperPaperInfo, FormNewspaperPaperContent } from "../common/NewspaperWidgetData"
import { FormTopComponent } from "./FormTopComponent"
import { NewspaperWidgetCommon } from "../common/NewspaperWidgetCommon"
const TAG = "DailyNewspaperWidgetCard"
let storageWidgetImageUpdate = new LocalStorage();
@Entry(storageWidgetImageUpdate)
@Component
struct DailyNewspaperWidgetCard {
@LocalStorageProp('paperType') paperType: FormNewspaperPaperType = FormNewspaperPaperType.unknown;
@LocalStorageProp('paperInfo') paperInfo: FormNewspaperPaperInfo = {} as FormNewspaperPaperInfo;
@LocalStorageProp('paperContents') paperContents: FormNewspaperPaperContent[] = [];
private DAILY_NEWSPAPER_DEEP_LINK = "rmrbapp://rmrb.app/openwith?type=topic&subType=moring_evening_news&pageId=&relId=&skipType=1"
build() {
Column() {
FormTopComponent({
paperType: this.paperType
})
.width("100%").height(36)
// .margin({top: 3})
.onClick(() => {
jumpWithDeepLink(this.DAILY_NEWSPAPER_DEEP_LINK, this)
})
Row() {
if (this.paperInfo.showLeftImage) {
this.leftImageView()
}
if (this.paperContents.length) {
this.listView()
} else {
EmptyView()
}
}.width("100%")
.layoutWeight(1)
}
}
@Builder leftImageView() {
Stack({ alignContent: Alignment.Bottom }) {
Image(this.paperInfo.leftImageUrl)
// Image($r("app.media.desktop_card_comp_place_holder_16_9"))
.alt($r("app.media.desktop_card_comp_place_holder_16_9"))
.objectFit(ImageFit.Cover)
.aspectRatio(87/116)
.autoResize(true)
.borderRadius(6)
.onClick(() => {
jumpWithDeepLink(this.paperInfo.leftDeepLink || "", this)
})
Text(this.paperInfo.leftTitle)
.textOverflow({ overflow: TextOverflow.Ellipsis })
.fontSize(11)
.fontWeight(800)
.lineHeight(14)
.maxLines(2)
.fontColor(Color.White)
.padding({left: 4 + 4, bottom:6, right: 4 + 4})
}
.margin({left: 12, top: 2, bottom: 12})
// .backgroundColor(Color.Red)
.width(87)
}
@Builder listView() {
List() {
ForEach(this.paperContents, (item: FormNewspaperPaperContent, index: number) => {
ListItem() {
ContentCellView({content: item, index: index, hasImage: (item.coverUrl && item.coverUrl.length > 0 ? true : false)})
}
}, (item: FormNewspaperPaperContent, index) => index + JSON.stringify(item))
}
.divider(
{strokeWidth: 0.5,
color: "#f5f5f5",
startMargin: 0,
endMargin: 0})
.width("100%")
.margin({left: this.paperInfo.showLeftImage ? 8 : 12, top: 0, bottom: 12, right: 12})
.layoutWeight(1)
}
}
@Component
struct ContentCellView {
@Prop content: FormNewspaperPaperContent
private index : number = 0
private hasImage: boolean = false
build() {
Row() {
Text(this.content.title)
.maxLines((this.content.coverUrl && this.content.coverUrl.length > 0 ? 2 : 1))
.textOverflow({ overflow: TextOverflow.Ellipsis })
.fontSize(14)
.lineHeight(20)
.fontWeight(FontWeight.Medium)
.fontColor(Color.Black)
.layoutWeight(1)
// .backgroundColor(Color.Red)
.offset({y: this.hasImage && this.index == 0 ? -3 : 0})
.margin({
top: this.hasImage ? 0 : 4,
bottom: this.hasImage ? 0 : 6})
if (this.hasImage) {
Image(this.content.coverUrl)
// Image($r("app.media.desktop_card_comp_place_holder_3_4"))
.alt($r("app.media.desktop_card_comp_place_holder_3_4"))
.objectFit(ImageFit.Cover)
.height(40)
.aspectRatio(75/50)
.borderRadius(4)
.margin({left: 10, bottom: 8, top: this.index == 0 ? 2 : 8, right: 0})
.autoResize(true)
}
}
.justifyContent(FlexAlign.Start)
.onClick(() => {
jumpWithDeepLink(this.content.deepLink || "", this)
})
}
}
@Component
struct EmptyView {
build() {
Row() {
Image($r("app.media.desktop_card_empty_bg"))
.objectFit(ImageFit.Fill)
// .width("100%")
// .height("100%")
.margin({left: 12, bottom: 12, right: 12, top: 4})
}
.height("100%")
.width("100%")
// .backgroundColor(Color.Red)
}
}
function jumpWithDeepLink(deepLink: string, component: Object) {
console.log(TAG + "jumpWithDeepLink: " + deepLink)
if (deepLink.length == 0) {
return
}
const deepLinkKey: string = NewspaperWidgetCommon.JumpParam.DeepLinkKey
const fromDailyNewspaperKey: string = NewspaperWidgetCommon.JumpParam.FromNewspaperWidgetKey
postCardAction(component, {
action: NewspaperWidgetCommon.PosterCardAction.ActionRouter,
abilityName: NewspaperWidgetCommon.PosterCardAction.MainAbilityName,
params: {
deepLinkKey: deepLink,
fromDailyNewspaperKey: true
}
});
}
\ No newline at end of file
... ...
import { FormNewspaperPaperType } from "../common/NewspaperWidgetData"
@Component
export struct FormTopComponent {
@Prop paperType: FormNewspaperPaperType
build() {
Row() {
if (this.paperType == FormNewspaperPaperType.noon) {
Image($r("app.media.desktop_card_newspaper_noon"))
.fancy()
} else if (this.paperType == FormNewspaperPaperType.evening) {
Image($r("app.media.desktop_card_newspaper_evening"))
.fancy()
} else {
Image($r("app.media.desktop_card_newspaper_moring"))
.fancy()
}
Image($r("app.media.desktop_card_peopledaily_logo"))
.width(213).height(18)
.offset({right: -10})
}
.justifyContent(FlexAlign.SpaceBetween)
.alignItems(VerticalAlign.Center)
.width("100%").height("100%")
}
}
@Extend(Image) function fancy() {
.width(58)
.height(20)
.margin({left: 12})
}
\ No newline at end of file
... ...
... ... @@ -15,7 +15,8 @@
"pages": "$profile:main_pages",
"abilities": [
{
"name": "EntryAbility", // 这里不能改动,和后台推送有绑定
"name": "EntryAbility",
// 这里不能改动,和后台推送有绑定
"srcEntry": "./ets/entryability/EntryAbility.ets",
"description": "$string:EntryAbility_desc",
"icon": "$media:app_icon_layer",
... ... @@ -32,20 +33,24 @@
"action.system.home",
"com.test.pushaction"
],
"uris" : [{
"scheme": 'rmrbapp',
"host": 'rmrb.app',
'port': '8080',
"path": 'openwith'
}]
"uris": [
{
"scheme": 'rmrbapp',
"host": 'rmrb.app',
'port': '8080',
"path": 'openwith'
}
]
}
]
}
],
"metadata": [{
"name": "client_id",
"value": "110737325"
}],
"metadata": [
{
"name": "client_id",
"value": "110737325"
}
],
"requestPermissions": [
{
"name": "ohos.permission.CAMERA",
... ... @@ -100,6 +105,22 @@
{
"name": "ohos.permission.INTERNET"
},
],
"extensionAbilities": [
{
"name": "DailyNewspaperFormAbility",
"srcEntry": "./ets/dailynewspaperformability/DailyNewspaperFormAbility.ets",
"label": "$string:DailyNewspaperFormAbility_label",
"description": "$string:DailyNewspaperFormAbility_desc",
"type": "form",
"metadata": [
{
"name": "ohos.extension.form",
"resource": "$profile:form_config"
}
],
}
]
}
}
\ No newline at end of file
... ...
... ... @@ -63,6 +63,22 @@
{
"name": "reason_read_write_media",
"value": "user_grant"
},
{
"name": "DailyNewspaperFormAbility_desc",
"value": "早晚报卡片"
},
{
"name": "DailyNewspaperFormAbility_label",
"value": "早晚报卡片"
},
{
"name": "DailyNewspaperWidget_desc",
"value": "有品质的新闻"
},
{
"name": "DailyNewspaperWidget_display_name",
"value": "早晚报"
}
]
}
\ No newline at end of file
... ...
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" fill="none" version="1.1" width="316" height="116" viewBox="0 0 316 116"><g><g><rect x="0" y="7" width="214" height="14" rx="0" fill="#F5F5F5" fill-opacity="1"/></g><g><rect x="0" y="73" width="214" height="14" rx="0" fill="#F5F5F5" fill-opacity="1"/></g><g><rect x="0" y="29" width="150" height="14" rx="0" fill="#F5F5F5" fill-opacity="1"/></g><g><rect x="0" y="95" width="150" height="14" rx="0" fill="#F5F5F5" fill-opacity="1"/></g><g><g><rect x="241" y="0" width="75" height="50" rx="4" fill="#F5F5F5" fill-opacity="1"/></g></g><g><g><rect x="241" y="66" width="75" height="50" rx="4" fill="#F5F5F5" fill-opacity="1"/></g></g><g><line x1="0" y1="57.75" x2="316.0000305175781" y2="57.75" fill-opacity="0" stroke-opacity="1" stroke="#EDEDED" fill="none" stroke-width="0.5"/></g></g></svg>
\ No newline at end of file
... ...
{
"forms": [
{
"name": "DailyNewspaperWidget",
"displayName": "$string:DailyNewspaperWidget_display_name",
"description": "$string:DailyNewspaperWidget_desc",
"src": "./ets/dailynewspaperwidget/pages/DailyNewspaperWidgetCard.ets",
"uiSyntax": "arkts",
"window": {
"designWidth": 720,
"autoDesignWidth": true
},
"colorMode": "light",
"isDynamic": true,
"isDefault": true,
"updateEnabled": true,
"scheduledUpdateTime": "10:30",
"updateDuration": 1,
"defaultDimension": "2*4",
"supportDimensions": [
"2*4"
]
}
]
}
\ No newline at end of file
... ...