Skip to content

Commit cb10ca9

Browse files
committed
feat: implement batch action bottomsheet for multi-app operations
1 parent a3d29a5 commit cb10ca9

38 files changed

Lines changed: 2640 additions & 1462 deletions
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
package com.appcontrolx.data.model
2+
3+
/**
4+
* Status of an action executed on a single app.
5+
*/
6+
enum class ActionStatus {
7+
SUCCESS,
8+
FAILED,
9+
SKIPPED
10+
}
11+
12+
/**
13+
* Result of executing an action on a single app.
14+
* Used to track individual app results in batch operations.
15+
*/
16+
data class AppActionResult(
17+
val packageName: String,
18+
val appName: String,
19+
val status: ActionStatus,
20+
val errorMessage: String? = null
21+
)

app/src/main/java/com/appcontrolx/data/model/AppInfo.kt

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,11 @@ data class AppInfo(
3131
val installedTime: Long,
3232
val lastUpdateTime: Long,
3333
val size: Long = 0L,
34+
/**
35+
* The UID of the application, used for appops commands.
36+
* Requirements: 4.1, 4.2
37+
*/
38+
val uid: Int = 0,
3439
/**
3540
* The running state of the app detected via multi-method detection.
3641
* Requirements: 4.6

app/src/main/java/com/appcontrolx/data/model/AppListFilter.kt

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,15 @@
11
package com.appcontrolx.data.model
22

3+
/**
4+
* App type filter options (User/System/All).
5+
* Default is USER to show only user-installed apps.
6+
*/
7+
enum class AppTypeFilter(val displayName: String) {
8+
USER("User Apps"),
9+
SYSTEM("System Apps"),
10+
ALL("All Apps")
11+
}
12+
313
/**
414
* Filter type options for app list filtering.
515
* Requirements: 3.4
@@ -25,8 +35,11 @@ enum class SortType(val displayName: String) {
2535
/**
2636
* Data class representing the current filter and sort configuration for the app list.
2737
* Requirements: 3.4, 3.5
38+
*
39+
* Default appTypeFilter is USER to show only user apps initially.
2840
*/
2941
data class AppListFilter(
42+
val appTypeFilter: AppTypeFilter = AppTypeFilter.USER,
3043
val filterType: FilterType = FilterType.ALL,
3144
val sortType: SortType = SortType.NAME_ASC
3245
)
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
package com.appcontrolx.data.model
2+
3+
/**
4+
* Actions available for batch operations on multiple apps.
5+
* Used by ActionBottomSheet for batch execution.
6+
*/
7+
enum class BatchAction {
8+
FREEZE,
9+
UNFREEZE,
10+
FORCE_STOP,
11+
RESTRICT_BACKGROUND,
12+
ALLOW_BACKGROUND;
13+
14+
/**
15+
* Human-readable display name for UI
16+
*/
17+
val displayName: String
18+
get() = when (this) {
19+
FREEZE -> "Freeze"
20+
UNFREEZE -> "Unfreeze"
21+
FORCE_STOP -> "Force Stop"
22+
RESTRICT_BACKGROUND -> "Restrict Background"
23+
ALLOW_BACKGROUND -> "Allow Background"
24+
}
25+
26+
/**
27+
* Convert to AppAction for execution via AppControlManager
28+
*/
29+
fun toAppAction(): AppAction = when (this) {
30+
FREEZE -> AppAction.FREEZE
31+
UNFREEZE -> AppAction.UNFREEZE
32+
FORCE_STOP -> AppAction.FORCE_STOP
33+
RESTRICT_BACKGROUND -> AppAction.RESTRICT_BACKGROUND
34+
ALLOW_BACKGROUND -> AppAction.ALLOW_BACKGROUND
35+
}
36+
}
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
package com.appcontrolx.data.model
2+
3+
/**
4+
* Result of executing a batch action on multiple apps.
5+
* Contains the action performed, individual results, and summary counts.
6+
*/
7+
data class BatchExecutionResult(
8+
val action: BatchAction,
9+
val results: List<AppActionResult>,
10+
val successCount: Int = results.count { it.status == ActionStatus.SUCCESS },
11+
val failureCount: Int = results.count { it.status == ActionStatus.FAILED },
12+
val skippedCount: Int = results.count { it.status == ActionStatus.SKIPPED }
13+
) {
14+
/**
15+
* Total number of apps processed
16+
*/
17+
val totalCount: Int
18+
get() = results.size
19+
20+
/**
21+
* Whether all apps were processed successfully (no failures)
22+
*/
23+
val isFullSuccess: Boolean
24+
get() = failureCount == 0 && successCount > 0
25+
26+
/**
27+
* Whether any apps were skipped due to safety validation
28+
*/
29+
val hasSkippedApps: Boolean
30+
get() = skippedCount > 0
31+
}

app/src/main/java/com/appcontrolx/data/model/SystemInfo.kt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,8 @@ package com.appcontrolx.data.model
66
data class CpuInfo(
77
val usagePercent: Float,
88
val temperature: Float?, // Celsius, null if unavailable
9-
val cores: Int
9+
val cores: Int,
10+
val coreFrequencies: List<Long> = emptyList() // MHz per core
1011
)
1112

1213
/**

app/src/main/java/com/appcontrolx/di/ExecutorModule.kt

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@ import com.appcontrolx.domain.executor.ShizukuExecutor
99
import com.appcontrolx.domain.manager.ActionLogger
1010
import com.appcontrolx.domain.manager.AnimationScaleManager
1111
import com.appcontrolx.domain.manager.AnimationScaleManagerImpl
12+
import com.appcontrolx.domain.manager.AppControlManager
13+
import com.appcontrolx.domain.manager.BatteryManager
1214
import com.appcontrolx.domain.manager.DisplayManager
1315
import dagger.Module
1416
import dagger.Provides
@@ -107,4 +109,24 @@ object ExecutorModule {
107109
fun provideAnimationScaleManager(
108110
commandExecutor: CommandExecutor
109111
): AnimationScaleManager = AnimationScaleManagerImpl(commandExecutor)
112+
113+
/**
114+
* Provides AppControlManager for app control actions (freeze, unfreeze, force-stop, etc.).
115+
* Requirements: 2.1, 2.2, 2.3, 2.4
116+
*/
117+
@Provides
118+
@Singleton
119+
fun provideAppControlManager(
120+
commandExecutor: CommandExecutor
121+
): AppControlManager = AppControlManager(commandExecutor)
122+
123+
/**
124+
* Provides BatteryManager for background restriction control.
125+
* Requirements: 4.1, 4.2
126+
*/
127+
@Provides
128+
@Singleton
129+
fun provideBatteryManager(
130+
commandExecutor: CommandExecutor
131+
): BatteryManager = BatteryManager(commandExecutor)
110132
}

