react-native-geofence-manager 0.1.0-beta.10 → 0.1.0-beta.12

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 (47) hide show
  1. package/README.md +157 -37
  2. package/android/src/main/AndroidManifest.xml +17 -16
  3. package/android/src/main/java/com/geofencemanager/GeofenceBroadcastReceiver.kt +87 -0
  4. package/android/src/main/java/com/geofencemanager/GeofenceHeadlessTaskService.kt +78 -142
  5. package/android/src/main/java/com/geofencemanager/GeofenceManagerModule.kt +155 -156
  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/index.js +40 -38
  10. package/lib/module/index.js.map +1 -1
  11. package/lib/module/models/GeofenceEvent.js +4 -0
  12. package/lib/module/models/GeofenceEvent.js.map +1 -0
  13. package/lib/module/models/GeofenceEventType.js +2 -0
  14. package/lib/module/models/GeofenceEventType.js.map +1 -0
  15. package/lib/module/models/GeofenceModel.js +2 -0
  16. package/lib/module/models/GeofenceModel.js.map +1 -0
  17. package/lib/module/models/index.js +6 -0
  18. package/lib/module/models/index.js.map +1 -0
  19. package/lib/module/package.json +1 -0
  20. package/lib/typescript/package.json +1 -0
  21. package/lib/typescript/src/NativeGeofenceManager.d.ts +3 -16
  22. package/lib/typescript/src/NativeGeofenceManager.d.ts.map +1 -1
  23. package/lib/typescript/src/index.d.ts +27 -26
  24. package/lib/typescript/src/index.d.ts.map +1 -1
  25. package/lib/typescript/src/models/GeofenceEvent.d.ts +11 -0
  26. package/lib/typescript/src/models/GeofenceEvent.d.ts.map +1 -0
  27. package/lib/typescript/src/models/GeofenceEventType.d.ts +2 -0
  28. package/lib/typescript/src/models/GeofenceEventType.d.ts.map +1 -0
  29. package/lib/typescript/src/models/GeofenceModel.d.ts +8 -0
  30. package/lib/typescript/src/models/GeofenceModel.d.ts.map +1 -0
  31. package/lib/typescript/src/models/index.d.ts +4 -0
  32. package/lib/typescript/src/models/index.d.ts.map +1 -0
  33. package/package.json +16 -15
  34. package/src/NativeGeofenceManager.ts +4 -29
  35. package/src/index.tsx +59 -73
  36. package/src/models/GeofenceEvent.ts +11 -0
  37. package/src/models/GeofenceEventType.ts +1 -0
  38. package/src/models/GeofenceModel.ts +7 -0
  39. package/src/models/index.ts +3 -0
  40. package/android/src/main/java/com/geofencemanager/GeofenceReceiver.kt +0 -183
  41. package/android/src/main/java/com/geofencemanager/RegionManager.kt +0 -390
  42. package/lib/commonjs/NativeGeofenceManager.js +0 -15
  43. package/lib/commonjs/NativeGeofenceManager.js.map +0 -1
  44. package/lib/commonjs/index.js +0 -47
  45. package/lib/commonjs/index.js.map +0 -1
  46. package/lib/commonjs/package.json +0 -1
  47. package/react-native.config.js +0 -11
