react-native-geofence-manager 0.1.0-beta.15 → 0.1.0-beta.16

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 CHANGED
@@ -1,161 +1,161 @@
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,
64
- // opcional: string livre (ex. JSON) — volta em GeofenceEvent.data
65
- data: JSON.stringify({ lojaId: '123', promo: true }),
66
- },
67
- ]);
68
- // ok === true se o registro foi aceito pelo sistema
69
- ```
70
-
71
- ### 3. Listener com o app em primeiro plano
72
-
73
- ```tsx
74
- import { addGeofenceEventListener } from 'react-native-geofence-manager';
75
-
76
- const unsubscribe = addGeofenceEventListener((event) => {
77
- console.log(event.geofenceId, event.event); // 'enter' | 'exit' | 'dwell'
78
- });
79
-
80
- // ao desmontar:
81
- unsubscribe();
82
- ```
83
-
84
- ### 4. Remover todas as geofences
85
-
86
- ```tsx
87
- import { removeAllGeofences } from 'react-native-geofence-manager';
88
-
89
- await removeAllGeofences();
90
- ```
91
-
92
- ---
93
-
94
- ## Modelo de geofence (`GeofenceModel`)
95
-
96
- | Campo | Tipo | Obrigatório | Descrição |
97
- |---------------|----------|-------------|------------------------------------|
98
- | `id` | `string` | Sim | ID único da geofence |
99
- | `latitude` | `number` | Sim | Latitude do centro |
100
- | `longitude` | `number` | Sim | Longitude do centro |
101
- | `name` | `string` | Não | Nome exibido nos eventos |
102
- | `radius` | `number` | Não | Raio em metros |
103
- | `data` | `string` | Não | Payload opaco; repassado no evento para você dar `JSON.parse` etc. |
104
-
105
- ---
106
-
107
- ## Evento (`GeofenceEvent`)
108
-
109
- - `geofenceId` — ID da geofence
110
- - `event` — `'enter' | 'exit' | 'dwell'`
111
- - `timestamp`, `name`, `latitude`, `longitude`, `radius` — quando disponíveis
112
- - `data` — mesma string definida em `GeofenceModel.data` (vazio se não houver)
113
-
114
- ---
115
-
116
- ## Permissões (Android)
117
-
118
- 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.
119
-
120
- ---
121
-
122
- ## App de exemplo
123
-
124
- Na raiz do repositório:
125
-
126
- ```sh
127
- yarn example start
128
- yarn example android
129
- ```
130
-
131
- O diretório `example/` demonstra permissões, registro de geofences, listener e notificações locais (Notifee).
132
-
133
- ---
134
-
135
- ## API exportada (resumo)
136
-
137
- | Export | Descrição |
138
- |--------------------------------|------------------------------------------------|
139
- | `addGeofences` | Registra uma lista de geofences |
140
- | `removeAllGeofences` | Remove todas as geofences registradas |
141
- | `addGeofenceEventListener` | Inscreve callback para eventos (foreground) |
142
- | `onGeofenceEvent` | Alias de `addGeofenceEventListener` |
143
- | `registerGeofenceHeadlessTask` | Registra handler para background/app fechado |
144
- | `GEOFENCE_HEADLESS_TASK_NAME` | Nome interno da headless task |
145
- | Tipos `GeofenceModel`, `GeofenceEvent`, etc. | TypeScript |
146
-
147
- ---
148
-
149
- ## Contributing
150
-
151
- - [Development workflow](CONTRIBUTING.md#development-workflow)
152
- - [Sending a pull request](CONTRIBUTING.md#sending-a-pull-request)
153
- - [Code of conduct](CODE_OF_CONDUCT.md)
154
-
155
- ## License
156
-
157
- MIT
158
-
159
- ---
160
-
161
- 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,
64
+ // opcional: string livre (ex. JSON) — volta em GeofenceEvent.data
65
+ data: JSON.stringify({ lojaId: '123', promo: true }),
66
+ },
67
+ ]);
68
+ // ok === true se o registro foi aceito pelo sistema
69
+ ```
70
+
71
+ ### 3. Listener com o app em primeiro plano
72
+
73
+ ```tsx
74
+ import { addGeofenceEventListener } from 'react-native-geofence-manager';
75
+
76
+ const unsubscribe = addGeofenceEventListener((event) => {
77
+ console.log(event.geofenceId, event.event); // 'enter' | 'exit' | 'dwell'
78
+ });
79
+
80
+ // ao desmontar:
81
+ unsubscribe();
82
+ ```
83
+
84
+ ### 4. Remover todas as geofences
85
+
86
+ ```tsx
87
+ import { removeAllGeofences } from 'react-native-geofence-manager';
88
+
89
+ await removeAllGeofences();
90
+ ```
91
+
92
+ ---
93
+
94
+ ## Modelo de geofence (`GeofenceModel`)
95
+
96
+ | Campo | Tipo | Obrigatório | Descrição |
97
+ |---------------|----------|-------------|------------------------------------|
98
+ | `id` | `string` | Sim | ID único da geofence |
99
+ | `latitude` | `number` | Sim | Latitude do centro |
100
+ | `longitude` | `number` | Sim | Longitude do centro |
101
+ | `name` | `string` | Não | Nome exibido nos eventos |
102
+ | `radius` | `number` | Não | Raio em metros |
103
+ | `data` | `string` | Não | Payload opaco; repassado no evento para você dar `JSON.parse` etc. |
104
+
105
+ ---
106
+
107
+ ## Evento (`GeofenceEvent`)
108
+
109
+ - `geofenceId` — ID da geofence
110
+ - `event` — `'enter' | 'exit' | 'dwell'`
111
+ - `timestamp`, `name`, `latitude`, `longitude`, `radius` — quando disponíveis
112
+ - `data` — mesma string definida em `GeofenceModel.data` (vazio se não houver)
113
+
114
+ ---
115
+
116
+ ## Permissões (Android)
117
+
118
+ 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.
119
+
120
+ ---
121
+
122
+ ## App de exemplo
123
+
124
+ Na raiz do repositório:
125
+
126
+ ```sh
127
+ yarn example start
128
+ yarn example android
129
+ ```
130
+
131
+ O diretório `example/` demonstra permissões, registro de geofences, listener e notificações locais (Notifee).
132
+
133
+ ---
134
+
135
+ ## API exportada (resumo)
136
+
137
+ | Export | Descrição |
138
+ |--------------------------------|------------------------------------------------|
139
+ | `addGeofences` | Registra uma lista de geofences |
140
+ | `removeAllGeofences` | Remove todas as geofences registradas |
141
+ | `addGeofenceEventListener` | Inscreve callback para eventos (foreground) |
142
+ | `onGeofenceEvent` | Alias de `addGeofenceEventListener` |
143
+ | `registerGeofenceHeadlessTask` | Registra handler para background/app fechado |
144
+ | `GEOFENCE_HEADLESS_TASK_NAME` | Nome interno da headless task |
145
+ | Tipos `GeofenceModel`, `GeofenceEvent`, etc. | TypeScript |
146
+
147
+ ---
148
+
149
+ ## Contributing
150
+
151
+ - [Development workflow](CONTRIBUTING.md#development-workflow)
152
+ - [Sending a pull request](CONTRIBUTING.md#sending-a-pull-request)
153
+ - [Code of conduct](CODE_OF_CONDUCT.md)
154
+
155
+ ## License
156
+
157
+ MIT
158
+
159
+ ---
160
+
161
+ Made with [create-react-native-library](https://github.com/callstack/react-native-builder-bob)
@@ -1,88 +1,91 @@
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.modules.core.DeviceEventManagerModule
13
- import com.google.android.gms.location.Geofence
14
- import com.google.android.gms.location.GeofencingEvent
15
-
16
- class GeofenceBroadcastReceiver : BroadcastReceiver() {
17
-
18
- override fun onReceive(context: Context?, intent: Intent?) {
19
- if (context == null || intent == null) return
20
- if (intent.action != GeofenceManagerModule.GEOFENCE_ACTION) return
21
-
22
- val geofencingEvent = GeofencingEvent.fromIntent(intent) ?: return
23
- if (geofencingEvent.hasError()) return
24
-
25
- if (GeofenceManagerModule.isWithinGracePeriod(context)) return
26
-
27
- val transition = geofencingEvent.geofenceTransition
28
- val eventName = when (transition) {
29
- Geofence.GEOFENCE_TRANSITION_ENTER -> "enter"
30
- Geofence.GEOFENCE_TRANSITION_EXIT -> "exit"
31
- Geofence.GEOFENCE_TRANSITION_DWELL -> "dwell"
32
- else -> return
33
- }
34
-
35
- val triggeringGeofences = geofencingEvent.triggeringGeofences ?: return
36
- if (triggeringGeofences.isEmpty()) return
37
-
38
- val reactContext = GeofenceManagerModule.reactContextRef
39
- val timestamp = System.currentTimeMillis().toDouble()
40
- val isForeground = reactContext != null && reactContext.hasActiveReactInstance()
41
-
42
- if (isForeground) {
43
- val ctx = reactContext!!
44
- val geofenceIds = triggeringGeofences.map { it.requestId }
45
- val eventNameForRunnable = eventName
46
- Handler(Looper.getMainLooper()).postDelayed({
47
- if (!ctx.hasActiveReactInstance()) return@postDelayed
48
- try {
49
- val emitter = ctx.getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter::class.java)
50
- for (geofenceId in geofenceIds) {
51
- val details = GeofenceManagerModule.getGeofenceDetails(context, geofenceId)
52
- val params = Arguments.createMap().apply {
53
- putString("geofenceId", geofenceId)
54
- putString("event", eventNameForRunnable)
55
- putDouble("timestamp", timestamp)
56
- putString("name", details?.name ?: "")
57
- details?.let {
58
- putDouble("latitude", it.latitude)
59
- putDouble("longitude", it.longitude)
60
- putDouble("radius", it.radius.toDouble())
61
- }
62
- putString("data", details?.userData ?: "")
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
- try {
80
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
81
- appContext.startForegroundService(serviceIntent)
82
- } else {
83
- appContext.startService(serviceIntent)
84
- }
85
- } catch (_: Exception) { }
86
- }
87
- }
88
- }
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.ReactApplicationContext
11
+ import com.facebook.react.modules.core.DeviceEventManagerModule
12
+ import com.google.android.gms.location.Geofence
13
+ import com.google.android.gms.location.GeofencingEvent
14
+
15
+ class GeofenceBroadcastReceiver : BroadcastReceiver() {
16
+
17
+ override fun onReceive(context: Context?, intent: Intent?) {
18
+ if (context == null || intent == null) return
19
+ if (intent.action != GeofenceManagerModule.GEOFENCE_ACTION) return
20
+
21
+ val geofencingEvent = GeofencingEvent.fromIntent(intent) ?: return
22
+ if (geofencingEvent.hasError()) return
23
+
24
+ if (GeofenceManagerModule.isWithinGracePeriod(context)) return
25
+
26
+ val transition = geofencingEvent.geofenceTransition
27
+ val eventName = when (transition) {
28
+ Geofence.GEOFENCE_TRANSITION_ENTER -> "enter"
29
+ Geofence.GEOFENCE_TRANSITION_EXIT -> "exit"
30
+ Geofence.GEOFENCE_TRANSITION_DWELL -> "dwell"
31
+ else -> return
32
+ }
33
+
34
+ val triggeringGeofences = geofencingEvent.triggeringGeofences ?: return
35
+ if (triggeringGeofences.isEmpty()) return
36
+
37
+ val reactContext = GeofenceManagerModule.reactContextRef
38
+ val timestamp = System.currentTimeMillis().toDouble()
39
+ val isForeground = reactContext != null && reactContext.hasActiveReactInstance()
40
+
41
+ if (isForeground) {
42
+ val ctx = reactContext!!
43
+ val geofenceIds = triggeringGeofences.map { it.requestId }
44
+ val eventNameForRunnable = eventName
45
+ Handler(Looper.getMainLooper()).postDelayed({
46
+ if (!ctx.hasActiveReactInstance()) return@postDelayed
47
+ try {
48
+ val emitter = ctx.getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter::class.java)
49
+ for (geofenceId in geofenceIds) {
50
+ val details = GeofenceManagerModule.getGeofenceDetails(context, geofenceId)
51
+ val params = GeofenceManagerModule.buildEventParams(
52
+ geofenceId,
53
+ eventNameForRunnable,
54
+ timestamp,
55
+ details
56
+ ) ?: continue
57
+ emitter.emit("onGeofenceTransition", params)
58
+ }
59
+ } catch (_: Exception) { }
60
+ }, 50)
61
+ return
62
+ }
63
+
64
+ val appContext = context.applicationContext
65
+ HeadlessJsTaskService.acquireWakeLockNow(appContext)
66
+ for (geofence in triggeringGeofences) {
67
+ val details = GeofenceManagerModule.getGeofenceDetails(context, geofence.requestId)
68
+ if (GeofenceManagerModule.buildEventParams(
69
+ geofence.requestId,
70
+ eventName,
71
+ timestamp,
72
+ details
73
+ ) == null
74
+ ) {
75
+ continue
76
+ }
77
+ val serviceIntent = Intent(appContext, GeofenceHeadlessTaskService::class.java).apply {
78
+ putExtra(GeofenceHeadlessTaskService.EXTRA_GEOFENCE_ID, geofence.requestId)
79
+ putExtra(GeofenceHeadlessTaskService.EXTRA_EVENT, eventName)
80
+ putExtra(GeofenceHeadlessTaskService.EXTRA_TIMESTAMP, timestamp)
81
+ }
82
+ try {
83
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
84
+ appContext.startForegroundService(serviceIntent)
85
+ } else {
86
+ appContext.startService(serviceIntent)
87
+ }
88
+ } catch (_: Exception) { }
89
+ }
90
+ }
91
+ }