diff --git a/app/(tabs)/explore.tsx b/app/(tabs)/explore.tsx
index 1ee2bc0..3980ac2 100644
--- a/app/(tabs)/explore.tsx
+++ b/app/(tabs)/explore.tsx
@@ -28,8 +28,7 @@ export default function TabTwoScreen() {
requestDeviceInfo,
updateActivationTime,
sendIdentityCheck,
- transferSampleFile,
- transferEmptyTestPackage,
+ transferMedia,
} = useBleExplorer();
@@ -164,12 +163,7 @@ export default function TabTwoScreen() {
-
diff --git a/ble/core/BleClient.ts b/ble/core/BleClient.ts
index fed144e..c76eb9c 100644
--- a/ble/core/BleClient.ts
+++ b/ble/core/BleClient.ts
@@ -1,7 +1,7 @@
-import {BleManager, Device, Characteristic, BleError as PlxError, ScanOptions} from 'react-native-ble-plx';
-import {Platform, PermissionsAndroid} from 'react-native';
+import {BleManager, Device, Characteristic, ScanOptions} from 'react-native-ble-plx';
+import {Platform} from 'react-native';
import {BleDevice, ConnectionState, BleError, ScanResult} from './types';
-import {BLE_UUIDS} from "@/ble";
+import {BLE_UUIDS} from "../protocol/Constants";
export class BleClient {
private static instance: BleClient;
diff --git a/ble/core/types.ts b/ble/core/types.ts
index cbef747..bb0d727 100644
--- a/ble/core/types.ts
+++ b/ble/core/types.ts
@@ -1,30 +1,30 @@
-import { Device, BleError as PlxBleError } from 'react-native-ble-plx';
+import {Device} from 'react-native-ble-plx';
export interface BleScanInfo {
- rawData?: ArrayBuffer;
- rssi: number;
- isEnableConnect: boolean;
+ rawData?: ArrayBuffer;
+ rssi: number;
+ isEnableConnect: boolean;
}
export type BleDevice = Device & {
- scanInfo?: BleScanInfo;
- connected?: boolean;
+ scanInfo?: BleScanInfo;
+ connected?: boolean;
};
export enum ConnectionState {
- DISCONNECTED = 'disconnected',
- CONNECTING = 'connecting',
- CONNECTED = 'connected',
- DISCONNECTING = 'disconnecting',
+ DISCONNECTED = 'disconnected',
+ CONNECTING = 'connecting',
+ CONNECTED = 'connected',
+ DISCONNECTING = 'disconnecting',
}
export interface BleError {
- errorCode: number;
- message: string;
- attErrorCode?: number | null;
- iosErrorCode?: number | null;
- androidErrorCode?: number | null;
- reason?: string | null;
+ errorCode: number;
+ message: string;
+ attErrorCode?: number | null;
+ iosErrorCode?: number | null;
+ androidErrorCode?: number | null;
+ reason?: string | null;
}
export interface ScanResult {
diff --git a/ble/hooks/useBleExplorer.ts b/ble/hooks/useBleExplorer.ts
index 4869027..1c65d95 100644
--- a/ble/hooks/useBleExplorer.ts
+++ b/ble/hooks/useBleExplorer.ts
@@ -6,7 +6,7 @@ import {
BleClient,
BleDevice,
BleError,
- BleProtocolService, APP_COMMAND_TYPES,
+ BleProtocolService,
ConnectionState,
DeviceInfo, COMMAND_TYPES,
} from '../index';
@@ -333,7 +333,7 @@ export const useBleExplorer = () => {
}
};
- const convertToANI = useCallback(async (tempDir: Directory, media: ImagePicker.ImagePickerAsset): Promise => {
+ const convertToANIAsFile = useCallback(async (tempDir: Directory, media: ImagePicker.ImagePickerAsset): Promise => {
const tempFile = new File(tempDir, `${media.fileName?.split('.')[0]}.ani`)
const formData = new FormData();
formData.append('file', {
@@ -356,7 +356,27 @@ export const useBleExplorer = () => {
return tempFile;
}, []);
- const transferSampleFile = useCallback(async () => {
+ const convertToANIAsBuffer = useCallback(async (tempDir: Directory, media: ImagePicker.ImagePickerAsset): Promise => {
+ const formData = new FormData();
+ formData.append('file', {
+ uri: media.uri,
+ name: media.fileName || 'video.mp4',
+ type: media.mimeType || 'video/mp4',
+ } as any);
+ const response = await fetch("https://bowongai-test--ani-video-converter-fastapi-app.modal.run/api/convert/ani", {
+ method: "POST",
+ body: formData,
+ headers: {'Accept': 'multipart/form-data',}
+ });
+ if (!response.ok) {
+ throw new Error(`Conversion failed with status ${response.status}`);
+ }
+ const content = await response.arrayBuffer()
+ console.debug(`Converted video size : ${content.byteLength} bytes`);
+ return content;
+ }, []);
+
+ const transferMedia = useCallback(async () => {
if (!state.connectedDevice) {
setError('No device connected');
return;
@@ -368,25 +388,22 @@ export const useBleExplorer = () => {
console.debug(`[${state.connectedDevice.id}] processing ${medias.length} files...`);
for (const media of medias) {
if (media.type === 'video') {
- let tempFile: File;
+ // let tempFile: File;
+ let tempBuffer: ArrayBuffer;
console.debug(`Converting video: ${media.fileName || 'video'}...`);
setState(prev => ({...prev, loading: {...prev.loading, converting: true}}));
- tempFile = await convertToANI(tempDir, media)
+ // tempFile = await convertToANIAsFile(tempDir, media)
+ tempBuffer = await convertToANIAsBuffer(tempDir, media)
setState(prev => ({...prev, loading: {...prev.loading, converting: false}}));
console.log(`Transferring converted file to device...`);
- await fileTransferService.transferFile(
- state.connectedDevice.id,
- tempFile.uri,
- COMMAND_TYPES.TRANSFER_ANI_VIDEO,
- (progress) => {
- setState(prev => ({...prev, transferProgress: progress * 100}));
- // Optional: throttle logs to avoid spam
- if (Math.round(progress * 100) % 10 === 0) {
- // addLog(`Transfer progress: ${Math.round(progress * 100)}%`);
- }
+ await fileTransferService.transferFile(state.connectedDevice.id, tempBuffer, COMMAND_TYPES.TRANSFER_ANI_VIDEO, (progress) => {
+ setState(prev => ({...prev, transferProgress: progress * 100}));
+ // Optional: throttle logs to avoid spam
+ if (Math.round(progress * 100) % 10 === 0) {
+ // addLog(`Transfer progress: ${Math.round(progress * 100)}%`);
}
- );
- tempFile.delete();
+ });
+ // tempFile.delete();
console.log(`Transfer successful`);
} else if (media.type === 'image') {
try {
@@ -397,18 +414,15 @@ export const useBleExplorer = () => {
if (isGif) {
console.debug(`Converting GIF to ANI: ${media.fileName || 'gif'}...`);
- const tempFile = await convertToANI(tempDir, media)
+ // const tempFile = await convertToANIAsFile(tempDir, media)
+ const tempBuffer = await convertToANIAsBuffer(tempDir, media)
console.log(`Transferring converted file to device...`);
- await fileTransferService.transferFile(
- state.connectedDevice.id,
- tempFile.uri,
- COMMAND_TYPES.TRANSFER_ANI_VIDEO,
- (progress) => {
+ await fileTransferService.transferFile(state.connectedDevice.id, tempBuffer, COMMAND_TYPES.TRANSFER_ANI_VIDEO, (progress) => {
setState(prev => ({...prev, transferProgress: progress * 100}));
}
);
console.log(`Transfer successful`);
- tempFile.delete();
+ // tempFile.delete();
console.log(`Cleaned up temp file`);
return;
}
@@ -456,20 +470,7 @@ export const useBleExplorer = () => {
console.log(`Unsupported media type: ${media.type}`);
}
}
- }, [state.connectedDevice, fileTransferService, convertToANI, setError]);
-
- const transferEmptyTestPackage = useCallback(async () => {
- if (!state.connectedDevice) {
- setError('No device connected');
- return;
- }
- await fileTransferService.transferTestPackage(
- state.connectedDevice.id,
- (progress) => {
- setState(prev => ({...prev, transferProgress: progress * 100}));
- }
- )
- }, [fileTransferService, setError, state.connectedDevice])
+ }, [state.connectedDevice, fileTransferService, convertToANIAsBuffer, setError]);
const clearLogs = useCallback(() => setState(prev => ({...prev, logs: []})), []);
@@ -482,8 +483,7 @@ export const useBleExplorer = () => {
requestDeviceInfo,
queryDeviceVersion,
queryActivationStatus,
- transferSampleFile,
- transferEmptyTestPackage,
+ transferMedia,
clearLogs,
updateActivationTime: () => {
},
diff --git a/ble/services/BleProtocolService.ts b/ble/services/BleProtocolService.ts
index ad25b35..b0fde40 100644
--- a/ble/services/BleProtocolService.ts
+++ b/ble/services/BleProtocolService.ts
@@ -162,7 +162,7 @@ export class BleProtocolService {
console.debug(`[BleProtocolService] Sending with MTU=${mtu}, maxDataSize=${safeMaxDataSize}`);
const rawPayloadHex = payload.reduce((acc, val) => acc + val.toString(16).padStart(2, '0') + ' ', '');
- const formattedRawPayloadHex = rawPayloadHex.substring(0, 512) + "\n......\n" + rawPayloadHex.substring(rawPayloadHex.length - 512);
+ const formattedRawPayloadHex = rawPayloadHex.substring(0, 512 * 2) + "\n......\n" + rawPayloadHex.substring(rawPayloadHex.length - (512 * 2));
console.debug(`[BleProtocolService] Sending payload size=${payload.byteLength}, raw payload hex=\n${formattedRawPayloadHex}`);
const frames = ProtocolManager.createFrame(type, payload, FRAME_CONSTANTS.HEAD_APP_TO_DEVICE, safeMaxDataSize);
const total = frames.length;
diff --git a/ble/services/DeviceInfoService.ts b/ble/services/DeviceInfoService.ts
index 9c29654..3423059 100644
--- a/ble/services/DeviceInfoService.ts
+++ b/ble/services/DeviceInfoService.ts
@@ -1,5 +1,5 @@
import {BleProtocolService} from './BleProtocolService';
-import {APP_COMMAND_TYPES, COMMAND_TYPES, RESPONSE_TYPES} from '../protocol/Constants';
+import {COMMAND_TYPES, RESPONSE_TYPES} from '../protocol/Constants';
import {DeviceInfo, ActivationStatus} from '../protocol/types';
import {Buffer} from 'buffer';
diff --git a/ble/services/FileTransferService.ts b/ble/services/FileTransferService.ts
index 783b629..8cc6a49 100644
--- a/ble/services/FileTransferService.ts
+++ b/ble/services/FileTransferService.ts
@@ -16,22 +16,28 @@ export class FileTransferService {
return FileTransferService.instance;
}
- public async transferFile(deviceId: string, filePath: string, type: APP_COMMAND_TYPES, onProgress?: (progress: number) => void): Promise {
+ public async transferFile(deviceId: string, file: string | ArrayBuffer, type: APP_COMMAND_TYPES, onProgress?: (progress: number) => void): Promise {
try {
- const response = await fetch(filePath);
- if (!response.ok) {
- throw new Error(`Failed to load file: ${response.statusText}`);
+ const startAt = Date.now();
+ let arrayBuffer: ArrayBuffer;
+ if (file instanceof ArrayBuffer) {
+ arrayBuffer = file
+ } else {
+ const response = await fetch(file);
+ if (!response.ok) {
+ throw new Error(`Failed to load file: ${response.statusText}`);
+ }
+ const blob = await response.blob();
+ const reader = new FileReader();
+ arrayBuffer = await new Promise((resolve, reject) => {
+ reader.onload = () => resolve(reader.result as ArrayBuffer);
+ reader.onerror = reject;
+ reader.readAsArrayBuffer(blob);
+ });
}
- const blob = await response.blob();
-
- const reader = new FileReader();
- const arrayBuffer = await new Promise((resolve, reject) => {
- reader.onload = () => resolve(reader.result as ArrayBuffer);
- reader.onerror = reject;
- reader.readAsArrayBuffer(blob);
- });
-
await this.protocol.send(deviceId, type, arrayBuffer, onProgress);
+ const transferredAt = Date.now();
+ console.debug(`File transferred in ${(transferredAt - startAt) / 1000} s`);
} catch (e) {
console.error("File transfer failed", e);
throw e;