Skip to content

Commit 2f58cd1

Browse files
committed
refactor: decouple CategoryHeaderItem from WorkspaceIntent and implement WorkspaceDependencyAnalyzerContent for enhanced dependency management
1 parent c7737a8 commit 2f58cd1

19 files changed

Lines changed: 1227 additions & 435 deletions

‎composeApp/src/jvmMain/kotlin/com/meet/dev/analyzer/data/models/workspace/WorkspaceUsageAnalysis.kt‎

Lines changed: 97 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,57 @@ enum class ResourceCategory(
7272
)
7373
}
7474

75+
enum class ResourceGroup(
76+
val displayName: String,
77+
val description: String
78+
) {
79+
EMULATOR(
80+
"Emulators & System Images",
81+
"Configured Android virtual devices and downloaded system images used to run them."
82+
),
83+
84+
SDK_TOOLING(
85+
"Android SDK & Build Tools",
86+
"Android SDK platforms, build-tools, sources, NDKs, and CMake compiler versions."
87+
),
88+
89+
GRADLE(
90+
"Gradle Caches & Wrappers",
91+
"Gradle wrapper versions, downloaded dependency caches, build caches, daemons, and toolchain JDKs."
92+
),
93+
94+
KOTLIN(
95+
"Kotlin Native Compilers",
96+
"Cached Kotlin Native compiler binaries used for multiplatform projects."
97+
)
98+
}
99+
100+
val ResourceCategory.group: ResourceGroup
101+
get() = when (this) {
102+
103+
ResourceCategory.ANDROID_AVD,
104+
ResourceCategory.ANDROID_SYSTEM_IMAGE ->
105+
ResourceGroup.EMULATOR
106+
107+
ResourceCategory.ANDROID_SDK_PLATFORM,
108+
ResourceCategory.ANDROID_BUILD_TOOLS,
109+
ResourceCategory.ANDROID_NDK,
110+
ResourceCategory.ANDROID_CMAKE,
111+
ResourceCategory.ANDROID_SDK_SOURCES ->
112+
ResourceGroup.SDK_TOOLING
113+
114+
ResourceCategory.GRADLE_WRAPPER,
115+
ResourceCategory.GRADLE_DEPENDENCY_CACHE,
116+
ResourceCategory.GRADLE_DAEMON,
117+
ResourceCategory.GRADLE_BUILD_CACHE,
118+
ResourceCategory.GRADLE_TRANSFORMS_CACHE,
119+
ResourceCategory.GRADLE_TEMP_FILES,
120+
ResourceCategory.GRADLE_JDK ->
121+
ResourceGroup.GRADLE
122+
123+
ResourceCategory.KOTLIN_NATIVE ->
124+
ResourceGroup.KOTLIN
125+
}
75126
data class UnusedResourceItem(
76127
val uniqueId: String = Uuid.random().toString(),
77128
val name: String,
@@ -84,8 +135,53 @@ data class UnusedResourceItem(
84135
val usedByProjects: List<String> = emptyList()
85136
)
86137

138+
data class WorkspaceDependencyInfo(
139+
val id: String, // e.g. "com.squareup.okhttp3:okhttp"
140+
val group: String, // e.g. "com.squareup.okhttp3"
141+
val artifact: String, // e.g. "okhttp"
142+
val versionsInUse: Map<String, List<String>> // Map of VersionString -> List of ProjectNames using it
143+
)
144+
145+
data class WorkspacePluginInfo(
146+
val id: String, // e.g. "com.android.application"
147+
val versionsInUse: Map<String, List<String>> // Map of VersionString -> List of ProjectNames using it
148+
)
149+
87150
data class WorkspaceAnalysisResult(
88151
val projects: List<ProjectOverviewInfo>,
89152
val unusedResources: List<UnusedResourceItem>,
90-
val activeResources: List<UnusedResourceItem>
153+
val activeResources: List<UnusedResourceItem>,
154+
val workspaceDependencies: List<WorkspaceDependencyInfo> = emptyList(),
155+
val workspacePlugins: List<WorkspacePluginInfo> = emptyList()
91156
)
157+
158+
enum class CleanupSafety(val label: String) {
159+
HIGH("Recommended"),
160+
MEDIUM("Safe"),
161+
CAUTION("Caution")
162+
}
163+
164+
val ResourceCategory.safety: CleanupSafety
165+
get() = when (this) {
166+
167+
ResourceCategory.GRADLE_TEMP_FILES,
168+
ResourceCategory.GRADLE_DAEMON ->
169+
CleanupSafety.HIGH
170+
171+
ResourceCategory.GRADLE_BUILD_CACHE,
172+
ResourceCategory.GRADLE_TRANSFORMS_CACHE,
173+
ResourceCategory.GRADLE_DEPENDENCY_CACHE,
174+
ResourceCategory.GRADLE_WRAPPER,
175+
ResourceCategory.ANDROID_SDK_SOURCES,
176+
ResourceCategory.KOTLIN_NATIVE,
177+
ResourceCategory.ANDROID_SDK_PLATFORM,
178+
ResourceCategory.ANDROID_BUILD_TOOLS,
179+
ResourceCategory.ANDROID_NDK,
180+
ResourceCategory.ANDROID_CMAKE ->
181+
CleanupSafety.MEDIUM
182+
183+
ResourceCategory.GRADLE_JDK,
184+
ResourceCategory.ANDROID_SYSTEM_IMAGE,
185+
ResourceCategory.ANDROID_AVD ->
186+
CleanupSafety.CAUTION
187+
}

‎composeApp/src/jvmMain/kotlin/com/meet/dev/analyzer/data/repository/project/helpers/DependencyAnalyzer.kt‎

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,9 @@ class DependencyAnalyzer {
5252

5353
AppLogger.d(tag = tag) { "Finding dependencies" }
5454

55+
val versionResolver = GradleVersionResolver()
56+
versionResolver.collectExtVariables(moduleBuildFileInfos)
57+
5558
// normal dependencies → implementation("group:artifact:version") , implementation "group:artifact:version" , implementation 'group:artifact:version'
5659
val normalDepRegex = Regex(
5760
"""(implementation|api|ksp|kapt|compileOnly|runtimeOnly|testImplementation|androidTestImplementation)\s*\(?["']([^"':]+):([^"':]+):([^"']+)["']\)?"""
@@ -86,7 +89,7 @@ class DependencyAnalyzer {
8689
val type = match.groupValues[1] // implementation
8790
val group = match.groupValues[2] // com.google.android.material
8891
val artifact = match.groupValues[3] // material
89-
val version = match.groupValues[4] // 1.11.0
92+
val version = versionResolver.resolve(match.groupValues[4]) // Resolve version
9093

9194
val availableGradleVersions = findAvailableVersionsInGradleCache(
9295
groupId = group,

‎composeApp/src/jvmMain/kotlin/com/meet/dev/analyzer/data/repository/project/helpers/GradleVersionResolver.kt‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -31,10 +31,10 @@ class GradleVersionResolver {
3131

3232
// Handle $variable or ${variable}
3333
if (version.contains("$")) {
34-
var resolved = version
34+
var resolved: String = version
3535
val varRegex = Regex("""\$([a-zA-Z0-9_]+)|\$\{([a-zA-Z0-9_]+)\}""")
3636
varRegex.findAll(version).forEach { match ->
37-
val varName = match.groupValues[1].ifEmpty { match.groupValues[2] }
37+
val varName = if (match.groupValues[1].isNotEmpty()) match.groupValues[1] else match.groupValues[2]
3838
extVars[varName]?.let {
3939
resolved = resolved.replace(match.value, it)
4040
}

‎composeApp/src/jvmMain/kotlin/com/meet/dev/analyzer/data/repository/project/helpers/PluginAnalyzer.kt‎

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,9 @@ class PluginAnalyzer {
3636
AppLogger.d(tag = tag) { "Finding plugins" }
3737

3838
val plugins = arrayListOf<Plugin>()
39+
40+
val versionResolver = GradleVersionResolver()
41+
versionResolver.collectExtVariables(moduleBuildFileInfos)
3942

4043
// Regex list
4144
val regexList = listOf(
@@ -61,7 +64,7 @@ class PluginAnalyzer {
6164
match.groupValues[1] // ex: com.google.gms.google-services
6265
val groupId = id.substringBeforeLast(".") // ex com.google.gms
6366
val artifactId = id.substringAfterLast('.') // ex google-services
64-
val version = match.groupValues[2] // ex: 8.3.2
67+
val version = versionResolver.resolve(match.groupValues[2]) ?: match.groupValues[2] // ex: 8.3.2
6568

6669
val availableGradleVersions = findAvailableVersionsInGradleCache(
6770
groupId = groupId,
@@ -90,7 +93,7 @@ class PluginAnalyzer {
9093
// Case: classpath 'com.android.tools.build:gradle:8.0.2'
9194
regexList[2].pattern -> {
9295
val id = match.groupValues[1] // ex: com.android.tools.build:gradle
93-
val version = match.groupValues[2] // ex: 8.3.2
96+
val version = versionResolver.resolve(match.groupValues[2]) ?: match.groupValues[2] // ex: 8.3.2
9497
val groupId = id.substringBeforeLast(":") // ex com.android.tools.build
9598
val artifactId = id.substringAfterLast(':') // ex gradle
9699

‎composeApp/src/jvmMain/kotlin/com/meet/dev/analyzer/data/repository/workspace/WorkspaceRepositoryImpl.kt‎

Lines changed: 60 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,18 @@
11
package com.meet.dev.analyzer.data.repository.workspace
22

3+
import com.meet.dev.analyzer.data.models.project.Dependency
4+
import com.meet.dev.analyzer.data.models.project.Plugin
35
import com.meet.dev.analyzer.data.models.project.ProjectOverviewInfo
46
import com.meet.dev.analyzer.data.models.workspace.ResourceCategory
57
import com.meet.dev.analyzer.data.models.workspace.UnusedResourceItem
68
import com.meet.dev.analyzer.data.models.workspace.WorkspaceAnalysisResult
9+
import com.meet.dev.analyzer.data.models.workspace.WorkspaceDependencyInfo
10+
import com.meet.dev.analyzer.data.models.workspace.WorkspacePluginInfo
711
import com.meet.dev.analyzer.data.repository.project.helpers.ProjectFileScanner
812
import com.meet.dev.analyzer.data.repository.project.helpers.ProjectOverviewAnalyzer
913
import com.meet.dev.analyzer.data.repository.project.helpers.VersionCatalogParser
14+
import com.meet.dev.analyzer.data.repository.project.helpers.DependencyAnalyzer
15+
import com.meet.dev.analyzer.data.repository.project.helpers.PluginAnalyzer
1016
import com.meet.dev.analyzer.data.repository.storage.helpers.AndroidSdkAnalyzer
1117
import com.meet.dev.analyzer.data.repository.storage.helpers.AvdAnalyzer
1218
import com.meet.dev.analyzer.data.repository.storage.helpers.GradleAnalyzer
@@ -25,7 +31,9 @@ class WorkspaceRepositoryImpl(
2531
private val androidSdkAnalyzer: AndroidSdkAnalyzer,
2632
private val gradleAnalyzer: GradleAnalyzer,
2733
private val konanAnalyzer: KonanAnalyzer,
28-
private val avdAnalyzer: AvdAnalyzer
34+
private val avdAnalyzer: AvdAnalyzer,
35+
private val dependencyAnalyzer: DependencyAnalyzer,
36+
private val pluginAnalyzer: PluginAnalyzer
2937
) : WorkspaceRepository {
3038

3139
private val TAG = tagName(javaClass)
@@ -62,6 +70,10 @@ class WorkspaceRepositoryImpl(
6270
val usedCmakeVersions = mutableSetOf<String>()
6371
val usedJdkVersions = mutableSetOf<String>()
6472

73+
// val gradleModulesInfo = gradleAnalyzer.getGradleModulesInfo()
74+
// val rawDepsList = mutableListOf<Pair<String, Dependency>>()
75+
// val rawPluginsList = mutableListOf<Pair<String, Plugin>>()
76+
6577
projectDirs.forEachIndexed { index, projectDir ->
6678
val progressVal = 0.1f + (0.5f * (index.toFloat() / totalProjects))
6779
onProgress(progressVal, "Analyzing project: ${projectDir.name}")
@@ -97,6 +109,24 @@ class WorkspaceRepositoryImpl(
97109
overview.ndkVersion?.let { usedNdkVersions.add(it) }
98110
overview.cmakeVersion?.let { usedCmakeVersions.add(it) }
99111
overview.jdkVersion?.let { usedJdkVersions.add(it) }
112+
//
113+
// try {
114+
// val plugins = pluginAnalyzer.findPlugin(
115+
// moduleBuildFileInfos = moduleBuildFileInfos,
116+
// versionCatalog = versionCatalog,
117+
// gradleModulesInfo = gradleModulesInfo
118+
// )
119+
// plugins.forEach { rawPluginsList.add(overview.projectName to it) }
120+
//
121+
// val dependencies = dependencyAnalyzer.findDependencies(
122+
// moduleBuildFileInfos = moduleBuildFileInfos,
123+
// versionCatalog = versionCatalog,
124+
// gradleModulesInfo = gradleModulesInfo
125+
// )
126+
// dependencies.forEach { rawDepsList.add(overview.projectName to it) }
127+
// } catch (e: Exception) {
128+
// AppLogger.e(TAG, e) { "Failed to extract dependencies for ${projectDir.name}" }
129+
// }
100130
}
101131

102132
AppLogger.i(TAG) {
@@ -344,36 +374,20 @@ class WorkspaceRepositoryImpl(
344374
val apiVal = apiRegex.find(item.name)?.groupValues?.get(1)
345375
?: item.name.substringAfter("android-")
346376

347-
val usedByAnyProject = usedCompileSdks.contains(apiVal) ||
348-
usedCompileSdks.any { it.contains(apiVal) } ||
349-
usedTargetSdks.contains(apiVal) ||
350-
usedTargetSdks.any { it.contains(apiVal) } ||
351-
usedMinSdks.contains(apiVal) ||
352-
usedMinSdks.any { it.contains(apiVal) }
353-
354377
val usedByAnyAvd = avdInfo.avdItemList.any { avd ->
355378
avd.apiLevel == apiVal || avd.apiLevel == "android-$apiVal"
356379
}
357380

358-
val isUsed = usedByAnyProject || usedByAnyAvd
359-
val usingProjects = if (usedByAnyProject) {
360-
projects.filter {
361-
it.compileSdkVersion == apiVal || it.compileSdkVersion?.contains(apiVal) == true ||
362-
it.targetSdkVersion == apiVal || it.targetSdkVersion?.contains(apiVal) == true ||
363-
it.minSdkVersion == apiVal || it.minSdkVersion?.contains(apiVal) == true
364-
}.map { it.projectName }
365-
} else emptyList()
366-
367381
val resource = UnusedResourceItem(
368382
name = "System Image ${item.name}",
369383
version = apiVal,
370384
category = ResourceCategory.ANDROID_SYSTEM_IMAGE,
371385
path = item.path,
372386
sizeBytes = item.sizeBytes,
373387
sizeFormatted = item.sizeReadable,
374-
usedByProjects = usingProjects
388+
usedByProjects = emptyList()
375389
)
376-
if (isUsed) activeResources.add(resource) else unusedResources.add(resource)
390+
if (usedByAnyAvd) activeResources.add(resource) else unusedResources.add(resource)
377391
}
378392

379393
// 2.k Gradle Daemons
@@ -445,15 +459,40 @@ class WorkspaceRepositoryImpl(
445459
}
446460
}
447461

462+
// val aggregatedDeps = rawDepsList.groupBy { it.second.id }.map { (depId, pairs) ->
463+
// val first = pairs.first().second
464+
// val group = first.group
465+
// val name = first.name
466+
// val versionsMap = pairs.groupBy { it.second.version ?: "unspecified" }
467+
// .mapValues { entry -> entry.value.map { it.first }.distinct().sorted() }
468+
// WorkspaceDependencyInfo(
469+
// id = depId,
470+
// group = group,
471+
// artifact = name,
472+
// versionsInUse = versionsMap
473+
// )
474+
// }.sortedBy { it.id.lowercase() }
475+
//
476+
// val aggregatedPlugins = rawPluginsList.groupBy { it.second.id }.map { (pluginId, pairs) ->
477+
// val versionsMap = pairs.groupBy { it.second.version ?: "unspecified" }
478+
// .mapValues { entry -> entry.value.map { it.first }.distinct().sorted() }
479+
// WorkspacePluginInfo(
480+
// id = pluginId,
481+
// versionsInUse = versionsMap
482+
// )
483+
// }.sortedBy { it.id.lowercase() }
484+
448485
onProgress(1f, "Analysis Complete")
449486
WorkspaceAnalysisResult(
450487
projects = projects,
451488
unusedResources = unusedResources.sortedByDescending { it.sizeBytes },
452-
activeResources = activeResources.sortedByDescending { it.sizeBytes }
489+
activeResources = activeResources.sortedByDescending { it.sizeBytes },
490+
// workspaceDependencies = aggregatedDeps,
491+
// workspacePlugins = aggregatedPlugins
453492
)
454493
} catch (e: Exception) {
455494
AppLogger.e(TAG, e) { "Error analyzing workspace" }
456-
WorkspaceAnalysisResult(emptyList(), emptyList(), emptyList())
495+
WorkspaceAnalysisResult(emptyList(), emptyList(), emptyList(), emptyList(), emptyList())
457496
}
458497
}
459498

‎composeApp/src/jvmMain/kotlin/com/meet/dev/analyzer/presentation/screen/cleanbuild/components/ConfirmationDialog.kt‎

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import androidx.compose.foundation.layout.PaddingValues
77
import androidx.compose.foundation.layout.Row
88
import androidx.compose.foundation.layout.Spacer
99
import androidx.compose.foundation.layout.fillMaxWidth
10+
import androidx.compose.foundation.layout.fillMaxSize
1011
import androidx.compose.foundation.layout.height
1112
import androidx.compose.foundation.layout.heightIn
1213
import androidx.compose.foundation.layout.padding
@@ -126,13 +127,13 @@ fun ConfirmationDialog(
126127
)
127128

128129
Spacer(modifier = Modifier.height(8.dp))
129-
BoxWithConstraints {
130+
BoxWithConstraints(modifier = Modifier.fillMaxWidth().height(320.dp)) {
130131
val scrollState = rememberLazyGridState()
131132

132133
LazyVerticalGrid(
133134
state = scrollState,
134135
columns = GridCells.Fixed(2),
135-
modifier = Modifier.heightIn(max = 400.dp),
136+
modifier = Modifier.fillMaxSize(),
136137
contentPadding = PaddingValues(
137138
top = 8.dp,
138139
bottom = 8.dp,

‎composeApp/src/jvmMain/kotlin/com/meet/dev/analyzer/presentation/screen/cleanbuild/components/DeletionProgressDialog.kt‎

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,9 @@ import androidx.compose.foundation.layout.Column
77
import androidx.compose.foundation.layout.PaddingValues
88
import androidx.compose.foundation.layout.Row
99
import androidx.compose.foundation.layout.Spacer
10+
import androidx.compose.foundation.layout.fillMaxSize
1011
import androidx.compose.foundation.layout.fillMaxWidth
1112
import androidx.compose.foundation.layout.height
12-
import androidx.compose.foundation.layout.heightIn
1313
import androidx.compose.foundation.layout.padding
1414
import androidx.compose.foundation.layout.size
1515
import androidx.compose.foundation.layout.width
@@ -221,12 +221,11 @@ fun DeletionProgressDialog(
221221
Spacer(modifier = Modifier.height(16.dp))
222222

223223
// Progress list with scrollbar
224-
BoxWithConstraints(modifier = Modifier.fillMaxWidth()) {
224+
BoxWithConstraints(modifier = Modifier.fillMaxWidth().height(320.dp)) {
225225
LazyColumn(
226226
state = scrollState,
227227
modifier = Modifier
228-
.fillMaxWidth()
229-
.heightIn(max = 400.dp),
228+
.fillMaxSize(),
230229
contentPadding = PaddingValues(
231230
end = 12.dp // scrollbar breathing space
232231
),

‎composeApp/src/jvmMain/kotlin/com/meet/dev/analyzer/presentation/screen/workspace/WorkspaceIntent.kt‎

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,10 +27,17 @@ sealed interface WorkspaceIntent {
2727
data class OnProjectHighlight(val projectName: String) : WorkspaceIntent
2828
data object OnClearHighlights : WorkspaceIntent
2929
data object OnToggleSelectionPanel : WorkspaceIntent
30+
data object OnSelectRecommended : WorkspaceIntent
3031

3132
// Analytics event trackers
3233
data object TrackFeedbackOpened : WorkspaceIntent
3334
data object TrackReviewPromptShown : WorkspaceIntent
3435
data object TrackFeedbackCancelled : WorkspaceIntent
3536
data object TrackWorkspaceAnalyzerOpened : WorkspaceIntent
37+
38+
// Dependency Analyzer tab intents
39+
data class OnWorkspaceTabChange(val tab: WorkspaceTab) : WorkspaceIntent
40+
data class OnDependencyTabChange(val tab: DependencyTypeTab) : WorkspaceIntent
41+
data class OnDependencySearchChange(val query: String) : WorkspaceIntent
42+
data class OnToggleConflictsOnly(val showOnly: Boolean) : WorkspaceIntent
3643
}

0 commit comments

Comments
 (0)