1package com.peachpasswords.updates23import android.app.NotificationChannel4import android.app.NotificationManager5import android.app.PendingIntent6import android.content.Context7import android.content.Intent8import android.os.Build9import androidx.core.app.NotificationCompat10import androidx.core.app.NotificationManagerCompat11import androidx.hilt.work.HiltWorker12import androidx.work.BackoffPolicy13import androidx.work.Constraints14import androidx.work.CoroutineWorker15import androidx.work.ExistingPeriodicWorkPolicy16import androidx.work.ExistingWorkPolicy17import androidx.work.NetworkType18import androidx.work.OneTimeWorkRequestBuilder19import androidx.work.PeriodicWorkRequestBuilder20import androidx.work.WorkManager21import androidx.work.WorkerParameters22import com.peachpasswords.MainActivity23import com.peachpasswords.R24import dagger.assisted.Assisted25import dagger.assisted.AssistedInject26import java.util.concurrent.TimeUnit272829const val EXTRA_OPEN_UPDATE = "peach_open_update"303132333435363738@HiltWorker39class UpdateCheckWorker @AssistedInject constructor(40 @Assisted appContext: Context,41 @Assisted params: WorkerParameters,42 private val updates: UpdateRepository,43) : CoroutineWorker(appContext, params) {4445 override suspend fun doWork(): Result {46 val manifest = try {47 updates.check()48 } catch (e: Exception) {49 50 51 return Result.retry()52 } ?: return Result.success()5354 if (updates.shouldNotifyFor(manifest.version) &&55 postUpdateNotification(applicationContext, manifest.version)56 ) {57 updates.markNotified(manifest.version)58 }59 return Result.success()60 }6162 companion object {63 private const val PERIODIC_WORK = "peach_update_check"64 private const val ONE_SHOT_WORK = "peach_update_check_now"6566 private val NETWORK_ONLY = Constraints.Builder()67 .setRequiredNetworkType(NetworkType.CONNECTED)68 .build()6970 71 fun schedulePeriodic(context: Context) {72 WorkManager.getInstance(context).enqueueUniquePeriodicWork(73 PERIODIC_WORK,74 ExistingPeriodicWorkPolicy.KEEP,75 PeriodicWorkRequestBuilder<UpdateCheckWorker>(1, TimeUnit.DAYS)76 .setConstraints(NETWORK_ONLY)77 .setBackoffCriteria(BackoffPolicy.EXPONENTIAL, 30, TimeUnit.MINUTES)78 .build(),79 )80 }8182 83 fun checkNow(context: Context) {84 WorkManager.getInstance(context).enqueueUniqueWork(85 ONE_SHOT_WORK,86 ExistingWorkPolicy.REPLACE,87 OneTimeWorkRequestBuilder<UpdateCheckWorker>()88 .setConstraints(NETWORK_ONLY)89 .build(),90 )91 }92 }93}9495private const val CHANNEL_ID = "peach_updates"96private const val NOTIFICATION_ID = 4301979899100101102103104fun postUpdateNotification(context: Context, version: String): Boolean {105 val manager = NotificationManagerCompat.from(context)106 if (!manager.areNotificationsEnabled()) return false107108 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {109 val system = context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager110 system.createNotificationChannel(111 NotificationChannel(CHANNEL_ID, "Peach updates", NotificationManager.IMPORTANCE_LOW).apply {112 description = "Tells you when a newer version of Peach is available."113 setShowBadge(false)114 },115 )116 }117118 val open = PendingIntent.getActivity(119 context,120 0,121 Intent(context, MainActivity::class.java)122 .putExtra(EXTRA_OPEN_UPDATE, true)123 .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TOP),124 PendingIntent.FLAG_IMMUTABLE or PendingIntent.FLAG_UPDATE_CURRENT,125 )126127 val notification = NotificationCompat.Builder(context, CHANNEL_ID)128 .setContentTitle("Peach update available — v$version")129 .setContentText("Tap to see what changed and install it.")130 .setSmallIcon(R.drawable.ic_launcher_monochrome)131 .setPriority(NotificationCompat.PRIORITY_LOW)132 .setAutoCancel(true)133 .setContentIntent(open)134 .build()135136 return try {137 manager.notify(NOTIFICATION_ID, notification)138 true139 } catch (e: SecurityException) {140 141 142 false143 }144}