expo-app-blocker 0.1.57 → 0.1.59

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.
@@ -21,14 +21,22 @@ class AppBlockerService : Service() {
21
21
  private val handler = Handler(Looper.getMainLooper())
22
22
  private var lastForegroundPackage: String? = null
23
23
  private lateinit var overlayManager: OverlayManager
24
+ @Volatile private var paused = false
25
+
26
+ private val resumeRunnable = Runnable {
27
+ Log.d(TAG, "Temporary unlock expired, resuming blocking")
28
+ paused = false
29
+ }
24
30
 
25
31
  private val pollRunnable = object : Runnable {
26
32
  override fun run() {
27
- val foregroundPackage = getCurrentForegroundPackage()
28
- if (foregroundPackage != null && foregroundPackage != lastForegroundPackage) {
29
- Log.d(TAG, "Foreground changed: $foregroundPackage")
30
- lastForegroundPackage = foregroundPackage
31
- handleForegroundChange(foregroundPackage)
33
+ if (!paused) {
34
+ val foregroundPackage = getCurrentForegroundPackage()
35
+ if (foregroundPackage != null && foregroundPackage != lastForegroundPackage) {
36
+ Log.d(TAG, "Foreground changed: $foregroundPackage")
37
+ lastForegroundPackage = foregroundPackage
38
+ handleForegroundChange(foregroundPackage)
39
+ }
32
40
  }
33
41
  handler.postDelayed(this, POLL_INTERVAL_MS)
34
42
  }
@@ -115,7 +123,17 @@ class AppBlockerService : Service() {
115
123
  }
116
124
 
117
125
  override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
118
- Log.d(TAG, "AppBlockerService onStartCommand")
126
+ val action = intent?.action
127
+ if (action == ACTION_TEMPORARY_UNLOCK) {
128
+ val minutes = intent.getIntExtra(EXTRA_DURATION_MINUTES, 0)
129
+ if (minutes > 0) {
130
+ Log.d(TAG, "Temporary unlock for $minutes minutes")
131
+ handler.removeCallbacks(resumeRunnable)
132
+ paused = true
133
+ overlayManager.hide()
134
+ handler.postDelayed(resumeRunnable, minutes * 60_000L)
135
+ }
136
+ }
119
137
  return START_STICKY
120
138
  }
121
139
 
@@ -181,6 +199,8 @@ class AppBlockerService : Service() {
181
199
  private const val BLOCKED_NOTIFICATION_ID = 9002
182
200
  private const val POLL_INTERVAL_MS = 500L
183
201
  private const val LOOKBACK_WINDOW_MS = 10_000L
202
+ private const val ACTION_TEMPORARY_UNLOCK = "expo.modules.appblocker.TEMPORARY_UNLOCK"
203
+ private const val EXTRA_DURATION_MINUTES = "duration_minutes"
184
204
 
185
205
  fun start(context: Context) {
186
206
  val intent = Intent(context, AppBlockerService::class.java)
@@ -195,5 +215,17 @@ class AppBlockerService : Service() {
195
215
  val intent = Intent(context, AppBlockerService::class.java)
196
216
  context.stopService(intent)
197
217
  }
218
+
219
+ fun temporaryUnlock(context: Context, durationMinutes: Int) {
220
+ val intent = Intent(context, AppBlockerService::class.java).apply {
221
+ action = ACTION_TEMPORARY_UNLOCK
222
+ putExtra(EXTRA_DURATION_MINUTES, durationMinutes)
223
+ }
224
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
225
+ context.startForegroundService(intent)
226
+ } else {
227
+ context.startService(intent)
228
+ }
229
+ }
198
230
  }
199
231
  }
@@ -113,6 +113,11 @@ class ExpoAppBlockerModule : Module() {
113
113
  Log.d(TAG, "stopMonitoring called")
114
114
  }
115
115
 
116
+ Function("temporaryUnlockAndroid") { durationMinutes: Int ->
117
+ AppBlockerService.temporaryUnlock(context, durationMinutes)
118
+ Log.d(TAG, "temporaryUnlockAndroid: $durationMinutes minutes")
119
+ }
120
+
116
121
  AsyncFunction("getInstalledApps") {
117
122
  val pm = context.packageManager
118
123
  val intent = Intent(Intent.ACTION_MAIN).apply {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "expo-app-blocker",
3
- "version": "0.1.57",
3
+ "version": "0.1.59",
4
4
  "description": "Expo module for cross-platform app blocking. Android: UsageStatsManager + Overlay. iOS: Screen Time API (FamilyControls + ManagedSettings + DeviceActivity).",
5
5
  "main": "src/index.ts",
6
6
  "types": "src/index.ts",
package/src/index.ts CHANGED
@@ -169,8 +169,9 @@ export function isAppBlocked(bundleIdentifier: string): boolean {
169
169
  // ──────────────────────────────────────────────────────────────────────────────
170
170
 
171
171
  export async function temporaryUnlock(durationMinutes: number = 15): Promise<TemporaryUnlockResult> {
172
- if (Platform.OS !== "ios") {
173
- throw new Error("Temporary unlock is only available on iOS");
172
+ if (Platform.OS === "android") {
173
+ NativeModule.temporaryUnlockAndroid(Math.max(1, Math.round(durationMinutes)));
174
+ return { unlocked: true, expiresAt: Date.now() + durationMinutes * 60_000 };
174
175
  }
175
176
  return NativeModule.temporaryUnlock(durationMinutes);
176
177
  }