Skip to content

Commit 3d6c516

Browse files
committed
Fix: PermissionsFragment checklist UI, What's New dialog, ExecutionModeValidator, string resources
1 parent 4e63b65 commit 3d6c516

File tree

6 files changed

+135
-22
lines changed

6 files changed

+135
-22
lines changed

.gitignore

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ ehthumbs.db
5151
Desktop.ini
5252

5353
# Proposal docs (internal only)
54-
app-control-x-proposal/
54+
.app-control-x-proposal/
5555

5656
# Kiro IDE (local steering rules)
5757
.kiro/
Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
package com.appcontrolx.service
2+
3+
import android.content.Context
4+
import com.appcontrolx.data.local.SettingsDataStore
5+
import com.appcontrolx.utils.Constants
6+
import kotlinx.coroutines.flow.first
7+
import javax.inject.Inject
8+
import javax.inject.Singleton
9+
10+
@Singleton
11+
class ExecutionModeValidator @Inject constructor(
12+
private val context: Context,
13+
private val permissionBridge: PermissionBridge,
14+
private val settingsDataStore: SettingsDataStore
15+
) {
16+
17+
/**
18+
* Validate current execution mode and fallback to view-only if needed
19+
*/
20+
suspend fun validateAndFallback(): ValidationResult {
21+
val currentMode = settingsDataStore.executionMode.first()
22+
23+
return when (currentMode) {
24+
Constants.MODE_ROOT -> {
25+
if (permissionBridge.isRootAvailable()) {
26+
ValidationResult.Valid(currentMode)
27+
} else {
28+
settingsDataStore.setExecutionMode(Constants.MODE_NONE)
29+
ValidationResult.Fallback(Constants.MODE_NONE, "Root access is no longer available")
30+
}
31+
}
32+
33+
Constants.MODE_SHIZUKU -> {
34+
if (permissionBridge.isShizukuReady()) {
35+
ValidationResult.Valid(currentMode)
36+
} else {
37+
settingsDataStore.setExecutionMode(Constants.MODE_NONE)
38+
ValidationResult.Fallback(Constants.MODE_NONE, "Shizuku is no longer available")
39+
}
40+
}
41+
42+
else -> ValidationResult.Valid(currentMode)
43+
}
44+
}
45+
46+
/**
47+
* Check if we can switch to the requested mode
48+
*/
49+
suspend fun canSwitchTo(mode: String): SwitchResult {
50+
return when (mode) {
51+
Constants.MODE_ROOT -> {
52+
if (permissionBridge.checkRootNow()) {
53+
SwitchResult.Success
54+
} else {
55+
SwitchResult.Failed("Root access denied or not available")
56+
}
57+
}
58+
59+
Constants.MODE_SHIZUKU -> {
60+
when {
61+
!permissionBridge.isShizukuAvailable() -> {
62+
SwitchResult.Failed("Shizuku is not installed or running")
63+
}
64+
!permissionBridge.isShizukuPermissionGranted() -> {
65+
SwitchResult.Failed("Shizuku permission not granted")
66+
}
67+
else -> SwitchResult.Success
68+
}
69+
}
70+
71+
Constants.MODE_NONE -> SwitchResult.Success
72+
73+
else -> SwitchResult.Failed("Unknown execution mode")
74+
}
75+
}
76+
77+
sealed class ValidationResult {
78+
data class Valid(val mode: String) : ValidationResult()
79+
data class Fallback(val newMode: String, val reason: String) : ValidationResult()
80+
}
81+
82+
sealed class SwitchResult {
83+
object Success : SwitchResult()
84+
data class Failed(val reason: String) : SwitchResult()
85+
}
86+
}

app/src/main/java/com/appcontrolx/ui/MainActivity.kt

Lines changed: 12 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package com.appcontrolx.ui
22

