Skip to content

Commit 06d41a0

Browse files
committed
Add UpdateScheduleManager and its Android and Desktop implementations
1 parent abaa77d commit 06d41a0

4 files changed

Lines changed: 112 additions & 1 deletion

File tree

composeApp/src/androidMain/kotlin/zed/rainxch/githubstore/app/GithubStoreApp.kt

Lines changed: 75 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,16 +4,26 @@ import android.app.Application
44
import android.app.NotificationChannel
55
import android.app.NotificationManager
66
import android.os.Build
7+
import co.touchlab.kermit.Logger
8+
import kotlinx.coroutines.CoroutineScope
9+
import kotlinx.coroutines.Dispatchers
10+
import kotlinx.coroutines.SupervisorJob
11+
import kotlinx.coroutines.flow.first
12+
import kotlinx.coroutines.launch
713
import org.koin.android.ext.android.get
814
import org.koin.android.ext.koin.androidContext
915
import zed.rainxch.core.data.services.PackageEventReceiver
1016
import zed.rainxch.core.data.services.UpdateScheduler
17+
import zed.rainxch.core.domain.model.InstallSource
18+
import zed.rainxch.core.domain.model.InstalledApp
1119
import zed.rainxch.core.domain.repository.InstalledAppsRepository
20+
import zed.rainxch.core.domain.repository.ThemesRepository
1221
import zed.rainxch.core.domain.system.PackageMonitor
1322
import zed.rainxch.githubstore.app.di.initKoin
1423

1524
class GithubStoreApp : Application() {
1625
private var packageEventReceiver: PackageEventReceiver? = null
26+
private val appScope = CoroutineScope(SupervisorJob() + Dispatchers.IO)
1727

1828
override fun onCreate() {
1929
super.onCreate()
@@ -25,6 +35,7 @@ class GithubStoreApp : Application() {
2535
createNotificationChannels()
2636
registerPackageEventReceiver()
2737
scheduleBackgroundUpdateChecks()
38+
registerSelfAsInstalledApp()
2839
}
2940

3041
private fun createNotificationChannels() {
@@ -70,10 +81,73 @@ class GithubStoreApp : Application() {
7081
}
7182

7283
private fun scheduleBackgroundUpdateChecks() {
73-
UpdateScheduler.schedule(context = this)
84+
appScope.launch {
85+
val intervalHours = get<ThemesRepository>().getUpdateCheckInterval().first()
86+
UpdateScheduler.schedule(context = this@GithubStoreApp, intervalHours = intervalHours)
87+
}
88+
}
89+
90+
/**
91+
* Automatically registers GitHub Store itself as a tracked installed app on first launch.
92+
* This allows the app to track its own updates and auto-update via Shizuku.
93+
*/
94+
private fun registerSelfAsInstalledApp() {
95+
appScope.launch {
96+
try {
97+
val repo = get<InstalledAppsRepository>()
98+
val existing = repo.getAppByPackage(SELF_PACKAGE_NAME)
99+
if (existing != null) return@launch
100+
101+
val packageMonitor = get<PackageMonitor>()
102+
val systemInfo = packageMonitor.getInstalledPackageInfo(SELF_PACKAGE_NAME)
103+
104+
val now = System.currentTimeMillis()
105+
val versionName = systemInfo?.versionName ?: ""
106+
val versionCode = systemInfo?.versionCode ?: 0L
107+
108+
val selfApp = InstalledApp(
109+
packageName = SELF_PACKAGE_NAME,
110+
repoId = 0L,
111+
repoName = SELF_REPO_NAME,
112+
repoOwner = SELF_REPO_OWNER,
113+
repoOwnerAvatarUrl = "https://avatars.githubusercontent.com/u/221085707",
114+
repoDescription = "A cross-platform app store for GitHub releases",
115+
primaryLanguage = "Kotlin",
116+
repoUrl = "https://github.com/$SELF_REPO_OWNER/$SELF_REPO_NAME",
117+
installedVersion = versionName,
118+
installedAssetName = null,
119+
installedAssetUrl = null,
120+
latestVersion = null,
121+
latestAssetName = null,
122+
latestAssetUrl = null,
123+
latestAssetSize = null,
124+
appName = "GitHub Store",
125+
installSource = InstallSource.THIS_APP,
126+
installedAt = now,
127+
lastCheckedAt = 0L,
128+
lastUpdatedAt = now,
129+
isUpdateAvailable = false,
130+
updateCheckEnabled = true,
131+
releaseNotes = null,
132+
systemArchitecture = "",
133+
fileExtension = "apk",
134+
isPendingInstall = false,
135+
installedVersionName = versionName,
136+
installedVersionCode = versionCode,
137+
)
138+
139+
repo.saveInstalledApp(selfApp)
140+
Logger.i { "GithubStoreApp: Registered self as tracked installed app (v$versionName)" }
141+
} catch (e: Exception) {
142+
Logger.e { "GithubStoreApp: Failed to register self: ${e.message}" }
143+
}
144+
}
74145
}
75146

76147
companion object {
148+
private const val SELF_PACKAGE_NAME = "zed.rainxch.githubstore"
149+
private const val SELF_REPO_OWNER = "OpenHub-Store"
150+
private const val SELF_REPO_NAME = "GitHub-Store"
77151
const val UPDATES_CHANNEL_ID = "app_updates"
78152
const val UPDATE_SERVICE_CHANNEL_ID = "update_service"
79153
}
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
package zed.rainxch.core.data.services
2+
3+
import android.content.Context
4+
import zed.rainxch.core.domain.system.UpdateScheduleManager
5+
6+
class AndroidUpdateScheduleManager(
7+
private val context: Context,
8+
) : UpdateScheduleManager {
9+
override fun reschedule(intervalHours: Long) {
10+
UpdateScheduler.reschedule(context, intervalHours)
11+
}
12+
}
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
package zed.rainxch.core.data.services
2+
3+
import zed.rainxch.core.domain.system.UpdateScheduleManager
4+
5+
/**
6+
* No-op implementation for Desktop — WorkManager is Android-only.
7+
*/
8+
class DesktopUpdateScheduleManager : UpdateScheduleManager {
9+
override fun reschedule(intervalHours: Long) {
10+
// No background scheduler on Desktop
11+
}
12+
}
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
package zed.rainxch.core.domain.system
2+
3+
/**
4+
* Abstraction for rescheduling background update checks.
5+
* Android implementation delegates to WorkManager; Desktop is a no-op.
6+
*/
7+
interface UpdateScheduleManager {
8+
/**
9+
* Reschedules the periodic update check with a new interval.
10+
* Takes effect immediately (replaces existing schedule).
11+
*/
12+
fun reschedule(intervalHours: Long)
13+
}

0 commit comments

Comments
 (0)