From d92ee7b8b2eef4a658baa1d684c413e008a455cd Mon Sep 17 00:00:00 2001 From: D4rK7355608 Date: Sun, 12 May 2024 10:47:54 +0300 Subject: [PATCH] App code base refactored and updated code documentation. --- .github/ISSUE_TEMPLATE/bug_report.yml | 2 +- .github/ISSUE_TEMPLATE/feature_request.yml | 2 +- .github/dependabot.yml | 12 +- .../kotlin/com/d4rk/cleaner/MainActivity.kt | 76 +++--- .../kotlin/com/d4rk/cleaner/MainComposable.kt | 208 +++++++-------- .../com/d4rk/cleaner/adapters/CpuAdapter.kt | 41 --- .../cleaner/data/navigation/NavigationItem.kt | 2 +- .../d4rk/cleaner/data/navigation/Screen.kt | 12 +- .../cleaner/dialogs/RequireRestartDialog.kt | 2 +- .../d4rk/cleaner/ui/help/HelpComposable.kt | 2 +- .../d4rk/cleaner/ui/home/HomeComposable.kt | 199 ++++++++------- .../d4rk/cleaner/ui/memory/MemoryFragment.kt | 56 ++-- .../cleaner/ui/startup/StartupComposable.kt | 4 +- .../cleaner/ui/support/SupportComposable.kt | 122 ++++----- .../cleaner/ui/viewmodel/MemoryViewModel.kt | 68 ----- .../cleaner/ui/whitelist/WhitelistActivity.kt | 20 +- .../d4rk/cleaner/utils/ComposablesUtils.kt | 240 +++++++++--------- .../com/d4rk/cleaner/utils/FileScanner.kt | 40 ++- .../kotlin/com/d4rk/cleaner/utils/Utils.kt | 25 +- app/src/main/res/layout/fragment_memory.xml | 3 - 20 files changed, 525 insertions(+), 611 deletions(-) delete mode 100644 app/src/main/kotlin/com/d4rk/cleaner/adapters/CpuAdapter.kt delete mode 100644 app/src/main/kotlin/com/d4rk/cleaner/ui/viewmodel/MemoryViewModel.kt diff --git a/.github/ISSUE_TEMPLATE/bug_report.yml b/.github/ISSUE_TEMPLATE/bug_report.yml index 56cbb60..20da5f3 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.yml +++ b/.github/ISSUE_TEMPLATE/bug_report.yml @@ -1,7 +1,7 @@ name: Bug Report description: File a bug report title: "[Bug]: " -labels: ["bug", "triage me"] +labels: [ "bug", "triage me" ] body: - type: markdown attributes: diff --git a/.github/ISSUE_TEMPLATE/feature_request.yml b/.github/ISSUE_TEMPLATE/feature_request.yml index 49905ce..8775570 100644 --- a/.github/ISSUE_TEMPLATE/feature_request.yml +++ b/.github/ISSUE_TEMPLATE/feature_request.yml @@ -1,7 +1,7 @@ name: Feature request description: File a feature request title: "[FR]: " -labels: ["enhancement", "triage me"] +labels: [ "enhancement", "triage me" ] body: - type: markdown attributes: diff --git a/.github/dependabot.yml b/.github/dependabot.yml index bcf259e..2897296 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -1,8 +1,8 @@ version: 2 updates: -- package-ecosystem: gradle - directory: "/" - schedule: - interval: weekly - time: "11:00" - open-pull-requests-limit: 10 + - package-ecosystem: gradle + directory: "/" + schedule: + interval: weekly + time: "11:00" + open-pull-requests-limit: 10 diff --git a/app/src/main/kotlin/com/d4rk/cleaner/MainActivity.kt b/app/src/main/kotlin/com/d4rk/cleaner/MainActivity.kt index 926231a..ca6a16a 100644 --- a/app/src/main/kotlin/com/d4rk/cleaner/MainActivity.kt +++ b/app/src/main/kotlin/com/d4rk/cleaner/MainActivity.kt @@ -29,12 +29,12 @@ import kotlinx.coroutines.launch import kotlinx.coroutines.tasks.await class MainActivity : ComponentActivity() { - private lateinit var dataStore : DataStore - private lateinit var appUpdateManager : AppUpdateManager - private var appUpdateNotificationsManager : AppUpdateNotificationsManager = - AppUpdateNotificationsManager(this) + private lateinit var dataStore: DataStore + private lateinit var appUpdateManager: AppUpdateManager + private var appUpdateNotificationsManager: AppUpdateNotificationsManager = + AppUpdateNotificationsManager(this) - override fun onCreate(savedInstanceState : Bundle?) { + override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) installSplashScreen() enableEdgeToEdge() @@ -44,7 +44,7 @@ class MainActivity : ComponentActivity() { setContent { AppTheme { Surface( - modifier = Modifier.fillMaxSize() , color = MaterialTheme.colorScheme.background + modifier = Modifier.fillMaxSize(), color = MaterialTheme.colorScheme.background ) { MainComposable() } @@ -64,28 +64,30 @@ class MainActivity : ComponentActivity() { } /** - * Handles the result of an activity launched for result. + * Overrides the `onActivityResult` method to handle the result of an activity launched for result. * - * This function overrides the `onActivityResult` method to handle the result of a specific request code (1) - * used for in-app updates. It checks the `resultCode` to determine the outcome of the update process and - * displays appropriate UI feedback using Snackbar. + * This function is specifically designed to handle the result of a request code (1) + * which is used for in-app updates. It checks the `resultCode` to determine the outcome of the update process. + * Depending on the `resultCode`, it either displays a Snackbar message indicating a successful update or + * calls a function to show a Snackbar message indicating that the update failed. * - * @param requestCode The request code that was specified when starting the activity for result. - * @param resultCode The result code returned by the activity upon completion. - * @param data The data returned by the activity, if any. + * @param requestCode The integer request code originally supplied to startActivityForResult(), + * allowing you to identify who this result came from. + * @param resultCode The integer result code returned by the child activity through its setResult(). + * @param data An Intent, which can return result data to the caller (various data can be attached to Intent "extras"). */ @Suppress("DEPRECATION") @Deprecated("Deprecated in Java") - override fun onActivityResult(requestCode : Int , resultCode : Int , data : Intent?) { - super.onActivityResult(requestCode , resultCode , data) + override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { + super.onActivityResult(requestCode, resultCode, data) if (requestCode == 1) { when (resultCode) { RESULT_OK -> { val snackbar = Snackbar.make( - findViewById(android.R.id.content) , - R.string.snack_app_updated , + findViewById(android.R.id.content), + R.string.snack_app_updated, Snackbar.LENGTH_LONG - ).setAction(android.R.string.ok , null) + ).setAction(android.R.string.ok, null) snackbar.show() } @@ -97,13 +99,18 @@ class MainActivity : ComponentActivity() { } /** - * Checks for the availability of flexible updates and triggers the appropriate update flow if conditions are met. + * Checks for the availability of updates and triggers the appropriate update flow if conditions are met. * - * This function uses the provided lifecycle scope to asynchronously check for available updates using the - * Google Play Core library. If a flexible update is available and meets certain conditions, it triggers - * the update flow. + * This function uses the lifecycle scope to asynchronously check for available updates using the + * Google Play Core library. If an update is available and meets certain conditions, it triggers + * the update flow. The update can be of two types: IMMEDIATE or FLEXIBLE. * - * @param lifecycleScope The lifecycle scope used for launching coroutines, typically obtained from the hosting activity. + * For an IMMEDIATE update, it checks if the client version is more than 90 days old. If so, it triggers the update. + * For a FLEXIBLE update, it checks if the client version is less than 90 days old. If so, it triggers the update. + * + * The function also ensures that no developer-triggered update is in progress before triggering a new update. + * + * @param lifecycleScope The lifecycle scope used for launching coroutines, obtained from the hosting activity. */ private fun checkForFlexibleUpdate() { lifecycleScope.launch { @@ -120,7 +127,7 @@ class MainActivity : ComponentActivity() { info.clientVersionStalenessDays()?.let { if (it > 90) { appUpdateManager.startUpdateFlowForResult( - info , AppUpdateType.IMMEDIATE , this@MainActivity , 1 + info, AppUpdateType.IMMEDIATE, this@MainActivity, 1 ) } } @@ -132,7 +139,7 @@ class MainActivity : ComponentActivity() { info.clientVersionStalenessDays()?.let { if (it < 90) { appUpdateManager.startUpdateFlowForResult( - info , AppUpdateType.FLEXIBLE , this@MainActivity , 1 + info, AppUpdateType.FLEXIBLE, this@MainActivity, 1 ) } } @@ -143,9 +150,16 @@ class MainActivity : ComponentActivity() { } } + /** + * Displays a Snackbar message indicating that the update process has failed. + * + * This function creates a Snackbar with a message indicating that the update process has failed. + * The Snackbar includes a "Try Again" action which, when clicked, triggers the `checkForFlexibleUpdate` function + * to check for updates and initiate the appropriate update flow if conditions are met. + */ private fun showUpdateFailedSnackbar() { val snackbar = Snackbar.make( - findViewById(android.R.id.content) , R.string.snack_update_failed , Snackbar.LENGTH_LONG + findViewById(android.R.id.content), R.string.snack_update_failed, Snackbar.LENGTH_LONG ).setAction(R.string.try_again) { checkForFlexibleUpdate() } @@ -158,10 +172,12 @@ class MainActivity : ComponentActivity() { } /** - * Sets up application settings based on data stored in a dataStore. + * Configures application settings based on data stored in a DataStore. * * This function uses a lifecycle coroutine scope to asynchronously retrieve the value of `usageAndDiagnostics` - * from the dataStore and adjusts Firebase Analytics and Crashlytics collection settings accordingly. + * from the DataStore. It then adjusts the Firebase Analytics and Crashlytics collection settings based on the retrieved value. + * + * If `usageAndDiagnostics` is enabled, both Firebase Analytics and Crashlytics data collection will be enabled. If it's not, data collection will be disabled. * * @see androidx.lifecycle.lifecycleScope * @see androidx.datastore.preferences.core.DataStore @@ -172,7 +188,7 @@ class MainActivity : ComponentActivity() { lifecycleScope.launch { val isEnabled = dataStore.usageAndDiagnostics.first() FirebaseAnalytics.getInstance(this@MainActivity) - .setAnalyticsCollectionEnabled(isEnabled) + .setAnalyticsCollectionEnabled(isEnabled) FirebaseCrashlytics.getInstance().setCrashlyticsCollectionEnabled(isEnabled) } } @@ -181,7 +197,7 @@ class MainActivity : ComponentActivity() { lifecycleScope.launch { if (dataStore.startup.first()) { dataStore.saveStartup(false) - startActivity(Intent(this@MainActivity , StartupActivity::class.java)) + startActivity(Intent(this@MainActivity, StartupActivity::class.java)) } } } diff --git a/app/src/main/kotlin/com/d4rk/cleaner/MainComposable.kt b/app/src/main/kotlin/com/d4rk/cleaner/MainComposable.kt index 7748f1f..eff5ea5 100644 --- a/app/src/main/kotlin/com/d4rk/cleaner/MainComposable.kt +++ b/app/src/main/kotlin/com/d4rk/cleaner/MainComposable.kt @@ -36,7 +36,6 @@ import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.runtime.saveable.rememberSaveable import androidx.compose.ui.Modifier import androidx.compose.ui.platform.LocalContext -import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.stringResource import androidx.compose.ui.unit.dp import androidx.navigation.compose.NavHost @@ -59,113 +58,114 @@ import kotlinx.coroutines.launch @Composable fun MainComposable() { val bottomBarItems = listOf( - Screen.Home , Screen.AppManager , Screen.MemoryManager + Screen.Home, Screen.AppManager, Screen.MemoryManager ) val drawerItems = listOf( NavigationItem( - title = R.string.whitelist , selectedIcon = Icons.AutoMirrored.Outlined.ListAlt - ) , + title = R.string.whitelist, selectedIcon = Icons.AutoMirrored.Outlined.ListAlt + ), NavigationItem( - title = R.string.image_optimizer , selectedIcon = Icons.Outlined.Image - ) , + title = R.string.image_optimizer, selectedIcon = Icons.Outlined.Image + ), NavigationItem( - title = R.string.settings , - selectedIcon = Icons.Outlined.Settings , - ) , + title = R.string.settings, + selectedIcon = Icons.Outlined.Settings, + ), NavigationItem( - title = R.string.help_and_feedback , - selectedIcon = Icons.AutoMirrored.Outlined.Help , - ) , + title = R.string.help_and_feedback, + selectedIcon = Icons.AutoMirrored.Outlined.Help, + ), NavigationItem( - title = R.string.updates , - selectedIcon = Icons.AutoMirrored.Outlined.EventNote , - ) , + title = R.string.updates, + selectedIcon = Icons.AutoMirrored.Outlined.EventNote, + ), NavigationItem( - title = R.string.share , selectedIcon = Icons.Outlined.Share - ) , + title = R.string.share, selectedIcon = Icons.Outlined.Share + ), ) val drawerState = rememberDrawerState(initialValue = DrawerValue.Closed) val scope = rememberCoroutineScope() val navController = rememberNavController() val context = LocalContext.current - val selectedItemIndex by rememberSaveable { mutableIntStateOf(- 1) } - ModalNavigationDrawer(drawerState = drawerState , drawerContent = { + val selectedItemIndex by rememberSaveable { mutableIntStateOf(-1) } + ModalNavigationDrawer(drawerState = drawerState, drawerContent = { ModalDrawerSheet { Spacer(modifier = Modifier.height(16.dp)) - drawerItems.forEachIndexed { index , item -> + drawerItems.forEachIndexed { index, item -> val title = stringResource(item.title) - NavigationDrawerItem(label = { Text(text = title) } , - selected = index == selectedItemIndex , - onClick = { - when (item.title) { - R.string.whitelist -> { - Utils.openActivity( - context , WhitelistActivity::class.java - ) - } + NavigationDrawerItem( + label = { Text(text = title) }, + selected = index == selectedItemIndex, + onClick = { + when (item.title) { + R.string.whitelist -> { + Utils.openActivity( + context, WhitelistActivity::class.java + ) + } - R.string.image_optimizer -> { - Utils.openActivity( - context , ImagePickerActivity::class.java - ) - } + R.string.image_optimizer -> { + Utils.openActivity( + context, ImagePickerActivity::class.java + ) + } - R.string.settings -> { - Utils.openActivity( - context , SettingsActivity::class.java - ) - } + R.string.settings -> { + Utils.openActivity( + context, SettingsActivity::class.java + ) + } - R.string.help_and_feedback -> { - Utils.openActivity( - context , HelpActivity::class.java - ) - } + R.string.help_and_feedback -> { + Utils.openActivity( + context, HelpActivity::class.java + ) + } - R.string.updates -> { - Utils.openUrl( - context , - "https://github.com/D4rK7355608/${context.packageName}/blob/master/CHANGELOG.md" - ) - } + R.string.updates -> { + Utils.openUrl( + context, + "https://github.com/D4rK7355608/${context.packageName}/blob/master/CHANGELOG.md" + ) + } - R.string.share -> { - val shareIntent = Intent().apply { - this.action = Intent.ACTION_SEND - this.putExtra( - Intent.EXTRA_TEXT , context.getString( - R.string.summary_share_message , - "https://play.google.com/store/apps/details?id=${context.packageName}" - ) - ) - this.type = "text/plain" - } - context.startActivity( - Intent.createChooser( - shareIntent , context.resources.getText( - R.string.send_email_using - ) - ) - ) - } - } - scope.launch { - drawerState.close() - } - } , - icon = { - Icon( - item.selectedIcon , contentDescription = title - ) - } , - badge = { - item.badgeCount?.let { - Text(text = item.badgeCount.toString()) - } - } , - modifier = Modifier.padding(NavigationDrawerItemDefaults.ItemPadding) + R.string.share -> { + val shareIntent = Intent().apply { + this.action = Intent.ACTION_SEND + this.putExtra( + Intent.EXTRA_TEXT, context.getString( + R.string.summary_share_message, + "https://play.google.com/store/apps/details?id=${context.packageName}" + ) + ) + this.type = "text/plain" + } + context.startActivity( + Intent.createChooser( + shareIntent, context.resources.getText( + R.string.send_email_using + ) + ) + ) + } + } + scope.launch { + drawerState.close() + } + }, + icon = { + Icon( + item.selectedIcon, contentDescription = title + ) + }, + badge = { + item.badgeCount?.let { + Text(text = item.badgeCount.toString()) + } + }, + modifier = Modifier.padding(NavigationDrawerItemDefaults.ItemPadding) ) if (item.title == R.string.image_optimizer) { HorizontalDivider(modifier = Modifier.padding(8.dp)) @@ -173,11 +173,11 @@ fun MainComposable() { } } - } , content = { + }, content = { Scaffold(topBar = { TopAppBar(title = { Text(text = stringResource(R.string.app_name)) - } , navigationIcon = { + }, navigationIcon = { IconButton(onClick = { scope.launch { drawerState.apply { @@ -186,41 +186,41 @@ fun MainComposable() { } }) { Icon( - imageVector = Icons.Default.Menu , contentDescription = "Menu" + imageVector = Icons.Default.Menu, contentDescription = "Menu" ) } - } , actions = { + }, actions = { IconButton(onClick = { - Utils.openActivity(context , SupportActivity::class.java) + Utils.openActivity(context, SupportActivity::class.java) }) { Icon( - Icons.Outlined.VolunteerActivism , + Icons.Outlined.VolunteerActivism, contentDescription = "Support" ) } }) - } , bottomBar = { + }, bottomBar = { NavigationBar { val navBackStackEntry by navController.currentBackStackEntryAsState() val currentRoute = navBackStackEntry?.destination?.route bottomBarItems.forEach { screen -> NavigationBarItem(icon = { val iconResource = - if (currentRoute == screen.route) screen.selectedIcon else screen.icon - Icon(iconResource , contentDescription = null) - } , - label = { Text(stringResource(screen.title)) } , - selected = currentRoute == screen.route , - onClick = { - navController.navigate(screen.route) { - popUpTo(navController.graph.startDestinationId) - launchSingleTop = true - } - }) + if (currentRoute == screen.route) screen.selectedIcon else screen.icon + Icon(iconResource, contentDescription = null) + }, + label = { Text(stringResource(screen.title)) }, + selected = currentRoute == screen.route, + onClick = { + navController.navigate(screen.route) { + popUpTo(navController.graph.startDestinationId) + launchSingleTop = true + } + }) } } }) { innerPadding -> - NavHost(navController , startDestination = Screen.Home.route) { + NavHost(navController, startDestination = Screen.Home.route) { composable(Screen.Home.route) { Box(modifier = Modifier.padding(innerPadding)) { HomeComposable() diff --git a/app/src/main/kotlin/com/d4rk/cleaner/adapters/CpuAdapter.kt b/app/src/main/kotlin/com/d4rk/cleaner/adapters/CpuAdapter.kt deleted file mode 100644 index e4e81e8..0000000 --- a/app/src/main/kotlin/com/d4rk/cleaner/adapters/CpuAdapter.kt +++ /dev/null @@ -1,41 +0,0 @@ -package com.d4rk.cleaner.adapters - -import android.view.LayoutInflater -import android.view.ViewGroup -import androidx.recyclerview.widget.DiffUtil -import androidx.recyclerview.widget.RecyclerView -import com.d4rk.cleaner.data.CpuApp -import com.d4rk.cleaner.data.CpuAppDiffCallback -import com.d4rk.cleaner.databinding.ItemCpuListBinding - -class CpuAdapter(private var apps: List) : - RecyclerView.Adapter() { - override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): CpuViewHolder { - val inflater = LayoutInflater.from(parent.context) - val binding = ItemCpuListBinding.inflate(inflater, parent, false) - return CpuViewHolder(binding) - } - - override fun onBindViewHolder(holder: CpuViewHolder, position: Int) { - val app = apps[position] - holder.bind(app) - } - - override fun getItemCount(): Int { - return apps.size - } - - fun updateAppsList(newApps: List) { - val diffCallback = CpuAppDiffCallback(apps, newApps) - val diffResult = DiffUtil.calculateDiff(diffCallback) - apps = newApps - diffResult.dispatchUpdatesTo(this) - } - - inner class CpuViewHolder(private val binding: ItemCpuListBinding) : - RecyclerView.ViewHolder(binding.root) { - fun bind(app: CpuApp) { - binding.appIcon.setImageDrawable(app.icon) - } - } -} \ No newline at end of file diff --git a/app/src/main/kotlin/com/d4rk/cleaner/data/navigation/NavigationItem.kt b/app/src/main/kotlin/com/d4rk/cleaner/data/navigation/NavigationItem.kt index b643232..db40040 100644 --- a/app/src/main/kotlin/com/d4rk/cleaner/data/navigation/NavigationItem.kt +++ b/app/src/main/kotlin/com/d4rk/cleaner/data/navigation/NavigationItem.kt @@ -3,5 +3,5 @@ package com.d4rk.cleaner.data.navigation import androidx.compose.ui.graphics.vector.ImageVector data class NavigationItem( - val title : Int , val selectedIcon : ImageVector , val badgeCount : Int? = null + val title: Int, val selectedIcon: ImageVector, val badgeCount: Int? = null ) \ No newline at end of file diff --git a/app/src/main/kotlin/com/d4rk/cleaner/data/navigation/Screen.kt b/app/src/main/kotlin/com/d4rk/cleaner/data/navigation/Screen.kt index ed36317..4771f6a 100644 --- a/app/src/main/kotlin/com/d4rk/cleaner/data/navigation/Screen.kt +++ b/app/src/main/kotlin/com/d4rk/cleaner/data/navigation/Screen.kt @@ -11,18 +11,18 @@ import androidx.compose.ui.graphics.vector.ImageVector import com.d4rk.cleaner.R sealed class Screen( - val route : String , val icon : ImageVector , val selectedIcon : ImageVector , val title : Int + val route: String, val icon: ImageVector, val selectedIcon: ImageVector, val title: Int ) { - data object Home : Screen("Home" , Icons.Outlined.Home , Icons.Filled.Home , R.string.home) + data object Home : Screen("Home", Icons.Outlined.Home, Icons.Filled.Home, R.string.home) data object AppManager : Screen( - "App Manager" , - Icons.Sharp.AppRegistration , - Icons.Rounded.AppRegistration , + "App Manager", + Icons.Sharp.AppRegistration, + Icons.Rounded.AppRegistration, R.string.app_manager ) data object MemoryManager : Screen( - "Memory Manager" , Icons.Sharp.Storage , Icons.Rounded.Storage , R.string.memory_manager + "Memory Manager", Icons.Sharp.Storage, Icons.Rounded.Storage, R.string.memory_manager ) } \ No newline at end of file diff --git a/app/src/main/kotlin/com/d4rk/cleaner/dialogs/RequireRestartDialog.kt b/app/src/main/kotlin/com/d4rk/cleaner/dialogs/RequireRestartDialog.kt index 9fe6bf2..a45a46c 100644 --- a/app/src/main/kotlin/com/d4rk/cleaner/dialogs/RequireRestartDialog.kt +++ b/app/src/main/kotlin/com/d4rk/cleaner/dialogs/RequireRestartDialog.kt @@ -27,4 +27,4 @@ class RequireRestartDialog : DialogFragment() { Process.killProcess(Process.myPid()) }.setNegativeButton(android.R.string.cancel, null).show() } -} +} \ No newline at end of file diff --git a/app/src/main/kotlin/com/d4rk/cleaner/ui/help/HelpComposable.kt b/app/src/main/kotlin/com/d4rk/cleaner/ui/help/HelpComposable.kt index 2393841..d46f184 100644 --- a/app/src/main/kotlin/com/d4rk/cleaner/ui/help/HelpComposable.kt +++ b/app/src/main/kotlin/com/d4rk/cleaner/ui/help/HelpComposable.kt @@ -150,7 +150,7 @@ fun HelpComposable(activity: HelpActivity) { }, modifier = Modifier .padding(6.dp) - .bounceClick() + .bounceClick() .constrainAs(fabButton) { bottom.linkTo(parent.bottom) end.linkTo(parent.end) diff --git a/app/src/main/kotlin/com/d4rk/cleaner/ui/home/HomeComposable.kt b/app/src/main/kotlin/com/d4rk/cleaner/ui/home/HomeComposable.kt index 1d04449..729f2dc 100644 --- a/app/src/main/kotlin/com/d4rk/cleaner/ui/home/HomeComposable.kt +++ b/app/src/main/kotlin/com/d4rk/cleaner/ui/home/HomeComposable.kt @@ -60,7 +60,7 @@ import java.io.File @Composable fun HomeComposable() { val context = LocalContext.current - val viewModel : HomeViewModel = viewModel() + val viewModel: HomeViewModel = viewModel() val progress by viewModel.progress.observeAsState(0.3f) val storageUsed by viewModel.storageUsed.observeAsState("0") val storageTotal by viewModel.storageTotal.observeAsState("0") @@ -70,72 +70,71 @@ fun HomeComposable() { ) { Box( modifier = Modifier - .weight(4f) - .fillMaxWidth() + .weight(4f) + .fillMaxWidth() ) { - if (! showCleaningComposable.value) { + if (!showCleaningComposable.value) { CircularDeterminateIndicator( - progress = progress , - storageUsed = storageUsed , - storageTotal = storageTotal , + progress = progress, + storageUsed = storageUsed, + storageTotal = storageTotal, modifier = Modifier - .align(Alignment.TopCenter) - .offset(y = 98.dp) + .align(Alignment.TopCenter) + .offset(y = 98.dp) ) Image( - painter = painterResource(R.drawable.ic_clean) , - contentDescription = null , + painter = painterResource(R.drawable.ic_clean), + contentDescription = null, modifier = Modifier - .align(Alignment.BottomCenter) - .padding(24.dp) - .size(128.dp , 66.dp) + .align(Alignment.BottomCenter) + .padding(24.dp) + .size(128.dp, 66.dp) ) - } - else { + } else { AnalyzeComposable() } } Row( modifier = Modifier - .fillMaxWidth() - .height(102.dp) - .padding(bottom = 16.dp) , + .fillMaxWidth() + .height(102.dp) + .padding(bottom = 16.dp), horizontalArrangement = Arrangement.SpaceEvenly ) { FilledTonalButton( modifier = Modifier - .weight(1f) - .fillMaxHeight() - .padding(start = 16.dp , end = 8.dp) - .bounceClick(), + .weight(1f) + .fillMaxHeight() + .padding(start = 16.dp, end = 8.dp) + .bounceClick(), onClick = { Utils.openActivity( - context , StartupActivity::class.java + context, StartupActivity::class.java ) }, shape = MaterialTheme.shapes.medium ) { Column( - horizontalAlignment = Alignment.CenterHorizontally , - verticalArrangement = Arrangement.Center , + horizontalAlignment = Alignment.CenterHorizontally, + verticalArrangement = Arrangement.Center, modifier = Modifier - .fillMaxSize() - .padding(ButtonDefaults.ContentPadding) + .fillMaxSize() + .padding(ButtonDefaults.ContentPadding) ) { Icon( - painterResource(R.drawable.ic_broom) , - contentDescription = null , + painterResource(R.drawable.ic_broom), + contentDescription = null, modifier = Modifier.size(ButtonDefaults.IconSize) ) - Text(text = "Clean" , style = MaterialTheme.typography.bodyMedium) + Text(text = "Clean", style = MaterialTheme.typography.bodyMedium) } } FilledTonalButton( modifier = Modifier - .weight(1f) - .fillMaxHeight() - .padding(start = 8.dp , end = 16.dp) - .bounceClick(), + .weight(1f) + .fillMaxHeight() + .padding(start = 8.dp, end = 16.dp) + .bounceClick(), onClick = { viewModel.analyze() showCleaningComposable.value = true @@ -143,18 +142,18 @@ fun HomeComposable() { shape = MaterialTheme.shapes.medium ) { Column( - horizontalAlignment = Alignment.CenterHorizontally , - verticalArrangement = Arrangement.Center , + horizontalAlignment = Alignment.CenterHorizontally, + verticalArrangement = Arrangement.Center, modifier = Modifier - .fillMaxSize() - .padding(ButtonDefaults.ContentPadding) + .fillMaxSize() + .padding(ButtonDefaults.ContentPadding) ) { Icon( - painterResource(R.drawable.ic_search) , - contentDescription = null , + painterResource(R.drawable.ic_search), + contentDescription = null, modifier = Modifier.size(ButtonDefaults.IconSize) ) - Text(text = "Analyze" , style = MaterialTheme.typography.bodyMedium) + Text(text = "Analyze", style = MaterialTheme.typography.bodyMedium) } } } @@ -174,35 +173,35 @@ fun HomeComposable() { */ @Composable fun CircularDeterminateIndicator( - progress : Float , storageUsed : String , storageTotal : String , modifier : Modifier = Modifier + progress: Float, storageUsed: String, storageTotal: String, modifier: Modifier = Modifier ) { val animatedProgress by animateFloatAsState( - targetValue = progress , - animationSpec = tween(durationMillis = 1000 , easing = LinearOutSlowInEasing) , + targetValue = progress, + animationSpec = tween(durationMillis = 1000, easing = LinearOutSlowInEasing), label = "" ) Box( - contentAlignment = Alignment.Center , modifier = modifier.size(240.dp) + contentAlignment = Alignment.Center, modifier = modifier.size(240.dp) ) { CircularProgressIndicator( - progress = { 1f } , - modifier = Modifier.fillMaxSize() , - color = MaterialTheme.colorScheme.primaryContainer , - strokeWidth = 6.dp , + progress = { 1f }, + modifier = Modifier.fillMaxSize(), + color = MaterialTheme.colorScheme.primaryContainer, + strokeWidth = 6.dp, ) CircularProgressIndicator( - progress = { animatedProgress } , + progress = { animatedProgress }, modifier = Modifier - .animateContentSize() - .fillMaxSize() , - color = MaterialTheme.colorScheme.primary , - strokeWidth = 6.dp , - strokeCap = StrokeCap.Round , + .animateContentSize() + .fillMaxSize(), + color = MaterialTheme.colorScheme.primary, + strokeWidth = 6.dp, + strokeCap = StrokeCap.Round, ) Text( - text = "$storageUsed/$storageTotal GB \n Used" , - textAlign = TextAlign.Center , + text = "$storageUsed/$storageTotal GB \n Used", + textAlign = TextAlign.Center, style = MaterialTheme.typography.titleLarge ) } @@ -218,7 +217,7 @@ fun CircularDeterminateIndicator( */ @Composable fun AnalyzeComposable() { - val viewModel : HomeViewModel = viewModel() + val viewModel: HomeViewModel = viewModel() val files by viewModel.scannedFiles.asFlow().collectAsState(initial = listOf()) val allFilesSelected by viewModel.allFilesSelected @@ -228,45 +227,45 @@ fun AnalyzeComposable() { Column( modifier = Modifier - .animateContentSize() - .fillMaxWidth() - .padding(16.dp) , + .animateContentSize() + .fillMaxWidth() + .padding(16.dp), horizontalAlignment = Alignment.End ) { OutlinedCard( modifier = Modifier - .weight(1f) - .fillMaxWidth() , + .weight(1f) + .fillMaxWidth(), ) { Column { LazyColumn( modifier = Modifier - .fillMaxWidth() - .padding(16.dp) + .fillMaxWidth() + .padding(16.dp) ) { items(files) { file -> FileItemComposable( - file = file , - item = file.name , - context = LocalContext.current , - viewModel = viewModel , + file = file, + item = file.name, + context = LocalContext.current, + viewModel = viewModel, ) } } } } Row( - modifier = Modifier.fillMaxWidth() , - verticalAlignment = Alignment.CenterVertically , - horizontalArrangement = Arrangement.SpaceBetween , + modifier = Modifier.fillMaxWidth(), + verticalAlignment = Alignment.CenterVertically, + horizontalArrangement = Arrangement.SpaceBetween, ) { Text( - text = "Status" , - color = MaterialTheme.colorScheme.primary , + text = "Status", + color = MaterialTheme.colorScheme.primary, ) SelectAllComposable( - checked = allFilesSelected , - onCheckedChange = { viewModel.selectAllFiles(it) } , + checked = allFilesSelected, + onCheckedChange = { viewModel.selectAllFiles(it) }, ) } } @@ -283,34 +282,34 @@ fun AnalyzeComposable() { */ @Composable fun SelectAllComposable( - checked : Boolean , onCheckedChange : (Boolean) -> Unit + checked: Boolean, onCheckedChange: (Boolean) -> Unit ) { Row( modifier = Modifier - .fillMaxWidth() - .animateContentSize() , - verticalAlignment = Alignment.CenterVertically , + .fillMaxWidth() + .animateContentSize(), + verticalAlignment = Alignment.CenterVertically, horizontalArrangement = Arrangement.End ) { val interactionSource = remember { MutableInteractionSource() } FilterChip( modifier = Modifier.bounceClick(), - selected = checked , + selected = checked, onClick = { - onCheckedChange(! checked) - } , - label = { Text("Select All") } , + onCheckedChange(!checked) + }, + label = { Text("Select All") }, leadingIcon = { - AnimatedContent(targetState = checked , label = "") { targetChecked -> + AnimatedContent(targetState = checked, label = "") { targetChecked -> if (targetChecked) { Icon( - imageVector = Icons.Filled.Check , - contentDescription = null , + imageVector = Icons.Filled.Check, + contentDescription = null, ) } } - } , - interactionSource = interactionSource , + }, + interactionSource = interactionSource, ) } } @@ -328,11 +327,11 @@ fun SelectAllComposable( * @param context The Android `Context` used to access resources like file extensions and corresponding icons. */ -val fileIconMap = mutableMapOf() +val fileIconMap = mutableMapOf() @Composable fun FileItemComposable( - file : File , item : String = "" , viewModel : HomeViewModel , context : Context + file: File, item: String = "", viewModel: HomeViewModel, context: Context ) { context.resources.getStringArray(R.array.apk_extensions).forEach { fileIconMap[it] = R.drawable.ic_apk_document @@ -357,25 +356,25 @@ fun FileItemComposable( val iconResource = fileIconMap[fileExtension] ?: R.drawable.ic_file_present Row( - modifier = Modifier.fillMaxWidth() , horizontalArrangement = Arrangement.Center + modifier = Modifier.fillMaxWidth(), horizontalArrangement = Arrangement.Center ) { Icon( - painter = painterResource(iconResource) , - contentDescription = null , + painter = painterResource(iconResource), + contentDescription = null, modifier = Modifier.align(Alignment.CenterVertically) ) Spacer(modifier = Modifier.width(4.dp)) Text( - text = item , modifier = Modifier - .weight(1f) - .align(Alignment.CenterVertically) + text = item, modifier = Modifier + .weight(1f) + .align(Alignment.CenterVertically) ) Spacer(modifier = Modifier.width(4.dp)) Checkbox( - checked = viewModel.fileSelectionStates[file] ?: false , + checked = viewModel.fileSelectionStates[file] ?: false, onCheckedChange = { isChecked -> viewModel.fileSelectionStates[file] = isChecked - } , + }, modifier = Modifier.align(Alignment.CenterVertically) ) } diff --git a/app/src/main/kotlin/com/d4rk/cleaner/ui/memory/MemoryFragment.kt b/app/src/main/kotlin/com/d4rk/cleaner/ui/memory/MemoryFragment.kt index c9f9185..cfa3e89 100644 --- a/app/src/main/kotlin/com/d4rk/cleaner/ui/memory/MemoryFragment.kt +++ b/app/src/main/kotlin/com/d4rk/cleaner/ui/memory/MemoryFragment.kt @@ -3,37 +3,37 @@ package com.d4rk.cleaner.ui.memory import androidx.fragment.app.Fragment class MemoryFragment : Fragment() { -/* private lateinit var binding: FragmentMemoryBinding - private lateinit var viewModel: MemoryViewModel - private var updateMemoryJob: Job? = null - private val handler = Handler(Looper.getMainLooper()) - private val updateInterval = 1000L - private lateinit var cpuAdapter: CpuAdapter - private val cpuAppsList = mutableListOf() - private val navController: NavController by lazy { - findNavController() - } + /* private lateinit var binding: FragmentMemoryBinding + private lateinit var viewModel: MemoryViewModel + private var updateMemoryJob: Job? = null + private val handler = Handler(Looper.getMainLooper()) + private val updateInterval = 1000L + private lateinit var cpuAdapter: CpuAdapter + private val cpuAppsList = mutableListOf() + private val navController: NavController by lazy { + findNavController() + } - override fun onCreateView( - inflater: LayoutInflater, - container: ViewGroup?, - savedInstanceState: Bundle? - ): View { - binding = FragmentMemoryBinding.inflate(inflater, container, false) - cpuAdapter = CpuAdapter(cpuAppsList) - CoroutineScope(Dispatchers.Main).launch { - updateMemoryInfo() + override fun onCreateView( + inflater: LayoutInflater, + container: ViewGroup?, + savedInstanceState: Bundle? + ): View { + binding = FragmentMemoryBinding.inflate(inflater, container, false) + cpuAdapter = CpuAdapter(cpuAppsList) + CoroutineScope(Dispatchers.Main).launch { + updateMemoryInfo() + } + return binding.root } - return binding.root - } - override fun onViewCreated(view: View, savedInstanceState: Bundle?) { - super.onViewCreated(view, savedInstanceState) - setAnimations() - FastScrollerBuilder(binding.scrollView).useMd2Style().build() - MobileAds.initialize(requireContext()) - binding.adBannerView.loadAd(AdRequest.Builder().build()) - *//* binding.buttonAnalyze.setOnClickListener { + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + setAnimations() + FastScrollerBuilder(binding.scrollView).useMd2Style().build() + MobileAds.initialize(requireContext()) + binding.adBannerView.loadAd(AdRequest.Builder().build()) + *//* binding.buttonAnalyze.setOnClickListener { navController.navigate(R.id.navigation_home) }*//* } diff --git a/app/src/main/kotlin/com/d4rk/cleaner/ui/startup/StartupComposable.kt b/app/src/main/kotlin/com/d4rk/cleaner/ui/startup/StartupComposable.kt index 13bb2d7..1e1ed15 100644 --- a/app/src/main/kotlin/com/d4rk/cleaner/ui/startup/StartupComposable.kt +++ b/app/src/main/kotlin/com/d4rk/cleaner/ui/startup/StartupComposable.kt @@ -79,8 +79,8 @@ fun StartupComposable() { } } ExtendedFloatingActionButton(modifier = Modifier - .align(Alignment.BottomEnd) - .bounceClick(), + .align(Alignment.BottomEnd) + .bounceClick(), text = { Text(stringResource(R.string.agree)) }, onClick = { Utils.openActivity( diff --git a/app/src/main/kotlin/com/d4rk/cleaner/ui/support/SupportComposable.kt b/app/src/main/kotlin/com/d4rk/cleaner/ui/support/SupportComposable.kt index 8235de2..9817e2d 100644 --- a/app/src/main/kotlin/com/d4rk/cleaner/ui/support/SupportComposable.kt +++ b/app/src/main/kotlin/com/d4rk/cleaner/ui/support/SupportComposable.kt @@ -50,67 +50,67 @@ import kotlinx.coroutines.launch @OptIn(ExperimentalMaterial3Api::class) @Composable -fun SupportComposable(activity : SupportActivity) { +fun SupportComposable(activity: SupportActivity) { val context = LocalContext.current val coroutineScope = rememberCoroutineScope() - val skuDetailsMap = remember { mutableStateMapOf() } - val billingClient = rememberBillingClient(context , coroutineScope , activity , skuDetailsMap) + val skuDetailsMap = remember { mutableStateMapOf() } + val billingClient = rememberBillingClient(context, coroutineScope, activity, skuDetailsMap) val scrollBehavior = TopAppBarDefaults.enterAlwaysScrollBehavior(rememberTopAppBarState()) - Scaffold(modifier = Modifier.nestedScroll(scrollBehavior.nestedScrollConnection) , topBar = { - LargeTopAppBar(title = { Text(stringResource(R.string.support_us)) } , navigationIcon = { + Scaffold(modifier = Modifier.nestedScroll(scrollBehavior.nestedScrollConnection), topBar = { + LargeTopAppBar(title = { Text(stringResource(R.string.support_us)) }, navigationIcon = { IconButton(onClick = { activity.finish() }) { Icon( - Icons.AutoMirrored.Filled.ArrowBack , contentDescription = null + Icons.AutoMirrored.Filled.ArrowBack, contentDescription = null ) } - } , scrollBehavior = scrollBehavior + }, scrollBehavior = scrollBehavior ) }) { paddingValues -> LazyColumn( modifier = Modifier - .fillMaxHeight() - .padding(paddingValues) , + .fillMaxHeight() + .padding(paddingValues), ) { item { Text( - text = stringResource(R.string.paid_support) , - modifier = Modifier.padding(start = 16.dp , top = 16.dp) , - style = MaterialTheme.typography.titleLarge , + text = stringResource(R.string.paid_support), + modifier = Modifier.padding(start = 16.dp, top = 16.dp), + style = MaterialTheme.typography.titleLarge, ) } item { OutlinedCard( modifier = Modifier - .fillMaxWidth() - .padding(16.dp) + .fillMaxWidth() + .padding(16.dp) ) { Column { Text( - text = stringResource(R.string.summary_donations) , + text = stringResource(R.string.summary_donations), modifier = Modifier.padding(16.dp) ) LazyRow( modifier = Modifier - .fillMaxWidth() - .padding(horizontal = 16.dp) , + .fillMaxWidth() + .padding(horizontal = 16.dp), horizontalArrangement = Arrangement.SpaceEvenly ) { item { FilledTonalButton( modifier = Modifier - .fillMaxWidth() - .bounceClick() , + .fillMaxWidth() + .bounceClick(), onClick = { activity.initiatePurchase( - "low_donation" , skuDetailsMap , billingClient + "low_donation", skuDetailsMap, billingClient ) - } , + }, ) { Icon( - painterResource(R.drawable.ic_paid) , - contentDescription = null , + painterResource(R.drawable.ic_paid), + contentDescription = null, modifier = Modifier.size(ButtonDefaults.IconSize) ) Spacer(modifier = Modifier.size(ButtonDefaults.IconSpacing)) @@ -120,17 +120,17 @@ fun SupportComposable(activity : SupportActivity) { item { FilledTonalButton( modifier = Modifier - .fillMaxWidth() - .bounceClick() , + .fillMaxWidth() + .bounceClick(), onClick = { activity.initiatePurchase( - "normal_donation" , skuDetailsMap , billingClient + "normal_donation", skuDetailsMap, billingClient ) - } , + }, ) { Icon( - painterResource(R.drawable.ic_paid) , - contentDescription = null , + painterResource(R.drawable.ic_paid), + contentDescription = null, modifier = Modifier.size(ButtonDefaults.IconSize) ) Spacer(modifier = Modifier.size(ButtonDefaults.IconSpacing)) @@ -140,24 +140,24 @@ fun SupportComposable(activity : SupportActivity) { } LazyRow( modifier = Modifier - .fillMaxWidth() - .padding(16.dp) , + .fillMaxWidth() + .padding(16.dp), horizontalArrangement = Arrangement.SpaceEvenly ) { item { FilledTonalButton( modifier = Modifier - .fillMaxWidth() - .bounceClick() , + .fillMaxWidth() + .bounceClick(), onClick = { activity.initiatePurchase( - "high_donation" , skuDetailsMap , billingClient + "high_donation", skuDetailsMap, billingClient ) - } , + }, ) { Icon( - painterResource(R.drawable.ic_paid) , - contentDescription = null , + painterResource(R.drawable.ic_paid), + contentDescription = null, modifier = Modifier.size(ButtonDefaults.IconSize) ) Spacer(modifier = Modifier.size(ButtonDefaults.IconSpacing)) @@ -168,17 +168,17 @@ fun SupportComposable(activity : SupportActivity) { FilledTonalButton( modifier = Modifier - .fillMaxWidth() - .bounceClick() , + .fillMaxWidth() + .bounceClick(), onClick = { activity.initiatePurchase( - "extreme_donation" , skuDetailsMap , billingClient + "extreme_donation", skuDetailsMap, billingClient ) - } , + }, ) { Icon( - painterResource(R.drawable.ic_paid) , - contentDescription = null , + painterResource(R.drawable.ic_paid), + contentDescription = null, modifier = Modifier.size(ButtonDefaults.IconSize) ) Spacer(modifier = Modifier.size(ButtonDefaults.IconSpacing)) @@ -191,24 +191,24 @@ fun SupportComposable(activity : SupportActivity) { } item { Text( - text = stringResource(R.string.non_paid_support) , - modifier = Modifier.padding(start = 16.dp) , - style = MaterialTheme.typography.titleLarge , + text = stringResource(R.string.non_paid_support), + modifier = Modifier.padding(start = 16.dp), + style = MaterialTheme.typography.titleLarge, ) } item { FilledTonalButton( onClick = { - Utils.openUrl(context , "https://bit.ly/3p8bpj") - } , + Utils.openUrl(context, "https://bit.ly/3p8bpj") + }, modifier = Modifier - .fillMaxWidth() - .bounceClick() - .padding(16.dp) , + .fillMaxWidth() + .bounceClick() + .padding(16.dp), ) { Icon( - painterResource(R.drawable.ic_paid) , - contentDescription = null , + painterResource(R.drawable.ic_paid), + contentDescription = null, modifier = Modifier.size(ButtonDefaults.IconSize) ) Spacer(modifier = Modifier.size(ButtonDefaults.IconSpacing)) @@ -222,20 +222,20 @@ fun SupportComposable(activity : SupportActivity) { @Composable fun rememberBillingClient( - context : Context , - coroutineScope : CoroutineScope , - activity : SupportActivity , - skuDetailsMap : SnapshotStateMap -) : BillingClient { + context: Context, + coroutineScope: CoroutineScope, + activity: SupportActivity, + skuDetailsMap: SnapshotStateMap +): BillingClient { val billingClient = remember { - BillingClient.newBuilder(context).setListener { _ , _ -> }.enablePendingPurchases().build() + BillingClient.newBuilder(context).setListener { _, _ -> }.enablePendingPurchases().build() } DisposableEffect(billingClient) { billingClient.startConnection(object : BillingClientStateListener { - override fun onBillingSetupFinished(billingResult : BillingResult) { + override fun onBillingSetupFinished(billingResult: BillingResult) { if (billingResult.responseCode == BillingClient.BillingResponseCode.OK) { coroutineScope.launch { - activity.querySkuDetails(billingClient , skuDetailsMap) + activity.querySkuDetails(billingClient, skuDetailsMap) } } } diff --git a/app/src/main/kotlin/com/d4rk/cleaner/ui/viewmodel/MemoryViewModel.kt b/app/src/main/kotlin/com/d4rk/cleaner/ui/viewmodel/MemoryViewModel.kt deleted file mode 100644 index 4fcad39..0000000 --- a/app/src/main/kotlin/com/d4rk/cleaner/ui/viewmodel/MemoryViewModel.kt +++ /dev/null @@ -1,68 +0,0 @@ -package com.d4rk.cleaner.ui.viewmodel - -import androidx.lifecycle.MutableLiveData -import androidx.lifecycle.ViewModel -import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.SupervisorJob -import kotlinx.coroutines.cancelChildren -import kotlinx.coroutines.delay -import kotlinx.coroutines.isActive -import kotlinx.coroutines.launch -import kotlinx.coroutines.withContext -import java.io.BufferedReader -import java.io.InputStreamReader - -class MemoryViewModel : ViewModel() { - private val cpuTemperatureLiveData = MutableLiveData() - private val viewModelJob = SupervisorJob() - private val viewModelScope = CoroutineScope(Dispatchers.Main + viewModelJob) - private val updateInterval = 1000L - - init { - startCpuTemperatureUpdates() - } - - fun startCpuTemperatureUpdates() { - viewModelScope.launch { - while (isActive) { - val temperature = calculateCpuTemperature() - cpuTemperatureLiveData.postValue(temperature) - delay(updateInterval) - } - } - } - - fun stopCpuTemperatureUpdates() { - viewModelJob.cancelChildren() - } - - fun getCpuTemperature(): Double { - return cpuTemperatureLiveData.value ?: 0.0 - } - - private suspend fun calculateCpuTemperature(): Double? = withContext(Dispatchers.IO) { - var temperature: Double? = null - try { - val process = Runtime.getRuntime().exec("cat /sys/class/thermal/thermal_zone0/temp") - val reader = BufferedReader(InputStreamReader(process.inputStream)) - val tempStr = reader.readLine() - reader.close() - process.waitFor() - if (!tempStr.isNullOrEmpty()) { - val tempMilliCelsius = tempStr.toIntOrNull() - if (tempMilliCelsius != null) { - temperature = tempMilliCelsius / 1000.0 - } - } - } catch (e: Exception) { - e.printStackTrace() - } - return@withContext temperature - } - - override fun onCleared() { - super.onCleared() - viewModelJob.cancel() - } -} \ No newline at end of file diff --git a/app/src/main/kotlin/com/d4rk/cleaner/ui/whitelist/WhitelistActivity.kt b/app/src/main/kotlin/com/d4rk/cleaner/ui/whitelist/WhitelistActivity.kt index ec0f8ce..45fbbdd 100644 --- a/app/src/main/kotlin/com/d4rk/cleaner/ui/whitelist/WhitelistActivity.kt +++ b/app/src/main/kotlin/com/d4rk/cleaner/ui/whitelist/WhitelistActivity.kt @@ -1,14 +1,26 @@ package com.d4rk.cleaner.ui.whitelist import android.os.Bundle +import androidx.activity.compose.setContent +import androidx.activity.enableEdgeToEdge import androidx.appcompat.app.AppCompatActivity -import com.d4rk.cleaner.databinding.ActivityWhitelistBinding +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.Surface +import androidx.compose.ui.Modifier +import com.d4rk.cleaner.ui.settings.display.theme.AppTheme class WhitelistActivity : AppCompatActivity() { - private lateinit var binding: ActivityWhitelistBinding override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) - binding = ActivityWhitelistBinding.inflate(layoutInflater) - setContentView(binding.root) + enableEdgeToEdge() + setContent { + AppTheme { + Surface( + modifier = Modifier.fillMaxSize(), color = MaterialTheme.colorScheme.background + ) { + } + } + } } } \ No newline at end of file diff --git a/app/src/main/kotlin/com/d4rk/cleaner/utils/ComposablesUtils.kt b/app/src/main/kotlin/com/d4rk/cleaner/utils/ComposablesUtils.kt index e5086b1..3aa4dfa 100644 --- a/app/src/main/kotlin/com/d4rk/cleaner/utils/ComposablesUtils.kt +++ b/app/src/main/kotlin/com/d4rk/cleaner/utils/ComposablesUtils.kt @@ -42,49 +42,47 @@ import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.unit.dp /** - * Creates a clickable card with a title and a switch within your app's preferences. + * Creates a clickable card with a title and a switch for app preference screens. * - * This composable is useful for displaying settings or preferences that can be toggled on or off. - * When clicked, the card toggles the switch and calls the provided callback function. + * This composable function displays a card with a title and a switch. The entire card is clickable and toggles the switch when clicked, calling the provided `onSwitchToggled` callback function with the new state. + * The switch displays a check icon when it's in the 'on' state. * * @param title The text displayed on the card's title. - * @param switchState A state variable holding the current on/off state of the switch (true for on). - * @param onSwitchToggled A callback function called whenever the switch is toggled. - * This function receives the new state of the switch (boolean) as a parameter. + * @param switchState A state variable holding the current on/off state of the switch. Set to true for on and false for off. + * @param onSwitchToggled A callback function that is called whenever the switch is toggled. This function receives the new state of the switch (boolean) as a parameter. */ @Composable fun SwitchCardComposable( - title : String , switchState : State , onSwitchToggled : (Boolean) -> Unit + title: String, switchState: State, onSwitchToggled: (Boolean) -> Unit ) { Card(modifier = Modifier - .fillMaxWidth() - .padding(24.dp) - .clip(RoundedCornerShape(28.dp)) - .clickable { - onSwitchToggled(! switchState.value) - }) { + .fillMaxWidth() + .padding(24.dp) + .clip(RoundedCornerShape(28.dp)) + .clickable { + onSwitchToggled(!switchState.value) + }) { Row( modifier = Modifier - .fillMaxWidth() - .padding(16.dp) , - horizontalArrangement = Arrangement.SpaceBetween , + .fillMaxWidth() + .padding(16.dp), + horizontalArrangement = Arrangement.SpaceBetween, verticalAlignment = Alignment.CenterVertically ) { Text(text = title) - Switch(checked = switchState.value , - onCheckedChange = onSwitchToggled , - thumbContent = if (switchState.value) { - { - Icon( - Icons.Filled.Check , - contentDescription = null , - modifier = Modifier.size(SwitchDefaults.IconSize) , - ) - } - } - else { - null - }) + Switch(checked = switchState.value, + onCheckedChange = onSwitchToggled, + thumbContent = if (switchState.value) { + { + Icon( + Icons.Filled.Check, + contentDescription = null, + modifier = Modifier.size(SwitchDefaults.IconSize), + ) + } + } else { + null + }) } } } @@ -92,61 +90,58 @@ fun SwitchCardComposable( /** * Displays a category header within your app's preference screens. * - * Use this composable within a scrollable container to separate different sections of - * your app's preferences with clear category titles. + * This composable function is used to display a category header in your app's preference screens. It helps in separating different sections of your app's preferences with clear category titles. The title is displayed in a distinct style and color to differentiate it from other preference items. * - * @param title The text to be displayed as the category header. + * @param title The text to be displayed as the category header. This is typically the name of the category. */ @Composable fun PreferenceCategoryItem( - title : String + title: String ) { Text( - text = title , - color = MaterialTheme.colorScheme.primary , - style = MaterialTheme.typography.bodyMedium.copy(fontWeight = FontWeight.SemiBold) , - modifier = Modifier.padding(start = 16.dp , top = 16.dp) + text = title, + color = MaterialTheme.colorScheme.primary, + style = MaterialTheme.typography.bodyMedium.copy(fontWeight = FontWeight.SemiBold), + modifier = Modifier.padding(start = 16.dp, top = 16.dp) ) } /** * Creates a clickable preference item for app preference screens. * - * This composable displays a preference item with an optional icon, title, and summary. - * Clicking the entire row triggers the provided `onClick` callback function. + * This composable function displays a preference item with an optional icon, title, and summary. The entire row is clickable and triggers the provided `onClick` callback function when clicked. * - * @param icon An optional icon to be displayed at the beginning of the preference item. (Provide a `Painter` object) - * @param title The main title text displayed for the preference item. - * @param summary An optional secondary text displayed below the title for additional information. - * @param onClick A callback function that is called when the entire preference item is clicked. - * Leave this empty if no action is needed on click. + * @param icon An optional icon to be displayed at the start of the preference item. If provided, it should be an `ImageVector` object. + * @param title An optional main title text displayed for the preference item. + * @param summary An optional secondary text displayed below the title for additional information about the preference. + * @param onClick A callback function that is called when the entire preference item is clicked. If no action is needed on click, this can be left empty. */ @Composable fun PreferenceItem( - icon : ImageVector? = null , - title : String? = null , - summary : String? = null , - onClick : () -> Unit = {} + icon: ImageVector? = null, + title: String? = null, + summary: String? = null, + onClick: () -> Unit = {} ) { Row( modifier = Modifier - .fillMaxWidth() - .clip(RoundedCornerShape(16.dp)) - .clickable(onClick = onClick) , verticalAlignment = Alignment.CenterVertically + .fillMaxWidth() + .clip(RoundedCornerShape(16.dp)) + .clickable(onClick = onClick), verticalAlignment = Alignment.CenterVertically ) { icon?.let { Spacer(modifier = Modifier.width(16.dp)) - Icon(it , contentDescription = null) + Icon(it, contentDescription = null) Spacer(modifier = Modifier.width(16.dp)) } Column( modifier = Modifier.padding(16.dp) ) { title?.let { - Text(text = it , style = MaterialTheme.typography.titleLarge) + Text(text = it, style = MaterialTheme.typography.titleLarge) } summary?.let { - Text(text = it , style = MaterialTheme.typography.bodyMedium) + Text(text = it, style = MaterialTheme.typography.bodyMedium) } } } @@ -155,49 +150,48 @@ fun PreferenceItem( /** * Creates a clickable preference item with a switch for app preference screens. * - * This composable combines an icon (optional), title, summary (optional), and a switch. Clicking - * the entire row toggles the switch and calls the provided `onCheckedChange` callback function. + * This composable function combines an optional icon, title, optional summary, and a switch into a single row. + * The entire row is clickable and toggles the switch when clicked, calling the provided `onCheckedChange` callback function with the new state. * - * @param icon An optional icon to be displayed at the beginning of the preference item. (Provide a `Painter` object) + * @param icon An optional icon to be displayed at the start of the preference item. If provided, it should be an `ImageVector` object. * @param title The main title text displayed for the preference item. - * @param summary An optional secondary text displayed below the title for additional information. - * @param checked The initial state of the switch (true for on, false for off). - * @param onCheckedChange A callback function called whenever the switch is toggled. - * This function receives the new state of the switch (boolean) as a parameter. + * @param summary An optional secondary text displayed below the title for additional information about the preference. + * @param checked The initial state of the switch. Set to true for on and false for off. + * @param onCheckedChange A callback function that is called whenever the switch is toggled. This function receives the new state of the switch (boolean) as a parameter. */ @Composable fun SwitchPreferenceItem( - icon : ImageVector? = null , - title : String , - summary : String? = null , - checked : Boolean , - onCheckedChange : (Boolean) -> Unit + icon: ImageVector? = null, + title: String, + summary: String? = null, + checked: Boolean, + onCheckedChange: (Boolean) -> Unit ) { Row( modifier = Modifier - .fillMaxWidth() - .clip(RoundedCornerShape(16.dp)) - .clickable(onClick = { onCheckedChange(! checked) }) , + .fillMaxWidth() + .clip(RoundedCornerShape(16.dp)) + .clickable(onClick = { onCheckedChange(!checked) }), verticalAlignment = Alignment.CenterVertically ) { icon?.let { Spacer(modifier = Modifier.width(16.dp)) - Icon(it , contentDescription = null) + Icon(it, contentDescription = null) Spacer(modifier = Modifier.width(16.dp)) } Column( modifier = Modifier - .padding(16.dp) - .weight(1f) + .padding(16.dp) + .weight(1f) ) { - Text(text = title , style = MaterialTheme.typography.titleLarge) + Text(text = title, style = MaterialTheme.typography.titleLarge) summary?.let { - Text(text = it , style = MaterialTheme.typography.bodyMedium) + Text(text = it, style = MaterialTheme.typography.bodyMedium) } } Switch( - checked = checked , - onCheckedChange = onCheckedChange , + checked = checked, + onCheckedChange = onCheckedChange, modifier = Modifier.padding(16.dp) ) } @@ -206,91 +200,89 @@ fun SwitchPreferenceItem( /** * Creates a clickable preference item with a switch and a divider for app preference screens. * - * This composable combines an optional icon, title, summary, switch, and a divider. Clicking - * the entire row triggers the provided `onClick` callback function. Toggling the switch calls + * This composable function combines an optional icon, title, summary, switch, and a divider into a single row. + * The entire row is clickable and triggers the provided `onClick` callback function when clicked. + * The switch is toggled on or off based on the `checked` parameter, and any change in its state calls * the `onCheckedChange` callback with the new state. * - * @param icon An optional icon to be displayed at the beginning of the preference item. (Provide a `Painter` object) + * @param icon An optional icon to be displayed at the start of the preference item. If provided, it should be an `ImageVector` object. * @param title The main title text displayed for the preference item. - * @param summary A secondary text displayed below the title for additional information. - * @param checked The initial state of the switch (true for on, false for off). - * @param onCheckedChange A callback function called whenever the switch is toggled. - * This function receives the new state of the switch (boolean) as a parameter. - * @param onClick A callback function that is called when the entire preference item is clicked. - * Leave this empty if no action is needed on click. + * @param summary A secondary text displayed below the title for additional information about the preference. + * @param checked The initial state of the switch. Set to true for on and false for off. + * @param onCheckedChange A callback function that is called whenever the switch is toggled. This function receives the new state of the switch (boolean) as a parameter. + * @param onClick A callback function that is called when the entire preference item is clicked. If no action is needed on click, this can be left empty. */ @Composable fun SwitchPreferenceItemWithDivider( - icon : ImageVector? = null , - title : String , - summary : String , - checked : Boolean , - onCheckedChange : (Boolean) -> Unit , - onClick : () -> Unit + icon: ImageVector? = null, + title: String, + summary: String, + checked: Boolean, + onCheckedChange: (Boolean) -> Unit, + onClick: () -> Unit ) { Row( modifier = Modifier - .fillMaxWidth() - .clip(RoundedCornerShape(16.dp)) - .clickable(onClick = onClick) , verticalAlignment = Alignment.CenterVertically + .fillMaxWidth() + .clip(RoundedCornerShape(16.dp)) + .clickable(onClick = onClick), verticalAlignment = Alignment.CenterVertically ) { icon?.let { Spacer(modifier = Modifier.width(16.dp)) - Icon(it , contentDescription = null) + Icon(it, contentDescription = null) Spacer(modifier = Modifier.width(16.dp)) } Column( modifier = Modifier - .padding(16.dp) - .weight(1f) + .padding(16.dp) + .weight(1f) ) { - Text(text = title , style = MaterialTheme.typography.titleLarge) - Text(text = summary , style = MaterialTheme.typography.bodyMedium) + Text(text = title, style = MaterialTheme.typography.titleLarge) + Text(text = summary, style = MaterialTheme.typography.bodyMedium) } VerticalDivider( modifier = Modifier - .height(32.dp) - .align(Alignment.CenterVertically) , - color = MaterialTheme.colorScheme.onSurface.copy(alpha = 0.3f) , + .height(32.dp) + .align(Alignment.CenterVertically), + color = MaterialTheme.colorScheme.onSurface.copy(alpha = 0.3f), thickness = 1.dp ) Switch( - checked = checked , - onCheckedChange = onCheckedChange , + checked = checked, + onCheckedChange = onCheckedChange, modifier = Modifier.padding(16.dp) ) } } -enum class ButtonState { Pressed , Idle } +enum class ButtonState { Pressed, Idle } @SuppressLint("ReturnFromAwaitPointerEventScope") @Composable fun Modifier.bounceClick() = composed { var buttonState by remember { mutableStateOf(ButtonState.Idle) } val scale by animateFloatAsState( - if (buttonState == ButtonState.Pressed) 0.95f else 1f , label = "" + if (buttonState == ButtonState.Pressed) 0.95f else 1f, label = "" ) this - .graphicsLayer { - scaleX = scale - scaleY = scale - } - .clickable(interactionSource = remember { MutableInteractionSource() } , - indication = null , - onClick = { }) - .pointerInput(buttonState) { - awaitPointerEventScope { - buttonState = if (buttonState == ButtonState.Pressed) { - waitForUpOrCancellation() - ButtonState.Idle - } - else { - awaitFirstDown(false) - ButtonState.Pressed - } + .graphicsLayer { + scaleX = scale + scaleY = scale + } + .clickable(interactionSource = remember { MutableInteractionSource() }, + indication = null, + onClick = { }) + .pointerInput(buttonState) { + awaitPointerEventScope { + buttonState = if (buttonState == ButtonState.Pressed) { + waitForUpOrCancellation() + ButtonState.Idle + } else { + awaitFirstDown(false) + ButtonState.Pressed } } + } } \ No newline at end of file diff --git a/app/src/main/kotlin/com/d4rk/cleaner/utils/FileScanner.kt b/app/src/main/kotlin/com/d4rk/cleaner/utils/FileScanner.kt index 5a5fd24..e8d9aad 100644 --- a/app/src/main/kotlin/com/d4rk/cleaner/utils/FileScanner.kt +++ b/app/src/main/kotlin/com/d4rk/cleaner/utils/FileScanner.kt @@ -8,13 +8,12 @@ import kotlinx.coroutines.flow.first import java.io.File /** - * Utility class for scanning and filtering files based on specified preferences. + * A utility class for scanning and filtering files based on user-defined preferences. * - * This class scans and filters files based on preferences such as aggressive filtering, generic filtering, - * and other criteria defined by the provided resources. + * This class scans and filters files in the external storage directory based on user preferences such as file types (e.g., generic, archive, APK, audio, video, image files). The preferences are retrieved from a DataStore instance and the file types are defined in the provided Resources instance. * - * @property dataStore DataStore instance for accessing preferences. - * @property resources Resources instance for accessing string arrays. + * @property dataStore A DataStore instance used for accessing user preferences. + * @property resources A Resources instance used for accessing string arrays that define file types. */ class FileScanner(private val dataStore: DataStore, private val resources: Resources) { @@ -24,8 +23,7 @@ class FileScanner(private val dataStore: DataStore, private val resources: Resou /** * Initiates the file scanning process asynchronously. * - * This function loads preferences, retrieves all files, applies filters, and logs the result. - * The scanning process runs on a background thread to avoid blocking the main thread. + * This function loads user preferences, retrieves all files in the external storage directory, applies filters based on the preferences, and stores the filtered files. The scanning process runs asynchronously to avoid blocking the main thread. * * @throws Exception If an error occurs during the scanning process. */ @@ -36,31 +34,25 @@ class FileScanner(private val dataStore: DataStore, private val resources: Resou } /** - * Loads preferences from the data store into the [preferences] map. + * Loads user preferences from the data store into the [preferences] map. + * + * The preferences include whether to filter generic files, archive files, APK files, audio files, video files, and image files. */ private suspend fun loadPreferences() { preferences = mapOf( "generic_extensions" to dataStore.genericFilter.first(), - // "delete_empty_folders" to dataStore.deleteEmptyFolders.first() , "archive_extensions" to dataStore.deleteArchives.first(), - // "delete_invalid_media" to dataStore.deleteInvalidMedia.first() , - // "delete_corpse_files" to dataStore.deleteCorpseFiles.first() , "apk_extensions" to dataStore.deleteApkFiles.first(), "image_extensions" to dataStore.deleteImageFiles.first(), "audio_extensions" to dataStore.deleteAudioFiles.first(), - "video_extensions" to dataStore.deleteVideoFiles.first(), - //"double_checker" to dataStore.doubleChecker.first() , - //"clipboard_clean" to dataStore.clipboardClean.first() , - // "auto_whitelist" to dataStore.autoWhitelist.first() , - //"one_click_clean" to dataStore.oneClickClean.first() , - // "daily_clean" to dataStore.dailyCleaner.first() + "video_extensions" to dataStore.deleteVideoFiles.first() ) } /** * Retrieves all files from the external storage directory recursively. * - * @return List of all files found in the external storage. + * @return A list of all files found in the external storage directory. */ private fun getAllFiles(): List { val files = mutableListOf() @@ -81,13 +73,12 @@ class FileScanner(private val dataStore: DataStore, private val resources: Resou } /** - * Filters files based on defined preferences and returns them as a sequence. + * Filters files based on user-defined preferences and returns them as a sequence. * - * This function takes a list of all files as input and returns a sequence of files that match the defined preferences. - * The sequence allows for real-time display of filtered files as they are discovered during the scanning process. + * This function takes a list of all files as input and returns a sequence of files that match the user-defined preferences. The sequence allows for real-time display of filtered files as they are discovered during the scanning process. * * @param allFiles The list of all files to filter. - * @return A sequence of files filtered based on preferences. + * @return A sequence of files filtered based on user-defined preferences. */ private fun filterFiles(allFiles: List): Sequence { return sequence { @@ -137,6 +128,11 @@ class FileScanner(private val dataStore: DataStore, private val resources: Resou } } + /** + * Returns the list of filtered files. + * + * @return A list of files that match the user-defined preferences. + */ fun getFilteredFiles(): List { return filteredFiles } diff --git a/app/src/main/kotlin/com/d4rk/cleaner/utils/Utils.kt b/app/src/main/kotlin/com/d4rk/cleaner/utils/Utils.kt index b562144..e9f1074 100644 --- a/app/src/main/kotlin/com/d4rk/cleaner/utils/Utils.kt +++ b/app/src/main/kotlin/com/d4rk/cleaner/utils/Utils.kt @@ -6,14 +6,20 @@ import android.net.Uri import android.provider.Settings /** - * Utility object for common operations. + * A utility object for performing common operations such as opening URLs, activities, and app notification settings. + * + * This object provides functions to open a URL in the default browser, open an activity, and open the app's notification settings. + * All operations are performed in the context of an Android application. */ object Utils { /** - * Opens a URL in the default browser. + * Opens a specified URL in the default browser. * - * @param context The Android context. + * This function creates an Intent with the ACTION_VIEW action and the specified URL, and starts an activity with this intent. + * The activity runs in a new task. + * + * @param context The Android context in which the URL should be opened. * @param url The URL to open. */ fun openUrl(context: Context, url: String) { @@ -24,9 +30,11 @@ object Utils { } /** - * Opens an activity. + * Opens a specified activity. + * + * This function creates an Intent with the specified activity class, and starts an activity with this intent. The activity runs in a new task. * - * @param context The Android context. + * @param context The Android context in which the activity should be opened. * @param activityClass The class of the activity to open. */ fun openActivity(context: Context, activityClass: Class<*>) { @@ -37,9 +45,12 @@ object Utils { } /** - * Opens the app notification settings. + * Opens the app's notification settings. + * + * This function creates an Intent with the ACTION_APP_NOTIFICATION_SETTINGS action and the app's package name, and starts an activity with this intent. + * The activity runs in a new task. * - * @param context The Android context. + * @param context The Android context in which the app's notification settings should be opened. */ fun openAppNotificationSettings(context: Context) { val intent = Intent(Settings.ACTION_APP_NOTIFICATION_SETTINGS).apply { diff --git a/app/src/main/res/layout/fragment_memory.xml b/app/src/main/res/layout/fragment_memory.xml index 590c7a6..8054771 100644 --- a/app/src/main/res/layout/fragment_memory.xml +++ b/app/src/main/res/layout/fragment_memory.xml @@ -1,7 +1,4 @@