diff --git a/app/build.gradle.kts b/app/build.gradle.kts index a2fac75e..5edf673b 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -147,6 +147,7 @@ dependencies { implementation(libs.rikka.shizuku.api) implementation(libs.rikka.shizuku.provider) + implementation(libs.androidx.work.runtime.ktx) implementation(libs.androidx.runtime.android) implementation(libs.androidx.annotation) implementation(libs.androidx.activity.compose) diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 5d00e4b2..de65a1ff 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -83,6 +83,7 @@ diff --git a/app/src/main/kotlin/com/dergoogler/mmrl/ui/activity/MainActivity.kt b/app/src/main/kotlin/com/dergoogler/mmrl/ui/activity/MainActivity.kt index c7d989f4..f2493d7e 100644 --- a/app/src/main/kotlin/com/dergoogler/mmrl/ui/activity/MainActivity.kt +++ b/app/src/main/kotlin/com/dergoogler/mmrl/ui/activity/MainActivity.kt @@ -31,6 +31,8 @@ class MainActivity : MMRLComponentActivity() { val splashScreen = installSplashScreen() super.onCreate(savedInstanceState) + startRepoUpdateService() + splashScreen.setKeepOnScreenCondition { isLoading } setBaseContent { diff --git a/app/src/main/kotlin/com/dergoogler/mmrl/worker/RepoUpdateWorker.kt b/app/src/main/kotlin/com/dergoogler/mmrl/worker/RepoUpdateWorker.kt new file mode 100644 index 00000000..ddd58e7f --- /dev/null +++ b/app/src/main/kotlin/com/dergoogler/mmrl/worker/RepoUpdateWorker.kt @@ -0,0 +1,113 @@ +package com.dergoogler.mmrl.worker + +import android.app.NotificationChannel +import android.app.NotificationManager +import android.content.Context +import androidx.core.app.NotificationCompat +import androidx.room.Room +import androidx.work.CoroutineWorker +import androidx.work.ForegroundInfo +import androidx.work.WorkerParameters +import com.dergoogler.mmrl.R +import com.dergoogler.mmrl.database.AppDatabase +import com.dergoogler.mmrl.stub.IRepoManager +import timber.log.Timber + +class RepoUpdateWorker( + private val context: Context, + params: WorkerParameters +) : CoroutineWorker(context, params) { + + override suspend fun doWork(): Result { + setForeground(createForegroundInfo()) + + return try { + val updated = updateRepositories() + + if (updated) { + createNotification( + title = context.getString(R.string.repo_update_service), + message = context.getString(R.string.repo_update_service_desc) + ) + } else { + createNotification( + title = context.getString(R.string.repo_update_service_failed), + message = context.getString(R.string.repo_update_service_failed_desc) + ) + } + Result.success() + } catch (e: Exception) { + Result.failure() + } + } + + private suspend fun updateRepositories(): Boolean { + + val database: AppDatabase = Room.databaseBuilder( + applicationContext, + AppDatabase::class.java, + "mmrl" + ).build() + + val repoDao = database.repoDao() + val repos = database.repoDao().getAll() + + + repos.forEach { repo -> + val repoManager = IRepoManager.build(repo.url) + + try { + val response = + repoManager.modules.execute() + + if (response.isSuccessful && response.body() != null) { + val modulesJson = response.body()!! + + if (repo.metadata.timestamp != modulesJson.metadata.timestamp) { + val updatedRepo = repo.copy(modulesJson = modulesJson) + repoDao.insert(updatedRepo) + } + } else { + Timber.e("Failed to fetch data for repo: ${repo.url}") + return false + } + } catch (e: Exception) { + Timber.e(e, "Error while updating repo: ${repo.url}") + return false + } + } + + return true + } + + private fun createNotification(title: String, message: String) { + val notificationManager = + context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager + + + val channel = NotificationChannel( + "RepoUpdateChannel", + "Repo Updates", + NotificationManager.IMPORTANCE_DEFAULT + ) + notificationManager.createNotificationChannel(channel) + + val notification = NotificationCompat.Builder(context, "RepoUpdateChannel") + .setContentTitle(title) + .setContentText(message) + .setSmallIcon(R.drawable.box) + .build() + + notificationManager.notify(1, notification) + } + + private fun createForegroundInfo(): ForegroundInfo { + val notification = NotificationCompat.Builder(applicationContext, "repo_update_channel") + .setContentTitle("Updating Repositories") + .setSmallIcon(R.drawable.box) + .setPriority(NotificationCompat.PRIORITY_DEFAULT) + .build() + + return ForegroundInfo(1, notification) + } +} \ No newline at end of file diff --git a/app/src/main/kotlin/ext/dergoogler/mmrl/activity/MMRLComponentActivity.kt b/app/src/main/kotlin/ext/dergoogler/mmrl/activity/MMRLComponentActivity.kt index 50ab858f..c3f9bb2c 100644 --- a/app/src/main/kotlin/ext/dergoogler/mmrl/activity/MMRLComponentActivity.kt +++ b/app/src/main/kotlin/ext/dergoogler/mmrl/activity/MMRLComponentActivity.kt @@ -1,9 +1,12 @@ package ext.dergoogler.mmrl.activity +import android.app.AlarmManager +import android.app.PendingIntent import android.content.Context import android.content.Intent import android.net.Uri import android.os.Bundle +import android.os.SystemClock import androidx.activity.ComponentActivity import androidx.activity.compose.setContent import androidx.activity.enableEdgeToEdge @@ -11,8 +14,12 @@ import androidx.compose.runtime.Composable import androidx.compose.runtime.CompositionContext import androidx.compose.runtime.CompositionLocalProvider import androidx.compose.runtime.getValue -import androidx.compose.ui.platform.LocalContext import androidx.lifecycle.compose.collectAsStateWithLifecycle +import androidx.work.Constraints +import androidx.work.ExistingPeriodicWorkPolicy +import androidx.work.NetworkType +import androidx.work.PeriodicWorkRequestBuilder +import androidx.work.WorkManager import com.dergoogler.mmrl.R import com.dergoogler.mmrl.repository.LocalRepository import com.dergoogler.mmrl.repository.UserPreferencesRepository @@ -21,7 +28,9 @@ import com.dergoogler.mmrl.ui.activity.InstallActivity import com.dergoogler.mmrl.ui.activity.ModConfActivity import com.dergoogler.mmrl.ui.providable.LocalUserPreferences import com.dergoogler.mmrl.ui.theme.AppTheme +import com.dergoogler.mmrl.worker.RepoUpdateWorker import dagger.hilt.android.AndroidEntryPoint +import java.util.concurrent.TimeUnit import javax.inject.Inject import kotlin.system.exitProcess @@ -70,6 +79,23 @@ open class MMRLComponentActivity : ComponentActivity() { } } + fun startRepoUpdateService() { + val updateRequest = PeriodicWorkRequestBuilder(6, TimeUnit.HOURS) + .setConstraints( + Constraints.Builder() + .setRequiredNetworkType(NetworkType.CONNECTED) + .build() + ) + .build() + + WorkManager.getInstance(this) + .enqueueUniquePeriodicWork( + "RepoUpdateWork", + ExistingPeriodicWorkPolicy.UPDATE, + updateRequest + ) + } + companion object { fun startModConfActivity(context: Context, modId: String) { val intent = Intent(context, ModConfActivity::class.java) diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 3d6856e6..aaed1415 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -209,5 +209,9 @@ %s\nā€¦ %d more Additional Notes Repo added + Repositories updated successfully + Don\'t worry, MMRL updated your installed repos for you. šŸ˜‹ + No updates found for repositories + There are no updates for repositories \ No newline at end of file diff --git a/app/src/main/res/values/strings_untranslatable.xml b/app/src/main/res/values/strings_untranslatable.xml index 9c937090..d71a4599 100644 --- a/app/src/main/res/values/strings_untranslatable.xml +++ b/app/src/main/res/values/strings_untranslatable.xml @@ -2,6 +2,7 @@ MMRL @string/module_install + ModConf Shizuku diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index e17471db..001a1b78 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -36,6 +36,7 @@ squareRetrofit = "2.11.0" squareOkhttp = "4.12.0" squareMoshi = "1.15.1" runtimeAndroid = "1.7.3" +workRuntimeKtx = "2.8.0" [libraries] androidx-activity-compose = { group = "androidx.activity", name = "activity-compose", version.ref = "androidxActivity" } @@ -66,6 +67,7 @@ androidx-room-compiler = { group = "androidx.room", name = "room-compiler", vers androidx-room-ktx = { group = "androidx.room", name = "room-ktx", version.ref = "androidxRoom" } androidx-room-runtime = { group = "androidx.room", name = "room-runtime", version.ref = "androidxRoom" } androidx-ui = { module = "androidx.compose.ui:ui" } +androidx-work-runtime-ktx = { module = "androidx.work:work-runtime-ktx", version.ref = "workRuntimeKtx" } coil-compose = { module = "io.coil-kt:coil-compose", version.ref = "coilCompose" } compose-markdown = { module = "com.github.jeziellago:compose-markdown", version.ref = "composeMarkdown" } hilt-android = { group = "com.google.dagger", name = "hilt-android", version.ref = "hilt" }