package/README.md CHANGED
@@ -1,37 +1,157 @@
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
+ ## App de exemplo
119
+
120
+ Na raiz do repositório:
121
+
122
+ ```sh
123
+ yarn example start
124
+ yarn example android
125
+ ```
126
+
127
+ O diretório `example/` demonstra permissões, registro de geofences, listener e notificações locais (Notifee).
128
+
129
+ ---
130
+
131
+ ## API exportada (resumo)
132
+
133
+ | Export | Descrição |
134
+ |--------------------------------|------------------------------------------------|
135
+ | `addGeofences` | Registra uma lista de geofences |
136
+ | `removeAllGeofences` | Remove todas as geofences registradas |
137
+ | `addGeofenceEventListener` | Inscreve callback para eventos (foreground) |
138
+ | `onGeofenceEvent` | Alias de `addGeofenceEventListener` |
139
+ | `registerGeofenceHeadlessTask` | Registra handler para background/app fechado |
140
+ | `GEOFENCE_HEADLESS_TASK_NAME` | Nome interno da headless task |
141
+ | Tipos `GeofenceModel`, `GeofenceEvent`, etc. | TypeScript |
142
+
143
+ ---
144
+
145
+ ## Contributing
146
+
147
+ - [Development workflow](CONTRIBUTING.md#development-workflow)
148
+ - [Sending a pull request](CONTRIBUTING.md#sending-a-pull-request)
149
+ - [Code of conduct](CODE_OF_CONDUCT.md)
150
+
151
+ ## License
152
+
153
+ MIT
154
+
155
+ ---
156
+
157
+ 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,87 @@
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.os.Handler
8
+ import android.os.Looper
9
+ import com.facebook.react.HeadlessJsTaskService
10
+ import com.facebook.react.bridge.Arguments
11
+ import com.facebook.react.bridge.ReactApplicationContext
12
+ import com.facebook.react.bridge.WritableMap
13
+ import com.facebook.react.modules.core.DeviceEventManagerModule
14
+ import com.google.android.gms.location.Geofence
15
+ import com.google.android.gms.location.GeofencingEvent
16
+
17
+ class GeofenceBroadcastReceiver : BroadcastReceiver() {
18
+
19
+ override fun onReceive(context: Context?, intent: Intent?) {
20
+ if (context == null || intent == null) return
21
+ if (intent.action != GeofenceManagerModule.GEOFENCE_ACTION) return
22
+
23
+ val geofencingEvent = GeofencingEvent.fromIntent(intent) ?: return
24
+ if (geofencingEvent.hasError()) return
25
+
26
+ if (GeofenceManagerModule.isWithinGracePeriod(context)) return
27
+
28
+ val transition = geofencingEvent.geofenceTransition
29
+ val eventName = when (transition) {
30
+ Geofence.GEOFENCE_TRANSITION_ENTER -> "enter"
31
+ Geofence.GEOFENCE_TRANSITION_EXIT -> "exit"
32
+ Geofence.GEOFENCE_TRANSITION_DWELL -> "dwell"
33
+ else -> return
34
+ }
35
+
36
+ val triggeringGeofences = geofencingEvent.triggeringGeofences ?: return
37
+ if (triggeringGeofences.isEmpty()) return
38
+
39
+ val reactContext = GeofenceManagerModule.reactContextRef
40
+ val timestamp = System.currentTimeMillis().toDouble()
41
+ val isForeground = reactContext != null && reactContext.hasActiveReactInstance()
42
+
43
+ if (isForeground) {
44
+ val ctx = reactContext!!
45
+ val geofenceIds = triggeringGeofences.map { it.requestId }
46
+ val eventNameForRunnable = eventName
47
+ Handler(Looper.getMainLooper()).postDelayed({
48
+ if (!ctx.hasActiveReactInstance()) return@postDelayed
49
+ try {
50
+ val emitter = ctx.getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter::class.java)
51
+ for (geofenceId in geofenceIds) {
52
+ val details = GeofenceManagerModule.getGeofenceDetails(context, geofenceId)
53
+ val params = Arguments.createMap().apply {
54
+ putString("geofenceId", geofenceId)
55
+ putString("event", eventNameForRunnable)
56
+ putDouble("timestamp", timestamp)
57
+ putString("name", details?.name ?: "")
58
+ details?.let {
59
+ putDouble("latitude", it.latitude)
60
+ putDouble("longitude", it.longitude)
61
+ putDouble("radius", it.radius.toDouble())
62
+ }
63
+ }
64
+ emitter.emit("onGeofenceTransition", params)
65
+ }
66
+ } catch (_: Exception) { }
67
+ }, 50)
68
+ return
69
+ }
70
+
71
+ val appContext = context.applicationContext
72
+ HeadlessJsTaskService.acquireWakeLockNow(appContext)
73
+ for (geofence in triggeringGeofences) {
74
+ val serviceIntent = Intent(appContext, GeofenceHeadlessTaskService::class.java).apply {
75
+ putExtra(GeofenceHeadlessTaskService.EXTRA_GEOFENCE_ID, geofence.requestId)
76
+ putExtra(GeofenceHeadlessTaskService.EXTRA_EVENT, eventName)
77
+ putExtra(GeofenceHeadlessTaskService.EXTRA_TIMESTAMP, timestamp)
78
+ }
79
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
80
+ appContext.startForegroundService(serviceIntent)
81
+ } else {
82
+ appContext.startService(serviceIntent)
83
+ }
84
+ }
85
+ }
86
+
87
+ }
@@ -1,163 +1,99 @@
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
5
- import android.util.Log
7
+ import android.os.Build
6
8
  import com.facebook.react.HeadlessJsTaskService
7
9
  import com.facebook.react.bridge.Arguments
8
10
  import com.facebook.react.jstasks.HeadlessJsTaskConfig
9
11
 
