wiliot-sdk-rn 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/GatewaySdk.podspec +27 -0
- package/README.md +99 -0
- package/android/build.gradle +72 -0
- package/android/src/main/AndroidManifest.xml +32 -0
- package/android/src/main/java/com/gatewaysdk/GatewayScanModule.kt +392 -0
- package/android/src/main/java/com/gatewaysdk/GatewayScanService.kt +521 -0
- package/android/src/main/java/com/gatewaysdk/GatewaySdkPackage.kt +22 -0
- package/ios/GatewaySdk/GatewayScanModule.m +59 -0
- package/ios/GatewaySdk/GatewayScanModule.swift +255 -0
- package/ios/GatewaySdk/GatewaySdk-Bridging-Header.h +14 -0
- package/lib/GatewaySDK.d.ts +150 -0
- package/lib/GatewaySDK.d.ts.map +1 -0
- package/lib/GatewaySDK.js +429 -0
- package/lib/GatewaySDK.js.map +1 -0
- package/lib/auth/ApiError.d.ts +27 -0
- package/lib/auth/ApiError.d.ts.map +1 -0
- package/lib/auth/ApiError.js +55 -0
- package/lib/auth/ApiError.js.map +1 -0
- package/lib/auth/AuthClient.d.ts +87 -0
- package/lib/auth/AuthClient.d.ts.map +1 -0
- package/lib/auth/AuthClient.js +198 -0
- package/lib/auth/AuthClient.js.map +1 -0
- package/lib/auth/TokenStorage.d.ts +49 -0
- package/lib/auth/TokenStorage.d.ts.map +1 -0
- package/lib/auth/TokenStorage.js +117 -0
- package/lib/auth/TokenStorage.js.map +1 -0
- package/lib/auth/index.d.ts +9 -0
- package/lib/auth/index.d.ts.map +1 -0
- package/lib/auth/index.js +18 -0
- package/lib/auth/index.js.map +1 -0
- package/lib/ble/BackgroundService.d.ts +85 -0
- package/lib/ble/BackgroundService.d.ts.map +1 -0
- package/lib/ble/BackgroundService.js +194 -0
- package/lib/ble/BackgroundService.js.map +1 -0
- package/lib/ble/BleScanner.d.ts +125 -0
- package/lib/ble/BleScanner.d.ts.map +1 -0
- package/lib/ble/BleScanner.js +482 -0
- package/lib/ble/BleScanner.js.map +1 -0
- package/lib/ble/index.d.ts +8 -0
- package/lib/ble/index.d.ts.map +1 -0
- package/lib/ble/index.js +13 -0
- package/lib/ble/index.js.map +1 -0
- package/lib/datapath/Datapath.d.ts +225 -0
- package/lib/datapath/Datapath.d.ts.map +1 -0
- package/lib/datapath/Datapath.js +527 -0
- package/lib/datapath/Datapath.js.map +1 -0
- package/lib/datapath/PacerFilter.d.ts +210 -0
- package/lib/datapath/PacerFilter.d.ts.map +1 -0
- package/lib/datapath/PacerFilter.js +415 -0
- package/lib/datapath/PacerFilter.js.map +1 -0
- package/lib/datapath/PacketBuilder.d.ts +218 -0
- package/lib/datapath/PacketBuilder.d.ts.map +1 -0
- package/lib/datapath/PacketBuilder.js +420 -0
- package/lib/datapath/PacketBuilder.js.map +1 -0
- package/lib/datapath/PacketDedup.d.ts +111 -0
- package/lib/datapath/PacketDedup.d.ts.map +1 -0
- package/lib/datapath/PacketDedup.js +236 -0
- package/lib/datapath/PacketDedup.js.map +1 -0
- package/lib/datapath/RssiEncoder.d.ts +187 -0
- package/lib/datapath/RssiEncoder.d.ts.map +1 -0
- package/lib/datapath/RssiEncoder.js +298 -0
- package/lib/datapath/RssiEncoder.js.map +1 -0
- package/lib/datapath/TagsTable.d.ts +261 -0
- package/lib/datapath/TagsTable.d.ts.map +1 -0
- package/lib/datapath/TagsTable.js +504 -0
- package/lib/datapath/TagsTable.js.map +1 -0
- package/lib/datapath/TbcCalculator.d.ts +190 -0
- package/lib/datapath/TbcCalculator.d.ts.map +1 -0
- package/lib/datapath/TbcCalculator.js +332 -0
- package/lib/datapath/TbcCalculator.js.map +1 -0
- package/lib/datapath/index.d.ts +23 -0
- package/lib/datapath/index.d.ts.map +1 -0
- package/lib/datapath/index.js +48 -0
- package/lib/datapath/index.js.map +1 -0
- package/lib/index.d.ts +28 -0
- package/lib/index.d.ts.map +1 -0
- package/lib/index.js +104 -0
- package/lib/index.js.map +1 -0
- package/lib/mqtt/MqttManager.d.ts +172 -0
- package/lib/mqtt/MqttManager.d.ts.map +1 -0
- package/lib/mqtt/MqttManager.js +551 -0
- package/lib/mqtt/MqttManager.js.map +1 -0
- package/lib/mqtt/index.d.ts +7 -0
- package/lib/mqtt/index.d.ts.map +1 -0
- package/lib/mqtt/index.js +11 -0
- package/lib/mqtt/index.js.map +1 -0
- package/lib/provider/SdkProvider.d.ts +478 -0
- package/lib/provider/SdkProvider.d.ts.map +1 -0
- package/lib/provider/SdkProvider.js +271 -0
- package/lib/provider/SdkProvider.js.map +1 -0
- package/lib/provider/index.d.ts +8 -0
- package/lib/provider/index.d.ts.map +1 -0
- package/lib/provider/index.js +19 -0
- package/lib/provider/index.js.map +1 -0
- package/lib/rtk-query/index.d.ts +936 -0
- package/lib/rtk-query/index.d.ts.map +1 -0
- package/lib/rtk-query/index.js +193 -0
- package/lib/rtk-query/index.js.map +1 -0
- package/lib/types/index.d.ts +318 -0
- package/lib/types/index.d.ts.map +1 -0
- package/lib/types/index.js +8 -0
- package/lib/types/index.js.map +1 -0
- package/lib/utils/index.d.ts +68 -0
- package/lib/utils/index.d.ts.map +1 -0
- package/lib/utils/index.js +181 -0
- package/lib/utils/index.js.map +1 -0
- package/package.json +100 -0
- package/src/GatewaySDK.ts +515 -0
- package/src/auth/ApiError.ts +66 -0
- package/src/auth/AuthClient.ts +283 -0
- package/src/auth/TokenStorage.ts +140 -0
- package/src/auth/index.ts +9 -0
- package/src/ble/BackgroundService.ts +217 -0
- package/src/ble/BleScanner.ts +555 -0
- package/src/ble/index.ts +8 -0
- package/src/datapath/Datapath.ts +714 -0
- package/src/datapath/PacerFilter.ts +600 -0
- package/src/datapath/PacketBuilder.ts +568 -0
- package/src/datapath/PacketDedup.ts +333 -0
- package/src/datapath/RssiEncoder.ts +412 -0
- package/src/datapath/TagsTable.ts +698 -0
- package/src/datapath/TbcCalculator.ts +446 -0
- package/src/datapath/index.ts +88 -0
- package/src/index.ts +138 -0
- package/src/mqtt/MqttManager.ts +668 -0
- package/src/mqtt/index.ts +7 -0
- package/src/provider/SdkProvider.tsx +314 -0
- package/src/provider/index.ts +18 -0
- package/src/rtk-query/index.ts +247 -0
- package/src/types/index.ts +363 -0
- package/src/utils/index.ts +195 -0
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
require 'json'
|
|
2
|
+
|
|
3
|
+
package = JSON.parse(File.read(File.join(__dir__, 'package.json')))
|
|
4
|
+
|
|
5
|
+
Pod::Spec.new do |s|
|
|
6
|
+
s.name = "GatewaySdk"
|
|
7
|
+
s.version = package['version']
|
|
8
|
+
s.summary = package['description']
|
|
9
|
+
s.homepage = "https://github.com/example/gateway-sdk"
|
|
10
|
+
s.license = package['license']
|
|
11
|
+
s.authors = { "Gateway SDK" => "sdk@example.com" }
|
|
12
|
+
s.platforms = { :ios => "13.0" }
|
|
13
|
+
s.source = { :git => "https://github.com/example/gateway-sdk.git", :tag => "v#{s.version}" }
|
|
14
|
+
|
|
15
|
+
s.source_files = "ios/**/*.{h,m,mm,swift}"
|
|
16
|
+
s.exclude_files = "ios/Pods/**/*"
|
|
17
|
+
|
|
18
|
+
s.dependency "React-Core"
|
|
19
|
+
|
|
20
|
+
# Enable Swift
|
|
21
|
+
s.swift_version = "5.0"
|
|
22
|
+
|
|
23
|
+
# Info.plist settings documentation
|
|
24
|
+
s.info_plist = {
|
|
25
|
+
'UIBackgroundModes' => ['bluetooth-central']
|
|
26
|
+
}
|
|
27
|
+
end
|
package/README.md
ADDED
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
# wiliot-sdk-rn
|
|
2
|
+
|
|
3
|
+
Wiliot SDK for React Native - BLE scanning, MQTT, bridges, pixels, and IoT device management.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm install wiliot-sdk-rn react-native-ble-manager sp-react-native-mqtt @react-native-async-storage/async-storage
|
|
9
|
+
cd ios && pod install
|
|
10
|
+
```
|
|
11
|
+
|
|
12
|
+
## Quick Start
|
|
13
|
+
|
|
14
|
+
```tsx
|
|
15
|
+
import { SdkProvider, useSdk, useSdkState } from 'wiliot-sdk-rn';
|
|
16
|
+
|
|
17
|
+
// 1. Wrap your app (in App.tsx)
|
|
18
|
+
export default function App() {
|
|
19
|
+
return (
|
|
20
|
+
<SdkProvider
|
|
21
|
+
config={{
|
|
22
|
+
apiBaseUrl: 'https://api.wiliot.com',
|
|
23
|
+
mqttBrokerUrl: 'mqtts://mqtt.wiliot.com:8883',
|
|
24
|
+
ownerId: 'YOUR_OWNER_ID',
|
|
25
|
+
apiKey: 'YOUR_API_KEY',
|
|
26
|
+
}}
|
|
27
|
+
autoInitialize
|
|
28
|
+
>
|
|
29
|
+
<YourApp />
|
|
30
|
+
</SdkProvider>
|
|
31
|
+
);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
// 2. Use anywhere in your app
|
|
35
|
+
function ScanScreen() {
|
|
36
|
+
const { startScan, stopScan } = useSdk();
|
|
37
|
+
const { isScanning, connectionState, devices } = useSdkState();
|
|
38
|
+
|
|
39
|
+
return (
|
|
40
|
+
<View>
|
|
41
|
+
<Text>Status: {connectionState}</Text>
|
|
42
|
+
<Button title={isScanning ? 'Stop' : 'Scan'} onPress={() => isScanning ? stopScan() : startScan()} />
|
|
43
|
+
<FlatList data={Object.values(devices)} renderItem={({ item }) => <Text>{item.id}</Text>} />
|
|
44
|
+
</View>
|
|
45
|
+
);
|
|
46
|
+
}
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
## Platform Setup
|
|
50
|
+
|
|
51
|
+
### iOS (Info.plist)
|
|
52
|
+
```xml
|
|
53
|
+
<key>NSBluetoothAlwaysUsageDescription</key>
|
|
54
|
+
<string>Required for BLE scanning</string>
|
|
55
|
+
<key>UIBackgroundModes</key>
|
|
56
|
+
<array><string>bluetooth-central</string></array>
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
### Android
|
|
60
|
+
No additional setup needed - permissions are included automatically.
|
|
61
|
+
|
|
62
|
+
## Hooks
|
|
63
|
+
|
|
64
|
+
| Hook | Returns |
|
|
65
|
+
|------|---------|
|
|
66
|
+
| `useSdk()` | `{ sdk, initialize, startScan, stopScan, connect, disconnect, shutdown }` |
|
|
67
|
+
| `useSdkState()` | `{ initialized, isScanning, connectionState, packetsForwarded, devices, ... }` |
|
|
68
|
+
| `useSdkReady()` | `boolean` - true when initialized and connected |
|
|
69
|
+
| `useDevices()` | `BleDevice[]` - discovered devices |
|
|
70
|
+
| `useConnectionState()` | `'disconnected' \| 'connecting' \| 'connected' \| 'error'` |
|
|
71
|
+
| `useIsScanning()` | `boolean` |
|
|
72
|
+
|
|
73
|
+
## Background Scanning
|
|
74
|
+
|
|
75
|
+
```tsx
|
|
76
|
+
// Enable background scanning (foreground service on Android)
|
|
77
|
+
const { startScan } = useSdk();
|
|
78
|
+
await startScan(true); // true = background enabled
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
## Advanced: Direct SDK Access
|
|
82
|
+
|
|
83
|
+
```tsx
|
|
84
|
+
import { createGatewaySDK } from 'wiliot-sdk-rn';
|
|
85
|
+
|
|
86
|
+
const sdk = createGatewaySDK({
|
|
87
|
+
apiBaseUrl: 'https://api.wiliot.com',
|
|
88
|
+
mqttBrokerUrl: 'mqtts://mqtt.wiliot.com:8883',
|
|
89
|
+
ownerId: 'YOUR_OWNER_ID',
|
|
90
|
+
apiKey: 'YOUR_API_KEY',
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
await sdk.initialize();
|
|
94
|
+
await sdk.startScan();
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
## License
|
|
98
|
+
|
|
99
|
+
MIT
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
buildscript {
|
|
2
|
+
ext.safeExtGet = {prop, fallback ->
|
|
3
|
+
rootProject.ext.has(prop) ? rootProject.ext.get(prop) : fallback
|
|
4
|
+
}
|
|
5
|
+
repositories {
|
|
6
|
+
google()
|
|
7
|
+
mavenCentral()
|
|
8
|
+
}
|
|
9
|
+
dependencies {
|
|
10
|
+
classpath("com.android.tools.build:gradle:8.1.0")
|
|
11
|
+
classpath("org.jetbrains.kotlin:kotlin-gradle-plugin:${safeExtGet('kotlinVersion', '1.9.22')}")
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
plugins {
|
|
16
|
+
id 'com.android.library'
|
|
17
|
+
id 'kotlin-android'
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
def safeExtGet(prop, fallback) {
|
|
21
|
+
return rootProject.ext.has(prop) ? rootProject.ext.get(prop) : fallback
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
android {
|
|
25
|
+
namespace "com.gatewaysdk"
|
|
26
|
+
compileSdkVersion safeExtGet('compileSdkVersion', 34)
|
|
27
|
+
|
|
28
|
+
defaultConfig {
|
|
29
|
+
minSdkVersion safeExtGet('minSdkVersion', 24)
|
|
30
|
+
targetSdkVersion safeExtGet('targetSdkVersion', 34)
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
buildTypes {
|
|
34
|
+
release {
|
|
35
|
+
minifyEnabled false
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
compileOptions {
|
|
40
|
+
sourceCompatibility JavaVersion.VERSION_17
|
|
41
|
+
targetCompatibility JavaVersion.VERSION_17
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
kotlinOptions {
|
|
45
|
+
jvmTarget = '17'
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
sourceSets {
|
|
49
|
+
main {
|
|
50
|
+
java.srcDirs = ['src/main/java']
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
repositories {
|
|
56
|
+
maven {
|
|
57
|
+
url "$projectDir/../node_modules/react-native/android"
|
|
58
|
+
}
|
|
59
|
+
mavenCentral()
|
|
60
|
+
google()
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
dependencies {
|
|
64
|
+
implementation "com.facebook.react:react-android:+"
|
|
65
|
+
implementation "org.jetbrains.kotlin:kotlin-stdlib:${safeExtGet('kotlinVersion', '1.9.22')}"
|
|
66
|
+
|
|
67
|
+
// Coroutines for async operations
|
|
68
|
+
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:1.7.3"
|
|
69
|
+
|
|
70
|
+
// BLE support (optional, for native-level scanning)
|
|
71
|
+
implementation "no.nordicsemi.android:ble:2.7.0"
|
|
72
|
+
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
<?xml version="1.0" encoding="utf-8"?>
|
|
2
|
+
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
|
3
|
+
package="com.gatewaysdk">
|
|
4
|
+
|
|
5
|
+
<!-- BLE Permissions -->
|
|
6
|
+
<uses-permission android:name="android.permission.BLUETOOTH" android:maxSdkVersion="30"/>
|
|
7
|
+
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN" android:maxSdkVersion="30"/>
|
|
8
|
+
<uses-permission android:name="android.permission.BLUETOOTH_SCAN" android:usesPermissionFlags="neverForLocation"/>
|
|
9
|
+
<uses-permission android:name="android.permission.BLUETOOTH_CONNECT"/>
|
|
10
|
+
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>
|
|
11
|
+
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/>
|
|
12
|
+
|
|
13
|
+
<!-- Background Service Permissions -->
|
|
14
|
+
<uses-permission android:name="android.permission.FOREGROUND_SERVICE"/>
|
|
15
|
+
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_CONNECTED_DEVICE"/>
|
|
16
|
+
<uses-permission android:name="android.permission.WAKE_LOCK"/>
|
|
17
|
+
<uses-permission android:name="android.permission.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS"/>
|
|
18
|
+
<uses-permission android:name="android.permission.POST_NOTIFICATIONS"/>
|
|
19
|
+
|
|
20
|
+
<!-- BLE Feature -->
|
|
21
|
+
<uses-feature android:name="android.hardware.bluetooth_le" android:required="true"/>
|
|
22
|
+
|
|
23
|
+
<application>
|
|
24
|
+
<!-- Background Scan Service -->
|
|
25
|
+
<service
|
|
26
|
+
android:name=".GatewayScanService"
|
|
27
|
+
android:enabled="true"
|
|
28
|
+
android:exported="false"
|
|
29
|
+
android:foregroundServiceType="connectedDevice"/>
|
|
30
|
+
</application>
|
|
31
|
+
|
|
32
|
+
</manifest>
|
|
@@ -0,0 +1,392 @@
|
|
|
1
|
+
package com.gatewaysdk
|
|
2
|
+
|
|
3
|
+
import android.Manifest
|
|
4
|
+
import android.annotation.SuppressLint
|
|
5
|
+
import android.bluetooth.BluetoothAdapter
|
|
6
|
+
import android.bluetooth.BluetoothManager
|
|
7
|
+
import android.content.BroadcastReceiver
|
|
8
|
+
import android.content.Context
|
|
9
|
+
import android.content.Intent
|
|
10
|
+
import android.content.IntentFilter
|
|
11
|
+
import android.content.pm.PackageManager
|
|
12
|
+
import android.net.Uri
|
|
13
|
+
import android.os.Build
|
|
14
|
+
import android.os.PowerManager
|
|
15
|
+
import android.provider.Settings
|
|
16
|
+
import androidx.core.content.ContextCompat
|
|
17
|
+
import com.facebook.react.bridge.*
|
|
18
|
+
import com.facebook.react.modules.core.DeviceEventManagerModule
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Gateway SDK Native Scan Module
|
|
22
|
+
*
|
|
23
|
+
* Provides native Android functionality for BLE background scanning:
|
|
24
|
+
* - Foreground service management
|
|
25
|
+
* - Battery optimization handling
|
|
26
|
+
* - Permission checking
|
|
27
|
+
* - Bluetooth state monitoring
|
|
28
|
+
*/
|
|
29
|
+
class GatewayScanModule(private val reactContext: ReactApplicationContext) :
|
|
30
|
+
ReactContextBaseJavaModule(reactContext) {
|
|
31
|
+
|
|
32
|
+
companion object {
|
|
33
|
+
const val NAME = "GatewayScanModule"
|
|
34
|
+
|
|
35
|
+
// Broadcast actions
|
|
36
|
+
const val ACTION_SCAN_RESTART = "com.gatewaysdk.SCAN_RESTART"
|
|
37
|
+
const val ACTION_BUFFERED_RESULTS = "com.gatewaysdk.BUFFERED_SCAN_RESULTS"
|
|
38
|
+
const val ACTION_DOZE_MODE_CHANGED = "com.gatewaysdk.DOZE_MODE_CHANGED"
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
private val bluetoothManager: BluetoothManager? by lazy {
|
|
42
|
+
reactContext.getSystemService(Context.BLUETOOTH_SERVICE) as? BluetoothManager
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
private val bluetoothAdapter: BluetoothAdapter? by lazy {
|
|
46
|
+
bluetoothManager?.adapter
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
private val powerManager: PowerManager? by lazy {
|
|
50
|
+
reactContext.getSystemService(Context.POWER_SERVICE) as? PowerManager
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
// Broadcast receivers
|
|
54
|
+
private var bluetoothReceiver: BroadcastReceiver? = null
|
|
55
|
+
private var scanRestartReceiver: BroadcastReceiver? = null
|
|
56
|
+
private var bufferedResultsReceiver: BroadcastReceiver? = null
|
|
57
|
+
private var dozeModeReceiver: BroadcastReceiver? = null
|
|
58
|
+
|
|
59
|
+
init {
|
|
60
|
+
registerReceivers()
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
override fun getName(): String = NAME
|
|
64
|
+
|
|
65
|
+
override fun getConstants(): Map<String, Any> {
|
|
66
|
+
return mapOf(
|
|
67
|
+
"moduleName" to NAME,
|
|
68
|
+
"supportedEvents" to listOf(
|
|
69
|
+
"BluetoothStateChanged",
|
|
70
|
+
"ScanRestartRequired",
|
|
71
|
+
"BufferedScanResults",
|
|
72
|
+
"DozeModeChanged"
|
|
73
|
+
)
|
|
74
|
+
)
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
// ========================================================================
|
|
78
|
+
// Background Scan Service Control
|
|
79
|
+
// ========================================================================
|
|
80
|
+
|
|
81
|
+
@ReactMethod
|
|
82
|
+
fun startBackgroundScan(promise: Promise) {
|
|
83
|
+
try {
|
|
84
|
+
val intent = Intent(reactContext, GatewayScanService::class.java).apply {
|
|
85
|
+
action = GatewayScanService.ACTION_START
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
|
89
|
+
reactContext.startForegroundService(intent)
|
|
90
|
+
} else {
|
|
91
|
+
reactContext.startService(intent)
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
promise.resolve(true)
|
|
95
|
+
} catch (e: Exception) {
|
|
96
|
+
promise.reject("START_ERROR", "Failed to start background scan: ${e.message}", e)
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
@ReactMethod
|
|
101
|
+
fun stopBackgroundScan(promise: Promise) {
|
|
102
|
+
try {
|
|
103
|
+
val intent = Intent(reactContext, GatewayScanService::class.java).apply {
|
|
104
|
+
action = GatewayScanService.ACTION_STOP
|
|
105
|
+
}
|
|
106
|
+
reactContext.stopService(intent)
|
|
107
|
+
promise.resolve(true)
|
|
108
|
+
} catch (e: Exception) {
|
|
109
|
+
promise.reject("STOP_ERROR", "Failed to stop background scan: ${e.message}", e)
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
@ReactMethod
|
|
114
|
+
fun isBackgroundScanSupported(promise: Promise) {
|
|
115
|
+
// Background scanning is supported on Android 8.0+
|
|
116
|
+
promise.resolve(Build.VERSION.SDK_INT >= Build.VERSION_CODES.O)
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
@ReactMethod
|
|
120
|
+
fun isServiceRunning(promise: Promise) {
|
|
121
|
+
promise.resolve(GatewayScanService.isRunning)
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
// ========================================================================
|
|
125
|
+
// Permission Checking
|
|
126
|
+
// ========================================================================
|
|
127
|
+
|
|
128
|
+
@ReactMethod
|
|
129
|
+
fun hasPermissions(promise: Promise) {
|
|
130
|
+
val permissions = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
|
|
131
|
+
listOf(
|
|
132
|
+
Manifest.permission.BLUETOOTH_SCAN,
|
|
133
|
+
Manifest.permission.BLUETOOTH_CONNECT,
|
|
134
|
+
Manifest.permission.ACCESS_FINE_LOCATION
|
|
135
|
+
)
|
|
136
|
+
} else {
|
|
137
|
+
listOf(Manifest.permission.ACCESS_FINE_LOCATION)
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
val allGranted = permissions.all { permission ->
|
|
141
|
+
ContextCompat.checkSelfPermission(reactContext, permission) == PackageManager.PERMISSION_GRANTED
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
promise.resolve(allGranted)
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
@ReactMethod
|
|
148
|
+
fun hasNotificationPermission(promise: Promise) {
|
|
149
|
+
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
|
|
150
|
+
val granted = ContextCompat.checkSelfPermission(
|
|
151
|
+
reactContext,
|
|
152
|
+
Manifest.permission.POST_NOTIFICATIONS
|
|
153
|
+
) == PackageManager.PERMISSION_GRANTED
|
|
154
|
+
promise.resolve(granted)
|
|
155
|
+
} else {
|
|
156
|
+
promise.resolve(true)
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
// ========================================================================
|
|
161
|
+
// Battery Optimization
|
|
162
|
+
// ========================================================================
|
|
163
|
+
|
|
164
|
+
@ReactMethod
|
|
165
|
+
fun isBatteryOptimizationDisabled(promise: Promise) {
|
|
166
|
+
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
|
|
167
|
+
val isIgnoring = powerManager?.isIgnoringBatteryOptimizations(reactContext.packageName) ?: false
|
|
168
|
+
promise.resolve(isIgnoring)
|
|
169
|
+
} else {
|
|
170
|
+
promise.resolve(true)
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
@SuppressLint("BatteryLife")
|
|
175
|
+
@ReactMethod
|
|
176
|
+
fun requestBatteryOptimizationExemption(promise: Promise) {
|
|
177
|
+
try {
|
|
178
|
+
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
|
|
179
|
+
val intent = Intent(Settings.ACTION_REQUEST_IGNORE_BATTERY_OPTIMIZATIONS).apply {
|
|
180
|
+
data = Uri.parse("package:${reactContext.packageName}")
|
|
181
|
+
addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
|
|
182
|
+
}
|
|
183
|
+
reactContext.startActivity(intent)
|
|
184
|
+
}
|
|
185
|
+
promise.resolve(true)
|
|
186
|
+
} catch (e: Exception) {
|
|
187
|
+
promise.reject("BATTERY_OPT_ERROR", "Failed to request exemption: ${e.message}", e)
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
@ReactMethod
|
|
192
|
+
fun openBatteryOptimizationSettings(promise: Promise) {
|
|
193
|
+
try {
|
|
194
|
+
val intent = Intent(Settings.ACTION_IGNORE_BATTERY_OPTIMIZATION_SETTINGS).apply {
|
|
195
|
+
addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
|
|
196
|
+
}
|
|
197
|
+
reactContext.startActivity(intent)
|
|
198
|
+
promise.resolve(true)
|
|
199
|
+
} catch (e: Exception) {
|
|
200
|
+
promise.reject("SETTINGS_ERROR", "Failed to open settings: ${e.message}", e)
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
// ========================================================================
|
|
205
|
+
// Bluetooth State
|
|
206
|
+
// ========================================================================
|
|
207
|
+
|
|
208
|
+
@ReactMethod
|
|
209
|
+
fun isBluetoothEnabled(promise: Promise) {
|
|
210
|
+
promise.resolve(bluetoothAdapter?.isEnabled ?: false)
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
@ReactMethod
|
|
214
|
+
fun openBluetoothSettings(promise: Promise) {
|
|
215
|
+
try {
|
|
216
|
+
val intent = Intent(Settings.ACTION_BLUETOOTH_SETTINGS).apply {
|
|
217
|
+
addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
|
|
218
|
+
}
|
|
219
|
+
reactContext.startActivity(intent)
|
|
220
|
+
promise.resolve(true)
|
|
221
|
+
} catch (e: Exception) {
|
|
222
|
+
promise.reject("SETTINGS_ERROR", "Failed to open Bluetooth settings: ${e.message}", e)
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
// ========================================================================
|
|
227
|
+
// Device Info
|
|
228
|
+
// ========================================================================
|
|
229
|
+
|
|
230
|
+
@ReactMethod
|
|
231
|
+
fun getDeviceManufacturer(promise: Promise) {
|
|
232
|
+
promise.resolve(Build.MANUFACTURER)
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
@ReactMethod
|
|
236
|
+
fun isSamsungDevice(promise: Promise) {
|
|
237
|
+
promise.resolve(Build.MANUFACTURER.lowercase() == "samsung")
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
@ReactMethod
|
|
241
|
+
fun isDeviceInDozeMode(promise: Promise) {
|
|
242
|
+
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
|
|
243
|
+
promise.resolve(powerManager?.isDeviceIdleMode ?: false)
|
|
244
|
+
} else {
|
|
245
|
+
promise.resolve(false)
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
@ReactMethod
|
|
250
|
+
fun getPowerStateInfo(promise: Promise) {
|
|
251
|
+
val info = Arguments.createMap().apply {
|
|
252
|
+
putBoolean("isIgnoringBatteryOptimizations",
|
|
253
|
+
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M)
|
|
254
|
+
powerManager?.isIgnoringBatteryOptimizations(reactContext.packageName) ?: false
|
|
255
|
+
else true
|
|
256
|
+
)
|
|
257
|
+
putBoolean("isDeviceIdleMode",
|
|
258
|
+
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M)
|
|
259
|
+
powerManager?.isDeviceIdleMode ?: false
|
|
260
|
+
else false
|
|
261
|
+
)
|
|
262
|
+
putBoolean("isPowerSaveMode",
|
|
263
|
+
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP)
|
|
264
|
+
powerManager?.isPowerSaveMode ?: false
|
|
265
|
+
else false
|
|
266
|
+
)
|
|
267
|
+
putBoolean("isInteractive", powerManager?.isInteractive ?: true)
|
|
268
|
+
}
|
|
269
|
+
promise.resolve(info)
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
// ========================================================================
|
|
273
|
+
// Event Emitting
|
|
274
|
+
// ========================================================================
|
|
275
|
+
|
|
276
|
+
private fun sendEvent(eventName: String, params: WritableMap?) {
|
|
277
|
+
reactContext
|
|
278
|
+
.getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter::class.java)
|
|
279
|
+
.emit(eventName, params)
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
@ReactMethod
|
|
283
|
+
fun addListener(eventName: String) {
|
|
284
|
+
// Required for RN 0.65+
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
@ReactMethod
|
|
288
|
+
fun removeListeners(count: Int) {
|
|
289
|
+
// Required for RN 0.65+
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
// ========================================================================
|
|
293
|
+
// Broadcast Receivers
|
|
294
|
+
// ========================================================================
|
|
295
|
+
|
|
296
|
+
private fun registerReceivers() {
|
|
297
|
+
// Bluetooth state receiver
|
|
298
|
+
bluetoothReceiver = object : BroadcastReceiver() {
|
|
299
|
+
override fun onReceive(context: Context?, intent: Intent?) {
|
|
300
|
+
if (intent?.action == BluetoothAdapter.ACTION_STATE_CHANGED) {
|
|
301
|
+
val state = intent.getIntExtra(BluetoothAdapter.EXTRA_STATE, BluetoothAdapter.ERROR)
|
|
302
|
+
val enabled = state == BluetoothAdapter.STATE_ON
|
|
303
|
+
|
|
304
|
+
val params = Arguments.createMap().apply {
|
|
305
|
+
putBoolean("enabled", enabled)
|
|
306
|
+
}
|
|
307
|
+
sendEvent("BluetoothStateChanged", params)
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
val bluetoothFilter = IntentFilter(BluetoothAdapter.ACTION_STATE_CHANGED)
|
|
313
|
+
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
|
|
314
|
+
reactContext.registerReceiver(bluetoothReceiver, bluetoothFilter, Context.RECEIVER_NOT_EXPORTED)
|
|
315
|
+
} else {
|
|
316
|
+
reactContext.registerReceiver(bluetoothReceiver, bluetoothFilter)
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
// Scan restart receiver
|
|
320
|
+
scanRestartReceiver = object : BroadcastReceiver() {
|
|
321
|
+
override fun onReceive(context: Context?, intent: Intent?) {
|
|
322
|
+
val params = Arguments.createMap().apply {
|
|
323
|
+
putDouble("timestamp", System.currentTimeMillis().toDouble())
|
|
324
|
+
}
|
|
325
|
+
sendEvent("ScanRestartRequired", params)
|
|
326
|
+
}
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
val restartFilter = IntentFilter(ACTION_SCAN_RESTART)
|
|
330
|
+
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
|
|
331
|
+
reactContext.registerReceiver(scanRestartReceiver, restartFilter, Context.RECEIVER_NOT_EXPORTED)
|
|
332
|
+
} else {
|
|
333
|
+
reactContext.registerReceiver(scanRestartReceiver, restartFilter)
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
// Buffered results receiver
|
|
337
|
+
bufferedResultsReceiver = object : BroadcastReceiver() {
|
|
338
|
+
override fun onReceive(context: Context?, intent: Intent?) {
|
|
339
|
+
val results = intent?.getStringExtra("results") ?: "[]"
|
|
340
|
+
val count = intent?.getIntExtra("count", 0) ?: 0
|
|
341
|
+
|
|
342
|
+
val params = Arguments.createMap().apply {
|
|
343
|
+
putString("results", results)
|
|
344
|
+
putInt("count", count)
|
|
345
|
+
putDouble("timestamp", System.currentTimeMillis().toDouble())
|
|
346
|
+
}
|
|
347
|
+
sendEvent("BufferedScanResults", params)
|
|
348
|
+
}
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
val bufferedFilter = IntentFilter(ACTION_BUFFERED_RESULTS)
|
|
352
|
+
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
|
|
353
|
+
reactContext.registerReceiver(bufferedResultsReceiver, bufferedFilter, Context.RECEIVER_NOT_EXPORTED)
|
|
354
|
+
} else {
|
|
355
|
+
reactContext.registerReceiver(bufferedResultsReceiver, bufferedFilter)
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
// Doze mode receiver
|
|
359
|
+
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
|
|
360
|
+
dozeModeReceiver = object : BroadcastReceiver() {
|
|
361
|
+
override fun onReceive(context: Context?, intent: Intent?) {
|
|
362
|
+
val isDozeMode = powerManager?.isDeviceIdleMode ?: false
|
|
363
|
+
val isBatteryOptExempt = powerManager?.isIgnoringBatteryOptimizations(
|
|
364
|
+
reactContext.packageName
|
|
365
|
+
) ?: false
|
|
366
|
+
|
|
367
|
+
val params = Arguments.createMap().apply {
|
|
368
|
+
putBoolean("isDozeMode", isDozeMode)
|
|
369
|
+
putBoolean("isBatteryOptExempt", isBatteryOptExempt)
|
|
370
|
+
putDouble("timestamp", System.currentTimeMillis().toDouble())
|
|
371
|
+
}
|
|
372
|
+
sendEvent("DozeModeChanged", params)
|
|
373
|
+
}
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
val dozeFilter = IntentFilter(PowerManager.ACTION_DEVICE_IDLE_MODE_CHANGED)
|
|
377
|
+
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
|
|
378
|
+
reactContext.registerReceiver(dozeModeReceiver, dozeFilter, Context.RECEIVER_NOT_EXPORTED)
|
|
379
|
+
} else {
|
|
380
|
+
reactContext.registerReceiver(dozeModeReceiver, dozeFilter)
|
|
381
|
+
}
|
|
382
|
+
}
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
override fun invalidate() {
|
|
386
|
+
super.invalidate()
|
|
387
|
+
bluetoothReceiver?.let { reactContext.unregisterReceiver(it) }
|
|
388
|
+
scanRestartReceiver?.let { reactContext.unregisterReceiver(it) }
|
|
389
|
+
bufferedResultsReceiver?.let { reactContext.unregisterReceiver(it) }
|
|
390
|
+
dozeModeReceiver?.let { reactContext.unregisterReceiver(it) }
|
|
391
|
+
}
|
|
392
|
+
}
|