react-native-geofence-manager 0.1.0-beta.11 → 0.1.0-beta.13

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.
Files changed (52) hide show
  1. package/README.md +173 -37
  2. package/android/src/main/AndroidManifest.xml +17 -16
  3. package/android/src/main/java/com/geofencemanager/GeofenceBroadcastReceiver.kt +133 -0
  4. package/android/src/main/java/com/geofencemanager/GeofenceHeadlessTaskService.kt +98 -141
  5. package/android/src/main/java/com/geofencemanager/GeofenceManagerModule.kt +187 -154
  6. package/ios/GeofenceManager.mm +21 -21
  7. package/lib/module/NativeGeofenceManager.js +1 -7
  8. package/lib/module/NativeGeofenceManager.js.map +1 -1
  9. package/lib/module/geofenceDebugLog.js +22 -0
  10. package/lib/module/geofenceDebugLog.js.map +1 -0
  11. package/lib/module/index.js +79 -35
  12. package/lib/module/index.js.map +1 -1
  13. package/lib/module/models/GeofenceEvent.js +4 -0
  14. package/lib/module/models/GeofenceEvent.js.map +1 -0
  15. package/lib/module/models/GeofenceEventType.js +2 -0
  16. package/lib/module/models/GeofenceEventType.js.map +1 -0
  17. package/lib/module/models/GeofenceModel.js +2 -0
  18. package/lib/module/models/GeofenceModel.js.map +1 -0
  19. package/lib/module/models/index.js +6 -0
  20. package/lib/module/models/index.js.map +1 -0
  21. package/lib/module/package.json +1 -0
  22. package/lib/typescript/package.json +1 -0
  23. package/lib/typescript/src/NativeGeofenceManager.d.ts +3 -16
  24. package/lib/typescript/src/NativeGeofenceManager.d.ts.map +1 -1
  25. package/lib/typescript/src/geofenceDebugLog.d.ts +3 -0
  26. package/lib/typescript/src/geofenceDebugLog.d.ts.map +1 -0
  27. package/lib/typescript/src/index.d.ts +27 -26
  28. package/lib/typescript/src/index.d.ts.map +1 -1
  29. package/lib/typescript/src/models/GeofenceEvent.d.ts +11 -0
  30. package/lib/typescript/src/models/GeofenceEvent.d.ts.map +1 -0
  31. package/lib/typescript/src/models/GeofenceEventType.d.ts +2 -0
  32. package/lib/typescript/src/models/GeofenceEventType.d.ts.map +1 -0
  33. package/lib/typescript/src/models/GeofenceModel.d.ts +8 -0
  34. package/lib/typescript/src/models/GeofenceModel.d.ts.map +1 -0
  35. package/lib/typescript/src/models/index.d.ts +4 -0
  36. package/lib/typescript/src/models/index.d.ts.map +1 -0
  37. package/package.json +16 -15
  38. package/src/NativeGeofenceManager.ts +4 -29
  39. package/src/geofenceDebugLog.ts +28 -0
  40. package/src/index.tsx +99 -71
  41. package/src/models/GeofenceEvent.ts +11 -0
  42. package/src/models/GeofenceEventType.ts +1 -0
  43. package/src/models/GeofenceModel.ts +7 -0
  44. package/src/models/index.ts +3 -0
  45. package/android/src/main/java/com/geofencemanager/GeofenceReceiver.kt +0 -192
  46. package/android/src/main/java/com/geofencemanager/RegionManager.kt +0 -387
  47. package/lib/commonjs/NativeGeofenceManager.js +0 -15
  48. package/lib/commonjs/NativeGeofenceManager.js.map +0 -1
  49. package/lib/commonjs/index.js +0 -47
  50. package/lib/commonjs/index.js.map +0 -1
  51. package/lib/commonjs/package.json +0 -1
  52. package/react-native.config.js +0 -11
