Skip to content

Commit e07506c

Browse files
committed
Stabilize core workflows and bump version 2.0
1 parent b05cc36 commit e07506c

6 files changed

Lines changed: 54 additions & 21 deletions

File tree

app/build.gradle.kts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,8 +27,8 @@ android {
2727
applicationId = "com.saltatoryimpulse.scrolliosis"
2828
minSdk = 26 // Supports 95%+ of Android devices
2929
targetSdk = 35
30-
versionCode = 1
31-
versionName = "1.0"
30+
versionCode = 2
31+
versionName = "2.0"
3232
}
3333

3434
signingConfigs {

app/src/main/java/com/saltatoryimpulse/scrolliosis/Constants.kt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ object Constants {
1414

1515
const val UNLOCK_DURATION_MS = 5 * 60 * 1000L
1616
const val SERVICE_START_GRACE_MS = 8_000L
17+
const val SETUP_GRACE_DURATION_MS = 3 * 60 * 1000L
1718
const val FOREGROUND_POLL_INTERVAL_MS = 150L
1819
const val BLOCK_INTERCEPT_DEBOUNCE_MS = 350L
1920
const val SERVICE_REVIVE_DELAY_MS = 1_000L

app/src/main/java/com/saltatoryimpulse/scrolliosis/GateService.kt

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -144,25 +144,24 @@ class GateService : AccessibilityService(), KoinComponent {
144144
return false
145145
}
146146

147-
if (pkg.contains("packageinstaller") || pkg.contains("permissioncontroller")) {
148-
return true
147+
if (PermissionUtils.isSetupGraceActive(this)) {
148+
return false
149149
}
150150

151-
if (pkg.contains("settings") || pkg.contains("securitycenter") || pkg.contains("safecenter") ||
152-
pkg.contains("systemmanager") || pkg.contains("seccontainer")) {
151+
if (pkg.contains("packageinstaller") || pkg.contains("permissioncontroller")) {
153152
return true
154153
}
155154

155+
val appPackageName = packageName()
156156
val fastText = event.text.toString().lowercase()
157157
val targetsScrolliosis = fastText.contains("scrolliosis") ||
158-
fastText.contains(packageName.lowercase()) ||
159-
fastText.contains(packageName())
158+
fastText.contains(appPackageName.lowercase())
160159

161160
if (targetsScrolliosis) return true
162161

163162
val rootNode = rootInActiveWindow ?: return false
164163
return try {
165-
scanNodeForAnyText(rootNode, listOf("scrolliosis", packageName(), packageName.lowercase()))
164+
scanNodeForAnyText(rootNode, listOf("scrolliosis", appPackageName.lowercase()))
166165
} catch (e: Exception) {
167166
Log.w(Constants.TAG, "Error scanning accessibility node tree", e)
168167
false

app/src/main/java/com/saltatoryimpulse/scrolliosis/MainActivity.kt

Lines changed: 15 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,14 @@ class MainActivity : ComponentActivity(), KoinComponent {
3030
// PERSISTENT ENFORCEMENT STATE: Isolated from the NavHost backstack
3131
private var isSystemLocked = mutableStateOf(false)
3232

33+
private fun requiresOnboarding(): Boolean {
34+
val hasAccessibility = PermissionUtils.isAccessibilityServiceEnabled(this)
35+
val hasOverlay = PermissionUtils.canDrawOverlays(this)
36+
val hasNotifications = PermissionUtils.hasNotificationPermission(this)
37+
val hasUsageAccess = PermissionUtils.hasUsageAccess(this)
38+
return !hasAccessibility || !hasOverlay || !hasNotifications || !hasUsageAccess
39+
}
40+
3341
override fun onCreate(savedInstanceState: Bundle?) {
3442
super.onCreate(savedInstanceState)
3543

@@ -49,11 +57,7 @@ class MainActivity : ComponentActivity(), KoinComponent {
4957
navController = currentNavController
5058

5159
// Check required permissions (accessibility, overlay, notifications, usage access)
52-
val hasAccessibility = PermissionUtils.isAccessibilityServiceEnabled(this)
53-
val hasOverlay = PermissionUtils.canDrawOverlays(this)
54-
val hasNotifications = PermissionUtils.hasNotificationPermission(this)
55-
val hasUsageAccess = PermissionUtils.hasUsageAccess(this)
56-
val needsOnboarding = !hasAccessibility || !hasOverlay || !hasNotifications || !hasUsageAccess
60+
val needsOnboarding = requiresOnboarding()
5761

5862
Surface(
5963
modifier = Modifier.fillMaxSize(),
@@ -94,9 +98,8 @@ class MainActivity : ComponentActivity(), KoinComponent {
9498
needsNotifications = !PermissionUtils.hasNotificationPermission(this@MainActivity),
9599
needsUsageAccess = !PermissionUtils.hasUsageAccess(this@MainActivity),
96100
onCheckPermissions = {
97-
if (PermissionUtils.isAccessibilityServiceEnabled(this@MainActivity) &&
98-
PermissionUtils.canDrawOverlays(this@MainActivity) &&
99-
PermissionUtils.hasUsageAccess(this@MainActivity)) {
101+
if (!requiresOnboarding()) {
102+
PermissionUtils.clearSetupGrace(this@MainActivity)
100103
currentNavController.navigate("home") {
101104
popUpTo("onboarding") { inclusive = true }
102105
}
@@ -143,6 +146,9 @@ class MainActivity : ComponentActivity(), KoinComponent {
143146
override fun onResume() {
144147
super.onResume()
145148
overlayController.removeBlockingShield()
149+
if (!requiresOnboarding()) {
150+
PermissionUtils.clearSetupGrace(this)
151+
}
146152
lifecycleScope.launch {
147153
installedAppCatalog.refreshCatalog()
148154
}
@@ -192,10 +198,7 @@ class MainActivity : ComponentActivity(), KoinComponent {
192198
setIntent(intent)
193199
overlayController.removeBlockingShield()
194200

195-
val hasAccessibility = PermissionUtils.isAccessibilityServiceEnabled(this)
196-
val hasOverlay = PermissionUtils.canDrawOverlays(this)
197-
val hasUsageAccess = PermissionUtils.hasUsageAccess(this)
198-
val needsOnboarding = !hasAccessibility || !hasOverlay || !hasUsageAccess
201+
val needsOnboarding = requiresOnboarding()
199202

200203
handleRouting(intent, navController, needsOnboarding)
201204
}

app/src/main/java/com/saltatoryimpulse/scrolliosis/PermissionsUtils.kt

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,8 @@ import androidx.core.content.ContextCompat
1818
* Engineered for 100% accuracy across AOSP and OEM-modified Android skins.
1919
*/
2020
object PermissionUtils {
21+
private const val PREFS_NAME = "scrolliosis_runtime"
22+
private const val KEY_SETUP_GRACE_UNTIL = "setup_grace_until"
2123

2224
/**
2325
* ACCESSIBILITY HEALTH CHECK:
@@ -113,6 +115,28 @@ object PermissionUtils {
113115
return mode == AppOpsManager.MODE_ALLOWED
114116
}
115117

118+
fun extendSetupGrace(context: Context, durationMs: Long = Constants.SETUP_GRACE_DURATION_MS) {
119+
val prefs = context.applicationContext.getSharedPreferences(PREFS_NAME, Context.MODE_PRIVATE)
120+
val currentValue = prefs.getLong(KEY_SETUP_GRACE_UNTIL, 0L)
121+
val extendedUntil = maxOf(currentValue, System.currentTimeMillis() + durationMs)
122+
prefs.edit().putLong(KEY_SETUP_GRACE_UNTIL, extendedUntil).apply()
123+
}
124+
125+
fun clearSetupGrace(context: Context) {
126+
context.applicationContext
127+
.getSharedPreferences(PREFS_NAME, Context.MODE_PRIVATE)
128+
.edit()
129+
.remove(KEY_SETUP_GRACE_UNTIL)
130+
.apply()
131+
}
132+
133+
fun isSetupGraceActive(context: Context): Boolean {
134+
val graceUntil = context.applicationContext
135+
.getSharedPreferences(PREFS_NAME, Context.MODE_PRIVATE)
136+
.getLong(KEY_SETUP_GRACE_UNTIL, 0L)
137+
return graceUntil > System.currentTimeMillis()
138+
}
139+
116140
/**
117141
* Returns the most recent foreground package we can infer from UsageStats.
118142
* This acts as a fallback path when Accessibility events are delayed or skipped.

app/src/main/java/com/saltatoryimpulse/scrolliosis/ui/screens/OnboardingScreen.kt

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,7 @@ fun OnboardingScreen(
7777
try {
7878
installedAppCatalog.primeInstalledApps()
7979
} finally {
80+
PermissionUtils.clearSetupGrace(context)
8081
isPrimingInstalledApps = false
8182
hasStartedSetup = false
8283
onCheckPermissions()
@@ -106,6 +107,7 @@ fun OnboardingScreen(
106107
isNotifsNeeded && !notifAttempted -> { currentDialog = SetupStep.NONE }
107108
// STEP 2: STANDARD BATTERY (Whitelist)
108109
isBatteryNeeded -> {
110+
PermissionUtils.extendSetupGrace(context)
109111
val intent = Intent(Settings.ACTION_REQUEST_IGNORE_BATTERY_OPTIMIZATIONS).apply {
110112
data = Uri.parse("package:${context.packageName}")
111113
}
@@ -119,6 +121,7 @@ fun OnboardingScreen(
119121
// STEP 3: USAGE ACCESS (Fallback foreground detection)
120122
isUsageAccessNeeded -> {
121123
try {
124+
PermissionUtils.extendSetupGrace(context)
122125
context.startActivity(Intent(Settings.ACTION_USAGE_ACCESS_SETTINGS))
123126
} catch (e: Exception) {
124127
currentDialog = SetupStep.NONE
@@ -243,6 +246,7 @@ fun OnboardingScreen(
243246
confirmText = "GRANT AUTHORITY",
244247
onConfirm = {
245248
currentDialog = SetupStep.NONE
249+
PermissionUtils.extendSetupGrace(context)
246250
context.startActivity(Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION, Uri.parse("package:${context.packageName}")))
247251
},
248252
onDismiss = {
@@ -259,6 +263,7 @@ fun OnboardingScreen(
259263
confirmText = "OPEN SETTINGS",
260264
onConfirm = {
261265
currentDialog = SetupStep.NONE
266+
PermissionUtils.extendSetupGrace(context)
262267
context.startActivity(Intent(Settings.ACTION_ACCESSIBILITY_SETTINGS))
263268
},
264269
onDismiss = {
@@ -276,6 +281,7 @@ fun OnboardingScreen(
276281
onConfirm = {
277282
currentDialog = SetupStep.NONE
278283
manufacturerStepAttempted = true
284+
PermissionUtils.extendSetupGrace(context)
279285
ManufacturerUtils.openPowerManagementSettings(context)
280286
},
281287
onDismiss = {

0 commit comments

Comments
 (0)