react-native-flic2 2.0.0-beta.1 → 2.0.0-beta.10
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/README.md +85 -22
- package/android/build.gradle +1 -1
- package/android/src/main/AndroidManifest.xml +23 -0
- package/android/src/main/java/nl/xguard/flic2/ActivityUtil.kt +29 -0
- package/android/src/main/java/{com → nl/xguard}/flic2/Flic2ButtonListener.kt +19 -3
- package/android/src/main/java/{com → nl/xguard}/flic2/Flic2Converter.kt +2 -3
- package/android/src/main/java/{com → nl/xguard}/flic2/Flic2Module.kt +96 -56
- package/android/src/main/java/{com → nl/xguard}/flic2/Flic2Package.kt +2 -1
- package/android/src/main/java/nl/xguard/flic2/Flic2Service.kt +274 -0
- package/ios/Flic2.mm +12 -12
- package/lib/module/NativeFlic2.js.map +1 -1
- package/lib/module/index.js +6 -14
- package/lib/module/index.js.map +1 -1
- package/lib/typescript/src/NativeFlic2.d.ts +14 -49
- package/lib/typescript/src/NativeFlic2.d.ts.map +1 -1
- package/lib/typescript/src/index.d.ts +17 -52
- package/lib/typescript/src/index.d.ts.map +1 -1
- package/package.json +23 -23
- package/src/NativeFlic2.ts +30 -28
- package/src/index.ts +18 -40
- package/android/src/main/java/com/flic2/Flic2Service.kt +0 -112
- package/lib/module/index.bak.js +0 -161
- package/lib/module/index.bak.js.map +0 -1
- package/lib/typescript/src/index.bak.d.ts +0 -1
- package/lib/typescript/src/index.bak.d.ts.map +0 -1
- package/src/index.bak.tsx +0 -159
package/README.md
CHANGED
|
@@ -66,6 +66,65 @@ The library automatically includes the necessary permissions in `AndroidManifest
|
|
|
66
66
|
|
|
67
67
|
You'll need to request these permissions before scanning for buttons. Use a library like `react-native-permissions` or implement permission requests manually.
|
|
68
68
|
|
|
69
|
+
#### Customizing the Foreground Service Notification (Android)
|
|
70
|
+
|
|
71
|
+
The library runs a foreground service to keep Flic2 buttons connected in the background. You can customize the notification appearance by adding metadata to your app's `AndroidManifest.xml`:
|
|
72
|
+
|
|
73
|
+
```xml
|
|
74
|
+
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
|
|
75
|
+
<application>
|
|
76
|
+
<!-- Your existing application configuration -->
|
|
77
|
+
|
|
78
|
+
<!-- Customize Flic2 foreground service notification -->
|
|
79
|
+
<meta-data
|
|
80
|
+
android:name="nl.xguard.flic2.notification_title"
|
|
81
|
+
android:value="My Flic2 Service" />
|
|
82
|
+
<meta-data
|
|
83
|
+
android:name="nl.xguard.flic2.notification_text"
|
|
84
|
+
android:value="Flic2 buttons are active" />
|
|
85
|
+
<meta-data
|
|
86
|
+
android:name="nl.xguard.flic2.notification_icon"
|
|
87
|
+
android:resource="@drawable/ic_notification" />
|
|
88
|
+
<meta-data
|
|
89
|
+
android:name="nl.xguard.flic2.notification_channel_name"
|
|
90
|
+
android:value="Flic2 Notifications" />
|
|
91
|
+
<meta-data
|
|
92
|
+
android:name="nl.xguard.flic2.notification_channel_description"
|
|
93
|
+
android:value="Notifications for Flic2 button connections" />
|
|
94
|
+
<meta-data
|
|
95
|
+
android:name="nl.xguard.flic2.notification_id"
|
|
96
|
+
android:value="123321" />
|
|
97
|
+
<meta-data
|
|
98
|
+
android:name="nl.xguard.flic2.notification_channel_id"
|
|
99
|
+
android:value="my_custom_channel_id" />
|
|
100
|
+
</application>
|
|
101
|
+
</manifest>
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
**Available Configuration Options:**
|
|
105
|
+
|
|
106
|
+
- `nl.xguard.flic2.notification_title` - Notification title (default: "Flic 2")
|
|
107
|
+
- `nl.xguard.flic2.notification_text` - Notification text (default: "Flic 2 service is running")
|
|
108
|
+
- `nl.xguard.flic2.notification_icon` - Notification icon resource ID (default: system info icon)
|
|
109
|
+
- Use `@drawable/your_icon_name` or `@mipmap/your_icon_name` format
|
|
110
|
+
- `nl.xguard.flic2.notification_channel_name` - Notification channel name (default: "Flic2Channel")
|
|
111
|
+
- `nl.xguard.flic2.notification_channel_description` - Notification channel description (default: "Flic2Channel")
|
|
112
|
+
- `nl.xguard.flic2.notification_id` - Notification ID integer (default: 123321)
|
|
113
|
+
- `nl.xguard.flic2.notification_channel_id` - Notification channel ID string (default: "Notification_Channel_Flic2Service")
|
|
114
|
+
|
|
115
|
+
**Example with Custom Icon:**
|
|
116
|
+
|
|
117
|
+
1. Add your notification icon to `android/app/src/main/res/drawable/` (e.g., `ic_flic2_notification.png`)
|
|
118
|
+
|
|
119
|
+
2. Add metadata to `AndroidManifest.xml`:
|
|
120
|
+
```xml
|
|
121
|
+
<meta-data
|
|
122
|
+
android:name="nl.xguard.flic2.notification_icon"
|
|
123
|
+
android:resource="@drawable/ic_flic2_notification" />
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
**Note:** The notification icon must be a white/transparent icon suitable for Android notifications. If you don't specify a custom icon, the system default info icon will be used.
|
|
127
|
+
|
|
69
128
|
## Basic Usage
|
|
70
129
|
|
|
71
130
|
### 1. Initialize the Library (Global Setup)
|
|
@@ -390,7 +449,7 @@ export default Flic2Example;
|
|
|
390
449
|
|
|
391
450
|
### Initialization
|
|
392
451
|
|
|
393
|
-
#### `initialize(): Promise<
|
|
452
|
+
#### `initialize(): Promise<void>`
|
|
394
453
|
|
|
395
454
|
Initialize the Flic2 manager. This must be called before using any other methods.
|
|
396
455
|
|
|
@@ -400,7 +459,7 @@ await Flic2.initialize();
|
|
|
400
459
|
|
|
401
460
|
### Scanning
|
|
402
461
|
|
|
403
|
-
#### `startScan(): Promise<
|
|
462
|
+
#### `startScan(): Promise<void>`
|
|
404
463
|
|
|
405
464
|
Start scanning for new Flic2 buttons. The scan will emit `scanStatusChange` events.
|
|
406
465
|
|
|
@@ -408,7 +467,7 @@ Start scanning for new Flic2 buttons. The scan will emit `scanStatusChange` even
|
|
|
408
467
|
await Flic2.startScan();
|
|
409
468
|
```
|
|
410
469
|
|
|
411
|
-
#### `stopScan(): Promise<
|
|
470
|
+
#### `stopScan(): Promise<void>`
|
|
412
471
|
|
|
413
472
|
Stop an ongoing scan.
|
|
414
473
|
|
|
@@ -442,7 +501,7 @@ Get a specific button by UUID.
|
|
|
442
501
|
const button = await Flic2.getButton('button-uuid');
|
|
443
502
|
```
|
|
444
503
|
|
|
445
|
-
#### `connectAllKnownButtons(): Promise<
|
|
504
|
+
#### `connectAllKnownButtons(): Promise<void>`
|
|
446
505
|
|
|
447
506
|
Connect to all previously known buttons.
|
|
448
507
|
|
|
@@ -450,7 +509,7 @@ Connect to all previously known buttons.
|
|
|
450
509
|
await Flic2.connectAllKnownButtons();
|
|
451
510
|
```
|
|
452
511
|
|
|
453
|
-
#### `disconnectAllKnownButtons(): Promise<
|
|
512
|
+
#### `disconnectAllKnownButtons(): Promise<void>`
|
|
454
513
|
|
|
455
514
|
Disconnect all connected buttons.
|
|
456
515
|
|
|
@@ -458,7 +517,7 @@ Disconnect all connected buttons.
|
|
|
458
517
|
await Flic2.disconnectAllKnownButtons();
|
|
459
518
|
```
|
|
460
519
|
|
|
461
|
-
#### `forgetButton(uuid: string): Promise<
|
|
520
|
+
#### `forgetButton(uuid: string): Promise<void>`
|
|
462
521
|
|
|
463
522
|
Forget (unpair) a specific button.
|
|
464
523
|
|
|
@@ -466,7 +525,7 @@ Forget (unpair) a specific button.
|
|
|
466
525
|
await Flic2.forgetButton('button-uuid');
|
|
467
526
|
```
|
|
468
527
|
|
|
469
|
-
#### `forgetAllButtons(): Promise<
|
|
528
|
+
#### `forgetAllButtons(): Promise<void>`
|
|
470
529
|
|
|
471
530
|
Forget all buttons.
|
|
472
531
|
|
|
@@ -476,50 +535,54 @@ await Flic2.forgetAllButtons();
|
|
|
476
535
|
|
|
477
536
|
### Button Configuration
|
|
478
537
|
|
|
479
|
-
#### `buttonConnect(uuid: string): Promise<
|
|
538
|
+
#### `buttonConnect(uuid: string): Promise<FlicButton>`
|
|
480
539
|
|
|
481
|
-
Connect to a specific button.
|
|
540
|
+
Connect to a specific button. Returns the button object.
|
|
482
541
|
|
|
483
542
|
```tsx
|
|
484
|
-
await Flic2.buttonConnect('button-uuid');
|
|
543
|
+
const button = await Flic2.buttonConnect('button-uuid');
|
|
485
544
|
```
|
|
486
545
|
|
|
487
|
-
#### `buttonDisconnect(uuid: string): Promise<
|
|
546
|
+
#### `buttonDisconnect(uuid: string): Promise<FlicButton>`
|
|
488
547
|
|
|
489
|
-
Disconnect a specific button.
|
|
548
|
+
Disconnect a specific button. Returns the button object.
|
|
490
549
|
|
|
491
550
|
```tsx
|
|
492
|
-
await Flic2.buttonDisconnect('button-uuid');
|
|
551
|
+
const button = await Flic2.buttonDisconnect('button-uuid');
|
|
493
552
|
```
|
|
494
553
|
|
|
495
|
-
#### `buttonSetNickname(uuid: string, nickname: string): Promise<
|
|
554
|
+
#### `buttonSetNickname(uuid: string, nickname: string): Promise<FlicButton>`
|
|
496
555
|
|
|
497
|
-
Set a custom nickname for a button.
|
|
556
|
+
Set a custom nickname for a button. Returns the updated button object.
|
|
498
557
|
|
|
499
558
|
```tsx
|
|
500
|
-
await Flic2.buttonSetNickname('button-uuid', 'My Button');
|
|
559
|
+
const button = await Flic2.buttonSetNickname('button-uuid', 'My Button');
|
|
501
560
|
```
|
|
502
561
|
|
|
503
|
-
#### `buttonSetTriggerMode(uuid: string, mode: TriggerModeType): Promise<
|
|
562
|
+
#### `buttonSetTriggerMode(uuid: string, mode: TriggerModeType): Promise<FlicButton>`
|
|
504
563
|
|
|
505
|
-
Set the trigger mode for a button. Modes:
|
|
564
|
+
Set the trigger mode for a button. Returns the updated button object. Modes:
|
|
506
565
|
- `0`: Click and Hold
|
|
507
566
|
- `1`: Click and Double Click
|
|
508
567
|
- `2`: Click and Double Click and Hold
|
|
509
568
|
- `3`: Click only
|
|
510
569
|
|
|
570
|
+
**Note:** This method is only supported on iOS. On Android, it will reject with an error.
|
|
571
|
+
|
|
511
572
|
```tsx
|
|
512
|
-
await Flic2.buttonSetTriggerMode('button-uuid', 3); // Click only
|
|
573
|
+
const button = await Flic2.buttonSetTriggerMode('button-uuid', 3); // Click only
|
|
513
574
|
```
|
|
514
575
|
|
|
515
|
-
#### `buttonSetLatencyMode(uuid: string, mode: LatencyModeType): Promise<
|
|
576
|
+
#### `buttonSetLatencyMode(uuid: string, mode: LatencyModeType): Promise<FlicButton>`
|
|
516
577
|
|
|
517
|
-
Set the latency mode for a button. Modes:
|
|
578
|
+
Set the latency mode for a button. Returns the updated button object. Modes:
|
|
518
579
|
- `0`: Normal latency
|
|
519
580
|
- `1`: Low latency
|
|
520
581
|
|
|
582
|
+
**Note:** This method is only supported on iOS. On Android, it will reject with an error.
|
|
583
|
+
|
|
521
584
|
```tsx
|
|
522
|
-
await Flic2.buttonSetLatencyMode('button-uuid', 1); // Low latency
|
|
585
|
+
const button = await Flic2.buttonSetLatencyMode('button-uuid', 1); // Low latency
|
|
523
586
|
```
|
|
524
587
|
|
|
525
588
|
#### `getBatteryHealth(uuid: string): Promise<boolean>`
|
package/android/build.gradle
CHANGED
|
@@ -15,6 +15,7 @@
|
|
|
15
15
|
<!-- Foreground service permission -->
|
|
16
16
|
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
|
|
17
17
|
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_CONNECTED_DEVICE" />
|
|
18
|
+
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
|
|
18
19
|
|
|
19
20
|
<application>
|
|
20
21
|
<service
|
|
@@ -22,6 +23,28 @@
|
|
|
22
23
|
android:enabled="true"
|
|
23
24
|
android:exported="false"
|
|
24
25
|
android:foregroundServiceType="connectedDevice" />
|
|
26
|
+
|
|
27
|
+
<receiver
|
|
28
|
+
android:name=".Flic2Service$BootUpReceiver"
|
|
29
|
+
android:enabled="true"
|
|
30
|
+
android:permission="android.permission.RECEIVE_BOOT_COMPLETED"
|
|
31
|
+
android:exported="false">
|
|
32
|
+
<intent-filter>
|
|
33
|
+
<action android:name="android.intent.action.BOOT_COMPLETED" />
|
|
34
|
+
<category android:name="android.intent.category.DEFAULT" />
|
|
35
|
+
</intent-filter>
|
|
36
|
+
</receiver>
|
|
37
|
+
|
|
38
|
+
<receiver
|
|
39
|
+
android:name=".Flic2Service$UpdateReceiver"
|
|
40
|
+
android:enabled="true"
|
|
41
|
+
android:exported="false">
|
|
42
|
+
<intent-filter>
|
|
43
|
+
<action android:name="android.intent.action.PACKAGE_REPLACED" />
|
|
44
|
+
<data
|
|
45
|
+
android:scheme="package" />
|
|
46
|
+
</intent-filter>
|
|
47
|
+
</receiver>
|
|
25
48
|
</application>
|
|
26
49
|
|
|
27
50
|
</manifest>
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
package nl.xguard.flic2
|
|
2
|
+
|
|
3
|
+
import android.app.ActivityManager
|
|
4
|
+
import android.content.Context
|
|
5
|
+
import android.content.Intent
|
|
6
|
+
import android.os.Build
|
|
7
|
+
|
|
8
|
+
object ActivityUtil {
|
|
9
|
+
private const val TAG = "ActivityUtil"
|
|
10
|
+
|
|
11
|
+
fun isServiceRunning(context: Context, serviceClass: Class<*>): Boolean {
|
|
12
|
+
val manager = context.getSystemService(Context.ACTIVITY_SERVICE) as ActivityManager
|
|
13
|
+
for (service in manager.getRunningServices(Integer.MAX_VALUE)) {
|
|
14
|
+
if (serviceClass.name == service.service.className) {
|
|
15
|
+
return true
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
return false
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
fun startForegroundService(context: Context, intent: Intent) {
|
|
22
|
+
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
|
23
|
+
context.startForegroundService(intent)
|
|
24
|
+
} else {
|
|
25
|
+
context.startService(intent)
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
package
|
|
1
|
+
package nl.xguard.flic2
|
|
2
2
|
|
|
3
3
|
import com.facebook.react.bridge.Arguments
|
|
4
4
|
import com.facebook.react.bridge.WritableMap
|
|
@@ -47,7 +47,15 @@ class Flic2ButtonEventListener(
|
|
|
47
47
|
val event = if (isDown) "buttonDown" else "buttonUp"
|
|
48
48
|
emitEvent(createButtonEvent(button, event).apply {
|
|
49
49
|
putBoolean("queued", wasQueued)
|
|
50
|
-
|
|
50
|
+
// Match old Android implementation and iOS:
|
|
51
|
+
// - Age is in seconds
|
|
52
|
+
// - Only meaningful for queued events; 0 for real-time events
|
|
53
|
+
val ageSeconds = if (wasQueued) {
|
|
54
|
+
(button.readyTimestamp - timestamp) / 1000.0
|
|
55
|
+
} else {
|
|
56
|
+
0.0
|
|
57
|
+
}
|
|
58
|
+
putDouble("age", ageSeconds)
|
|
51
59
|
})
|
|
52
60
|
}
|
|
53
61
|
|
|
@@ -94,7 +102,15 @@ class Flic2ButtonEventListener(
|
|
|
94
102
|
}
|
|
95
103
|
emitEvent(createButtonEvent(button, event).apply {
|
|
96
104
|
putBoolean("queued", wasQueued)
|
|
97
|
-
|
|
105
|
+
// Match old Android implementation and iOS:
|
|
106
|
+
// - Age is in seconds
|
|
107
|
+
// - Only meaningful for queued events; 0 for real-time events
|
|
108
|
+
val ageSeconds = if (wasQueued) {
|
|
109
|
+
(button.readyTimestamp - timestamp) / 1000.0
|
|
110
|
+
} else {
|
|
111
|
+
0.0
|
|
112
|
+
}
|
|
113
|
+
putDouble("age", ageSeconds)
|
|
98
114
|
})
|
|
99
115
|
}
|
|
100
116
|
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
package
|
|
1
|
+
package nl.xguard.flic2
|
|
2
2
|
|
|
3
3
|
import com.facebook.react.bridge.Arguments
|
|
4
4
|
import com.facebook.react.bridge.WritableArray
|
|
@@ -31,8 +31,7 @@ object Flic2Converter {
|
|
|
31
31
|
putInt("firmwareRevision", button.getFirmwareVersion())
|
|
32
32
|
|
|
33
33
|
// Check if ready by comparing connection state
|
|
34
|
-
|
|
35
|
-
putBoolean("isReady", isReady)
|
|
34
|
+
putBoolean("isReady", connState == Flic2Button.CONNECTION_STATE_CONNECTED_READY)
|
|
36
35
|
|
|
37
36
|
// Get battery level from BatteryLevel object
|
|
38
37
|
val batteryLevel = button.getLastKnownBatteryLevel()
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
package
|
|
1
|
+
package nl.xguard.flic2
|
|
2
2
|
|
|
3
3
|
import android.content.ComponentName
|
|
4
4
|
import android.content.Context
|
|
@@ -48,8 +48,7 @@ class Flic2Module(reactContext: ReactApplicationContext) :
|
|
|
48
48
|
private val serviceConnection = object : ServiceConnection {
|
|
49
49
|
override fun onServiceConnected(name: ComponentName?, service: IBinder?) {
|
|
50
50
|
Log.d(TAG, "Service connected")
|
|
51
|
-
|
|
52
|
-
flic2Service = binder.getService()
|
|
51
|
+
flic2Service = (service as Flic2Service.Flic2ServiceBinder).getService()
|
|
53
52
|
serviceBound = true
|
|
54
53
|
|
|
55
54
|
// Set up listeners for existing buttons
|
|
@@ -57,14 +56,13 @@ class Flic2Module(reactContext: ReactApplicationContext) :
|
|
|
57
56
|
manager.buttons.forEach { button ->
|
|
58
57
|
setupButtonListener(button)
|
|
59
58
|
}
|
|
59
|
+
// Update foreground service state based on button count
|
|
60
|
+
updateForegroundServiceState(manager.buttons.size)
|
|
60
61
|
}
|
|
61
62
|
|
|
62
63
|
// Resolve the initialize promise if pending
|
|
63
64
|
initializePromise?.let { promise ->
|
|
64
|
-
promise.resolve(
|
|
65
|
-
putBoolean("success", true)
|
|
66
|
-
putString("message", "Manager initialized successfully")
|
|
67
|
-
})
|
|
65
|
+
promise.resolve(null)
|
|
68
66
|
initializePromise = null
|
|
69
67
|
}
|
|
70
68
|
}
|
|
@@ -88,6 +86,31 @@ class Flic2Module(reactContext: ReactApplicationContext) :
|
|
|
88
86
|
|
|
89
87
|
override fun invalidate() {
|
|
90
88
|
super.invalidate()
|
|
89
|
+
|
|
90
|
+
// Remove all button listeners before cleanup to prevent callbacks after teardown
|
|
91
|
+
try {
|
|
92
|
+
val manager = flic2Service?.getManager()
|
|
93
|
+
if (manager != null) {
|
|
94
|
+
buttonListeners.forEach { (uuid, listener) ->
|
|
95
|
+
try {
|
|
96
|
+
// Find the button and remove the listener
|
|
97
|
+
val button = manager.buttons.find { it.uuid == uuid }
|
|
98
|
+
if (button != null) {
|
|
99
|
+
button.removeListener(listener)
|
|
100
|
+
Log.d(TAG, "Removed listener for button during invalidate: $uuid")
|
|
101
|
+
}
|
|
102
|
+
} catch (e: Exception) {
|
|
103
|
+
Log.w(TAG, "Failed to remove listener for button during invalidate: $uuid", e)
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
} catch (e: Exception) {
|
|
108
|
+
Log.w(TAG, "Error during listener cleanup in invalidate", e)
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
// Clear listeners map
|
|
112
|
+
buttonListeners.clear()
|
|
113
|
+
|
|
91
114
|
moduleScope.cancel()
|
|
92
115
|
if (serviceBound) {
|
|
93
116
|
reactApplicationContext.unbindService(serviceConnection)
|
|
@@ -104,11 +127,10 @@ class Flic2Module(reactContext: ReactApplicationContext) :
|
|
|
104
127
|
|
|
105
128
|
val intent = Intent(reactApplicationContext, Flic2Service::class.java)
|
|
106
129
|
|
|
107
|
-
//
|
|
108
|
-
if (
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
reactApplicationContext.startService(intent)
|
|
130
|
+
// Check if service is already running
|
|
131
|
+
if (!ActivityUtil.isServiceRunning(reactApplicationContext, Flic2Service::class.java)) {
|
|
132
|
+
// Start service
|
|
133
|
+
ActivityUtil.startForegroundService(reactApplicationContext, intent)
|
|
112
134
|
}
|
|
113
135
|
|
|
114
136
|
// Bind to service - promise will be resolved in onServiceConnected
|
|
@@ -180,14 +202,17 @@ class Flic2Module(reactContext: ReactApplicationContext) :
|
|
|
180
202
|
override fun onComplete(result: Int, subCode: Int, button: Flic2Button?) {
|
|
181
203
|
Log.d(TAG, "Scan complete: result=$result, button=${button?.uuid}")
|
|
182
204
|
|
|
183
|
-
val resultCode = mapScanResultToCode(result)
|
|
184
|
-
|
|
185
205
|
if (result == Flic2ScanCallback.RESULT_SUCCESS && button != null) {
|
|
186
206
|
// Auto-connect (trigger mode not available in Android v1.1.0+)
|
|
187
207
|
button.connect()
|
|
188
208
|
|
|
189
209
|
setupButtonListener(button)
|
|
190
210
|
|
|
211
|
+
// Update foreground service state after adding button
|
|
212
|
+
flic2Service?.getManager()?.let { manager ->
|
|
213
|
+
updateForegroundServiceState(manager.buttons.size)
|
|
214
|
+
}
|
|
215
|
+
|
|
191
216
|
// Emit discovered event as button event (like iOS)
|
|
192
217
|
emitOnButtonEvent(Arguments.createMap().apply {
|
|
193
218
|
putString("uuid", button.uuid)
|
|
@@ -195,24 +220,20 @@ class Flic2Module(reactContext: ReactApplicationContext) :
|
|
|
195
220
|
putMap("button", Flic2Converter.buttonToMap(button))
|
|
196
221
|
})
|
|
197
222
|
} else {
|
|
198
|
-
|
|
199
|
-
Log.e(TAG, "Scan failed with error code: $errorCode")
|
|
223
|
+
Log.e(TAG, "Scan failed with error code: ${Flic2Converter.scanResultToString(result)}")
|
|
200
224
|
}
|
|
201
225
|
|
|
202
226
|
// Emit scan completion with result code
|
|
203
227
|
emitOnScanStatusChange(Arguments.createMap().apply {
|
|
204
228
|
putString("event", "completion")
|
|
205
229
|
putString("eventName", "completion")
|
|
206
|
-
putInt("result",
|
|
230
|
+
putInt("result", mapScanResultToCode(result))
|
|
207
231
|
})
|
|
208
232
|
}
|
|
209
233
|
})
|
|
210
234
|
|
|
211
235
|
// Return immediately - scan results will come through events
|
|
212
|
-
promise.resolve(
|
|
213
|
-
putBoolean("success", true)
|
|
214
|
-
putString("message", "Scan started")
|
|
215
|
-
})
|
|
236
|
+
promise.resolve(null)
|
|
216
237
|
}
|
|
217
238
|
|
|
218
239
|
override fun stopScan(promise: Promise) {
|
|
@@ -226,10 +247,7 @@ class Flic2Module(reactContext: ReactApplicationContext) :
|
|
|
226
247
|
scanJob?.cancel()
|
|
227
248
|
manager.stopScan()
|
|
228
249
|
|
|
229
|
-
promise.resolve(
|
|
230
|
-
putBoolean("success", true)
|
|
231
|
-
putString("message", "Scan stopped")
|
|
232
|
-
})
|
|
250
|
+
promise.resolve(null)
|
|
233
251
|
} catch (e: Exception) {
|
|
234
252
|
Log.e(TAG, "Failed to stop scan", e)
|
|
235
253
|
promise.reject("STOP_SCAN_ERROR", "Failed to stop scan: ${e.message}", e)
|
|
@@ -253,16 +271,27 @@ class Flic2Module(reactContext: ReactApplicationContext) :
|
|
|
253
271
|
// Disconnect before forgetting like iOS
|
|
254
272
|
button.disconnectOrAbortPendingConnection()
|
|
255
273
|
|
|
256
|
-
//
|
|
274
|
+
// Explicitly remove listener from button before forgetting (matches old implementation)
|
|
275
|
+
val listener = buttonListeners[uuid]
|
|
276
|
+
if (listener != null) {
|
|
277
|
+
try {
|
|
278
|
+
button.removeListener(listener)
|
|
279
|
+
Log.d(TAG, "Removed listener for button: $uuid")
|
|
280
|
+
} catch (e: Exception) {
|
|
281
|
+
Log.w(TAG, "Failed to remove listener for button: $uuid", e)
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
// Remove listener from map
|
|
257
286
|
buttonListeners.remove(uuid)
|
|
258
287
|
|
|
259
288
|
// Forget button
|
|
260
289
|
manager.forgetButton(button)
|
|
261
290
|
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
291
|
+
// Update foreground service state after removing button
|
|
292
|
+
updateForegroundServiceState(manager.buttons.size)
|
|
293
|
+
|
|
294
|
+
promise.resolve(null)
|
|
266
295
|
} catch (e: Exception) {
|
|
267
296
|
Log.e(TAG, "Failed to forget button", e)
|
|
268
297
|
promise.reject("FORGET_ERROR", "Failed to forget button: ${e.message}", e)
|
|
@@ -281,10 +310,7 @@ class Flic2Module(reactContext: ReactApplicationContext) :
|
|
|
281
310
|
|
|
282
311
|
button.connect()
|
|
283
312
|
|
|
284
|
-
promise.resolve(
|
|
285
|
-
putBoolean("success", true)
|
|
286
|
-
putString("message", "Connection initiated")
|
|
287
|
-
})
|
|
313
|
+
promise.resolve(Flic2Converter.buttonToMap(button))
|
|
288
314
|
} catch (e: Exception) {
|
|
289
315
|
Log.e(TAG, "Failed to connect button", e)
|
|
290
316
|
promise.reject("CONNECT_ERROR", "Failed to connect: ${e.message}", e)
|
|
@@ -301,10 +327,7 @@ class Flic2Module(reactContext: ReactApplicationContext) :
|
|
|
301
327
|
|
|
302
328
|
button.disconnectOrAbortPendingConnection()
|
|
303
329
|
|
|
304
|
-
promise.resolve(
|
|
305
|
-
putBoolean("success", true)
|
|
306
|
-
putString("message", "Disconnection initiated")
|
|
307
|
-
})
|
|
330
|
+
promise.resolve(Flic2Converter.buttonToMap(button))
|
|
308
331
|
} catch (e: Exception) {
|
|
309
332
|
Log.e(TAG, "Failed to disconnect button", e)
|
|
310
333
|
promise.reject("DISCONNECT_ERROR", "Failed to disconnect: ${e.message}", e)
|
|
@@ -336,10 +359,7 @@ class Flic2Module(reactContext: ReactApplicationContext) :
|
|
|
336
359
|
// v1.1.0 uses setName() method instead of property
|
|
337
360
|
button.setName(nickname)
|
|
338
361
|
|
|
339
|
-
promise.resolve(
|
|
340
|
-
putBoolean("success", true)
|
|
341
|
-
putString("message", "Nickname set")
|
|
342
|
-
})
|
|
362
|
+
promise.resolve(Flic2Converter.buttonToMap(button))
|
|
343
363
|
} catch (e: Exception) {
|
|
344
364
|
Log.e(TAG, "Failed to set nickname", e)
|
|
345
365
|
promise.reject("SET_NICKNAME_ERROR", "Failed to set nickname: ${e.message}", e)
|
|
@@ -362,10 +382,7 @@ class Flic2Module(reactContext: ReactApplicationContext) :
|
|
|
362
382
|
button.connect()
|
|
363
383
|
}
|
|
364
384
|
|
|
365
|
-
promise.resolve(
|
|
366
|
-
putBoolean("success", true)
|
|
367
|
-
putString("message", "All buttons connection initiated")
|
|
368
|
-
})
|
|
385
|
+
promise.resolve(null)
|
|
369
386
|
} catch (e: Exception) {
|
|
370
387
|
Log.e(TAG, "Failed to connect all buttons", e)
|
|
371
388
|
promise.reject("CONNECT_ALL_ERROR", "Failed to connect all buttons: ${e.message}", e)
|
|
@@ -387,10 +404,7 @@ class Flic2Module(reactContext: ReactApplicationContext) :
|
|
|
387
404
|
button.disconnectOrAbortPendingConnection()
|
|
388
405
|
}
|
|
389
406
|
|
|
390
|
-
promise.resolve(
|
|
391
|
-
putBoolean("success", true)
|
|
392
|
-
putString("message", "All buttons disconnection initiated")
|
|
393
|
-
})
|
|
407
|
+
promise.resolve(null)
|
|
394
408
|
} catch (e: Exception) {
|
|
395
409
|
Log.e(TAG, "Failed to disconnect all buttons", e)
|
|
396
410
|
promise.reject("DISCONNECT_ALL_ERROR", "Failed to disconnect all buttons: ${e.message}", e)
|
|
@@ -409,15 +423,31 @@ class Flic2Module(reactContext: ReactApplicationContext) :
|
|
|
409
423
|
val buttons = manager.buttons.toList()
|
|
410
424
|
|
|
411
425
|
buttons.forEach { button ->
|
|
426
|
+
// Explicitly remove listener from button before forgetting (matches old implementation)
|
|
427
|
+
val listener = buttonListeners[button.uuid]
|
|
428
|
+
if (listener != null) {
|
|
429
|
+
try {
|
|
430
|
+
button.removeListener(listener)
|
|
431
|
+
Log.d(TAG, "Removed listener for button: ${button.uuid}")
|
|
432
|
+
} catch (e: Exception) {
|
|
433
|
+
Log.w(TAG, "Failed to remove listener for button: ${button.uuid}", e)
|
|
434
|
+
}
|
|
435
|
+
}
|
|
436
|
+
|
|
437
|
+
// Remove listener from map
|
|
412
438
|
buttonListeners.remove(button.uuid)
|
|
439
|
+
|
|
440
|
+
// Disconnect before forgetting
|
|
413
441
|
button.disconnectOrAbortPendingConnection()
|
|
442
|
+
|
|
443
|
+
// Forget button
|
|
414
444
|
manager.forgetButton(button)
|
|
415
445
|
}
|
|
416
446
|
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
447
|
+
// Update foreground service state after removing all buttons
|
|
448
|
+
updateForegroundServiceState(manager.buttons.size)
|
|
449
|
+
|
|
450
|
+
promise.resolve(null)
|
|
421
451
|
} catch (e: Exception) {
|
|
422
452
|
Log.e(TAG, "Failed to forget all buttons", e)
|
|
423
453
|
promise.reject("FORGET_ALL_ERROR", "Failed to forget all buttons: ${e.message}", e)
|
|
@@ -432,8 +462,7 @@ class Flic2Module(reactContext: ReactApplicationContext) :
|
|
|
432
462
|
return
|
|
433
463
|
}
|
|
434
464
|
|
|
435
|
-
|
|
436
|
-
promise.resolve(scanning)
|
|
465
|
+
promise.resolve(scanJob != null && scanJob?.isActive == true)
|
|
437
466
|
} catch (e: Exception) {
|
|
438
467
|
Log.e(TAG, "Failed to check scanning status", e)
|
|
439
468
|
promise.reject("IS_SCANNING_ERROR", "Failed to check scanning status: ${e.message}", e)
|
|
@@ -462,6 +491,16 @@ class Flic2Module(reactContext: ReactApplicationContext) :
|
|
|
462
491
|
buttonListeners[button.uuid] = listener
|
|
463
492
|
}
|
|
464
493
|
|
|
494
|
+
private fun updateForegroundServiceState(buttonCount: Int) {
|
|
495
|
+
if (buttonCount > 0) {
|
|
496
|
+
// Start foreground service when buttons exist
|
|
497
|
+
flic2Service?.startForegroundService()
|
|
498
|
+
} else {
|
|
499
|
+
// Stop foreground service when no buttons
|
|
500
|
+
flic2Service?.stopForegroundService()
|
|
501
|
+
}
|
|
502
|
+
}
|
|
503
|
+
|
|
465
504
|
private fun mapScanResultToCode(result: Int): Int {
|
|
466
505
|
// Map Android library's 9 result codes (0-8) to TypeScript enum codes (0-21) matching iOS
|
|
467
506
|
// Android library only provides these constants, so we map them to the closest equivalent
|
|
@@ -479,3 +518,4 @@ class Flic2Module(reactContext: ReactApplicationContext) :
|
|
|
479
518
|
}
|
|
480
519
|
}
|
|
481
520
|
}
|
|
521
|
+
|