package/README.md CHANGED
@@ -1,37 +1,173 @@
1
- # react-native-geofence-manager
2
-
3
- Robust manager for your geofencing areas.
4
-
5
- ## Installation
6
-
7
-
8
- ```sh
9
- npm install react-native-geofence-manager
10
- ```
11
-
12
-
13
- ## Usage
14
-
15
-
16
- ```js
17
- import { multiply } from 'react-native-geofence-manager';
18
-
19
- // ...
20
-
21
- const result = multiply(3, 7);
22
- ```
23
-
24
-
25
- ## Contributing
26
-
27
- - [Development workflow](CONTRIBUTING.md#development-workflow)
28
- - [Sending a pull request](CONTRIBUTING.md#sending-a-pull-request)
29
- - [Code of conduct](CODE_OF_CONDUCT.md)
30
-
31
- ## License
32
-
33
- MIT
34
-
35
- ---
36
-
37
- Made with [create-react-native-library](https://github.com/callstack/react-native-builder-bob)
1
+ # react-native-geofence-manager
2
+
3
+ > **⚠️ Status: POC (proof of concept)**
4
+ > Esta biblioteca é um **protótipo experimental**. A API, o comportamento e a estabilidade podem mudar sem aviso prévio. **Não use em produção** sem avaliar riscos e fazer testes próprios.
5
+
6
+ > **📱 Plataforma: apenas Android**
7
+ > A implementação nativa existe **somente para Android** (Google Play Services / Geofencing API). No **iOS** não há suporte funcional — não espere geofences nesta lib em dispositivos Apple.
8
+
9
+ ---
10
+
11
+ ## O que faz
12
+
13
+ Permite registrar **geofences circulares** e receber eventos de **entrada**, **saída** e **permanência (dwell)** no JavaScript, inclusive quando o app está em **segundo plano** ou **fechado**, desde que a headless task esteja registrada no `index` do app.
14
+
15
+ ---
16
+
17
+ ## Instalação
18
+
19
+ ```sh
20
+ npm install react-native-geofence-manager
21
+ # ou
22
+ yarn add react-native-geofence-manager
23
+ ```
24
+
25
+ Siga o fluxo normal de autolinking do React Native. **Use apenas em projetos Android** para obter o comportamento descrito abaixo.
26
+
27
+ ---
28
+
29
+ ## Uso
30
+
31
+ ### 1. Registrar a headless task (obrigatório para eventos com app em background/fechado)
32
+
33
+ No arquivo de entrada do app (`index.js` / `index.tsx`), **antes** de `AppRegistry.registerComponent`:
34
+
35
+ ```tsx
36
+ import { AppRegistry } from 'react-native';
37
+ import {
38
+ registerGeofenceHeadlessTask,
39
+ type GeofenceEvent,
40
+ } from 'react-native-geofence-manager';
41
+ import App from './App';
42
+ import { name as appName } from './app.json';
43
+
44
+ registerGeofenceHeadlessTask((event: GeofenceEvent) => {
45
+ // Ex.: notificação local, log, envio para API
46
+ console.log('Geofence (headless):', event.geofenceId, event.event);
47
+ });
48
+
49
+ AppRegistry.registerComponent(appName, () => App);
50
+ ```
51
+
52
+ ### 2. Adicionar geofences
53
+
54
+ ```tsx
55
+ import { addGeofences } from 'react-native-geofence-manager';
56
+
57
+ const ok = await addGeofences([
58
+ {
59
+ id: 'loja-1',
60
+ name: 'Loja centro',
61
+ latitude: -23.5505,
62
+ longitude: -46.6333,
63
+ radius: 150, // metros (opcional; padrão nativo ~100 m se omitido)
64
+ },
65
+ ]);
66
+ // ok === true se o registro foi aceito pelo sistema
67
+ ```
68
+
69
+ ### 3. Listener com o app em primeiro plano
70
+
71
+ ```tsx
72
+ import { addGeofenceEventListener } from 'react-native-geofence-manager';
73
+
74
+ const unsubscribe = addGeofenceEventListener((event) => {
75
+ console.log(event.geofenceId, event.event); // 'enter' | 'exit' | 'dwell'
76
+ });
77
+
78
+ // ao desmontar:
79
+ unsubscribe();
80
+ ```
81
+
82
+ ### 4. Remover todas as geofences
83
+
84
+ ```tsx
85
+ import { removeAllGeofences } from 'react-native-geofence-manager';
86
+
87
+ await removeAllGeofences();
88
+ ```
89
+
90
+ ---
91
+
92
+ ## Modelo de geofence (`GeofenceModel`)
93
+
94
+ | Campo | Tipo | Obrigatório | Descrição |
95
+ |---------------|----------|-------------|------------------------------------|
96
+ | `id` | `string` | Sim | ID único da geofence |
97
+ | `latitude` | `number` | Sim | Latitude do centro |
98
+ | `longitude` | `number` | Sim | Longitude do centro |
99
+ | `name` | `string` | Não | Nome exibido nos eventos |
100
+ | `radius` | `number` | Não | Raio em metros |
101
+
102
+ ---
103
+
104
+ ## Evento (`GeofenceEvent`)
105
+
106
+ - `geofenceId` — ID da geofence
107
+ - `event` — `'enter' | 'exit' | 'dwell'`
108
+ - `timestamp`, `name`, `latitude`, `longitude`, `radius` — quando disponíveis
109
+
110
+ ---
111
+
112
+ ## Permissões (Android)
113
+
114
+ Para geofences confiáveis é necessário **localização** (fina ou aproximada) e, em geral, **localização em segundo plano** (Android 10+) se quiser eventos com o app fechado. Notificações (API 33+) dependem do seu fluxo. Consulte a documentação oficial do Android e o app de exemplo.
115
+
116
+ ---
117
+
118
+ ## Depuração (logs)
119
+
120
+ A lib emite logs em cada etapa para localizar falhas (SDK nativo + JS).
121
+
122
+ **Android (Logcat)** — filtre pela tag:
123
+
124
+ ```text
125
+ adb logcat -s GeofenceManager
126
+ ```
127
+
128
+ Você verá, entre outros: inicialização do módulo, `addGeofences` (sucesso/falha do Play Services), `BroadcastReceiver` (transição, foreground vs headless), `HeadlessTaskService` e erros de reflexão.
129
+
130
+ **JavaScript** — mensagens com prefixo `[GeofenceManager]` no console (Metro) e também no Logcat como `ReactNativeJS` ao rodar no dispositivo.
131
+
132
+ ---
133
+
134
+ ## App de exemplo
135
+
136
+ Na raiz do repositório:
137
+
138
+ ```sh
139
+ yarn example start
140
+ yarn example android
141
+ ```
142
+
143
+ O diretório `example/` demonstra permissões, registro de geofences, listener e notificações locais (Notifee).
144
+
145
+ ---
146
+
147
+ ## API exportada (resumo)
148
+
149
+ | Export | Descrição |
150
+ |--------------------------------|------------------------------------------------|
151
+ | `addGeofences` | Registra uma lista de geofences |
152
+ | `removeAllGeofences` | Remove todas as geofences registradas |
153
+ | `addGeofenceEventListener` | Inscreve callback para eventos (foreground) |
154
+ | `onGeofenceEvent` | Alias de `addGeofenceEventListener` |
155
+ | `registerGeofenceHeadlessTask` | Registra handler para background/app fechado |
156
+ | `GEOFENCE_HEADLESS_TASK_NAME` | Nome interno da headless task |
157
+ | Tipos `GeofenceModel`, `GeofenceEvent`, etc. | TypeScript |
158
+
159
+ ---
160
+
161
+ ## Contributing
162
+
163
+ - [Development workflow](CONTRIBUTING.md#development-workflow)
164
+ - [Sending a pull request](CONTRIBUTING.md#sending-a-pull-request)
165
+ - [Code of conduct](CODE_OF_CONDUCT.md)
166
+
167
+ ## License
168
+
169
+ MIT
170
+
171
+ ---
172
+
173
+ Made with [create-react-native-library](https://github.com/callstack/react-native-builder-bob)
@@ -1,16 +1,17 @@
1
- <manifest xmlns:android="http://schemas.android.com/apk/res/android">
2
- <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
3
- <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
4
- <uses-permission android:name="android.permission.ACCESS_BACKGROUND_LOCATION" />
5
- <uses-permission android:name="android.permission.WAKE_LOCK" />
6
-
7
- <application>
8
- <receiver
9
- android:name=".GeofenceReceiver"
10
- android:exported="false" />
11
-
12
- <service
13
- android:name=".GeofenceHeadlessTaskService"
14
- android:exported="false" />
15
- </application>
16
- </manifest>
1
+ <manifest xmlns:android="http://schemas.android.com/apk/res/android">
2
+ <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
3
+ <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
4
+ <uses-permission android:name="android.permission.ACCESS_BACKGROUND_LOCATION" />
5
+ <uses-permission android:name="android.permission.WAKE_LOCK" />
6
+ <uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
7
+ <uses-permission android:name="android.permission.FOREGROUND_SERVICE_LOCATION" />
8
+ <application>
9
+ <receiver
10
+ android:name=".GeofenceBroadcastReceiver"
11
+ android:exported="false" />
12
+ <service
13
+ android:name=".GeofenceHeadlessTaskService"
14
+ android:exported="false"
15
+ android:foregroundServiceType="location" />
16
+ </application>
17
+ </manifest>
@@ -0,0 +1,133 @@
1
+ package com.geofencemanager
2
+
3
+ import android.content.BroadcastReceiver
4
+ import android.content.Context
5
+ import android.content.Intent
6
+ import android.os.Build
7
+ import android.util.Log
8
+ import android.os.Handler
9
+ import android.os.Looper
10
+ import com.facebook.react.HeadlessJsTaskService
11
+ import com.facebook.react.bridge.Arguments
12
+ import com.facebook.react.bridge.ReactApplicationContext
13
+ import com.facebook.react.bridge.WritableMap
14
+ import com.facebook.react.modules.core.DeviceEventManagerModule
15
+ import com.google.android.gms.location.Geofence
16
+ import com.google.android.gms.location.GeofencingEvent
17
+
18
+ class GeofenceBroadcastReceiver : BroadcastReceiver() {
19
+
20
+ companion object {
21
+ private const val TAG = "GeofenceManager"
22
+ }
23
+
24
+ override fun onReceive(context: Context?, intent: Intent?) {
25
+ Log.i(TAG, "BroadcastReceiver.onReceive: invoked action=${intent?.action}")
26
+ if (context == null || intent == null) {
27
+ Log.w(TAG, "BroadcastReceiver.onReceive: abort — null context or intent")
28
+ return
29
+ }
30
+ if (intent.action != GeofenceManagerModule.GEOFENCE_ACTION) {
31
+ Log.w(TAG, "BroadcastReceiver.onReceive: wrong action, expected ${GeofenceManagerModule.GEOFENCE_ACTION}")
32
+ return
33
+ }
34
+
35
+ val geofencingEvent = GeofencingEvent.fromIntent(intent)
36
+ if (geofencingEvent == null) {
37
+ Log.e(TAG, "BroadcastReceiver.onReceive: GeofencingEvent.fromIntent returned null")
38
+ return
39
+ }
40
+ if (geofencingEvent.hasError()) {
41
+ Log.e(TAG, "BroadcastReceiver.onReceive: Geofencing error code=${geofencingEvent.errorCode}")
42
+ return
43
+ }
44
+
45
+ if (GeofenceManagerModule.isWithinGracePeriod(context)) {
46
+ Log.i(TAG, "BroadcastReceiver.onReceive: skipped — within grace period after addGeofences")
47
+ return
48
+ }
49
+
50
+ val transition = geofencingEvent.geofenceTransition
51
+ val eventName = when (transition) {
52
+ Geofence.GEOFENCE_TRANSITION_ENTER -> "enter"
53
+ Geofence.GEOFENCE_TRANSITION_EXIT -> "exit"
54
+ Geofence.GEOFENCE_TRANSITION_DWELL -> "dwell"
55
+ else -> {
56
+ Log.w(TAG, "BroadcastReceiver.onReceive: unknown transition=$transition")
57
+ return
58
+ }
59
+ }
60
+ Log.i(TAG, "BroadcastReceiver.onReceive: transition=$eventName")
61
+
62
+ val triggeringGeofences = geofencingEvent.triggeringGeofences
63
+ if (triggeringGeofences == null || triggeringGeofences.isEmpty()) {
64
+ Log.w(TAG, "BroadcastReceiver.onReceive: no triggering geofences")
65
+ return
66
+ }
67
+
68
+ val reactContext = GeofenceManagerModule.reactContextRef
69
+ val timestamp = System.currentTimeMillis().toDouble()
70
+ val isForeground = reactContext != null && reactContext.hasActiveReactInstance()
71
+ Log.i(
72
+ TAG,
73
+ "BroadcastReceiver.onReceive: ids=${triggeringGeofences.map { it.requestId }} isForeground=$isForeground hasReactContextRef=${reactContext != null}"
74
+ )
75
+
76
+ if (isForeground) {
77
+ val ctx = reactContext!!
78
+ val geofenceIds = triggeringGeofences.map { it.requestId }
79
+ val eventNameForRunnable = eventName
80
+ Handler(Looper.getMainLooper()).postDelayed({
81
+ if (!ctx.hasActiveReactInstance()) {
82
+ Log.w(TAG, "BroadcastReceiver: foreground path — React instance gone after delay, skipping emit")
83
+ return@postDelayed
84
+ }
85
+ try {
86
+ val emitter = ctx.getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter::class.java)
87
+ for (geofenceId in geofenceIds) {
88
+ val details = GeofenceManagerModule.getGeofenceDetails(context, geofenceId)
89
+ val params = Arguments.createMap().apply {
90
+ putString("geofenceId", geofenceId)
91
+ putString("event", eventNameForRunnable)
92
+ putDouble("timestamp", timestamp)
93
+ putString("name", details?.name ?: "")
94
+ details?.let {
95
+ putDouble("latitude", it.latitude)
96
+ putDouble("longitude", it.longitude)
97
+ putDouble("radius", it.radius.toDouble())
98
+ }
99
+ }
100
+ Log.i(TAG, "BroadcastReceiver: emitting onGeofenceTransition id=$geofenceId event=$eventNameForRunnable")
101
+ emitter.emit("onGeofenceTransition", params)
102
+ }
103
+ } catch (e: Exception) {
104
+ Log.e(TAG, "BroadcastReceiver: emit to JS failed", e)
105
+ }
106
+ }, 50)
107
+ return
108
+ }
109
+
110
+ Log.i(TAG, "BroadcastReceiver: background/headless path — starting GeofenceHeadlessTaskService")
111
+ val appContext = context.applicationContext
112
+ HeadlessJsTaskService.acquireWakeLockNow(appContext)
113
+ for (geofence in triggeringGeofences) {
114
+ val serviceIntent = Intent(appContext, GeofenceHeadlessTaskService::class.java).apply {
115
+ putExtra(GeofenceHeadlessTaskService.EXTRA_GEOFENCE_ID, geofence.requestId)
116
+ putExtra(GeofenceHeadlessTaskService.EXTRA_EVENT, eventName)
117
+ putExtra(GeofenceHeadlessTaskService.EXTRA_TIMESTAMP, timestamp)
118
+ }
119
+ try {
120
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
121
+ appContext.startForegroundService(serviceIntent)
122
+ Log.i(TAG, "BroadcastReceiver: startForegroundService for id=${geofence.requestId}")
123
+ } else {
124
+ appContext.startService(serviceIntent)
125
+ Log.i(TAG, "BroadcastReceiver: startService for id=${geofence.requestId}")
126
+ }
127
+ } catch (e: Exception) {
128
+ Log.e(TAG, "BroadcastReceiver: failed to start headless service", e)
129
+ }
130
+ }
131
+ }
132
+
133
+ }
@@ -1,7 +1,10 @@
1
1
  package com.geofencemanager
2
2
 
3
+ import android.app.Notification
4
+ import android.app.NotificationChannel
5
+ import android.app.NotificationManager
3
6
  import android.content.Intent
4
- import android.os.Bundle
7
+ import android.os.Build
5
8
  import android.util.Log
6
9
  import com.facebook.react.HeadlessJsTaskService
7
10
  import com.facebook.react.bridge.Arguments
@@ -9,155 +12,109 @@ import com.facebook.react.jstasks.HeadlessJsTaskConfig
9
12
 
10
13
  class GeofenceHeadlessTaskService : HeadlessJsTaskService() {
11
14
 
12
- companion object {
13
- private const val TAG = "GeofenceHeadlessTask"
14
- }
15
-
16
- override fun onCreate() {
17
- super.onCreate()
18
- Log.d(TAG, "════════════════════════════════════════")
19
- Log.d(TAG, "🚀 SERVICE CREATED - onCreate()")
20
- Log.d(TAG, "════════════════════════════════════════")
21
- }
22
-
15
+ /**
16
+ * Quando o app está em background/frozen, reactContext existe mas hasActiveReactInstance()
17
+ * retorna false e a task não roda. Nesse caso chamamos createReactContextAndScheduleTask
18
+ * (via reflexão) em onStartCommand para que dwell/exit sejam executados.
19
+ * startForeground() é chamado para que o sistema inicie o serviço mesmo em background (dwell/exit).
20
+ */
23
21
  override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
24
- Log.d(TAG, "════════════════════════════════════════")
25
- Log.d(TAG, "▶️ ON_START_COMMAND()")
26
- Log.d(TAG, "════════════════════════════════════════")
27
- Log.d(TAG, " └─ Intent: $intent")
28
- Log.d(TAG, " └─ Flags: $flags")
29
- Log.d(TAG, " └─ StartId: $startId")
30
-
31
- return try {
32
- val result = super.onStartCommand(intent, flags, startId)
33
- Log.d(TAG, " └─ Super.onStartCommand result: $result")
34
- result
35
- } catch (e: Exception) {
36
- Log.e(TAG, "💥 ERRO em onStartCommand:")
37
- Log.e(TAG, " └─ ${e.javaClass.simpleName}: ${e.message}")
38
- Log.e(TAG, " └─ StackTrace: ${e.stackTraceToString()}")
39
- START_NOT_STICKY
22
+ Log.i(TAG, "HeadlessTaskService.onStartCommand extras=${intent?.extras}")
23
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
24
+ ensureNotificationChannel()
25
+ val notification = buildNotification(intent)
26
+ startForeground(NOTIFICATION_ID, notification)
27
+ Log.d(TAG, "HeadlessTaskService: startForeground done")
40
28
  }
29
+ val taskConfig = getTaskConfig(intent)
30
+ if (taskConfig == null) {
31
+ Log.e(TAG, "HeadlessTaskService: getTaskConfig returned null — check EXTRA_GEOFENCE_ID / EXTRA_EVENT")
32
+ return START_NOT_STICKY
33
+ }
34
+ val ctx = reactContext
35
+ val hasInstance = ctx?.hasActiveReactInstance() == true
36
+ Log.i(TAG, "HeadlessTaskService: hasActiveReactInstance=$hasInstance")
37
+ if (ctx != null && !ctx.hasActiveReactInstance()) {
38
+ HeadlessJsTaskService.acquireWakeLockNow(this)
39
+ try {
40
+ val method = HeadlessJsTaskService::class.java.getDeclaredMethod(
41
+ "createReactContextAndScheduleTask",
42
+ HeadlessJsTaskConfig::class.java
43
+ )
44
+ method.isAccessible = true
45
+ method.invoke(this, taskConfig)
46
+ Log.i(TAG, "HeadlessTaskService: createReactContextAndScheduleTask invoked OK")
47
+ } catch (e: Exception) {
48
+ Log.e(TAG, "HeadlessTaskService: reflection createReactContextAndScheduleTask failed", e)
49
+ return super.onStartCommand(intent, flags, startId)
50
+ }
51
+ return START_REDELIVER_INTENT
52
+ }
53
+ Log.i(TAG, "HeadlessTaskService: delegating to super.onStartCommand")
54
+ return super.onStartCommand(intent, flags, startId)
41
55
  }
42
56
 
43
- override fun onHeadlessJsTaskStart(taskId: Int) {
44
- Log.d(TAG, "════════════════════════════════════════")
45
- Log.d(TAG, "🎯 HEADLESS JS TASK STARTED!")
46
- Log.d(TAG, "════════════════════════════════════════")
47
- Log.d(TAG, " └─ TaskId: $taskId")
48
- super.onHeadlessJsTaskStart(taskId)
49
- }
57
+ override fun getTaskConfig(intent: Intent?): HeadlessJsTaskConfig? {
58
+ if (intent == null) {
59
+ Log.w(TAG, "getTaskConfig: null intent")
60
+ return null
61
+ }
62
+ val geofenceId = intent.getStringExtra(EXTRA_GEOFENCE_ID)
63
+ val event = intent.getStringExtra(EXTRA_EVENT)
64
+ if (geofenceId == null || event == null) {
65
+ Log.e(TAG, "getTaskConfig: missing geofenceId or event (id=$geofenceId event=$event)")
66
+ return null
67
+ }
68
+ val timestamp = intent.getDoubleExtra(EXTRA_TIMESTAMP, 0.0)
69
+
70
+ val details = GeofenceManagerModule.getGeofenceDetails(this, geofenceId)
71
+ Log.i(TAG, "getTaskConfig: task=${GeofenceManagerModule.GEOFENCE_HEADLESS_TASK_NAME} id=$geofenceId event=$event")
72
+ val data = Arguments.createMap().apply {
73
+ putString("geofenceId", geofenceId)
74
+ putString("event", event)
75
+ putDouble("timestamp", timestamp)
76
+ putString("name", details?.name ?: "")
77
+ putDouble("latitude", details?.latitude ?: 0.0)
78
+ putDouble("longitude", details?.longitude ?: 0.0)
79
+ putDouble("radius", details?.radius?.toDouble() ?: 0.0)
80
+ }
50
81
 
51
- override fun onHeadlessJsTaskFinish(taskId: Int) {
52
- Log.d(TAG, "════════════════════════════════════════")
53
- Log.d(TAG, "✅ HEADLESS JS TASK FINISHED!")
54
- Log.d(TAG, "════════════════════════════════════════")
55
- Log.d(TAG, " └─ TaskId: $taskId")
56
- super.onHeadlessJsTaskFinish(taskId)
82
+ return HeadlessJsTaskConfig(
83
+ GeofenceManagerModule.GEOFENCE_HEADLESS_TASK_NAME,
84
+ data,
85
+ 5000L,
86
+ false
87
+ )
57
88
  }
58
89
 
59
- override fun onDestroy() {
60
- Log.d(TAG, "════════════════════════════════════════")
61
- Log.d(TAG, "💀 SERVICE DESTROYED - onDestroy()")
62
- Log.d(TAG, "════════════════════════════════════════")
63
- super.onDestroy()
90
+ private fun ensureNotificationChannel() {
91
+ if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) return
92
+ val channel = NotificationChannel(
93
+ CHANNEL_ID,
94
+ "Geofence",
95
+ NotificationManager.IMPORTANCE_LOW
96
+ ).apply { setShowBadge(false) }
97
+ (getSystemService(NOTIFICATION_SERVICE) as NotificationManager)
98
+ .createNotificationChannel(channel)
64
99
  }
65
100
 
66
- override fun getTaskConfig(intent: Intent?): HeadlessJsTaskConfig? {
67
- Log.d(TAG, "════════════════════════════════════════")
68
- Log.d(TAG, "🔧 GET_TASK_CONFIG() CHAMADO")
69
- Log.d(TAG, "════════════════════════════════════════")
70
- Log.d(TAG, "⏰ Timestamp: ${System.currentTimeMillis()}")
71
- Log.d(TAG, "📦 Intent recebido: $intent")
72
-
73
- try {
74
- val extras = intent?.extras
75
- if (extras == null) {
76
- Log.e(TAG, "❌ Intent extras é NULL - retornando null")
77
- return null
78
- }
79
-
80
- Log.d(TAG, "📋 Extras disponíveis:")
81
- for (key in extras.keySet()) {
82
- Log.d(TAG, " └─ $key = ${extras.get(key)}")
83
- }
84
-
85
- val eventName = extras.getString("eventName")
86
- if (eventName == null) {
87
- Log.e(TAG, "❌ eventName é NULL - retornando null")
88
- return null
89
- }
90
-
91
- val regionId = extras.getString("regionId")
92
- if (regionId == null) {
93
- Log.e(TAG, "❌ regionId é NULL - retornando null")
94
- return null
95
- }
96
-
97
- Log.d(TAG, "✅ Dados extraídos:")
98
- Log.d(TAG, " └─ eventName: $eventName")
99
- Log.d(TAG, " └─ regionId: $regionId")
100
-
101
- val regionName = extras.getString("regionName") ?: ""
102
- val regionData = extras.getString("regionData") ?: ""
103
- val regionLatitude = extras.getDouble("regionLatitude", 0.0)
104
- val regionLongitude = extras.getDouble("regionLongitude", 0.0)
105
- val regionRadius = extras.getDouble("regionRadius", 100.0)
106
-
107
- Log.d(TAG, "📍 Region data:")
108
- Log.d(TAG, " └─ name: $regionName")
109
- Log.d(TAG, " └─ data: $regionData")
110
- Log.d(TAG, " └─ latitude: $regionLatitude")
111
- Log.d(TAG, " └─ longitude: $regionLongitude")
112
- Log.d(TAG, " └─ radius: $regionRadius")
113
-
114
- val regionMap = Arguments.createMap().apply {
115
- putString("id", regionId)
116
- putString("name", regionName)
117
- putString("data", regionData)
118
- putDouble("latitude", regionLatitude)
119
- putDouble("longitude", regionLongitude)
120
- putDouble("radius", regionRadius)
121
- }
122
-
123
- val timestamp = System.currentTimeMillis().toDouble()
124
- val data = Arguments.createMap().apply {
125
- putString("event", eventName)
126
- putMap("region", regionMap)
127
- putDouble("timestamp", timestamp)
128
- }
129
-
130
- Log.d(TAG, "📦 HeadlessTask data construído:")
131
- Log.d(TAG, " └─ event: $eventName")
132
- Log.d(TAG, " └─ timestamp: $timestamp")
133
-
134
- val timeout = RegionManager.getHeadlessTimeout(applicationContext)
135
- Log.d(TAG, "⏱️ Timeout configurado: ${timeout}ms")
136
-
137
- Log.d(TAG, "🔧 Criando HeadlessJsTaskConfig:")
138
- Log.d(TAG, " └─ taskKey: ${GeofenceReceiver.HEADLESS_TASK_NAME}")
139
- Log.d(TAG, " └─ timeout: ${timeout}ms")
140
- Log.d(TAG, " └─ allowedInForeground: true")
141
-
142
- val config = HeadlessJsTaskConfig(
143
- GeofenceReceiver.HEADLESS_TASK_NAME,
144
- data,
145
- timeout.toLong(),
146
- true
147
- )
148
-
149
- Log.d(TAG, "════════════════════════════════════════")
150
- Log.d(TAG, "✅ HeadlessJsTaskConfig CRIADO COM SUCESSO!")
151
- Log.d(TAG, "════════════════════════════════════════")
101
+ private fun buildNotification(intent: Intent?): Notification {
102
+ val event = intent?.getStringExtra(EXTRA_EVENT) ?: "geofence"
103
+ val smallIcon = applicationInfo.icon.takeIf { it != 0 } ?: android.R.drawable.ic_dialog_info
104
+ return Notification.Builder(this, CHANNEL_ID)
105
+ .setContentTitle("Geofence")
106
+ .setContentText("Evento: $event")
107
+ .setSmallIcon(smallIcon)
108
+ .setPriority(Notification.PRIORITY_LOW)
109
+ .build()
110
+ }
152
111
 
153
- return config
154
- } catch (e: Exception) {
155
- Log.e(TAG, "════════════════════════════════════════")
156
- Log.e(TAG, "💥 EXCEÇÃO em getTaskConfig():")
157
- Log.e(TAG, "════════════════════════════════════════")
158
- Log.e(TAG, " └─ ${e.javaClass.simpleName}: ${e.message}")
159
- Log.e(TAG, " └─ StackTrace: ${e.stackTraceToString()}")
160
- return null
161
- }
112
+ companion object {
113
+ private const val TAG = "GeofenceManager"
114
+ private const val CHANNEL_ID = "geofence_manager_channel"
115
+ private const val NOTIFICATION_ID = 9001
116
+ const val EXTRA_GEOFENCE_ID = "geofenceId"
117
+ const val EXTRA_EVENT = "event"
118
+ const val EXTRA_TIMESTAMP = "timestamp"
162
119
  }
163
120
  }