• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1/*
2 * Copyright (c) 2025-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 systemParameter from '@ohos.systemParameterEnhance';
17import deviceInfo from '@ohos.deviceInfo';
18import UIExtensionContentSession from '@ohos.app.ability.UIExtensionContentSession';
19import opp from '@ohos.bluetooth.opp';
20import common from '@ohos.app.ability.common';
21import fs from '@ohos.file.fs';
22import fileUri from '@ohos.file.fileuri';
23import { BusinessError } from '@ohos.base';
24
25export const CLOSE = 1;
26
27export const BACK = 0;
28
29const TAG: string = 'BluetoothShare: ';
30const BUNDLE_NAME: string = 'com.huawei.hmos.settings';
31const ABILITY_NAME: string = 'BluetoothOppServiceUIExtensionAbility';
32const CONTROL_TYPE_ALLOW_SEND_RECEIVE: string = '1';
33const CONTROL_TYPE_DISALLOW_SEND_ALLOW_RECEIVE: string = '2';
34const MAX_FILE_NUM: number = 300;
35const TRANSMIT_CONTROL_PROP_KEY: string = 'persist.distributed_scene.datafiles_trans_ctrl';
36
37@Entry
38@Component
39export struct BtServicesComponent {
40  uiExtensionProxy?: UIExtensionProxy;
41  private serverMac: string = '';
42  private fileHolders: Array<opp.FileHolder> = [];
43  /**
44   * 设备类型:PC
45   */
46  private static readonly DEVICE_TYPE_PC: string = 'pc';
47
48  /**
49   * 设备类型: 2in1
50   */
51  private static readonly DEVICE_TYPE_PC_NEW: string = '2in1';
52
53  build() {
54    Column() {
55      UIExtensionComponent({
56        bundleName: BUNDLE_NAME,
57        abilityName: ABILITY_NAME,
58        parameters: {
59          'ability.want.params.uiExtensionType': 'sys/commonUI',
60        }
61      })
62        .backgroundColor('#01111111')
63        .onRemoteReady((proxy) => {
64          console.log(TAG, 'onRemoteReady.');
65          this.uiExtensionProxy = proxy;
66        })
67        .onError((error) => {
68          console.log(TAG, `onError code: ${error?.code} message: ${error?.message}`);
69        })
70        .onTerminated(() => {
71        })
72        .onReceive((data: Record<string, string | Object>) => {
73          console.log(TAG, 'onReceive: ' + data?.action);
74          if (!data) {
75            console.error(TAG, 'onReceive error');
76            return;
77          }
78          this.dealReceivedData(data);
79        })
80        .size({ width: '100%', height: '100%'})
81        .expandSafeArea([SafeAreaType.SYSTEM], [SafeAreaEdge.BOTTOM, SafeAreaEdge.TOP])
82    }
83  }
84
85  private needDisableShare() : boolean {
86    try {
87      console.log(TAG, 'systemParameter get start');
88      const info: string = systemParameter.getSync(TRANSMIT_CONTROL_PROP_KEY, CONTROL_TYPE_ALLOW_SEND_RECEIVE);
89      return info === CONTROL_TYPE_DISALLOW_SEND_ALLOW_RECEIVE;
90    } catch (err) {
91      console.error(TAG, 'systemParameter get failed, msg: ${err.message}');
92      return false;
93    }
94  }
95
96  /**
97   * 获取设备类型
98   * @return string
99   */
100  private static getDeviceType() : string {
101    return deviceInfo.deviceType;
102  }
103
104  private static isPC() : boolean {
105    return BtServicesComponent.getDeviceType() === BtServicesComponent.DEVICE_TYPE_PC ||
106      BtServicesComponent.getDeviceType() === BtServicesComponent.DEVICE_TYPE_PC_NEW;
107  }
108
109  /**
110   * 关闭、退出蓝牙分享页面
111   */
112  private closeSheetPage(session: UIExtensionContentSession, code: number, message?: string) {
113    if (!session) {
114      console.log(TAG, `invalid session`);
115      return;
116    }
117    console.log(TAG, `closeSheetPage start`);
118    let result: common.AbilityResult = {
119      resultCode: code
120    };
121    if (message) {
122      result.want = {
123        parameters: {
124          message: message
125        }
126      }
127    }
128    try {
129      session?.terminateSelfWithResult(result).then(() => {
130        console.log(TAG, `terminateSelfWithResult success`);
131      }).catch((error: Error) => {
132        console.log(TAG, `terminateSelfWithResult failed, err name: ${error?.name}`);
133      });
134    } catch (err) {
135      console.log(TAG, `closeSheetPage error, msg: ${err.message}`);
136    }
137  }
138
139  aboutToAppear() : void {
140    console.log(TAG, 'aboutToAppear.');
141    if (BtServicesComponent.isPC() && this.needDisableShare()) {
142      console.log(TAG, 'is PC IT device');
143      const session: UIExtensionContentSession | undefined =
144        LocalStorage.getShared()?.get<UIExtensionContentSession>('session');
145      if (session === undefined) {
146        console.log(TAG, 'cannot get session');
147        return;
148      }
149      console.log(TAG, 'closeSheetPage');
150      this.closeSheetPage(session, CLOSE);
151    }
152  }
153
154  aboutToDisappear() : void {
155    console.log(TAG, 'aboutToDisappear.');
156  }
157
158  private async dealReceivedData(data: Record<string, Object>): Promise<void> {
159    const session: UIExtensionContentSession | undefined = LocalStorage.getShared()?.get<UIExtensionContentSession>('session');
160    if (session === undefined) {
161      console.log(TAG, 'cannot get session.');
162      return;
163    }
164    if (data['action'] === 'sendDeviceInfo') {
165      console.log(TAG, 'sendDeviceInfo message.');
166      let serverMac: string = data['deviceInfo'] as string;
167      this.serverMac = serverMac;
168      this.sendData();
169    }
170    if (data['action'] === 'closeSheet') {
171      console.log(TAG, 'closeSheet message.');
172      this.closeSheetPage(session, CLOSE);
173    }
174    if (data['action'] === 'backSheet') {
175      console.log(TAG, 'backSheet message.');
176      this.closeSheetPage(session, BACK);
177    }
178  }
179
180  private dealSysFileUris(sysShareFileUris: string[], sysShareFilelength: number) : string[] {
181    let result: string[] = [];
182    for (let i = 0; i < sysShareFilelength; i++) {
183      let sandboxUri: string = new fileUri.FileUri(sysShareFileUris[i]).path;
184      let isDirectory = fs.statSync(sandboxUri).isDirectory();
185      if (isDirectory) {
186        console.log(TAG, 'share file directory.');
187        let directoryFiles: string[] = fs.listFileSync(sandboxUri);
188        console.log(TAG, 'directoryFiles length is ' + directoryFiles.length);
189        let length: number = (directoryFiles.length > MAX_FILE_NUM) ? MAX_FILE_NUM : directoryFiles.length;
190        let uris: string[] = [];
191        for (let j = 0; j < length; j++) {
192          uris.push(sandboxUri + '/' + directoryFiles[j]);
193        }
194        result.push(...this.dealSysFileUris(uris, length));
195      } else {
196        result.push(sysShareFileUris[i]);
197      }
198    }
199    return result;
200  }
201
202  private async sendData() : Promise<void> {
203    console.log(`${TAG} sendData is called.`);
204    let sysShareFileUris: string[] = AppStorage.Get('sendFileUris');
205    let sysShareFilelength: number = AppStorage.Get('sendFileUrisLength');
206    if ((sysShareFileUris == undefined) || (sysShareFileUris == null) ||
207        (sysShareFilelength == undefined) || (sysShareFilelength == null)) {
208      console.log(TAG, 'sysShareFileUris is undefined');
209      return;
210    }
211    try {
212      let oppProfile = opp.createOppServerProfile();
213      let fileUris: string[] = this.dealSysFileUris(sysShareFileUris, sysShareFilelength);
214      let fileUrisLength: number = (fileUris.length > MAX_FILE_NUM) ? MAX_FILE_NUM : fileUris.length;
215      for (let i = 0; i < fileUrisLength; i++) {
216        let file = fs.openSync(fileUris[i], fs.OpenMode.READ_ONLY);
217        let filePath = decodeURIComponent(fileUris[i]);
218        console.log(`${TAG} deal file:` + file.fd);
219        let stat: fs.Stat = fs.statSync(file.fd);
220        let fileHolder: opp.FileHolder = {
221          filePath: filePath,
222          fileSize: stat.size,
223          fileFd: file.fd
224        };
225        this.fileHolders.push(fileHolder);
226      }
227      if (this.fileHolders.length <= 0) {
228        console.log(TAG, 'fileHolders is undefined');
229        return;
230      }
231      await oppProfile.sendFile(this.serverMac, this.fileHolders);
232      const session: UIExtensionContentSession | undefined =
233        LocalStorage.getShared()?.get<UIExtensionContentSession>('session');
234      for (let i = 0; i < this.fileHolders.length; i++) {
235        console.log(TAG, 'close fileHolders fd.');
236        if (this.fileHolders[i].fileFd != -1) {
237          fs.close(this.fileHolders[i].fileFd);
238          this.fileHolders[i].fileFd = -1;
239        }
240      }
241    } catch (err) {
242      console.error(`${TAG} sendData err, code is ${err.code}, message is ${err.message}`);
243    }
244    const session: UIExtensionContentSession | undefined =
245      LocalStorage.getShared()?.get<UIExtensionContentSession>('session');
246    if (session == undefined) {
247      console.log(TAG, 'cannot get session.');
248      return;
249    }
250    this.closeSheetPage(session, CLOSE);
251  }
252}