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:
parent
6a63ce992a
commit
8994b42760
@ -86,7 +86,7 @@
|
|||||||
android:exported="false" />
|
android:exported="false" />
|
||||||
|
|
||||||
<service
|
<service
|
||||||
android:name=".data.updater.UpdateDownloaderService"
|
android:name=".data.updater.UpdaterService"
|
||||||
android:exported="false" />
|
android:exported="false" />
|
||||||
|
|
||||||
<service
|
<service
|
||||||
|
@ -8,7 +8,7 @@ import com.evernote.android.job.JobManager
|
|||||||
import eu.kanade.tachiyomi.data.backup.BackupCreatorJob
|
import eu.kanade.tachiyomi.data.backup.BackupCreatorJob
|
||||||
import eu.kanade.tachiyomi.data.library.LibraryUpdateJob
|
import eu.kanade.tachiyomi.data.library.LibraryUpdateJob
|
||||||
import eu.kanade.tachiyomi.data.notification.Notifications
|
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 eu.kanade.tachiyomi.util.LocaleHelper
|
||||||
import org.acra.ACRA
|
import org.acra.ACRA
|
||||||
import org.acra.annotation.ReportsCrashes
|
import org.acra.annotation.ReportsCrashes
|
||||||
@ -60,7 +60,7 @@ open class App : Application() {
|
|||||||
JobManager.create(this).addJobCreator { tag ->
|
JobManager.create(this).addJobCreator { tag ->
|
||||||
when (tag) {
|
when (tag) {
|
||||||
LibraryUpdateJob.TAG -> LibraryUpdateJob()
|
LibraryUpdateJob.TAG -> LibraryUpdateJob()
|
||||||
UpdateCheckerJob.TAG -> UpdateCheckerJob()
|
UpdaterJob.TAG -> UpdaterJob()
|
||||||
BackupCreatorJob.TAG -> BackupCreatorJob()
|
BackupCreatorJob.TAG -> BackupCreatorJob()
|
||||||
else -> null
|
else -> null
|
||||||
}
|
}
|
||||||
|
@ -3,7 +3,7 @@ package eu.kanade.tachiyomi
|
|||||||
import eu.kanade.tachiyomi.data.library.LibraryUpdateJob
|
import eu.kanade.tachiyomi.data.library.LibraryUpdateJob
|
||||||
import eu.kanade.tachiyomi.data.preference.PreferencesHelper
|
import eu.kanade.tachiyomi.data.preference.PreferencesHelper
|
||||||
import eu.kanade.tachiyomi.data.preference.getOrDefault
|
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
|
import java.io.File
|
||||||
|
|
||||||
object Migrations {
|
object Migrations {
|
||||||
@ -25,7 +25,7 @@ object Migrations {
|
|||||||
if (oldVersion < 14) {
|
if (oldVersion < 14) {
|
||||||
// Restore jobs after upgrading to evernote's job scheduler.
|
// Restore jobs after upgrading to evernote's job scheduler.
|
||||||
if (BuildConfig.INCLUDE_UPDATER && preferences.automaticUpdates()) {
|
if (BuildConfig.INCLUDE_UPDATER && preferences.automaticUpdates()) {
|
||||||
UpdateCheckerJob.setupTask()
|
UpdaterJob.setupTask()
|
||||||
}
|
}
|
||||||
LibraryUpdateJob.setupTask()
|
LibraryUpdateJob.setupTask()
|
||||||
}
|
}
|
||||||
|
@ -3,6 +3,7 @@ package eu.kanade.tachiyomi.data.notification
|
|||||||
import android.app.PendingIntent
|
import android.app.PendingIntent
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
|
import android.net.Uri
|
||||||
import eu.kanade.tachiyomi.ui.main.MainActivity
|
import eu.kanade.tachiyomi.ui.main.MainActivity
|
||||||
import eu.kanade.tachiyomi.util.getUriCompat
|
import eu.kanade.tachiyomi.util.getUriCompat
|
||||||
import java.io.File
|
import java.io.File
|
||||||
@ -43,11 +44,10 @@ object NotificationHandler {
|
|||||||
* Returns [PendingIntent] that prompts user with apk install intent
|
* Returns [PendingIntent] that prompts user with apk install intent
|
||||||
*
|
*
|
||||||
* @param context context
|
* @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 intent = Intent(Intent.ACTION_VIEW).apply {
|
||||||
val uri = file.getUriCompat(context)
|
|
||||||
setDataAndType(uri, "application/vnd.android.package-archive")
|
setDataAndType(uri, "application/vnd.android.package-archive")
|
||||||
flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_GRANT_READ_URI_PERMISSION
|
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.
|
* @param assets assets of latest release.
|
||||||
*/
|
*/
|
||||||
class GithubRelease(@SerializedName("tag_name") val version: String,
|
class GithubRelease(@SerializedName("tag_name") val version: String,
|
||||||
@SerializedName("body") val changeLog: String,
|
@SerializedName("body") val changeLog: String,
|
||||||
@SerializedName("assets") val assets: List<Assets>) {
|
@SerializedName("assets") private val assets: List<Assets>) {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get download link of latest release from the 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 eu.kanade.tachiyomi.BuildConfig
|
||||||
import rx.Observable
|
import rx.Observable
|
||||||
|
|
||||||
class GithubUpdateChecker() {
|
class GithubUpdateChecker {
|
||||||
|
|
||||||
private val service: GithubService = GithubService.create()
|
private val service: GithubService = GithubService.create()
|
||||||
|
|
||||||
|
@ -3,5 +3,5 @@ package eu.kanade.tachiyomi.data.updater
|
|||||||
sealed class GithubUpdateResult {
|
sealed class GithubUpdateResult {
|
||||||
|
|
||||||
class NewUpdate(val release: GithubRelease): 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
|
package eu.kanade.tachiyomi.data.updater
|
||||||
|
|
||||||
import android.app.PendingIntent
|
import android.app.PendingIntent
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
import android.support.v4.app.NotificationCompat
|
import android.support.v4.app.NotificationCompat
|
||||||
import com.evernote.android.job.Job
|
import com.evernote.android.job.Job
|
||||||
import com.evernote.android.job.JobManager
|
import com.evernote.android.job.JobManager
|
||||||
import com.evernote.android.job.JobRequest
|
import com.evernote.android.job.JobRequest
|
||||||
import eu.kanade.tachiyomi.R
|
import eu.kanade.tachiyomi.R
|
||||||
import eu.kanade.tachiyomi.data.notification.Notifications
|
import eu.kanade.tachiyomi.data.notification.Notifications
|
||||||
import eu.kanade.tachiyomi.util.notificationManager
|
import eu.kanade.tachiyomi.util.notificationManager
|
||||||
|
|
||||||
class UpdateCheckerJob : Job() {
|
class UpdaterJob : Job() {
|
||||||
|
|
||||||
override fun onRunJob(params: Params): Result {
|
override fun onRunJob(params: Params): Result {
|
||||||
return GithubUpdateChecker()
|
return GithubUpdateChecker()
|
||||||
.checkForUpdate()
|
.checkForUpdate()
|
||||||
.map { result ->
|
.map { result ->
|
||||||
if (result is GithubUpdateResult.NewUpdate) {
|
if (result is GithubUpdateResult.NewUpdate) {
|
||||||
val url = result.release.downloadLink
|
val url = result.release.downloadLink
|
||||||
|
|
||||||
val intent = Intent(context, UpdateDownloaderService::class.java).apply {
|
val intent = Intent(context, UpdaterService::class.java).apply {
|
||||||
putExtra(UpdateDownloaderService.EXTRA_DOWNLOAD_URL, url)
|
putExtra(UpdaterService.EXTRA_DOWNLOAD_URL, url)
|
||||||
}
|
}
|
||||||
|
|
||||||
NotificationCompat.Builder(context, Notifications.CHANNEL_COMMON).update {
|
NotificationCompat.Builder(context, Notifications.CHANNEL_COMMON).update {
|
||||||
setContentTitle(context.getString(R.string.app_name))
|
setContentTitle(context.getString(R.string.app_name))
|
||||||
setContentText(context.getString(R.string.update_check_notification_update_available))
|
setContentText(context.getString(R.string.update_check_notification_update_available))
|
||||||
setSmallIcon(android.R.drawable.stat_sys_download_done)
|
setSmallIcon(android.R.drawable.stat_sys_download_done)
|
||||||
// Download action
|
// Download action
|
||||||
addAction(android.R.drawable.stat_sys_download_done,
|
addAction(android.R.drawable.stat_sys_download_done,
|
||||||
context.getString(R.string.action_download),
|
context.getString(R.string.action_download),
|
||||||
PendingIntent.getService(context, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT))
|
PendingIntent.getService(context, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Job.Result.SUCCESS
|
Job.Result.SUCCESS
|
||||||
}
|
}
|
||||||
.onErrorReturn { Job.Result.FAILURE }
|
.onErrorReturn { Job.Result.FAILURE }
|
||||||
// Sadly, the task needs to be synchronous.
|
// Sadly, the task needs to be synchronous.
|
||||||
.toBlocking()
|
.toBlocking()
|
||||||
.single()
|
.single()
|
||||||
}
|
}
|
||||||
|
|
||||||
fun NotificationCompat.Builder.update(block: NotificationCompat.Builder.() -> Unit) {
|
fun NotificationCompat.Builder.update(block: NotificationCompat.Builder.() -> Unit) {
|
||||||
block()
|
block()
|
||||||
context.notificationManager.notify(Notifications.ID_UPDATER, build())
|
context.notificationManager.notify(Notifications.ID_UPDATER, build())
|
||||||
}
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
const val TAG = "UpdateChecker"
|
const val TAG = "UpdateChecker"
|
||||||
|
|
||||||
fun setupTask() {
|
fun setupTask() {
|
||||||
JobRequest.Builder(TAG)
|
JobRequest.Builder(TAG)
|
||||||
.setPeriodic(24 * 60 * 60 * 1000, 60 * 60 * 1000)
|
.setPeriodic(24 * 60 * 60 * 1000, 60 * 60 * 1000)
|
||||||
.setRequiredNetworkType(JobRequest.NetworkType.CONNECTED)
|
.setRequiredNetworkType(JobRequest.NetworkType.CONNECTED)
|
||||||
.setRequirementsEnforced(true)
|
.setRequirementsEnforced(true)
|
||||||
.setUpdateCurrent(true)
|
.setUpdateCurrent(true)
|
||||||
.build()
|
.build()
|
||||||
.schedule()
|
.schedule()
|
||||||
}
|
}
|
||||||
|
|
||||||
fun cancelTask() {
|
fun cancelTask() {
|
||||||
JobManager.instance().cancelAllForTag(TAG)
|
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.IntentService
|
||||||
import android.app.PendingIntent
|
import android.app.PendingIntent
|
||||||
import android.content.BroadcastReceiver
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
import android.content.IntentFilter
|
|
||||||
import eu.kanade.tachiyomi.BuildConfig
|
import eu.kanade.tachiyomi.BuildConfig
|
||||||
|
import eu.kanade.tachiyomi.R
|
||||||
import eu.kanade.tachiyomi.network.GET
|
import eu.kanade.tachiyomi.network.GET
|
||||||
import eu.kanade.tachiyomi.network.NetworkHelper
|
import eu.kanade.tachiyomi.network.NetworkHelper
|
||||||
import eu.kanade.tachiyomi.network.ProgressListener
|
import eu.kanade.tachiyomi.network.ProgressListener
|
||||||
import eu.kanade.tachiyomi.network.newCallWithProgress
|
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.saveTo
|
||||||
import eu.kanade.tachiyomi.util.sendLocalBroadcastSync
|
|
||||||
import eu.kanade.tachiyomi.util.unregisterLocalReceiver
|
|
||||||
import timber.log.Timber
|
import timber.log.Timber
|
||||||
import uy.kohesive.injekt.injectLazy
|
import uy.kohesive.injekt.injectLazy
|
||||||
import java.io.File
|
import java.io.File
|
||||||
|
|
||||||
class UpdateDownloaderService : IntentService(UpdateDownloaderService::class.java.name) {
|
class UpdaterService : IntentService(UpdaterService::class.java.name) {
|
||||||
/**
|
/**
|
||||||
* Network helper
|
* Network helper
|
||||||
*/
|
*/
|
||||||
private val network: NetworkHelper by injectLazy()
|
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)
|
private val notifier by lazy { UpdaterNotifier(this) }
|
||||||
|
|
||||||
|
|
||||||
override fun onCreate() {
|
|
||||||
super.onCreate()
|
|
||||||
// Register receiver
|
|
||||||
registerLocalReceiver(updaterNotificationReceiver, IntentFilter(INTENT_FILTER_NAME))
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onDestroy() {
|
|
||||||
// Unregister receiver
|
|
||||||
unregisterLocalReceiver(updaterNotificationReceiver)
|
|
||||||
super.onDestroy()
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onHandleIntent(intent: Intent?) {
|
override fun onHandleIntent(intent: Intent?) {
|
||||||
if (intent == null) return
|
if (intent == null) return
|
||||||
|
|
||||||
|
val title = intent.getStringExtra(EXTRA_DOWNLOAD_TITLE) ?: getString(R.string.app_name)
|
||||||
val url = intent.getStringExtra(EXTRA_DOWNLOAD_URL) ?: return
|
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
|
* @param url url location of file
|
||||||
*/
|
*/
|
||||||
fun downloadApk(url: String) {
|
private fun downloadApk(title: String, url: String) {
|
||||||
// Show notification download starting.
|
// Show notification download starting.
|
||||||
sendInitialBroadcast()
|
notifier.onDownloadStarted(title)
|
||||||
|
|
||||||
val progressListener = object : ProgressListener {
|
val progressListener = object : ProgressListener {
|
||||||
|
|
||||||
@ -73,7 +58,7 @@ class UpdateDownloaderService : IntentService(UpdateDownloaderService::class.jav
|
|||||||
if (progress > savedProgress && currentTime - 200 > lastTick) {
|
if (progress > savedProgress && currentTime - 200 > lastTick) {
|
||||||
savedProgress = progress
|
savedProgress = progress
|
||||||
lastTick = currentTime
|
lastTick = currentTime
|
||||||
sendProgressBroadcast(progress)
|
notifier.onProgressChange(progress)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -91,80 +76,32 @@ class UpdateDownloaderService : IntentService(UpdateDownloaderService::class.jav
|
|||||||
response.close()
|
response.close()
|
||||||
throw Exception("Unsuccessful response")
|
throw Exception("Unsuccessful response")
|
||||||
}
|
}
|
||||||
sendInstallBroadcast(apkFile.absolutePath)
|
notifier.onDownloadFinished(apkFile.getUriCompat(this))
|
||||||
} catch (error: Exception) {
|
} catch (error: Exception) {
|
||||||
Timber.e(error)
|
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 {
|
companion object {
|
||||||
/**
|
|
||||||
* Name of Local BroadCastReceiver.
|
|
||||||
*/
|
|
||||||
private val INTENT_FILTER_NAME = UpdateDownloaderService::class.java.name
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Download url.
|
* 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.
|
* Downloads a new update and let the user install the new version from a notification.
|
||||||
* @param context the application context.
|
* @param context the application context.
|
||||||
* @param url the url to the new update.
|
* @param url the url to the new update.
|
||||||
*/
|
*/
|
||||||
fun downloadUpdate(context: Context, url: String) {
|
fun downloadUpdate(context: Context, url: String, title: String = context.getString(R.string.app_name)) {
|
||||||
val intent = Intent(context, UpdateDownloaderService::class.java).apply {
|
val intent = Intent(context, UpdaterService::class.java).apply {
|
||||||
|
putExtra(EXTRA_DOWNLOAD_TITLE, title)
|
||||||
putExtra(EXTRA_DOWNLOAD_URL, url)
|
putExtra(EXTRA_DOWNLOAD_URL, url)
|
||||||
}
|
}
|
||||||
context.startService(intent)
|
context.startService(intent)
|
||||||
@ -177,7 +114,7 @@ class UpdateDownloaderService : IntentService(UpdateDownloaderService::class.jav
|
|||||||
* @return [PendingIntent]
|
* @return [PendingIntent]
|
||||||
*/
|
*/
|
||||||
internal fun downloadApkPendingService(context: Context, url: String): 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)
|
putExtra(EXTRA_DOWNLOAD_URL, url)
|
||||||
}
|
}
|
||||||
return PendingIntent.getService(context, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT)
|
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.R
|
||||||
import eu.kanade.tachiyomi.data.updater.GithubUpdateChecker
|
import eu.kanade.tachiyomi.data.updater.GithubUpdateChecker
|
||||||
import eu.kanade.tachiyomi.data.updater.GithubUpdateResult
|
import eu.kanade.tachiyomi.data.updater.GithubUpdateResult
|
||||||
import eu.kanade.tachiyomi.data.updater.UpdateCheckerJob
|
import eu.kanade.tachiyomi.data.updater.UpdaterJob
|
||||||
import eu.kanade.tachiyomi.data.updater.UpdateDownloaderService
|
import eu.kanade.tachiyomi.data.updater.UpdaterService
|
||||||
import eu.kanade.tachiyomi.ui.base.controller.DialogController
|
import eu.kanade.tachiyomi.ui.base.controller.DialogController
|
||||||
import eu.kanade.tachiyomi.util.toast
|
import eu.kanade.tachiyomi.util.toast
|
||||||
import rx.Subscription
|
import rx.Subscription
|
||||||
@ -59,9 +59,9 @@ class SettingsAboutController : SettingsController() {
|
|||||||
onChange { newValue ->
|
onChange { newValue ->
|
||||||
val checked = newValue as Boolean
|
val checked = newValue as Boolean
|
||||||
if (checked) {
|
if (checked) {
|
||||||
UpdateCheckerJob.setupTask()
|
UpdaterJob.setupTask()
|
||||||
} else {
|
} else {
|
||||||
UpdateCheckerJob.cancelTask()
|
UpdaterJob.cancelTask()
|
||||||
}
|
}
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
@ -148,7 +148,7 @@ class SettingsAboutController : SettingsController() {
|
|||||||
if (appContext != null) {
|
if (appContext != null) {
|
||||||
// Start download
|
// Start download
|
||||||
val url = args.getString(URL_KEY)
|
val url = args.getString(URL_KEY)
|
||||||
UpdateDownloaderService.downloadUpdate(appContext, url)
|
UpdaterService.downloadUpdate(appContext, url)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.build()
|
.build()
|
||||||
|
@ -13,9 +13,8 @@ import java.io.File
|
|||||||
* @param context context of application
|
* @param context context of application
|
||||||
*/
|
*/
|
||||||
fun File.getUriCompat(context: Context): Uri {
|
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)
|
FileProvider.getUriForFile(context, BuildConfig.APPLICATION_ID + ".provider", this)
|
||||||
else Uri.fromFile(this)
|
else Uri.fromFile(this)
|
||||||
return uri
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user