• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1/*
2 * Copyright (c) 2022-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 deviceManager from '@ohos.distributedHardware.deviceManager';
16import UIExtensionContentSession from '@ohos.app.ability.UIExtensionContentSession'
17import mediaquery from '@ohos.mediaquery';
18import deviceInfo from '@ohos.deviceInfo';
19import inputMethod from '@ohos.inputMethod';
20import Constant from '../common/constant';
21import accessibility from '@ohos.accessibility';
22import common from '@ohos.app.ability.common';
23import i18n from '@ohos.i18n';
24
25let dmClass: deviceManager.DeviceManager | null;
26let TAG = '[DeviceManagerUI:InputPinDialog]==>'
27const ACTION_CANCEL_PINCODE_INPUT: number = 4
28const ACTION_DONE_PINCODE_INPUT: number = 5
29const MSG_PIN_CODE_ERROR: number = 0
30const MSG_CANCEL_PIN_CODE_INPUT: number = 3
31const MSG_DOING_AUTH: number = 4
32const MODEL_PIN: string = 'pin';
33const MODEL_PASSWORD: string = 'password';
34
35@CustomDialog
36struct InputCustomDialog {
37  @State password: string = '';
38  @State passwordCircle: string[] = ['', '', '', '', '', ''];
39  @State isTimes: number = 3;
40  @State errorTips: Resource = $r('app.plural.dm_incorrect_code', this.isTimes, this.isTimes);
41  @State errorTipsVisible: Visibility = Visibility.None;
42  @State heightNum: number = 600;
43  @State targetDeviceName: string = '';
44  @State model: string = MODEL_PIN;
45  @State isPC: boolean = false;
46  @State btnColor: ResourceColor = Color.Transparent;
47  listener: mediaquery.MediaQueryListener = mediaquery.matchMediaSync('(orientation: landscape)');
48  controller?: CustomDialogController;
49  private scroller: Scroller = new Scroller();
50
51  onPortrait(mediaQueryResult: mediaquery.MediaQueryResult) {
52    if (mediaQueryResult.matches as boolean) {
53      this.heightNum = 100;
54    } else {
55      this.heightNum = 800;
56    }
57  }
58
59  aboutToDisappear() {
60    console.info(TAG + 'InputCustomDialog aboutToDisappear');
61    let ims = inputMethod.getSetting();
62    ims.off('imeShow');
63  }
64
65  aboutToAppear() {
66    console.info(TAG + 'InputCustomDialog aboutToAppear');
67    this.isPC = Constant.isPC();
68    let ims = inputMethod.getSetting();
69    ims.on('imeShow', (info: Array<inputMethod.InputWindowInfo>) => {
70      this.scroller.scrollTo({yOffset: 72, xOffset: 0});
71    });
72    if (AppStorage.get('mediaQueryResult') != null && AppStorage.get('mediaQueryResult') as boolean) {
73      this.heightNum = 100;
74    }
75    if (AppStorage.get('targetDeviceName') != null) {
76      this.targetDeviceName = AppStorage.get('targetDeviceName') as string;
77      console.log('targetDeviceName is ' + this.targetDeviceName);
78    }
79    if (AppStorage.get('model') != null) {
80      this.model = AppStorage.get('model') as string;
81      console.log('model is ' + this.model);
82    }
83    deviceManager.createDeviceManager('com.ohos.devicemanagerui.input',
84      (err: Error, dm: deviceManager.DeviceManager) => {
85      if (err) {
86        console.log('createDeviceManager err:' + JSON.stringify(err) + '  --fail:' + '${dm}');
87        return;
88      }
89      dmClass = dm;
90      dmClass.on('uiStateChange', (data: Record<string, string>) => {
91        console.log('uiStateChange executed, dialog closed' + JSON.stringify(data));
92        let tmpStr: Record<string, number> = JSON.parse(data.param);
93        let msg: number = tmpStr.uiStateMsg as number;
94        if (msg === MSG_DOING_AUTH) {
95          this.errorTips = $r('app.string.dm_authenticating');
96          this.errorTipsVisible = Visibility.Visible;
97          return;
98        }
99        if (msg === MSG_CANCEL_PIN_CODE_INPUT) {
100          this.destruction();
101          return;
102        }
103        if (msg === MSG_PIN_CODE_ERROR) {
104          this.inputCodeError();
105        }
106      })
107    });
108    this.listener.on('change', (mediaQueryResult: mediaquery.MediaQueryResult) => {
109      this.onPortrait(mediaQueryResult);
110    });
111  }
112
113  sendAccessibilityEvent(times: number) {
114    console.log(TAG + 'sendAccessibilityEvent in');
115    let context = getContext(this) as common.UIAbilityContext;
116    let str = context.resourceManager.getPluralStringValueSync($r('app.plural.dm_incorrect_code').id, times)
117    let eventInfo: accessibility.EventInfo = ({
118      type: 'announceForAccessibility',
119      bundleName: 'com.ohos.devicemanagerui',
120      triggerAction: 'common',
121      textAnnouncedForAccessibility: str
122    })
123
124    try {
125      accessibility.sendAccessibilityEvent(eventInfo).then(()=>{
126        console.info(`${TAG} Succeeded in send event, eventInfo is ${JSON.stringify(eventInfo)}`);
127      });
128    } catch (error) {
129      console.info(`${TAG} Failed in send event, error.message is ${error.message}`);
130    }
131  }
132
133  inputCodeError() {
134    console.log(TAG + 'inputCodeError in');
135    if (this.model == MODEL_PASSWORD) {
136      this.errorTips = $r('app.string.dm_password_error');
137    } else {
138      this.isTimes--;
139      this.errorTips = $r('app.plural.dm_incorrect_code', this.isTimes, this.isTimes);
140      this.sendAccessibilityEvent(this.isTimes);
141    }
142    this.password = '';
143    this.errorTipsVisible = Visibility.Visible;
144    this.passwordCircle = ['', '', '', '', '', ''];
145  }
146
147  cancel() {
148    console.log('cancle');
149    if (dmClass) {
150      console.log('deviceManager exist');
151    } else {
152      console.log('createDeviceManager is null');
153      return;
154    }
155    console.log('cancle' + ACTION_CANCEL_PINCODE_INPUT);
156    this.setUserOperation(ACTION_CANCEL_PINCODE_INPUT, 'extra');
157    this.destruction();
158  }
159
160  confirm() {
161    console.log('confirm');
162    if (this.password == null || this.password == '') {
163      return;
164    }
165    if (dmClass) {
166      console.log('deviceManager exist');
167    } else {
168      console.log('createDeviceManager is null');
169      return;
170    }
171    console.log('confirm' + JSON.stringify(ACTION_DONE_PINCODE_INPUT));
172    this.setUserOperation(ACTION_DONE_PINCODE_INPUT, this.password);
173  }
174
175  setUserOperation(operation: number, extra: string) {
176    console.log('setUserOperation: ' + operation + 'password' + extra);
177    if (dmClass == null) {
178      console.log('setUserOperation: ' + 'dmClass null');
179      return;
180    }
181    try {
182      dmClass.setUserOperation(operation, extra);
183    } catch (error) {
184      console.log('dmClass setUserOperation failed');
185    }
186  }
187
188  destruction() {
189    console.info(TAG + 'destruction');
190    let inputMethodController = inputMethod.getController();
191    inputMethodController.hideTextInput();
192    let session = AppStorage.get<UIExtensionContentSession>('inputSession');
193    if (session) {
194      console.info(TAG + 'terminateSelf');
195      session.terminateSelf();
196    }
197  }
198
199  isNumberSix(str: string): boolean {
200    console.info(TAG + 'isNumber6 in');
201    const reg: RegExp = new RegExp('^[0-9]{6}$');
202    return reg.test(str);
203  }
204
205  passwordOnChange(value: string) {
206    console.info(TAG + 'passwordOnChange in');
207    if (this.isNumberSix(value)) {
208      this.confirm();
209    }
210  }
211
212  private isTibetanLanguages(): boolean {
213    console.info(`${TAG} isTibetanLanguages in`);
214    let locale = new Intl.Locale(i18n.System.getSystemLanguage()).toString();
215    console.info(`${TAG} isTibetanLanguages: ${locale}`);
216    return Constant.TIBETAN_LANGUAGES.includes(locale);
217  }
218
219  build() {
220    GridRow({
221      columns: { xs: 4, sm: 8, md: this.isPC ? 24 : 12 },
222      gutter: { x: 4 },
223      breakpoints: { value: ['600vp', '840vp'] }
224    }) {
225      GridCol({ span: { xs: 4, sm: 4, md: this.isPC ? 6 : 4 }, offset: { sm: 2, md: this.isPC ? 9 : 4 } }) {
226        Scroll(this.scroller) {
227          Column() {
228            Column() {
229              Text($r('app.string.dm_connect', this.targetDeviceName))
230                .fontSize($r('sys.float.ohos_id_text_size_dialog_tittle'))
231                .fontWeight(FontWeight.Bold)
232                .fontColor($r('sys.color.ohos_id_color_text_primary'))
233                .margin({ top: 12, bottom: 3 })
234                .width('auto')
235                .textAlign(TextAlign.Center)
236                .maxLines(2)
237                .textOverflow({ overflow: TextOverflow.Ellipsis })
238                .minFontSize(12)
239                .maxFontSize($r('sys.float.ohos_id_text_size_dialog_tittle'))
240                .heightAdaptivePolicy(TextHeightAdaptivePolicy.LAYOUT_CONSTRAINT_FIRST)
241                .lineHeight(this.isTibetanLanguages() ? 31 : 0)
242
243              Text($r('app.string.dm_enter_connect_code'))
244                .fontSize($r('sys.float.ohos_id_text_size_body2'))
245                .fontWeight(FontWeight.Regular)
246                .fontColor($r('sys.color.ohos_id_color_text_secondary'))
247                .margin({ bottom: 8 })
248                .width('auto')
249                .maxLines(2)
250                .textAlign(TextAlign.Center)
251                .textOverflow({ overflow: TextOverflow.Ellipsis })
252                .minFontSize(12)
253                .maxFontSize($r('sys.float.ohos_id_text_size_body2'))
254                .heightAdaptivePolicy(TextHeightAdaptivePolicy.LAYOUT_CONSTRAINT_FIRST)
255                .lineHeight(this.isTibetanLanguages() ? 22 : 0)
256            }
257            .margin({ left: 24, right: 24 })
258            .constraintSize({ minHeight: 72 })
259            .justifyContent(FlexAlign.Center)
260
261            Stack() {
262              List() {
263                ListItem() {
264                  Flex({ justifyContent: FlexAlign.Center }) {
265                    ForEach(this.passwordCircle, (item:string) => {
266                      Flex({ justifyContent: FlexAlign.Center, alignItems: ItemAlign.Center }) {
267                        Text(item)
268                          .fontSize($r('sys.float.ohos_id_text_size_headline7'))
269                          .fontColor($r('sys.color.ohos_id_color_text_primary'))
270                          .fontWeight(FontWeight.Medium)
271                      }.width('10%')
272                      .height('100%')
273                      .visibility(item === '' ? Visibility.None : Visibility.Visible)
274                    })
275                    ForEach(this.passwordCircle, (item: string) => {
276                      Flex({ justifyContent: FlexAlign.Center, alignItems: ItemAlign.Center }) {
277                        Column()
278                          .width(12)
279                          .height(12)
280                          .border({ width: 2, color: $r('sys.color.ohos_id_color_primary'), radius: 12})
281                      }.width('10%')
282                      .height('100%')
283                      .visibility(item === '' ? Visibility.Visible : Visibility.None)
284                    })
285                  }
286                }
287              }
288              TextInput({ placeholder: '', text: this.password})
289                .defaultFocus(true)
290                .onAppear(() => {
291                  focusControl.requestFocus('inputpin')
292                })
293                .id('inputpin')
294                .type(8)
295                .height(60)
296                .opacity(0)
297                .fontColor(('rgba(0,0,0,0)'))
298                .backgroundColor(('rgba(0,0,0,0)'))
299                .caretColor(('rgba(0,0,0,0)'))
300                .maxLength(6)
301                .margin({ bottom: 8 })
302                .width('100%')
303                .onChange((value: string) => {
304                  this.password = value;
305                  if (value.length > 6) {
306                    return;
307                  }
308                  let length = value.length;
309                  for (let i = 0; i < 6; i++) {
310                    if (i < length) {
311                      this.passwordCircle[i] = value[i];
312                    } else {
313                      this.passwordCircle[i] = '';
314                    }
315                  }
316                  let gThis = this;
317                  setTimeout(()=> {
318                    gThis.passwordOnChange(value);
319                  }, 50)
320                })
321            }.height(48)
322            .margin({ top: 12, bottom: 16})
323
324            Flex({ justifyContent: FlexAlign.Center, alignItems: ItemAlign.Center }) {
325              Text(this.errorTips)
326                  .fontSize($r('sys.float.ohos_id_text_size_body2'))
327                  .fontWeight(FontWeight.Medium)
328                  .fontColor($r('sys.color.ohos_id_color_warning'))
329                  .lineHeight(this.isTibetanLanguages() ? 22 : 0)
330            }.visibility(this.errorTipsVisible)
331            .margin({ bottom: 16,
332                      left: $r('sys.float.ohos_id_corner_radius_dialog'),
333                      right: $r('sys.float.ohos_id_corner_radius_dialog') })
334
335            Flex({ justifyContent: FlexAlign.Center }) {
336              Button($r('app.string.dm_cancel'))
337                .constraintSize({ minHeight: 40 })
338                .fontSize($r('sys.float.ohos_id_text_size_button1'))
339                .onClick(() => {
340                  if (this.controller) {
341                    this.controller.close();
342                  }
343                  this.cancel();
344                })
345                .width('100%')
346                .backgroundColor(this.btnColor)
347                .fontColor($r('sys.color.ohos_id_color_text_primary_activated'))
348                .onHover((isHover?: boolean, event?: HoverEvent): void => {
349                  if (isHover) {
350                    this.btnColor = $r('sys.color.ohos_id_color_hover');
351                  } else {
352                    this.btnColor = this.isPC ? $r('sys.color.ohos_id_color_button_normal') : Color.Transparent;
353                  }
354                })
355                .stateStyles({
356                  pressed: {
357                    .backgroundColor($r('sys.color.ohos_id_color_click_effect'))
358                  },
359                  normal: {
360                    .backgroundColor(this.isPC ? $r('sys.color.ohos_id_color_button_normal') : Color.Transparent)
361                  }
362                })
363            }.margin({
364              left: 16,
365              right: 16,
366              bottom: this.isPC ? 24 : 16 })
367          }
368        }
369        .scrollable(ScrollDirection.Vertical)
370        .scrollBar(BarState.On)
371        .constraintSize({ maxHeight: `${this.heightNum}`})
372        .borderRadius($r('sys.float.ohos_id_corner_radius_dialog'))
373        .backgroundBlurStyle(BlurStyle.COMPONENT_ULTRA_THICK)
374        .margin({
375          left: $r('sys.float.ohos_id_dialog_margin_bottom'),
376          right: $r('sys.float.ohos_id_dialog_margin_bottom')
377        })
378      }
379    }.margin({top: 8, bottom: 20})
380  }
381}
382
383@Entry
384@Component
385struct dialogPlusPage {
386  mediaQueryListener: mediaquery.MediaQueryListener = mediaquery.matchMediaSync('(orientation: landscape)');
387  dialogController: CustomDialogController = new CustomDialogController({
388    builder: InputCustomDialog(),
389    autoCancel: false,
390    alignment: DialogAlignment.Center,
391    offset: { dx: 0, dy: 0 },
392    customStyle: true,
393    maskColor: $r('sys.color.ohos_id_color_mask_thin')
394  });
395
396  aboutToAppear() {
397    console.log(TAG + 'aboutToAppear aboutToAppear');
398    this.mediaQueryListener.on('change', this.onPortrait.bind(this));
399  }
400
401  onPortrait(mediaQueryResult: mediaquery.MediaQueryResult) {
402    AppStorage.setOrCreate('mediaQueryResult', mediaQueryResult.matches as boolean);
403  }
404
405  aboutToDisappear() {
406    console.log(TAG + 'aboutToDisappear aboutToDisappear')
407    if (dmClass != null) {
408      try {
409        dmClass.off('uiStateChange');
410        dmClass.release();
411      } catch (error) {
412        console.log('dmClass release failed');
413      }
414      dmClass = null
415    }
416  }
417
418  build() {
419    Column(this.dialogController.open())
420  }
421}