• 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 image from '@ohos.multimedia.image';
20import wantAgent from '@ohos.app.ability.wantAgent';
21import { WantAgent } from '@ohos.wantAgent';
22import { BusinessError } from '@ohos.base';
23import { DrawableDescriptor } from '@ohos.arkui.drawableDescriptor';
24import opp from '@ohos.bluetooth.opp';
25import commonEventManager from '@ohos.commonEventManager';
26import backgroundTaskManager from '@ohos.backgroundTaskManager';
27import fs from '@ohos.file.fs';
28
29const TAG: string = '[BT_SEND_SERVICE]==>'
30
31export default class BluetoothSendUIAbility extends UIAbility {
32    private oppProfile = opp.createOppServerProfile();
33    private timeInterval: number = 0;
34    private capsuleNotificationID: number = 200;
35    private cancelTransEvent: string = 'ohos.event.notification.BT.TAP_CANCEL';
36    private subscriber: commonEventManager.CommonEventSubscriber | null = null;
37    private subscribeinfo: commonEventManager.CommonEventSubscribeInfo = {
38        events: [
39            'ohos.event.notification.BT.LIVEVIEW_REMOVE'
40        ]
41    };
42    private timerIdCreate: number;
43    private timerIdCreated: boolean = false;
44
45    onCreate(want: Want): void {
46        console.info(TAG, 'BluetoothSendUIAbility onCreate');
47        this.timerIdCreate = setTimeout(() => {
48            console.info(TAG, 'onCreate 10 seconds but nothing received');
49            this.handleTerminate();
50        }, 10000);
51        this.timerIdCreated = true;
52        this.subscribeTransferState();
53        this.readyReceiveEvent();
54        this.startContinuousTask();
55    }
56
57    onDestroy(): void {
58        console.info(TAG, 'BluetoothSendUIAbility onDestroy');
59    }
60
61    onForeground(): void {
62        console.info(TAG, 'BluetoothSendUIAbility onForeground');
63    }
64
65    handleTerminate() {
66        this.stopContinuousTask();
67        this.context.terminateSelf();
68    }
69
70    readyReceiveEvent() {
71        try {
72            console.info(TAG, 'readyReceiveEvent');
73            this.subscriber = commonEventManager.createSubscriberSync(this.subscribeinfo);
74            try {
75                commonEventManager.subscribe(this.subscriber,
76                (err: BusinessError, data: commonEventManager.CommonEventData) => {
77                    if (err) {
78                        console.error(TAG, `subscribe failed, code is ${err.code}, message is ${err.message}`);
79                        this.handleTerminate();
80                    } else {
81                        this.handleReceivedEvent(data);
82                    }
83                });
84            } catch (error) {
85                let err: BusinessError = error as BusinessError;
86                console.error(TAG, `subscribe failed, code is ${err.code}, message is ${err.message}`);
87                this.handleTerminate();
88            }
89        } catch (error) {
90            let err: BusinessError = error as BusinessError;
91            console.error(TAG, `createSubscriber failed, code is ${err.code}, message is ${err.message}`);
92            this.handleTerminate();
93        }
94    }
95
96    handleReceivedEvent(data: commonEventManager.CommonEventData) {
97        console.info(TAG, 'handleReceivedEvent: ' + data.event);
98        switch (data.event) {
99          case 'ohos.event.notification.BT.LIVEVIEW_REMOVE': {
100              this.oppProfile.cancelTransfer();
101              this.handleTerminate();
102              break;
103          }
104          default: {
105              break;
106          }
107        }
108    }
109
110    subscribeTransferState() {
111        try {
112            this.subscriberLiveViewNotification();
113            this.oppProfile.on('transferStateChange', (data: opp.OppTransferInformation) => {
114                if (this.timerIdCreated) {
115                    this.timerIdCreated = false;
116                    clearTimeout(this.timerIdCreate);
117                }
118                if (data.status == 1) {
119                    this.pullUpSendProgressNotification(data.currentBytes / data.totalBytes * 100,
120                    this.getFileName(data.filePath));
121                } else if (data.status == 2) {
122                    if (data.result != 0) {
123                        console.info(TAG, 'send fail');
124                        this.cancelSendProgressNotification(0);
125                        this.pullUpReceiveResultNotification(0, 1);
126                    }
127                    console.info(TAG, 'transfer finished');
128                    this.oppProfile.off('transferStateChange');
129                    let dirPath = this.context.filesDir;
130                    console.log(TAG, 'delete dirPath is ' + dirPath);
131                    fs.rmdirSync(dirPath);
132                }
133            });
134            console.log(TAG, 'oppProfile.transferStateChange');
135        } catch (err) {
136            console.error(TAG, 'subscribeTransferState err');
137            this.handleTerminate();
138        }
139    }
140
141    async getNotificationWantAgent(info: string): Promise<WantAgent> {
142        let wantAgentObjUse: WantAgent;
143        let wantAgentInfo: wantAgent.WantAgentInfo = {
144            wants: [
145                {
146                    action: info,
147                }
148            ],
149            actionType: wantAgent.OperationType.SEND_COMMON_EVENT,
150            requestCode: 0,
151            wantAgentFlags: [wantAgent.WantAgentFlags.CONSTANT_FLAG],
152        };
153        wantAgentObjUse = await wantAgent.getWantAgent(wantAgentInfo);
154        console.info(TAG, 'getNotificationWantAgent success for ' + info);
155        return wantAgentObjUse;
156    }
157
158    async publishFinishNotification(wantAgentObj: WantAgent, successNum: number, failNum: number) {
159        console.info(TAG, 'publishFinishNotification');
160        let notificationRequest: notificationManager.NotificationRequest = {
161            content: {
162                notificationContentType: notificationManager.ContentType.NOTIFICATION_CONTENT_BASIC_TEXT,
163                normal: {
164                    title: this.context.resourceManager.getStringSync($r('app.string.bluetooth_send_finish_title').id),
165                    text: this.getFormatString($r('app.string.bluetooth_send_finish_text'),
166                        this.getFormatPlural($r('app.plural.bluetooth_send_finish_success_text', successNum, successNum), successNum),
167                        this.getFormatPlural($r('app.plural.bluetooth_send_finish_fail_text', failNum, failNum), failNum))
168                }
169            },
170            id: 2,
171            notificationSlotType: notificationManager.SlotType.SERVICE_INFORMATION,
172            wantAgent: wantAgentObj,
173            tapDismissed: true,
174        };
175        notificationManager.publish(notificationRequest).then(() => {
176            console.info(TAG, 'publishFinishNotification success');
177        }).catch((err: BusinessError) => {
178            console.error(TAG, 'publishFinishNotification fail');
179            this.handleTerminate();
180        });
181    }
182
183    async pullUpReceiveResultNotification(successNum: number, failNum: number) {
184        console.info(TAG, 'pullUpReceiveResultNotification');
185        let wantAgentObj: WantAgent;
186        wantAgentObj = await this.getNotificationWantAgent('ohos.event.notification.BT.FINISH_SEND');
187        await this.publishFinishNotification(wantAgentObj, successNum, failNum);
188    }
189
190    async publishTransProgessNotification(imagePixelMapButton: image.PixelMap, imagePixelMapCapsule: image.PixelMap,
191        wantAgentObjRemove: WantAgent, percent: number, name: string) {
192        console.info(TAG, 'publishTransProgessNotification');
193        let notificationRequest: notificationManager.NotificationRequest = {
194            notificationSlotType: notificationManager.SlotType.LIVE_VIEW,
195            id: this.capsuleNotificationID,
196            content: {
197                notificationContentType: notificationManager.ContentType.NOTIFICATION_CONTENT_SYSTEM_LIVE_VIEW,
198                systemLiveView: {
199                    title: this.context.resourceManager.getStringSync($r('app.string.bluetooth_transfer_notification_title').id),
200                    text: this.context.resourceManager.getStringSync($r('app.string.bluetooth_transfer_send_text').id) + name,
201                    typeCode: 8,
202                    button: {
203                        names: [this.cancelTransEvent],
204                        icons: [imagePixelMapButton],
205                    },
206                    capsule: {
207                        title: 'bluetooth',
208                        icon: imagePixelMapCapsule,
209                        backgroundColor: '#0A59F7',
210                    },
211                    progress: {
212                        maxValue: 100,
213                        currentValue: percent,
214                        isPercentage: true,
215                    },
216                }
217            },
218            tapDismissed: false,
219            removalWantAgent: wantAgentObjRemove
220        };
221
222        notificationManager.publish(notificationRequest).then(() => {
223            console.info(TAG, 'publishTransProgessNotification success');
224            if (percent === 100) {
225                this.cancelSendProgressNotification(1);
226            }
227        }).catch((err: BusinessError) => {
228            console.error(TAG, 'publishTransProgessNotification fail');
229        });
230    }
231
232    async pullUpSendProgressNotification(percent: number, name: string) {
233        const currentDate: Date = new Date();
234        const currentTimeInMsUsingGetTime: number = currentDate.getTime();
235        if (percent !== 100 && (currentTimeInMsUsingGetTime - this.timeInterval) < 1000) {
236            return;
237        }
238        this.timeInterval = currentTimeInMsUsingGetTime;
239
240        console.info(TAG, 'ready to pullUpSendProgressNotification');
241        let imagePixelMapButton: image.PixelMap | undefined = undefined;
242        let imagePixelMapCapsule: image.PixelMap | undefined = undefined;
243        try {
244            let drawableDescriptor1: DrawableDescriptor = this.context.resourceManager.getDrawableDescriptor($r('app.media.public_cancel_filled').id);
245            imagePixelMapButton = drawableDescriptor1.getPixelMap();
246            let drawableDescriptor2: DrawableDescriptor = this.context.resourceManager.getDrawableDescriptor($r('app.media.foreground').id);
247            imagePixelMapCapsule = drawableDescriptor2.getPixelMap();
248        } catch (error) {
249            let code = (error as BusinessError).code;
250            let message = (error as BusinessError).message;
251            console.error(TAG, `getDrawableDescriptor failed, error code is ${code}, message is ${message}`);
252            return;
253        }
254
255        let wantAgentObjRemove: WantAgent;
256        wantAgentObjRemove = await this.getNotificationWantAgent('ohos.event.notification.BT.LIVEVIEW_REMOVE');
257        await this.publishTransProgessNotification(imagePixelMapButton, imagePixelMapCapsule, wantAgentObjRemove,
258            percent, name);
259    }
260
261    cancelSendProgressNotification(successOrNot: number) {
262        console.info(TAG, 'cancelSendProgressNotification ready to cancel.');
263        notificationManager.cancel(this.capsuleNotificationID).then(() => {
264            console.info(TAG, 'Succeeded in canceling notification.');
265            if (successOrNot === 1) {
266                this.pullUpReceiveResultNotification(1, 0);
267            }
268            this.handleTerminate();
269        }).catch((err: BusinessError) => {
270            console.error(TAG, `failed to cancel notification. Code is ${err.code}, message is ${err.message}`)
271            this.handleTerminate();
272        });
273    }
274
275    subscriberLiveViewNotification(): void {
276        let subscriber: notificationManager.SystemLiveViewSubscriber = {
277            onResponse: (id: number, option: notificationManager.ButtonOptions) => {
278                switch (option.buttonName) {
279                    case this.cancelTransEvent: {
280                        console.info(TAG, 'cancel transfer.');
281                        this.oppProfile.cancelTransfer();
282                        this.cancelSendProgressNotification(0);
283                        break;
284                    }
285                    default: {
286                        break;
287                    }
288                }
289            }
290        };
291        try {
292            notificationManager.subscribeSystemLiveView(subscriber);
293        } catch(e) {
294            console.error(TAG, 'subscriberLiveViewNotification fail');
295        }
296    }
297
298    getFileName(filePath: string): string {
299        let extension = filePath.substring(filePath.lastIndexOf('/') + 1);
300        return extension;
301    }
302
303    getFormatString(resource: Resource, value1: string, value2: string): string {
304        let result = this.context.resourceManager.getStringSync(resource.id);
305        result = result.replace('%1$s', value1);
306        result = result.replace('%2$s', value2);
307        return result;
308    }
309
310    getFormatPlural(resource: Resource, value: number): string {
311        let result = this.context.resourceManager.getPluralStringValueSync(resource.id, value);
312        result = result.replace('%d', value.toString());
313        return result;
314    }
315
316    async startContinuousTask() {
317        let wantAgentObj: WantAgent;
318        wantAgentObj = await this.getNotificationWantAgent('ohos.event.notification.BT.BACK_RUNNING');
319        backgroundTaskManager.startBackgroundRunning(this.context,
320        backgroundTaskManager.BackgroundMode.BLUETOOTH_INTERACTION, wantAgentObj).then(() => {
321            console.info(TAG, `Succeeded in operationing startBackgroundRunning.`);
322        }).catch((err: BusinessError) => {
323            console.error(TAG, `Failed to operation startBackgroundRunning. Code is ${err.code}, message is ${err.message}`);
324        });
325    }
326
327    stopContinuousTask() {
328        backgroundTaskManager.stopBackgroundRunning(this.context).then(() => {
329            console.info(TAG, `Succeeded in operationing stopBackgroundRunning.`);
330        }).catch((err: BusinessError) => {
331            console.error(TAG, `Failed to operation stopBackgroundRunning. Code is ${err.code}, message is ${err.message}`);
332        });
333    }
334}