1/* 2 * Copyright (c) 2025 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 UIAbility from '@ohos.app.ability.UIAbility'; 17import Want from '@ohos.app.ability.Want'; 18import notificationManager from '@ohos.notificationManager'; 19import Notification from '@ohos.notification'; 20import Base from '@ohos.base'; 21import image from '@ohos.multimedia.image'; 22import fs from '@ohos.file.fs'; 23import wantAgent from '@ohos.app.ability.wantAgent'; 24import { WantAgent } from '@ohos.app.ability.wantAgent'; 25import commonEventManager from '@ohos.commonEventManager'; 26import resourceManager from '@ohos.resourceManager'; 27import opp from '@ohos.bluetooth.opp'; 28import type { BusinessError } from '@ohos.base'; 29import fileUri from '@ohos.file.fileuri'; 30import rpc from '@ohos.rpc'; 31import { DrawableDescriptor } from '@ohos.arkui.drawableDescriptor'; 32import backgroundTaskManager from '@ohos.backgroundTaskManager'; 33import systemParameterEnhance from '@ohos.systemParameterEnhance'; 34 35const TAG: string = '[BT_RECEIVE_SERVICE]==>' 36const EVENT_TYPE_TRANSACTION: number = 6; 37const BT_TRANSACTION_EVENT: string = 'usual.event.bluetooth.REPORT.TRANSACTION'; 38 39const RESULT_SUCCESS: number = 0; 40const RESULT_ERROR_UNSUPPORTED_TYPE: number = 1; 41const RESULT_ERROR_BAD_REQUEST: number = 2; 42const RESULT_ERROR_NOT_ACCEPTABLE: number = 3; 43const RESULT_ERROR_CANCELED: number = 4; 44const RESULT_ERROR_CONNECTION_FAILED: number = 5; 45const RESULT_ERROR_TRANSFER_FAILED: number = 6; 46const RESULT_ERROR_UNKNOWN: number = 7; 47const RESULT_SUCCESS_DISCONNECT: number = 9; 48 49const STATUS_PENDING: number = 0; 50const STATUS_RUNNING: number = 1; 51const STATUS_FINISH: number = 2; 52const OPP_TRANSFER_USER_ACCEPT: number = 0; 53const OPP_TRANSFER_AUTO_ACCEPT: number = 1; 54const UNKNOWN_VALUE: number = -1; 55 56const btTransactionStatisticsType = { 57 TRANSACTION_TYPE_OPP_SEND : 1, 58 TRANSACTION_TYPE_OPP_RECEIVE : 2, 59}; 60 61const btTransactionStatisticsResult = { 62 TRANSACTION_RESULT_TOTAL : 1, 63 TRANSACTION_RESULT_SUCCESS : 2, 64 TRANSACTION_RESULT_FAIL : 3, 65}; 66 67const btTransactionStatisticsSceneCode = { 68 TRANSACTION_SCENECODE_NA : 0, 69 TRANSACTION_SCENECODE_1 : 1, 70 TRANSACTION_SCENECODE_2 : 2, 71 TRANSACTION_SCENECODE_3 : 3, 72 TRANSACTION_SCENECODE_4 : 4, 73 TRANSACTION_SCENECODE_5 : 5, 74 TRANSACTION_SCENECODE_6 : 6, 75 TRANSACTION_SCENECODE_7 : 7, 76 TRANSACTION_SCENECODE_8 : 8, 77 TRANSACTION_SCENECODE_9 : 9, 78 TRANSACTION_SCENECODE_10 : 10, 79 TRANSACTION_SCENECODE_11 : 11, 80 TRANSACTION_SCENECODE_12 : 12, 81 TRANSACTION_SCENECODE_13 : 13, 82 TRANSACTION_SCENECODE_14 : 14, 83 TRANSACTION_SCENECODE_15 : 15, 84 TRANSACTION_SCENECODE_16 : 16, 85}; 86 87export default class BluetoothReceiveUIAbility extends UIAbility { 88 private subscriber: commonEventManager.CommonEventSubscriber | null = null; 89 private subscribeinfo: commonEventManager.CommonEventSubscribeInfo = { 90 events: [ 91 'usual.event.bluetooth.OPP.TAP.ACCEPT', 92 'usual.event.bluetooth.OPP.TAP.REJECT', 93 'usual.event.bluetooth.OPP.TAP.REMOVE', 94 'ohos.event.notification.BT.LIVEVIEW_REMOVE', 95 'usual.event.bluetooth.OPP.FINISH.NOTIFICATION', 96 'usual.event.bluetooth.OPP.FINISH.REMOVE', 97 'ohos.event.notification.BT.GET_URI', 98 'usual.event.bluetooth.OPP.RECEIVE' 99 ] 100 } 101 private cancelTransEvent: string = 'ohos.event.notification.BT.TAP_CANCEL'; 102 private capsuleNotificationID: number = 100; 103 private timeInterval: number = 0; 104 private receiveDirectory: string = ''; 105 private fileName: string = ''; 106 private sandBoxUri: string = ''; 107 private oppProfile = opp.createOppServerProfile(); 108 private tapEventIsAllowed = true; 109 private transfering: boolean = false; 110 private timerIdCreate: number; 111 private timerIdReceive: number; 112 private fileNameToReceiveMap = new Map(); 113 private totalCount: number = 0; 114 private currentCount: number = 0; 115 private successCount: number = 0; 116 private failedCount: number = 0; 117 private isEnabledLive2: boolean = false; 118 119 onCreate(want: Want): void { 120 console.info(TAG, 'BluetoothReceiveUIAbility onCreate'); 121 this.readyReceiveEvent(); 122 this.startContinuousTask(); 123 this.timerIdCreate = setTimeout(() => { 124 console.info(TAG, 'onCreate 1 second but nothing received'); 125 this.reportBtTransactionChr(btTransactionStatisticsResult.TRANSACTION_RESULT_FAIL, 126 1, btTransactionStatisticsSceneCode.TRANSACTION_SCENECODE_1, 1); 127 this.handleTerminate(); 128 }, 1000); 129 } 130 131 onDestroy(): void { 132 console.info(TAG, 'BluetoothReceiveUIAbility onDestroy'); 133 } 134 135 onForeground(): void { 136 console.info(TAG, 'BluetoothReceiveUIAbility onForeground'); 137 } 138 139 handleTerminate() { 140 console.info(TAG, 'handleTerminate'); 141 this.stopContinuousTask(); 142 this.context.terminateSelf(); 143 } 144 145 reportBtChrDataByResult(dataResult: number) { 146 console.info(TAG, 'reportBtChrDataByResult dataResult ' + dataResult + 147 ' totalCount' + this.totalCount + ' successCount' + this.successCount); 148 if (dataResult == RESULT_SUCCESS) { 149 this.reportBtTransactionChr(btTransactionStatisticsResult.TRANSACTION_RESULT_SUCCESS, 150 1, btTransactionStatisticsSceneCode.TRANSACTION_SCENECODE_NA, 0); 151 } else if ((dataResult == RESULT_ERROR_NOT_ACCEPTABLE || 152 dataResult == RESULT_ERROR_BAD_REQUEST) && this.totalCount > 0) { 153 this.reportBtTransactionChr(btTransactionStatisticsResult.TRANSACTION_RESULT_FAIL, 154 this.totalCount, dataResult, this.totalCount); 155 this.reportBtTransactionChr(btTransactionStatisticsResult.TRANSACTION_RESULT_TOTAL, 156 this.totalCount - 1, btTransactionStatisticsSceneCode.TRANSACTION_SCENECODE_NA, 0); 157 } else if ((dataResult == RESULT_ERROR_CANCELED || dataResult == RESULT_ERROR_TRANSFER_FAILED) && 158 this.totalCount - this.successCount >= 1) { 159 this.reportBtTransactionChr(btTransactionStatisticsResult.TRANSACTION_RESULT_FAIL, 160 this.totalCount - this.successCount, dataResult, this.totalCount - this.successCount); 161 this.reportBtTransactionChr(btTransactionStatisticsResult.TRANSACTION_RESULT_TOTAL, 162 this.totalCount - this.successCount -1, btTransactionStatisticsSceneCode.TRANSACTION_SCENECODE_NA, 0); 163 } else if (dataResult == RESULT_ERROR_CONNECTION_FAILED) { 164 this.reportBtTransactionChr(btTransactionStatisticsResult.TRANSACTION_RESULT_FAIL, 1, dataResult, 1); 165 } 166 this.reportBtTransactionChr(btTransactionStatisticsResult.TRANSACTION_RESULT_TOTAL, 167 1, btTransactionStatisticsSceneCode.TRANSACTION_SCENECODE_NA, 0); 168 } 169 170 reportBtTransactionChr(result: number, resultCount: number, sceneCode: number, sceneCodeCount: number) { 171 const options: commonEventManager.CommonEventPublishData = { 172 code: 0, 173 data: 'message', 174 subscriberPermissions: [], 175 isOrdered: true, 176 isSticky: false, 177 parameters: { 'transactionType': btTransactionStatisticsType.TRANSACTION_TYPE_OPP_RECEIVE, 'result': result, 178 'resultCount': resultCount, 'sceneCode': sceneCode, 'sceneCodeCount': sceneCodeCount} 179 } 180 commonEventManager.publish(BT_TRANSACTION_EVENT, options, (err) => { 181 if (err) { 182 console.info(TAG, 'get bt transaction event publish failed.' + JSON.stringify(err)); 183 } else { 184 console.info(TAG, 'get bt transaction event publish success.'); 185 } 186 }) 187 } 188 189 async readyReceiveEvent() { 190 try { 191 console.info(TAG, 'readyReceiveEvent'); 192 clearTimeout(this.timerIdCreate); 193 this.subscriber = commonEventManager.createSubscriberSync(this.subscribeinfo); 194 try { 195 await commonEventManager.subscribe(this.subscriber, 196 (err: BusinessError, data: commonEventManager.CommonEventData) => { 197 if (err) { 198 console.error(TAG, `subscribe failed, code is ${err.code}, message is ${err.message}`); 199 this.handleTerminate(); 200 } else { 201 this.handleReceivedEvent(data); 202 } 203 }); 204 } catch (error) { 205 let err: BusinessError = error as BusinessError; 206 console.error(TAG, `subscribe failed, code is ${err.code}, message is ${err.message}`); 207 this.handleTerminate(); 208 } 209 } catch (error) { 210 let err: BusinessError = error as BusinessError; 211 console.error(TAG, `createSubscriber failed, code is ${err.code}, message is ${err.message}`); 212 this.handleTerminate(); 213 } 214 } 215 216 handleReceivedEvent(data: commonEventManager.CommonEventData) { 217 console.info(TAG, 'handleReceivedEvent: ' + data.event); 218 switch (data.event) { 219 case 'usual.event.bluetooth.OPP.TAP.ACCEPT': { 220 console.info(TAG, 'OPP.TAP.ACCEPT tapEventIsAllowed is ' + this.tapEventIsAllowed); 221 if (data.parameters == undefined) { 222 console.error(TAG, `data.parameters undefined`); 223 break; 224 } 225 let acceptType = data.parameters?.['acceptType']; 226 console.info(TAG, 'acceptType is ' + acceptType); 227 if (acceptType == OPP_TRANSFER_USER_ACCEPT && this.tapEventIsAllowed) { 228 clearTimeout(this.timerIdReceive); 229 this.currentCount = 1; 230 this.subscriberLiveViewNotification(); 231 this.startOPPReceiveServiceUIAbility(); 232 this.tapEventIsAllowed = false; 233 } else if (acceptType == OPP_TRANSFER_AUTO_ACCEPT) { 234 this.fileName = data.parameters?.['fileName']; 235 this.currentCount = data.parameters?.['currentCount']; 236 this.totalCount = data.parameters?.['totalCount']; 237 this.startOPPReceiveServiceUIAbility(); 238 } 239 break; 240 } 241 case 'usual.event.bluetooth.OPP.TAP.REJECT': { 242 console.info(TAG, 'tapEventIsAllowed is ' + this.tapEventIsAllowed); 243 if (!this.tapEventIsAllowed) { 244 break; 245 } 246 clearTimeout(this.timerIdReceive); 247 this.transfering = false; 248 this.oppProfile.setIncomingFileConfirmation(false, -1); 249 this.reportBtTransactionChr(btTransactionStatisticsResult.TRANSACTION_RESULT_FAIL, 250 this.totalCount, btTransactionStatisticsSceneCode.TRANSACTION_SCENECODE_2, this.totalCount); 251 this.reportBtTransactionChr(btTransactionStatisticsResult.TRANSACTION_RESULT_TOTAL, 252 this.totalCount, btTransactionStatisticsSceneCode.TRANSACTION_SCENECODE_NA, 0); 253 this.pullUpReceiveResultNotification(); 254 this.handleTerminate(); 255 this.tapEventIsAllowed = false; 256 break; 257 } 258 case 'usual.event.bluetooth.OPP.TAP.REMOVE': { 259 console.info(TAG, 'tapEventIsAllowed is ' + this.tapEventIsAllowed); 260 clearTimeout(this.timerIdReceive); 261 this.transfering = false; 262 this.oppProfile.setIncomingFileConfirmation(false, -1); 263 this.reportBtTransactionChr(btTransactionStatisticsResult.TRANSACTION_RESULT_FAIL, 264 this.totalCount, btTransactionStatisticsSceneCode.TRANSACTION_SCENECODE_5, this.totalCount); 265 this.reportBtTransactionChr(btTransactionStatisticsResult.TRANSACTION_RESULT_TOTAL, 266 this.totalCount, btTransactionStatisticsSceneCode.TRANSACTION_SCENECODE_NA, 0); 267 this.pullUpReceiveResultNotification(); 268 this.handleTerminate(); 269 this.tapEventIsAllowed = true; 270 break; 271 } 272 case 'ohos.event.notification.BT.LIVEVIEW_REMOVE': { 273 this.tapEventIsAllowed = true; 274 this.oppProfile.cancelTransfer(); 275 this.pullUpReceiveResultNotification(); 276 break; 277 } 278 case 'usual.event.bluetooth.OPP.FINISH.NOTIFICATION': { 279 this.oppProfile.setLastReceivedFileUri(this.receiveDirectory); 280 setTimeout(() => { 281 if (this.transfering) { 282 console.info(TAG, 'still transfering, do not terminate'); 283 return; 284 } 285 console.info(TAG, 'transfer finished, terminateSelf'); 286 this.handleTerminate(); 287 }, 100); 288 break; 289 } 290 case 'usual.event.bluetooth.OPP.FINISH.REMOVE': { 291 setTimeout(() => { 292 if (this.transfering) { 293 console.info(TAG, 'still transfering, do not terminate'); 294 return; 295 } 296 console.info(TAG, 'transfer finished, terminateSelf'); 297 this.handleTerminate(); 298 }, 100); 299 break; 300 } 301 case 'ohos.event.notification.BT.GET_URI': { 302 this.handleBtUri(data); 303 break; 304 } 305 case 'usual.event.bluetooth.OPP.RECEIVE': { 306 clearTimeout(this.timerIdCreate); 307 if (data.parameters == undefined) { 308 console.error(TAG, 'data.parameters undefined'); 309 break; 310 } 311 this.fileName = data.parameters?.['fileName']; 312 this.totalCount = data.parameters?.['totalCount']; 313 if (this.fileNameToReceiveMap.has(this.fileName)) { 314 console.error(TAG, 'fileName is repeat, discard this OPP.RECEIVE'); 315 break; 316 } 317 this.fileNameToReceiveMap.set(this.fileName, true); 318 this.transfering = true; 319 this.pullUpNotification(this.fileName); 320 break; 321 } 322 default: { 323 break; 324 } 325 } 326 } 327 328 async handleBtUri(data: commonEventManager.CommonEventData) { 329 if (data.parameters == undefined) { 330 console.error(TAG, 'data.parameters undefined'); 331 return; 332 } 333 334 this.oppProfile.off('transferStateChange'); 335 this.oppProfile.on('transferStateChange', (data: opp.OppTransferInformation) => { 336 this.totalCount = data.totalCount; 337 this.currentCount = data.currentCount; 338 console.info(TAG, 'transferStateChange status' + data.status + ' result ' + data.result); 339 if (data.status == STATUS_RUNNING) { 340 this.pullUpSendProgressNotification(data.currentBytes / data.totalBytes * 100, this.fileName); 341 } else if (data.status == STATUS_FINISH) { 342 if (data.result != RESULT_SUCCESS_DISCONNECT) { 343 data.result == RESULT_SUCCESS ? this.successCount++ : this.failedCount++; 344 this.reportBtChrDataByResult(data.result); 345 } 346 if (data.result != RESULT_SUCCESS && data.filePath != undefined && data.filePath.trim().length > 0) { 347 this.tapEventIsAllowed = true; 348 console.info(TAG, `unlink file begin`); 349 const uri = new fileUri.FileUri(this.receiveDirectory); 350 let deleteSandBoxUri = uri.path; 351 fs.unlinkSync(deleteSandBoxUri); 352 } 353 if (data.currentCount == data.totalCount || data.result == RESULT_ERROR_CANCELED || 354 data.result == RESULT_ERROR_TRANSFER_FAILED) { 355 this.tapEventIsAllowed = true; 356 this.cancelSendProgressNotification(); 357 this.pullUpReceiveResultNotification(); 358 this.oppProfile.off('transferStateChange'); 359 console.info(TAG, 'transferStateChange' + data.result); 360 } 361 } 362 }); 363 364 if ((data.parameters.uri == undefined) || (data.parameters.uri == null) || 365 (data.parameters.uri.length == 0)) { 366 console.error(TAG, 'uri not right'); 367 return; 368 } 369 this.receiveDirectory = data.parameters.uri; 370 try { 371 const uri = new fileUri.FileUri(data.parameters.uri); 372 if (!fs.accessSync(uri.path)) { 373 console.error(TAG, 'uri path is not access'); 374 return; 375 } 376 if (data.parameters.fileType == 'files') { 377 this.sandBoxUri = uri.path + '/' + this.fileName; 378 this.receiveDirectory = this.receiveDirectory + '/' + this.fileName; 379 } else { 380 this.sandBoxUri = uri.path; 381 } 382 let file = fs.openSync(this.sandBoxUri, fs.OpenMode.CREATE | fs.OpenMode.READ_WRITE); 383 await this.oppProfile.setIncomingFileConfirmation(true, file.fd); 384 fs.close(file.fd); 385 } catch (err) { 386 console.error(TAG, `openFd failed, code: ${err?.code} message:${err?.message}`); 387 return; 388 } 389 } 390 391 handleReceive(data: commonEventManager.CommonEventData) { 392 this.pullUpNotification(this.fileName); 393 } 394 395 async pullUpNotification(fileName: string) { 396 console.info(TAG, 'pullUpNotification'); 397 let wantAgentObjAccept: WantAgent; 398 let wantAgentObjReject: WantAgent; 399 let wantAgentObjRemove: WantAgent; 400 wantAgentObjAccept = await this.getNotificationWantAgent('ohos.event.notification.BT.TAP_ACCEPT'); 401 wantAgentObjReject = await this.getNotificationWantAgent('ohos.event.notification.BT.TAP_REJECT'); 402 wantAgentObjRemove = await this.getNotificationWantAgent('ohos.event.notification.BT.TAP_REMOVE'); 403 await this.publishReceiveNotification(wantAgentObjReject, wantAgentObjAccept, wantAgentObjRemove, fileName); 404 } 405 406 async getNotificationWantAgent(info: string): Promise<WantAgent> { 407 let wantAgentObjUse: WantAgent; 408 let wantAgentInfo: wantAgent.WantAgentInfo = { 409 wants: [ 410 { 411 action: info, 412 } 413 ], 414 actionType: wantAgent.OperationType.SEND_COMMON_EVENT, 415 requestCode: 0, 416 wantAgentFlags: [wantAgent.WantAgentFlags.CONSTANT_FLAG], 417 }; 418 wantAgentObjUse = await wantAgent.getWantAgent(wantAgentInfo); 419 console.info(TAG, 'getNotificationWantAgent success for ' + info); 420 return wantAgentObjUse; 421 } 422 423 async publishReceiveNotification(wantAgentObjReject: WantAgent, wantAgentObjAccept: WantAgent, 424 wantAgentObjRemove: WantAgent, fileName: string) { 425 let waitTime: number = 50 * 1000; 426 let timeout: number = new Date().getTime() + waitTime; 427 let notificationRequest: notificationManager.NotificationRequest = { 428 content: { 429 notificationContentType: notificationManager.ContentType.NOTIFICATION_CONTENT_BASIC_TEXT, 430 normal: { 431 title: this.context.resourceManager.getStringSync($r('app.string.bluetooth_receive_notification_title').id), 432 text: fileName, 433 } 434 }, 435 id: 1, 436 notificationSlotType: notificationManager.SlotType.SERVICE_INFORMATION, 437 actionButtons: [ 438 { 439 title: this.context.resourceManager.getStringSync($r('app.string.bluetooth_receive_notification_button_reject').id), 440 wantAgent: wantAgentObjReject 441 }, 442 { 443 title: this.context.resourceManager.getStringSync($r('app.string.bluetooth_receive_notification_button_accept').id), 444 wantAgent: wantAgentObjAccept 445 } 446 ], 447 autoDeletedTime: timeout, 448 removalWantAgent: wantAgentObjRemove 449 }; 450 notificationManager.publish(notificationRequest).then(() => { 451 console.info(TAG, 'publishReceiveNotification success'); 452 this.timerIdReceive = setTimeout(() => { 453 console.info(TAG, 'receive notification 50 seconds but no tap'); 454 this.transfering = false; 455 this.oppProfile.setIncomingFileConfirmation(false, -1); 456 this.reportBtChrDataByResult(RESULT_ERROR_NOT_ACCEPTABLE); 457 this.pullUpReceiveResultNotification(); 458 this.handleTerminate(); 459 }, waitTime); 460 }).catch((err: Base.BusinessError) => { 461 console.error(TAG, 'publishReceiveNotification fail'); 462 this.handleTerminate(); 463 }); 464 } 465 466 resetCount() { 467 console.info(TAG, 'resetCount'); 468 this.totalCount = 0; 469 this.currentCount = 0; 470 this.successCount = 0; 471 this.failedCount = 0; 472 } 473 474 async publishFinishNotification(wantAgentObj: WantAgent, wantAgentObjRemove: WantAgent, 475 successNum: number, failNum: number) { 476 console.info(TAG, 'publishFinishNotification'); 477 let notificationRequest: notificationManager.NotificationRequest = { 478 content: { 479 notificationContentType: notificationManager.ContentType.NOTIFICATION_CONTENT_BASIC_TEXT, 480 normal: { 481 title: this.context.resourceManager.getStringSync($r('app.string.bluetooth_receive_finish_title').id), 482 text: this.getFormatString($r('app.string.bluetooth_receive_finish_text'), 483 this.getFormatPlural($r('app.plural.bluetooth_receive_finish_success_text', successNum, successNum), successNum), 484 this.getFormatPlural($r('app.plural.bluetooth_receive_finish_fail_text', failNum, failNum), failNum)) 485 } 486 }, 487 id: 2, 488 notificationSlotType: notificationManager.SlotType.SERVICE_INFORMATION, 489 wantAgent: wantAgentObj, 490 tapDismissed: true, 491 removalWantAgent: wantAgentObjRemove 492 }; 493 notificationManager.publish(notificationRequest).then(() => { 494 console.info(TAG, 'publishFinishNotification show success'); 495 this.resetCount(); 496 }).catch((err: Base.BusinessError) => { 497 console.error(TAG, 'publishFinishNotification fail'); 498 this.handleTerminate(); 499 }); 500 } 501 502 startOPPReceiveServiceUIAbility() { 503 console.info(TAG, 'startOPPReceiveServiceUIAbility'); 504 AppStorage.setOrCreate('oppProfile', this.oppProfile); 505 this.oppProfile.setIncomingFileConfirmation(true, -1); 506 } 507 508 async pullUpReceiveResultNotification() { 509 console.info(TAG, 'pullUpNotification successCount' + this.successCount + 510 'failedCount' + this.failedCount + 'totalCount' + this.totalCount); 511 if (this.successCount + this.failedCount != this.totalCount && this.totalCount >= this.successCount) { 512 this.failedCount = this.totalCount - this.successCount; 513 } 514 let wantAgentObj: WantAgent; 515 let wantAgentObjRemove: WantAgent; 516 wantAgentObj = await this.getNotificationWantAgent('ohos.event.notification.BT.FINISH_NOTIFICATION'); 517 wantAgentObjRemove = await this.getNotificationWantAgent('ohos.event.notification.BT.FINISH_REMOVE'); 518 await this.publishFinishNotification(wantAgentObj, wantAgentObjRemove, this.successCount, this.failedCount); 519 } 520 521 async publishTransProgessNotification(imagePixelMapButton: image.PixelMap, imagePixelMapCapsule: image.PixelMap, 522 wantAgentObjRemove: WantAgent, percent: number, name: string, currentCount: number, totalCount: number) { 523 console.info(TAG, 'publishTransProgessNotification'); 524 let progressTitle = this.getFormatNum($r('app.string.bluetooth_receive_count_text'), currentCount, totalCount); 525 let progressName = name; 526 if (this.totalCount == UNKNOWN_VALUE) { 527 progressTitle = 528 this.context.resourceManager.getStringSync($r('app.string.bluetooth_transfer_notification_title').id); 529 progressName = 530 this.context.resourceManager.getStringSync($r('app.string.bluetooth_transfer_receive_text').id) + name; 531 } 532 let notificationRequest: notificationManager.NotificationRequest = { 533 notificationSlotType: notificationManager.SlotType.LIVE_VIEW, 534 id: this.capsuleNotificationID, 535 content: { 536 notificationContentType: notificationManager.ContentType.NOTIFICATION_CONTENT_SYSTEM_LIVE_VIEW, 537 systemLiveView: { 538 title: progressTitle, 539 text: progressName, 540 typeCode: 8, 541 button: { 542 names: [this.cancelTransEvent], 543 iconsResource: [$r('app.media.public_cancel_filled')], 544 }, 545 capsule: { 546 title: 'bluetooth', 547 icon: imagePixelMapCapsule, 548 backgroundColor: '#0A59F7', 549 }, 550 progress: { 551 maxValue: 100, 552 currentValue: percent, 553 isPercentage: true, 554 }, 555 } 556 }, 557 tapDismissed: false, 558 removalWantAgent: wantAgentObjRemove 559 }; 560 561 notificationManager.publish(notificationRequest).then(() => { 562 console.info(TAG, 'publishTransProgessNotification success'); 563 if (percent == 100 && this.currentCount == this.totalCount) { 564 this.cancelSendProgressNotification(); 565 } 566 }).catch((err: Base.BusinessError) => { 567 console.error(TAG, 'publishTransProgessNotification fail'); 568 }); 569 } 570 571 async pullUpSendProgressNotification(percent: number, name: string) { 572 const currentDate: Date = new Date(); 573 const currentTimeInMsUsingGetTime: number = currentDate.getTime(); 574 if (percent !== 100 && (currentTimeInMsUsingGetTime - this.timeInterval) < 1000) { 575 return; 576 } 577 this.timeInterval = currentTimeInMsUsingGetTime; 578 579 console.info(TAG, 'ready to pullUpSendProgressNotification'); 580 this.isEnabledLive2 = systemParameterEnhance.getSync('persist.systemui.live2', 'false') == 'true'; 581 console.info(TAG, 'this.isEnabledLive2 = ' + this.isEnabledLive2); 582 583 let imagePixelMapButton: image.PixelMap | undefined = undefined; 584 let imagePixelMapCapsule: image.PixelMap | undefined = undefined; 585 try { 586 let drawableDescriptor1: DrawableDescriptor = this.context.resourceManager.getDrawableDescriptor($r('app.media.public_cancel_filled').id); 587 imagePixelMapButton = drawableDescriptor1.getPixelMap(); 588 let drawableDescriptor2: DrawableDescriptor; 589 if (!this.isEnabledLive2) { 590 drawableDescriptor2 = 591 this.context.resourceManager.getDrawableDescriptor($r('app.media.foreground').id); 592 } else { 593 drawableDescriptor2 = 594 this.context.resourceManager.getDrawableDescriptor($r('app.media.foregroundSmall').id); 595 } 596 imagePixelMapCapsule = drawableDescriptor2.getPixelMap(); 597 } catch (error) { 598 let code = (error as BusinessError).code; 599 let message = (error as BusinessError).message; 600 console.error(TAG, `getDrawableDescriptor failed, error code is ${code}, message is ${message}`); 601 return; 602 } 603 604 let wantAgentObjRemove: WantAgent; 605 wantAgentObjRemove = await this.getNotificationWantAgent('ohos.event.notification.BT.LIVEVIEW_REMOVE'); 606 await this.publishTransProgessNotification(imagePixelMapButton, imagePixelMapCapsule, 607 wantAgentObjRemove, percent, name, this.currentCount, this.totalCount); 608 } 609 610 cancelSendProgressNotification() { 611 console.info(TAG, 'cancelSendProgressNotification ready to cancel.'); 612 notificationManager.cancel(this.capsuleNotificationID).then(() => { 613 console.info(TAG, 'Succeeded in canceling notification'); 614 }).catch((err: BusinessError) => { 615 console.error(TAG, `failed to cancel notification. Code is ${err.code}, message is ${err.message}`) 616 }); 617 this.transfering = false; 618 if (this.successCount == 0) { 619 console.info(TAG, 'no success file, terminate it.'); 620 this.handleTerminate(); 621 } 622 } 623 624 subscriberLiveViewNotification(): void { 625 let subscriber: notificationManager.SystemLiveViewSubscriber = { 626 onResponse: (id: number, option: notificationManager.ButtonOptions) => { 627 switch (option.buttonName) { 628 case this.cancelTransEvent: { 629 console.info(TAG, 'cancel transfer.'); 630 this.tapEventIsAllowed = true; 631 this.oppProfile.cancelTransfer(); 632 this.cancelSendProgressNotification(); 633 this.pullUpReceiveResultNotification(); 634 break; 635 } 636 default: { 637 break; 638 } 639 } 640 } 641 }; 642 try { 643 notificationManager.subscribeSystemLiveView(subscriber); 644 } catch(e) { 645 console.error(TAG, 'subscriberLiveViewNotification fail'); 646 } 647 } 648 649 getFormatString(resource: Resource, value1: string, value2: string): string { 650 let result = this.context.resourceManager.getStringSync(resource.id); 651 result = result.replace('%1$s', value1); 652 result = result.replace('%2$s', value2); 653 return result; 654 } 655 656 getFormatNum(resource: Resource, value1: number, value2: number): string { 657 let result = this.context.resourceManager.getStringSync(resource.id); 658 result = result.replace('%1$d', value1.toString()); 659 result = result.replace('%2$d', value2.toString()); 660 return result; 661 } 662 663 getFormatPlural(resource: Resource, value: number): string { 664 let result = this.context.resourceManager.getPluralStringValueSync(resource.id, value); 665 result = result.replace('%d', value.toString()); 666 return result; 667 } 668 669 async startContinuousTask() { 670 let wantAgentObj: WantAgent; 671 wantAgentObj = await this.getNotificationWantAgent('ohos.event.notification.BT.BACK_RUNNING'); 672 backgroundTaskManager.startBackgroundRunning(this.context, 673 backgroundTaskManager.BackgroundMode.BLUETOOTH_INTERACTION, wantAgentObj).then(() => { 674 console.info(TAG, `Succeeded in operationing startBackgroundRunning.`); 675 }).catch((err: BusinessError) => { 676 console.error(TAG, `Failed to operation startBackgroundRunning. Code is ${err.code}, message is ${err.message}`); 677 }); 678 } 679 680 stopContinuousTask() { 681 backgroundTaskManager.stopBackgroundRunning(this.context).then(() => { 682 console.info(TAG, `Succeeded in operationing stopBackgroundRunning.`); 683 }).catch((err: BusinessError) => { 684 console.error(TAG, `Failed to operation stopBackgroundRunning. Code is ${err.code}, message is ${err.message}`); 685 }); 686 } 687} 688