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}