Skip to content

Commit

Permalink
Added view model for the app manager
Browse files Browse the repository at this point in the history
  • Loading branch information
D4rK7355608 committed Jul 9, 2024
1 parent 10c7945 commit c4d1b98
Show file tree
Hide file tree
Showing 5 changed files with 127 additions and 38 deletions.
2 changes: 1 addition & 1 deletion app/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ android {
applicationId = "com.d4rk.cleaner"
minSdk = 26
targetSdk = 34
versionCode = 81
versionCode = 84
versionName = "2.0.0"
archivesName = "${applicationId}-v${versionName}"
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
Expand Down
7 changes: 7 additions & 0 deletions app/src/main/kotlin/com/d4rk/cleaner/data/model/ui/ApkInfo.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package com.d4rk.cleaner.data.model.ui

data class ApkInfo(
val id: Long,
val path: String,
val size: Long
)
Original file line number Diff line number Diff line change
@@ -1,13 +1,12 @@
package com.d4rk.cleaner.ui.appmanager

import android.app.Application
import android.content.Intent
import android.content.pm.ApplicationInfo
import android.content.pm.PackageManager
import android.graphics.Bitmap
import android.graphics.Canvas
import android.graphics.drawable.BitmapDrawable
import android.net.Uri
import android.provider.MediaStore
import android.provider.Settings
import androidx.compose.foundation.Image
import androidx.compose.foundation.layout.Box
Expand All @@ -33,7 +32,7 @@ import androidx.compose.material3.TabRowDefaults
import androidx.compose.material3.TabRowDefaults.tabIndicatorOffset
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableIntStateOf
import androidx.compose.runtime.mutableStateOf
Expand All @@ -49,24 +48,24 @@ import androidx.compose.ui.res.imageResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.unit.dp
import androidx.lifecycle.viewmodel.compose.viewModel
import com.d4rk.cleaner.R
import com.d4rk.cleaner.data.model.ui.ApkInfo
import java.io.File

/**
* Composable function for managing and displaying different app categories.
*
* This composable function displays tabs for "Installed Apps", "System Apps", and "App Install Files".
* Each tab shows corresponding app information based on the selected category.
*/
@Composable
fun AppManagerComposable() {

val viewModel: AppManagerViewModel = viewModel(
factory = AppManagerViewModelFactory(LocalContext.current.applicationContext as Application)
)
val tabs = listOf("Installed Apps", "System Apps", "App Install Files")
var selectedIndex by remember { mutableIntStateOf(0) }
var apps by remember { mutableStateOf(listOf<ApplicationInfo>()) }
val context = LocalContext.current
LaunchedEffect(Unit) {
apps = context.packageManager.getInstalledApplications(PackageManager.GET_META_DATA)
}
val installedApps by viewModel.installedApps.collectAsState()
val apkFiles by viewModel.apkFiles.collectAsState()

Column {
TabRow(
Expand Down Expand Up @@ -94,9 +93,11 @@ fun AppManagerComposable() {
}
}
when (selectedIndex) {
0 -> AppsComposable(apps.filter { it.flags and ApplicationInfo.FLAG_SYSTEM == 0 })
1 -> AppsComposable(apps.filter { it.flags and ApplicationInfo.FLAG_SYSTEM != 0 })
2 -> ApksComposable()
0 -> AppsComposable(
apps = installedApps.filter { it.flags and ApplicationInfo.FLAG_SYSTEM == 0 })
1 -> AppsComposable(
apps = installedApps.filter { it.flags and ApplicationInfo.FLAG_SYSTEM != 0 })
2 -> ApksComposable(apkFiles = apkFiles)
}
}
}
Expand Down Expand Up @@ -181,7 +182,9 @@ fun AppItemComposable(
}

DropdownMenu(expanded = showMenu, onDismissRequest = { showMenu = false }) {
DropdownMenuItem(text = { Text(stringResource(R.string.uninstall)) },
DropdownMenuItem(text = {
Text(stringResource(R.string.uninstall))
},
onClick = {
val uri = Uri.fromParts("package", app.packageName, null)
val intent = Intent(Intent.ACTION_DELETE, uri)
Expand Down Expand Up @@ -220,33 +223,16 @@ fun AppItemComposable(
* Composable function for displaying a list of APK files on the device.
*/
@Composable
fun ApksComposable() {
val context = LocalContext.current
val uri = MediaStore.Files.getContentUri("external")
val cursor = context.contentResolver.query(
uri,
arrayOf(MediaStore.Files.FileColumns.DATA),
MediaStore.Files.FileColumns.MIME_TYPE + "=?",
arrayOf("application/vnd.android.package-archive"),
null
)

var apkPaths = listOf<String>()
cursor?.use {
while (it.moveToNext()) {
val dataColumnIndex = it.getColumnIndex(MediaStore.Files.FileColumns.DATA)
val filePath = it.getString(dataColumnIndex)
apkPaths = apkPaths + filePath
}
}
fun ApksComposable(apkFiles: List<ApkInfo>) {

LazyColumn {
items(apkPaths) { apkPath ->
ApkItemComposable(apkPath)
items(apkFiles) { apkInfo ->
ApkItemComposable(apkPath = apkInfo.path)
}
}
}


/**
* Composable function for displaying detailed information about an APK file.
*
Expand Down Expand Up @@ -321,7 +307,8 @@ fun ApkItemComposable(apkPath: String) {
DropdownMenuItem(text = { Text("Install") }, onClick = {
val installIntent = Intent(Intent.ACTION_VIEW)
installIntent.setDataAndType(
Uri.fromFile(apkFile), "application/vnd.android.package-archive"
Uri.fromFile(apkFile),
"application/vnd.android.package-archive"
)
installIntent.flags = Intent.FLAG_ACTIVITY_NEW_TASK
context.startActivity(installIntent)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
package com.d4rk.cleaner.ui.appmanager

import android.app.Application
import android.content.pm.ApplicationInfo
import android.content.pm.PackageManager
import android.database.Cursor
import android.net.Uri
import android.provider.MediaStore
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import com.d4rk.cleaner.data.model.ui.ApkInfo
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext

class AppManagerViewModel(private val application: Application) : ViewModel() {
private val _installedApps = MutableStateFlow<List<ApplicationInfo>>(emptyList())
val installedApps: StateFlow<List<ApplicationInfo>> = _installedApps.asStateFlow()

private val _apkFiles = MutableStateFlow<List<ApkInfo>>(emptyList())
val apkFiles: StateFlow<List<ApkInfo>> = _apkFiles.asStateFlow()


init {
loadInstalledApps()
loadApkFiles()
}

private fun loadInstalledApps() {
viewModelScope.launch {
_installedApps.value = getInstalledApps()
}
}

private suspend fun getInstalledApps(): List<ApplicationInfo> {
return withContext(Dispatchers.IO) {
application.packageManager.getInstalledApplications(PackageManager.GET_META_DATA)
}
}

private fun loadApkFiles() {
viewModelScope.launch {
_apkFiles.value = getApkFilesFromStorage()
}
}

private suspend fun getApkFilesFromStorage(): List<ApkInfo> {
return withContext(Dispatchers.IO) {
val apkFiles = mutableListOf<ApkInfo>()
val uri: Uri = MediaStore.Files.getContentUri("external")
val projection = arrayOf(
MediaStore.Files.FileColumns._ID,
MediaStore.Files.FileColumns.DATA,
MediaStore.Files.FileColumns.SIZE
)
val selection = "${MediaStore.Files.FileColumns.MIME_TYPE} = ?"
val selectionArgs = arrayOf("application/vnd.android.package-archive")
val cursor: Cursor? = application.contentResolver.query(
uri, projection, selection, selectionArgs, null
)

cursor?.use {
val idColumn = it.getColumnIndexOrThrow(MediaStore.Files.FileColumns._ID)
val dataColumn = it.getColumnIndexOrThrow(MediaStore.Files.FileColumns.DATA)
val sizeColumn = it.getColumnIndexOrThrow(MediaStore.Files.FileColumns.SIZE)

while (it.moveToNext()) {
val id = it.getLong(idColumn)
val path = it.getString(dataColumn)
val size = it.getLong(sizeColumn)
apkFiles.add(ApkInfo(id, path, size))
}
}
apkFiles
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package com.d4rk.cleaner.ui.appmanager

import android.app.Application
import androidx.lifecycle.ViewModel
import androidx.lifecycle.ViewModelProvider

class AppManagerViewModelFactory(private val application: Application) : ViewModelProvider.Factory {
override fun <T : ViewModel> create(modelClass: Class<T>): T {
if (modelClass.isAssignableFrom(AppManagerViewModel::class.java)) {
@Suppress("UNCHECKED_CAST")
return AppManagerViewModel(application) as T
}
throw IllegalArgumentException("Unknown ViewModel class")
}
}

0 comments on commit c4d1b98

Please sign in to comment.