xugenyuan

ref |> 早晚报桌面组件图片显示相关逻辑

@@ -68,3 +68,7 @@ export { FastClickUtil } from './src/main/ets/utils/FastClickUtil'; @@ -68,3 +68,7 @@ export { FastClickUtil } from './src/main/ets/utils/FastClickUtil';
68 68
69 // export { PublicPopupDialogView } from "./src/main/ets/pubComps/dialog/PublicPopupDialogView" 69 // export { PublicPopupDialogView } from "./src/main/ets/pubComps/dialog/PublicPopupDialogView"
70 export { PublicDialogManager, CloseAction } from "./src/main/ets/pubComps/dialog/PublicDialogManager" 70 export { PublicDialogManager, CloseAction } from "./src/main/ets/pubComps/dialog/PublicDialogManager"
  71 +
  72 +export { CrptoUtils } from "./src/main/ets/utils/CrptoUtils"
  73 +
  74 +export { FileUtils } from "./src/main/ets/utils/FileUtils"
  1 +import cryptoFramework from '@ohos.security.cryptoFramework';
  2 +import buffer from '@ohos.buffer';
  3 +
  4 +export class CrptoUtils {
  5 +
  6 + static md5(message: string) : Promise<string> {
  7 + return CrptoUtils.mdFunc(message, 'MD5')
  8 + }
  9 +
  10 + static mdFunc(message: string, algoName: string = 'MD5'): Promise<string> {
  11 + return new Promise<string>(async (reslove, fail) => {
  12 +
  13 + try {
  14 + let md = cryptoFramework.createMd(algoName);
  15 + // 数据量较少时,可以只做一次update,将数据全部传入,接口未对入参长度做限制
  16 + await md.update({ data: new Uint8Array(buffer.from(message, 'utf-8').buffer) });
  17 + let mdResult = await md.digest();
  18 + // console.info('Md result:' + mdResult.data);
  19 +
  20 + // const string = mdResult.data.map((charCode, index, array) => {
  21 + // return String.fromCharCode(charCode)
  22 + // }).join("")
  23 +
  24 + let string = "";
  25 + for (let i = 0; i < mdResult.data.length; i++) {
  26 + string += mdResult.data[i].toString(16).padStart(2, "0")
  27 + }
  28 +
  29 + // const string = mdResult.data.join('')
  30 +
  31 + reslove(string)
  32 + } catch (e) {
  33 + fail(e)
  34 + }
  35 + })
  36 + }
  37 +}
  38 +
  39 +
  40 +
  41 +
  1 +
  2 +import fs from '@ohos.file.fs';
  3 +import { BusinessError } from '@kit.BasicServicesKit';
  4 +
  5 +export class FileUtils {
  6 +
  7 + // 文件是否存在,忽略错误
  8 + static fileExsit(path: string) : Promise<boolean> {
  9 + return new Promise((reslove, fail) => {
  10 + fs.stat(path).then(() => {
  11 + reslove(true)
  12 + }).catch((error: BusinessError) => {
  13 + if (error.code = 13900002) {
  14 + reslove(false)
  15 + } else {
  16 + reslove(true)
  17 + }
  18 + })
  19 + });
  20 + }
  21 +
  22 + static makeDirIfNotExsit(path: string) : Promise<void> {
  23 + return new Promise((reslove, fail) => {
  24 + fs.stat(path).then(() => {
  25 + reslove()
  26 + }).catch((error: BusinessError) => {
  27 + if (error.code = 13900002) {
  28 + fs.mkdirSync(path)
  29 + }
  30 + reslove()
  31 + })
  32 + })
  33 + }
  34 +}
1 import { formBindingData, FormExtensionAbility, formInfo, formProvider } from '@kit.FormKit'; 1 import { formBindingData, FormExtensionAbility, formInfo, formProvider } from '@kit.FormKit';
2 import { Want } from '@kit.AbilityKit'; 2 import { Want } from '@kit.AbilityKit';
3 -import { Logger, NetworkManager, SPHelper, StringUtils } from 'wdKit/Index'; 3 +import { FileUtils, Logger, NetworkManager, SPHelper, StringUtils } from 'wdKit/Index';
4 import { BusinessError } from '@kit.BasicServicesKit'; 4 import { BusinessError } from '@kit.BasicServicesKit';
5 -import { FormDataType, NewspaperDataFetcher } from './NewspaperDataFetcher'; 5 +import { NewspaperDataFetcher } from './NewspaperDataFetcher';
6 import { JSON } from '@kit.ArkTS'; 6 import { JSON } from '@kit.ArkTS';
7 -import { FormNewspaperPaperType } from '../dailynewspaperwidget/common/NewspaperWidgetData'; 7 +import { FormNewspaperData, FormNewspaperPaperType } from '../dailynewspaperwidget/common/NewspaperWidgetData';
8 import { HostEnum, HostManager, WDHttp } from 'wdNetwork/Index'; 8 import { HostEnum, HostManager, WDHttp } from 'wdNetwork/Index';
  9 +import fs from '@ohos.file.fs';
