mirror of
				https://github.com/mihonapp/mihon.git
				synced 2025-11-04 08:08:55 +01:00 
			
		
		
		
	Remove local broadcast receiver to prevent race conditions (#1123)
* Remove local broadcast receiver to prevent run exceptions. Added option to set tile for extension update.
This commit is contained in:
		
				
					committed by
					
						
						GitHub
					
				
			
			
				
	
			
			
			
						parent
						
							6a63ce992a
						
					
				
				
					commit
					8994b42760
				
			@@ -8,7 +8,7 @@ import com.evernote.android.job.JobManager
 | 
			
		||||
import eu.kanade.tachiyomi.data.backup.BackupCreatorJob
 | 
			
		||||
import eu.kanade.tachiyomi.data.library.LibraryUpdateJob
 | 
			
		||||
import eu.kanade.tachiyomi.data.notification.Notifications
 | 
			
		||||
import eu.kanade.tachiyomi.data.updater.UpdateCheckerJob
 | 
			
		||||
import eu.kanade.tachiyomi.data.updater.UpdaterJob
 | 
			
		||||
import eu.kanade.tachiyomi.util.LocaleHelper
 | 
			
		||||
import org.acra.ACRA
 | 
			
		||||
import org.acra.annotation.ReportsCrashes
 | 
			
		||||
@@ -60,7 +60,7 @@ open class App : Application() {
 | 
			
		||||
        JobManager.create(this).addJobCreator { tag ->
 | 
			
		||||
            when (tag) {
 | 
			
		||||
                LibraryUpdateJob.TAG -> LibraryUpdateJob()
 | 
			
		||||
                UpdateCheckerJob.TAG -> UpdateCheckerJob()
 | 
			
		||||
                UpdaterJob.TAG -> UpdaterJob()
 | 
			
		||||
                BackupCreatorJob.TAG -> BackupCreatorJob()
 | 
			
		||||
                else -> null
 | 
			
		||||
            }
 | 
			
		||||
 
 | 
			
		||||
@@ -3,7 +3,7 @@ package eu.kanade.tachiyomi
 | 
			
		||||
import eu.kanade.tachiyomi.data.library.LibraryUpdateJob
 | 
			
		||||
import eu.kanade.tachiyomi.data.preference.PreferencesHelper
 | 
			
		||||
import eu.kanade.tachiyomi.data.preference.getOrDefault
 | 
			
		||||
import eu.kanade.tachiyomi.data.updater.UpdateCheckerJob
 | 
			
		||||
import eu.kanade.tachiyomi.data.updater.UpdaterJob
 | 
			
		||||
import java.io.File
 | 
			
		||||
 | 
			
		||||
object Migrations {
 | 
			
		||||
@@ -25,7 +25,7 @@ object Migrations {
 | 
			
		||||
            if (oldVersion < 14) {
 | 
			
		||||
                // Restore jobs after upgrading to evernote's job scheduler.
 | 
			
		||||
                if (BuildConfig.INCLUDE_UPDATER && preferences.automaticUpdates()) {
 | 
			
		||||
                    UpdateCheckerJob.setupTask()
 | 
			
		||||
                    UpdaterJob.setupTask()
 | 
			
		||||
                }
 | 
			
		||||
                LibraryUpdateJob.setupTask()
 | 
			
		||||
            }
 | 
			
		||||
 
 | 
			
		||||
@@ -3,6 +3,7 @@ package eu.kanade.tachiyomi.data.notification
 | 
			
		||||
import android.app.PendingIntent
 | 
			
		||||
import android.content.Context
 | 
			
		||||
import android.content.Intent
 | 
			
		||||
import android.net.Uri
 | 
			
		||||
import eu.kanade.tachiyomi.ui.main.MainActivity
 | 
			
		||||
import eu.kanade.tachiyomi.util.getUriCompat
 | 
			
		||||
import java.io.File
 | 
			
		||||
@@ -43,11 +44,10 @@ object NotificationHandler {
 | 
			
		||||
     * Returns [PendingIntent] that prompts user with apk install intent
 | 
			
		||||
     *
 | 
			
		||||
     * @param context context
 | 
			
		||||
     * @param file file of apk that is installed
 | 
			
		||||
     * @param uri uri of apk that is installed
 | 
			
		||||
     */
 | 
			
		||||
    fun installApkPendingActivity(context: Context, file: File): PendingIntent {
 | 
			
		||||
    fun installApkPendingActivity(context: Context, uri: Uri): PendingIntent {
 | 
			
		||||
        val intent = Intent(Intent.ACTION_VIEW).apply {
 | 
			
		||||
            val uri = file.getUriCompat(context)
 | 
			
		||||
            setDataAndType(uri, "application/vnd.android.package-archive")
 | 
			
		||||
            flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_GRANT_READ_URI_PERMISSION
 | 
			
		||||
        }
 | 
			
		||||
 
 | 
			
		||||
@@ -11,8 +11,8 @@ import com.google.gson.annotations.SerializedName
 | 
			
		||||
 * @param assets assets of latest release.
 | 
			
		||||
 */
 | 
			
		||||
class GithubRelease(@SerializedName("tag_name") val version: String,
 | 
			
		||||
        @SerializedName("body") val changeLog: String,
 | 
			
		||||
        @SerializedName("assets") val assets: List<Assets>) {
 | 
			
		||||
                    @SerializedName("body") val changeLog: String,
 | 
			
		||||
                    @SerializedName("assets") private val assets: List<Assets>) {
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Get download link of latest release from the assets.
 | 
			
		||||
 
 | 
			
		||||
@@ -3,7 +3,7 @@ package eu.kanade.tachiyomi.data.updater
 | 
			
		||||
import eu.kanade.tachiyomi.BuildConfig
 | 
			
		||||
import rx.Observable
 | 
			
		||||
 | 
			
		||||
class GithubUpdateChecker() {
 | 
			
		||||
class GithubUpdateChecker {
 | 
			
		||||
 | 
			
		||||
    private val service: GithubService = GithubService.create()
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -3,5 +3,5 @@ package eu.kanade.tachiyomi.data.updater
 | 
			
		||||
sealed class GithubUpdateResult {
 | 
			
		||||
 | 
			
		||||
    class NewUpdate(val release: GithubRelease): GithubUpdateResult()
 | 
			
		||||
    class NoNewUpdate(): GithubUpdateResult()
 | 
			
		||||
    class NoNewUpdate : GithubUpdateResult()
 | 
			
		||||
}
 | 
			
		||||
@@ -1,147 +0,0 @@
 | 
			
		||||
package eu.kanade.tachiyomi.data.updater
 | 
			
		||||
 | 
			
		||||
import android.content.BroadcastReceiver
 | 
			
		||||
import android.content.Context
 | 
			
		||||
import android.content.Intent
 | 
			
		||||
import android.support.v4.app.NotificationCompat
 | 
			
		||||
import eu.kanade.tachiyomi.R
 | 
			
		||||
import eu.kanade.tachiyomi.data.notification.NotificationHandler
 | 
			
		||||
import eu.kanade.tachiyomi.data.notification.NotificationReceiver
 | 
			
		||||
import eu.kanade.tachiyomi.data.notification.Notifications
 | 
			
		||||
import eu.kanade.tachiyomi.util.notificationManager
 | 
			
		||||
import java.io.File
 | 
			
		||||
import eu.kanade.tachiyomi.BuildConfig.APPLICATION_ID as ID
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Local [BroadcastReceiver] that runs on UI thread
 | 
			
		||||
 * Notification calls from [UpdateDownloaderService] should be made from here.
 | 
			
		||||
 */
 | 
			
		||||
internal class UpdateDownloaderReceiver(val context: Context) : BroadcastReceiver() {
 | 
			
		||||
 | 
			
		||||
    companion object {
 | 
			
		||||
        private const val NAME = "UpdateDownloaderReceiver"
 | 
			
		||||
 | 
			
		||||
        // Called to show initial notification.
 | 
			
		||||
        internal const val NOTIFICATION_UPDATER_INITIAL = "$ID.$NAME.UPDATER_INITIAL"
 | 
			
		||||
 | 
			
		||||
        // Called to show progress notification.
 | 
			
		||||
        internal const val NOTIFICATION_UPDATER_PROGRESS = "$ID.$NAME.UPDATER_PROGRESS"
 | 
			
		||||
 | 
			
		||||
        // Called to show install notification.
 | 
			
		||||
        internal const val NOTIFICATION_UPDATER_INSTALL = "$ID.$NAME.UPDATER_INSTALL"
 | 
			
		||||
 | 
			
		||||
        // Called to show error notification
 | 
			
		||||
        internal const val NOTIFICATION_UPDATER_ERROR = "$ID.$NAME.UPDATER_ERROR"
 | 
			
		||||
 | 
			
		||||
        // Value containing action of BroadcastReceiver
 | 
			
		||||
        internal const val EXTRA_ACTION = "$ID.$NAME.ACTION"
 | 
			
		||||
 | 
			
		||||
        // Value containing progress
 | 
			
		||||
        internal const val EXTRA_PROGRESS = "$ID.$NAME.PROGRESS"
 | 
			
		||||
 | 
			
		||||
        // Value containing apk path
 | 
			
		||||
        internal const val EXTRA_APK_PATH = "$ID.$NAME.APK_PATH"
 | 
			
		||||
 | 
			
		||||
        // Value containing apk url
 | 
			
		||||
        internal const val EXTRA_APK_URL = "$ID.$NAME.APK_URL"
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Notification shown to user
 | 
			
		||||
     */
 | 
			
		||||
    private val notification = NotificationCompat.Builder(context, Notifications.CHANNEL_COMMON)
 | 
			
		||||
 | 
			
		||||
    override fun onReceive(context: Context, intent: Intent) {
 | 
			
		||||
        when (intent.getStringExtra(EXTRA_ACTION)) {
 | 
			
		||||
            NOTIFICATION_UPDATER_INITIAL -> basicNotification()
 | 
			
		||||
            NOTIFICATION_UPDATER_PROGRESS -> updateProgress(intent.getIntExtra(EXTRA_PROGRESS, 0))
 | 
			
		||||
            NOTIFICATION_UPDATER_INSTALL -> installNotification(intent.getStringExtra(EXTRA_APK_PATH))
 | 
			
		||||
            NOTIFICATION_UPDATER_ERROR -> errorNotification(intent.getStringExtra(EXTRA_APK_URL))
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Called to show basic notification
 | 
			
		||||
     */
 | 
			
		||||
    private fun basicNotification() {
 | 
			
		||||
        // Create notification
 | 
			
		||||
        with(notification) {
 | 
			
		||||
            setContentTitle(context.getString(R.string.app_name))
 | 
			
		||||
            setContentText(context.getString(R.string.update_check_notification_download_in_progress))
 | 
			
		||||
            setSmallIcon(android.R.drawable.stat_sys_download)
 | 
			
		||||
            setOngoing(true)
 | 
			
		||||
        }
 | 
			
		||||
        notification.show()
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Called to show progress notification
 | 
			
		||||
     *
 | 
			
		||||
     * @param progress progress of download
 | 
			
		||||
     */
 | 
			
		||||
    private fun updateProgress(progress: Int) {
 | 
			
		||||
        with(notification) {
 | 
			
		||||
            setProgress(100, progress, false)
 | 
			
		||||
            setOnlyAlertOnce(true)
 | 
			
		||||
        }
 | 
			
		||||
        notification.show()
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Called to show install notification
 | 
			
		||||
     *
 | 
			
		||||
     * @param path path of file
 | 
			
		||||
     */
 | 
			
		||||
    private fun installNotification(path: String) {
 | 
			
		||||
        // Prompt the user to install the new update.
 | 
			
		||||
        with(notification) {
 | 
			
		||||
            setContentText(context.getString(R.string.update_check_notification_download_complete))
 | 
			
		||||
            setSmallIcon(android.R.drawable.stat_sys_download_done)
 | 
			
		||||
            setOnlyAlertOnce(false)
 | 
			
		||||
            setProgress(0, 0, false)
 | 
			
		||||
            // Install action
 | 
			
		||||
            setContentIntent(NotificationHandler.installApkPendingActivity(context, File(path)))
 | 
			
		||||
            addAction(R.drawable.ic_system_update_grey_24dp_img,
 | 
			
		||||
                    context.getString(R.string.action_install),
 | 
			
		||||
                    NotificationHandler.installApkPendingActivity(context, File(path)))
 | 
			
		||||
            // Cancel action
 | 
			
		||||
            addAction(R.drawable.ic_clear_grey_24dp_img,
 | 
			
		||||
                    context.getString(R.string.action_cancel),
 | 
			
		||||
                    NotificationReceiver.dismissNotificationPendingBroadcast(context, Notifications.ID_UPDATER))
 | 
			
		||||
        }
 | 
			
		||||
        notification.show()
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Called to show error notification
 | 
			
		||||
     *
 | 
			
		||||
     * @param url url of apk
 | 
			
		||||
     */
 | 
			
		||||
    private fun errorNotification(url: String) {
 | 
			
		||||
        // Prompt the user to retry the download.
 | 
			
		||||
        with(notification) {
 | 
			
		||||
            setContentText(context.getString(R.string.update_check_notification_download_error))
 | 
			
		||||
            setSmallIcon(android.R.drawable.stat_sys_warning)
 | 
			
		||||
            setOnlyAlertOnce(false)
 | 
			
		||||
            setProgress(0, 0, false)
 | 
			
		||||
            // Retry action
 | 
			
		||||
            addAction(R.drawable.ic_refresh_grey_24dp_img,
 | 
			
		||||
                    context.getString(R.string.action_retry),
 | 
			
		||||
                    UpdateDownloaderService.downloadApkPendingService(context, url))
 | 
			
		||||
            // Cancel action
 | 
			
		||||
            addAction(R.drawable.ic_clear_grey_24dp_img,
 | 
			
		||||
                    context.getString(R.string.action_cancel),
 | 
			
		||||
                    NotificationReceiver.dismissNotificationPendingBroadcast(context, Notifications.ID_UPDATER))
 | 
			
		||||
        }
 | 
			
		||||
        notification.show()
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Shows a notification from this builder.
 | 
			
		||||
     *
 | 
			
		||||
     * @param id the id of the notification.
 | 
			
		||||
     */
 | 
			
		||||
    private fun NotificationCompat.Builder.show(id: Int = Notifications.ID_UPDATER) {
 | 
			
		||||
        context.notificationManager.notify(id, build())
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -1,67 +1,67 @@
 | 
			
		||||
package eu.kanade.tachiyomi.data.updater
 | 
			
		||||
 | 
			
		||||
import android.app.PendingIntent
 | 
			
		||||
import android.content.Intent
 | 
			
		||||
import android.support.v4.app.NotificationCompat
 | 
			
		||||
import com.evernote.android.job.Job
 | 
			
		||||
import com.evernote.android.job.JobManager
 | 
			
		||||
import com.evernote.android.job.JobRequest
 | 
			
		||||
import eu.kanade.tachiyomi.R
 | 
			
		||||
import eu.kanade.tachiyomi.data.notification.Notifications
 | 
			
		||||
import eu.kanade.tachiyomi.util.notificationManager
 | 
			
		||||
 | 
			
		||||
class UpdateCheckerJob : Job() {
 | 
			
		||||
 | 
			
		||||
    override fun onRunJob(params: Params): Result {
 | 
			
		||||
        return GithubUpdateChecker()
 | 
			
		||||
                .checkForUpdate()
 | 
			
		||||
                .map { result ->
 | 
			
		||||
                    if (result is GithubUpdateResult.NewUpdate) {
 | 
			
		||||
                        val url = result.release.downloadLink
 | 
			
		||||
 | 
			
		||||
                        val intent = Intent(context, UpdateDownloaderService::class.java).apply {
 | 
			
		||||
                            putExtra(UpdateDownloaderService.EXTRA_DOWNLOAD_URL, url)
 | 
			
		||||
                        }
 | 
			
		||||
 | 
			
		||||
                        NotificationCompat.Builder(context, Notifications.CHANNEL_COMMON).update {
 | 
			
		||||
                            setContentTitle(context.getString(R.string.app_name))
 | 
			
		||||
                            setContentText(context.getString(R.string.update_check_notification_update_available))
 | 
			
		||||
                            setSmallIcon(android.R.drawable.stat_sys_download_done)
 | 
			
		||||
                            // Download action
 | 
			
		||||
                            addAction(android.R.drawable.stat_sys_download_done,
 | 
			
		||||
                                    context.getString(R.string.action_download),
 | 
			
		||||
                                    PendingIntent.getService(context, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT))
 | 
			
		||||
                        }
 | 
			
		||||
                    }
 | 
			
		||||
                    Job.Result.SUCCESS
 | 
			
		||||
                }
 | 
			
		||||
                .onErrorReturn { Job.Result.FAILURE }
 | 
			
		||||
                // Sadly, the task needs to be synchronous.
 | 
			
		||||
                .toBlocking()
 | 
			
		||||
                .single()
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fun NotificationCompat.Builder.update(block: NotificationCompat.Builder.() -> Unit) {
 | 
			
		||||
        block()
 | 
			
		||||
        context.notificationManager.notify(Notifications.ID_UPDATER, build())
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    companion object {
 | 
			
		||||
        const val TAG = "UpdateChecker"
 | 
			
		||||
 | 
			
		||||
        fun setupTask() {
 | 
			
		||||
            JobRequest.Builder(TAG)
 | 
			
		||||
                    .setPeriodic(24 * 60 * 60 * 1000, 60 * 60 * 1000)
 | 
			
		||||
                    .setRequiredNetworkType(JobRequest.NetworkType.CONNECTED)
 | 
			
		||||
                    .setRequirementsEnforced(true)
 | 
			
		||||
                    .setUpdateCurrent(true)
 | 
			
		||||
                    .build()
 | 
			
		||||
                    .schedule()
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        fun cancelTask() {
 | 
			
		||||
            JobManager.instance().cancelAllForTag(TAG)
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
package eu.kanade.tachiyomi.data.updater
 | 
			
		||||
 | 
			
		||||
import android.app.PendingIntent
 | 
			
		||||
import android.content.Intent
 | 
			
		||||
import android.support.v4.app.NotificationCompat
 | 
			
		||||
import com.evernote.android.job.Job
 | 
			
		||||
import com.evernote.android.job.JobManager
 | 
			
		||||
import com.evernote.android.job.JobRequest
 | 
			
		||||
import eu.kanade.tachiyomi.R
 | 
			
		||||
import eu.kanade.tachiyomi.data.notification.Notifications
 | 
			
		||||
import eu.kanade.tachiyomi.util.notificationManager
 | 
			
		||||
 | 
			
		||||
class UpdaterJob : Job() {
 | 
			
		||||
 | 
			
		||||
    override fun onRunJob(params: Params): Result {
 | 
			
		||||
        return GithubUpdateChecker()
 | 
			
		||||
                .checkForUpdate()
 | 
			
		||||
                .map { result ->
 | 
			
		||||
                    if (result is GithubUpdateResult.NewUpdate) {
 | 
			
		||||
                        val url = result.release.downloadLink
 | 
			
		||||
 | 
			
		||||
                        val intent = Intent(context, UpdaterService::class.java).apply {
 | 
			
		||||
                            putExtra(UpdaterService.EXTRA_DOWNLOAD_URL, url)
 | 
			
		||||
                        }
 | 
			
		||||
 | 
			
		||||
                        NotificationCompat.Builder(context, Notifications.CHANNEL_COMMON).update {
 | 
			
		||||
                            setContentTitle(context.getString(R.string.app_name))
 | 
			
		||||
                            setContentText(context.getString(R.string.update_check_notification_update_available))
 | 
			
		||||
                            setSmallIcon(android.R.drawable.stat_sys_download_done)
 | 
			
		||||
                            // Download action
 | 
			
		||||
                            addAction(android.R.drawable.stat_sys_download_done,
 | 
			
		||||
                                    context.getString(R.string.action_download),
 | 
			
		||||
                                    PendingIntent.getService(context, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT))
 | 
			
		||||
                        }
 | 
			
		||||
                    }
 | 
			
		||||
                    Job.Result.SUCCESS
 | 
			
		||||
                }
 | 
			
		||||
                .onErrorReturn { Job.Result.FAILURE }
 | 
			
		||||
                // Sadly, the task needs to be synchronous.
 | 
			
		||||
                .toBlocking()
 | 
			
		||||
                .single()
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fun NotificationCompat.Builder.update(block: NotificationCompat.Builder.() -> Unit) {
 | 
			
		||||
        block()
 | 
			
		||||
        context.notificationManager.notify(Notifications.ID_UPDATER, build())
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    companion object {
 | 
			
		||||
        const val TAG = "UpdateChecker"
 | 
			
		||||
 | 
			
		||||
        fun setupTask() {
 | 
			
		||||
            JobRequest.Builder(TAG)
 | 
			
		||||
                    .setPeriodic(24 * 60 * 60 * 1000, 60 * 60 * 1000)
 | 
			
		||||
                    .setRequiredNetworkType(JobRequest.NetworkType.CONNECTED)
 | 
			
		||||
                    .setRequirementsEnforced(true)
 | 
			
		||||
                    .setUpdateCurrent(true)
 | 
			
		||||
                    .build()
 | 
			
		||||
                    .schedule()
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        fun cancelTask() {
 | 
			
		||||
            JobManager.instance().cancelAllForTag(TAG)
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
@@ -0,0 +1,109 @@
 | 
			
		||||
package eu.kanade.tachiyomi.data.updater
 | 
			
		||||
 | 
			
		||||
import android.content.Context
 | 
			
		||||
import android.net.Uri
 | 
			
		||||
import android.support.v4.app.NotificationCompat
 | 
			
		||||
import eu.kanade.tachiyomi.R
 | 
			
		||||
import eu.kanade.tachiyomi.data.notification.NotificationHandler
 | 
			
		||||
import eu.kanade.tachiyomi.data.notification.NotificationReceiver
 | 
			
		||||
import eu.kanade.tachiyomi.data.notification.Notifications
 | 
			
		||||
import eu.kanade.tachiyomi.util.notificationManager
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * DownloadNotifier is used to show notifications when downloading and update.
 | 
			
		||||
 *
 | 
			
		||||
 * @param context context of application.
 | 
			
		||||
 */
 | 
			
		||||
internal class UpdaterNotifier(private val context: Context) {
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Builder to manage notifications.
 | 
			
		||||
     */
 | 
			
		||||
    private val notification by lazy {
 | 
			
		||||
        NotificationCompat.Builder(context, Notifications.CHANNEL_COMMON)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Call to show notification.
 | 
			
		||||
     *
 | 
			
		||||
     * @param id id of the notification channel.
 | 
			
		||||
     */
 | 
			
		||||
    private fun NotificationCompat.Builder.show(id: Int = Notifications.ID_UPDATER) {
 | 
			
		||||
        context.notificationManager.notify(id, build())
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Call when apk download starts.
 | 
			
		||||
     *
 | 
			
		||||
     * @param title tile of notification.
 | 
			
		||||
     */
 | 
			
		||||
    fun onDownloadStarted(title: String) {
 | 
			
		||||
        with(notification) {
 | 
			
		||||
            setContentTitle(title)
 | 
			
		||||
            setContentText(context.getString(R.string.update_check_notification_download_in_progress))
 | 
			
		||||
            setSmallIcon(android.R.drawable.stat_sys_download)
 | 
			
		||||
            setOngoing(true)
 | 
			
		||||
        }
 | 
			
		||||
        notification.show()
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Call when apk download progress changes.
 | 
			
		||||
     *
 | 
			
		||||
     * @param progress progress of download (xx%/100).
 | 
			
		||||
     */
 | 
			
		||||
    fun onProgressChange(progress: Int) {
 | 
			
		||||
        with(notification) {
 | 
			
		||||
            setProgress(100, progress, false)
 | 
			
		||||
            setOnlyAlertOnce(true)
 | 
			
		||||
        }
 | 
			
		||||
        notification.show()
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Call when apk download is finished.
 | 
			
		||||
     *
 | 
			
		||||
     * @param uri path location of apk.
 | 
			
		||||
     */
 | 
			
		||||
    fun onDownloadFinished(uri: Uri) {
 | 
			
		||||
        with(notification) {
 | 
			
		||||
            setContentText(context.getString(R.string.update_check_notification_download_complete))
 | 
			
		||||
            setSmallIcon(android.R.drawable.stat_sys_download_done)
 | 
			
		||||
            setOnlyAlertOnce(false)
 | 
			
		||||
            setProgress(0, 0, false)
 | 
			
		||||
            // Install action
 | 
			
		||||
            setContentIntent(NotificationHandler.installApkPendingActivity(context, uri))
 | 
			
		||||
            addAction(R.drawable.ic_system_update_grey_24dp_img,
 | 
			
		||||
                    context.getString(R.string.action_install),
 | 
			
		||||
                    NotificationHandler.installApkPendingActivity(context, uri))
 | 
			
		||||
            // Cancel action
 | 
			
		||||
            addAction(R.drawable.ic_clear_grey_24dp_img,
 | 
			
		||||
                    context.getString(R.string.action_cancel),
 | 
			
		||||
                    NotificationReceiver.dismissNotificationPendingBroadcast(context, Notifications.ID_UPDATER))
 | 
			
		||||
        }
 | 
			
		||||
        notification.show()
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Call when apk download throws a error
 | 
			
		||||
     *
 | 
			
		||||
     * @param url web location of apk to download.
 | 
			
		||||
     */
 | 
			
		||||
    fun onDownloadError(url: String) {
 | 
			
		||||
        with(notification) {
 | 
			
		||||
            setContentText(context.getString(R.string.update_check_notification_download_error))
 | 
			
		||||
            setSmallIcon(android.R.drawable.stat_sys_warning)
 | 
			
		||||
            setOnlyAlertOnce(false)
 | 
			
		||||
            setProgress(0, 0, false)
 | 
			
		||||
            // Retry action
 | 
			
		||||
            addAction(R.drawable.ic_refresh_grey_24dp_img,
 | 
			
		||||
                    context.getString(R.string.action_retry),
 | 
			
		||||
                    UpdaterService.downloadApkPendingService(context, url))
 | 
			
		||||
            // Cancel action
 | 
			
		||||
            addAction(R.drawable.ic_clear_grey_24dp_img,
 | 
			
		||||
                    context.getString(R.string.action_cancel),
 | 
			
		||||
                    NotificationReceiver.dismissNotificationPendingBroadcast(context, Notifications.ID_UPDATER))
 | 
			
		||||
        }
 | 
			
		||||
        notification.show(Notifications.ID_UPDATER)
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -2,52 +2,37 @@ package eu.kanade.tachiyomi.data.updater
 | 
			
		||||
 | 
			
		||||
import android.app.IntentService
 | 
			
		||||
import android.app.PendingIntent
 | 
			
		||||
import android.content.BroadcastReceiver
 | 
			
		||||
import android.content.Context
 | 
			
		||||
import android.content.Intent
 | 
			
		||||
import android.content.IntentFilter
 | 
			
		||||
import eu.kanade.tachiyomi.BuildConfig
 | 
			
		||||
import eu.kanade.tachiyomi.R
 | 
			
		||||
import eu.kanade.tachiyomi.network.GET
 | 
			
		||||
import eu.kanade.tachiyomi.network.NetworkHelper
 | 
			
		||||
import eu.kanade.tachiyomi.network.ProgressListener
 | 
			
		||||
import eu.kanade.tachiyomi.network.newCallWithProgress
 | 
			
		||||
import eu.kanade.tachiyomi.util.registerLocalReceiver
 | 
			
		||||
import eu.kanade.tachiyomi.util.getUriCompat
 | 
			
		||||
import eu.kanade.tachiyomi.util.saveTo
 | 
			
		||||
import eu.kanade.tachiyomi.util.sendLocalBroadcastSync
 | 
			
		||||
import eu.kanade.tachiyomi.util.unregisterLocalReceiver
 | 
			
		||||
import timber.log.Timber
 | 
			
		||||
import uy.kohesive.injekt.injectLazy
 | 
			
		||||
import java.io.File
 | 
			
		||||
 | 
			
		||||
class UpdateDownloaderService : IntentService(UpdateDownloaderService::class.java.name) {
 | 
			
		||||
class UpdaterService : IntentService(UpdaterService::class.java.name) {
 | 
			
		||||
    /**
 | 
			
		||||
     * Network helper
 | 
			
		||||
     */
 | 
			
		||||
    private val network: NetworkHelper by injectLazy()
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Local [BroadcastReceiver] that runs on UI thread
 | 
			
		||||
     * Notifier for the updater state and progress.
 | 
			
		||||
     */
 | 
			
		||||
    private val updaterNotificationReceiver = UpdateDownloaderReceiver(this)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    override fun onCreate() {
 | 
			
		||||
        super.onCreate()
 | 
			
		||||
        // Register receiver
 | 
			
		||||
        registerLocalReceiver(updaterNotificationReceiver, IntentFilter(INTENT_FILTER_NAME))
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    override fun onDestroy() {
 | 
			
		||||
        // Unregister receiver
 | 
			
		||||
        unregisterLocalReceiver(updaterNotificationReceiver)
 | 
			
		||||
        super.onDestroy()
 | 
			
		||||
    }
 | 
			
		||||
    private val notifier by lazy { UpdaterNotifier(this) }
 | 
			
		||||
 | 
			
		||||
    override fun onHandleIntent(intent: Intent?) {
 | 
			
		||||
        if (intent == null) return
 | 
			
		||||
 | 
			
		||||
        val title = intent.getStringExtra(EXTRA_DOWNLOAD_TITLE) ?: getString(R.string.app_name)
 | 
			
		||||
        val url = intent.getStringExtra(EXTRA_DOWNLOAD_URL) ?: return
 | 
			
		||||
        downloadApk(url)
 | 
			
		||||
        downloadApk(title, url)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
@@ -55,9 +40,9 @@ class UpdateDownloaderService : IntentService(UpdateDownloaderService::class.jav
 | 
			
		||||
     *
 | 
			
		||||
     * @param url url location of file
 | 
			
		||||
     */
 | 
			
		||||
    fun downloadApk(url: String) {
 | 
			
		||||
    private fun downloadApk(title: String, url: String) {
 | 
			
		||||
        // Show notification download starting.
 | 
			
		||||
        sendInitialBroadcast()
 | 
			
		||||
        notifier.onDownloadStarted(title)
 | 
			
		||||
 | 
			
		||||
        val progressListener = object : ProgressListener {
 | 
			
		||||
 | 
			
		||||
@@ -73,7 +58,7 @@ class UpdateDownloaderService : IntentService(UpdateDownloaderService::class.jav
 | 
			
		||||
                if (progress > savedProgress && currentTime - 200 > lastTick) {
 | 
			
		||||
                    savedProgress = progress
 | 
			
		||||
                    lastTick = currentTime
 | 
			
		||||
                    sendProgressBroadcast(progress)
 | 
			
		||||
                    notifier.onProgressChange(progress)
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
@@ -91,80 +76,32 @@ class UpdateDownloaderService : IntentService(UpdateDownloaderService::class.jav
 | 
			
		||||
                response.close()
 | 
			
		||||
                throw Exception("Unsuccessful response")
 | 
			
		||||
            }
 | 
			
		||||
            sendInstallBroadcast(apkFile.absolutePath)
 | 
			
		||||
            notifier.onDownloadFinished(apkFile.getUriCompat(this))
 | 
			
		||||
        } catch (error: Exception) {
 | 
			
		||||
            Timber.e(error)
 | 
			
		||||
            sendErrorBroadcast(url)
 | 
			
		||||
            notifier.onDownloadError(url)
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Show notification download starting.
 | 
			
		||||
     */
 | 
			
		||||
    private fun sendInitialBroadcast() {
 | 
			
		||||
        val intent = Intent(INTENT_FILTER_NAME).apply {
 | 
			
		||||
            putExtra(UpdateDownloaderReceiver.EXTRA_ACTION, UpdateDownloaderReceiver.NOTIFICATION_UPDATER_INITIAL)
 | 
			
		||||
        }
 | 
			
		||||
        sendLocalBroadcastSync(intent)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Show notification progress changed
 | 
			
		||||
     *
 | 
			
		||||
     * @param progress progress of download
 | 
			
		||||
     */
 | 
			
		||||
    private fun sendProgressBroadcast(progress: Int) {
 | 
			
		||||
        val intent = Intent(INTENT_FILTER_NAME).apply {
 | 
			
		||||
            putExtra(UpdateDownloaderReceiver.EXTRA_ACTION, UpdateDownloaderReceiver.NOTIFICATION_UPDATER_PROGRESS)
 | 
			
		||||
            putExtra(UpdateDownloaderReceiver.EXTRA_PROGRESS, progress)
 | 
			
		||||
        }
 | 
			
		||||
        sendLocalBroadcastSync(intent)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Show install notification.
 | 
			
		||||
     *
 | 
			
		||||
     * @param path location of file
 | 
			
		||||
     */
 | 
			
		||||
    private fun sendInstallBroadcast(path: String){
 | 
			
		||||
        val intent = Intent(INTENT_FILTER_NAME).apply {
 | 
			
		||||
            putExtra(UpdateDownloaderReceiver.EXTRA_ACTION, UpdateDownloaderReceiver.NOTIFICATION_UPDATER_INSTALL)
 | 
			
		||||
            putExtra(UpdateDownloaderReceiver.EXTRA_APK_PATH, path)
 | 
			
		||||
        }
 | 
			
		||||
        sendLocalBroadcastSync(intent)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Show error notification.
 | 
			
		||||
     *
 | 
			
		||||
     * @param url url of file
 | 
			
		||||
     */
 | 
			
		||||
    private fun sendErrorBroadcast(url: String){
 | 
			
		||||
        val intent = Intent(INTENT_FILTER_NAME).apply {
 | 
			
		||||
            putExtra(UpdateDownloaderReceiver.EXTRA_ACTION, UpdateDownloaderReceiver.NOTIFICATION_UPDATER_ERROR)
 | 
			
		||||
            putExtra(UpdateDownloaderReceiver.EXTRA_APK_URL, url)
 | 
			
		||||
        }
 | 
			
		||||
        sendLocalBroadcastSync(intent)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    companion object {
 | 
			
		||||
        /**
 | 
			
		||||
         * Name of Local BroadCastReceiver.
 | 
			
		||||
         */
 | 
			
		||||
        private val INTENT_FILTER_NAME = UpdateDownloaderService::class.java.name
 | 
			
		||||
 | 
			
		||||
        /**
 | 
			
		||||
         * Download url.
 | 
			
		||||
         */
 | 
			
		||||
        internal const val EXTRA_DOWNLOAD_URL = "${BuildConfig.APPLICATION_ID}.UpdateDownloaderService.DOWNLOAD_URL"
 | 
			
		||||
        internal const val EXTRA_DOWNLOAD_URL = "${BuildConfig.APPLICATION_ID}.UpdaterService.DOWNLOAD_URL"
 | 
			
		||||
 | 
			
		||||
        /**
 | 
			
		||||
         * Download title
 | 
			
		||||
         */
 | 
			
		||||
        internal const val EXTRA_DOWNLOAD_TITLE = "${BuildConfig.APPLICATION_ID}.UpdaterService.DOWNLOAD_TITLE"
 | 
			
		||||
 | 
			
		||||
        /**
 | 
			
		||||
         * Downloads a new update and let the user install the new version from a notification.
 | 
			
		||||
         * @param context the application context.
 | 
			
		||||
         * @param url the url to the new update.
 | 
			
		||||
         */
 | 
			
		||||
        fun downloadUpdate(context: Context, url: String) {
 | 
			
		||||
            val intent = Intent(context, UpdateDownloaderService::class.java).apply {
 | 
			
		||||
        fun downloadUpdate(context: Context, url: String, title: String = context.getString(R.string.app_name)) {
 | 
			
		||||
            val intent = Intent(context, UpdaterService::class.java).apply {
 | 
			
		||||
                putExtra(EXTRA_DOWNLOAD_TITLE, title)
 | 
			
		||||
                putExtra(EXTRA_DOWNLOAD_URL, url)
 | 
			
		||||
            }
 | 
			
		||||
            context.startService(intent)
 | 
			
		||||
@@ -177,7 +114,7 @@ class UpdateDownloaderService : IntentService(UpdateDownloaderService::class.jav
 | 
			
		||||
         * @return [PendingIntent]
 | 
			
		||||
         */
 | 
			
		||||
        internal fun downloadApkPendingService(context: Context, url: String): PendingIntent {
 | 
			
		||||
            val intent = Intent(context, UpdateDownloaderService::class.java).apply {
 | 
			
		||||
            val intent = Intent(context, UpdaterService::class.java).apply {
 | 
			
		||||
                putExtra(EXTRA_DOWNLOAD_URL, url)
 | 
			
		||||
            }
 | 
			
		||||
            return PendingIntent.getService(context, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT)
 | 
			
		||||
@@ -11,8 +11,8 @@ import eu.kanade.tachiyomi.BuildConfig
 | 
			
		||||
import eu.kanade.tachiyomi.R
 | 
			
		||||
import eu.kanade.tachiyomi.data.updater.GithubUpdateChecker
 | 
			
		||||
import eu.kanade.tachiyomi.data.updater.GithubUpdateResult
 | 
			
		||||
import eu.kanade.tachiyomi.data.updater.UpdateCheckerJob
 | 
			
		||||
import eu.kanade.tachiyomi.data.updater.UpdateDownloaderService
 | 
			
		||||
import eu.kanade.tachiyomi.data.updater.UpdaterJob
 | 
			
		||||
import eu.kanade.tachiyomi.data.updater.UpdaterService
 | 
			
		||||
import eu.kanade.tachiyomi.ui.base.controller.DialogController
 | 
			
		||||
import eu.kanade.tachiyomi.util.toast
 | 
			
		||||
import rx.Subscription
 | 
			
		||||
@@ -59,9 +59,9 @@ class SettingsAboutController : SettingsController() {
 | 
			
		||||
                onChange { newValue ->
 | 
			
		||||
                    val checked = newValue as Boolean
 | 
			
		||||
                    if (checked) {
 | 
			
		||||
                        UpdateCheckerJob.setupTask()
 | 
			
		||||
                        UpdaterJob.setupTask()
 | 
			
		||||
                    } else {
 | 
			
		||||
                        UpdateCheckerJob.cancelTask()
 | 
			
		||||
                        UpdaterJob.cancelTask()
 | 
			
		||||
                    }
 | 
			
		||||
                    true
 | 
			
		||||
                }
 | 
			
		||||
@@ -148,7 +148,7 @@ class SettingsAboutController : SettingsController() {
 | 
			
		||||
                        if (appContext != null) {
 | 
			
		||||
                            // Start download
 | 
			
		||||
                            val url = args.getString(URL_KEY)
 | 
			
		||||
                            UpdateDownloaderService.downloadUpdate(appContext, url)
 | 
			
		||||
                            UpdaterService.downloadUpdate(appContext, url)
 | 
			
		||||
                        }
 | 
			
		||||
                    }
 | 
			
		||||
                    .build()
 | 
			
		||||
 
 | 
			
		||||
@@ -13,9 +13,8 @@ import java.io.File
 | 
			
		||||
 * @param context context of application
 | 
			
		||||
 */
 | 
			
		||||
fun File.getUriCompat(context: Context): Uri {
 | 
			
		||||
    val uri = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N)
 | 
			
		||||
    return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N)
 | 
			
		||||
        FileProvider.getUriForFile(context, BuildConfig.APPLICATION_ID + ".provider", this)
 | 
			
		||||
    else Uri.fromFile(this)
 | 
			
		||||
    return uri
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user