@sahil_sensei/react-native-app-usage
v0.3.0
Published
Android App Usage Stats, Screen Time & Digital Wellbeing data for React Native
Maintainers
Readme
@sahil_sensei/react-native-app-usage
Android app usage, screen time, launch count, installed app metadata, and app icons for React Native.
Use this package when you want to build dashboards like Digital Wellbeing, screen time charts, app usage lists, focus mode summaries, or parental-control style usage views.
Breaking change in 0.3.0 —
AppInfo.iconnow returns afile://URI pointing at a cached PNG on disk (was:data:image/png;base64,...). The native module writes the PNG to the host app'scacheDir/app-icons/and only re-encodes when the app'sversionCodeor icon bytes change. New fieldsiconHash,versionCode, andversionNameare also returned. Consumers using<Image source={{ uri: app.icon }} />need no code change; consumers that previously decoded the base64 themselves should drop that step.
Features
- Check whether Usage Access permission is enabled.
- Open the Android Usage Access settings screen.
- Get installed apps with app name, package name, UID, cached PNG icon URI, icon hash, version code, and version name.
- Get daily, weekly, and monthly app usage stats.
- Get hourly usage breakdown for one app on a selected date.
- TypeScript types included.
Platform Support
| Platform | Support | | --- | --- | | Android | Supported | | iOS | Not supported |
Android is required because the package uses Android's UsageStats APIs.
Installation
npm install @sahil_sensei/react-native-app-usageor:
yarn add @sahil_sensei/react-native-app-usageFor React Native CLI projects, rebuild the Android app after installing:
npx react-native run-androidAndroid Permission
The package includes these Android permissions in its manifest:
<uses-permission
android:name="android.permission.PACKAGE_USAGE_STATS"
tools:ignore="ProtectedPermissions" />
<uses-permission
android:name="android.permission.QUERY_ALL_PACKAGES"
tools:ignore="QueryAllPackagesPermission" />PACKAGE_USAGE_STATS is a special Android permission. Users must enable it manually in system settings; it cannot be requested with a normal runtime permission dialog.
Use hasUsagePermission() to check access and openUsagePermissionSettings() to send the user to the correct Android settings page.
Quick Start
import {
getDailyUsage,
hasUsagePermission,
openUsagePermissionSettings,
} from '@sahil_sensei/react-native-app-usage';
async function loadUsage() {
const hasPermission = await hasUsagePermission();
if (!hasPermission) {
await openUsagePermissionSettings();
return [];
}
const usage = await getDailyUsage();
return usage.sort(
(a, b) => b.totalTimeInForeground - a.totalTimeInForeground
);
}Complete React Native Example
This example renders today's app usage with app icons and formatted screen time.
import React, { useEffect, useState } from 'react';
import {
ActivityIndicator,
FlatList,
Image,
SafeAreaView,
Text,
TouchableOpacity,
View,
} from 'react-native';
import {
getDailyUsage,
hasUsagePermission,
openUsagePermissionSettings,
} from '@sahil_sensei/react-native-app-usage';
import type { AppUsageStat } from '@sahil_sensei/react-native-app-usage';
function formatDuration(ms: number) {
const totalMinutes = Math.floor(ms / 60000);
const hours = Math.floor(totalMinutes / 60);
const minutes = totalMinutes % 60;
if (hours === 0) {
return `${minutes}m`;
}
return `${hours}h ${minutes}m`;
}
export default function AppUsageScreen() {
const [loading, setLoading] = useState(true);
const [hasPermission, setHasPermission] = useState(false);
const [apps, setApps] = useState<AppUsageStat[]>([]);
async function load() {
setLoading(true);
try {
const permission = await hasUsagePermission();
setHasPermission(permission);
if (!permission) {
setApps([]);
return;
}
const usage = await getDailyUsage();
const sortedUsage = usage.sort(
(a, b) => b.totalTimeInForeground - a.totalTimeInForeground
);
setApps(sortedUsage);
} finally {
setLoading(false);
}
}
useEffect(() => {
load();
}, []);
if (loading) {
return <ActivityIndicator />;
}
if (!hasPermission) {
return (
<SafeAreaView style={{ flex: 1, padding: 24, justifyContent: 'center' }}>
<Text style={{ fontSize: 20, fontWeight: '700', marginBottom: 8 }}>
Usage access required
</Text>
<Text style={{ marginBottom: 16 }}>
Enable Usage Access so the app can read screen time data.
</Text>
<TouchableOpacity onPress={openUsagePermissionSettings}>
<Text style={{ color: '#2563eb', fontWeight: '700' }}>
Open Settings
</Text>
</TouchableOpacity>
</SafeAreaView>
);
}
return (
<SafeAreaView style={{ flex: 1 }}>
<FlatList
data={apps}
keyExtractor={(item) => item.packageName}
contentContainerStyle={{ padding: 16 }}
renderItem={({ item }) => (
<View
style={{
flexDirection: 'row',
alignItems: 'center',
paddingVertical: 12,
borderBottomWidth: 1,
borderBottomColor: '#e5e7eb',
}}
>
<Image
source={{ uri: item.icon }}
style={{ width: 44, height: 44, borderRadius: 10, marginRight: 12 }}
/>
<View style={{ flex: 1 }}>
<Text style={{ fontSize: 16, fontWeight: '700' }}>
{item.name}
</Text>
<Text style={{ color: '#6b7280' }}>{item.packageName}</Text>
</View>
<Text style={{ fontWeight: '700' }}>
{formatDuration(item.totalTimeInForeground)}
</Text>
</View>
)}
/>
</SafeAreaView>
);
}API
hasUsagePermission()
Checks whether the app has Android Usage Access permission.
const hasPermission: boolean = await hasUsagePermission();openUsagePermissionSettings()
Opens the Android Usage Access settings screen.
await openUsagePermissionSettings();getInstalledApps(includeSystemApps?)
Returns installed apps with metadata and icons.
const apps = await getInstalledApps();
const allApps = await getInstalledApps(true);getDailyUsage()
Returns app usage for the last day.
const usage = await getDailyUsage();getWeeklyUsage()
Returns app usage for the last week.
const usage = await getWeeklyUsage();getMonthlyUsage()
Returns app usage for the last month.
const usage = await getMonthlyUsage();getHourlyUsage(packageName, date?)
Returns a 24-hour breakdown for one app. If date is not provided, today's date is used.
const hourly = await getHourlyUsage('com.instagram.android');
const yesterday = await getHourlyUsage(
'com.instagram.android',
new Date('2026-05-05')
);Types
interface AppInfo {
packageName: string;
name: string;
uid: string;
icon: string;
}
interface AppUsageStat extends AppInfo {
totalTimeInForeground: number;
lastTimeUsed: number;
launchCount: number;
}
interface HourlyUsage {
hour: number;
durationMs: number;
opens: number;
}Example Response
[
{
packageName: 'com.instagram.android',
name: 'Instagram',
uid: '10245',
icon: 'data:image/png;base64,iVBORw0KGgo...',
totalTimeInForeground: 5400000,
lastTimeUsed: 1778063400000,
launchCount: 12,
},
];totalTimeInForeground and durationMs are returned in milliseconds. lastTimeUsed is a Unix timestamp in milliseconds.
Notes
- This package is Android only.
- Always check permission before calling usage APIs.
- App icons are returned as base64 data URIs and can be used directly with
<Image source={{ uri: icon }} />. - If you publish your app on Google Play, review Google's policy for
QUERY_ALL_PACKAGESbefore release.
License
MIT
Made with create-react-native-library
