react-native-flic2 2.0.0-beta.2 → 2.0.0-beta.20
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/Flic2.podspec +14 -4
- package/Flic2Device.podspec +21 -0
- package/Flic2Simulator.podspec +20 -0
- package/README.md +135 -22
- package/android/build.gradle +1 -1
- package/android/src/main/java/{com → nl/xguard}/flic2/ActivityUtil.kt +1 -1
- 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 +70 -53
- package/android/src/main/java/{com → nl/xguard}/flic2/Flic2Package.kt +2 -1
- package/android/src/main/java/{com → nl/xguard}/flic2/Flic2Service.kt +11 -22
- package/ios/Flic2.mm +45 -29
- package/ios/Flic2SimulatorStub.h +5 -0
- package/ios/Flic2SimulatorStub.mm +134 -0
- package/lib/module/NativeFlic2.js +45 -1
- package/lib/module/NativeFlic2.js.map +1 -1
- package/lib/module/index.js +10 -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 +76 -29
- package/src/index.ts +25 -40
- 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/Flic2.podspec
CHANGED
|
@@ -13,10 +13,20 @@ Pod::Spec.new do |s|
|
|
|
13
13
|
s.platforms = { :ios => min_ios_version_supported }
|
|
14
14
|
s.source = { :git => "https://github.com/X-Guard/react-native-flic2.git", :tag => "#{s.version}" }
|
|
15
15
|
|
|
16
|
-
s
|
|
17
|
-
s.private_header_files = "ios/**/*.h"
|
|
16
|
+
install_modules_dependencies(s)
|
|
18
17
|
|
|
19
|
-
|
|
18
|
+
# Keep the root spec codegen-only so consumers can opt into
|
|
19
|
+
# Device/Simulator implementations explicitly.
|
|
20
|
+
s.default_subspecs = []
|
|
20
21
|
|
|
21
|
-
|
|
22
|
+
s.subspec "Device" do |sp|
|
|
23
|
+
sp.source_files = "ios/Flic2.{h,mm}"
|
|
24
|
+
sp.private_header_files = "ios/Flic2.h"
|
|
25
|
+
sp.ios.vendored_frameworks = "ios/flic2lib.framework"
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
s.subspec "Simulator" do |sp|
|
|
29
|
+
sp.source_files = "ios/Flic2SimulatorStub.{h,mm}"
|
|
30
|
+
sp.private_header_files = "ios/Flic2SimulatorStub.h"
|
|
31
|
+
end
|
|
22
32
|
end
|
|
@@ -0,0 +1,21 @@
|
|
|
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 = "Flic2Device"
|
|
7
|
+
s.version = package["version"]
|
|
8
|
+
s.summary = package["description"]
|
|
9
|
+
s.homepage = package["homepage"]
|
|
10
|
+
s.license = package["license"]
|
|
11
|
+
s.authors = package["author"]
|
|
12
|
+
|
|
13
|
+
s.platforms = { :ios => min_ios_version_supported }
|
|
14
|
+
s.source = { :git => "https://github.com/X-Guard/react-native-flic2.git", :tag => "#{s.version}" }
|
|
15
|
+
|
|
16
|
+
install_modules_dependencies(s)
|
|
17
|
+
|
|
18
|
+
s.source_files = "ios/Flic2.{h,mm}"
|
|
19
|
+
s.private_header_files = "ios/Flic2.h"
|
|
20
|
+
s.ios.vendored_frameworks = "ios/flic2lib.framework"
|
|
21
|
+
end
|
|
@@ -0,0 +1,20 @@
|
|
|
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 = "Flic2Simulator"
|
|
7
|
+
s.version = package["version"]
|
|
8
|
+
s.summary = package["description"]
|
|
9
|
+
s.homepage = package["homepage"]
|
|
10
|
+
s.license = package["license"]
|
|
11
|
+
s.authors = package["author"]
|
|
12
|
+
|
|
13
|
+
s.platforms = { :ios => min_ios_version_supported }
|
|
14
|
+
s.source = { :git => "https://github.com/X-Guard/react-native-flic2.git", :tag => "#{s.version}" }
|
|
15
|
+
|
|
16
|
+
install_modules_dependencies(s)
|
|
17
|
+
|
|
18
|
+
s.source_files = "ios/Flic2SimulatorStub.{h,mm}"
|
|
19
|
+
s.private_header_files = "ios/Flic2SimulatorStub.h"
|
|
20
|
+
end
|
package/README.md
CHANGED
|
@@ -37,6 +37,10 @@ npm install react-native-flic2
|
|
|
37
37
|
cd ios && pod install && cd ..
|
|
38
38
|
```
|
|
39
39
|
|
|
40
|
+
**Need simulator builds?**
|
|
41
|
+
See `Troubleshooting` -> `Running on iOS Simulator` for a full configuration
|
|
42
|
+
example and launch commands.
|
|
43
|
+
|
|
40
44
|
2. **Add Bluetooth permissions to `Info.plist`:**
|
|
41
45
|
Add the following keys to your `ios/YourApp/Info.plist`:
|
|
42
46
|
```xml
|
|
@@ -66,6 +70,65 @@ The library automatically includes the necessary permissions in `AndroidManifest
|
|
|
66
70
|
|
|
67
71
|
You'll need to request these permissions before scanning for buttons. Use a library like `react-native-permissions` or implement permission requests manually.
|
|
68
72
|
|
|
73
|
+
#### Customizing the Foreground Service Notification (Android)
|
|
74
|
+
|
|
75
|
+
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`:
|
|
76
|
+
|
|
77
|
+
```xml
|
|
78
|
+
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
|
|
79
|
+
<application>
|
|
80
|
+
<!-- Your existing application configuration -->
|
|
81
|
+
|
|
82
|
+
<!-- Customize Flic2 foreground service notification -->
|
|
83
|
+
<meta-data
|
|
84
|
+
android:name="nl.xguard.flic2.notification_title"
|
|
85
|
+
android:value="My Flic2 Service" />
|
|
86
|
+
<meta-data
|
|
87
|
+
android:name="nl.xguard.flic2.notification_text"
|
|
88
|
+
android:value="Flic2 buttons are active" />
|
|
89
|
+
<meta-data
|
|
90
|
+
android:name="nl.xguard.flic2.notification_icon"
|
|
91
|
+
android:resource="@drawable/ic_notification" />
|
|
92
|
+
<meta-data
|
|
93
|
+
android:name="nl.xguard.flic2.notification_channel_name"
|
|
94
|
+
android:value="Flic2 Notifications" />
|
|
95
|
+
<meta-data
|
|
96
|
+
android:name="nl.xguard.flic2.notification_channel_description"
|
|
97
|
+
android:value="Notifications for Flic2 button connections" />
|
|
98
|
+
<meta-data
|
|
99
|
+
android:name="nl.xguard.flic2.notification_id"
|
|
100
|
+
android:value="123321" />
|
|
101
|
+
<meta-data
|
|
102
|
+
android:name="nl.xguard.flic2.notification_channel_id"
|
|
103
|
+
android:value="my_custom_channel_id" />
|
|
104
|
+
</application>
|
|
105
|
+
</manifest>
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
**Available Configuration Options:**
|
|
109
|
+
|
|
110
|
+
- `nl.xguard.flic2.notification_title` - Notification title (default: "Flic 2")
|
|
111
|
+
- `nl.xguard.flic2.notification_text` - Notification text (default: "Flic 2 service is running")
|
|
112
|
+
- `nl.xguard.flic2.notification_icon` - Notification icon resource ID (default: system info icon)
|
|
113
|
+
- Use `@drawable/your_icon_name` or `@mipmap/your_icon_name` format
|
|
114
|
+
- `nl.xguard.flic2.notification_channel_name` - Notification channel name (default: "Flic2Channel")
|
|
115
|
+
- `nl.xguard.flic2.notification_channel_description` - Notification channel description (default: "Flic2Channel")
|
|
116
|
+
- `nl.xguard.flic2.notification_id` - Notification ID integer (default: 123321)
|
|
117
|
+
- `nl.xguard.flic2.notification_channel_id` - Notification channel ID string (default: "Notification_Channel_Flic2Service")
|
|
118
|
+
|
|
119
|
+
**Example with Custom Icon:**
|
|
120
|
+
|
|
121
|
+
1. Add your notification icon to `android/app/src/main/res/drawable/` (e.g., `ic_flic2_notification.png`)
|
|
122
|
+
|
|
123
|
+
2. Add metadata to `AndroidManifest.xml`:
|
|
124
|
+
```xml
|
|
125
|
+
<meta-data
|
|
126
|
+
android:name="nl.xguard.flic2.notification_icon"
|
|
127
|
+
android:resource="@drawable/ic_flic2_notification" />
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
**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.
|
|
131
|
+
|
|
69
132
|
## Basic Usage
|
|
70
133
|
|
|
71
134
|
### 1. Initialize the Library (Global Setup)
|
|
@@ -390,7 +453,7 @@ export default Flic2Example;
|
|
|
390
453
|
|
|
391
454
|
### Initialization
|
|
392
455
|
|
|
393
|
-
#### `initialize(): Promise<
|
|
456
|
+
#### `initialize(): Promise<void>`
|
|
394
457
|
|
|
395
458
|
Initialize the Flic2 manager. This must be called before using any other methods.
|
|
396
459
|
|
|
@@ -400,7 +463,7 @@ await Flic2.initialize();
|
|
|
400
463
|
|
|
401
464
|
### Scanning
|
|
402
465
|
|
|
403
|
-
#### `startScan(): Promise<
|
|
466
|
+
#### `startScan(): Promise<void>`
|
|
404
467
|
|
|
405
468
|
Start scanning for new Flic2 buttons. The scan will emit `scanStatusChange` events.
|
|
406
469
|
|
|
@@ -408,7 +471,7 @@ Start scanning for new Flic2 buttons. The scan will emit `scanStatusChange` even
|
|
|
408
471
|
await Flic2.startScan();
|
|
409
472
|
```
|
|
410
473
|
|
|
411
|
-
#### `stopScan(): Promise<
|
|
474
|
+
#### `stopScan(): Promise<void>`
|
|
412
475
|
|
|
413
476
|
Stop an ongoing scan.
|
|
414
477
|
|
|
@@ -442,7 +505,7 @@ Get a specific button by UUID.
|
|
|
442
505
|
const button = await Flic2.getButton('button-uuid');
|
|
443
506
|
```
|
|
444
507
|
|
|
445
|
-
#### `connectAllKnownButtons(): Promise<
|
|
508
|
+
#### `connectAllKnownButtons(): Promise<void>`
|
|
446
509
|
|
|
447
510
|
Connect to all previously known buttons.
|
|
448
511
|
|
|
@@ -450,7 +513,7 @@ Connect to all previously known buttons.
|
|
|
450
513
|
await Flic2.connectAllKnownButtons();
|
|
451
514
|
```
|
|
452
515
|
|
|
453
|
-
#### `disconnectAllKnownButtons(): Promise<
|
|
516
|
+
#### `disconnectAllKnownButtons(): Promise<void>`
|
|
454
517
|
|
|
455
518
|
Disconnect all connected buttons.
|
|
456
519
|
|
|
@@ -458,7 +521,7 @@ Disconnect all connected buttons.
|
|
|
458
521
|
await Flic2.disconnectAllKnownButtons();
|
|
459
522
|
```
|
|
460
523
|
|
|
461
|
-
#### `forgetButton(uuid: string): Promise<
|
|
524
|
+
#### `forgetButton(uuid: string): Promise<void>`
|
|
462
525
|
|
|
463
526
|
Forget (unpair) a specific button.
|
|
464
527
|
|
|
@@ -466,7 +529,7 @@ Forget (unpair) a specific button.
|
|
|
466
529
|
await Flic2.forgetButton('button-uuid');
|
|
467
530
|
```
|
|
468
531
|
|
|
469
|
-
#### `forgetAllButtons(): Promise<
|
|
532
|
+
#### `forgetAllButtons(): Promise<void>`
|
|
470
533
|
|
|
471
534
|
Forget all buttons.
|
|
472
535
|
|
|
@@ -476,50 +539,54 @@ await Flic2.forgetAllButtons();
|
|
|
476
539
|
|
|
477
540
|
### Button Configuration
|
|
478
541
|
|
|
479
|
-
#### `buttonConnect(uuid: string): Promise<
|
|
542
|
+
#### `buttonConnect(uuid: string): Promise<FlicButton>`
|
|
480
543
|
|
|
481
|
-
Connect to a specific button.
|
|
544
|
+
Connect to a specific button. Returns the button object.
|
|
482
545
|
|
|
483
546
|
```tsx
|
|
484
|
-
await Flic2.buttonConnect('button-uuid');
|
|
547
|
+
const button = await Flic2.buttonConnect('button-uuid');
|
|
485
548
|
```
|
|
486
549
|
|
|
487
|
-
#### `buttonDisconnect(uuid: string): Promise<
|
|
550
|
+
#### `buttonDisconnect(uuid: string): Promise<FlicButton>`
|
|
488
551
|
|
|
489
|
-
Disconnect a specific button.
|
|
552
|
+
Disconnect a specific button. Returns the button object.
|
|
490
553
|
|
|
491
554
|
```tsx
|
|
492
|
-
await Flic2.buttonDisconnect('button-uuid');
|
|
555
|
+
const button = await Flic2.buttonDisconnect('button-uuid');
|
|
493
556
|
```
|
|
494
557
|
|
|
495
|
-
#### `buttonSetNickname(uuid: string, nickname: string): Promise<
|
|
558
|
+
#### `buttonSetNickname(uuid: string, nickname: string): Promise<FlicButton>`
|
|
496
559
|
|
|
497
|
-
Set a custom nickname for a button.
|
|
560
|
+
Set a custom nickname for a button. Returns the updated button object.
|
|
498
561
|
|
|
499
562
|
```tsx
|
|
500
|
-
await Flic2.buttonSetNickname('button-uuid', 'My Button');
|
|
563
|
+
const button = await Flic2.buttonSetNickname('button-uuid', 'My Button');
|
|
501
564
|
```
|
|
502
565
|
|
|
503
|
-
#### `buttonSetTriggerMode(uuid: string, mode: TriggerModeType): Promise<
|
|
566
|
+
#### `buttonSetTriggerMode(uuid: string, mode: TriggerModeType): Promise<FlicButton>`
|
|
504
567
|
|
|
505
|
-
Set the trigger mode for a button. Modes:
|
|
568
|
+
Set the trigger mode for a button. Returns the updated button object. Modes:
|
|
506
569
|
- `0`: Click and Hold
|
|
507
570
|
- `1`: Click and Double Click
|
|
508
571
|
- `2`: Click and Double Click and Hold
|
|
509
572
|
- `3`: Click only
|
|
510
573
|
|
|
574
|
+
**Note:** This method is only supported on iOS. On Android, it will reject with an error.
|
|
575
|
+
|
|
511
576
|
```tsx
|
|
512
|
-
await Flic2.buttonSetTriggerMode('button-uuid', 3); // Click only
|
|
577
|
+
const button = await Flic2.buttonSetTriggerMode('button-uuid', 3); // Click only
|
|
513
578
|
```
|
|
514
579
|
|
|
515
|
-
#### `buttonSetLatencyMode(uuid: string, mode: LatencyModeType): Promise<
|
|
580
|
+
#### `buttonSetLatencyMode(uuid: string, mode: LatencyModeType): Promise<FlicButton>`
|
|
516
581
|
|
|
517
|
-
Set the latency mode for a button. Modes:
|
|
582
|
+
Set the latency mode for a button. Returns the updated button object. Modes:
|
|
518
583
|
- `0`: Normal latency
|
|
519
584
|
- `1`: Low latency
|
|
520
585
|
|
|
586
|
+
**Note:** This method is only supported on iOS. On Android, it will reject with an error.
|
|
587
|
+
|
|
521
588
|
```tsx
|
|
522
|
-
await Flic2.buttonSetLatencyMode('button-uuid', 1); // Low latency
|
|
589
|
+
const button = await Flic2.buttonSetLatencyMode('button-uuid', 1); // Low latency
|
|
523
590
|
```
|
|
524
591
|
|
|
525
592
|
#### `getBatteryHealth(uuid: string): Promise<boolean>`
|
|
@@ -728,6 +795,52 @@ const scanForButtons = async () => {
|
|
|
728
795
|
|
|
729
796
|
## Troubleshooting
|
|
730
797
|
|
|
798
|
+
### Running on iOS Simulator
|
|
799
|
+
|
|
800
|
+
`flic2lib` is device-only. For simulator builds, use the no-op simulator pod and a
|
|
801
|
+
dedicated simulator build configuration.
|
|
802
|
+
|
|
803
|
+
1. Add a simulator configuration in Xcode (for example `DebugSimulator`) and a scheme that uses it.
|
|
804
|
+
2. Map pods by configuration in your app `Podfile`:
|
|
805
|
+
|
|
806
|
+
```ruby
|
|
807
|
+
# Example
|
|
808
|
+
project 'YourApp.xcodeproj', {
|
|
809
|
+
'Debug' => :debug,
|
|
810
|
+
'DebugSimulator' => :debug,
|
|
811
|
+
'Release' => :release,
|
|
812
|
+
}
|
|
813
|
+
|
|
814
|
+
target 'YourApp' do
|
|
815
|
+
# ... your existing use_react_native! setup
|
|
816
|
+
|
|
817
|
+
pod 'Flic2Device',
|
|
818
|
+
:path => '../node_modules/react-native-flic2',
|
|
819
|
+
:configurations => ['Debug', 'Release']
|
|
820
|
+
|
|
821
|
+
pod 'Flic2Simulator',
|
|
822
|
+
:path => '../node_modules/react-native-flic2',
|
|
823
|
+
:configurations => ['DebugSimulator']
|
|
824
|
+
end
|
|
825
|
+
```
|
|
826
|
+
|
|
827
|
+
3. Install pods normally (no env flags):
|
|
828
|
+
|
|
829
|
+
```sh
|
|
830
|
+
cd ios && bundle exec pod install && cd ..
|
|
831
|
+
```
|
|
832
|
+
|
|
833
|
+
4. Launch simulator build (example):
|
|
834
|
+
|
|
835
|
+
```sh
|
|
836
|
+
npx react-native run-ios --scheme YourAppSimulator --mode DebugSimulator --simulator "iPhone 17"
|
|
837
|
+
```
|
|
838
|
+
|
|
839
|
+
Expected behavior on simulator:
|
|
840
|
+
- App builds and launches.
|
|
841
|
+
- Flic calls are no-op fallback behavior.
|
|
842
|
+
- Real button communication works only on physical devices.
|
|
843
|
+
|
|
731
844
|
### Buttons not connecting
|
|
732
845
|
|
|
733
846
|
- Ensure Bluetooth is enabled on the device
|
package/android/build.gradle
CHANGED
|
@@ -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.getReadyTimestamp() - 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.getReadyTimestamp() - 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
|
|
@@ -63,10 +62,7 @@ class Flic2Module(reactContext: ReactApplicationContext) :
|
|
|
63
62
|
|
|
64
63
|
// Resolve the initialize promise if pending
|
|
65
64
|
initializePromise?.let { promise ->
|
|
66
|
-
promise.resolve(
|
|
67
|
-
putBoolean("success", true)
|
|
68
|
-
putString("message", "Manager initialized successfully")
|
|
69
|
-
})
|
|
65
|
+
promise.resolve(null)
|
|
70
66
|
initializePromise = null
|
|
71
67
|
}
|
|
72
68
|
}
|
|
@@ -90,6 +86,31 @@ class Flic2Module(reactContext: ReactApplicationContext) :
|
|
|
90
86
|
|
|
91
87
|
override fun invalidate() {
|
|
92
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
|
+
|
|
93
114
|
moduleScope.cancel()
|
|
94
115
|
if (serviceBound) {
|
|
95
116
|
reactApplicationContext.unbindService(serviceConnection)
|
|
@@ -107,8 +128,7 @@ class Flic2Module(reactContext: ReactApplicationContext) :
|
|
|
107
128
|
val intent = Intent(reactApplicationContext, Flic2Service::class.java)
|
|
108
129
|
|
|
109
130
|
// Check if service is already running
|
|
110
|
-
|
|
111
|
-
if (!isRunning) {
|
|
131
|
+
if (!ActivityUtil.isServiceRunning(reactApplicationContext, Flic2Service::class.java)) {
|
|
112
132
|
// Start service
|
|
113
133
|
ActivityUtil.startForegroundService(reactApplicationContext, intent)
|
|
114
134
|
}
|
|
@@ -182,8 +202,6 @@ class Flic2Module(reactContext: ReactApplicationContext) :
|
|
|
182
202
|
override fun onComplete(result: Int, subCode: Int, button: Flic2Button?) {
|
|
183
203
|
Log.d(TAG, "Scan complete: result=$result, button=${button?.uuid}")
|
|
184
204
|
|
|
185
|
-
val resultCode = mapScanResultToCode(result)
|
|
186
|
-
|
|
187
205
|
if (result == Flic2ScanCallback.RESULT_SUCCESS && button != null) {
|
|
188
206
|
// Auto-connect (trigger mode not available in Android v1.1.0+)
|
|
189
207
|
button.connect()
|
|
@@ -202,24 +220,20 @@ class Flic2Module(reactContext: ReactApplicationContext) :
|
|
|
202
220
|
putMap("button", Flic2Converter.buttonToMap(button))
|
|
203
221
|
})
|
|
204
222
|
} else {
|
|
205
|
-
|
|
206
|
-
Log.e(TAG, "Scan failed with error code: $errorCode")
|
|
223
|
+
Log.e(TAG, "Scan failed with error code: ${Flic2Converter.scanResultToString(result)}")
|
|
207
224
|
}
|
|
208
225
|
|
|
209
226
|
// Emit scan completion with result code
|
|
210
227
|
emitOnScanStatusChange(Arguments.createMap().apply {
|
|
211
228
|
putString("event", "completion")
|
|
212
229
|
putString("eventName", "completion")
|
|
213
|
-
putInt("result",
|
|
230
|
+
putInt("result", mapScanResultToCode(result))
|
|
214
231
|
})
|
|
215
232
|
}
|
|
216
233
|
})
|
|
217
234
|
|
|
218
235
|
// Return immediately - scan results will come through events
|
|
219
|
-
promise.resolve(
|
|
220
|
-
putBoolean("success", true)
|
|
221
|
-
putString("message", "Scan started")
|
|
222
|
-
})
|
|
236
|
+
promise.resolve(null)
|
|
223
237
|
}
|
|
224
238
|
|
|
225
239
|
override fun stopScan(promise: Promise) {
|
|
@@ -233,10 +247,7 @@ class Flic2Module(reactContext: ReactApplicationContext) :
|
|
|
233
247
|
scanJob?.cancel()
|
|
234
248
|
manager.stopScan()
|
|
235
249
|
|
|
236
|
-
promise.resolve(
|
|
237
|
-
putBoolean("success", true)
|
|
238
|
-
putString("message", "Scan stopped")
|
|
239
|
-
})
|
|
250
|
+
promise.resolve(null)
|
|
240
251
|
} catch (e: Exception) {
|
|
241
252
|
Log.e(TAG, "Failed to stop scan", e)
|
|
242
253
|
promise.reject("STOP_SCAN_ERROR", "Failed to stop scan: ${e.message}", e)
|
|
@@ -260,7 +271,18 @@ class Flic2Module(reactContext: ReactApplicationContext) :
|
|
|
260
271
|
// Disconnect before forgetting like iOS
|
|
261
272
|
button.disconnectOrAbortPendingConnection()
|
|
262
273
|
|
|
263
|
-
//
|
|
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
|
|
264
286
|
buttonListeners.remove(uuid)
|
|
265
287
|
|
|
266
288
|
// Forget button
|
|
@@ -269,10 +291,7 @@ class Flic2Module(reactContext: ReactApplicationContext) :
|
|
|
269
291
|
// Update foreground service state after removing button
|
|
270
292
|
updateForegroundServiceState(manager.buttons.size)
|
|
271
293
|
|
|
272
|
-
promise.resolve(
|
|
273
|
-
putBoolean("success", true)
|
|
274
|
-
putString("message", "Button forgotten")
|
|
275
|
-
})
|
|
294
|
+
promise.resolve(null)
|
|
276
295
|
} catch (e: Exception) {
|
|
277
296
|
Log.e(TAG, "Failed to forget button", e)
|
|
278
297
|
promise.reject("FORGET_ERROR", "Failed to forget button: ${e.message}", e)
|
|
@@ -291,10 +310,7 @@ class Flic2Module(reactContext: ReactApplicationContext) :
|
|
|
291
310
|
|
|
292
311
|
button.connect()
|
|
293
312
|
|
|
294
|
-
promise.resolve(
|
|
295
|
-
putBoolean("success", true)
|
|
296
|
-
putString("message", "Connection initiated")
|
|
297
|
-
})
|
|
313
|
+
promise.resolve(Flic2Converter.buttonToMap(button))
|
|
298
314
|
} catch (e: Exception) {
|
|
299
315
|
Log.e(TAG, "Failed to connect button", e)
|
|
300
316
|
promise.reject("CONNECT_ERROR", "Failed to connect: ${e.message}", e)
|
|
@@ -311,10 +327,7 @@ class Flic2Module(reactContext: ReactApplicationContext) :
|
|
|
311
327
|
|
|
312
328
|
button.disconnectOrAbortPendingConnection()
|
|
313
329
|
|
|
314
|
-
promise.resolve(
|
|
315
|
-
putBoolean("success", true)
|
|
316
|
-
putString("message", "Disconnection initiated")
|
|
317
|
-
})
|
|
330
|
+
promise.resolve(Flic2Converter.buttonToMap(button))
|
|
318
331
|
} catch (e: Exception) {
|
|
319
332
|
Log.e(TAG, "Failed to disconnect button", e)
|
|
320
333
|
promise.reject("DISCONNECT_ERROR", "Failed to disconnect: ${e.message}", e)
|
|
@@ -346,10 +359,7 @@ class Flic2Module(reactContext: ReactApplicationContext) :
|
|
|
346
359
|
// v1.1.0 uses setName() method instead of property
|
|
347
360
|
button.setName(nickname)
|
|
348
361
|
|
|
349
|
-
promise.resolve(
|
|
350
|
-
putBoolean("success", true)
|
|
351
|
-
putString("message", "Nickname set")
|
|
352
|
-
})
|
|
362
|
+
promise.resolve(Flic2Converter.buttonToMap(button))
|
|
353
363
|
} catch (e: Exception) {
|
|
354
364
|
Log.e(TAG, "Failed to set nickname", e)
|
|
355
365
|
promise.reject("SET_NICKNAME_ERROR", "Failed to set nickname: ${e.message}", e)
|
|
@@ -372,10 +382,7 @@ class Flic2Module(reactContext: ReactApplicationContext) :
|
|
|
372
382
|
button.connect()
|
|
373
383
|
}
|
|
374
384
|
|
|
375
|
-
promise.resolve(
|
|
376
|
-
putBoolean("success", true)
|
|
377
|
-
putString("message", "All buttons connection initiated")
|
|
378
|
-
})
|
|
385
|
+
promise.resolve(null)
|
|
379
386
|
} catch (e: Exception) {
|
|
380
387
|
Log.e(TAG, "Failed to connect all buttons", e)
|
|
381
388
|
promise.reject("CONNECT_ALL_ERROR", "Failed to connect all buttons: ${e.message}", e)
|
|
@@ -397,10 +404,7 @@ class Flic2Module(reactContext: ReactApplicationContext) :
|
|
|
397
404
|
button.disconnectOrAbortPendingConnection()
|
|
398
405
|
}
|
|
399
406
|
|
|
400
|
-
promise.resolve(
|
|
401
|
-
putBoolean("success", true)
|
|
402
|
-
putString("message", "All buttons disconnection initiated")
|
|
403
|
-
})
|
|
407
|
+
promise.resolve(null)
|
|
404
408
|
} catch (e: Exception) {
|
|
405
409
|
Log.e(TAG, "Failed to disconnect all buttons", e)
|
|
406
410
|
promise.reject("DISCONNECT_ALL_ERROR", "Failed to disconnect all buttons: ${e.message}", e)
|
|
@@ -419,18 +423,31 @@ class Flic2Module(reactContext: ReactApplicationContext) :
|
|
|
419
423
|
val buttons = manager.buttons.toList()
|
|
420
424
|
|
|
421
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
|
|
422
438
|
buttonListeners.remove(button.uuid)
|
|
439
|
+
|
|
440
|
+
// Disconnect before forgetting
|
|
423
441
|
button.disconnectOrAbortPendingConnection()
|
|
442
|
+
|
|
443
|
+
// Forget button
|
|
424
444
|
manager.forgetButton(button)
|
|
425
445
|
}
|
|
426
446
|
|
|
427
447
|
// Update foreground service state after removing all buttons
|
|
428
448
|
updateForegroundServiceState(manager.buttons.size)
|
|
429
449
|
|
|
430
|
-
promise.resolve(
|
|
431
|
-
putBoolean("success", true)
|
|
432
|
-
putString("message", "All buttons forgotten")
|
|
433
|
-
})
|
|
450
|
+
promise.resolve(null)
|
|
434
451
|
} catch (e: Exception) {
|
|
435
452
|
Log.e(TAG, "Failed to forget all buttons", e)
|
|
436
453
|
promise.reject("FORGET_ALL_ERROR", "Failed to forget all buttons: ${e.message}", e)
|
|
@@ -445,8 +462,7 @@ class Flic2Module(reactContext: ReactApplicationContext) :
|
|
|
445
462
|
return
|
|
446
463
|
}
|
|
447
464
|
|
|
448
|
-
|
|
449
|
-
promise.resolve(scanning)
|
|
465
|
+
promise.resolve(scanJob != null && scanJob?.isActive == true)
|
|
450
466
|
} catch (e: Exception) {
|
|
451
467
|
Log.e(TAG, "Failed to check scanning status", e)
|
|
452
468
|
promise.reject("IS_SCANNING_ERROR", "Failed to check scanning status: ${e.message}", e)
|
|
@@ -502,3 +518,4 @@ class Flic2Module(reactContext: ReactApplicationContext) :
|
|
|
502
518
|
}
|
|
503
519
|
}
|
|
504
520
|
}
|
|
521
|
+
|