Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions app/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,9 @@ dependencies {
}

implementation(dependencyNotation = libs.androidx.constraintlayout.compose)
implementation(dependencyNotation = libs.glance)
implementation(dependencyNotation = libs.glance.appwidget)
implementation(dependencyNotation = libs.glance.material3)

// Image Compression
implementation(dependencyNotation = libs.compressor)
Expand Down
10 changes: 10 additions & 0 deletions app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -201,5 +201,15 @@
android:value="eu_consent_policy" />

<receiver android:name=".app.notifications.notifications.CleanupDismissReceiver" android:exported="false" />
<receiver
android:name=".app.widgets.StorageWidgetReceiver"
android:exported="true">
<intent-filter>
<action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
</intent-filter>
<meta-data
android:name="android.appwidget.provider"
android:resource="@xml/cleaner_widget_info" />
</receiver>
</application>
</manifest>
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import android.content.Context
import androidx.work.Constraints
import androidx.work.ExistingPeriodicWorkPolicy
import androidx.work.PeriodicWorkRequestBuilder
import androidx.work.OneTimeWorkRequestBuilder
import androidx.work.WorkManager
import com.d4rk.cleaner.core.data.datastore.DataStore
import kotlinx.coroutines.runBlocking
Expand Down Expand Up @@ -32,4 +33,10 @@ object AutoCleanScheduler {
fun cancel(context: Context) {
WorkManager.getInstance(context).cancelUniqueWork(WORK_NAME)
}

fun runOnce(context: Context) {
val request = OneTimeWorkRequestBuilder<AutoCleanWorker>()
.build()
WorkManager.getInstance(context).enqueue(request)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ fun CleaningSettingsList(paddingValues : PaddingValues) {
val streakReminderEnabled: Boolean by dataStore.streakReminderEnabled.collectAsState(initial = false)
val showStreakCardPref: Boolean by dataStore.showStreakCard.collectAsState(initial = true)
val autoCleanEnabled: Boolean by dataStore.autoCleanEnabled.collectAsState(initial = false)
val widgetActionsEnabled: Boolean by dataStore.widgetActionsEnabled.collectAsState(initial = true)

LazyColumn(
modifier = Modifier
Expand Down Expand Up @@ -307,5 +308,25 @@ fun CleaningSettingsList(paddingValues : PaddingValues) {
}
}
}

item {
PreferenceCategoryItem(title = stringResource(id = R.string.widget_settings_title))
SmallVerticalSpacer()

Column(
modifier = Modifier
.padding(horizontal = SizeConstants.LargeSize)
.clip(shape = RoundedCornerShape(size = SizeConstants.LargeSize))
) {
SwitchPreferenceItem(
title = stringResource(id = R.string.enable_widget_actions),
checked = widgetActionsEnabled,
) { isChecked ->
CoroutineScope(Dispatchers.IO).launch {
dataStore.saveWidgetActionsEnabled(isChecked)
}
}
}
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
package com.d4rk.cleaner.app.widgets

import android.content.Context
import android.content.Intent
import androidx.compose.runtime.Composable
import androidx.compose.ui.unit.dp
import androidx.glance.GlanceId
import androidx.glance.GlanceModifier
import androidx.glance.Image
import androidx.glance.ImageProvider
import androidx.glance.action.clickable
import androidx.glance.appwidget.GlanceAppWidget
import androidx.glance.appwidget.GlanceAppWidgetReceiver
import androidx.glance.appwidget.action.ActionCallback
import androidx.glance.appwidget.action.ActionParameters
import androidx.glance.appwidget.action.actionRunCallback
import androidx.glance.appwidget.provideContent
import androidx.glance.layout.Alignment
import androidx.glance.layout.Row
import androidx.glance.layout.Spacer
import androidx.glance.layout.fillMaxWidth
import androidx.glance.layout.padding
import androidx.glance.layout.width
import com.d4rk.cleaner.R
import com.d4rk.cleaner.app.auto.AutoCleanScheduler
import com.d4rk.cleaner.app.main.ui.MainActivity
import com.d4rk.cleaner.core.data.datastore.DataStore
import kotlinx.coroutines.flow.first
import kotlinx.coroutines.runBlocking

class CleanerWidgetReceiver : GlanceAppWidgetReceiver() {
override val glanceAppWidget: GlanceAppWidget = CleanerWidget()
}

class CleanerWidget : GlanceAppWidget() {
override suspend fun provideGlance(context: Context, id: GlanceId) {
provideContent { WidgetContent() }
}

@Composable
private fun WidgetContent() {
Row(
modifier = GlanceModifier.fillMaxWidth().padding(8.dp),
verticalAlignment = Alignment.CenterVertically,
horizontalAlignment = Alignment.CenterHorizontally
) {
Image(
provider = ImageProvider(R.drawable.ic_folder_search),
contentDescription = "scan",
modifier = GlanceModifier.clickable(actionRunCallback<OpenScanAction>())
)
Spacer(modifier = GlanceModifier.width(16.dp))
Image(
provider = ImageProvider(R.drawable.ic_auto_fix_high),
contentDescription = "clean",
modifier = GlanceModifier.clickable(actionRunCallback<RunCleanAction>())
)
}
}
}

class OpenScanAction : ActionCallback {
override suspend fun onAction(context: Context, glanceId: GlanceId, parameters: ActionParameters) {
if (context.isWidgetActionsEnabled()) {
val intent = Intent(context, MainActivity::class.java).apply {
flags = Intent.FLAG_ACTIVITY_NEW_TASK
putExtra("open_scan", true)
}
context.startActivity(intent)
}
}
}

class RunCleanAction : ActionCallback {
override suspend fun onAction(context: Context, glanceId: GlanceId, parameters: ActionParameters) {
if (context.isWidgetActionsEnabled()) {
AutoCleanScheduler.runOnce(context)
}
}
}

private fun Context.isWidgetActionsEnabled(): Boolean {
val ds = DataStore(this)
return runBlocking { ds.widgetActionsEnabled.first() }
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package com.d4rk.cleaner.app.widgets

import androidx.glance.appwidget.GlanceAppWidget
import androidx.glance.appwidget.GlanceAppWidgetReceiver
import com.d4rk.cleaner.app.widgets.ui.StorageStatsWidget

class StorageWidgetReceiver : GlanceAppWidgetReceiver() {
override val glanceAppWidget: GlanceAppWidget = StorageStatsWidget()
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package com.d4rk.cleaner.app.widgets.data

import android.app.Application
import com.d4rk.cleaner.app.clean.memory.data.MemoryRepositoryImpl
import com.d4rk.cleaner.app.clean.memory.domain.data.model.StorageInfo

class StorageStatsRepository(private val application: Application) {
suspend fun getStorageInfo(): StorageInfo {
return MemoryRepositoryImpl(application).getStorageInfo()
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package com.d4rk.cleaner.app.widgets.domain.actions

import android.content.Context
import android.content.Intent
import androidx.glance.GlanceId
import androidx.glance.action.ActionParameters
import androidx.glance.appwidget.action.ActionCallback
import com.d4rk.cleaner.app.main.ui.MainActivity
import com.d4rk.cleaner.app.widgets.domain.actions.isWidgetActionsEnabled

class OpenScanAction : ActionCallback {
override suspend fun onAction(context: Context, glanceId: androidx.glance.GlanceId, parameters: ActionParameters) {
if (context.isWidgetActionsEnabled()) {
val intent = Intent(context, MainActivity::class.java).apply {
flags = Intent.FLAG_ACTIVITY_NEW_TASK
putExtra("open_scan", true)
}
context.startActivity(intent)
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package com.d4rk.cleaner.app.widgets.domain.actions

import android.content.Context
import androidx.glance.GlanceId
import androidx.glance.action.ActionParameters
import androidx.glance.appwidget.action.ActionCallback
import com.d4rk.cleaner.app.auto.AutoCleanScheduler
import com.d4rk.cleaner.app.widgets.domain.actions.isWidgetActionsEnabled

class RunCleanAction : ActionCallback {
override suspend fun onAction(context: Context, glanceId: GlanceId, parameters: ActionParameters) {
if (context.isWidgetActionsEnabled()) {
AutoCleanScheduler.runOnce(context)
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package com.d4rk.cleaner.app.widgets.domain.actions

import android.content.Context
import androidx.glance.GlanceModifier
import androidx.glance.action.clickable
import androidx.glance.appwidget.action.actionRunCallback
import com.d4rk.cleaner.core.data.datastore.DataStore
import kotlinx.coroutines.flow.first
import kotlinx.coroutines.runBlocking

internal fun Context.isWidgetActionsEnabled(): Boolean {
val ds = DataStore(this)
return runBlocking { ds.widgetActionsEnabled.first() }
}

internal fun GlanceModifier.scanAction() = clickable(actionRunCallback<OpenScanAction>())

internal fun GlanceModifier.cleanAction() = clickable(actionRunCallback<RunCleanAction>())
Loading
Loading