1/* 2 * Copyright (c) 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' 17 18let dmClass: deviceManager.DeviceManager | null; 19let TAG = '[DeviceManagerUI:InputPinDialog]==>'; 20const ACTION_CANCEL_PINCODE_INPUT: number = 4; 21const ACTION_DONE_PINCODE_INPUT: number = 5; 22const MSG_PIN_CODE_ERROR: number = 0; 23const MSG_CANCEL_PIN_CODE_INPUT: number = 3; 24const MSG_DOING_AUTH: number = 4; 25const BUTTON_BG_COLOR = 'rgba(255, 255, 255, 0.15)'; 26const TOAST_DURATION_TIME_MS = 1500; 27const MAX_PINCODE_LENGTH = 6; 28 29@Entry 30@Component 31struct Index { 32 @State isTimes: number = 3; 33 @State passwordCircle: string[] = ['', '', '', '', '', '']; 34 @State errorTips: Resource = $r('app.plural.dm_incorrect_code', this.isTimes, this.isTimes); 35 @State @Watch('onChangeInput') input: string = ''; 36 private keyboards: string[][] = [ 37 ['1', '2', '3'], 38 ['4', '5', '6'], 39 ['7', '8', '9'], 40 ['', '0', 'x'], 41 ]; 42 @State isUserInput: boolean = false; 43 @State isUserOperate: boolean = false; 44 45 dialogController: CustomDialogController = new CustomDialogController({ 46 builder: errTips({attemptTimes: this.isTimes, errTips: this.errorTips}), 47 autoCancel: false, 48 alignment: DialogAlignment.Center, 49 customStyle: true, 50 maskColor: $r('sys.color.ohos_id_color_mask_thin'), 51 }); 52 53 aboutToAppear() { 54 if (dmClass) { 55 console.log(TAG + 'deviceManager exist'); 56 return; 57 } 58 deviceManager.createDeviceManager('com.ohos.devicemanagerui.input', 59 (err: Error, dm: deviceManager.DeviceManager) => { 60 if (err) { 61 console.log('createDeviceManager err:' + JSON.stringify(err) + ' --fail:' + '${dm}'); 62 return; 63 } 64 dmClass = dm; 65 dmClass.on('uiStateChange', (data: Record<string, string>) => { 66 console.log('uiStateChange executed, dialog closed' + JSON.stringify(data)); 67 let tmpStr: Record<string, number> = JSON.parse(data.param); 68 let msg: number = tmpStr.uiStateMsg as number; 69 if (msg === MSG_DOING_AUTH) { 70 this.errorTips = $r('app.string.dm_authenticating'); 71 return; 72 } 73 if (msg === MSG_CANCEL_PIN_CODE_INPUT) { 74 this.destruction(); 75 return; 76 } 77 if (msg === MSG_PIN_CODE_ERROR) { 78 this.isTimes--; 79 this.errorTips = $r('app.plural.dm_incorrect_code', this.isTimes, this.isTimes); 80 this.input = ''; 81 this.passwordCircle = ['', '', '', '', '', '']; 82 this.showToast(); 83 } 84 }); 85 }); 86 } 87 88 showToast() { 89 if (this.dialogController) { 90 this.dialogController.open(); 91 setTimeout(() => { 92 this.dialogController.close(); 93 }, TOAST_DURATION_TIME_MS); 94 } 95 } 96 97 onPageHide() { 98 console.log('onPageHide'); 99 if (this.isUserOperate) { 100 console.log('user operate'); 101 return; 102 } 103 this.cancel(); 104 } 105 106 destruction() { 107 console.log(TAG + 'destruction begin'); 108 if (dmClass != null) { 109 try { 110 dmClass.off('uiStateChange'); 111 dmClass.release(); 112 } catch (error) { 113 console.log('dmClass release failed'); 114 } 115 dmClass = null; 116 } 117 let session = AppStorage.get<UIExtensionContentSession>('inputSession'); 118 if (session) { 119 session.terminateSelf(); 120 } 121 } 122 123 setUserOperation(operation: number, extra: string) { 124 console.log('setUserOperation: ' + operation + 'input' + extra); 125 if (dmClass === null) { 126 console.log('setUserOperation: ' + 'dmClass null'); 127 return; 128 } 129 try { 130 this.isUserOperate = true; 131 dmClass.setUserOperation(operation, extra); 132 } catch (error) { 133 console.log('dmClass setUserOperation failed'); 134 } 135 } 136 137 cancel() { 138 console.log('cancle'); 139 if (dmClass) { 140 console.log('deviceManager exist'); 141 } else { 142 console.log('createDeviceManager is null'); 143 return; 144 } 145 console.log('cancle' + ACTION_CANCEL_PINCODE_INPUT); 146 this.setUserOperation(ACTION_CANCEL_PINCODE_INPUT, 'extra'); 147 this.destruction(); 148 } 149 150 confirm() { 151 console.log('confirm'); 152 if (this.input === null || this.input === '') { 153 return; 154 } 155 if (dmClass) { 156 console.log('deviceManager exist'); 157 } else { 158 console.log('createDeviceManager is null'); 159 return; 160 } 161 console.log('confirm' + JSON.stringify(ACTION_DONE_PINCODE_INPUT)); 162 this.setUserOperation(ACTION_DONE_PINCODE_INPUT, this.input); 163 } 164 165 build() { 166 Column() { 167 this.titleBuilder(); 168 this.keyboardMocker(); 169 } 170 .backgroundColor(Color.Black) 171 .height('100%') 172 .width('100%') 173 } 174 175 @Builder 176 keyBuilder(key: string) { 177 GridItem() { 178 Button(key) 179 .backgroundColor(BUTTON_BG_COLOR) 180 .fontColor('#FFFFFF') 181 .fontSize($r('sys.float.ohos_id_text_size_headline6')) 182 .onClick(() => { 183 if (this.input.length >= MAX_PINCODE_LENGTH) { 184 return; 185 } 186 this.input += key.toString(); 187 }) 188 .size({ width: 60, height: 48 }) 189 .margin({ left: 3, top: 2 }) 190 .visibility(key === '' ? Visibility.None : Visibility.Visible) 191 } 192 } 193 194 @Builder 195 processKey(key: string) { 196 if (key === 'x') { 197 this.SymbolDelete(); 198 } else { 199 this.keyBuilder(key); 200 } 201 } 202 203 @Builder 204 keyboardMocker() { 205 Grid() { 206 ForEach(this.keyboards, (keysArr: string[]) => { 207 ForEach(keysArr, (key: string) => { 208 this.processKey(key); 209 }) 210 }) 211 } 212 .width(189) 213 .height(200) 214 .margin({ top: 2 }) 215 } 216 217 @Builder 218 SymbolDelete() { 219 SymbolGlyph($r('sys.symbol.delete_left_fill')) 220 .fontSize('28vp') 221 .renderingStrategy(SymbolRenderingStrategy.MULTIPLE_OPACITY) 222 .fontColor(['#FFFFFF']) 223 .margin({ left: 10, top: 5 }) 224 .onClick(() => { 225 this.input = this.input.slice(0, -1); 226 }) 227 .visibility(this.isUserInput ? Visibility.Visible : Visibility.None) 228 } 229 230 @Builder 231 PinCode() { 232 Flex({ justifyContent: FlexAlign.Center, alignItems: ItemAlign.Center }) { 233 ForEach(this.passwordCircle, (item:string) => { 234 Flex({ justifyContent: FlexAlign.Center, alignItems: ItemAlign.Center }) { 235 Text(item) 236 .fontSize($r('sys.float.ohos_id_text_size_sub_title2')) 237 .fontColor('#FFFFFF') 238 .fontWeight(FontWeight.Medium) 239 }.width('7%') 240 .visibility(item === '' ? Visibility.None : Visibility.Visible) 241 }) 242 ForEach(this.passwordCircle, (item: string) => { 243 Flex({ justifyContent: FlexAlign.Center, alignItems: ItemAlign.Center }) { 244 Column() 245 .width(9) 246 .height(9) 247 .border({ width: 1, color: '#FFFFFF', radius: 9}) 248 .alignItems(HorizontalAlign.Center) 249 }.width('7%') 250 .visibility(item === '' ? Visibility.Visible : Visibility.None) 251 }) 252 } 253 } 254 255 @Builder 256 titleBuilder() { 257 Text($r('app.string.dm_enter_peer_connect_code')) 258 .fontColor('#FFFFFF') 259 .fontSize($r('sys.float.ohos_id_text_size_body2')) 260 .margin({ top: 10 }) 261 .textOverflow({ overflow: TextOverflow.MARQUEE }) 262 .visibility(this.isUserInput ? Visibility.None : Visibility.Visible) 263 .height(19) 264 Stack() { 265 List() { 266 ListItem() { 267 this.PinCode(); 268 } 269 } 270 } 271 .visibility(this.isUserInput ? Visibility.Visible : Visibility.None) 272 .margin({ top: 9 }) 273 .height(20) 274 } 275 276 onChangeInput(changedPropertyName: string) { 277 let length = this.input.length; 278 for (let i = 0; i < MAX_PINCODE_LENGTH; ++i) { 279 if (i < length) { 280 this.passwordCircle[i] = this.input[i]; 281 } else { 282 this.passwordCircle[i] = ''; 283 } 284 } 285 if (length === MAX_PINCODE_LENGTH) { 286 setTimeout(() => { 287 this.setUserOperation(ACTION_DONE_PINCODE_INPUT, this.input); 288 }, 50); 289 } 290 this.isUserInput = true; 291 } 292} 293 294@CustomDialog 295struct errTips { 296 controller?: CustomDialogController; 297 @Link attemptTimes: number; 298 @Link errTips: Resource; 299 build() { 300 Column() { 301 Text(this.errTips) 302 .fontSize(15) 303 .fontColor('#FFFFFF') 304 .textAlign(TextAlign.Center) 305 .backgroundColor('#F2404040') 306 } 307 .justifyContent(FlexAlign.Center) 308 .width(205) 309 .padding({left: 12, right: 12, top: 10, bottom: 10 }) 310 .backgroundColor('#F2404040') 311 .borderRadius(19) 312 } 313}