• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1/*
2 * Copyright (c) 2025 Huawei Device Co., Ltd.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 *     http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15import common from '@ohos.app.ability.common';
16import I18n from '@ohos.i18n';
17import image from '@ohos.multimedia.image';
18import uniformDataStruct from '@ohos.data.uniformDataStruct';
19import { BusinessError } from '@kit.BasicServicesKit';
20import { display } from '@kit.ArkUI';
21
22export enum FormType {
23  TYPE_BIG = 0,
24  TYPE_MID = 1,
25  TYPE_SMALL = 2
26}
27
28enum TextType {
29  TITLE = 0,
30  DESCRIPTION = 1,
31  APP_NAME = 2
32}
33
34const TAG: string = 'udmf.ContentFormCard';
35const defaultIcon: string =
36  '82,73,70,70,60,3,0,0,87,69,66,80,86,80,56,32,48,3,0,0,144,67,0,157,1,42,36,2,76,1,62,145,72,161,76,37,164,163,34,3' +
37    '4,151,40,24,176,18,9,105,110,225,117,81,27,243,141,167,87,231,251,1,151,228,76,129,74,56,124,143,240,134,221,17,24' +
38    '5,145,49,195,251,155,103,15,145,254,16,219,162,62,178,38,56,127,115,108,225,242,63,194,27,116,71,214,68,199,15,238' +
39    ',109,156,62,71,248,67,110,136,250,200,152,225,253,205,179,135,200,255,8,109,209,31,89,19,28,63,185,182,112,249,31,' +
40    '225,13,186,35,235,34,99,135,247,54,206,31,35,252,33,183,68,125,100,76,112,254,230,217,195,228,75,0,41,63,219,242,2' +
41    '38,77,44,240,251,18,157,13,186,35,235,34,99,135,247,54,206,31,35,249,8,172,169,162,121,152,235,226,174,0,65,245,14' +
42    '5,49,195,251,155,103,15,145,254,16,219,50,4,52,148,102,170,225,73,64,87,161,183,68,125,100,76,112,254,230,217,195,' +
43    '228,71,209,214,155,210,69,175,155,95,117,236,130,111,176,161,115,26,13,253,205,179,135,200,255,8,109,209,31,89,19,' +
44    '28,63,185,182,112,248,134,3,147,196,80,183,60,143,240,134,221,17,245,145,49,195,251,155,103,9,153,121,194,183,243,' +
45    '118,43,147,107,248,164,83,185,180,54,232,143,172,137,142,31,220,219,56,124,136,157,203,110,159,181,177,87,164,132,' +
46    '51,246,217,120,189,13,186,35,235,34,99,134,241,245,180,72,132,116,112,254,7,167,195,150,227,244,98,234,67,237,155,' +
47    '35,135,102,236,204,223,23,161,183,68,125,100,75,176,70,248,207,116,46,59,232,218,137,15,41,225,38,20,162,105,88,3,' +
48    '59,221,52,249,17,46,76,68,130,195,148,187,103,15,145,253,241,76,10,132,82,146,126,208,179,241,65,64,84,151,15,193,' +
49    '27,58,174,246,254,217,195,225,201,8,103,237,178,241,122,27,116,71,210,161,106,19,234,133,230,77,60,101,201,227,55,' +
50    '59,2,148,71,237,122,200,152,222,202,193,86,94,164,111,28,63,185,180,88,205,133,69,41,39,237,156,62,237,252,33,183,' +
51    '68,126,68,34,111,88,1,159,60,108,76,112,252,104,245,218,227,1,255,172,137,142,31,220,219,56,124,143,239,99,182,153' +
52    ',157,89,206,237,156,41,135,174,215,24,15,76,90,90,193,245,145,49,195,251,155,103,15,145,18,140,226,36,22,28,165,21' +
53    '8,7,174,215,23,217,167,25,36,48,125,100,76,112,254,230,217,195,196,106,61,255,30,253,149,0,0,254,254,226,128,0,0,0' +
54    ',0,0,8,43,156,5,139,91,64,214,164,5,157,168,214,71,99,143,63,110,129,210,71,53,1,30,120,20,41,161,99,5,167,202,76,' +
55    '251,103,189,240,128,146,208,198,255,248,206,215,46,193,53,91,227,66,219,241,255,4,235,164,113,76,186,21,195,174,10' +
56    ',72,252,102,101,0,19,200,26,224,13,190,145,249,137,208,169,128,196,203,52,114,184,23,26,103,126,29,119,157,143,214' +
57    ',115,91,208,138,148,47,18,132,3,189,65,160,138,162,129,225,223,121,199,68,111,66,131,240,170,9,87,178,109,244,143,' +
58    '204,78,245,205,43,87,181,148,112,162,163,53,27,128,197,247,165,165,55,37,6,212,240,48,76,139,191,173,182,51,61,7,1' +
59    '38,70,81,93,158,178,96,58,63,135,99,61,33,123,114,106,17,205,205,245,73,209,248,208,230,67,84,83,67,62,174,199,125' +
60    ',7,42,68,205,119,254,54,95,35,146,246,87,229,105,194,49,134,23,113,205,13,105,146,10,231,32,0,26,210,69,47,127,104' +
61    ',73,141,205,245,214,23,231,110,132,188,27,13,88,8,43,145,225,60,68,0,42,15,95,85,238,25,204,75,166,163,127,0,0';
62const MAX_CARD_SCALE: number = 1.2;
63const MIN_CARD_SCALE: number = 0.8;
64const SMALL_MIN_CARD_SCALE: number = 0.4;
65const DEFAULT_BIG_CARD_SIZE: number = 200;
66const DEFAULT_MID_CARD_WIDTH: number = 200;
67const DEFAULT_MID_CARD_HEIGHT: number = 100;
68const DEFAULT_SMALL_CARD_WIDTH: number = 137;
69const DEFAULT_SMALL_CARD_HEIGHT: number = 83;
70const DEFAULT_EMPTY_TITLE_WIDTH: number = 70;
71const DEFAULT_EMPTY_APPNAME_WIDTH: number = 40;
72const SMALL_EMPTY_TITLE_WIDTH: number = 50;
73const SMALL_EMPTY_APPNAME_WIDTH: number = 30;
74const DEFAULT_EMPTY_TEXT_RADIUS: number = 2;
75const DEFAULT_EMPTY_DESCRIPTION_WIDTH: string = '100%';
76const CARD_BACKGROUND: string = '#E6FFFFFF';
77const TRANSLATE_BACKGROUND: string = '#00000000';
78const APP_NAME_COLOR: string = '#99182431';
79const DEFAULT_THUMB_BACKGROUND: string = '#CCCCCC';
80const DEFAULT_FONT_BACKGROUND: string = '#55CCCCCC';
81const TITLE_FONT_COLOR: string = '#ff182431';
82const DESCRIPTION_FONT_COLOR: string = '#99182431';
83const DEFAULT_MID_IMAGE_HEIGHT: number = 72; // 4x2卡片,图片默认高度
84const DEFAULT_SMALL_IMAGE_HEIGHT: number = 59; // 2x1卡片,图片默认高度
85const DEFAULT_DENSITY: number = 3.25;
86
87interface CardStyle {
88  thumbWidth: number,
89  thumbHeight: number,
90  thumbMarginLeft?: number,
91  titleFontSize: number,
92  titleFontLineHeight: number,
93  titleFontMarginTop: number,
94  descriptionFontSize: number,
95  descriptionLineHeight: number,
96  maxDescriptionFontSize?: number,
97  maxDescriptionLineHeight?: number,
98  descriptionMarginTop: number,
99  dividerMarginTop: number,
100  appNameFontSize: number,
101  appIconSize: number,
102  cardRadius: number,
103  appNameMarginLeft: number,
104  appNameLineHeight: number,
105  cardPadding: number,
106  cardPaddingBottom: number,
107  cardPaddingTop: number
108}
109
110const BIG_CARD_STYLE: CardStyle = {
111  thumbWidth: 200,
112  thumbHeight: 120,
113  titleFontSize: 14,
114  titleFontLineHeight: 16,
115  titleFontMarginTop: 10,
116  descriptionFontSize: 14,
117  descriptionLineHeight: 16,
118  descriptionMarginTop: 4,
119  dividerMarginTop: 5,
120  appNameFontSize: 10,
121  appNameLineHeight: 14,
122  appIconSize: 12,
123  cardRadius: 16,
124  appNameMarginLeft: 6.5,
125  cardPadding: 12,
126  cardPaddingBottom: 10,
127  cardPaddingTop: 5
128}
129
130const MID_CARD_STYLE: CardStyle = {
131  thumbWidth: 36,
132  thumbHeight: 48,
133  thumbMarginLeft: 14,
134  descriptionFontSize: 10,
135  descriptionLineHeight: 12,
136  maxDescriptionFontSize: 14,
137  maxDescriptionLineHeight: 16,
138  descriptionMarginTop: 10,
139  titleFontSize: 14,
140  titleFontLineHeight: 16,
141  titleFontMarginTop: 14,
142  dividerMarginTop: 5,
143  appNameFontSize: 10,
144  appNameLineHeight: 14,
145  appIconSize: 12,
146  cardRadius: 16,
147  appNameMarginLeft: 6.5,
148  cardPadding: 12,
149  cardPaddingBottom: 10,
150  cardPaddingTop: 5
151}
152
153const SMALL_CARD_STYLE: CardStyle = {
154  thumbWidth: 24,
155  thumbHeight: 24,
156  thumbMarginLeft: 8,
157  titleFontSize: 12,
158  titleFontLineHeight: 14,
159  titleFontMarginTop: 9,
160  descriptionFontSize: 10,
161  descriptionLineHeight: 12,
162  maxDescriptionFontSize: 12,
163  maxDescriptionLineHeight: 14,
164  descriptionMarginTop: 4,
165  dividerMarginTop: 5,
166  appNameFontSize: 10,
167  appIconSize: 12,
168  cardRadius: 12,
169  appNameMarginLeft: 4,
170  appNameLineHeight: 12,
171  cardPadding: 8,
172  cardPaddingBottom: 8,
173  cardPaddingTop: 4
174}
175
176@Preview
177@Component
178export struct ContentFormCard {
179  @Prop @Watch('formTypeChange') formType: FormType = FormType.TYPE_MID;
180  private contentFormData: uniformDataStruct.ContentForm | undefined = undefined;
181  private formStyle: CardStyle = MID_CARD_STYLE;
182  private controller: TextController = new TextController();
183  @State cardScale: number = 1;
184  @Prop @Watch('formSizeChange') formWidth: number = 0;
185  @Prop @Watch('formSizeChange') formHeight: number = 0;
186  @State cardWidth: number = 0;
187  @State cardHeight: number = 0;
188  @State defaultThumbImage: image.PixelMap | undefined = undefined;
189  @State thumbImage: image.PixelMap | undefined = undefined;
190  @State appImage: image.PixelMap | undefined = undefined;
191  @State lineCount: number = 1;
192  @State isMirrorLanguageType: boolean = false;
193  private handleOnClick: () => void = () => {
194  };
195
196  aboutToAppear(): void {
197    this.initSystemLanguage();
198    this.initCardStyle();
199    this.createPixelMap();
200  }
201
202  aboutToDisappear(): void {
203    this.contentFormData = undefined;
204    this.thumbImage = undefined;
205    this.appImage = undefined;
206  }
207
208  formTypeChange(): void {
209    switch (this.formType) {
210      case FormType.TYPE_BIG:
211        this.formWidth = DEFAULT_BIG_CARD_SIZE;
212        break;
213      case FormType.TYPE_MID:
214        this.formWidth = DEFAULT_MID_CARD_WIDTH;
215        break;
216      default:
217        this.formWidth = DEFAULT_SMALL_CARD_WIDTH;
218        break;
219    }
220    this.initCardStyle();
221  }
222
223  formSizeChange(): void {
224    this.initCardStyle();
225  }
226
227  initCardScale(widthScale: number, defaultWidth: number, defaultHeight: number): void {
228    let minScale = this.formType === FormType.TYPE_SMALL ? SMALL_MIN_CARD_SCALE : MIN_CARD_SCALE;
229    if (widthScale > MAX_CARD_SCALE) {
230      this.cardScale = MAX_CARD_SCALE;
231    } else if (widthScale < minScale) {
232      this.cardScale = minScale;
233    } else {
234      this.cardScale = widthScale;
235    }
236    this.cardWidth = defaultWidth * this.cardScale;
237    this.cardHeight =
238      (this.contentFormData?.title === '' && this.formHeight > 0) ? this.formHeight : defaultHeight * this.cardScale;
239    console.info(`${TAG}, widthScale:${this.cardScale}, cardScale: ${this.cardScale}, ` +
240      `cardWidth: ${this.cardWidth}, cardHeight: ${this.cardHeight}`);
241  }
242
243  initCardStyle(): void {
244    let widthScale = 1;
245    this.lineCount = 1;
246    switch (this.formType) {
247      case FormType.TYPE_BIG:
248        this.formStyle = BIG_CARD_STYLE;
249        widthScale = this.formWidth ? this.formWidth / DEFAULT_BIG_CARD_SIZE : 1;
250        this.initCardScale(widthScale, DEFAULT_BIG_CARD_SIZE, DEFAULT_BIG_CARD_SIZE);
251        break;
252      case FormType.TYPE_MID:
253        this.formStyle = MID_CARD_STYLE;
254        widthScale = this.formWidth ? this.formWidth / DEFAULT_MID_CARD_WIDTH : 1;
255        this.initCardScale(widthScale, DEFAULT_MID_CARD_WIDTH, DEFAULT_MID_CARD_HEIGHT);
256        break;
257      default:
258        this.formStyle = SMALL_CARD_STYLE;
259        widthScale = this.formWidth ? this.formWidth / DEFAULT_SMALL_CARD_WIDTH : 1;
260        this.initCardScale(widthScale, DEFAULT_SMALL_CARD_WIDTH, DEFAULT_SMALL_CARD_HEIGHT);
261        break;
262    }
263  }
264
265  @Styles
266  thumbStyle() {
267    .width('100%')
268    .padding({
269      left: this.formStyle.cardPadding * this.cardScale,
270      right: this.formStyle.cardPadding * this.cardScale
271    })
272    .layoutWeight(1)
273  }
274
275  @Builder
276  ThumbImage() {
277    Column() {
278      if (this.formHeight > 0) {
279        Image(this.thumbImage ? this.thumbImage : this.defaultThumbImage)
280          .objectFit(ImageFit.Contain)
281          .width('100%')
282          .layoutWeight(1)
283          .draggable(false)
284          .id('cardThumbImage')
285      } else {
286        Image(this.thumbImage ? this.thumbImage : this.defaultThumbImage)
287          .objectFit(ImageFit.Contain)
288          .width('100%')
289          .aspectRatio(this.getAspectRatio())
290          .draggable(false)
291          .id('cardThumbImage')
292      }
293    }
294    .size({ width: '100%' })
295    .layoutWeight(this.formHeight > 0 ? 1 : 0)
296    .backgroundColor(this.thumbImage ? CARD_BACKGROUND : DEFAULT_THUMB_BACKGROUND)
297  }
298
299  @Builder
300  CardDivider() {
301    Divider()
302      .height(1)//.color(CARD_DIVIDER_COLOR)
303      .opacity(0.5)
304      .padding({
305        left: this.formStyle.cardPadding * this.cardScale,
306        right: this.formStyle.cardPadding * this.cardScale
307      })
308  }
309
310  @Builder
311  AppView() {
312    Row({ space: this.formStyle.appNameMarginLeft * this.cardScale }) {
313      Image(this.appImage)
314        .width(this.formStyle.appIconSize * this.cardScale)
315        .height(this.formStyle.appIconSize * this.cardScale)
316        .objectFit(ImageFit.Fill)
317        .alt($r('sys.media.ohos_app_icon'))
318        .borderRadius($r('sys.float.corner_radius_level1'))
319        .draggable(false)
320        .id('cardAppIcon')
321      Text(this.contentFormData?.appName ? this.contentFormData?.appName : ' ')
322        .fontSize(this.formStyle.appNameFontSize * this.cardScale)
323        .fontColor(APP_NAME_COLOR)
324        .maxLines(1)
325        .lineHeight(this.formStyle.appNameLineHeight * this.cardScale)
326        .textOverflow({ overflow: TextOverflow.Ellipsis })
327        .constraintSize({ minWidth: this.getTextSize(TextType.APP_NAME, this.contentFormData?.appName) })
328        .backgroundColor(this.getTextBackground(this.contentFormData?.appName))
329        .fontWeight(FontWeight.Regular)
330        .borderRadius(this.contentFormData?.title === '' ? 0 : DEFAULT_EMPTY_TEXT_RADIUS)
331        .direction(this.isMirrorLanguageType ? Direction.Rtl : Direction.Ltr)
332        .maxFontScale(1)
333        .layoutWeight(1)
334        .id('cardAppName')
335
336    }
337    .padding({
338      left: this.formStyle.cardPadding * this.cardScale,
339      right: this.formStyle.cardPadding * this.cardScale,
340      top: this.formStyle.cardPaddingTop * this.cardScale,
341      bottom: this.formStyle.cardPaddingBottom * this.cardScale,
342    })
343  }
344
345  @Builder
346  TitleText() {
347    Text(this.contentFormData?.title)
348      .fontSize(this.formStyle.titleFontSize * this.cardScale)
349      .fontColor(TITLE_FONT_COLOR)
350      .fontWeight(FontWeight.Bold)
351      .maxLines(1)
352      .textOverflow({ overflow: TextOverflow.Ellipsis })
353      .height(this.formStyle.titleFontLineHeight * this.cardScale)
354      .margin({ top: this.formStyle.titleFontMarginTop * this.cardScale })
355      .constraintSize({ minWidth: this.getTextSize(TextType.TITLE, this.contentFormData?.title) })
356      .backgroundColor(this.getTextBackground(this.contentFormData?.title))
357      .borderRadius(this.contentFormData?.title === '' ? 0 : DEFAULT_EMPTY_TEXT_RADIUS)
358      .direction(this.isMirrorLanguageType ? Direction.Rtl : Direction.Ltr)
359      .maxFontScale(1)
360      .id('cardTitleText')
361  }
362
363  @Builder
364  Card4x4() {
365    Column() {
366      Image(this.thumbImage ? this.thumbImage : this.defaultThumbImage)
367        .objectFit(ImageFit.Cover)
368        .width('100%')
369        .height(this.formStyle.thumbHeight * this.cardScale)
370        .backgroundColor(this.thumbImage ? CARD_BACKGROUND : DEFAULT_THUMB_BACKGROUND)
371        .draggable(false)
372        .id('cardThumbImage')
373      Column() {
374        this.TitleText()
375        Text(this.contentFormData?.description)
376          .fontSize(this.formStyle.descriptionFontSize * this.cardScale)
377          .fontColor(DESCRIPTION_FONT_COLOR)
378          .fontWeight(FontWeight.Regular)
379          .maxLines(1)
380          .textOverflow({ overflow: TextOverflow.Ellipsis })
381          .constraintSize({ minWidth: this.getTextSize(TextType.DESCRIPTION, this.contentFormData?.description) })
382          .height(this.formStyle.descriptionLineHeight * this.cardScale)
383          .margin({ top: this.formStyle.descriptionMarginTop * this.cardScale })
384          .backgroundColor(this.getTextBackground(this.contentFormData?.description))
385          .fontWeight(FontWeight.Regular)
386          .borderRadius(this.contentFormData?.description ? 0 : DEFAULT_EMPTY_TEXT_RADIUS)
387          .direction(this.isMirrorLanguageType ? Direction.Rtl : Direction.Ltr)
388          .maxFontScale(1)
389          .id('cardDescription')
390      }
391      .alignItems(HorizontalAlign.Start)
392      .width('100%')
393      .padding({
394        left: this.formStyle.cardPadding * this.cardScale,
395        right: this.formStyle.cardPadding * this.cardScale
396      })
397      .margin({ bottom: this.formStyle.dividerMarginTop * this.cardScale })
398      .justifyContent(FlexAlign.Center)
399
400      this.CardDivider()
401      this.AppView()
402    }
403    .size({ width: '100%', height: this.cardHeight })
404  }
405
406  @Builder
407  DescriptionView() {
408    Text(this.contentFormData?.description ? this.contentFormData?.description : ' ', { controller: this.controller })
409      .fontColor(DESCRIPTION_FONT_COLOR)
410      .fontWeight(FontWeight.Regular)
411      .maxLines(2)
412      .fontWeight(FontWeight.Regular)
413      .textOverflow({ overflow: TextOverflow.Ellipsis })
414      .lineHeight((this.lineCount === 1 ?
415        (this.formStyle.maxDescriptionLineHeight ? this.formStyle.maxDescriptionLineHeight :
416        this.formStyle.descriptionLineHeight) : this.formStyle.descriptionLineHeight) * this.cardScale)
417      .fontSize(this.getDescriptionFontSize() * this.cardScale)
418      .constraintSize({ minWidth: this.getTextSize(TextType.DESCRIPTION, this.contentFormData?.description) })
419      .backgroundColor(this.getTextBackground(this.contentFormData?.description))
420      .borderRadius(this.contentFormData?.description ? 0 : DEFAULT_EMPTY_TEXT_RADIUS)
421      .onAreaChange(() => {
422        let layoutManager: LayoutManager = this.controller.getLayoutManager();
423        this.lineCount = layoutManager.getLineCount();
424        if (layoutManager.getLineCount() === 2) {
425          this.lineCount = 2;
426        }
427      })
428      .direction(this.isMirrorLanguageType ? Direction.Rtl : Direction.Ltr)
429      .maxFontScale(1)
430      .id('cardDescription')
431  }
432
433  @Builder
434  Card4x2() {
435    Column() {
436      if (this.contentFormData?.title === '') { // show the card only thumbData
437        this.ThumbImage()
438      } else { // show the card has thumbData and title,description
439        Row() {
440          Column({ space: this.formStyle.descriptionMarginTop * this.cardScale }) {
441            this.TitleText()
442            this.DescriptionView()
443          }
444          .layoutWeight(1)
445          .alignItems(HorizontalAlign.Start)
446
447          if (this.thumbImage) {
448            Image(this.thumbImage)
449              .width(this.formStyle.thumbWidth * this.cardScale)
450              .height(this.formStyle.thumbHeight * this.cardScale)
451              .objectFit(this.thumbImage ? ImageFit.Cover : ImageFit.Contain)
452              .borderRadius(4)
453              .draggable(false)
454              .margin({
455                right: this.isMirrorLanguageType ? (this.formStyle.thumbMarginLeft as number * this.cardScale) : 0,
456                left: this.isMirrorLanguageType ? 0 : (this.formStyle.thumbMarginLeft as number * this.cardScale),
457                top: this.formStyle.titleFontMarginTop * this.cardScale
458              })
459              .id('cardThumbImage')
460          }
461        }
462        .thumbStyle()
463        .margin({ bottom: this.formStyle.dividerMarginTop * this.cardScale })
464        .alignItems(VerticalAlign.Top)
465      }
466
467      this.CardDivider();
468      this.AppView();
469    }
470    .size({ width: '100%' })
471    .constraintSize(this.getThumbViewConstraintSize())
472  }
473
474  @Builder
475  Card2x1() {
476    Column() {
477      if (this.contentFormData?.title === '') { // show the card only thumbData
478        this.ThumbImage()
479      } else {
480        Column() {
481          this.TitleText()
482          Row() {
483            Column() {
484              this.DescriptionView()
485            }
486            .layoutWeight(1)
487            .alignItems(HorizontalAlign.Start)
488
489            if (this.thumbImage) {
490              Image(this.thumbImage)
491                .objectFit(ImageFit.Cover)
492                .borderRadius($r('sys.float.corner_radius_level2'))
493                .width(this.formStyle.thumbWidth * this.cardScale)
494                .height(this.formStyle.thumbHeight * this.cardScale)
495                .draggable(false)
496                .margin({
497                  left: this.isMirrorLanguageType ? 0 : (this.formStyle.thumbMarginLeft as number * this.cardScale),
498                  right: this.isMirrorLanguageType ? (this.formStyle.thumbMarginLeft as number * this.cardScale) : 0
499                })
500                .id('cardThumbImage')
501            }
502          }
503          .margin({ top: this.formStyle.descriptionMarginTop * this.cardScale })
504          .layoutWeight(1)
505        }
506        .thumbStyle()
507        .alignItems(HorizontalAlign.Start)
508        .margin({ bottom: this.formStyle.dividerMarginTop * this.cardScale })
509      }
510      this.CardDivider()
511      this.AppView()
512    }
513    .size({ width: '100%' })
514    .constraintSize(this.getThumbViewConstraintSize())
515  }
516
517  build() {
518    Column() {
519      if (this.initSystemLanguage() && this.formType === FormType.TYPE_BIG) {
520        this.Card4x4();
521      } else if (this.formType === FormType.TYPE_MID) {
522        this.Card4x2();
523      } else {
524        this.Card2x1();
525      }
526    }
527    .borderRadius(this.formStyle.cardRadius * this.cardScale)
528    .clip(true)
529    .backgroundColor(CARD_BACKGROUND)
530    .backgroundBlurStyle(BlurStyle.COMPONENT_ULTRA_THICK,
531      { colorMode: ThemeColorMode.LIGHT, adaptiveColor: AdaptiveColor.DEFAULT, scale: 1.0 })
532    .shadow(ShadowStyle.OUTER_DEFAULT_SM)
533    .width(this.cardWidth)
534    .onClick(() => {
535      this.handleOnClick();
536      if (!this.contentFormData?.linkUri) {
537        console.warn(`${TAG}, linkUri is null`);
538        return;
539      }
540      try {
541        let context = getContext(this) as common.UIAbilityContext;
542        context.openLink(this.contentFormData?.linkUri, { appLinkingOnly: false, parameters: {} });
543      } catch (err) {
544        let error = err as BusinessError;
545        console.error(`${TAG}, Failed to openLink, code is ${error.code}, message is ${error.message}`);
546      }
547    })
548  }
549
550  initSystemLanguage(): boolean {
551    try {
552      this.isMirrorLanguageType = I18n.isRTL(I18n.System.getSystemLanguage());
553    } catch (err) {
554      let error = err as BusinessError;
555      console.error(`${TAG}, Failed to init system language, code is ${error.code}, message is ${error.message}`);
556    }
557    return true;
558  }
559
560  async getPixelMap(uint: Uint8Array, callback: Function): Promise<void> {
561    let imageResource: image.ImageSource | undefined = undefined;
562    try {
563      imageResource = image.createImageSource(uint.buffer);
564      let pixelMapData = await imageResource?.createPixelMap();
565      callback(pixelMapData);
566      imageResource.release();
567    } catch (err) {
568      let error = err as BusinessError;
569      console.error(`${TAG}, Failed to create pixelMap, code is ${error.code}, message is ${error.message}`);
570    }
571  }
572
573  transStringToUint8Array(srcData: string): Uint8Array {
574    const arr: string[] = srcData.split(',');
575    const uint8Array = new Uint8Array(arr.length);
576    arr.forEach((value, index) => {
577      uint8Array[index] = parseInt(value);
578    })
579    return uint8Array;
580  }
581
582  createPixelMap(): void {
583    let defaultThumbData = this.transStringToUint8Array(defaultIcon);
584    this.getPixelMap(defaultThumbData, (pixelMap: image.PixelMap) => {
585      this.defaultThumbImage = pixelMap;
586    })
587
588    if (this.contentFormData && this.contentFormData?.thumbData) {
589      if (!(this.contentFormData?.thumbData instanceof Uint8Array)) {
590        console.error(`${TAG}, thumbData is not Uint8Array`);
591        return;
592      }
593      this.getPixelMap(this.contentFormData?.thumbData, (pixelMap: image.PixelMap) => {
594        this.thumbImage = pixelMap;
595      })
596    }
597    if (this.contentFormData && this.contentFormData?.appIcon) {
598      if (!(this.contentFormData?.appIcon instanceof Uint8Array)) {
599        console.error(`${TAG}, appIcon is not Uint8Array`);
600        return;
601      }
602      this.getPixelMap(this.contentFormData?.appIcon, (pixelMap: image.PixelMap) => {
603        this.appImage = pixelMap;
604      })
605    }
606  }
607
608  getAspectRatio(): number {
609    let iamgeSize = this.thumbImage?.getImageInfoSync().size;
610    let defaultCardWidth = this.formType === FormType.TYPE_MID ? DEFAULT_MID_CARD_WIDTH : DEFAULT_SMALL_CARD_WIDTH;
611    let defaultImageHeight =
612      this.formType === FormType.TYPE_MID ? DEFAULT_MID_IMAGE_HEIGHT : DEFAULT_SMALL_IMAGE_HEIGHT;
613    if (iamgeSize && this.thumbImage) {
614      if ((iamgeSize.width / iamgeSize.height) > (defaultCardWidth / (defaultImageHeight * MIN_CARD_SCALE))) {
615        return defaultCardWidth / (defaultImageHeight * MIN_CARD_SCALE);
616      }
617      if ((iamgeSize.width / iamgeSize.height) < (defaultCardWidth / (defaultImageHeight * MAX_CARD_SCALE))) {
618        return defaultCardWidth / (defaultImageHeight * MAX_CARD_SCALE);
619      }
620      return iamgeSize.width / iamgeSize.height;
621    }
622    return defaultCardWidth / defaultImageHeight;
623  }
624
625  getTextBackground(text: string | undefined): string {
626    if (text && text.length > 0) {
627      return TRANSLATE_BACKGROUND;
628    }
629    return DEFAULT_FONT_BACKGROUND;
630  }
631
632  getTextSize(textType: TextType, text: string | undefined): number | string {
633    if (textType === TextType.TITLE) {
634      if (text === '' || text === undefined || text === null) {
635        if (this.formType === FormType.TYPE_SMALL) {
636          return SMALL_EMPTY_TITLE_WIDTH;
637        }
638        return DEFAULT_EMPTY_TITLE_WIDTH;
639      }
640      return DEFAULT_EMPTY_DESCRIPTION_WIDTH;
641    }
642    if (textType === TextType.APP_NAME) {
643      if (text === '' || text === undefined || text === null) {
644        if (this.formType === FormType.TYPE_SMALL) {
645          return SMALL_EMPTY_APPNAME_WIDTH;
646        }
647        return DEFAULT_EMPTY_APPNAME_WIDTH;
648      }
649      return DEFAULT_EMPTY_DESCRIPTION_WIDTH;
650    }
651    return '100%'
652  }
653
654  getThumbViewConstraintSize(): ConstraintSizeOptions {
655    if (this.contentFormData?.title !== '') {
656      return { maxHeight: this.cardHeight, minHeight: this.cardHeight };
657    } else {
658      if (this.formHeight > 0) {
659        return {
660          maxHeight: this.formHeight,
661          minHeight: this.formHeight
662        };
663      }
664      return {
665        maxHeight: this.cardHeight * MAX_CARD_SCALE,
666        minHeight: this.cardHeight * MIN_CARD_SCALE
667      };
668    }
669  }
670
671  getDescriptionFontSize(): number {
672    return this.lineCount === 1 ? (this.formStyle.maxDescriptionFontSize ? this.formStyle.maxDescriptionFontSize :
673    this.formStyle.descriptionFontSize) : this.formStyle.descriptionFontSize;
674  }
675}