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