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 10 { 11 "name": "bluetoothTab", agreed to in writing, software 12 * distributed under the License is distributed on an "AS IS" BASIS, 13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 * See the License for the specific language governing permissions and 15 * limitations under the License. 16 */ 17 18import Router from '@system.router'; 19import deviceInfo from '@ohos.deviceInfo'; 20import BluetoothDevice from '../model/bluetoothImpl/BluetoothDevice'; 21import BluetoothDeviceController from '../controller/bluetooth/BluetoothDeviceController'; 22import LogUtil from '../../../../../../common/utils/src/main/ets/default/baseUtil/LogUtil'; 23import ConfigData from '../../../../../../common/utils/src/main/ets/default/baseUtil/ConfigData'; 24import HeadComponent from '../../../../../../common/component/src/main/ets/default/headComponent'; 25import EntryComponent from '../../../../../../common/component/src/main/ets/default/entryComponent'; 26import { BondState, DeviceType, ProfileConnectionState } from '../model/bluetoothImpl/BluetoothModel'; 27import BasicDataSource from '../../../../../../common/utils/src/main/ets/default/model/BasicDataSource'; 28import DeviceNameEntryComponent from '../../../../../../common/component/src/main/ets/default/deviceNameEntryComponent'; 29import ImageAnimatorComponent from '../../../../../../common/component/src/main/ets/default/imageAnimatorComponent'; 30import { Callback } from '@ohos.base'; 31 32const PAIRED_ITEM_NUMBER = 3; 33const PAGE_URI_DEVICE_NAME = 'pages/deviceName'; 34const PAGE_URI_BLUETOOTH_PAIRED_DEVICE_INFO = 'pages/bluetoothPairedDeviceInfo'; 35const deviceTypeInfo = deviceInfo.deviceType; 36let pinRequiredTIimer: number | undefined = undefined; 37 38@Entry 39@Component 40struct Bluetooth { 41 @StorageLink('bluetoothIsOn') isOn: boolean = false; 42 @StorageLink('bluetoothToggleEnabled') isEnabled: boolean = true; 43 @StorageLink('bluetoothLocalName') localName: string = ''; 44 private PAGE_TAG = ConfigData.TAG + 'Bluetooth page '; 45 private deviceController: BluetoothDeviceController = new BluetoothDeviceController(); 46 47 aboutToAppear(): void { 48 LogUtil.log(this.PAGE_TAG + 'aboutToAppear in : isOn = ' + this.isOn) 49 this.deviceController 50 .initData() 51 .subscribe(); 52 LogUtil.log(this.PAGE_TAG + 'aboutToAppear out : isOn = ' + this.isOn) 53 } 54 55 onPageShow(): void { 56 LogUtil.log(this.PAGE_TAG + 'onPageShow in : localName = ' + this.localName) 57 this.deviceController.getLocalName(); 58 LogUtil.log(this.PAGE_TAG + 'onPageShow out : localName = ' + this.localName) 59 } 60 61 aboutToDisappear(): void { 62 this.deviceController.unsubscribe(); 63 } 64 65 build() { 66 Column() { 67 GridContainer({ gutter: ConfigData.GRID_CONTAINER_GUTTER_24, margin: ConfigData.GRID_CONTAINER_MARGIN_24 }) { 68 Column() { 69 HeadComponent({ headName: $r('app.string.bluetoothTab'), isActive: true }); 70 71 Row() { 72 Text($r("app.string.bluetoothTab")) 73 .fontColor($r('sys.color.ohos_fa_text_primary')) 74 .fontSize($r("app.float.font_16")) 75 .fontWeight(FontWeight.Medium) 76 77 Blank() 78 79 Toggle({ type: ToggleType.Switch, isOn: this.isOn }) 80 .width('36vp') 81 .height('20vp') 82 .selectedColor('#007DFF') 83 .margin({ left: $r('app.float.wh_value_6') }) 84 .onChange((isOn: boolean) => { 85 LogUtil.log(this.PAGE_TAG + 'Toggle onClick: isOn = ' + isOn + ', enabled = ' + this.isEnabled) 86 if (!this.isEnabled) return; 87 this.deviceController.toggleValue(isOn); 88 }); 89 } 90 .margin({ top: $r("app.float.distance_8") }) 91 .width(ConfigData.WH_100_100) 92 .height($r('app.float.wh_value_56')) 93 .backgroundColor($r("app.color.white_bg_color")) 94 .borderRadius($r('app.float.wh_value_28')) 95 .padding({ left: $r('app.float.wh_value_12'), right: $r('app.float.wh_value_6') }) 96 .alignItems(VerticalAlign.Center) 97 .borderRadius($r('app.float.distance_24')) 98 99 Text($r('app.string.bluetooth_visible_to_nearby')) 100 .width(ConfigData.WH_100_100) 101 .fontSize($r('app.float.font_14')) 102 .fontColor($r('sys.color.ohos_id_color_text_secondary')) 103 .visibility(this.isOn ? Visibility.Visible : Visibility.None) 104 .height($r("app.float.wh_value_52")) 105 .lineHeight($r("app.float.wh_value_20")) 106 .padding({ 107 left: $r('app.float.wh_value_12'), 108 top: $r('app.float.distance_8'), 109 bottom: $r('app.float.distance_24') 110 }) 111 112 Scroll() { 113 Column() { 114 DeviceNameComponent({ 115 isEnabled: $isOn, 116 localName: $localName 117 }) 118 119 if (this.isOn) { 120 PairedDeviceComponent({ 121 controller: this.deviceController 122 }) 123 124 AvailableDeviceComponent({ 125 controller: this.deviceController, 126 }) 127 } 128 } 129 .width(ConfigData.WH_100_100) 130 } 131 .scrollBarWidth(0) 132 .width(ConfigData.WH_100_100) 133 .align(Alignment.TopStart) 134 .layoutWeight(ConfigData.LAYOUT_WEIGHT_1) 135 } 136 .useSizeType({ 137 sm: { span: 4, offset: 0 }, 138 md: { span: 6, offset: 1 }, 139 lg: { span: 8, offset: 2 } 140 }); 141 } 142 .width(ConfigData.WH_100_100) 143 .height(ConfigData.WH_100_100); 144 } 145 .backgroundColor($r("sys.color.ohos_id_color_sub_background")) 146 .width(ConfigData.WH_100_100) 147 .height(ConfigData.WH_100_100); 148 } 149} 150 151/** 152 * Device name component 153 */ 154@Component 155struct DeviceNameComponent { 156 @Link isEnabled: boolean; 157 @Link localName: string; 158 159 build() { 160 Row() { 161 DeviceNameEntryComponent({ 162 settingIcon: '', 163 settingTitle: $r('app.string.bluetooth_device_name'), 164 settingSummary: '', 165 settingValue: $localName, 166 settingArrow: "", 167 settingArrowStyle: '', 168 settingUri: '', 169 isEnabled: $isEnabled, 170 heights: ($r('app.float.wh_value_48')), 171 fontSize: ($r('app.float.font_16')) 172 }) 173 } 174 .padding($r("app.float.distance_4")) 175 .margin({ top: this.isEnabled ? $r('app.float.wh_value_0') : $r('app.float.wh_value_12') }) 176 .width(ConfigData.WH_100_100) 177 .height($r('app.float.wh_value_56')) 178 .borderRadius($r("app.float.radius_24")) 179 .backgroundColor($r("app.color.white_bg_color")) 180 .onClick(() => { 181 if (this.isEnabled) { 182 Router.push({ uri: PAGE_URI_DEVICE_NAME }); 183 } 184 }); 185 } 186} 187 188export interface bluetoothParam { 189 bluetoothDevice: string; 190} 191 192/** 193 * Paired device component 194 */ 195@Component 196struct PairedDeviceComponent { 197 @StorageLink('bluetoothPairedDevices') pairedDevices: BluetoothDevice[] = []; 198 @State isTouched: boolean = false; 199 @State hide: boolean = true; 200 private TAG_PAGE = ConfigData.TAG + 'PairedDeviceComponent '; 201 private controller?: BluetoothDeviceController; 202 203 aboutToAppear(): void { 204 if (this.controller) { 205 // bind component and initialize 206 this.controller.bindComponent(this) 207 .bindProperties(["pairedDevices"]) 208 .initData(); 209 } 210 } 211 212 build() { 213 Column() { 214 if (this.pairedDevices && this.pairedDevices.length > 0) { 215 // paired devices title 216 Row() { 217 Text($r('app.string.bluetooth_paired_devices')) 218 .width(ConfigData.WH_100_100) 219 .fontSize($r('app.float.font_14')) 220 .fontWeight(FontWeight.Medium) 221 .fontColor($r('sys.color.ohos_id_color_text_secondary')) 222 } 223 .width(ConfigData.WH_100_100) 224 .padding({ 225 left: $r('app.float.wh_value_12'), 226 top: $r('app.float.distance_19_5'), 227 bottom: $r('app.float.distance_9_5') 228 }) 229 230 List() { 231 // paired devices list 232 ForEach(this.pairedDevices, (item: BluetoothDevice, index?: number) => { 233 if (index !== undefined) { 234 if ((index < PAIRED_ITEM_NUMBER) || !this.hide) { 235 ListItem() { 236 Row() { 237 EntryComponent({ 238 settingIcon: getDeviceIconPath(item.deviceType), 239 settingTitle: item.deviceName, 240 settingSummary: this.getConnectionStateText(item), 241 settingValue: '', 242 settingArrow: JSON.parse(JSON.stringify($r("app.media.ic_public_settings"))), 243 settingArrowStyle: 'bluetooth', 244 settingUri: '', 245 titleFontColor: this.isHeadPhoneConnected(item) ? $r("app.color.bluetooth_text_color_highlight") : $r("sys.color.ohos_id_color_text_primary"), 246 image_wh: $r('app.float.wh_value_24'), 247 heights: this.getConnectionStateText(item) == '' ? $r('app.float.wh_value_48') : ($r('app.float.wh_value_56')), 248 fontSize: ($r('app.float.font_16')), 249 onArrowClick: () => { 250 LogUtil.info(this.TAG_PAGE + 'item go detail'); 251 this.gotoPairedDeviceInfo(item); 252 } 253 }); 254 } 255 .width(ConfigData.WH_100_100) 256 .borderRadius($r("app.float.radius_24")) 257 .backgroundColor($r("app.color.white_bg_color")) 258 .onClick(() => { 259 this.itemClicked(item); 260 }) 261 } 262 } 263 if ((this.hide && index === PAIRED_ITEM_NUMBER) || //more 264 (!this.hide && index >= PAIRED_ITEM_NUMBER && index == this.pairedDevices.length - 1)) { //put_away 265 ListItem() { 266 Stack({ alignContent: Alignment.Center }) { 267 Stack({ alignContent: Alignment.Center }) { 268 Text(this.hide ? $r('app.string.more') : $r('app.string.put_away')) 269 .fontColor($r('app.color.color_333333_grey')) 270 .fontSize($r('app.float.font_14')) 271 } 272 .width(ConfigData.WH_100_100) 273 .height($r("app.float.wh_value_48")) 274 .borderRadius($r("app.float.radius_20")) 275 .backgroundColor(this.isTouched ? $r("app.color.color_D8D8D8_grey") : $r("sys.color.ohos_id_color_foreground_contrary")) 276 .onTouch((event?: TouchEvent | undefined) => { 277 if (event?.type === TouchType.Down) { 278 this.isTouched = true; 279 } 280 if (event?.type === TouchType.Up) { 281 this.isTouched = false; 282 } 283 }) 284 .onClick(() => { 285 this.hide = !this.hide; 286 }) 287 } 288 .height($r("app.float.wh_value_48")) 289 .backgroundColor($r("sys.color.ohos_id_color_foreground_contrary")) 290 } 291 } 292 } 293 }, (item: BluetoothDevice) => { 294 return JSON.stringify(item) 295 }); 296 } 297 .padding($r("app.float.distance_4")) 298 .divider({ 299 strokeWidth: $r('app.float.divider_wh'), 300 color: $r('app.color.color_E3E3E3_grey'), 301 startMargin: $r('app.float.wh_48'), 302 endMargin: $r('app.float.wh_value_12') 303 }) 304 .backgroundColor($r("app.color.white_bg_color")) 305 .borderRadius($r("app.float.radius_24")) 306 } 307 } 308 } 309 310 /** 311 * Get connection state text 312 * @param device 313 */ 314 getConnectionStateText(device: BluetoothDevice): string { 315 let stateText: string = ''; 316 switch (device.connectionState) { 317 case ProfileConnectionState.STATE_DISCONNECTED: 318 stateText = ''; 319 break; 320 321 case ProfileConnectionState.STATE_CONNECTING: 322 stateText = JSON.parse(JSON.stringify($r('app.string.bluetooth_state_connecting'))); 323 break; 324 325 case ProfileConnectionState.STATE_CONNECTED: 326 if (device.deviceType === DeviceType.HEADPHONE) { 327 stateText = JSON.parse(JSON.stringify($r('app.string.bluetooth_state_connected'))); 328 } else { 329 stateText = ''; 330 } 331 break; 332 333 case ProfileConnectionState.STATE_DISCONNECTING: 334 stateText = JSON.parse(JSON.stringify($r('app.string.bluetooth_state_disconnecting'))); 335 break; 336 } 337 return stateText; 338 } 339 340 /** 341 * Disconnect Dialog 342 */ 343 showDisconnectDialog(deviceName: string, callback: Callback<void>) { 344 AlertDialog.show({ 345 title: $r('app.string.bluetooth_disconnect'), 346 message: $r("app.string.bluetooth_disconnect_device", deviceName), 347 primaryButton: { 348 value: $r('app.string.cancel'), 349 action: () => { 350 LogUtil.info(ConfigData.TAG + 'Closed callbacks'); 351 } 352 }, 353 secondaryButton: { 354 value: $r('app.string.confirm'), 355 action: () => { 356 LogUtil.info(ConfigData.TAG + `AlertDialog success:`); 357 callback(); 358 } 359 }, 360 alignment: deviceTypeInfo === 'phone' || deviceTypeInfo === 'default' ? DialogAlignment.Bottom : DialogAlignment.Center, 361 offset: ({ dx: 0, dy: deviceTypeInfo === 'phone' || deviceTypeInfo === 'default' ? '-24dp' : 0 }) 362 }) 363 } 364 365 /** 366 * Whether headphone connected. 367 * @param item device 368 * @return headphone connected or not 369 */ 370 private isHeadPhoneConnected(item: BluetoothDevice): boolean { 371 return item.deviceType === DeviceType.HEADPHONE && item.connectionState === ProfileConnectionState.STATE_CONNECTED 372 } 373 374 /** 375 * Goto paired device Info 376 * @param item device 377 */ 378 private gotoPairedDeviceInfo(item: BluetoothDevice) { 379 if (item.connectionState != ProfileConnectionState.STATE_CONNECTING 380 && item.connectionState != ProfileConnectionState.STATE_DISCONNECTING) { 381 LogUtil.info(this.TAG_PAGE + 'item right icon on click.'); 382 let param: bluetoothParam = { 383 bluetoothDevice: JSON.stringify(item) 384 } 385 Router.push({ 386 uri: PAGE_URI_BLUETOOTH_PAIRED_DEVICE_INFO, 387 params: param 388 }); 389 } 390 } 391 392 /** 393 * Item clicked 394 * @param item device 395 */ 396 private itemClicked(item: BluetoothDevice) { 397 switch (item.connectionState) { 398 case ProfileConnectionState.STATE_CONNECTED: 399 this.showDisconnectDialog(item.deviceName, () => { 400 if (this.controller) { 401 this.controller.disconnect(item.deviceId) 402 } 403 }); 404 break; 405 406 case ProfileConnectionState.STATE_DISCONNECTED: 407 if (this.controller && !this.controller.connect(item.deviceId)) { 408 this.showConnectFailedDialog(item.deviceName); 409 } 410 break; 411 } 412 } 413 414 /** 415 * Connect Failed Dialog 416 */ 417 private showConnectFailedDialog(deviceName: string) { 418 showDialog( 419 $r("app.string.bluetooth_connect_failed"), 420 $r("app.string.bluetooth_connect_failed_msg", deviceName), 421 $r("app.string.bluetooth_know_button") 422 ); 423 } 424} 425 426/** 427 * Discovering animator component 428 */ 429@Component 430struct DiscoveringAnimatorComponent { 431 build() { 432 Column() { 433 ImageAnimatorComponent({ 434 imageWidth: $r('app.float.wh_value_24'), 435 imageHeight: $r('app.float.wh_value_24') }) 436 } 437 } 438} 439 440export interface PinRequiredParam { 441 deviceId: string; 442 pinCode: string; 443} 444 445/** 446 * Available device component 447 */ 448@Component 449struct AvailableDeviceComponent { 450 @State isDeviceDiscovering: boolean = false; 451 @StorageLink('bluetoothAvailableDevices') @Watch("availableDevicesChange") availableDevices: BluetoothDevice[] = []; 452 @State availableDevicesList: AvailableDevicesDataSource = new AvailableDevicesDataSource(this.availableDevices); 453 @State pairPinCode: string = ''; 454 @StorageLink("controlPairing") controlPairing: boolean = true; 455 @StorageLink("pairData") pairData: BluetoothDevice = new BluetoothDevice(); 456 @StorageLink("pinRequiredParam") @Watch("pinRequiredParamChange") pinRequiredParam: PinRequiredParam = { 457 deviceId: '', 458 pinCode: '' 459 }; 460 private TAG_PAGE = ConfigData.TAG + 'AvailableDeviceComponent '; 461 private controller: BluetoothDeviceController | null = null; 462 private pairingDevice: BluetoothDevice | null = null; 463 pairDialog: CustomDialogController | null = new CustomDialogController({ 464 builder: PairDialog({ 465 deviceName: (this.pairingDevice && this.pairingDevice.deviceName) ? this.pairingDevice.deviceName : undefined, 466 pinCode: this.pairPinCode, 467 action: (accept: boolean) => { 468 this.confirmPairing(accept); 469 } 470 }), 471 alignment: deviceTypeInfo === 'phone' || deviceTypeInfo === 'default' ? DialogAlignment.Bottom : DialogAlignment.Center, 472 offset: ({ dx: 0, dy: deviceTypeInfo === 'phone' || deviceTypeInfo === 'default' ? '-24dp' : 0 }), 473 autoCancel: true, 474 }); 475 476 availableDevicesChange() { 477 this.availableDevicesList.setData(this.availableDevices) 478 this.availableDevicesList.notifyDataReload() 479 } 480 481 aboutToAppear(): void { 482 if (this.controller) { 483 // bind component and initialize 484 this.controller.bindComponent(this) 485 .bindProperties(["isDeviceDiscovering", "availableDevices", "pairPinCode"]) 486 .initData(); 487 488 this.controller.startBluetoothDiscovery(); 489 } 490 } 491 492 aboutToDisappear(): void { 493 this.pairDialog = null; 494 } 495 496 build() { 497 Column() { 498 Row() { 499 Row() { 500 // available devices title 501 Text($r('app.string.bluetooth_available_devices')) 502 .fontSize($r('app.float.font_14')) 503 .fontColor($r('sys.color.ohos_id_color_text_secondary')) 504 .height($r('app.float.distance_19')) 505 .fontWeight(FontWeight.Medium) 506 } 507 .padding({ 508 left: $r('sys.float.ohos_id_card_margin_start'), 509 right: $r('sys.float.ohos_id_card_margin_end'), 510 top: $r('app.float.distance_19_5'), 511 bottom: $r('app.float.distance_9_5') 512 }) 513 514 Blank() 515 516 // bluetooth discovering 517 if (this.isDeviceDiscovering) { 518 DiscoveringAnimatorComponent() 519 } 520 } 521 .height($r("app.float.wh_value_48")) 522 .width(ConfigData.WH_100_100) 523 524 if (this.availableDevices && this.availableDevices.length >= 1) { 525 List() { 526 // paired devices list 527 ForEach(this.availableDevices, (item: BluetoothDevice) => { 528 ListItem() { 529 Row() { 530 EntryComponent({ 531 settingIcon: getDeviceIconPath(item.deviceType), 532 settingTitle: item.deviceName ? item.deviceName : item.deviceId, 533 settingSummary: this.getPairStateText(item), 534 settingValue: '', 535 settingArrow: "", 536 settingArrowStyle: '', 537 settingUri: '', 538 image_wh: $r('app.float.wh_value_24'), 539 heights: this.getPairStateText(item) == '' ? $r('app.float.wh_value_56') : ($r('app.float.wh_value_64')), 540 fontSize: ($r('app.float.font_16')), 541 }); 542 } 543 .width(ConfigData.WH_100_100) 544 .borderRadius($r("app.float.radius_24")) 545 .backgroundColor($r("app.color.white_bg_color")) 546 .onClick(() => { 547 LogUtil.info(this.TAG_PAGE + 'item on click'); 548 if (this.controlPairing) { 549 this.pairDevice(item); 550 } else { 551 return; 552 } 553 }) 554 } 555 }, (item: BluetoothDevice) => { 556 return JSON.stringify(item); 557 }); 558 } 559 .padding($r("app.float.distance_4")) 560 .backgroundColor($r("app.color.white_bg_color")) 561 .borderRadius($r("app.float.radius_24")) 562 .divider({ 563 strokeWidth: $r('app.float.divider_wh'), 564 color: $r('app.color.color_E3E3E3_grey'), 565 startMargin: $r('app.float.wh_48'), 566 endMargin: $r('app.float.wh_value_12') 567 }) 568 } else { 569 // Scanning... 570 Text($r('app.string.scanning')) 571 .fontSize($r('sys.float.ohos_id_text_size_body2')) 572 .textCase(TextCase.UpperCase) 573 .fontWeight(FontWeight.Medium) 574 .fontColor($r("sys.color.ohos_id_color_primary")) 575 .height($r('app.float.wh_value_48')) 576 } 577 } 578 } 579 580 /** 581 * Get pair state text 582 * @param device 583 */ 584 getPairStateText(device: BluetoothDevice): string { 585 return device.connectionState == BondState.BOND_STATE_BONDING ? JSON.parse(JSON.stringify($r('app.string.bluetooth_state_pairing'))) : ''; 586 } 587 588 pinRequiredParamChange() { 589 clearTimeout(pinRequiredTIimer); 590 pinRequiredTIimer = setTimeout(() => { 591 this.pairPinCode = this.pinRequiredParam.pinCode; 592 this.pairingDevice = this.pairData; 593 if (this.pairDialog) { 594 this.pairDialog.open(); 595 } 596 () => { 597 this.showPairFailedDialog(); 598 } 599 }, 1000) 600 } 601 602 /** 603 * Pair device 604 * @param device 605 */ 606 pairDevice(device: BluetoothDevice) { 607 if (this.controller) { 608 this.controller.pair(device.deviceId); 609 } 610 } 611 612 /** 613 * Confirm pairing 614 */ 615 616 confirmPairing(accept: boolean) { 617 LogUtil.info(this.TAG_PAGE + 'confirmPairing pairingDevice'); 618 try { 619 if (this.pairingDevice && this.pairingDevice.deviceId != null && this.controller) { 620 this.controller.confirmPairing(this.pairingDevice.deviceId, accept); 621 } 622 } catch (err) { 623 LogUtil.info(this.TAG_PAGE + `confirmPairing pairingDevice error ${err}`); 624 } 625 } 626 627 /** 628 * Show pair failed dialog 629 */ 630 showPairFailedDialog() { 631 this.showPairingFailedDialog(); 632 } 633 634 /** 635 * Show connect Failed Dialog 636 */ 637 private showConnectFailedDialog(deviceName: string) { 638 showDialog( 639 $r("app.string.bluetooth_connect_failed"), 640 $r("app.string.bluetooth_connect_failed_msg", deviceName), 641 $r("app.string.bluetooth_know_button") 642 ); 643 } 644 645 /** 646 * Show pairing failed title Dialog 647 */ 648 private showPairingFailedDialog() { 649 showDialog( 650 $r("app.string.pairing_failed_title"), 651 $r("app.string.pairing_failed_message"), 652 $r("app.string.bluetooth_know_button") 653 ); 654 } 655} 656 657/** 658 * AvailableDevicesDataSource For Lazy Loading 659 */ 660class AvailableDevicesDataSource extends BasicDataSource { 661 private availableDevicesArray: BluetoothDevice[] | null = null; 662 663 constructor(availableDevicesArray: BluetoothDevice[]) { 664 super(); 665 this.availableDevicesArray = availableDevicesArray; 666 } 667 668 public setData(data: BluetoothDevice[]) { 669 this.availableDevicesArray = data; 670 } 671 672 public totalCount(): number { 673 if (this.availableDevicesArray) { 674 return this.availableDevicesArray.length; 675 } 676 return 0; 677 } 678 679 public getData(index: number): BluetoothDevice | null { 680 if (!this.availableDevicesArray) { 681 LogUtil.log(ConfigData.TAG + 'array is null.'); 682 return null; 683 } 684 if (index < 0 || index >= this.totalCount()) { 685 LogUtil.log(ConfigData.TAG + 'index out of range.'); 686 return null; 687 } 688 return this.availableDevicesArray[index]; 689 } 690 691 public delData(device: BluetoothDevice): void { 692 if (this.availableDevicesArray) { 693 let index = this.availableDevicesArray.indexOf(device); 694 this.availableDevicesArray.splice(index, 1); 695 this.notifyDataDelete(index); 696 } 697 } 698} 699 700/** 701 * Pair dialog 702 */ 703@CustomDialog 704struct PairDialog { 705 dialogController?: CustomDialogController; 706 private deviceName: string | undefined = undefined; 707 private pinCode: string = ''; 708 709 action: (accept: boolean) => void = (accept: boolean) => { 710 }; 711 712 aboutToAppear(): void { 713 LogUtil.log(ConfigData.TAG + `bluetooth PairDialog aboutToAppear.`) 714 } 715 716 build() { 717 Column() { 718 Text($r('app.string.bluetooth_pairing_request')) 719 .fontSize($r('app.float.font_20')) 720 .height($r('app.float.wh_value_56')) 721 .fontColor($r("sys.color.ohos_id_color_primary")) 722 .width(ConfigData.WH_100_100) 723 .fontWeight(500) 724 .padding({ 725 left: $r('app.float.distance_24'), 726 top: $r('app.float.distance_14'), 727 bottom: $r('app.float.distance_14') 728 }) 729 730 Column() { 731 if (this.pinCode) { 732 Text($r('app.string.bluetooth_pairing_intelligent_device_hit', this.deviceName, this.deviceName)) 733 .fontSize($r('sys.float.ohos_id_text_size_body1')) 734 .fontColor($r("sys.color.ohos_id_color_primary")) 735 .width(ConfigData.WH_100_100) 736 .fontWeight(FontWeight.Regular) 737 .margin({ bottom: $r('app.float.distance_16') }) 738 739 Text(`${this.pinCode}`) 740 .fontSize($r('app.float.pinCode_font_size')) 741 .fontWeight(500) 742 .fontColor($r("sys.color.ohos_id_color_primary")) 743 .width(ConfigData.WH_100_100) 744 .textAlign(TextAlign.Center) 745 .margin({ 746 top: $r('app.float.distance_6'), 747 bottom: $r('app.float.distance_10') 748 }) 749 } else { 750 Text($r('app.string.bluetooth_pairing_media_device_hit')) 751 .fontSize($r('app.float.font_16')) 752 .fontColor($r("sys.color.ohos_id_color_primary")) 753 .width(ConfigData.WH_100_100) 754 .margin({ bottom: $r('app.float.switch_summary_margin') }) 755 756 Text(this.deviceName) 757 .fontSize($r('app.float.font_16')) 758 .width(ConfigData.WH_100_100) 759 .fontWeight(FontWeight.Bold) 760 } 761 762 // button 763 Flex({ justifyContent: FlexAlign.Center, alignItems: ItemAlign.Center }) { 764 Button() { 765 Text($r('app.string.cancel')) 766 .fontSize($r('app.float.font_16')) 767 .fontColor('#007DFF') 768 .fontWeight(500) 769 } 770 .backgroundColor($r("app.color.white_bg_color")) 771 .width($r("app.float.wh_value_160")) 772 .height($r("app.float.wh_value_40")) 773 .flexGrow(1) 774 .onClick(() => { 775 if (this.dialogController) { 776 this.dialogController.close(); 777 } 778 this.action(false); 779 }) 780 781 Divider() 782 .height($r("app.float.wh_value_24")) 783 .strokeWidth(0.5) 784 .vertical(true) 785 .color($r("sys.color.ohos_id_color_list_separator")) 786 787 Button() { 788 Text($r('app.string.bluetooth_pair_button')) 789 .fontSize($r('app.float.font_16')) 790 .fontColor('#007DFF') 791 .fontWeight(500) 792 } 793 .backgroundColor($r("app.color.white_bg_color")) 794 .width($r("app.float.wh_value_160")) 795 .height($r("app.float.wh_value_40")) 796 .flexGrow(1) 797 .onClick(() => { 798 if (this.dialogController) { 799 this.dialogController.close(); 800 } 801 this.action(true); 802 }) 803 } 804 .width(ConfigData.WH_100_100) 805 .height($r('app.float.wh_value_56')) 806 .margin({ top: $r('app.float.wh_value_10') }) 807 .padding({ bottom: $r('app.float.wh_value_16') }) 808 } 809 .width(ConfigData.WH_100_100) 810 .padding({ 811 left: $r('app.float.distance_24'), 812 right: $r('app.float.distance_24') 813 }) 814 } 815 .width(deviceTypeInfo === 'phone' || deviceTypeInfo === 'default' ? ConfigData.WH_100_100 : $r("app.float.wh_value_410")) 816 .padding(deviceTypeInfo === 'phone' || deviceTypeInfo === 'default' ? { 817 left: $r("app.float.wh_value_12"), 818 right: $r("app.float.wh_value_12") 819 } : {}) 820 } 821} 822 823/** 824 * Get device icon resource 825 * @param type 826 * @return device icon path 827 */ 828function getDeviceIconPath(deviceType: string): string { 829 let path: string = "/res/image/ic_bluetooth_device.svg"; 830 switch (deviceType) { 831 case DeviceType.HEADPHONE: 832 path = "/res/image/ic_device_earphone_hero.svg"; 833 break; 834 835 case DeviceType.PHONE: 836 path = "/res/image/ic_public_devices_phone.svg"; 837 break; 838 839 case DeviceType.WATCH: 840 path = "/res/image/ic_device_watch.svg"; 841 break; 842 843 case DeviceType.COMPUTER: 844 path = "/res/image/ic_device_matebook.svg"; 845 break; 846 } 847 return path; 848} 849 850/** 851 * Pair mode prompt 852 * @param dialogTitle Dialog title 853 * @param dialogMessage Dialog message 854 * @param buttonValue Dialog buttonValue 855 */ 856function showDialog(dialogTitle: string | Resource, dialogMessage: string | Resource, buttonValue: string | Resource) { 857 LogUtil.info(ConfigData.TAG + 'Bluetooth page showDialog in.'); 858 859 AlertDialog.show({ 860 title: dialogTitle, 861 message: dialogMessage, 862 confirm: { 863 value: buttonValue, 864 action: () => { 865 LogUtil.info(ConfigData.TAG + 'Bluetooth page showDialog : Button-clicking callback'); 866 } 867 }, 868 cancel: () => { 869 LogUtil.info(ConfigData.TAG + 'Bluetooth page showDialog : Closed callbacks'); 870 } 871 }) 872 873 LogUtil.info(ConfigData.TAG + 'Bluetooth page showDialog out.'); 874}