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