• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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