1+ package cc.chenhe.qqnotifyevo.service
2+
3+ import android.app.NotificationChannel
4+ import android.app.NotificationManager
5+ import android.app.Service
6+ import android.content.Context
7+ import android.content.Intent
8+ import android.os.IBinder
9+ import androidx.core.app.NotificationCompat
10+ import androidx.core.app.NotificationManagerCompat
11+ import androidx.core.content.edit
12+ import androidx.core.os.UserManagerCompat
13+ import androidx.localbroadcastmanager.content.LocalBroadcastManager
14+ import androidx.preference.PreferenceManager
15+ import cc.chenhe.qqnotifyevo.R
16+ import cc.chenhe.qqnotifyevo.utils.ACTION_APPLICATION_UPGRADE_COMPLETE
17+ import cc.chenhe.qqnotifyevo.utils.NOTIFY_ID_UPGRADE
18+ import cc.chenhe.qqnotifyevo.utils.NOTIFY_SELF_FOREGROUND_SERVICE_CHANNEL_ID
19+ import cc.chenhe.qqnotifyevo.utils.UpgradeUtils
20+ import kotlinx.coroutines.Dispatchers
21+ import kotlinx.coroutines.GlobalScope
22+ import kotlinx.coroutines.launch
23+ import kotlinx.coroutines.withContext
24+ import timber.log.Timber
25+
26+ @Suppress(" FunctionName" )
27+ class UpgradeService : Service () {
28+
29+ companion object {
30+
31+ private const val TAG = " UpgradeService"
32+
33+ private const val EXTRA_OLD_VERSION = " old"
34+ private const val EXTRA_CURRENT_VERSION = " new"
35+
36+ private const val VERSION_2_0_2 = 20023
37+
38+ private var instance: UpgradeService ? = null
39+ private var preparedToRunning = false
40+
41+ /* *
42+ * 是否正在运行或者正准备运行。
43+ */
44+ fun isRunningOrPrepared (): Boolean {
45+ return preparedToRunning || isRunning()
46+ }
47+
48+ private fun isRunning (): Boolean {
49+ return try {
50+ // 如果服务被强制结束,标记没有释放,那么此处会抛出异常。
51+ instance?.ping() ? : false
52+ } catch (e: Exception ) {
53+ false
54+ }
55+ }
56+
57+ fun startIfNecessary (context : Context ): Boolean {
58+ val old: Long = UpgradeUtils .getOldVersion(context)
59+ val new: Long = UpgradeUtils .getCurrentVersion(context)
60+ if (old == new) {
61+ Timber .tag(TAG ).d(" Old version equals to the current, no need to upgrade. v=$new " )
62+ return false
63+ } else if (old > new) {
64+ // should never happen
65+ Timber .tag(TAG ).e(" Current version is lower than old version! current=$new , old=$old " )
66+ return false
67+ }
68+
69+ // old < new
70+ return if (shouldPerformUpgrade(old)) {
71+ preparedToRunning = true
72+ val i = Intent (context.applicationContext, UpgradeService ::class .java).apply {
73+ putExtra(EXTRA_OLD_VERSION , old)
74+ putExtra(EXTRA_CURRENT_VERSION , new)
75+ }
76+ context.startService(i)
77+ true
78+ } else {
79+ Timber .tag(TAG ).i(" No need to perform data migration, update version code directly $old → $new ." )
80+ UpgradeUtils .setOldVersion(context, new)
81+ false
82+ }
83+ }
84+
85+ private fun shouldPerformUpgrade (old : Long ): Boolean {
86+ return old in 1 .. VERSION_2_0_2
87+ }
88+ }
89+
90+ private var running = false
91+
92+ private lateinit var ctx: Context
93+
94+ private fun ping () = true
95+
96+ override fun onCreate () {
97+ super .onCreate()
98+ instance = this
99+ ctx = this .application
100+ createNotificationChannel()
101+ }
102+
103+ override fun onDestroy () {
104+ instance = null
105+ super .onDestroy()
106+ }
107+
108+ private fun createNotificationChannel () {
109+ val channel = NotificationChannel (NOTIFY_SELF_FOREGROUND_SERVICE_CHANNEL_ID ,
110+ getString(R .string.notify_self_foreground_service_channel_name),
111+ NotificationManager .IMPORTANCE_LOW ).apply {
112+ description = getString(R .string.notify_self_foreground_service_channel_name_des)
113+ }
114+ NotificationManagerCompat .from(this ).createNotificationChannel(channel)
115+ }
116+
117+ override fun onStartCommand (intent : Intent ? , flags : Int , startId : Int ): Int {
118+ if (! running) {
119+ running = true
120+ preparedToRunning = false
121+
122+ val notify = NotificationCompat .Builder (this , NOTIFY_SELF_FOREGROUND_SERVICE_CHANNEL_ID )
123+ .setSmallIcon(R .drawable.ic_notify_upgrade)
124+ .setContentTitle(getString(R .string.notify_upgrade))
125+ .setContentText(getString(R .string.notify_upgrade_text))
126+ .setPriority(NotificationCompat .PRIORITY_LOW )
127+ .setOngoing(true )
128+ .setOnlyAlertOnce(true )
129+ .build()
130+ startForeground(NOTIFY_ID_UPGRADE , notify)
131+
132+ GlobalScope .launch {
133+ val old = intent!! .getLongExtra(EXTRA_OLD_VERSION , - 10 )
134+ val new = intent.getLongExtra(EXTRA_CURRENT_VERSION , - 10 )
135+ if (old == - 10L || new == - 10L ) {
136+ Timber .tag(TAG ).e(" onStartCommand: unknown version. old=$old , new=$new " )
137+ complete(false , 0 )
138+ } else {
139+ startUpgrade(old, new)
140+ }
141+ }
142+ } else {
143+ preparedToRunning = false
144+ }
145+ return super .onStartCommand(intent, flags, startId)
146+ }
147+
148+ /* *
149+ * 升级完成后调用此函数。
150+ */
151+ private fun complete (success : Boolean , currentVersion : Long ) {
152+ if (success) {
153+ Timber .tag(TAG ).d(" Upgrade complete." )
154+ UpgradeUtils .setOldVersion(this , currentVersion)
155+ } else {
156+ Timber .tag(TAG ).e(" Upgrade error!" )
157+ }
158+ LocalBroadcastManager .getInstance(this ).sendBroadcast(Intent (ACTION_APPLICATION_UPGRADE_COMPLETE ))
159+ stopForeground(true )
160+ stopSelf()
161+ }
162+
163+ private suspend fun startUpgrade (oldVersion : Long , currentVersion : Long ) = withContext(Dispatchers .Main ) {
164+ Timber .tag(TAG ).d(" Start upgrade process. $oldVersion → $currentVersion " )
165+
166+ if (oldVersion in 1 .. VERSION_2_0_2 ) {
167+ migrate_1_to_2_0_2()
168+ }
169+
170+ complete(true , currentVersion)
171+ }
172+
173+ private suspend fun migrate_1_to_2_0_2 () = withContext(Dispatchers .Main ) {
174+ if (UserManagerCompat .isUserUnlocked(ctx)) {
175+ Timber .tag(TAG ).d(" Remove deprecated preferences." )
176+ PreferenceManager .getDefaultSharedPreferences(ctx).edit {
177+ remove(" friend_vibrate" )
178+ remove(" friend_ringtone" )
179+ remove(" group_notify" )
180+ remove(" group_ringtone" )
181+ remove(" group_vibrate" )
182+ remove(" qzone_notify" )
183+ remove(" qzone_ringtone" )
184+ remove(" qzone_vibrate" )
185+ }
186+ }
187+ }
188+
189+ override fun onBind (i : Intent ? ): IBinder ? {
190+ return null
191+ }
192+ }
0 commit comments