feat: section 4 — Android battery optimization prompt
One-time prompt on first launch (Android only) directing the user to exclude bincio-rec from battery optimization. Uses the direct REQUEST_IGNORE_BATTERY_OPTIMIZATIONS system dialog with a fallback to the general settings page for OEMs that block the direct intent. Dismissal persisted in AsyncStorage so prompt never repeats.
This commit is contained in:
@@ -4,6 +4,7 @@ import * as Notifications from 'expo-notifications';
|
|||||||
import { GestureHandlerRootView } from 'react-native-gesture-handler';
|
import { GestureHandlerRootView } from 'react-native-gesture-handler';
|
||||||
import { AppNavigator } from './src/navigation/AppNavigator';
|
import { AppNavigator } from './src/navigation/AppNavigator';
|
||||||
import { requestNotificationPermissions } from './src/services/gps';
|
import { requestNotificationPermissions } from './src/services/gps';
|
||||||
|
import { promptBatteryOptimizationIfNeeded } from './src/services/batteryOptimization';
|
||||||
|
|
||||||
// Show notifications even when the app is in the foreground (iOS suppresses by default)
|
// Show notifications even when the app is in the foreground (iOS suppresses by default)
|
||||||
Notifications.setNotificationHandler({
|
Notifications.setNotificationHandler({
|
||||||
@@ -18,6 +19,7 @@ Notifications.setNotificationHandler({
|
|||||||
export default function App() {
|
export default function App() {
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
requestNotificationPermissions();
|
requestNotificationPermissions();
|
||||||
|
promptBatteryOptimizationIfNeeded();
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|||||||
@@ -117,9 +117,9 @@ Items below are what remains before v1 is shippable.
|
|||||||
- [x] **Permission request** — `requestNotificationPermissions()` called in `App.tsx` on mount; foreground notification handler set at module level so iOS shows banners while app is active
|
- [x] **Permission request** — `requestNotificationPermissions()` called in `App.tsx` on mount; foreground notification handler set at module level so iOS shows banners while app is active
|
||||||
- [x] **Milestone tracker** — module-level `runningDistanceMeters` / `lastNotifiedKm` / `prevPoint` in `gps.ts`, reset on each `startGpsRecording()`; each incoming location adds haversine distance and fires a notification when a new km is crossed, gated on the `kmNotifications` AsyncStorage setting
|
- [x] **Milestone tracker** — module-level `runningDistanceMeters` / `lastNotifiedKm` / `prevPoint` in `gps.ts`, reset on each `startGpsRecording()`; each incoming location adds haversine distance and fires a notification when a new km is crossed, gated on the `kmNotifications` AsyncStorage setting
|
||||||
|
|
||||||
### 4 — Android battery optimization prompt (1–2 hours)
|
### 4 — Android battery optimization prompt ✅
|
||||||
|
|
||||||
- [ ] On first launch, detect if the app is affected by battery optimization (`expo-intent-launcher`) and show a one-time prompt directing the user to whitelist bincio-rec; persist dismissal in AsyncStorage so it only shows once
|
- [x] `src/services/batteryOptimization.ts` — Android-only, one-time prompt (dismissed flag in AsyncStorage); uses `REQUEST_IGNORE_BATTERY_OPTIMIZATIONS` intent to open the system dialog for bincio-rec directly; falls back to `IGNORE_BATTERY_OPTIMIZATION_SETTINGS` (general page) on OEMs that block the direct intent; called from `App.tsx` on mount alongside notification permission request
|
||||||
|
|
||||||
### 5 — Map (optional upgrade)
|
### 5 — Map (optional upgrade)
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,35 @@
|
|||||||
|
import { Platform, Alert } from 'react-native';
|
||||||
|
import * as IntentLauncher from 'expo-intent-launcher';
|
||||||
|
import AsyncStorage from '@react-native-async-storage/async-storage';
|
||||||
|
|
||||||
|
const PROMPT_SHOWN_KEY = 'batteryOptPromptShown';
|
||||||
|
|
||||||
|
export async function promptBatteryOptimizationIfNeeded(): Promise<void> {
|
||||||
|
if (Platform.OS !== 'android') return;
|
||||||
|
|
||||||
|
const shown = await AsyncStorage.getItem(PROMPT_SHOWN_KEY);
|
||||||
|
if (shown) return;
|
||||||
|
|
||||||
|
await AsyncStorage.setItem(PROMPT_SHOWN_KEY, 'true');
|
||||||
|
|
||||||
|
Alert.alert(
|
||||||
|
'Allow background GPS',
|
||||||
|
'To record accurately while the screen is off, bincio-rec needs to be excluded from battery optimization.\n\nTap "Open settings", find bincio-rec, and select "Don\'t optimize".',
|
||||||
|
[
|
||||||
|
{
|
||||||
|
text: 'Open settings',
|
||||||
|
onPress: () =>
|
||||||
|
IntentLauncher.startActivityAsync(
|
||||||
|
IntentLauncher.ActivityAction.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS,
|
||||||
|
{ data: 'package:com.bincio.rec' },
|
||||||
|
).catch(() =>
|
||||||
|
// Fallback: some OEMs don't support the direct intent — open the general page
|
||||||
|
IntentLauncher.startActivityAsync(
|
||||||
|
IntentLauncher.ActivityAction.IGNORE_BATTERY_OPTIMIZATION_SETTINGS,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
},
|
||||||
|
{ text: 'Later', style: 'cancel' },
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user