/*
* Copyright (c) 2025-2025 Huawei Device Co., Ltd.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import systemParameter from '@ohos.systemParameterEnhance';
import deviceInfo from '@ohos.deviceInfo';
import UIExtensionContentSession from '@ohos.app.ability.UIExtensionContentSession';
import opp from '@ohos.bluetooth.opp';
import common from '@ohos.app.ability.common';
import fs from '@ohos.file.fs';
import fileUri from '@ohos.file.fileuri';
import { BusinessError } from '@ohos.base';
export const CLOSE = 1;
export const BACK = 0;
const TAG: string = 'BluetoothShare: ';
const BUNDLE_NAME: string = 'com.huawei.hmos.settings';
const ABILITY_NAME: string = 'BluetoothOppServiceUIExtensionAbility';
const CONTROL_TYPE_ALLOW_SEND_RECEIVE: string = '1';
const CONTROL_TYPE_DISALLOW_SEND_ALLOW_RECEIVE: string = '2';
const MAX_FILE_NUM: number = 300;
const TRANSMIT_CONTROL_PROP_KEY: string = 'persist.distributed_scene.datafiles_trans_ctrl';
@Entry
@Component
export struct BtServicesComponent {
uiExtensionProxy?: UIExtensionProxy;
private serverMac: string = '';
private fileHolders: Array<opp.FileHolder> = [];
/**
* 设备类型:PC
*/
private static readonly DEVICE_TYPE_PC: string = 'pc';
/**
* 设备类型: 2in1
*/
private static readonly DEVICE_TYPE_PC_NEW: string = '2in1';
build() {
Column() {
UIExtensionComponent({
bundleName: BUNDLE_NAME,
abilityName: ABILITY_NAME,
parameters: {
'ability.want.params.uiExtensionType': 'sys/commonUI',
}
})
.backgroundColor('#01111111')
.onRemoteReady((proxy) => {
console.log(TAG, 'onRemoteReady.');
this.uiExtensionProxy = proxy;
})
.onError((error) => {
console.log(TAG, `onError code: ${error?.code} message: ${error?.message}`);
})
.onTerminated(() => {
})
.onReceive((data: Record<string, string | Object>) => {
console.log(TAG, 'onReceive: ' + data?.action);
if (!data) {
console.error(TAG, 'onReceive error');
return;
}
this.dealReceivedData(data);
})
.size({ width: '100%', height: '100%'})
.expandSafeArea([SafeAreaType.SYSTEM], [SafeAreaEdge.BOTTOM, SafeAreaEdge.TOP])
}
}
private needDisableShare() : boolean {
try {
console.log(TAG, 'systemParameter get start');
const info: string = systemParameter.getSync(TRANSMIT_CONTROL_PROP_KEY, CONTROL_TYPE_ALLOW_SEND_RECEIVE);
return info === CONTROL_TYPE_DISALLOW_SEND_ALLOW_RECEIVE;
} catch (err) {
console.error(TAG, 'systemParameter get failed, msg: ${err.message}');
return false;
}
}
/**
* 获取设备类型
* @return string
*/
private static getDeviceType() : string {
return deviceInfo.deviceType;
}
private static isPC() : boolean {
return BtServicesComponent.getDeviceType() === BtServicesComponent.DEVICE_TYPE_PC ||
BtServicesComponent.getDeviceType() === BtServicesComponent.DEVICE_TYPE_PC_NEW;
}
/**
* 关闭、退出蓝牙分享页面
*/
private closeSheetPage(session: UIExtensionContentSession, code: number, message?: string) {
if (!session) {
console.log(TAG, `invalid session`);
return;
}
console.log(TAG, `closeSheetPage start`);
let result: common.AbilityResult = {
resultCode: code
};
if (message) {
result.want = {
parameters: {
message: message
}
}
}
try {
session?.terminateSelfWithResult(result).then(() => {
console.log(TAG, `terminateSelfWithResult success`);
}).catch((error: Error) => {
console.log(TAG, `terminateSelfWithResult failed, err name: ${error?.name}`);
});
} catch (err) {
console.log(TAG, `closeSheetPage error, msg: ${err.message}`);
}
}
aboutToAppear() : void {
console.log(TAG, 'aboutToAppear.');
if (BtServicesComponent.isPC() && this.needDisableShare()) {
console.log(TAG, 'is PC IT device');
const session: UIExtensionContentSession | undefined =
LocalStorage.getShared()?.get<UIExtensionContentSession>('session');
if (session === undefined) {
console.log(TAG, 'cannot get session');
return;
}
console.log(TAG, 'closeSheetPage');
this.closeSheetPage(session, CLOSE);
}
}
aboutToDisappear() : void {
console.log(TAG, 'aboutToDisappear.');
}
private async dealReceivedData(data: Record<string, Object>): Promise<void> {
const session: UIExtensionContentSession | undefined = LocalStorage.getShared()?.get<UIExtensionContentSession>('session');
if (session === undefined) {
console.log(TAG, 'cannot get session.');
return;
}
if (data['action'] === 'sendDeviceInfo') {
console.log(TAG, 'sendDeviceInfo message.');
let serverMac: string = data['deviceInfo'] as string;
this.serverMac = serverMac;
this.sendData();
}
if (data['action'] === 'closeSheet') {
console.log(TAG, 'closeSheet message.');
this.closeSheetPage(session, CLOSE);
}
if (data['action'] === 'backSheet') {
console.log(TAG, 'backSheet message.');
this.closeSheetPage(session, BACK);
}
}
private dealSysFileUris(sysShareFileUris: string[], sysShareFilelength: number) : string[] {
let result: string[] = [];
for (let i = 0; i < sysShareFilelength; i++) {
let sandboxUri: string = new fileUri.FileUri(sysShareFileUris[i]).path;
console.log(`${TAG} dealSysFileUris sysShareUri : ${sysShareFileUris[i]} sandboxUri: ${sandboxUri}`);
let isDirectory = fs.statSync(sandboxUri).isDirectory();
if (isDirectory) {
console.log(TAG, 'share file directory.');
let directoryFiles: string[] = fs.listFileSync(sandboxUri);
console.log(TAG, 'directoryFiles length is ' + directoryFiles.length);
let length: number = (directoryFiles.length > MAX_FILE_NUM) ? MAX_FILE_NUM : directoryFiles.length;
let uris: string[] = [];
for (let j = 0; j < length; j++) {
uris.push(sandboxUri + '/' + directoryFiles[j]);
}
result.push(...this.dealSysFileUris(uris, length));
} else {
result.push(sysShareFileUris[i]);
}
}
return result;
}
private async sendData() : Promise<void> {
console.log(`${TAG} sendData is called.`);
let sysShareFileUris: string[] = AppStorage.Get('sendFileUris');
let sysShareFilelength: number = AppStorage.Get('sendFileUrisLength');
if ((sysShareFileUris == undefined) || (sysShareFileUris == null) ||
(sysShareFilelength == undefined) || (sysShareFilelength == null)) {
console.log(TAG, 'sysShareFileUris is undefined');
return;
}
try {
let oppProfile = opp.createOppServerProfile();
let fileUris: string[] = this.dealSysFileUris(sysShareFileUris, sysShareFilelength);
let fileUrisLength: number = (fileUris.length > MAX_FILE_NUM) ? MAX_FILE_NUM : fileUris.length;
for (let i = 0; i < fileUrisLength; i++) {
console.log(`${TAG} openSync fileUris is : ${fileUris[i]}`);
let file = fs.openSync(fileUris[i], fs.OpenMode.READ_ONLY);
let filePath = decodeURIComponent(fileUris[i]);
console.log(`${TAG} deal file: ${file.fd} filePath: ${filePath}`);
let stat: fs.Stat = fs.statSync(file.fd);
let fileHolder: opp.FileHolder = {
filePath: filePath,
fileSize: stat.size,
fileFd: file.fd
};
this.fileHolders.push(fileHolder);
}
if (this.fileHolders.length <= 0) {
console.log(TAG, 'fileHolders is undefined');
return;
}
await oppProfile.sendFile(this.serverMac, this.fileHolders);
const session: UIExtensionContentSession | undefined =
LocalStorage.getShared()?.get<UIExtensionContentSession>('session');
for (let i = 0; i < this.fileHolders.length; i++) {
console.log(TAG, 'close fileHolders fd.');
if (this.fileHolders[i].fileFd != -1) {
fs.close(this.fileHolders[i].fileFd);
this.fileHolders[i].fileFd = -1;
}
}
} catch (err) {
console.error(`${TAG} sendData err, code is ${err.code}, message is ${err.message}`);
}
const session: UIExtensionContentSession | undefined =
LocalStorage.getShared()?.get<UIExtensionContentSession>('session');
if (session == undefined) {
console.log(TAG, 'cannot get session.');
return;
}
this.closeSheetPage(session, CLOSE);
}
}