Skip to content

Commit 6f76ae5

Browse files
Improved the app manager
1 parent c8f93ad commit 6f76ae5

File tree

4 files changed

+91
-44
lines changed

4 files changed

+91
-44
lines changed

app/src/main/kotlin/com/d4rk/cleaner/data/model/ui/ApkInfo.kt renamed to app/src/main/kotlin/com/d4rk/cleaner/data/model/ui/appmanager/ui/ApkInfo.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
package com.d4rk.cleaner.data.model.ui
1+
package com.d4rk.cleaner.data.model.ui.appmanager.ui
22

33
data class ApkInfo(
44
val id: Long,
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
package com.d4rk.cleaner.data.model.ui.appmanager.ui
2+
3+
sealed class UiState<out T : Any> {
4+
object Loading : UiState<Nothing>()
5+
data class Success<out T : Any>(val data: T) : UiState<T>()
6+
data class Error(val exception: Exception) : UiState<Nothing>()
7+
}

app/src/main/kotlin/com/d4rk/cleaner/ui/appmanager/AppManagerComposable.kt

Lines changed: 63 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -9,11 +9,15 @@ import android.graphics.Canvas
99
import android.graphics.drawable.BitmapDrawable
1010
import android.net.Uri
1111
import android.provider.Settings
12+
import androidx.compose.animation.core.animateFloat
13+
import androidx.compose.animation.core.updateTransition
14+
1215
import androidx.compose.foundation.ExperimentalFoundationApi
1316
import androidx.compose.foundation.Image
1417
import androidx.compose.foundation.layout.Box
1518
import androidx.compose.foundation.layout.Column
1619
import androidx.compose.foundation.layout.Row
20+
import androidx.compose.foundation.layout.fillMaxSize
1721
import androidx.compose.foundation.layout.fillMaxWidth
1822
import androidx.compose.foundation.layout.padding
1923
import androidx.compose.foundation.layout.size
@@ -24,6 +28,7 @@ import androidx.compose.foundation.pager.rememberPagerState
2428
import androidx.compose.foundation.shape.RoundedCornerShape
2529
import androidx.compose.material.icons.Icons
2630
import androidx.compose.material.icons.outlined.MoreVert
31+
import androidx.compose.material3.CircularProgressIndicator
2732
import androidx.compose.material3.DropdownMenu
2833
import androidx.compose.material3.DropdownMenuItem
2934
import androidx.compose.material3.Icon
@@ -45,6 +50,7 @@ import androidx.compose.runtime.rememberCoroutineScope
4550
import androidx.compose.runtime.setValue
4651
import androidx.compose.ui.Alignment
4752
import androidx.compose.ui.Modifier
53+
import androidx.compose.ui.draw.alpha
4854
import androidx.compose.ui.draw.clip
4955
import androidx.compose.ui.graphics.ImageBitmap
5056
import androidx.compose.ui.graphics.asImageBitmap
@@ -55,7 +61,7 @@ import androidx.compose.ui.text.style.TextOverflow
5561
import androidx.compose.ui.unit.dp
5662
import androidx.lifecycle.viewmodel.compose.viewModel
5763
import com.d4rk.cleaner.R
58-
import com.d4rk.cleaner.data.model.ui.ApkInfo
64+
import com.d4rk.cleaner.data.model.ui.appmanager.ui.ApkInfo
5965
import com.d4rk.cleaner.utils.PermissionsUtils
6066
import kotlinx.coroutines.launch
6167
import java.io.File
@@ -73,56 +79,73 @@ fun AppManagerComposable() {
7379
val tabs = listOf("Installed Apps", "System Apps", "App Install Files")
7480
val pagerState = rememberPagerState(pageCount = { tabs.size })
7581
val coroutineScope = rememberCoroutineScope()
82+
val isLoading by viewModel.isLoading.collectAsState()
83+
val transition = updateTransition(targetState = ! isLoading, label = "LoadingTransition")
84+
85+
val contentAlpha by transition.animateFloat(label = "Content Alpha") {
86+
if (it) 1f else 0f
87+
}
7688

7789
LaunchedEffect(context) {
7890
if (!PermissionsUtils.hasStoragePermissions(context)) {
7991
PermissionsUtils.requestStoragePermissions(context as Activity)
8092
}
8193
}
8294

83-
Column {
84-
TabRow(
85-
selectedTabIndex = pagerState.currentPage,
86-
indicator = { tabPositions ->
87-
TabRowDefaults.PrimaryIndicator(
88-
modifier = Modifier.tabIndicatorOffset(tabPositions[pagerState.currentPage]),
89-
shape = RoundedCornerShape(
90-
topStart = 3.dp, topEnd = 3.dp, bottomEnd = 0.dp, bottomStart = 0.dp
91-
),
92-
)
93-
},
95+
if (isLoading) {
96+
Box(
97+
modifier = Modifier.fillMaxSize(),
98+
contentAlignment = Alignment.Center
9499
) {
95-
tabs.forEachIndexed { index, title ->
96-
Tab(
97-
text = {
98-
Text(
99-
text = title,
100-
maxLines = 1,
101-
overflow = TextOverflow.Ellipsis,
102-
color = MaterialTheme.colorScheme.onSurface
103-
)
104-
},
105-
selected = pagerState.currentPage == index,
106-
onClick = {
107-
coroutineScope.launch {
108-
pagerState.animateScrollToPage(index)
100+
CircularProgressIndicator()
101+
}
102+
} else {
103+
Column(
104+
modifier = Modifier.alpha(contentAlpha),
105+
) {
106+
TabRow(
107+
selectedTabIndex = pagerState.currentPage,
108+
indicator = { tabPositions ->
109+
TabRowDefaults.PrimaryIndicator(
110+
modifier = Modifier.tabIndicatorOffset(tabPositions[pagerState.currentPage]),
111+
shape = RoundedCornerShape(
112+
topStart = 3.dp, topEnd = 3.dp, bottomEnd = 0.dp, bottomStart = 0.dp
113+
),
114+
)
115+
},
116+
) {
117+
tabs.forEachIndexed { index, title ->
118+
Tab(
119+
text = {
120+
Text(
121+
text = title,
122+
maxLines = 1,
123+
overflow = TextOverflow.Ellipsis,
124+
color = MaterialTheme.colorScheme.onSurface
125+
)
126+
},
127+
selected = pagerState.currentPage == index,
128+
onClick = {
129+
coroutineScope.launch {
130+
pagerState.animateScrollToPage(index)
131+
}
109132
}
110-
}
111-
)
133+
)
134+
}
112135
}
113-
}
114136

115-
HorizontalPager(
116-
state = pagerState, // Only provide pagerState here
117-
) { page ->
118-
when (page) {
119-
0 -> AppsComposable(
120-
apps = viewModel.installedApps.collectAsState().value.filter { it.flags and ApplicationInfo.FLAG_SYSTEM == 0 }
121-
)
122-
1 -> AppsComposable(
123-
apps = viewModel.installedApps.collectAsState().value.filter { it.flags and ApplicationInfo.FLAG_SYSTEM != 0 }
124-
)
125-
2 -> ApksComposable(apkFiles = viewModel.apkFiles.collectAsState().value)
137+
HorizontalPager(
138+
state = pagerState,
139+
) { page ->
140+
when (page) {
141+
0 -> AppsComposable(
142+
apps = viewModel.installedApps.collectAsState().value.filter { it.flags and ApplicationInfo.FLAG_SYSTEM == 0 }
143+
)
144+
1 -> AppsComposable(
145+
apps = viewModel.installedApps.collectAsState().value.filter { it.flags and ApplicationInfo.FLAG_SYSTEM != 0 }
146+
)
147+
2 -> ApksComposable(apkFiles = viewModel.apkFiles.collectAsState().value)
148+
}
126149
}
127150
}
128151
}

app/src/main/kotlin/com/d4rk/cleaner/ui/appmanager/AppManagerViewModel.kt

Lines changed: 20 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,10 @@ import android.net.Uri
88
import android.provider.MediaStore
99
import androidx.lifecycle.ViewModel
1010
import androidx.lifecycle.viewModelScope
11-
import com.d4rk.cleaner.data.model.ui.ApkInfo
11+
import com.d4rk.cleaner.data.model.ui.appmanager.ui.ApkInfo
1212
import kotlinx.coroutines.Dispatchers
13+
import kotlinx.coroutines.async
14+
import kotlinx.coroutines.awaitAll
1315
import kotlinx.coroutines.flow.MutableStateFlow
1416
import kotlinx.coroutines.flow.StateFlow
1517
import kotlinx.coroutines.flow.asStateFlow
@@ -23,10 +25,25 @@ class AppManagerViewModel(private val application: Application) : ViewModel() {
2325
private val _apkFiles = MutableStateFlow<List<ApkInfo>>(emptyList())
2426
val apkFiles: StateFlow<List<ApkInfo>> = _apkFiles.asStateFlow()
2527

28+
private val _isLoading = MutableStateFlow(true)
29+
val isLoading : StateFlow<Boolean> = _isLoading.asStateFlow()
2630

2731
init {
28-
loadInstalledApps()
29-
loadApkFiles()
32+
loadAppData()
33+
}
34+
35+
private fun loadAppData() {
36+
viewModelScope.launch {
37+
_isLoading.value = true
38+
try {
39+
awaitAll(
40+
async { loadInstalledApps() },
41+
async { loadApkFiles() }
42+
)
43+
} finally {
44+
_isLoading.value = false
45+
}
46+
}
3047
}
3148

3249
private fun loadInstalledApps() {

0 commit comments

Comments
 (0)