10
12
  class GeofenceHeadlessTaskService : HeadlessJsTaskService() {
11
13
 
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
-
14
+ /**
15
+ * Quando o app está em background/frozen, reactContext existe mas hasActiveReactInstance()
16
+ * retorna false e a task não roda. Nesse caso chamamos createReactContextAndScheduleTask
17
+ * (via reflexão) em onStartCommand para que dwell/exit sejam executados.
18
+ * startForeground() é chamado para que o sistema inicie o serviço mesmo em background (dwell/exit).
19
+ */
23
20
  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
21
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
22
+ ensureNotificationChannel()
23
+ val notification = buildNotification(intent)
24
+ startForeground(NOTIFICATION_ID, notification)
25
+ }
26
+ val taskConfig = getTaskConfig(intent) ?: return START_NOT_STICKY
27
+ val ctx = reactContext
28
+ if (ctx != null && !ctx.hasActiveReactInstance()) {
29
+ HeadlessJsTaskService.acquireWakeLockNow(this)
30
+ try {
31
+ val method = HeadlessJsTaskService::class.java.getDeclaredMethod(
32
+ "createReactContextAndScheduleTask",
33
+ HeadlessJsTaskConfig::class.java
34
+ )
35
+ method.isAccessible = true
36
+ method.invoke(this, taskConfig)
37
+ } catch (_: Exception) {
38
+ return super.onStartCommand(intent, flags, startId)
39
+ }
40
+ return START_REDELIVER_INTENT
40
41
  }
42
+ return super.onStartCommand(intent, flags, startId)
41
43
  }
42
44
 
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
- }
45
+ override fun getTaskConfig(intent: Intent?): HeadlessJsTaskConfig? {
46
+ if (intent == null) return null
47
+ val geofenceId = intent.getStringExtra(EXTRA_GEOFENCE_ID) ?: return null
48
+ val event = intent.getStringExtra(EXTRA_EVENT) ?: return null
49
+ val timestamp = intent.getDoubleExtra(EXTRA_TIMESTAMP, 0.0)
50
+
51
+ val details = GeofenceManagerModule.getGeofenceDetails(this, geofenceId)
52
+ val data = Arguments.createMap().apply {
53
+ putString("geofenceId", geofenceId)
54
+ putString("event", event)
55
+ putDouble("timestamp", timestamp)
56
+ putString("name", details?.name ?: "")
57
+ putDouble("latitude", details?.latitude ?: 0.0)
58
+ putDouble("longitude", details?.longitude ?: 0.0)
59
+ putDouble("radius", details?.radius?.toDouble() ?: 0.0)
60
+ }
50
61
 
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)
62
+ return HeadlessJsTaskConfig(
63
+ GeofenceManagerModule.GEOFENCE_HEADLESS_TASK_NAME,
64
+ data,
65
+ 5000L,
66
+ false
67
+ )
57
68
  }
58
69
 
59
- override fun onDestroy() {
60
- Log.d(TAG, "════════════════════════════════════════")
61
- Log.d(TAG, "💀 SERVICE DESTROYED - onDestroy()")
62
- Log.d(TAG, "════════════════════════════════════════")
63
- super.onDestroy()
70
+ private fun ensureNotificationChannel() {
71
+ if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) return
72
+ val channel = NotificationChannel(
73
+ CHANNEL_ID,
74
+ "Geofence",
75
+ NotificationManager.IMPORTANCE_LOW
76
+ ).apply { setShowBadge(false) }
77
+ (getSystemService(NOTIFICATION_SERVICE) as NotificationManager)
78
+ .createNotificationChannel(channel)
64
79
  }
65
80
 
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, "════════════════════════════════════════")
81
+ private fun buildNotification(intent: Intent?): Notification {
82
+ val event = intent?.getStringExtra(EXTRA_EVENT) ?: "geofence"
83
+ val smallIcon = applicationInfo.icon.takeIf { it != 0 } ?: android.R.drawable.ic_dialog_info
84
+ return Notification.Builder(this, CHANNEL_ID)
85
+ .setContentTitle("Geofence")
86
+ .setContentText("Evento: $event")
87
+ .setSmallIcon(smallIcon)
88
+ .setPriority(Notification.PRIORITY_LOW)
89
+ .build()
90
+ }
152
91
 
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
- }
92
+ companion object {
93
+ private const val CHANNEL_ID = "geofence_manager_channel"
94
+ private const val NOTIFICATION_ID = 9001
95
+ const val EXTRA_GEOFENCE_ID = "geofenceId"
96
+ const val EXTRA_EVENT = "event"
97
+ const val EXTRA_TIMESTAMP = "timestamp"
162
98
  }
163
99
  }