diff --git a/app/src/main/java/neth/iecal/curbox/anti_stimulants/GrayScaleFilter.kt b/app/src/main/java/neth/iecal/curbox/anti_stimulants/GrayScaleFilter.kt index 67abee0a..ef70424d 100644 --- a/app/src/main/java/neth/iecal/curbox/anti_stimulants/GrayScaleFilter.kt +++ b/app/src/main/java/neth/iecal/curbox/anti_stimulants/GrayScaleFilter.kt @@ -44,15 +44,12 @@ class GrayScaleFilter : BaseBlocker() { fun doGrayscaleCheck(event: AccessibilityEvent?) { if (event == null || (event.eventType and TARGET_EVENTS_MASK) == 0) return - val currentPackageName = event.packageName?.toString() ?: return + val primaryPackage = event.packageName?.toString() ?: "" + val visiblePackages = service.getVisiblePackages(event) - // Skip check if it's the same package or system UI or keyboard - // We don't skip Curbox here because we want to disable grayscale when user is in the app - if (currentPackageName == lastPackageName || - currentPackageName == "com.android.systemui" || - ignoredGrayScalePackages.contains(currentPackageName)) return - - lastPackageName = currentPackageName + if (primaryPackage.isNotEmpty() && primaryPackage != lastPackageName && primaryPackage != "com.android.systemui" && !ignoredGrayScalePackages.contains(primaryPackage)) { + lastPackageName = primaryPackage + } val now = Calendar.getInstance() val calDay = now.get(Calendar.DAY_OF_WEEK) @@ -61,8 +58,11 @@ class GrayScaleFilter : BaseBlocker() { val currentMinutes = now.get(Calendar.HOUR_OF_DAY) * 60 + now.get(Calendar.MINUTE) var shouldGrayscale = false + var packageGrayscaled = "" + + for (currentPackageName in visiblePackages) { + if (currentPackageName == "com.android.systemui" || ignoredGrayScalePackages.contains(currentPackageName)) continue - // Don't grayscale Curbox itself to ensure usability for (group in grayscaleGroups) { if (!group.isActive) continue @@ -76,22 +76,26 @@ class GrayScaleFilter : BaseBlocker() { if (intervals == null || intervals.isEmpty()) { shouldGrayscale = true + packageGrayscaled = currentPackageName break } else { val isInInterval = intervals.any { isWithinInterval(currentMinutes, it) } if (isInInterval) { shouldGrayscale = true + packageGrayscaled = currentPackageName break } } } } + if (shouldGrayscale) break + } if (shouldGrayscale) { - Log.d("GrayScaleFilter", "Enabling monochrome for $currentPackageName") + Log.d("GrayScaleFilter", "Enabling monochrome for $packageGrayscaled") grayscaleControl.enableGrayscale(service) } else { - Log.d("GrayScaleFilter", "Disabling monochrome for $currentPackageName") + Log.d("GrayScaleFilter", "Disabling monochrome") grayscaleControl.disableGrayscale(service) } } diff --git a/app/src/main/java/neth/iecal/curbox/blockers/AppBlocker.kt b/app/src/main/java/neth/iecal/curbox/blockers/AppBlocker.kt index 17e57f85..3cdbe7ad 100644 --- a/app/src/main/java/neth/iecal/curbox/blockers/AppBlocker.kt +++ b/app/src/main/java/neth/iecal/curbox/blockers/AppBlocker.kt @@ -91,93 +91,108 @@ class AppBlocker : BaseBlocker() { fun doAppBlockerCheck(event: AccessibilityEvent?) { if (event == null || (event.eventType and TARGET_EVENTS_MASK) == 0) return - val packageName = event.packageName?.toString() ?: return - - if (lastPackage == packageName || packageName == service.packageName || ignoredApps.contains(packageName)) { - return + val primaryPackage = event.packageName?.toString() ?: "" + val visiblePackages = service.getVisiblePackages(event) + + if (primaryPackage.isNotEmpty() && primaryPackage != lastPackage && primaryPackage != service.packageName && !ignoredApps.contains(primaryPackage)) { + clearFinishedOnEachOpenSessions(primaryPackage) + lastPackage = primaryPackage } - clearFinishedOnEachOpenSessions(packageName) - val now = System.currentTimeMillis() - - blockedAppsList[packageName]?.let { entries -> - for (entry in entries) { - if (!entry.warningConfig.isOnOpenConfig || - isGroupInCooldown(entry.groupId, now) - ) { - continue - } - val activeWindow = entry.config.schedule.activeWindow(now) - if (activeWindow == null) { - entry.config.schedule.nextChangeAfter(now)?.let { nextChange -> - setUpForcedRefreshChecker( - "schedule:${entry.groupId}:$packageName", - nextChange - ) + var minRemainingForAll = Long.MAX_VALUE + var anyBlocked = false + var packageBlocked = "" + + for (packageName in visiblePackages) { + if (packageName == service.packageName || ignoredApps.contains(packageName)) continue + + val entries = blockedAppsList[packageName] + if (entries != null) { + var isThisPackageBlocked = false + for (entry in entries) { + if (!entry.warningConfig.isOnOpenConfig || isGroupInCooldown(entry.groupId, now)) continue + val activeWindow = entry.config.schedule.activeWindow(now) + if (activeWindow == null) { + entry.config.schedule.nextChangeAfter(now)?.let { nextChange -> + setUpForcedRefreshChecker("schedule:${entry.groupId}:$packageName", nextChange) + } + continue } - continue + notificationManager.stopTimer() + showWarningScreen(packageName, entry.groupId, entry.warningConfig) + isThisPackageBlocked = true + anyBlocked = true + packageBlocked = packageName + break } + + if (isThisPackageBlocked) break + + var minRemainingForPackage = Long.MAX_VALUE + for (entry in entries) { + if (entry.warningConfig.isOnOpenConfig) continue + if (isGroupInCooldown(entry.groupId, now)) continue + val activeWindow = entry.config.schedule.activeWindow(now) + if (activeWindow == null) { + entry.config.schedule.nextChangeAfter(now)?.let { nextChange -> + setUpForcedRefreshChecker("schedule:${entry.groupId}:$packageName", nextChange) + } + continue + } - notificationManager.stopTimer() - showWarningScreen(packageName, entry.groupId, entry.warningConfig) - return - } - - var minRemaining = Long.MAX_VALUE - for (entry in entries) { - if (entry.warningConfig.isOnOpenConfig) continue - if (isGroupInCooldown(entry.groupId, now)) continue - val activeWindow = entry.config.schedule.activeWindow(now) - if (activeWindow == null) { - entry.config.schedule.nextChangeAfter(now)?.let { nextChange -> - setUpForcedRefreshChecker( - "schedule:${entry.groupId}:$packageName", - nextChange + val currentUsage = runBlocking { + usageStats.getForegroundUsageBetween( + entry.groupPackages.toSet(), + activeWindow.startMs, + minOf(now, activeWindow.endMs) ) } - continue + val usageLimitMillis = getUsageLimitForToday(entry.config.usage) * 60_000L + val remainingUsage = usageLimitMillis - currentUsage + + if (remainingUsage <= 0) { + notificationManager.stopTimer() + showWarningScreen(packageName, entry.groupId, entry.warningConfig) + isThisPackageBlocked = true + anyBlocked = true + packageBlocked = packageName + break + } + if (remainingUsage < minRemainingForPackage) minRemainingForPackage = remainingUsage + setUpForcedRefreshChecker("schedule:${entry.groupId}:$packageName", activeWindow.endMs) } - val currentUsage = runBlocking { - usageStats.getForegroundUsageBetween( - entry.groupPackages.toSet(), - activeWindow.startMs, - minOf(now, activeWindow.endMs) - ) - } - val usageLimitMillis = getUsageLimitForToday(entry.config.usage) * 60_000L - val remainingUsage = usageLimitMillis - currentUsage + if (isThisPackageBlocked) break - if (remainingUsage <= 0) { - notificationManager.stopTimer() - showWarningScreen(packageName, entry.groupId, entry.warningConfig) - return + if (minRemainingForPackage != Long.MAX_VALUE) { + if (minRemainingForPackage < minRemainingForAll) minRemainingForAll = minRemainingForPackage + if (packageName == primaryPackage) { + notificationManager.startTimer( + totalMillis = minRemainingForPackage, + timerId = packageName, + title = service.getString(R.string.notification_title_remaining_usage) + ) + } + setUpForcedRefreshChecker("usage:$packageName", System.currentTimeMillis() + minRemainingForPackage) } - if (remainingUsage < minRemaining) minRemaining = remainingUsage - setUpForcedRefreshChecker( - "schedule:${entry.groupId}:$packageName", - activeWindow.endMs - ) } + if (anyBlocked) break + } - if (minRemaining != Long.MAX_VALUE) { - notificationManager.startTimer( - totalMillis = minRemaining, - timerId = packageName, - title = service.getString(R.string.notification_title_remaining_usage) - ) - setUpForcedRefreshChecker("usage:$packageName", System.currentTimeMillis() + minRemaining) - } else { - showNextCooldownNotification() + if (anyBlocked) { + // Expand the PiP window if it's not the primary package + if (packageBlocked.isNotEmpty() && packageBlocked != primaryPackage) { + val launchIntent = service.packageManager.getLaunchIntentForPackage(packageBlocked) + launchIntent?.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK) + try { + service.startActivity(launchIntent) + } catch (_: Exception) {} } - lastPackage = packageName return } showNextCooldownNotification() - - lastPackage = packageName } /** diff --git a/app/src/main/java/neth/iecal/curbox/blockers/FocusModeBlocker.kt b/app/src/main/java/neth/iecal/curbox/blockers/FocusModeBlocker.kt index 42e97770..ee56d093 100644 --- a/app/src/main/java/neth/iecal/curbox/blockers/FocusModeBlocker.kt +++ b/app/src/main/java/neth/iecal/curbox/blockers/FocusModeBlocker.kt @@ -106,41 +106,57 @@ class FocusModeBlocker : BaseBlocker() { } fun doFocusModeCheck(event: AccessibilityEvent?) { - val packageName = event?.packageName?.toString() ?: return - if (packageName == service.packageName) return + val primaryPackage = event?.packageName?.toString() ?: "" + val visiblePackages = service.getVisiblePackages(event) + + if (primaryPackage == service.packageName) return if (!service.isDelayOver(1000)) return if (focusModeData != null) { - if (lastPackage != packageName) { - lastPackage = packageName + var packageBlocked = "" + for (packageName in visiblePackages) { + if (packageName == service.packageName) continue when (focusModeData!!.focusGroupData.blockMode) { FocusBlockMode.BLOCK_SELECTED -> { if (focusModeData!!.focusGroupData.packages.contains(packageName)) { - service.pressHome() - - Log.d("focus mode","home pressed $packageName") - return + packageBlocked = packageName + break } } FocusBlockMode.BLOCK_ALL_EXCEPT_SELECTED -> { - if (!focusModeData!!.focusGroupData.packages.contains(packageName)) { - service.pressHome() - Log.d("focus mode","home pressed $packageName") - - return + if (!focusModeData!!.focusGroupData.packages.contains(packageName) && packageName != "com.android.systemui" && !packageName.startsWith("com.miui")) { + packageBlocked = packageName + break } } } } + if (packageBlocked.isNotEmpty()) { + if (lastPackage != packageBlocked) { + lastPackage = packageBlocked + service.pressHome() + Log.d("focus mode","home pressed $packageBlocked") + + if (packageBlocked != primaryPackage) { + val launchIntent = service.packageManager.getLaunchIntentForPackage(packageBlocked) + launchIntent?.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK) + try { service.startActivity(launchIntent) } catch (_: Exception) {} + } + return + } + } else if (primaryPackage.isNotEmpty()) { + lastPackage = primaryPackage + } + if (focusModeData!!.focusGroupData.keywords.isNotEmpty() && - URL_BAR_ID_LIST.containsKey(packageName)) { + URL_BAR_ID_LIST.containsKey(primaryPackage)) { val now = System.currentTimeMillis() // Throttle website checks to every 400ms within the same app to preserve performance if (now - lastWebsiteCheckTime > 400) { lastWebsiteCheckTime = now - if (keywordBlocker.isFocusWebsiteBlocked(packageName, focusKeywordsPatterns, focusModeData!!.focusGroupData.blockMode)) { + if (keywordBlocker.isFocusWebsiteBlocked(primaryPackage, focusKeywordsPatterns, focusModeData!!.focusGroupData.blockMode)) { if (now - lastBlockTime > 1500) { service.pressBack() Log.d("focus mode","back pressed") diff --git a/app/src/main/java/neth/iecal/curbox/services/BaseBlockingService.kt b/app/src/main/java/neth/iecal/curbox/services/BaseBlockingService.kt index d3985282..92c7b945 100644 --- a/app/src/main/java/neth/iecal/curbox/services/BaseBlockingService.kt +++ b/app/src/main/java/neth/iecal/curbox/services/BaseBlockingService.kt @@ -143,4 +143,20 @@ open class BaseBlockingService : AccessibilityService() { lastBackPressTimeStamp = SystemClock.uptimeMillis() } + + fun getVisiblePackages(event: AccessibilityEvent?): Set { + val packages = mutableSetOf() + event?.packageName?.toString()?.let { packages.add(it) } + + try { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { + windows?.forEach { window -> + window.root?.packageName?.toString()?.let { packages.add(it) } + } + } + } catch (_: Exception) {} + + return packages + } } + diff --git a/app/src/main/res/xml/app_blocker_service_config.xml b/app/src/main/res/xml/app_blocker_service_config.xml index 236b23f9..28073db9 100644 --- a/app/src/main/res/xml/app_blocker_service_config.xml +++ b/app/src/main/res/xml/app_blocker_service_config.xml @@ -2,7 +2,7 @@ \ No newline at end of file