Skip to content

Commit 161e230

Browse files
committed
feat: enhanced app detail - background state (RUN_IN/ANY), AOSP info button, cleaner UI
1 parent 467fcc3 commit 161e230

File tree

3 files changed

+109
-99
lines changed

3 files changed

+109
-99
lines changed

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

Lines changed: 63 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ import com.appcontrolx.model.AppInfo
1818
import com.appcontrolx.model.ExecutionMode
1919
import com.appcontrolx.rollback.ActionLog
2020
import com.appcontrolx.rollback.RollbackManager
21-
import com.appcontrolx.service.BackgroundStatus
21+
2222
import com.appcontrolx.service.BatteryPolicyManager
2323
import com.appcontrolx.service.PermissionBridge
2424
import com.appcontrolx.utils.SafetyValidator
@@ -28,9 +28,7 @@ import kotlinx.coroutines.Dispatchers
2828
import kotlinx.coroutines.launch
2929
import kotlinx.coroutines.withContext
3030
import java.io.File
31-
import java.text.SimpleDateFormat
32-
import java.util.Date
33-
import java.util.Locale
31+
3432

3533
class AppDetailBottomSheet : BottomSheetDialogFragment() {
3634

@@ -42,7 +40,7 @@ class AppDetailBottomSheet : BottomSheetDialogFragment() {
4240
private var policyManager: BatteryPolicyManager? = null
4341
private var rollbackManager: RollbackManager? = null
4442
private var executionMode: ExecutionMode = ExecutionMode.None
45-
private var currentBgStatus: BackgroundStatus = BackgroundStatus.DEFAULT
43+
4644

4745
var onActionCompleted: (() -> Unit)? = null
4846

@@ -132,17 +130,6 @@ class AppDetailBottomSheet : BottomSheetDialogFragment() {
132130
binding.tvMinSdk.text = getString(R.string.detail_sdk_format,
133131
packageInfo.applicationInfo.minSdkVersion)
134132

135-
// Dates
136-
val dateFormat = SimpleDateFormat("MMM dd, yyyy HH:mm", Locale.getDefault())
137-
binding.tvInstallDate.text = dateFormat.format(Date(packageInfo.firstInstallTime))
138-
binding.tvUpdateDate.text = dateFormat.format(Date(packageInfo.lastUpdateTime))
139-
140-
// Status
141-
binding.tvStatus.text = if (isEnabled) getString(R.string.status_enabled)
142-
else getString(R.string.status_disabled)
143-
binding.tvStatus.setTextColor(resources.getColor(
144-
if (isEnabled) R.color.status_positive else R.color.status_negative, null))
145-
146133
// Permissions count
147134
val permCount = packageInfo.requestedPermissions?.size ?: 0
148135
binding.tvPermissions.text = getString(R.string.detail_permissions_count, permCount)
@@ -170,34 +157,46 @@ class AppDetailBottomSheet : BottomSheetDialogFragment() {
170157
val packageName = arguments?.getString(ARG_PACKAGE_NAME) ?: return
171158

172159
lifecycleScope.launch {
173-
currentBgStatus = withContext(Dispatchers.IO) {
174-
policyManager?.getBackgroundStatus(packageName) ?: BackgroundStatus.DEFAULT
160+
// Load detailed background state via root commands
161+
val (runInBg, runAnyInBg) = withContext(Dispatchers.IO) {
162+
if (executor != null) {
163+
val runInBgResult = executor!!.execute("appops get $packageName RUN_IN_BACKGROUND")
164+
val runAnyInBgResult = executor!!.execute("appops get $packageName RUN_ANY_IN_BACKGROUND")
165+
Pair(
166+
parseAppOpsOutput(runInBgResult.getOrDefault("")),
167+
parseAppOpsOutput(runAnyInBgResult.getOrDefault(""))
168+
)
169+
} else {
170+
Pair("N/A", "N/A")
171+
}
175172
}
176173

177-
updateBatteryStatusUI()
174+
updateBatteryStatusUI(runInBg, runAnyInBg)
178175
}
179176
}
180177

181-
private fun updateBatteryStatusUI() {
182-
val (text, color) = when (currentBgStatus) {
183-
BackgroundStatus.RESTRICTED -> Pair(
184-
getString(R.string.status_restricted),
185-
R.color.status_negative
186-
)
187-
BackgroundStatus.ALLOWED -> Pair(
188-
getString(R.string.status_allowed),
189-
R.color.status_positive
190-
)
191-
BackgroundStatus.DEFAULT -> Pair(
192-
getString(R.string.status_default),
193-
R.color.status_neutral
194-
)
178+
private fun parseAppOpsOutput(output: String): String {
179+
return when {
180+
output.contains("ignore") -> "ignore"
181+
output.contains("deny") -> "deny"
182+
output.contains("allow") -> "allow"
183+
output.contains("default") -> "default"
184+
else -> "unknown"
195185
}
186+
}
187+
188+
private fun updateBatteryStatusUI(runInBg: String, runAnyInBg: String) {
189+
// RUN_IN_BACKGROUND
190+
binding.tvRunInBg.text = runInBg
191+
binding.tvRunInBg.setTextColor(resources.getColor(
192+
if (runInBg == "ignore" || runInBg == "deny") R.color.status_negative
193+
else R.color.status_positive, null))
196194

197-
binding.tvBatteryStatus.text = text
198-
binding.tvBatteryStatus.setTextColor(resources.getColor(color, null))
199-
200-
// Both buttons always visible - no hide/show
195+
// RUN_ANY_IN_BACKGROUND
196+
binding.tvRunAnyInBg.text = runAnyInBg
197+
binding.tvRunAnyInBg.setTextColor(resources.getColor(
198+
if (runAnyInBg == "ignore" || runAnyInBg == "deny") R.color.status_negative
199+
else R.color.status_positive, null))
201200
}
202201

203202
private fun setupButtons() {
@@ -277,6 +276,7 @@ class AppDetailBottomSheet : BottomSheetDialogFragment() {
277276

278277
binding.btnLaunchApp.setOnClickListener { launchApp() }
279278
binding.btnOpenSettings.setOnClickListener { openAppSettings() }
279+
binding.btnAospInfo.setOnClickListener { openAospAppInfo() }
280280
}
281281

282282
private fun launchApp() {
@@ -382,12 +382,8 @@ class AppDetailBottomSheet : BottomSheetDialogFragment() {
382382
}
383383

384384
if (success) {
385-
// Refresh background status
386-
val packageName = appInfo?.packageName ?: return@launch
387-
currentBgStatus = withContext(Dispatchers.IO) {
388-
policyManager?.getBackgroundStatus(packageName) ?: BackgroundStatus.DEFAULT
389-
}
390-
updateBatteryStatusUI()
385+
// Refresh background status display
386+
loadBatteryStatus()
391387

392388
binding.tvActionStatus.text = getString(R.string.action_completed, actionName)
393389
binding.tvActionStatus.setTextColor(resources.getColor(R.color.status_positive, null))
@@ -418,6 +414,7 @@ class AppDetailBottomSheet : BottomSheetDialogFragment() {
418414
binding.btnUninstall.isEnabled = enabled
419415
binding.btnLaunchApp.isEnabled = enabled
420416
binding.btnOpenSettings.isEnabled = enabled
417+
binding.btnAospInfo.isEnabled = enabled
421418
}
422419

423420
private fun setButtonEnabled(button: MaterialButton, enabled: Boolean) {
@@ -436,6 +433,29 @@ class AppDetailBottomSheet : BottomSheetDialogFragment() {
436433
}
437434
}
438435

436+
private fun openAospAppInfo() {
437+
val packageName = appInfo?.packageName ?: return
438+
try {
439+
// Use explicit AOSP Settings intent that won't be overridden by custom ROMs
440+
val intent = Intent().apply {
441+
setClassName("com.android.settings", "com.android.settings.applications.InstalledAppDetails")
442+
putExtra("package", packageName)
443+
putExtra("com.android.settings.ApplicationPkgName", packageName)
444+
addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
445+
}
446+
startActivity(intent)
447+
} catch (e: Exception) {
448+
// Fallback to standard settings
449+
try {
450+
startActivity(Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS).apply {
451+
data = Uri.parse("package:$packageName")
452+
})
453+
} catch (e2: Exception) {
454+
Toast.makeText(context, R.string.error_open_settings, Toast.LENGTH_SHORT).show()
455+
}
456+
}
457+
}
458+
439459
private fun formatFileSize(size: Long): String = when {
440460
size >= 1024 * 1024 * 1024 -> String.format("%.2f GB", size / (1024.0 * 1024 * 1024))
441461
size >= 1024 * 1024 -> String.format("%.2f MB", size / (1024.0 * 1024))

app/src/main/res/layout/bottom_sheet_app_detail.xml

Lines changed: 44 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -112,46 +112,62 @@
112112
android:orientation="vertical"
113113
android:padding="16dp">
114114

115-
<!-- Status Row -->
115+
<!-- Background State Section -->
116+
<TextView
117+
android:layout_width="wrap_content"
118+
android:layout_height="wrap_content"
119+
android:text="@string/detail_bg_state"
120+
android:textStyle="bold"
121+
android:textSize="13sp"
122+
android:textColor="@color/primary" />
123+
124+
<!-- RUN_IN_BACKGROUND -->
116125
<LinearLayout
117126
android:layout_width="match_parent"
118127
android:layout_height="wrap_content"
119128
android:orientation="horizontal"
120-
android:paddingVertical="6dp">
129+
android:paddingVertical="4dp"
130+
android:layout_marginTop="8dp">
121131
<TextView
122132
android:layout_width="0dp"
123133
android:layout_height="wrap_content"
124134
android:layout_weight="1"
125-
android:text="@string/detail_status"
126-
android:textColor="@color/on_surface_secondary" />
135+
android:text="RUN_IN_BACKGROUND"
136+
android:textColor="@color/on_surface_secondary"
137+
android:textSize="12sp"
138+
android:fontFamily="monospace" />
127139
<TextView
128-
android:id="@+id/tvStatus"
140+
android:id="@+id/tvRunInBg"
129141
android:layout_width="wrap_content"
130142
android:layout_height="wrap_content"
131-
android:textStyle="bold" />
143+
android:textStyle="bold"
144+
android:textSize="12sp" />
132145
</LinearLayout>
133146

134-
<!-- Battery Status Row -->
147+
<!-- RUN_ANY_IN_BACKGROUND -->
135148
<LinearLayout
136149
android:layout_width="match_parent"
137150
android:layout_height="wrap_content"
138151
android:orientation="horizontal"
139-
android:paddingVertical="6dp">
152+
android:paddingVertical="4dp">
140153
<TextView
141154
android:layout_width="0dp"
142155
android:layout_height="wrap_content"
143156
android:layout_weight="1"
144-
android:text="@string/detail_battery"
145-
android:textColor="@color/on_surface_secondary" />
157+
android:text="RUN_ANY_IN_BACKGROUND"
158+
android:textColor="@color/on_surface_secondary"
159+
android:textSize="12sp"
160+
android:fontFamily="monospace" />
146161
<TextView
147-
android:id="@+id/tvBatteryStatus"
162+
android:id="@+id/tvRunAnyInBg"
148163
android:layout_width="wrap_content"
149164
android:layout_height="wrap_content"
150-
android:textStyle="bold" />
165+
android:textStyle="bold"
166+
android:textSize="12sp" />
151167
</LinearLayout>
152168

153169
<View android:layout_width="match_parent" android:layout_height="1dp"
154-
android:background="@color/outline" android:layout_marginVertical="8dp" />
170+
android:background="@color/outline" android:layout_marginVertical="12dp" />
155171

156172
<!-- App Size -->
157173
<LinearLayout
@@ -242,47 +258,6 @@
242258
android:textSize="11sp"
243259
android:fontFamily="monospace"
244260
android:textIsSelectable="true" />
245-
246-
<View android:layout_width="match_parent" android:layout_height="1dp"
247-
android:background="@color/outline" android:layout_marginVertical="8dp" />
248-
249-
<!-- Installed -->
250-
<LinearLayout
251-
android:layout_width="match_parent"
252-
android:layout_height="wrap_content"
253-
android:orientation="horizontal"
254-
android:paddingVertical="6dp">
255-
<TextView
256-
android:layout_width="0dp"
257-
android:layout_height="wrap_content"
258-
android:layout_weight="1"
259-
android:text="@string/detail_installed"
260-
android:textColor="@color/on_surface_secondary" />
261-
<TextView
262-
android:id="@+id/tvInstallDate"
263-
android:layout_width="wrap_content"
264-
android:layout_height="wrap_content"
265-
android:textSize="12sp" />
266-
</LinearLayout>
267-
268-
<!-- Updated -->
269-
<LinearLayout
270-
android:layout_width="match_parent"
271-
android:layout_height="wrap_content"
272-
android:orientation="horizontal"
273-
android:paddingVertical="6dp">
274-
<TextView
275-
android:layout_width="0dp"
276-
android:layout_height="wrap_content"
277-
android:layout_weight="1"
278-
android:text="@string/detail_updated"
279-
android:textColor="@color/on_surface_secondary" />
280-
<TextView
281-
android:id="@+id/tvUpdateDate"
282-
android:layout_width="wrap_content"
283-
android:layout_height="wrap_content"
284-
android:textSize="12sp" />
285-
</LinearLayout>
286261
</LinearLayout>
287262
</com.google.android.material.card.MaterialCardView>
288263

@@ -295,7 +270,7 @@
295270
android:textStyle="bold"
296271
android:textSize="14sp" />
297272

298-
<!-- Actions Grid - 3 columns, ordered by safety -->
273+
<!-- Actions Grid - 3 columns -->
299274
<GridLayout
300275
android:layout_width="match_parent"
301276
android:layout_height="wrap_content"
@@ -387,7 +362,7 @@
387362
app:iconTint="@color/status_negative"
388363
style="@style/Widget.Material3.Button.TonalButton" />
389364

390-
<!-- Row 3: Restrict BG, Allow BG -->
365+
<!-- Row 3: Restrict BG, Allow BG, AOSP Info -->
391366
<com.google.android.material.button.MaterialButton
392367
android:id="@+id/btnRestrictBg"
393368
android:layout_width="0dp"
@@ -414,5 +389,18 @@
414389
app:iconPadding="2dp"
415390
style="@style/Widget.Material3.Button.TonalButton" />
416391

392+
<com.google.android.material.button.MaterialButton
393+
android:id="@+id/btnAospInfo"
394+
android:layout_width="0dp"
395+
android:layout_height="wrap_content"
396+
android:layout_columnWeight="1"
397+
android:text="@string/action_aosp_info"
398+
android:textSize="10sp"
399+
app:icon="@drawable/ic_settings"
400+
app:iconSize="20dp"
401+
app:iconGravity="top"
402+
app:iconPadding="2dp"
403+
style="@style/Widget.Material3.Button.TonalButton" />
404+
417405
</GridLayout>
418406
</LinearLayout>

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

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -187,6 +187,8 @@
187187
<string name="detail_system_app">System App</string>
188188
<string name="detail_user_app">User App</string>
189189
<string name="detail_quick_actions">Quick Actions</string>
190+
<string name="detail_bg_state">Background State</string>
191+
<string name="action_aosp_info">AOSP Info</string>
190192
<string name="detail_open_settings">Open in System Settings</string>
191193
<string name="action_disable">Disable</string>
192194
<string name="action_enable">Enable</string>

0 commit comments

Comments
 (0)