Skip to content

Commit

Permalink
Restart torrent service if app task is removed.
Browse files Browse the repository at this point in the history
  • Loading branch information
StageGuard committed Nov 3, 2024
1 parent c0ea9a1 commit bb62cb3
Show file tree
Hide file tree
Showing 2 changed files with 54 additions and 14 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,14 @@

package me.him188.ani.app.domain.torrent.service

import android.app.AlarmManager
import android.app.PendingIntent
import android.content.Context
import android.content.Intent
import android.os.Handler
import android.os.IBinder
import android.os.Looper
import android.os.PowerManager
import android.os.Process
import android.os.SystemClock
import androidx.lifecycle.LifecycleService
import androidx.lifecycle.lifecycleScope
import kotlinx.coroutines.CompletableDeferred
Expand All @@ -35,7 +36,6 @@ import me.him188.ani.app.data.models.preference.ProxySettings
import me.him188.ani.app.data.models.preference.TorrentPeerConfig
import me.him188.ani.app.domain.torrent.engines.AnitorrentEngine
import me.him188.ani.app.domain.torrent.service.proxy.TorrentEngineProxy
import me.him188.ani.app.platform.BuildConfig
import me.him188.ani.app.torrent.anitorrent.AnitorrentDownloaderFactory
import me.him188.ani.datasources.api.topic.FileSize.Companion.bytes
import me.him188.ani.utils.coroutines.IO_
Expand Down Expand Up @@ -72,6 +72,7 @@ class AniTorrentService : LifecycleService(), CoroutineScope {
}

private val notification = ServiceNotification(this)
private val alarmService: AlarmManager by lazy { getSystemService(Context.ALARM_SERVICE) as AlarmManager }
private val wakeLock: PowerManager.WakeLock by lazy {
(getSystemService(Context.POWER_SERVICE) as PowerManager)
.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "AniTorrentService::wake_lock")
Expand All @@ -89,8 +90,8 @@ class AniTorrentService : LifecycleService(), CoroutineScope {
torrentPeerConfig,
Path(saveDirDeferred.await()).inSystem,
coroutineContext,
AnitorrentDownloaderFactory()
)
AnitorrentDownloaderFactory(),
),
)
logger.info { "anitorrent is initialized." }
}
Expand Down Expand Up @@ -147,6 +148,30 @@ class AniTorrentService : LifecycleService(), CoroutineScope {
return true
}

/**
* 在 app 被从最近任务界面划掉时重启服务
*
* 一些系统, 比如 MIUI, 会在划掉任务的时候杀死整个 app.
*/
override fun onTaskRemoved(rootIntent: Intent?) {
val restartServicePendingIntent = PendingIntent.getService(
this, 1,
Intent(this, this::class.java).apply {
setPackage(packageName)
putExtra("notification_appearance", notification.notificationAppearance)
},
PendingIntent.FLAG_ONE_SHOT or PendingIntent.FLAG_IMMUTABLE,
)
logger.info { "Task of Ani app is removed, scheduling restart service." }
alarmService.set(
AlarmManager.ELAPSED_REALTIME,
SystemClock.elapsedRealtime() + 1000,
restartServicePendingIntent,
)

super.onTaskRemoved(rootIntent)
}

override fun onDestroy() {
logger.info { "AniTorrentService is stopping." }
val engine = kotlin.runCatching { anitorrent.getCompleted() }.getOrNull() ?: return
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@ import android.content.Intent
import android.graphics.Bitmap
import android.graphics.drawable.Icon
import android.os.Build
import android.os.Parcelable
import kotlinx.parcelize.Parcelize
import me.him188.ani.datasources.api.topic.FileSize
import me.him188.ani.datasources.api.topic.FileSize.Companion.bytes

Expand All @@ -28,7 +30,8 @@ class ServiceNotification(
) {
private val notificationService by lazy { context.getSystemService(NOTIFICATION_SERVICE) as NotificationManager }

private var notificationAppearance = defaultNotificationAppearance
var notificationAppearance = defaultNotificationAppearance
private set
private var notificationOpenActivityIntent: Intent? = null
private val stopServiceIntent by lazy {
PendingIntent.getService(
Expand All @@ -50,6 +53,13 @@ class ServiceNotification(
}

fun parseNotificationStrategyFromIntent(intent: Intent?) {
// parse notification appearance first if present
val appearance = intent?.getParcelable<NotificationAppearance>("notification_appearance")
if (appearance != null) {
notificationAppearance = appearance
return
}

val name = intent.getStringOrDefault("app_name") {
defaultNotificationAppearance.name
}
Expand All @@ -68,13 +78,8 @@ class ServiceNotification(
}
val icon = (intent?.getIntExtra("app_icon", -1) ?: -1)
.let { if (it != -1) defaultNotificationAppearance.icon else Icon.createWithResource(context, it) }

notificationOpenActivityIntent = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
intent?.getParcelableExtra("open_activity_intent", Intent::class.java)
} else {
intent?.getParcelableExtra<Intent>("open_activity_intent")
}

notificationOpenActivityIntent = intent?.getParcelable<Intent>("open_activity_intent")

notificationAppearance = NotificationAppearance(
name = name,
titleIdle = titleIdle,
Expand Down Expand Up @@ -159,6 +164,15 @@ class ServiceNotification(
return if (result == -1) default() else context.getString(result)
}

@Suppress("RemoveExplicitTypeArguments", "Deprecation")
private inline fun <reified T : Parcelable> Intent.getParcelable(extraName: String): T? {
return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
getParcelableExtra(extraName, T::class.java)
} else {
getParcelableExtra<T>(extraName)
}
}

companion object {
private const val NOTIFICATION_ID = 114
private const val NOTIFICATION_CHANNEL_ID = "me.him188.ani.app.domain.torrent.service.AniTorrentService"
Expand All @@ -174,14 +188,15 @@ class ServiceNotification(
}
}

@Parcelize
class NotificationAppearance(
val name: String,
val titleIdle: String,
val titleWorking: String,
val content: String,
val stopActionText: String,
val icon: Icon,
)
) : Parcelable

sealed class NotificationDisplayStrategy(val downloadSpeed: FileSize, val uploadSpeed: FileSize) {
class Idle(
Expand Down

0 comments on commit bb62cb3

Please sign in to comment.