1/** 2 * Copyright (c) 2021-2022 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 */ 15 16import Router from '@system.router'; 17import deviceInfo from '@ohos.deviceInfo'; 18import InputMethod from '@ohos.inputMethod'; 19import { PinSubType } from '../model/passwordImpl/PasswordModel'; 20import PasswordInputController from '../controller/password/PasswordInputController'; 21import LogUtil from '../../../../../../common/utils/src/main/ets/default/baseUtil/LogUtil'; 22import Log from '../../../../../../common/utils/src/main/ets/default/baseUtil/LogDecorator'; 23import ConfigData from '../../../../../../common/utils/src/main/ets/default/baseUtil/ConfigData'; 24import HeadComponent from '../../../../../../common/component/src/main/ets/default/headComponent'; 25import { RadioListItem } from '../../../../../../common/utils/src/main/ets/default/bean/RadioListItem'; 26import RadioListComponent from '../../../../../../common/component/src/main/ets/default/radioListComponent'; 27 28const deviceTypeInfo = deviceInfo.deviceType; 29const TAG = ConfigData.TAG + 'PasswdSetting.PasswdInput -> '; 30 31@Entry 32@Component 33struct PasswordInput { 34 private TAG_PAGE = ConfigData.TAG + 'PasswordInput page '; 35 private mController: PasswordInputController = new PasswordInputController(); 36 37 // bind Properties 38 @State @Watch("clearViewData") 39 private isInputFirstTime: boolean = true; 40 @State @Watch("clearViewData") 41 private passwordType: number = -1; 42// private password: string = ''; 43 @State password: string = ''; 44 @State passwordCircle: string[]= ["", "", "", "", "", ""]; 45 46 // private Properties 47 private pageRequestCode: number = -1; 48 private prevPageUri: string = ''; 49 private pinChallenge: string = ''; 50 private pinToken: string = ''; 51 @State private pageTitle: Resource = $r("app.string.endTextEmpty"); 52 @State private inputMessage: Resource = $r("app.string.endTextEmpty"); 53 @State private unlockMethodList: RadioListItem[] = []; 54 @State private buttonVisibility: Visibility = Visibility.Visible; 55 @State isTouchedLeft: boolean = false; 56 @State isTouchedRight: boolean = false; 57 @State isFocused: boolean = false; 58 59 // handler 60 private passwordOnChangeHandler: (value: string) => void = () => {}; 61 private okOnClickHandler: (event?: ClickEvent) => void = () => {}; 62 private unlockMethodChosenHandler: (value: number) => void = () => {}; 63 64 //dialog 65 private chooseUnlockMethodDialog: CustomDialogController | null = new CustomDialogController({ 66 builder: chooseUnlockMethodDialog({ 67 dialogTitle: $r('app.string.password_change_unlock_method'), 68 dataList: this.unlockMethodList, 69 checkedValue: this.passwordType.toString(), 70 chosenAction: (value) => { 71 this.unlockMethodChosenHandler(value); 72 } 73 }), 74 autoCancel: true, 75 alignment: deviceTypeInfo === 'phone' || deviceTypeInfo === 'default' ? DialogAlignment.Bottom : DialogAlignment.Center, 76 offset: ({ dx: 0, dy: deviceTypeInfo === 'phone' || deviceTypeInfo === 'default' ? '-24dp' : 0 }) 77 }); 78 79 aboutToAppear(): void { 80 this.getRouterParam(); 81 82 // bind event handlers 83 this.passwordOnChangeHandler = (value: string):void => this.mController.passwordOnChange(value); 84 this.okOnClickHandler = ():void => this.mController.inputFinish(); 85 this.unlockMethodChosenHandler = (value: number):void => this.mController.changePasswordType(value); 86 87 // bind component and initialize 88 this.mController.bindComponent(this) 89 .bindProperties(["passwordType", "isInputFirstTime", "password", "pinToken", "passwordCircle"]) 90 .initData() 91 .subscribe(); 92 93 this.updateView(); 94 } 95 96 aboutToDisappear(): void { 97 this.mController.unsubscribe(); 98 this.chooseUnlockMethodDialog = null; 99 } 100 101 /** 102 * Get the params from router 103 */ 104 getRouterParam() { 105 let param = Router.getParams() 106 if (!param) { 107 return; 108 } 109 this.pageRequestCode = param.pageRequestCode as number; 110 this.prevPageUri = param.prevPageUri as string; 111 this.pinChallenge = param.pinChallenge as string; 112 this.pinToken = param.pinToken as string; 113 this.passwordType = param.passwordType as number; 114 } 115 116 build() { 117 Column() { 118 GridContainer({ gutter: ConfigData.GRID_CONTAINER_GUTTER_24, margin: ConfigData.GRID_CONTAINER_MARGIN_24 }) { 119 Column() { 120 // head 121 HeadComponent({ headName: this.pageTitle, isActive: true }); 122 123 Column() { 124 // input message 125 Text(this.inputMessage) 126 .fontSize($r('sys.float.ohos_id_text_size_sub_title2')) 127 .fontWeight(FontWeight.Medium) 128 .fontColor($r("sys.color.ohos_id_color_primary")) 129 .margin({ top: $r("sys.float.ohos_id_default_padding_top") }) 130 .align((this.passwordType == PinSubType.PIN_MIXED) ? Alignment.Start : Alignment.Center) 131 132 // input password 133 if (this.passwordType == PinSubType.PIN_SIX) { 134 Row() { 135 Stack() { 136 TextInput({ placeholder: '', text: this.password }) 137 .height($r('app.float.distance_36')) 138 .width($r('app.float.wh_192')) 139 .opacity(0) 140 .fontColor(('rgba(0,0,0,0)')) 141 .backgroundColor(('rgba(0,0,0,0)')) 142 .caretColor(('rgba(0,0,0,0)')) 143 .maxLength(6) 144 .margin({bottom:$r('app.float.wh_value_8')}) 145 .onChange((value: string) => { 146 this.password = value; 147 if (value.length > 6) { 148 return; 149 } 150 let length = value.length; 151 for (let i = 0;i < 6; i++) { 152 if (i < length) { 153 this.passwordCircle[i] = value.charAt(i); 154 } else { 155 this.passwordCircle[i] = ''; 156 } 157 } 158 this.passwordOnChangeHandler(value); 159 }) 160 .onSubmit((enterKey) => { 161 InputMethod.getInputMethodController().stopInput() 162 .then((ret) => { 163 LogUtil.debug(`${ConfigData.TAG}, enterType: ${enterKey}, stopInput: ${ret}`); 164 }) 165 }); 166 167 List({ space: 24 }) { 168 ForEach(this.passwordCircle, (item: string) => { 169 ListItem() { 170 Column() 171 .width($r('app.float.wh_value_12')) 172 .height($r('app.float.wh_value_12')) 173 .backgroundColor(item === '' ? 'white' : 'black') 174 .border({ width: 1, color: 'black', radius: 12 }) 175 .margin({ top: $r('app.float.wh_value_12') }) 176 } 177 }) 178 } 179 .hitTestBehavior(HitTestMode.Transparent) 180 .listDirection(Axis.Horizontal) 181 } 182 .margin({ top: $r('app.float.wh_value_20'),bottom:$r('app.float.wh_value_12') }) 183 .width(ConfigData.WH_100_100) 184 .height($r("app.float.wh_value_32")) 185 } 186 } else { 187 Column() { 188 TextInput({ placeholder: '', text: this.password }) 189 .width(ConfigData.WH_100_100) 190 .height(ConfigData.WH_100_100) 191 .placeholderFont({ 192 size: $r("app.float.font_18"), 193 weight: FontWeight.Normal, 194 style: FontStyle.Normal 195 }) 196 .type(InputType.Password) 197 .enterKeyType(EnterKeyType.Done) 198 .caretColor($r('sys.color.ohos_id_color_text_primary_activated')) 199 .borderRadius(0) 200 .layoutWeight(ConfigData.LAYOUT_WEIGHT_1) 201 .backgroundColor($r('app.color.color_00000000_transparent')) 202 .onChange(this.passwordOnChangeHandler) 203 .onSubmit((enterKey) => { 204 InputMethod.getInputMethodController().stopInput() 205 .then((ret) => { 206 LogUtil.debug(`${ConfigData.TAG}, enterType: ${enterKey}, stopInput: ${ret}`); 207 }); 208 }) 209 .onFocus(() => { 210 LogUtil.info(TAG + "text input is focused"); 211 this.isFocused = true; 212 }) 213 214 Divider() 215 } 216 .margin({ top: $r('app.float.wh_value_32') }) 217 .height($r("app.float.wh_value_48")) 218 .padding({ 219 left: $r('sys.float.ohos_id_card_margin_start'), 220 right: $r('sys.float.ohos_id_card_margin_end'), 221 top: $r("app.float.distance_8"), 222 bottom: $r("app.float.distance_6") }) 223 } 224 225 CheckText(); 226 227 // change unlock method 228 Button({ type: ButtonType.Normal, stateEffect: true }) { 229 Text($r('app.string.password_change_unlock_method')) 230 .fontSize($r('sys.float.ohos_id_text_size_button1')) 231 .fontColor($r('sys.color.ohos_id_color_text_primary_activated')) 232 .fontWeight(FontWeight.Medium) 233 .align(Alignment.Center) 234 .alignSelf(ItemAlign.Center) 235 .textAlign(TextAlign.Center) 236 .visibility(this.isInputFirstTime ? Visibility.Visible : Visibility.Hidden) 237 } 238 .backgroundColor("rgba(0,0,0,0)") 239 .onClick(() => { 240 this.chooseUnlockMethodDialog?.open(); 241 }) 242 } 243 244 // button 245 Flex({ justifyContent: FlexAlign.SpaceBetween }) { 246 Row() { 247 Button({ type: ButtonType.Capsule, stateEffect: true }) { 248 Text($r('app.string.cancel')) 249 .fontWeight(FontWeight.Medium) 250 .fontSize($r('app.float.application_button_subtitle_size')) 251 .lineHeight($r('app.float.wh_value_22')) 252 .fontColor($r('app.color.font_color_007DFF')) 253 .height($r('app.float.application_button_height')) 254 .textAlign(TextAlign.Center) 255 } 256 .layoutWeight(ConfigData.LAYOUT_WEIGHT_1) 257 .backgroundColor(!this.isTouchedLeft ? $r("sys.color.ohos_id_color_button_normal") : $r("sys.color.ohos_id_color_foreground_contrary")) 258 .onTouch((event?: TouchEvent) => { 259 if (event?.type === TouchType.Down) { 260 this.isTouchedLeft = true; 261 } 262 263 if (event?.type === TouchType.Up) { 264 this.isTouchedLeft = false; 265 } 266 }) 267 .onClick(() => { 268 Router.back(); 269 }) 270 271 Column() 272 .width( $r('app.float.distance_12')) 273 .height($r('app.float.application_button_height')) 274 275 Button({ type: ButtonType.Capsule, stateEffect: true }) { 276 Text(this.isInputFirstTime ? $r('app.string.continue_') : $r('app.string.confirm')) 277 .fontWeight(FontWeight.Medium) 278 .fontSize($r('app.float.application_button_subtitle_size')) 279 .lineHeight($r('app.float.wh_value_22')) 280 .fontColor($r('app.color.font_color_007DFF')) 281 .height($r('app.float.application_button_height')) 282 .textAlign(TextAlign.Center) 283 } 284 .layoutWeight(ConfigData.LAYOUT_WEIGHT_1) 285 .backgroundColor(!this.isTouchedRight ? $r("sys.color.ohos_id_color_button_normal") : $r("sys.color.ohos_id_color_foreground_contrary")) 286 .onTouch((event?: TouchEvent) => { 287 if (event?.type === TouchType.Down) { 288 this.isTouchedRight = true; 289 } 290 291 if (event?.type === TouchType.Up) { 292 this.isTouchedRight = false; 293 this.isFocused = true; 294 } 295 }) 296 .onClick(this.okOnClickHandler) 297 } 298 .alignItems(this.isFocused === false ? VerticalAlign.Bottom : VerticalAlign.Top) 299 .height(this.passwordType == PinSubType.PIN_MIXED && (deviceTypeInfo === 'phone' || deviceTypeInfo === 'default') ? "58%" : "66%") 300 .padding(this.isFocused === false ? { bottom: 24 } : { top: (this.passwordType == PinSubType.PIN_MIXED ? "14%" : "16%") }) 301 } 302 .width(ConfigData.WH_100_100) 303 .visibility(this.buttonVisibility) 304 } 305 .useSizeType({ 306 sm: { span: 4, offset: 0 }, 307 md: { span: 6, offset: 1 }, 308 lg: { span: 8, offset: 2 } 309 }) 310 } 311 .width(ConfigData.WH_100_100) 312 .height(ConfigData.WH_100_100) 313 } 314 .backgroundColor($r("sys.color.ohos_id_color_sub_background")) 315 .width(ConfigData.WH_100_100) 316 .height(ConfigData.WH_100_100) 317 } 318 319 // --------------------------- updateView ----------------------- 320 /** 321 * Update view data 322 */ 323 clearViewData() { 324 AppStorage.SetOrCreate("checkMessage", ''); 325 this.password = ''; 326 this.passwordCircle = ["", "", "", "", "", ""]; 327 this.mController.bindComponent(this).initData(); 328 this.updateView(); 329 } 330 331 /** 332 * Update view 333 */ 334 updateView() { 335 this.pageTitle = this.getPageTitle(); 336 this.inputMessage = this.getInputMessage(); 337 this.unlockMethodList = this.getUnlockMethodList(); 338 this.buttonVisibility = this.getButtonVisibility(); 339 } 340 341 /** 342 * Get page title 343 * 344 * @return : page title 345 */ 346 getPageTitle(): Resource { 347 let title: Resource = $r('app.string.password_enter_password'); 348 switch (this.passwordType) { 349 case PinSubType.PIN_SIX: 350 351 case PinSubType.PIN_NUMBER: 352 title = $r('app.string.password_title_number'); 353 break; 354 355 case PinSubType.PIN_MIXED: 356 title = $r('app.string.password_title_character'); 357 break; 358 } 359 return title; 360 } 361 362 /** 363 * Get input message 364 * 365 * @return : message 366 */ 367 getInputMessage(): Resource { 368 let inputMessage: Resource = $r("app.string.endTextEmpty"); 369 if (this.isInputFirstTime) { 370 switch (this.passwordType) { 371 case PinSubType.PIN_SIX: 372 inputMessage = $r('app.string.password_message_number_6'); 373 break; 374 375 case PinSubType.PIN_NUMBER: 376 inputMessage = $r('app.string.password_message_custom'); 377 break; 378 379 case PinSubType.PIN_MIXED: 380 inputMessage = $r('app.string.password_message_character'); 381 break; 382 } 383 } else { 384 inputMessage = $r('app.string.password_message_repeat'); 385 } 386 return inputMessage; 387 } 388 389 /** 390 * Get unlock method list. 391 * 392 * @return : unlock method list 393 */ 394 getUnlockMethodList(): RadioListItem[] { 395 let list: RadioListItem[] = []; 396 if (!this.isInputFirstTime) { 397 return list; 398 } 399 400 if (this.passwordType != PinSubType.PIN_SIX) { 401 list.push({ 402 settingType: PinSubType.PIN_SIX, 403 settingTitle: $r('app.string.password_item_text_number_6') 404 }) 405 } 406 407 if (this.passwordType != PinSubType.PIN_NUMBER) { 408 list.push({ 409 settingType: PinSubType.PIN_NUMBER, 410 settingTitle: $r('app.string.password_item_text_custom') 411 }) 412 } 413 414 if (this.passwordType != PinSubType.PIN_MIXED) { 415 list.push({ 416 settingType: PinSubType.PIN_MIXED, 417 settingTitle: $r('app.string.password_item_text_character') 418 }) 419 } 420 return list; 421 } 422 423 /** 424 * Get button visibility 425 * 426 * @return : button visibility 427 */ 428 getButtonVisibility(): Visibility { 429 return this.passwordType == PinSubType.PIN_SIX ? Visibility.Hidden : Visibility.Visible; 430 } 431} 432 433// The check message need to change real time, put it in child component, so parent component does not refresh. 434@Component 435struct CheckText { 436 @StorageLink("checkMessage") 437 private checkMessage: string | Resource = ''; 438 439 build() { 440 Text(this.checkMessage ? this.checkMessage : $r('app.string.password_set_prompt')) 441 .fontSize($r('sys.float.ohos_id_text_size_body2')) 442 .fontWeight(FontWeight.Medium) 443 .fontColor($r('sys.color.ohos_id_color_warning')) 444 .align(Alignment.Center) 445 .textAlign(TextAlign.Center) 446 .margin({ top: $r('app.float.distance_4'), bottom: $r('app.float.distance_24') }) 447 } 448} 449 450/** 451 * Choose Unlock Method Dialog 452 */ 453@CustomDialog 454struct chooseUnlockMethodDialog { 455 controller?: CustomDialogController; 456 private dataList: RadioListItem[] = []; 457 private checkedValue: string = ''; 458 private dialogTitle: string | Resource = ""; 459 private chosenAction: (value: number) => void = () => {}; 460 @State isTouched: Boolean = false; 461 462 closeDialog() { 463 this.controller?.close(); 464 } 465 466 build() { 467 Column() { 468 Text(this.dialogTitle) 469 .height($r('app.float.wh_value_56')) 470 .margin({ left: $r('app.float.wh_value_24') }) 471 .width(ConfigData.WH_100_100) 472 .fontSize($r('app.float.font_20')) 473 .fontColor($r("sys.color.ohos_id_color_primary")) 474 .fontWeight(500) 475 476 RadioListComponent({ 477 dataList: this.dataList, 478 checkedValue: this.checkedValue, 479 showRadio: false, 480 onChange: (item: RadioListItem) => { 481 if (this.chosenAction != null) { 482 LogUtil.info(ConfigData.TAG + 'chooseUnlockMethodDialog : onCheckedAction : call back'); 483 this.chosenAction(item.settingType? item.settingType : 0); 484 } 485 this.closeDialog(); 486 } 487 }) 488 489 Text($r('app.string.cancel')) 490 .fontSize($r('app.float.application_button_subtitle_size')) 491 .fontColor($r('sys.color.ohos_id_color_focused_bg')) 492 .textAlign(TextAlign.Center) 493 .fontWeight(500) 494 .width(ConfigData.WH_100_100) 495 .height($r("app.float.wh_value_56")) 496 .margin({ top: $r("app.float.wh_value_6") }) 497 .padding({ top: $r("app.float.wh_value_12"), bottom: $r("app.float.wh_value_6") }) 498 .borderRadius($r('app.float.radius_20')) 499 .linearGradient(this.isTouched ? { 500 angle: 90, 501 direction: GradientDirection.Right, 502 colors: [[$r("app.color.DCEAF9"), 0.0], [$r("app.color.FAFAFA"), 1.0]] 503 } : { 504 angle: 90, 505 direction: GradientDirection.Right, 506 colors: [[$r("sys.color.ohos_id_color_foreground_contrary"), 1], [$r("sys.color.ohos_id_color_foreground_contrary"), 1]] 507 }) 508 .onTouch((event?: TouchEvent) => { 509 if (event?.type === TouchType.Down) { 510 this.isTouched = true; 511 } 512 513 if (event?.type === TouchType.Up) { 514 this.isTouched = false; 515 } 516 }) 517 .onClick(() => { 518 this.closeDialog(); 519 }) 520 .alignSelf(ItemAlign.Center) 521 } 522 } 523}