app/src/main/java/com/appcontrolx/domain/monitor/SystemMonitor.kt

Lines changed: 35 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -184,15 +184,48 @@ class SystemMonitor @Inject constructor(
184184
}
185185

186186
/**
187-
* Get complete CPU info including usage, temperature, and core count.
187+
* Get current frequency for each CPU core in MHz.
188+
* Reads from /sys/devices/system/cpu/cpuX/cpufreq/scaling_cur_freq.
189+
*
190+
* @return List of frequencies in MHz for each core, empty list if unavailable
191+
*/
192+
suspend fun getCoreFrequencies(): List<Long> = withContext(Dispatchers.IO) {
193+
val frequencies = mutableListOf<Long>()
194+
val cpuDir = File("/sys/devices/system/cpu/")
195+
196+
var coreIndex = 0
197+
while (true) {
198+
val freqFile = File(cpuDir, "cpu$coreIndex/cpufreq/scaling_cur_freq")
199+
if (!freqFile.exists()) break
200+
201+
try {
202+
if (freqFile.canRead()) {
203+
val freqKhz = freqFile.readText().trim().toLongOrNull() ?: 0L
204+
// Convert kHz to MHz
205+
frequencies.add(freqKhz / 1000)
206+
} else {
207+
frequencies.add(0L)
208+
}
209+
} catch (e: Exception) {
210+
frequencies.add(0L)
211+
}
212+
coreIndex++
213+
}
214+
215+
frequencies
216+
}
217+
218+
/**
219+
* Get complete CPU info including usage, temperature, core count, and frequencies.
188220
*
189221
* @return CpuInfo data class
190222
*/
191223
suspend fun getCpuInfo(): CpuInfo {
192224
return CpuInfo(
193225
usagePercent = getCpuUsage(),
194226
temperature = getCpuTemperature(),
195-
cores = getCpuCores()
227+
cores = getCpuCores(),
228+
coreFrequencies = getCoreFrequencies()
196229
)
197230
}
198231

app/src/main/java/com/appcontrolx/domain/scanner/AppScanner.kt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -252,6 +252,7 @@ class AppScanner @Inject constructor(
252252
installedTime = pkg.firstInstallTime,
253253
lastUpdateTime = pkg.lastUpdateTime,
254254
size = getAppSize(appInfo),
255+
uid = appInfo.uid,
255256
runningState = runningState
256257
)
257258
} catch (e: Exception) {

0 commit comments

Comments
 (0)