1/*
2 * Copyright (c) 2024 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 display from '@ohos.display';
17import settings from '@ohos.settings';
18import UIExtensionContentSession from '@ohos.app.ability.UIExtensionContentSession';
19import deviceInfo from '@ohos.deviceInfo';
20import { BusinessError } from '@ohos.base';
21import { EditableLeftIconType, EditableTitleBar } from '@ohos.arkui.advanced.EditableTitleBar';
22import mediaQuery from '@ohos.mediaquery';
23import ConfigurationConstant from '@ohos.app.ability.ConfigurationConstant';
24import CommonConstants, { FontSizeScale } from '../common/constants/CommonConstants';
25import { logger } from '../utils/Logger';
26import { TipsJumpUtils } from '../utils/TipsJumpUtils';
27import osAccount from '@ohos.account.osAccount';
28import systemParameterEnhance from '@ohos.systemParameterEnhance';
29import i18n from '@ohos.i18n';
30
31const TAG = '[ContinueSwitch_Page] : ';
32let context = getContext(this) as common.UIAbilityContext;
33let localStorage = LocalStorage.getShared();
34
35interface switchStatus {
36  open: string;
37  close: string;
38}
39
40let switchState: switchStatus = {
41  open: CommonConstants.SWITCH_STATUS_OPEN,
42  close: CommonConstants.SWITCH_STATUS_CLOSE
43}
44
45@Entry
46@Component
47struct ContinueSwitch {
48  @StorageLink('isSwitchOn') isSwitchOn: boolean | undefined = true;
49  @StorageLink('continueSession') continueSession: UIExtensionContentSession | undefined = undefined;
50  @State title: string = '';
51  @State screenHeight: number = 0;
52  @State screenWidth: number = 0;
53  @State shortSideSize: number = 0;
54  @State imageAnimatorHeight: number = this.getImageAnimatorHeight();
55  @State imageAnimatorWidth: number = this.getImageAnimatorWidth();
56  @State textWidth: number = 0;
57  @State gapLength: number = 0;
58  @State isShow: boolean = false;
59  @State is2in1: boolean = false;
60  @State portraitFunc: mediaQuery.MediaQueryResult | void | null = null;
61  @State isVideoVisible: Visibility = Visibility.Hidden;
62  @State contentHeight: number = 0;
63  @State imageArray: Array<ImageFrameInfo> = [];
64  @State animationState: AnimationStatus = AnimationStatus.Running;
65  @State reverse: boolean = false;
66  @State iterations: number = -1;
67  @State isEnabled: boolean = true;
68  @State isShowBack: boolean = true;
69  @State isAnimatorDone: boolean = false;
70  @StorageProp('currentFontSizeScale') @Watch('onFontSizeScaleChange') fontSizeScale: number = 1;
71  @State phoneSwitchTextTopMargin: string = '17vp';
72  @State phoneSwitchTextBottomMargin: string = '18vp';
73  private listener: mediaQuery.MediaQueryListener = mediaQuery.matchMediaSync('(dark-mode:true)');
74  private extContext?: common.UIExtensionContext;
75  private scroller: Scroller = new Scroller();
76  private learnMore: ResourceStr = $r('app.string.learn_more');
77  private continueDesc: ResourceStr = $r('app.string.continue_desc_text', '');
78  private accountManager: osAccount.AccountManager = osAccount.getAccountManager();
79  private startReason?: string = '';
80
81  onPortrait(mediaQueryResult: mediaQuery.MediaQueryResult): void {
82    logger.info(`${TAG} 'onPortrait in`);
83    if (mediaQueryResult.matches as boolean) {
84      this.extContext?.getApplicationContext().setColorMode(ConfigurationConstant.ColorMode.COLOR_MODE_DARK);
85    } else {
86      this.extContext?.getApplicationContext().setColorMode(ConfigurationConstant.ColorMode.COLOR_MODE_LIGHT);
87    }
88  }
89
90  onFontSizeScaleChange(): void {
91    logger.info(`${TAG} onFontSizeScaleChange`);
92    this.phoneSwitchTextTopMargin = this.UpdateMarginBasedOnFontSize(17, this.fontSizeScale);
93    this.phoneSwitchTextBottomMargin = this.UpdateMarginBasedOnFontSize(18, this.fontSizeScale);
94  }
95
96  /**
97   * Update the margins of the switch list according to the font size.
98   */
99  public UpdateMarginBasedOnFontSize(fontFp: number, fontSizeScale: number): string {
100    logger.info(`${TAG} getlistSpace, fontSizeScale: ${fontSizeScale} ; fontFp: ${fontFp}`);
101    switch (fontSizeScale) {
102      case FontSizeScale.XXL1:
103        return '16vp';
104      case FontSizeScale.XXL2:
105        return '20vp';
106      case FontSizeScale.XXL3:
107        return '24vp';
108      default:
109        return `${fontFp}vp`;
110    }
111  }
112
113  /**
114   *  Initialize the switch list spacing size
115   */
116  public phoneSwitchTextMarginInit(): void {
117    let fontSizeScale = parseFloat(systemParameterEnhance.getSync(CommonConstants.FONT_SIZE_SCALE_PARAM, '1'));
118    this.phoneSwitchTextTopMargin = this.UpdateMarginBasedOnFontSize(17, fontSizeScale);
119    this.phoneSwitchTextBottomMargin = this.UpdateMarginBasedOnFontSize(18, fontSizeScale);
120  }
121
122  getStringSync(): void {
123    logger.info(`${TAG} getStringSync in`);
124    try {
125      context.resourceManager.getStringValue($r('app.string.continue_title')
126        .id, (error: BusinessError, value: string) => {
127        if (error != null) {
128          logger.error(TAG + 'error is ' + error);
129        } else {
130          this.title = value;
131          logger.info(`${TAG} <aboutToAppear> this.title : ${this.title}`);
132        }
133      })
134    } catch (error) {
135      let code: number = (error as BusinessError).code;
136      let message: string = (error as BusinessError).message;
137      logger.error(`${TAG} callback getStringValue failed,error code: ${code},message: ${message}.`);
138    }
139  }
140
141  getImageArray(): void {
142    logger.info(`${TAG} getImageArray in`);
143    for (let i = 0; i <= CommonConstants.IMAGE_COUNT; ++i) {
144      this.imageArray.push({
145        src: $r(`app.media.continue_${i}`),
146        duration: (i == CommonConstants.IMAGE_COUNT) ? CommonConstants.IMG_ANIMATOR_OVER_DURATION
147          : CommonConstants.IMG_ANIMATOR_NORMAL_DURATION
148      })
149    }
150  }
151
152  getGapLength(): void {
153    logger.info(`${TAG} getGapLength in, deviceInfo.deviceType : ${deviceInfo.deviceType}`);
154    if (deviceInfo.deviceType == 'phone') {
155      this.gapLength = CommonConstants.GENERAL_PHONE_GAP_LENGTH;
156    } else if (deviceInfo.deviceType == '2in1' || deviceInfo.deviceType == 'tablet') {
157      this.gapLength = CommonConstants.PC_PAD_GAP_LENGTH;
158    }
159    logger.info(`${TAG} this.gapLength : ${this.gapLength}`);
160  }
161
162  getForegroundOsAccountLocalId(): void {
163    logger.info(`${TAG} getForegroundOsAccountLocalId in`);
164    try {
165      this.accountManager.getForegroundOsAccountLocalId().then((localId: number) => {
166        logger.info(`${TAG} getForegroundOsAccountLocalId, localId: ${localId}`);
167        this.getAccountInfo(localId);
168      }).catch((err: BusinessError) => {
169        logger.error(`${TAG} getForegroundOsAccountLocalId errCode: ${err?.code}`);
170      });
171    } catch (err) {
172      logger.error(`${TAG} getForegroundOsAccountLocalId exception: ${err?.message}`);
173    }
174  }
175
176  getAccountInfo(localId: number): void {
177    logger.info(`${TAG} getAccountInfo in`);
178    try {
179      this.accountManager.queryOsAccountById(localId).then((accountInfo: osAccount.OsAccountInfo) => {
180        logger.info(`${TAG} queryOsAccountById, accountInfo.type: ${accountInfo.type}`);
181        if (accountInfo.type === osAccount.OsAccountType.PRIVATE) {
182          let status: boolean = settings.setValueSync(context, CommonConstants.CONTINUE_SWITCH_KEY, switchState.close,
183            settings.domainName.USER_SECURITY);
184          this.isSwitchOn = false;
185          this.isEnabled = false;
186          logger.info(`${TAG} set value isSuccess : status = ${status}; set:Continue_Switch_Status is 0`);
187        }
188      }).catch((err: BusinessError) => {
189        logger.error(`${TAG} queryOsAccountById errCode: ${err?.code}`);
190      });
191    } catch (err) {
192      logger.error(`${TAG} queryOsAccountById exception: ${err?.message}`);
193    }
194  }
195
196  onPageShow() {
197    logger.info(`${TAG} onPageShow in`);
198    this.getGapLength();
199    display.getAllDisplays((err, data) => {
200      this.screenWidth = px2vp(data[0].width);
201      this.screenHeight = px2vp(data[0].height);
202      this.contentHeight = this.screenHeight;
203      logger.info(`${TAG} screenWidth = ${this.screenWidth}; screenHeight = ${this.screenHeight}`);
204    })
205    this.is2in1 = deviceInfo.deviceType === '2in1' ? true : false;
206  }
207
208  aboutToAppear() {
209    logger.info(`${TAG} aboutToAppear in`);
210    // Switch State Initialization
211    let value = settings.getValueSync(context, CommonConstants.CONTINUE_SWITCH_KEY, switchState.open,
212      settings.domainName.USER_SECURITY);
213    this.isSwitchOn = value != switchState.close ? true : false;
214    logger.info(`${TAG} <aboutToAppear> this.isSwitchOn : ${this.isSwitchOn}; value: ${value}`);
215
216    AppStorage.setOrCreate('isSwitchOn', this.isSwitchOn);
217    logger.info(`${TAG} AppStorage.get<boolean>(isSwitchOn) : ${AppStorage.get<boolean>('isSwitchOn')}`);
218
219    if (this.isSwitchOn) {
220      let status: boolean = settings.setValueSync(context, CommonConstants.CONTINUE_SWITCH_KEY, switchState.open,
221        settings.domainName.USER_SECURITY);
222      logger.info(`${TAG} set value success :${status}; set:Continue_Switch_Status is 1`);
223    }
224
225    this.getStringSync();
226    this.getImageArray();
227    this.getForegroundOsAccountLocalId();
228    this.listener.on('change', (mediaQueryResult: mediaQuery.MediaQueryResult) => {
229      this.onPortrait(mediaQueryResult);
230    })
231    this.extContext = localStorage.get<common.UIExtensionContext>('context');
232    this.startReason = AppStorage.get<string>('startReason');
233    this.isShowBack = AppStorage.get<boolean>('isShowBack') ?? true;
234    logger.info(`${TAG} aboutToAppear: startReason is ${this.startReason}, isShowBack: ${this.isShowBack}`);
235    if (this.isPhone()) {
236      this.checkFoldBackButton();
237    }
238    this.checkPcPadBackButton();
239    this.phoneSwitchTextMarginInit();
240    setTimeout(() => {
241      this.isAnimatorDone = true;
242    }, 20)
243  }
244
245  aboutToDisappear() {
246    logger.info(`${TAG} aboutToDisappear in`);
247  }
248
249  onBackPress() {
250    logger.info(`${TAG} onBackPress in`);
251  }
252
253  @Builder
254  NormalRootContent() {
255    this.titleBar();
256    this.ContentBuilder();
257  }
258
259  @Builder
260  SearchRootContent() {
261    NavDestination() {
262      this.ContentBuilder();
263    }
264    .hideTitleBar(false)
265    .title(this.title)
266    .backgroundColor($r('sys.color.ohos_id_color_titlebar_sub_bg'))
267  }
268
269  @Builder
270  titleBar() {
271    Column() {
272      EditableTitleBar({
273        leftIconStyle: EditableLeftIconType.Back,
274        title: $r('app.string.continue_title'),
275        isSaveIconRequired: false,
276        onCancel: () => {
277          if (this.continueSession) {
278            this.continueSession.sendData({ 'action': 'pop' })
279          } else {
280            logger.error(`${TAG} continueSession is undefined`);
281          }
282        }
283      })
284    }
285  }
286
287  @Builder
288  ContentBuilder() {
289    Scroll(this.scroller) {
290      Column() {
291        ImageAnimator()
292          .images(this.imageArray)
293          .state(this.animationState)
294          .reverse(this.reverse)
295          .fillMode(this.iterations)
296          .iterations(this.iterations)
297          .width(this.imageAnimatorWidth)
298          .height(this.imageAnimatorHeight)
299          .onStart(() => {
300            logger.info(`${TAG} ImageAnimator Start`);
301          })
302          .onFinish(() => {
303            logger.info(`${TAG} ImageAnimator Finish`);
304          })
305
306        Text() {
307          Span(this.continueDesc)
308            .fontFamily('HarmonyHeiTi')
309            .fontSize($r('sys.float.ohos_id_text_size_body2'))
310            .fontColor($r('sys.color.ohos_id_color_text_secondary'))
311            .fontWeight(FontWeight.Regular)
312          Span(this.learnMore)
313            .fontFamily('HarmonyHeiTi')
314            .fontSize($r('sys.float.ohos_id_text_size_body2'))
315            .fontColor($r('sys.color.ohos_id_color_text_primary_activated'))
316            .fontWeight(FontWeight.Medium)
317            .onClick(() => {
318              TipsJumpUtils.jumpTips(getContext(this) as common.UIAbilityContext, CommonConstants.FUN_NUM,
319                CommonConstants.TIPS_TYPE);
320            })
321        }
322        .margin({
323          bottom: CommonConstants.CONTINUE_DESC_TEXT_MARGIN_BOTTOM,
324          top: CommonConstants.CONTINUE_DESC_TEXT_MARGIN_TOP
325        })
326        .textAlign(TextAlign.Center)
327        .width('100%')
328
329        Column() {
330          Flex({
331            direction: FlexDirection.Row,
332            justifyContent: FlexAlign.SpaceBetween,
333            alignItems: ItemAlign.Center
334          }) {
335            Text($r('app.string.continue_title'))
336              .fontSize($r('sys.float.ohos_id_text_size_sub_title2'))
337              .fontWeight(FontWeight.Medium)
338              .fontColor($r('sys.color.ohos_id_color_text_primary'))
339              .accessibilityLevel('no')
340              .padding({
341                top: this.is2in1 ? CommonConstants.ITEM_LIST_PADDING_TOP_PC : this.phoneSwitchTextTopMargin,
342                bottom: this.is2in1 ? CommonConstants.ITEM_LIST_PADDING_BOTTOM_PC : this.phoneSwitchTextBottomMargin
343              })
344
345            Toggle({ type: ToggleType.Switch, isOn: this.isSwitchOn })
346              .width(CommonConstants.CONTINUE_SWITCH_WIDTH)
347              .height(CommonConstants.CONTINUE_SWITCH_HEIGHT)
348              .hoverEffect(HoverEffect.None)
349              .enabled(this.isEnabled)
350              .onChange((isOn: boolean) => {
351                logger.info(`${TAG} isOn: ${isOn}`);
352                this.isSwitchOn = isOn;
353                AppStorage.setAndLink('isSwitchOn', isOn);
354                if (isOn) {
355                  let status: boolean = settings.setValueSync(context, CommonConstants.CONTINUE_SWITCH_KEY,
356                    switchState.open, settings.domainName.USER_SECURITY);
357                  logger.info(`${TAG} is set success :${status}; set:Continue_Switch_Status is open`);
358                } else {
359                  let status: boolean = settings.setValueSync(context, CommonConstants.CONTINUE_SWITCH_KEY,
360                    switchState.close, settings.domainName.USER_SECURITY);
361                  logger.info(`${TAG} is set success :${status}; set:Continue_Switch_Status is close`);
362                }
363              })
364          }
365          .width('100%')
366          .padding({
367            left: CommonConstants.TEXT_LIST_ALIGN_DISTANCE,
368            right: CommonConstants.TEXT_LIST_ALIGN_DISTANCE
369          })
370          .backgroundColor($r('sys.color.ohos_id_color_list_card_bg'))
371          .borderRadius(this.is2in1 ? CommonConstants.PC_BORDER_RADIUS : CommonConstants.NON_PC_BORDER_RADIUS)
372          .accessibilityText(this.title)
373        }
374        .width('100%')
375        .constraintSize({
376          minHeight: CommonConstants.PC_LIST_HEIGHT
377        })
378
379        Column() {
380          Flex({
381            direction: FlexDirection.Row,
382            justifyContent: FlexAlign.Start,
383            alignItems: ItemAlign.Center
384          }) {
385            SymbolGlyph($r('sys.symbol.info_circle_fill'))
386              .fontWeight(FontWeight.Medium)
387              .fontSize(CommonConstants.SYMBOL_INFO_CIRCLE)
388              .fontColor([$r('sys.color.ohos_id_color_activated')])
389              .margin(this.isMirrorLanguages() ? { left: CommonConstants.SYMBOL_MARGIN_RIGHT } :
390                { right: CommonConstants.SYMBOL_MARGIN_RIGHT })
391              .width(CommonConstants.SYMBOL_INFO_CIRCLE)
392              .height(CommonConstants.SYMBOL_INFO_CIRCLE)
393              .accessibilityLevel('no')
394
395            Text($r('app.string.update_version_prompt'))
396              .fontSize($r('sys.float.ohos_id_text_size_body3'))
397              .fontWeight(FontWeight.Medium)
398              .fontColor($r('sys.color.ohos_id_color_text_primary'))
399              .textAlign(TextAlign.Start)
400              .lineHeight(-1)
401          }
402          .margin({ top: CommonConstants.UPDATE_PROMPT_MARGIN_TOP })
403        }
404        .padding({
405          left: CommonConstants.TEXT_LIST_ALIGN_DISTANCE,
406          right: CommonConstants.TEXT_LIST_ALIGN_DISTANCE
407        })
408
409        Column() {
410          Row() {
411            Text($r('app.string.continue_privacy_text'))
412              .fontSize($r('sys.float.ohos_id_text_size_body3'))
413              .fontWeight(FontWeight.Regular)
414              .margin({ top: CommonConstants.CONTINUE_PRIVACY_TEXT_MARGIN_TOP })
415              .fontColor($r('sys.color.ohos_id_color_text_secondary'))
416              .textAlign(TextAlign.Start)
417              .width('100%')
418              .lineHeight(this.isTibetanLanguages() ? 19 : 0)
419          }
420          .padding({
421            left: CommonConstants.TEXT_LIST_ALIGN_DISTANCE,
422            right: CommonConstants.TEXT_LIST_ALIGN_DISTANCE
423          })
424        }.width('100%')
425
426      }
427      .width('100%')
428      .padding({ left: this.gapLength, right: this.gapLength })
429      .margin({ bottom: this.contentHeight * 0.09 })
430      .backgroundColor($r('sys.color.ohos_id_color_sub_background'))
431      .visibility(this.getContentVisibility())
432    }
433    .width('100%')
434    .height(this.screenHeight)
435    .scrollable(ScrollDirection.Vertical)
436    .scrollBar(BarState.Off)
437    .align(Alignment.TopStart)
438    .friction(0.6)
439    .edgeEffect(EdgeEffect.Spring)
440    .onScrollEdge(() => {
441      logger.info('To the edge');
442    })
443    .onScrollStop(() => {
444      logger.info('Scroll Stop');
445    })
446    .onAreaChange((oldArea: Area, newArea: Area) => {
447      logger.info(`${TAG} Scroll, oldArea.height = ${oldArea.height}, newArea.height = ${newArea.height}`);
448    })
449  }
450
451  build() {
452    Column() {
453      if (this.isShowBack) {
454        this.NormalRootContent();
455      } else {
456        this.SearchRootContent();
457      }
458    }
459    .width('100%')
460    .height('100%')
461    .backgroundColor($r('sys.color.ohos_id_color_sub_background'))
462    .onAreaChange((oldArea: Area, newArea: Area) => {
463      logger.info(`${TAG} build column , oldArea.height = ${oldArea.height}, newArea.height = ${newArea.height}`);
464      logger.info(`${TAG} this.shortSideSize = ${this.shortSideSize}, this.imageAnimatorWidth =
465        ${this.imageAnimatorWidth}, this.imageAnimatorHeight = ${this.imageAnimatorHeight}`);
466    })
467  }
468
469  private getContentVisibility(): Visibility {
470    return (this.imageAnimatorWidth !== 0 && this.isAnimatorDone) ? Visibility.Visible : Visibility.Hidden;
471  }
472
473  private getImageAnimatorHeight(): number {
474    if (deviceInfo.deviceType === 'phone') {
475      return CommonConstants.ANIMATOR_HEIGHT_PHONE;
476    } else if (deviceInfo.deviceType === '2in1') {
477      return CommonConstants.ANIMATOR_HEIGHT_PC;
478    } else if (deviceInfo.deviceType === 'tablet') {
479      return CommonConstants.ANIMATOR_HEIGHT_PAD;
480    }
481    return CommonConstants.ANIMATOR_HEIGHT_PHONE;
482  }
483
484  private getImageAnimatorWidth(): number {
485    if (deviceInfo.deviceType === 'phone') {
486      return CommonConstants.ANIMATOR_WIDTH_PHONE;
487    } else if (deviceInfo.deviceType === '2in1') {
488      return CommonConstants.ANIMATOR_WIDTH_PC;
489    } else if (deviceInfo.deviceType === 'tablet') {
490      return CommonConstants.ANIMATOR_WIDTH_PAD;
491    }
492    return CommonConstants.ANIMATOR_WIDTH_PHONE;
493  }
494
495  private checkPcPadBackButton(): void {
496    logger.info(`${TAG} checkPcPadBackButton in`);
497    if (this.startReason === 'from_search') {
498      if (deviceInfo.deviceType === '2in1' || deviceInfo.deviceType === 'tablet') {
499        this.isShowBack = false;
500      }
501    }
502  }
503
504  private isPhone(): boolean {
505    logger.info(`${TAG} isPhone in`);
506    return (deviceInfo.deviceType === 'phone' || deviceInfo.deviceType === 'default');
507  }
508
509  private refreshFoldStatus(foldStatus: display.FoldStatus): void {
510    logger.info(`${TAG} refreshFoldStatus in. foldStatus: ${foldStatus}. startReason: ${this.startReason}`);
511    if (this.startReason === 'from_search') {
512      if (foldStatus === display.FoldStatus.FOLD_STATUS_FOLDED) {
513        this.isShowBack = true;
514        logger.info(`${TAG} foldStatus: ${foldStatus}. this.isShowBack: ${this.isShowBack}`);
515      } else {
516        this.isShowBack = false;
517        logger.info(`${TAG} foldStatus: ${foldStatus}. this.isShowBack: ${this.isShowBack}`);
518      }
519    } else {
520      this.isShowBack = true;
521      logger.info(`${TAG} startReason: ${this.startReason}. this.isShowBack: ${this.isShowBack}`);
522    }
523  }
524
525  private checkFoldBackButton(): void {
526    logger.info(`${TAG} checkFoldBackButton in`);
527    if (display.isFoldable()) {
528      try {
529        display.on('foldStatusChange', (foldStatus: display.FoldStatus) => {
530          let foldStatusValue = foldStatus.valueOf();
531          logger.info(`${TAG} checkFoldBackButton: foldStatusValue is ${foldStatusValue}`);
532          this.refreshFoldStatus(foldStatusValue);
533        })
534        let data: display.FoldStatus = display.getFoldStatus();
535        this.refreshFoldStatus(data);
536      } catch (err) {
537        logger.error(`${TAG} Register failed. exception: ${err.message}`);
538      }
539    }
540  }
541
542  private isMirrorLanguages(): boolean {
543    logger.info(`${TAG} isMirrorLanguages in`);
544    let locale = new Intl.Locale(i18n.System.getSystemLanguage()).toString();
545    return CommonConstants.MIRROR_LANGUAGES.includes(locale);
546  }
547
548  private isTibetanLanguages(): boolean {
549    logger.info(`${TAG} isTibetanLanguages in`);
550    let locale = new Intl.Locale(i18n.System.getSystemLanguage()).toString();
551    logger.info(`${TAG} isTibetanLanguages: ${locale}`);
552    return CommonConstants.TIBETAN_LANGUAGES.includes(locale);
553  }
554}