react-native-geofence-manager 0.1.0-beta.11 → 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.
- package/README.md +157 -37
- package/android/src/main/AndroidManifest.xml +17 -16
- package/android/src/main/java/com/geofencemanager/GeofenceBroadcastReceiver.kt +87 -0
- package/android/src/main/java/com/geofencemanager/GeofenceHeadlessTaskService.kt +78 -142
- package/android/src/main/java/com/geofencemanager/GeofenceManagerModule.kt +155 -156
- package/ios/GeofenceManager.mm +21 -21
- package/lib/module/NativeGeofenceManager.js +1 -7
- package/lib/module/NativeGeofenceManager.js.map +1 -1
- package/lib/module/index.js +40 -38
- package/lib/module/index.js.map +1 -1
- package/lib/module/models/GeofenceEvent.js +4 -0
- package/lib/module/models/GeofenceEvent.js.map +1 -0
- package/lib/module/models/GeofenceEventType.js +2 -0
- package/lib/module/models/GeofenceEventType.js.map +1 -0
- package/lib/module/models/GeofenceModel.js +2 -0
- package/lib/module/models/GeofenceModel.js.map +1 -0
- package/lib/module/models/index.js +6 -0
- package/lib/module/models/index.js.map +1 -0
- package/lib/module/package.json +1 -0
- package/lib/typescript/package.json +1 -0
- package/lib/typescript/src/NativeGeofenceManager.d.ts +3 -16
- package/lib/typescript/src/NativeGeofenceManager.d.ts.map +1 -1
- package/lib/typescript/src/index.d.ts +27 -26
- package/lib/typescript/src/index.d.ts.map +1 -1
- package/lib/typescript/src/models/GeofenceEvent.d.ts +11 -0
- package/lib/typescript/src/models/GeofenceEvent.d.ts.map +1 -0
- package/lib/typescript/src/models/GeofenceEventType.d.ts +2 -0
- package/lib/typescript/src/models/GeofenceEventType.d.ts.map +1 -0
- package/lib/typescript/src/models/GeofenceModel.d.ts +8 -0
- package/lib/typescript/src/models/GeofenceModel.d.ts.map +1 -0
- package/lib/typescript/src/models/index.d.ts +4 -0
- package/lib/typescript/src/models/index.d.ts.map +1 -0
- package/package.json +16 -15
- package/src/NativeGeofenceManager.ts +4 -29
- package/src/index.tsx +59 -73
- package/src/models/GeofenceEvent.ts +11 -0
- package/src/models/GeofenceEventType.ts +1 -0
- package/src/models/GeofenceModel.ts +7 -0
- package/src/models/index.ts +3 -0
- package/android/src/main/java/com/geofencemanager/GeofenceReceiver.kt +0 -192
- package/android/src/main/java/com/geofencemanager/RegionManager.kt +0 -387
- package/lib/commonjs/NativeGeofenceManager.js +0 -15
- package/lib/commonjs/NativeGeofenceManager.js.map +0 -1
- package/lib/commonjs/index.js +0 -47
- package/lib/commonjs/index.js.map +0 -1
- package/lib/commonjs/package.json +0 -1
- package/react-native.config.js +0 -11
package/README.md
CHANGED
|
@@ -1,37 +1,157 @@
|
|
|
1
|
-
# react-native-geofence-manager
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
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
|
-
|
|
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
|
-
<
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
android:
|
|
11
|
-
|
|
12
|
-
<service
|
|
13
|
-
android:name=".GeofenceHeadlessTaskService"
|
|
14
|
-
android:exported="false"
|
|
15
|
-
|
|
16
|
-
</
|
|
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.
|
|
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
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
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
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
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
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
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
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
62
|
+
return HeadlessJsTaskConfig(
|
|
63
|
+
GeofenceManagerModule.GEOFENCE_HEADLESS_TASK_NAME,
|
|
64
|
+
data,
|
|
65
|
+
5000L,
|
|
66
|
+
false
|
|
67
|
+
)
|
|
57
68
|
}
|
|
58
69
|
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
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
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
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
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
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
|
}
|