33
import android.os.Bundle
4+
import android.util.Log
45
import androidx.appcompat.app.AppCompatActivity
56
import androidx.navigation.fragment.NavHostFragment
67
import androidx.navigation.ui.setupWithNavController
@@ -10,7 +11,6 @@ import com.appcontrolx.R
1011
import com.appcontrolx.databinding.ActivityMainBinding
1112
import com.google.android.material.dialog.MaterialAlertDialogBuilder
1213
import dagger.hilt.android.AndroidEntryPoint
13-
import timber.log.Timber
1414

1515
@AndroidEntryPoint
1616
class MainActivity : AppCompatActivity() {
@@ -22,7 +22,7 @@ class MainActivity : AppCompatActivity() {
2222
binding = ActivityMainBinding.inflate(layoutInflater)
2323
setContentView(binding.root)
2424

25-
Timber.d("MainActivity created")
25+
Log.d("MainActivity", "MainActivity created")
2626
setupNavigation()
2727
showWhatsNewIfNeeded()
2828
}
@@ -48,15 +48,16 @@ class MainActivity : AppCompatActivity() {
4848

4949
private fun showWhatsNewDialog() {
5050
val whatsNew = """
51-
|• MVVM Architecture with Hilt DI
52-
|• Crash reporting with Firebase
53-
|• Optimized release build
54-
|• Smart app caching
55-
|• Status badges
56-
|• Tools tab with hidden settings
57-
|• Activity Launcher
58-
|• Batch operations
59-
|• Enhanced security
51+
|🏗️ MVVM Architecture + Hilt DI
52+
|🔥 Firebase Crashlytics integration
53+
|⚡ Optimized release build (ProGuard)
54+
|📊 Beautiful About page with stats
55+
|🚀 Activity Launcher with expandable groups
56+
|🎯 Enhanced batch operations
57+
|🔒 Runtime root/shizuku validation
58+
|🛡️ Enhanced security & input validation
59+
|🎨 Dark/Light theme toggle
60+
|✨ Clean UI improvements
6061
""".trimMargin()
6162

6263
MaterialAlertDialogBuilder(this)

app/src/main/java/com/appcontrolx/ui/setup/PermissionsFragment.kt

Lines changed: 32 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -112,26 +112,37 @@ class PermissionsFragment : Fragment() {
112112

113113
if (hasRoot) {
114114
selectMode(Constants.MODE_ROOT)
115+
binding.btnCheckRoot.text = getString(R.string.status_access_granted)
116+
binding.btnCheckRoot.isEnabled = false // Keep disabled when granted
115117
Toast.makeText(context, R.string.root_granted, Toast.LENGTH_SHORT).show()
116118
} else {
119+
binding.btnCheckRoot.isEnabled = true
120+
binding.btnCheckRoot.text = getString(R.string.btn_check)
117121
Toast.makeText(context, R.string.root_denied, Toast.LENGTH_SHORT).show()
118122
}
119123

120-
binding.btnCheckRoot.isEnabled = true
121-
binding.btnCheckRoot.text = getString(R.string.btn_check)
122124
updateUI()
123125
}
124126
}
125127

126128
private fun checkShizukuAccess() {
129+
binding.btnCheckShizuku.isEnabled = false
130+
binding.btnCheckShizuku.text = getString(R.string.btn_checking)
131+
127132
if (permissionBridge.isShizukuAvailable()) {
128133
if (permissionBridge.isShizukuPermissionGranted()) {
129134
shizukuGranted = true
130135
selectMode(Constants.MODE_SHIZUKU)
136+
binding.btnCheckShizuku.text = getString(R.string.status_access_granted)
137+
// Keep disabled when granted
131138
} else {
132139
permissionBridge.requestShizukuPermission()
140+
binding.btnCheckShizuku.isEnabled = true
141+
binding.btnCheckShizuku.text = getString(R.string.btn_check)
133142
}
134143
} else {
144+
binding.btnCheckShizuku.isEnabled = true
145+
binding.btnCheckShizuku.text = getString(R.string.btn_check)
135146
Toast.makeText(context, R.string.error_shizuku_not_available, Toast.LENGTH_SHORT).show()
136147
}
137148
updateUI()
@@ -201,14 +212,20 @@ class PermissionsFragment : Fragment() {
201212
binding.tvQueryAppsStatus.setTextColor(resources.getColor(R.color.status_positive, null))
202213
binding.btnQueryApps.visibility = View.GONE
203214

204-
// Root status
215+
// Root status & button
205216
binding.tvRootStatus.text = if (rootGranted)
206217
getString(R.string.status_granted) else getString(R.string.status_not_granted)
207218
binding.tvRootStatus.setTextColor(resources.getColor(
208219
if (rootGranted) R.color.status_positive else R.color.status_neutral, null))
209-
binding.ivRootCheck.visibility = if (binding.cardRoot.isChecked) View.VISIBLE else View.GONE
210220

211-
// Shizuku status
221+
// Root: show check icon on right when selected, update button text
222+
binding.ivRootCheck.visibility = if (rootGranted && binding.cardRoot.isChecked) View.VISIBLE else View.GONE
223+
if (rootGranted) {
224+
binding.btnCheckRoot.text = getString(R.string.status_access_granted)
225+
binding.btnCheckRoot.isEnabled = false
226+
}
227+
228+
// Shizuku status & button
212229
val shizukuAvailable = permissionBridge.isShizukuAvailable()
213230
binding.tvShizukuStatus.text = when {
214231
shizukuGranted -> getString(R.string.status_granted)
@@ -217,9 +234,15 @@ class PermissionsFragment : Fragment() {
217234
}
218235
binding.tvShizukuStatus.setTextColor(resources.getColor(
219236
if (shizukuGranted) R.color.status_positive else R.color.status_neutral, null))
220-
binding.ivShizukuCheck.visibility = if (binding.cardShizuku.isChecked) View.VISIBLE else View.GONE
221237

222-
// View only check
238+
// Shizuku: show check icon on right when selected, update button text
239+
binding.ivShizukuCheck.visibility = if (shizukuGranted && binding.cardShizuku.isChecked) View.VISIBLE else View.GONE
240+
if (shizukuGranted) {
241+
binding.btnCheckShizuku.text = getString(R.string.status_access_granted)
242+
binding.btnCheckShizuku.isEnabled = false
243+
}
244+
245+
// View only: always show check when selected (no permission needed)
223246
binding.ivViewOnlyCheck.visibility = if (binding.cardViewOnly.isChecked) View.VISIBLE else View.GONE
224247

225248
// Continue button - enabled if a mode is selected
@@ -229,7 +252,8 @@ class PermissionsFragment : Fragment() {
229252

230253
private fun setupContinueButton() {
231254
binding.btnContinue.setOnClickListener {
232-
(activity as? SetupActivity)?.completeSetup()
255+
// Go to Disclaimer page (next step)
256+
(activity as? SetupActivity)?.nextStep()
233257
}
234258
}
235259

app/src/main/java/com/appcontrolx/ui/setup/SetupActivity.kt

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -37,9 +37,10 @@ class SetupActivity : AppCompatActivity() {
3737
}
3838

3939
private fun setupViewPager() {
40+
// Order: Permissions first, then Disclaimer (warning)
4041
val fragments = listOf<Fragment>(
41-
DisclaimerFragment(),
42-
PermissionsFragment()
42+
PermissionsFragment(),
43+
DisclaimerFragment()
4344
)
4445

4546
binding.viewPager.adapter = SetupPagerAdapter(this, fragments)

app/src/main/res/values/strings.xml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,7 @@
7676
<string name="btn_start">Start</string>
7777
<string name="btn_check">Check</string>
7878
<string name="btn_checking">Checking...</string>
79+
<string name="status_access_granted">Access Granted</string>
7980
<string name="btn_get">Get</string>
8081
<string name="btn_grant">Grant</string>
8182
<string name="root_granted">Root access granted!</string>

0 commit comments

Comments
 (0)