9 10
10 const TAG = "DailyNewspaperFormAbility" 11 const TAG = "DailyNewspaperFormAbility"
11 12
12 export default class DailyNewspaperFormAbility extends FormExtensionAbility { 13 export default class DailyNewspaperFormAbility extends FormExtensionAbility {
  14 + lastDatas: Record<string, FormNewspaperData> = {}
  15 +
13 onAddForm(want: Want) { 16 onAddForm(want: Want) {
14 Logger.debug(TAG, "onAddForm with " + JSON.stringify(want)) 17 Logger.debug(TAG, "onAddForm with " + JSON.stringify(want))
15 18
@@ -20,18 +23,11 @@ export default class DailyNewspaperFormAbility extends FormExtensionAbility { @@ -20,18 +23,11 @@ export default class DailyNewspaperFormAbility extends FormExtensionAbility {
20 let isTempCard = want.parameters[formInfo.FormParam.TEMPORARY_KEY] as boolean 23 let isTempCard = want.parameters[formInfo.FormParam.TEMPORARY_KEY] as boolean
21 if (isTempCard === false) { // 如果为常态卡片,直接进行信息持久化 24 if (isTempCard === false) { // 如果为常态卡片,直接进行信息持久化
22 25
23 - Logger.debug(TAG, "开始刷新数据");  
24 - NewspaperDataFetcher.refreshDailyPaper().then((data) => {  
25 -  
26 - let formData = formBindingData.createFormBindingData(data);  
27 - formProvider.updateForm(formId, formData).catch((err: BusinessError) => {  
28 - Logger.debug(TAG, ` xFailed to updateForm. Code: ${err.code}, message: ${err.message}`);  
29 - });  
30 - }) 26 + this.fetchAndRefreshData(formId)
31 } 27 }
32 } 28 }
33 29
34 - let obj: FormDataType = {} 30 + let obj: FormNewspaperData = {} as FormNewspaperData
35 obj.paperType = FormNewspaperPaperType.unknown 31 obj.paperType = FormNewspaperPaperType.unknown
36 let formData = formBindingData.createFormBindingData(obj); 32 let formData = formBindingData.createFormBindingData(obj);
37 return formData; 33 return formData;
@@ -45,13 +41,9 @@ export default class DailyNewspaperFormAbility extends FormExtensionAbility { @@ -45,13 +41,9 @@ export default class DailyNewspaperFormAbility extends FormExtensionAbility {
45 onUpdateForm(formId: string) { 41 onUpdateForm(formId: string) {
46 // 若卡片支持定时更新/定点更新/卡片使用方主动请求更新功能,则提供方需要重写该方法以支持数据更新 42 // 若卡片支持定时更新/定点更新/卡片使用方主动请求更新功能,则提供方需要重写该方法以支持数据更新
47 Logger.debug(TAG, 'onUpdateForm ' + formId); 43 Logger.debug(TAG, 'onUpdateForm ' + formId);
48 - NewspaperDataFetcher.refreshDailyPaper().then((data) => {  
49 44
50 - let formData = formBindingData.createFormBindingData(data);  
51 - formProvider.updateForm(formId, formData).catch((err: BusinessError) => {  
52 - Logger.debug(TAG, ` xFailed to updateForm. Code: ${err.code}, message: ${err.message}`);  
53 - });  
54 - }) 45 + this.initApp()
  46 + this.fetchAndRefreshData(formId)
55 } 47 }
56 48
57 onFormEvent(formId: string, message: string) { 49 onFormEvent(formId: string, message: string) {
@@ -61,7 +53,9 @@ export default class DailyNewspaperFormAbility extends FormExtensionAbility { @@ -61,7 +53,9 @@ export default class DailyNewspaperFormAbility extends FormExtensionAbility {
61 53
62 onRemoveForm(formId: string) { 54 onRemoveForm(formId: string) {
63 // 当对应的卡片删除时触发的回调,入参是被删除的卡片ID 55 // 当对应的卡片删除时触发的回调,入参是被删除的卡片ID
64 - Logger.debug(TAG, 'onRemoveForm'); 56 + Logger.debug(TAG, 'onRemoveForm / formId: ' + formId);
  57 + let data = this.lastDatas[formId]
  58 + NewspaperDataFetcher.closeFilesWith(data)
65 } 59 }
66 60
67 // onConfigurationUpdate(config: Configuration) { 61 // onConfigurationUpdate(config: Configuration) {
@@ -75,6 +69,32 @@ export default class DailyNewspaperFormAbility extends FormExtensionAbility { @@ -75,6 +69,32 @@ export default class DailyNewspaperFormAbility extends FormExtensionAbility {
75 return formInfo.FormState.READY; 69 return formInfo.FormState.READY;
76 } 70 }
77 71
  72 + fetchAndRefreshData(formId: string) {
  73 + Logger.debug(TAG, "开始刷新数据");
  74 + NewspaperDataFetcher.refreshDailyPaper().then(async (data) => {
  75 +
  76 + let formData = formBindingData.createFormBindingData(data);
  77 + formProvider.updateForm(formId, formData).then(() => {
  78 + this.lastDatas[formId] = data
  79 + }).catch((err: BusinessError) => {
  80 + Logger.debug(TAG, ` xFailed to updateForm. Code: ${err.code}, message: ${err.message}`);
  81 + });
  82 +
  83 + let fileDir = this.context.getApplicationContext().filesDir + "/widget-daily-newspaper"
  84 + await FileUtils.makeDirIfNotExsit(fileDir)
  85 +
  86 + NewspaperDataFetcher.dealWithPictures(data, formId, fileDir, (data) => {
  87 + Logger.debug(TAG, `refresh ui with new pictuers`);
  88 + let formData = formBindingData.createFormBindingData(data);
  89 + formProvider.updateForm(formId, formData).then(() => {
  90 + this.lastDatas[formId] = data
  91 + }).catch((err: BusinessError) => {
  92 + Logger.error(TAG, ` xFailed to updateForm. Code: ${err.code}, message: ${err.message}`);
  93 + });
  94 + })
  95 + })
  96 + }
  97 +
78 initApp() { 98 initApp() {
79 // KV存储 99 // KV存储
80 SPHelper.init(this.context); 100 SPHelper.init(this.context);
1 import { CompInfoBean, ContentDTO, PageInfoBean } from 'wdBean/Index'; 1 import { CompInfoBean, ContentDTO, PageInfoBean } from 'wdBean/Index';
2 import { MorningEveningViewModel } from 'wdComponent/Index'; 2 import { MorningEveningViewModel } from 'wdComponent/Index';
3 -import { Logger } from 'wdKit/Index'; 3 +import { CrptoUtils, FileUtils, Logger } from 'wdKit/Index';
4 import { HttpUrlUtils, ResponseDTO, WDHttp } from 'wdNetwork/Index'; 4 import { HttpUrlUtils, ResponseDTO, WDHttp } from 'wdNetwork/Index';
5 import { DeepLinkUtil } from 'wdShare/Index' 5 import { DeepLinkUtil } from 'wdShare/Index'
6 -import { FormNewspaperPaperType, FormNewspaperPaperInfo, FormNewspaperPaperContent } from "../dailynewspaperwidget/common/NewspaperWidgetData" 6 +import { FormNewspaperPaperType, FormNewspaperPaperInfo, FormNewspaperPaperContent,
  7 + FormNewspaperData } from "../dailynewspaperwidget/common/NewspaperWidgetData"
  8 +import { http } from '@kit.NetworkKit';
  9 +import fs from '@ohos.file.fs';
  10 +import { BusinessError } from '@kit.BasicServicesKit';
  11 +import { JSON } from '@kit.ArkTS';
7 12
8 const TAG = "NewspaperDataFetcher" 13 const TAG = "NewspaperDataFetcher"
9 14
10 -export type FormDataType = Record<string, FormNewspaperPaperType | FormNewspaperPaperInfo | FormNewspaperPaperContent[]> 15 +// export type FormDataType = Record<string, FormNewspaperPaperType | FormNewspaperPaperInfo | FormNewspaperPaperContent[]>
11 16
12 export class NewspaperDataFetcher { 17 export class NewspaperDataFetcher {
13 18
14 - public static async refreshDailyPaper(): Promise<FormDataType> {  
15 - return new Promise<FormDataType>(async (reslove, fail) => { 19 + public static async refreshDailyPaper(): Promise<FormNewspaperData> {
  20 + return new Promise<FormNewspaperData>(async (reslove, fail) => {
16 21
17 - let data: FormDataType = { 'paperType': FormNewspaperPaperType.unknown } 22 + let data: FormNewspaperData = new FormNewspaperData()
18 data.paperInfo = { showLeftImage: false } 23 data.paperInfo = { showLeftImage: false }
19 24
20 try { 25 try {
21 let page: PageInfoBean = await MorningEveningViewModel.getDailyPaperTopic() 26 let page: PageInfoBean = await MorningEveningViewModel.getDailyPaperTopic()
22 - data.paperType = page.topicInfo?.frontFlag || FormNewspaperPaperType.unknown 27 + data.paperType = page.topicInfo?.topicPattern || FormNewspaperPaperType.unknown
23 28
24 let currentTime = new Date().getTime() 29 let currentTime = new Date().getTime()
25 let compInfo = await MorningEveningViewModel.getMorningEveningCompInfo( 30 let compInfo = await MorningEveningViewModel.getMorningEveningCompInfo(
@@ -29,7 +34,7 @@ export class NewspaperDataFetcher { @@ -29,7 +34,7 @@ export class NewspaperDataFetcher {
29 page.topicInfo?.topicId 34 page.topicInfo?.topicId
30 ) 35 )
31 36
32 - if (page.topicInfo.frontLinkObject) { 37 + if (page.topicInfo?.frontFlag && page.topicInfo.frontLinkObject) {
33 data.paperInfo.showLeftImage = true 38 data.paperInfo.showLeftImage = true
34 data.paperInfo.leftImageUrl = page.topicInfo.frontLinkObject.coverUrl 39 data.paperInfo.leftImageUrl = page.topicInfo.frontLinkObject.coverUrl
35 data.paperInfo.leftTitle = page.topicInfo.frontLinkObject.title 40 data.paperInfo.leftTitle = page.topicInfo.frontLinkObject.title
@@ -60,7 +65,7 @@ export class NewspaperDataFetcher { @@ -60,7 +65,7 @@ export class NewspaperDataFetcher {
60 }) 65 })
61 } 66 }
62 67
63 - static fakeData(): FormDataType { 68 + static fakeData(): FormNewspaperData {
64 let data : FormNewspaperPaperContent = { 69 let data : FormNewspaperPaperContent = {
65 title: "标题标题标题标题标题标题标题标题", 70 title: "标题标题标题标题标题标题标题标题",
66 coverUrl: "https://" 71 coverUrl: "https://"
@@ -72,6 +77,135 @@ export class NewspaperDataFetcher { @@ -72,6 +77,135 @@ export class NewspaperDataFetcher {
72 leftTitle: "leftTitleleftTitleleftTitleleftTitleleftTitleleftTitle" 77 leftTitle: "leftTitleleftTitleleftTitleleftTitleleftTitleleftTitle"
73 }, 78 },
74 "paperContents": [data, data, data] 79 "paperContents": [data, data, data]
  80 + } as FormNewspaperData
  81 + }
  82 +
  83 + static dealWithPictures(data: FormNewspaperData, formId: string ,tempDir: string, refreshCallback:(data: FormNewspaperData) => void) {
  84 +
  85 + let donwloadCount = data.paperContents.filter((value) => {
  86 + return value.coverUrl && value.coverUrl.length > 0
  87 + }).length
  88 +
  89 + let fileFDs: Record<string, string | number> = {};
  90 + if (data.paperInfo.leftImageUrl) {
  91 + donwloadCount += 1
  92 +
  93 + CrptoUtils.md5(data.paperInfo.leftImageUrl).then((md5String) => {
  94 +
  95 + const fileName = formId + "file" + md5String;
  96 + const filePath = tempDir + "/" + fileName
  97 + NewspaperDataFetcher.downloadUrlToPath(data.paperInfo.leftImageUrl!, filePath).then(() => {
  98 + let file = fs.openSync(filePath)
  99 + fileFDs[fileName] = file.fd
  100 + data.paperInfo.localLeftImageFileName = fileName
  101 + data.formImages = fileFDs
  102 +
  103 + if (--donwloadCount == 0) { refreshCallback(data) }
  104 + }).catch((e : BusinessError) => {
  105 + Logger.debug(TAG, "download file failed.");
  106 + if (--donwloadCount == 0) { refreshCallback(data) }
  107 + })
  108 + }).catch((e: BusinessError) => {
  109 + if (--donwloadCount == 0) { refreshCallback(data) }
  110 + })
  111 + }
  112 +
  113 +
  114 + for (let index = 0; index < data.paperContents.length; index++) {
  115 + const content = data.paperContents[index];
  116 + if (!content.coverUrl || content.coverUrl.length == 0) {
  117 + continue
  118 + }
  119 +
  120 + CrptoUtils.md5(content.coverUrl).then((md5String) => {
  121 + const fileName = formId + "file" + md5String;
  122 + const filePath = tempDir + "/" + fileName
  123 + NewspaperDataFetcher.downloadUrlToPath(content.coverUrl!, filePath).then(() => {
  124 + Logger.debug(TAG, "open file for display ");
  125 + let file = fs.openSync(filePath)
  126 + fileFDs[fileName] = file.fd
  127 + data.paperContents[index].localCoverFileName = fileName
  128 + data.formImages = fileFDs
  129 +
  130 + if (--donwloadCount == 0) { refreshCallback(data) }
  131 + }).catch((e : BusinessError) => {
  132 + Logger.debug(TAG, "download file failed." + JSON.stringify(e));
  133 + if (--donwloadCount == 0) { refreshCallback(data) }
  134 + })
  135 + }).catch((e: BusinessError) => {
  136 + if (--donwloadCount == 0) { refreshCallback(data) }
  137 + })
  138 + }
  139 + }
  140 +
  141 + static closeFilesWith(data?: FormNewspaperData) {
  142 + if (data && data.formImages) {
  143 + for (const obj of Object.entries(data.formImages)) {
  144 + const fileName = obj[0]
  145 + let fd: number = obj[1] as number
  146 + fs.close(fd)
  147 + }
75 } 148 }
76 } 149 }
  150 +
  151 + static downloadUrlToPath(url: string, toFilePath: string): Promise<void> {
  152 + Logger.debug(TAG, "will donwload url:" + url + " ======> " + toFilePath);
  153 +
  154 + return new Promise((reslove, fail) => {
  155 +
  156 + FileUtils.fileExsit(toFilePath).then((exsit: boolean) => {
  157 + if (exsit) {
  158 + Logger.debug(TAG, "file is exsit. " + toFilePath);
  159 + reslove()
  160 + return
  161 + }
  162 +
  163 + NewspaperDataFetcher.downloadUrlToPathWithout(url, toFilePath).then(() => {
  164 + reslove()
  165 + }).catch((e: BusinessError) => {
  166 + fail(e)
  167 + })
  168 + }).catch((e: BusinessError) => {
  169 + NewspaperDataFetcher.downloadUrlToPathWithout(url, toFilePath).then(() => {
  170 + reslove()
  171 + }).catch((e: BusinessError) => {
  172 + fail(e)
  173 + })
  174 + })
  175 + })
  176 + }
  177 +
  178 + static downloadUrlToPathWithout(url: string, toFilePath: string): Promise<void> {
  179 + Logger.debug(TAG, "will donwload url:" + url + " ======> " + toFilePath);
  180 +
  181 + return new Promise((reslove, fail) => {
  182 +
  183 + let httpRequest = http.createHttp()
  184 + httpRequest.request(url, (err, data) => {
  185 +
  186 + if (!err && data.responseCode == http.ResponseCode.OK) {
  187 + let imgFile = fs.openSync(toFilePath, fs.OpenMode.READ_WRITE | fs.OpenMode.CREATE);
  188 + let wroteLength = 0
  189 + fs.write(imgFile.fd, data.result as ArrayBuffer).then((writeLen: number) => {
  190 + Logger.debug(TAG, "write data to file succeed and size is:" + writeLen);
  191 + wroteLength = writeLen
  192 + }).catch((err: BusinessError) => {
  193 + Logger.error(TAG, "write data to file failed with error message: " + err.message + ", error code: " + err.code);
  194 + }).finally(() => {
  195 + fs.closeSync(imgFile);
  196 + httpRequest.destroy()
  197 +
  198 + wroteLength > 0 ? reslove() : fail("failed")
  199 + });
  200 + return
  201 + }
  202 +
  203 + httpRequest.destroy()
  204 + fail("failed")
  205 + });
  206 +
  207 + })
  208 + }
  209 +
  210 +
77 } 211 }
@@ -11,6 +11,8 @@ export class FormNewspaperPaperInfo { @@ -11,6 +11,8 @@ export class FormNewspaperPaperInfo {
11 showLeftImage: boolean = false 11 showLeftImage: boolean = false
12 12
13 leftImageUrl?: string 13 leftImageUrl?: string
  14 + localLeftImageFileName?: ResourceStr // 传递图片用
  15 + localLeftImageFileFD? : number // 传递图片用
14 leftTitle?: string 16 leftTitle?: string
15 leftDeepLink?: string 17 leftDeepLink?: string
16 18
@@ -20,5 +22,15 @@ export class FormNewspaperPaperInfo { @@ -20,5 +22,15 @@ export class FormNewspaperPaperInfo {
20 export class FormNewspaperPaperContent { 22 export class FormNewspaperPaperContent {
21 title: string = "" 23 title: string = ""
22 coverUrl?: string 24 coverUrl?: string
  25 + localCoverFileName?: ResourceStr // 传递图片用
  26 + localCoverFileFD? : number // 传递图片用
23 deepLink: string = "" 27 deepLink: string = ""
24 } 28 }
  29 +
  30 +export class FormNewspaperData {
  31 + paperType: FormNewspaperPaperType = FormNewspaperPaperType.unknown
  32 + paperInfo: FormNewspaperPaperInfo = {} as FormNewspaperPaperInfo
  33 + paperContents: FormNewspaperPaperContent[] = []
  34 +
  35 + formImages: Record<string, string | number> = {}
  36 +}
@@ -43,8 +43,8 @@ struct DailyNewspaperWidgetCard { @@ -43,8 +43,8 @@ struct DailyNewspaperWidgetCard {
43 43
44 @Builder leftImageView() { 44 @Builder leftImageView() {
45 Stack({ alignContent: Alignment.Bottom }) { 45 Stack({ alignContent: Alignment.Bottom }) {
46 - Image(this.paperInfo.leftImageUrl)  
47 - // Image($r("app.media.desktop_card_comp_place_holder_16_9")) 46 + Image("memory://" + this.paperInfo.localLeftImageFileName)
  47 + // Image(this.paperInfo.leftImageUrl)
48 .alt($r("app.media.desktop_card_comp_place_holder_16_9")) 48 .alt($r("app.media.desktop_card_comp_place_holder_16_9"))
49 .objectFit(ImageFit.Cover) 49 .objectFit(ImageFit.Cover)
50 .aspectRatio(87/116) 50 .aspectRatio(87/116)
@@ -109,8 +109,8 @@ struct ContentCellView { @@ -109,8 +109,8 @@ struct ContentCellView {
109 bottom: this.hasImage ? 0 : 6}) 109 bottom: this.hasImage ? 0 : 6})
110 110
111 if (this.hasImage) { 111 if (this.hasImage) {
112 - Image(this.content.coverUrl)  
113 - // Image($r("app.media.desktop_card_comp_place_holder_3_4")) 112 + Image("memory://" + this.content.localCoverFileName)
  113 + // Image(this.content.coverUrl)
114 .alt($r("app.media.desktop_card_comp_place_holder_3_4")) 114 .alt($r("app.media.desktop_card_comp_place_holder_3_4"))
115 .objectFit(ImageFit.Cover) 115 .objectFit(ImageFit.Cover)
116 .height(40) 116 .height(40)
@@ -14,8 +14,8 @@ @@ -14,8 +14,8 @@
14 "isDynamic": true, 14 "isDynamic": true,
15 "isDefault": true, 15 "isDefault": true,
16 "updateEnabled": true, 16 "updateEnabled": true,
17 - "scheduledUpdateTime": "10:30",  
18 - "updateDuration": 1, 17 + "scheduledUpdateTime": "18:26",
  18 + "updateDuration": 4,
19 "defaultDimension": "2*4", 19 "defaultDimension": "2*4",
20 "supportDimensions": [ 20 "supportDimensions": [
21 "2*4" 21 "2*4"