@@ -8,6 +8,7 @@ import com.meet.dev.analyzer.data.repository.project.helpers.ProjectFileScanner
88import com.meet.dev.analyzer.data.repository.project.helpers.ProjectOverviewAnalyzer
99import com.meet.dev.analyzer.data.repository.project.helpers.VersionCatalogParser
1010import com.meet.dev.analyzer.data.repository.storage.helpers.AndroidSdkAnalyzer
11+ import com.meet.dev.analyzer.data.repository.storage.helpers.AvdAnalyzer
1112import com.meet.dev.analyzer.data.repository.storage.helpers.GradleAnalyzer
1213import com.meet.dev.analyzer.data.repository.storage.helpers.KonanAnalyzer
1314import com.meet.dev.analyzer.utility.crash_report.AppLogger
@@ -23,7 +24,8 @@ class WorkspaceRepositoryImpl(
2324 private val projectOverviewAnalyzer : ProjectOverviewAnalyzer ,
2425 private val androidSdkAnalyzer : AndroidSdkAnalyzer ,
2526 private val gradleAnalyzer : GradleAnalyzer ,
26- private val konanAnalyzer : KonanAnalyzer
27+ private val konanAnalyzer : KonanAnalyzer ,
28+ private val avdAnalyzer : AvdAnalyzer
2729) : WorkspaceRepository {
2830
2931 private val TAG = tagName(javaClass)
@@ -58,6 +60,7 @@ class WorkspaceRepositoryImpl(
5860 val usedKotlinVersions = mutableSetOf<String >()
5961 val usedNdkVersions = mutableSetOf<String >()
6062 val usedCmakeVersions = mutableSetOf<String >()
63+ val usedJdkVersions = mutableSetOf<String >()
6164
6265 projectDirs.forEachIndexed { index, projectDir ->
6366 val progressVal = 0.1f + (0.5f * (index.toFloat() / totalProjects))
@@ -93,6 +96,7 @@ class WorkspaceRepositoryImpl(
9396 overview.kotlinVersion?.let { usedKotlinVersions.add(it) }
9497 overview.ndkVersion?.let { usedNdkVersions.add(it) }
9598 overview.cmakeVersion?.let { usedCmakeVersions.add(it) }
99+ overview.jdkVersion?.let { usedJdkVersions.add(it) }
96100 }
97101
98102 AppLogger .i(TAG ) {
@@ -102,7 +106,8 @@ class WorkspaceRepositoryImpl(
102106 " Gradle: $usedGradleVersions \n " +
103107 " Kotlin: $usedKotlinVersions \n " +
104108 " NDK: $usedNdkVersions \n " +
105- " CMake: $usedCmakeVersions "
109+ " CMake: $usedCmakeVersions \n " +
110+ " JDK: $usedJdkVersions "
106111 }
107112
108113 // 2. Scan Local System Resources
@@ -316,6 +321,130 @@ class WorkspaceRepositoryImpl(
316321 if (isUsed) activeResources.add(resource) else unusedResources.add(resource)
317322 }
318323
324+ // 2.i Android Virtual Devices
325+ onProgress(0.97f , " Scanning Android Virtual Devices..." )
326+ val avdInfo = avdAnalyzer.analyzeAvdData()
327+ avdInfo.avdItemList.forEach { item ->
328+ unusedResources.add(
329+ UnusedResourceItem (
330+ name = item.name,
331+ version = item.apiLevel ? : " Unknown" ,
332+ category = ResourceCategory .ANDROID_AVD ,
333+ path = item.path,
334+ sizeBytes = item.sizeBytes,
335+ sizeFormatted = item.actualStorage,
336+ usedByProjects = emptyList()
337+ )
338+ )
339+ }
340+
341+ // 2.j Android System Images
342+ onProgress(0.98f , " Scanning Android System Images..." )
343+ sdkInfo.systemImageInfo.systemImages.forEach { item ->
344+ val apiVal = apiRegex.find(item.name)?.groupValues?.get(1 )
345+ ? : item.name.substringAfter(" android-" )
346+
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+
354+ val usedByAnyAvd = avdInfo.avdItemList.any { avd ->
355+ avd.apiLevel == apiVal || avd.apiLevel == " android-$apiVal "
356+ }
357+
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+
367+ val resource = UnusedResourceItem (
368+ name = " System Image ${item.name} " ,
369+ version = apiVal,
370+ category = ResourceCategory .ANDROID_SYSTEM_IMAGE ,
371+ path = item.path,
372+ sizeBytes = item.sizeBytes,
373+ sizeFormatted = item.sizeReadable,
374+ usedByProjects = usingProjects
375+ )
376+ if (isUsed) activeResources.add(resource) else unusedResources.add(resource)
377+ }
378+
379+ // 2.k Gradle Daemons
380+ onProgress(0.86f , " Scanning Gradle daemons..." )
381+ gradleInfo.daemonInfo.daemonItems.forEach { item ->
382+ unusedResources.add(
383+ UnusedResourceItem (
384+ name = " Gradle Daemon Logs ${item.name} " ,
385+ version = item.name,
386+ category = ResourceCategory .GRADLE_DAEMON ,
387+ path = item.path,
388+ sizeBytes = item.sizeBytes,
389+ sizeFormatted = item.sizeReadable
390+ )
391+ )
392+ }
393+
394+ // 2.l Gradle Toolchain JDKs
395+ onProgress(0.87f , " Scanning Gradle JDKs..." )
396+ gradleInfo.jdkInfo.jdkItems.forEach { item ->
397+ val version = item.name
398+ val isUsed = usedJdkVersions.any { usedVer ->
399+ version.contains(usedVer) || usedVer.contains(version)
400+ }
401+ val usingProjects = if (isUsed) {
402+ projects.filter {
403+ it.jdkVersion != null && (version.contains(it.jdkVersion) || it.jdkVersion.contains(version))
404+ }.map { it.projectName }
405+ } else emptyList()
406+
407+ val resource = UnusedResourceItem (
408+ name = " Gradle Toolchain JDK ${item.name} " ,
409+ version = item.name,
410+ category = ResourceCategory .GRADLE_JDK ,
411+ path = item.path,
412+ sizeBytes = item.sizeBytes,
413+ sizeFormatted = item.sizeReadable,
414+ usedByProjects = usingProjects
415+ )
416+ if (isUsed) activeResources.add(resource) else unusedResources.add(resource)
417+ }
418+
419+ // 2.m Other Gradle Caches & Temp Files
420+ onProgress(0.88f , " Scanning other Gradle caches..." )
421+ gradleInfo.otherGradleFolderInfo.otherGradleFolderItems.forEach { item ->
422+ val category = when (item.version) {
423+ " .tmp" -> ResourceCategory .GRADLE_TEMP_FILES
424+ " build-cache-1" -> ResourceCategory .GRADLE_BUILD_CACHE
425+ " transforms-3" , " jars-9" -> ResourceCategory .GRADLE_TRANSFORMS_CACHE
426+ else -> null
427+ }
428+ if (category != null ) {
429+ unusedResources.add(
430+ UnusedResourceItem (
431+ name = when (item.version) {
432+ " .tmp" -> " Gradle Temporary Files"
433+ " build-cache-1" -> " Gradle Local Build Cache"
434+ " transforms-3" -> " Gradle Dependency Transforms Cache"
435+ " jars-9" -> " Gradle Cached Jars"
436+ else -> item.version
437+ },
438+ version = " Global" ,
439+ category = category,
440+ path = item.path,
441+ sizeBytes = item.sizeBytes,
442+ sizeFormatted = item.sizeReadable
443+ )
444+ )
445+ }
446+ }
447+
319448 onProgress(1f , " Analysis Complete" )
320449 WorkspaceAnalysisResult (
321450 projects = projects,
@@ -337,6 +466,13 @@ class WorkspaceRepositoryImpl(
337466 } else {
338467 val success = file.deleteRecursively()
339468 if (success) {
469+ if (file.name.endsWith(" .avd" , ignoreCase = true )) {
470+ val parent = file.parentFile
471+ val iniFile = File (parent, file.nameWithoutExtension + " .ini" )
472+ if (iniFile.exists()) {
473+ iniFile.delete()
474+ }
475+ }
340476 Pair (true , null )
341477 } else {
342478 Pair (
0 commit comments