
Security News
Axios Maintainer Confirms Social Engineering Attack Behind npm Compromise
Axios compromise traced to social engineering, showing how attacks on maintainers can bypass controls and expose the broader software supply chain.
@breakr/react-native-device-activity-android
Advanced tools
React Native Device Activity for Android - Screen blocking functionality using Accessibility Service
React Native library for Android that provides screen blocking functionality similar to Apple's DeviceActivity APIs. This library uses Android's Accessibility Service, Overlay permissions, and Usage Access to monitor and block apps during focus sessions.
# Using yarn
yarn add @breakr/react-native-device-activity-android
# Using npm
npm install @breakr/react-native-device-activity-android
Add the plugin to your app.json:
{
"expo": {
"plugins": ["@breakr/react-native-device-activity-android"]
}
}
Run prebuild to generate native Android code:
expo prebuild -p android
expo run:android
This library requires four Android permissions:
import DeviceActivityAndroid from '@breakr/react-native-device-activity-android'
// Check current status
const status = await DeviceActivityAndroid.getPermissionsStatus()
console.log(status)
// {
// accessibilityEnabled: false,
// overlayEnabled: false,
// usageAccessEnabled: false,
// scheduleExactAlarmEnabled: false
// }
// Open settings to grant permissions
await DeviceActivityAndroid.requestAccessibilityPermission()
await DeviceActivityAndroid.requestOverlayPermission()
await DeviceActivityAndroid.requestUsageAccessPermission()
Important: These permissions require the user to manually enable them in Android Settings. Your app should guide users through this process.
import DeviceActivityAndroid from '@breakr/react-native-device-activity-android'
// Start a focus session blocking Instagram and Twitter for 5 minutes
await DeviceActivityAndroid.startSession(
{
id: 'focus-session',
blockedPackages: ['com.instagram.android', 'com.twitter.android'],
endsAt: Date.now() + 5 * 60 * 1000, // 5 minutes from now
},
{
title: 'Stay Focused',
message: 'This app is blocked during your focus session.',
ctaText: 'Return to Focus',
}
)
// Stop the session
await DeviceActivityAndroid.stopSession('focus-session')
type SessionConfig = {
id: string // Unique session identifier
blockedPackages: string[] // Package IDs to block
allowPackages?: string[] // Whitelist mode (if provided, only these apps allowed)
startsAt?: number // Start time in ms since epoch (default: now)
endsAt?: number // End time in ms since epoch (default: indefinite)
reason?: string // Optional reason for the session
}
The library provides convenient methods for common blocking scenarios:
// Block all installed apps for 1 hour
await DeviceActivityAndroid.blockAllApps(
'deep-focus-session',
Date.now() + 60 * 60 * 1000,
{
title: 'Deep Focus Mode',
message: 'All apps are blocked for 1 hour.',
ctaText: 'Go Back',
}
)
const status = await DeviceActivityAndroid.getBlockStatus()
console.log(`Blocking: ${status.isBlocking}`)
console.log(`Active sessions: ${status.activeSessionCount}`)
console.log(`Session IDs: ${status.activeSessions.join(', ')}`)
console.log(`Current app: ${status.currentForegroundApp}`)
// Pause blocking for 60 seconds, then automatically resume
await DeviceActivityAndroid.temporaryUnblock(60)
// Listen for when blocking resumes
const subscription = DeviceActivityAndroid.addListener(event => {
if (event.type === 'temporary_unblock_ended') {
console.log('Blocking has resumed!')
subscription.remove()
}
})
// Block all apps for 5 minutes (300 seconds)
await DeviceActivityAndroid.temporaryBlock(300, {
title: 'Quick Focus',
subtitle: 'Taking a 5 minute break from apps',
primaryButtonLabel: 'Dismiss'
})
// Session automatically expires after duration
// Listen for when session expires
const subscription = DeviceActivityAndroid.addListener(event => {
if (event.type === 'session_expired') {
console.log('Temporary block ended:', event.sessionId)
subscription.remove()
}
})
// Stop all active blocking sessions
await DeviceActivityAndroid.unblockAllApps()
The library uses a combination of Android system APIs to detect and block apps:
UsageStatsManager Polling: Continuously monitors the foreground app using UsageStatsManager API, which provides reliable app detection without extensive accessibility permissions abuse.
Accessibility Service: Required to display system-level overlays and provide the accessibility context needed for blocking functionality.
Window Overlays: When a blocked app is detected, the library displays a full-screen overlay with customizable messaging to prevent access.
The blocking mechanism includes:
const subscription = DeviceActivityAndroid.addListener(event => {
switch (event.type) {
case 'block_shown':
console.log('Block shown for session:', event.sessionId)
break
case 'block_dismissed':
console.log('User dismissed block')
break
case 'app_attempt':
console.log('User tried to open:', event.packageName)
break
case 'service_state':
console.log('Service running:', event.running)
break
case 'temporary_unblock_ended':
console.log('Temporary unblock period has ended, blocking resumed')
break
}
})
// Clean up when done
subscription.remove()
// Get list of all installed user-facing apps
const apps = await DeviceActivityAndroid.getInstalledApps(true) // true = include icons
// Filter and display apps
apps.forEach(app => {
console.log(`${app.name} (${app.packageName})`)
if (app.icon) {
// icon is base64-encoded PNG, ready for <Image source={{ uri: `data:image/png;base64,${app.icon}` }} />
}
})
Note: This method filters out system apps and returns only apps with launcher activities. It includes updated system apps (like pre-installed apps that have been updated via Play Store).
You can display custom icons on the blocking overlay instead of the default emoji. This is useful for branding your focus/wellbeing app.
Add your icon asset to your project (e.g., assets/robot-head.png)
Create a constants file to manage versioning:
// constants.ts
export const ICON_VERSION_NUMBER = 1
export const ICON_ASSET_PATH = './assets/robot-head.png'
export const DEFAULT_ICON_SIZE = 64 // in dp
// utils/iconHelper.ts
import { NativeModules } from 'react-native'
import { ICON_ASSET_PATH, ICON_VERSION_NUMBER } from '../constants'
const { RNDeviceActivityAndroid } = NativeModules
export async function ensureCustomIconCached(): Promise<string | null> {
try {
const cachedPath = await RNDeviceActivityAndroid.ensureIconCached(
ICON_ASSET_PATH,
ICON_VERSION_NUMBER
)
if (cachedPath) {
console.log('Icon cached successfully:', cachedPath)
return cachedPath
}
return null
} catch (error) {
console.error('Error caching custom icon:', error)
return null
}
}
import DeviceActivityAndroid from '@breakr/react-native-device-activity-android'
import { ensureCustomIconCached } from './utils/iconHelper'
import { ICON_ASSET_PATH, DEFAULT_ICON_SIZE } from './constants'
// Cache the icon when app starts
useEffect(() => {
ensureCustomIconCached()
}, [])
// Use in shield style
await DeviceActivityAndroid.startSession(
{
id: 'focus-session',
blockedPackages: ['com.instagram.android'],
endsAt: Date.now() + 30 * 60 * 1000,
},
{
title: 'Stay Focused',
subtitle: 'This app is blocked during your focus session',
primaryImagePath: ICON_ASSET_PATH,
iconSize: DEFAULT_ICON_SIZE, // Size in dp (density-independent pixels)
backgroundColor: { red: 255, green: 253, blue: 249 },
}
)
The library uses a versioning system for icon caching:
./ prefix (e.g., ./assets/robot-head.png)breakr-icon-v{version}.png (e.g., breakr-icon-v1.png)/data/data/your.package/files/shield-icons/)The ensureIconCached() method:
null if the operation failsThe ShieldStyle type supports the following icon-related fields:
type ShieldStyle = {
// Icon configuration
primaryImagePath?: string // Path to custom icon image file
iconSize?: number // Icon size in dp (default: 64dp)
iconTint?: RGBColor // Optional tint color for the icon
// Deprecated fields (still supported for backwards compatibility)
iconSystemName?: string // System icon name (Android drawable resource)
// ... other style fields
}
Notes:
primaryImagePath is not provided or fails to load, the overlay shows a default emojigetPermissionsStatus(): Promise<PermissionsStatus> - Get current permission status for all required permissionsrequestAccessibilityPermission(): Promise<void> - Open system settings to enable Accessibility ServicerequestOverlayPermission(): Promise<void> - Open system settings to grant overlay permissionrequestUsageAccessPermission(): Promise<void> - Open system settings to grant usage access permissionstartSession(config: SessionConfig, style?: ShieldStyle, shieldId?: string): Promise<void> - Start a new blocking session with inline style or pre-configured shield IDupdateSession(config: Partial<SessionConfig> & { id: string }): Promise<void> - Update an existing session configurationstopSession(sessionId: string): Promise<void> - Stop a specific blocking session by IDstopAllSessions(): Promise<void> - Stop all active blocking sessionsblockAllApps(sessionId?: string, endsAt?: number, style?: ShieldStyle): Promise<void> - Block all installed user apps in one sessionunblockAllApps(): Promise<void> - Unblock all apps by stopping all sessions (alias for stopAllSessions)getBlockStatus(): Promise<BlockStatus> - Get current blocking status including active sessionstemporaryUnblock(durationSeconds: number): Promise<void> - Temporarily pause all blocking for N seconds, then auto-resumetemporaryBlock(durationSeconds: number, style?: ShieldStyle): Promise<void> - Block all apps for N seconds with automatic expirationgetCurrentForegroundApp(): Promise<ForegroundApp> - Get the current foreground app package name (best effort)getInstalledApps(includeIcons?: boolean): Promise<Array<{ packageName: string; name: string; category: number; icon?: string }>> - Get list of installed user-facing applications with optional icons and categoriesisServiceRunning(): Promise<boolean> - Check if the accessibility service is currently runninggetAppMetadataDebug(): Promise<Array<AppMetadata>> - DEBUG: Get comprehensive metadata for all installed apps (for debugging and development)configureShielding(configId: string, style: ShieldStyle): Promise<void> - Register a reusable shield configuration with a unique IDupdateShielding(configId: string, style: ShieldStyle): Promise<void> - Update an existing shield configurationremoveShielding(configId: string): Promise<boolean> - Remove a shield configuration by IDgetShieldingConfigurations(): Promise<{ [configId: string]: ShieldStyle }> - Get all registered shield configurationsensureIconCached(imagePath: string, version: number): Promise<string | null> - Copy custom icon from React Native assets to internal storage with versioning supportaddListener(callback: (event: BlockEvent) => void): { remove(): void } - Add a listener for block eventsEvent Types:
block_shown - Block overlay was shown for a sessionblock_dismissed - User dismissed the block overlay (primary button); redirects user back to your appsecondary_action - User tapped secondary button on block overlayapp_attempt - User attempted to open a blocked appservice_state - Accessibility service started or stoppedtemporary_unblock_ended - Temporary unblock period ended, blocking resumedsession_expired - A blocking session reached its end time and expiredNote: When the user taps the primary button (e.g., "Return to Focus"), they are automatically redirected back to your app. This helps guide users back to their focus/wellbeing app after attempting to access a blocked app.
See index.d.ts for complete type definitions.
IMPORTANT: This library uses Android Accessibility Services, which are subject to strict Google Play policies.
When submitting to Google Play, you MUST:
✅ Allowed:
❌ NOT Allowed:
See Google Play Accessibility Policy for details.
const running = await DeviceActivityAndroid.isServiceRunning()
if (!running) {
// Guide user to enable Accessibility Service
await DeviceActivityAndroid.requestAccessibilityPermission()
}
Check overlay permission:
const { overlayEnabled } = await DeviceActivityAndroid.getPermissionsStatus()
if (!overlayEnabled) {
await DeviceActivityAndroid.requestOverlayPermission()
}
For a comprehensive guide with detailed API examples and common use cases, see:
See the example app for a complete working implementation that demonstrates:
To run the example app:
cd apps/example
npx expo prebuild -p android
npx expo run:android
This package is part of a monorepo. To contribute:
yarn installcd apps/examplenpx expo prebuild -p android && npx expo run:androidpackages/device-activity-android/android/src/main/java/com/breakr/deviceactivity/packages/device-activity-android/src/index.tspackages/device-activity-android/index.d.tspackages/device-activity-android/plugin/app.plugin.jsAfter making native changes, rebuild the example app:
cd apps/example
npx expo prebuild -p android --clean
npx expo run:android
Initially, this library used AccessibilityEvent callbacks to detect app changes. However, this approach had reliability issues and raised concerns about excessive accessibility API usage for Play Store compliance.
The current implementation uses UsageStatsManager polling, which:
| Permission | Purpose | Request Method |
|---|---|---|
| Accessibility Service | Display system overlays, detect blocking context | requestAccessibilityPermission() |
| Draw Over Apps | Show blocking overlay on top of blocked apps | requestOverlayPermission() |
| Usage Access | Monitor foreground app via UsageStatsManager | requestUsageAccessPermission() |
| Schedule Exact Alarms | Enable temporary unblock feature with auto-resume | Granted automatically on install (Android 12+) |
All permissions must be granted by the user in system settings (except Schedule Exact Alarms, which is typically granted on install).
This library aims for API parity with react-native-device-activity for iOS. Key differences:
| Feature | iOS (DeviceActivity) | Android (This Package) |
|---|---|---|
| API Style | Shield configuration, Family Controls | Session-based blocking with style config |
| Permission Model | Screen Time API | Accessibility + Overlay + Usage Access |
| Enforcement | System-level, unbreakable | Overlay-based, can be dismissed (configurable) |
| App Detection | Native API | UsageStatsManager polling |
MIT
Contributions are welcome! This package will be open-sourced soon. Please ensure:
FAQs
React Native Device Activity for Android - Screen blocking functionality using Accessibility Service
We found that @breakr/react-native-device-activity-android demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago. It has 1 open source maintainer collaborating on the project.
Did you know?

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Security News
Axios compromise traced to social engineering, showing how attacks on maintainers can bypass controls and expose the broader software supply chain.

Security News
Node.js has paused its bug bounty program after funding ended, removing payouts for vulnerability reports but keeping its security process unchanged.

Security News
The Axios compromise shows how time-dependent dependency resolution makes exposure harder to detect and contain.