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