mirror of
https://github.com/mihonapp/mihon.git
synced 2024-11-21 20:47:03 -05:00
Rewrote UpdateDownloader to Kotlin
Added auto update check (every 12 hour) Warning message optional fix #256 Lots of bug fixes!
This commit is contained in:
parent
ec9c19ce7d
commit
55e9d2880c
23 changed files with 532 additions and 249 deletions
|
@ -1,6 +1,6 @@
|
||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<manifest package="eu.kanade.tachiyomi"
|
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
xmlns:android="http://schemas.android.com/apk/res/android">
|
package="eu.kanade.tachiyomi">
|
||||||
|
|
||||||
<uses-permission android:name="android.permission.INTERNET" />
|
<uses-permission android:name="android.permission.INTERNET" />
|
||||||
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
|
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
|
||||||
|
@ -68,7 +68,19 @@
|
||||||
</receiver>
|
</receiver>
|
||||||
|
|
||||||
<receiver
|
<receiver
|
||||||
android:name=".data.library.LibraryUpdateService$LibraryUpdateReceiver">
|
android:name=".data.library.LibraryUpdateService$SyncOnPowerConnected"
|
||||||
|
android:enabled="false">
|
||||||
|
<intent-filter>
|
||||||
|
<action android:name="android.intent.action.ACTION_POWER_CONNECTED" />
|
||||||
|
</intent-filter>
|
||||||
|
</receiver>
|
||||||
|
|
||||||
|
<receiver
|
||||||
|
android:name=".data.library.LibraryUpdateService$CancelUpdateReceiver">
|
||||||
|
</receiver>
|
||||||
|
|
||||||
|
<receiver
|
||||||
|
android:name=".data.updater.UpdateDownloader$InstallOnReceived">
|
||||||
</receiver>
|
</receiver>
|
||||||
|
|
||||||
<receiver
|
<receiver
|
||||||
|
@ -79,6 +91,15 @@
|
||||||
</intent-filter>
|
</intent-filter>
|
||||||
</receiver>
|
</receiver>
|
||||||
|
|
||||||
|
<receiver
|
||||||
|
android:name=".data.updater.UpdateDownloaderAlarm">
|
||||||
|
<intent-filter>
|
||||||
|
<action android:name="android.intent.action.BOOT_COMPLETED"/>
|
||||||
|
<action android:name="eu.kanade.CHECK_UPDATE"/>
|
||||||
|
</intent-filter>
|
||||||
|
</receiver>
|
||||||
|
|
||||||
|
|
||||||
<meta-data
|
<meta-data
|
||||||
android:name="eu.kanade.tachiyomi.data.cache.CoverGlideModule"
|
android:name="eu.kanade.tachiyomi.data.cache.CoverGlideModule"
|
||||||
android:value="GlideModule" />
|
android:value="GlideModule" />
|
||||||
|
|
|
@ -9,6 +9,8 @@ import android.os.IBinder
|
||||||
import android.os.PowerManager
|
import android.os.PowerManager
|
||||||
import android.support.v4.app.NotificationCompat
|
import android.support.v4.app.NotificationCompat
|
||||||
import android.util.Pair
|
import android.util.Pair
|
||||||
|
import com.github.pwittchen.reactivenetwork.library.ConnectivityStatus
|
||||||
|
import com.github.pwittchen.reactivenetwork.library.ReactiveNetwork
|
||||||
import eu.kanade.tachiyomi.App
|
import eu.kanade.tachiyomi.App
|
||||||
import eu.kanade.tachiyomi.R
|
import eu.kanade.tachiyomi.R
|
||||||
import eu.kanade.tachiyomi.data.database.DatabaseHelper
|
import eu.kanade.tachiyomi.data.database.DatabaseHelper
|
||||||
|
@ -16,42 +18,14 @@ import eu.kanade.tachiyomi.data.database.models.Manga
|
||||||
import eu.kanade.tachiyomi.data.preference.PreferencesHelper
|
import eu.kanade.tachiyomi.data.preference.PreferencesHelper
|
||||||
import eu.kanade.tachiyomi.data.source.SourceManager
|
import eu.kanade.tachiyomi.data.source.SourceManager
|
||||||
import eu.kanade.tachiyomi.ui.main.MainActivity
|
import eu.kanade.tachiyomi.ui.main.MainActivity
|
||||||
import eu.kanade.tachiyomi.util.AndroidComponentUtil
|
import eu.kanade.tachiyomi.util.*
|
||||||
import eu.kanade.tachiyomi.util.DeviceUtil
|
|
||||||
import eu.kanade.tachiyomi.util.notification
|
|
||||||
import eu.kanade.tachiyomi.util.notificationManager
|
|
||||||
import rx.Observable
|
import rx.Observable
|
||||||
import rx.Subscription
|
import rx.Subscription
|
||||||
import rx.schedulers.Schedulers
|
import rx.schedulers.Schedulers
|
||||||
import timber.log.Timber
|
|
||||||
import java.util.*
|
import java.util.*
|
||||||
import java.util.concurrent.atomic.AtomicInteger
|
import java.util.concurrent.atomic.AtomicInteger
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
// Intent key for forced library update
|
|
||||||
val UPDATE_IS_FORCED = "is_forced"
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get the start intent for [LibraryUpdateService].
|
|
||||||
* @param context the application context.
|
|
||||||
* @param isForced true when forcing library update
|
|
||||||
* @return the intent of the service.
|
|
||||||
*/
|
|
||||||
fun getIntent(context: Context, isForced: Boolean = false): Intent {
|
|
||||||
return Intent(context, LibraryUpdateService::class.java).apply {
|
|
||||||
putExtra(UPDATE_IS_FORCED, isForced)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the status of the service.
|
|
||||||
* @param context the application context.
|
|
||||||
* @return true if the service is running, false otherwise.
|
|
||||||
*/
|
|
||||||
fun isRunning(context: Context): Boolean {
|
|
||||||
return AndroidComponentUtil.isServiceRunning(context, LibraryUpdateService::class.java)
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This class will take care of updating the chapters of the manga from the library. It can be
|
* This class will take care of updating the chapters of the manga from the library. It can be
|
||||||
* started calling the [start] method. If it's already running, it won't do anything.
|
* started calling the [start] method. If it's already running, it won't do anything.
|
||||||
|
@ -77,6 +51,30 @@ class LibraryUpdateService : Service() {
|
||||||
companion object {
|
companion object {
|
||||||
val UPDATE_NOTIFICATION_ID = 1
|
val UPDATE_NOTIFICATION_ID = 1
|
||||||
|
|
||||||
|
// Intent key for manual library update
|
||||||
|
val UPDATE_IS_MANUAL = "is_manual"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the start intent for [LibraryUpdateService].
|
||||||
|
* @param context the application context.
|
||||||
|
* @param isManual true when user triggers library update.
|
||||||
|
* @return the intent of the service.
|
||||||
|
*/
|
||||||
|
fun getIntent(context: Context, isManual: Boolean = false): Intent {
|
||||||
|
return Intent(context, LibraryUpdateService::class.java).apply {
|
||||||
|
putExtra(UPDATE_IS_MANUAL, isManual)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the status of the service.
|
||||||
|
* @param context the application context.
|
||||||
|
* @return true if the service is running, false otherwise.
|
||||||
|
*/
|
||||||
|
fun isRunning(context: Context): Boolean {
|
||||||
|
return AndroidComponentUtil.isServiceRunning(context, LibraryUpdateService::class.java)
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Static method to start the service. It will be started only if there isn't another
|
* Static method to start the service. It will be started only if there isn't another
|
||||||
* instance already running.
|
* instance already running.
|
||||||
|
@ -125,36 +123,52 @@ class LibraryUpdateService : Service() {
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Method called when the service receives an intent. In this case, the content of the intent
|
* Method called when the service receives an intent.
|
||||||
* is irrelevant, because everything required is fetched in [updateLibrary].
|
* @param intent the start intent from.
|
||||||
* @param intent the intent from [start].
|
|
||||||
* @param flags the flags of the command.
|
* @param flags the flags of the command.
|
||||||
* @param startId the start id of this command.
|
* @param startId the start id of this command.
|
||||||
* @return the start value of the command.
|
* @return the start value of the command.
|
||||||
*/
|
*/
|
||||||
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
|
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
|
||||||
// If there's no network available, set a component to start this service again when
|
|
||||||
// a connection is available.
|
// Get connectivity status
|
||||||
if (!DeviceUtil.isNetworkConnected(this)) {
|
val connection = ReactiveNetwork().getConnectivityStatus(this, true)
|
||||||
Timber.i("Sync canceled, connection not available")
|
|
||||||
showWarningNotification(getString(R.string.notification_no_connection_title),
|
// Get library update restrictions
|
||||||
getString(R.string.notification_no_connection_body))
|
val restrictions = preferences.libraryUpdateRestriction()
|
||||||
|
|
||||||
|
// Check if users updates library manual
|
||||||
|
val isManualUpdate = intent?.getBooleanExtra(UPDATE_IS_MANUAL, false) ?: false
|
||||||
|
|
||||||
|
// Whether to cancel the update.
|
||||||
|
var cancelUpdate = false
|
||||||
|
|
||||||
|
// Check if device has internet connection
|
||||||
|
// Check if device has wifi connection if only wifi is enabled
|
||||||
|
if (connection == ConnectivityStatus.OFFLINE || ("wifi" in restrictions
|
||||||
|
&& connection != ConnectivityStatus.WIFI_CONNECTED_HAS_INTERNET)) {
|
||||||
|
|
||||||
|
if (isManualUpdate) {
|
||||||
|
toast(R.string.notification_no_connection_title)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Enable library update when connection available
|
||||||
AndroidComponentUtil.toggleComponent(this, SyncOnConnectionAvailable::class.java, true)
|
AndroidComponentUtil.toggleComponent(this, SyncOnConnectionAvailable::class.java, true)
|
||||||
|
cancelUpdate = true
|
||||||
|
}
|
||||||
|
if (!isManualUpdate && "ac" in restrictions && !DeviceUtil.isPowerConnected(this)) {
|
||||||
|
AndroidComponentUtil.toggleComponent(this, SyncOnPowerConnected::class.java, true)
|
||||||
|
cancelUpdate = true
|
||||||
|
}
|
||||||
|
|
||||||
|
if (cancelUpdate) {
|
||||||
stopSelf(startId)
|
stopSelf(startId)
|
||||||
return Service.START_NOT_STICKY
|
return Service.START_NOT_STICKY
|
||||||
}
|
}
|
||||||
|
|
||||||
// If user doesn't want to update while phone is not charging, cancel sync
|
// Stop enabled components.
|
||||||
else if (preferences.updateOnlyWhenCharging() && !(intent?.getBooleanExtra(UPDATE_IS_FORCED, false) ?: false) && !DeviceUtil.isPowerConnected(this)) {
|
AndroidComponentUtil.toggleComponent(this, SyncOnConnectionAvailable::class.java, false)
|
||||||
Timber.i("Sync canceled, not connected to ac power")
|
AndroidComponentUtil.toggleComponent(this, SyncOnPowerConnected::class.java, false)
|
||||||
// Create force library update intent
|
|
||||||
val forceIntent = getLibraryUpdateReceiverIntent(LibraryUpdateReceiver.FORCE_LIBRARY_UPDATE)
|
|
||||||
// Show warning
|
|
||||||
showWarningNotification(getString(R.string.notification_not_connected_to_ac_title),
|
|
||||||
getString(R.string.notification_not_connected_to_ac_body), forceIntent)
|
|
||||||
stopSelf(startId)
|
|
||||||
return Service.START_NOT_STICKY
|
|
||||||
}
|
|
||||||
|
|
||||||
// Unsubscribe from any previous subscription if needed.
|
// Unsubscribe from any previous subscription if needed.
|
||||||
subscription?.unsubscribe()
|
subscription?.unsubscribe()
|
||||||
|
@ -168,20 +182,11 @@ class LibraryUpdateService : Service() {
|
||||||
stopSelf(startId)
|
stopSelf(startId)
|
||||||
}, {
|
}, {
|
||||||
stopSelf(startId)
|
stopSelf(startId)
|
||||||
})
|
})
|
||||||
|
|
||||||
return Service.START_STICKY
|
return Service.START_STICKY
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates a PendingIntent for LibraryUpdate broadcast class
|
|
||||||
* @param action id of action
|
|
||||||
*/
|
|
||||||
fun getLibraryUpdateReceiverIntent(action: String): PendingIntent {
|
|
||||||
return PendingIntent.getBroadcast(this, 0,
|
|
||||||
Intent(this, LibraryUpdateReceiver::class.java).apply { this.action = action }, 0)
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Method that updates the library. It's called in a background thread, so it's safe to do
|
* Method that updates the library. It's called in a background thread, so it's safe to do
|
||||||
* heavy operations or network calls here.
|
* heavy operations or network calls here.
|
||||||
|
@ -195,7 +200,8 @@ class LibraryUpdateService : Service() {
|
||||||
val newUpdates = ArrayList<Manga>()
|
val newUpdates = ArrayList<Manga>()
|
||||||
val failedUpdates = ArrayList<Manga>()
|
val failedUpdates = ArrayList<Manga>()
|
||||||
|
|
||||||
val cancelIntent = getLibraryUpdateReceiverIntent(LibraryUpdateReceiver.CANCEL_LIBRARY_UPDATE)
|
val cancelIntent = PendingIntent.getBroadcast(this, 0,
|
||||||
|
Intent(this, CancelUpdateReceiver::class.java), 0)
|
||||||
|
|
||||||
// Get the manga list that is going to be updated.
|
// Get the manga list that is going to be updated.
|
||||||
val allLibraryMangas = db.getFavoriteMangas().executeAsBlocking()
|
val allLibraryMangas = db.getFavoriteMangas().executeAsBlocking()
|
||||||
|
@ -299,12 +305,11 @@ class LibraryUpdateService : Service() {
|
||||||
* @param body the body of the notification.
|
* @param body the body of the notification.
|
||||||
*/
|
*/
|
||||||
private fun showNotification(title: String, body: String) {
|
private fun showNotification(title: String, body: String) {
|
||||||
val n = notification() {
|
notificationManager.notify(UPDATE_NOTIFICATION_ID, notification() {
|
||||||
setSmallIcon(R.drawable.ic_refresh_white_24dp_img)
|
setSmallIcon(R.drawable.ic_refresh_white_24dp_img)
|
||||||
setContentTitle(title)
|
setContentTitle(title)
|
||||||
setContentText(body)
|
setContentText(body)
|
||||||
}
|
})
|
||||||
notificationManager.notify(UPDATE_NOTIFICATION_ID, n)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -314,35 +319,15 @@ class LibraryUpdateService : Service() {
|
||||||
* @param total the total progress.
|
* @param total the total progress.
|
||||||
*/
|
*/
|
||||||
private fun showProgressNotification(manga: Manga, current: Int, total: Int, cancelIntent: PendingIntent) {
|
private fun showProgressNotification(manga: Manga, current: Int, total: Int, cancelIntent: PendingIntent) {
|
||||||
val n = notification() {
|
notificationManager.notify(UPDATE_NOTIFICATION_ID, notification() {
|
||||||
setSmallIcon(R.drawable.ic_refresh_white_24dp_img)
|
setSmallIcon(R.drawable.ic_refresh_white_24dp_img)
|
||||||
setContentTitle(manga.title)
|
setContentTitle(manga.title)
|
||||||
setProgress(total, current, false)
|
setProgress(total, current, false)
|
||||||
setOngoing(true)
|
setOngoing(true)
|
||||||
addAction(R.drawable.ic_clear_grey_24dp_img, getString(android.R.string.cancel), cancelIntent)
|
addAction(R.drawable.ic_clear_grey_24dp_img, getString(android.R.string.cancel), cancelIntent)
|
||||||
}
|
})
|
||||||
notificationManager.notify(UPDATE_NOTIFICATION_ID, n)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Show warning message when library can't be updated
|
|
||||||
* @param warningTitle title of warning
|
|
||||||
* @param warningBody warning information
|
|
||||||
* @param pendingIntent Intent called when action clicked
|
|
||||||
*/
|
|
||||||
private fun showWarningNotification(warningTitle: String, warningBody: String, pendingIntent: PendingIntent? = null) {
|
|
||||||
val n = notification() {
|
|
||||||
setSmallIcon(R.drawable.ic_warning_white_24dp_img)
|
|
||||||
setContentTitle(warningTitle)
|
|
||||||
setStyle(NotificationCompat.BigTextStyle().bigText(warningBody))
|
|
||||||
setContentIntent(notificationIntent)
|
|
||||||
if (pendingIntent != null) {
|
|
||||||
addAction(R.drawable.ic_refresh_grey_24dp_img, getString(R.string.action_force), pendingIntent)
|
|
||||||
}
|
|
||||||
setAutoCancel(true)
|
|
||||||
}
|
|
||||||
notificationManager.notify(UPDATE_NOTIFICATION_ID, n)
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Shows the notification containing the result of the update done by the service.
|
* Shows the notification containing the result of the update done by the service.
|
||||||
|
@ -353,14 +338,13 @@ class LibraryUpdateService : Service() {
|
||||||
val title = getString(R.string.notification_update_completed)
|
val title = getString(R.string.notification_update_completed)
|
||||||
val body = getUpdatedMangasBody(updates, failed)
|
val body = getUpdatedMangasBody(updates, failed)
|
||||||
|
|
||||||
val n = notification() {
|
notificationManager.notify(UPDATE_NOTIFICATION_ID, notification() {
|
||||||
setSmallIcon(R.drawable.ic_refresh_white_24dp_img)
|
setSmallIcon(R.drawable.ic_refresh_white_24dp_img)
|
||||||
setContentTitle(title)
|
setContentTitle(title)
|
||||||
setStyle(NotificationCompat.BigTextStyle().bigText(body))
|
setStyle(NotificationCompat.BigTextStyle().bigText(body))
|
||||||
setContentIntent(notificationIntent)
|
setContentIntent(notificationIntent)
|
||||||
setAutoCancel(true)
|
setAutoCancel(true)
|
||||||
}
|
})
|
||||||
notificationManager.notify(UPDATE_NOTIFICATION_ID, n)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -385,7 +369,6 @@ class LibraryUpdateService : Service() {
|
||||||
* network changes.
|
* network changes.
|
||||||
*/
|
*/
|
||||||
class SyncOnConnectionAvailable : BroadcastReceiver() {
|
class SyncOnConnectionAvailable : BroadcastReceiver() {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Method called when a network change occurs.
|
* Method called when a network change occurs.
|
||||||
* @param context the application context.
|
* @param context the application context.
|
||||||
|
@ -394,35 +377,38 @@ class LibraryUpdateService : Service() {
|
||||||
override fun onReceive(context: Context, intent: Intent) {
|
override fun onReceive(context: Context, intent: Intent) {
|
||||||
if (DeviceUtil.isNetworkConnected(context)) {
|
if (DeviceUtil.isNetworkConnected(context)) {
|
||||||
AndroidComponentUtil.toggleComponent(context, this.javaClass, false)
|
AndroidComponentUtil.toggleComponent(context, this.javaClass, false)
|
||||||
context.startService(getIntent(context))
|
start(context)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Class that triggers the library to update.
|
* Class that triggers the library to update when connected to power.
|
||||||
*/
|
*/
|
||||||
class LibraryUpdateReceiver : BroadcastReceiver() {
|
class SyncOnPowerConnected: BroadcastReceiver() {
|
||||||
companion object {
|
/**
|
||||||
// Cancel library update action
|
* Method called when AC is connected.
|
||||||
val CANCEL_LIBRARY_UPDATE = "eu.kanade.CANCEL_LIBRARY_UPDATE"
|
* @param context the application context.
|
||||||
// Force library update
|
* @param intent the intent received.
|
||||||
val FORCE_LIBRARY_UPDATE = "eu.kanade.FORCE_LIBRARY_UPDATE"
|
*/
|
||||||
|
override fun onReceive(context: Context, intent: Intent) {
|
||||||
|
AndroidComponentUtil.toggleComponent(context, this.javaClass, false)
|
||||||
|
start(context)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class that stops updating the library.
|
||||||
|
*/
|
||||||
|
class CancelUpdateReceiver : BroadcastReceiver() {
|
||||||
/**
|
/**
|
||||||
* Method called when user wants a library update.
|
* Method called when user wants a library update.
|
||||||
* @param context the application context.
|
* @param context the application context.
|
||||||
* @param intent the intent received.
|
* @param intent the intent received.
|
||||||
*/
|
*/
|
||||||
override fun onReceive(context: Context, intent: Intent) {
|
override fun onReceive(context: Context, intent: Intent) {
|
||||||
when (intent.action) {
|
LibraryUpdateService.stop(context)
|
||||||
CANCEL_LIBRARY_UPDATE -> {
|
context.notificationManager.cancel(UPDATE_NOTIFICATION_ID)
|
||||||
LibraryUpdateService.stop(context)
|
|
||||||
context.notificationManager.cancel(UPDATE_NOTIFICATION_ID)
|
|
||||||
}
|
|
||||||
FORCE_LIBRARY_UPDATE -> LibraryUpdateService.start(context, true)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -70,16 +70,17 @@ class PreferenceKeys(context: Context) {
|
||||||
|
|
||||||
val removeAfterMarkedAsRead = context.getString(R.string.pref_remove_after_marked_as_read_key)
|
val removeAfterMarkedAsRead = context.getString(R.string.pref_remove_after_marked_as_read_key)
|
||||||
|
|
||||||
val updateOnlyWhenCharging = context.getString(R.string.pref_update_only_when_charging_key)
|
|
||||||
|
|
||||||
val libraryUpdateInterval = context.getString(R.string.pref_library_update_interval_key)
|
val libraryUpdateInterval = context.getString(R.string.pref_library_update_interval_key)
|
||||||
|
|
||||||
|
val libraryUpdateRestriction = context.getString(R.string.pref_library_update_restriction_key)
|
||||||
|
|
||||||
val filterDownloaded = context.getString(R.string.pref_filter_downloaded_key)
|
val filterDownloaded = context.getString(R.string.pref_filter_downloaded_key)
|
||||||
|
|
||||||
val filterUnread = context.getString(R.string.pref_filter_unread_key)
|
val filterUnread = context.getString(R.string.pref_filter_unread_key)
|
||||||
|
|
||||||
fun sourceUsername(sourceId: Int) = "pref_source_username_$sourceId"
|
fun sourceUsername(sourceId: Int) = "pref_source_username_$sourceId"
|
||||||
|
|
||||||
|
|
||||||
fun sourcePassword(sourceId: Int) = "pref_source_password_$sourceId"
|
fun sourcePassword(sourceId: Int) = "pref_source_password_$sourceId"
|
||||||
|
|
||||||
fun syncUsername(syncId: Int) = "pref_mangasync_username_$syncId"
|
fun syncUsername(syncId: Int) = "pref_mangasync_username_$syncId"
|
||||||
|
|
|
@ -39,6 +39,11 @@ class PreferencesHelper(private val context: Context) {
|
||||||
context.getString(R.string.pref_library_update_interval_key), 0)
|
context.getString(R.string.pref_library_update_interval_key), 0)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun getAutomaticUpdateStatus(context: Context): Boolean {
|
||||||
|
return PreferenceManager.getDefaultSharedPreferences(context).getBoolean(
|
||||||
|
context.getString(R.string.pref_enable_automatic_updates), false)
|
||||||
|
}
|
||||||
|
|
||||||
@JvmStatic
|
@JvmStatic
|
||||||
fun getTheme(context: Context): Int {
|
fun getTheme(context: Context): Int {
|
||||||
return PreferenceManager.getDefaultSharedPreferences(context).getInt(
|
return PreferenceManager.getDefaultSharedPreferences(context).getInt(
|
||||||
|
@ -132,10 +137,10 @@ class PreferencesHelper(private val context: Context) {
|
||||||
|
|
||||||
fun removeAfterMarkedAsRead() = prefs.getBoolean(keys.removeAfterMarkedAsRead, false)
|
fun removeAfterMarkedAsRead() = prefs.getBoolean(keys.removeAfterMarkedAsRead, false)
|
||||||
|
|
||||||
fun updateOnlyWhenCharging() = prefs.getBoolean(keys.updateOnlyWhenCharging, false)
|
|
||||||
|
|
||||||
fun libraryUpdateInterval() = rxPrefs.getInteger(keys.libraryUpdateInterval, 0)
|
fun libraryUpdateInterval() = rxPrefs.getInteger(keys.libraryUpdateInterval, 0)
|
||||||
|
|
||||||
|
fun libraryUpdateRestriction() = prefs.getStringSet(keys.libraryUpdateRestriction, emptySet())
|
||||||
|
|
||||||
fun filterDownloaded() = rxPrefs.getBoolean(keys.filterDownloaded, false)
|
fun filterDownloaded() = rxPrefs.getBoolean(keys.filterDownloaded, false)
|
||||||
|
|
||||||
fun filterUnread() = rxPrefs.getBoolean(keys.filterUnread, false)
|
fun filterUnread() = rxPrefs.getBoolean(keys.filterUnread, false)
|
||||||
|
|
|
@ -1,99 +0,0 @@
|
||||||
package eu.kanade.tachiyomi.data.updater;
|
|
||||||
|
|
||||||
import android.content.Context;
|
|
||||||
import android.content.Intent;
|
|
||||||
import android.net.Uri;
|
|
||||||
import android.os.AsyncTask;
|
|
||||||
|
|
||||||
import java.io.File;
|
|
||||||
import java.io.FileOutputStream;
|
|
||||||
import java.io.InputStream;
|
|
||||||
import java.net.HttpURLConnection;
|
|
||||||
import java.net.URL;
|
|
||||||
|
|
||||||
import javax.inject.Inject;
|
|
||||||
|
|
||||||
import eu.kanade.tachiyomi.App;
|
|
||||||
import eu.kanade.tachiyomi.data.preference.PreferencesHelper;
|
|
||||||
|
|
||||||
public class UpdateDownloader extends AsyncTask<String, Void, Void> {
|
|
||||||
/**
|
|
||||||
* Name of cache directory.
|
|
||||||
*/
|
|
||||||
private static final String PARAMETER_CACHE_DIRECTORY = "apk_downloads";
|
|
||||||
/**
|
|
||||||
* Interface to global information about an application environment.
|
|
||||||
*/
|
|
||||||
private final Context context;
|
|
||||||
/**
|
|
||||||
* Cache directory used for cache management.
|
|
||||||
*/
|
|
||||||
private final File cacheDir;
|
|
||||||
@Inject PreferencesHelper preferencesHelper;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Constructor of UpdaterCache.
|
|
||||||
*
|
|
||||||
* @param context application environment interface.
|
|
||||||
*/
|
|
||||||
public UpdateDownloader(Context context) {
|
|
||||||
App.get(context).getComponent().inject(this);
|
|
||||||
this.context = context;
|
|
||||||
|
|
||||||
// Get cache directory from parameter.
|
|
||||||
cacheDir = new File(preferencesHelper.downloadsDirectory().get(), PARAMETER_CACHE_DIRECTORY);
|
|
||||||
|
|
||||||
// Create cache directory.
|
|
||||||
createCacheDir();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Create cache directory if it doesn't exist
|
|
||||||
*
|
|
||||||
* @return true if cache dir is created otherwise false.
|
|
||||||
*/
|
|
||||||
@SuppressWarnings("UnusedReturnValue")
|
|
||||||
private boolean createCacheDir() {
|
|
||||||
return !cacheDir.exists() && cacheDir.mkdirs();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected Void doInBackground(String... args) {
|
|
||||||
try {
|
|
||||||
createCacheDir();
|
|
||||||
|
|
||||||
URL url = new URL(args[0]);
|
|
||||||
HttpURLConnection c = (HttpURLConnection) url.openConnection();
|
|
||||||
c.connect();
|
|
||||||
|
|
||||||
File outputFile = new File(cacheDir, "update.apk");
|
|
||||||
if (outputFile.exists()) {
|
|
||||||
//noinspection ResultOfMethodCallIgnored
|
|
||||||
outputFile.delete();
|
|
||||||
}
|
|
||||||
FileOutputStream fos = new FileOutputStream(outputFile);
|
|
||||||
|
|
||||||
InputStream is = c.getInputStream();
|
|
||||||
|
|
||||||
byte[] buffer = new byte[1024];
|
|
||||||
int len1;
|
|
||||||
while ((len1 = is.read(buffer)) != -1) {
|
|
||||||
fos.write(buffer, 0, len1);
|
|
||||||
}
|
|
||||||
fos.close();
|
|
||||||
is.close();
|
|
||||||
|
|
||||||
// Prompt install interface
|
|
||||||
Intent intent = new Intent(Intent.ACTION_VIEW);
|
|
||||||
intent.setDataAndType(Uri.fromFile(outputFile), "application/vnd.android.package-archive");
|
|
||||||
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); // without this flag android returned a intent error!
|
|
||||||
context.startActivity(intent);
|
|
||||||
|
|
||||||
} catch (Exception e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
@ -0,0 +1,199 @@
|
||||||
|
package eu.kanade.tachiyomi.data.updater
|
||||||
|
|
||||||
|
import android.app.Notification
|
||||||
|
import android.app.PendingIntent
|
||||||
|
import android.content.BroadcastReceiver
|
||||||
|
import android.content.Context
|
||||||
|
import android.content.Intent
|
||||||
|
import android.net.Uri
|
||||||
|
import android.os.AsyncTask
|
||||||
|
import android.support.v4.app.NotificationCompat
|
||||||
|
import eu.kanade.tachiyomi.App
|
||||||
|
import eu.kanade.tachiyomi.R
|
||||||
|
import eu.kanade.tachiyomi.data.network.NetworkHelper
|
||||||
|
import eu.kanade.tachiyomi.data.network.ProgressListener
|
||||||
|
import eu.kanade.tachiyomi.data.network.get
|
||||||
|
import eu.kanade.tachiyomi.util.notificationManager
|
||||||
|
import eu.kanade.tachiyomi.util.saveTo
|
||||||
|
import timber.log.Timber
|
||||||
|
import java.io.File
|
||||||
|
import javax.inject.Inject
|
||||||
|
|
||||||
|
class UpdateDownloader(private val context: Context) :
|
||||||
|
AsyncTask<String, Int, UpdateDownloader.DownloadResult>() {
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
/**
|
||||||
|
* Prompt user with apk install intent
|
||||||
|
* @param context context
|
||||||
|
* @param file file of apk that is installed
|
||||||
|
*/
|
||||||
|
fun installAPK(context: Context, file: File) {
|
||||||
|
// Prompt install interface
|
||||||
|
val intent = Intent(Intent.ACTION_VIEW)
|
||||||
|
intent.setDataAndType(Uri.fromFile(file), "application/vnd.android.package-archive")
|
||||||
|
// Without this flag android returned a intent error!
|
||||||
|
intent.flags = Intent.FLAG_ACTIVITY_NEW_TASK
|
||||||
|
context.startActivity(intent)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Inject lateinit var network: NetworkHelper
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Default download dir
|
||||||
|
*/
|
||||||
|
val apkFile = File(context.externalCacheDir, "update.apk")
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Notification builder
|
||||||
|
*/
|
||||||
|
val notificationBuilder = NotificationCompat.Builder(context)
|
||||||
|
|
||||||
|
init {
|
||||||
|
App.get(context).component.inject(this)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class containing download result
|
||||||
|
* @param url url of file
|
||||||
|
* @param successful status of download
|
||||||
|
*/
|
||||||
|
class DownloadResult(var url: String, var successful: Boolean)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called before downloading
|
||||||
|
*/
|
||||||
|
override fun onPreExecute() {
|
||||||
|
// Create download notification
|
||||||
|
with(notificationBuilder) {
|
||||||
|
setContentTitle(context.getString(R.string.update_check_notification_file_download))
|
||||||
|
setContentText(context.getString(R.string.update_check_notification_download_in_progress))
|
||||||
|
setSmallIcon(android.R.drawable.stat_sys_download)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun doInBackground(vararg params: String?): DownloadResult {
|
||||||
|
// Initialize information array containing path and url to file.
|
||||||
|
val result = DownloadResult(params[0]!!, false)
|
||||||
|
|
||||||
|
// Progress of the download
|
||||||
|
var savedProgress = 0
|
||||||
|
|
||||||
|
val progressListener = object : ProgressListener {
|
||||||
|
override fun update(bytesRead: Long, contentLength: Long, done: Boolean) {
|
||||||
|
val progress = (100 * bytesRead / contentLength).toInt()
|
||||||
|
if (progress > savedProgress) {
|
||||||
|
savedProgress = progress
|
||||||
|
publishProgress(progress)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
// Make the request and download the file
|
||||||
|
val response = network.requestBodyProgressBlocking(get(result.url), progressListener)
|
||||||
|
|
||||||
|
if (response.isSuccessful) {
|
||||||
|
response.body().source().saveTo(apkFile)
|
||||||
|
// Set download successful
|
||||||
|
result.successful = true
|
||||||
|
}
|
||||||
|
} catch (e: Exception) {
|
||||||
|
Timber.e(e, e.message)
|
||||||
|
}
|
||||||
|
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called when progress is updated
|
||||||
|
* @param values values containing progress
|
||||||
|
*/
|
||||||
|
override fun onProgressUpdate(vararg values: Int?) {
|
||||||
|
// Notify notification manager to update notification
|
||||||
|
values.getOrNull(0)?.let {
|
||||||
|
notificationBuilder.setProgress(100, it, false)
|
||||||
|
// Displays the progress bar on notification
|
||||||
|
context.notificationManager.notify(InstallOnReceived.notificationId, notificationBuilder.build())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called when download done
|
||||||
|
* @param result string containing download information
|
||||||
|
*/
|
||||||
|
override fun onPostExecute(result: DownloadResult) {
|
||||||
|
with(notificationBuilder) {
|
||||||
|
if (result.successful) {
|
||||||
|
setContentTitle(context.getString(R.string.app_name))
|
||||||
|
setContentText(context.getString(R.string.update_check_notification_download_complete))
|
||||||
|
addAction(R.drawable.ic_system_update_grey_24dp_img, context.getString(R.string.action_install),
|
||||||
|
getInstallOnReceivedIntent(InstallOnReceived.INSTALL_APK, apkFile.absolutePath))
|
||||||
|
addAction(R.drawable.ic_clear_grey_24dp_img, context.getString(R.string.action_cancel),
|
||||||
|
getInstallOnReceivedIntent(InstallOnReceived.CANCEL_NOTIFICATION))
|
||||||
|
} else {
|
||||||
|
setContentText(context.getString(R.string.update_check_notification_download_error))
|
||||||
|
addAction(R.drawable.ic_refresh_grey_24dp_img, context.getString(R.string.action_retry),
|
||||||
|
getInstallOnReceivedIntent(InstallOnReceived.RETRY_DOWNLOAD, result.url))
|
||||||
|
addAction(R.drawable.ic_clear_grey_24dp_img, context.getString(R.string.action_cancel),
|
||||||
|
getInstallOnReceivedIntent(InstallOnReceived.CANCEL_NOTIFICATION))
|
||||||
|
}
|
||||||
|
setSmallIcon(android.R.drawable.stat_sys_download_done)
|
||||||
|
setProgress(0, 0, false)
|
||||||
|
}
|
||||||
|
val notification = notificationBuilder.build()
|
||||||
|
notification.flags = Notification.FLAG_NO_CLEAR
|
||||||
|
context.notificationManager.notify(InstallOnReceived.notificationId, notification)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns broadcast intent
|
||||||
|
* @param action action name of broadcast intent
|
||||||
|
* @param path path of file | url of file
|
||||||
|
* @return broadcast intent
|
||||||
|
*/
|
||||||
|
fun getInstallOnReceivedIntent(action: String, path: String = ""): PendingIntent {
|
||||||
|
val intent = Intent(context, InstallOnReceived::class.java).apply {
|
||||||
|
this.action = action
|
||||||
|
putExtra(InstallOnReceived.FILE_LOCATION, path)
|
||||||
|
}
|
||||||
|
return PendingIntent.getBroadcast(context, 0, intent, 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* BroadcastEvent used to install apk or retry download
|
||||||
|
*/
|
||||||
|
class InstallOnReceived : BroadcastReceiver() {
|
||||||
|
companion object {
|
||||||
|
// Install apk action
|
||||||
|
val INSTALL_APK = "eu.kanade.INSTALL_APK"
|
||||||
|
|
||||||
|
// Retry download action
|
||||||
|
val RETRY_DOWNLOAD = "eu.kanade.RETRY_DOWNLOAD"
|
||||||
|
|
||||||
|
// Retry download action
|
||||||
|
val CANCEL_NOTIFICATION = "eu.kanade.CANCEL_NOTIFICATION"
|
||||||
|
|
||||||
|
// Absolute path of file || URL of file
|
||||||
|
val FILE_LOCATION = "file_location"
|
||||||
|
|
||||||
|
// Id of the notification
|
||||||
|
val notificationId = 2
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onReceive(context: Context, intent: Intent) {
|
||||||
|
when (intent.action) {
|
||||||
|
// Install apk.
|
||||||
|
INSTALL_APK -> UpdateDownloader.installAPK(context, File(intent.getStringExtra(FILE_LOCATION)))
|
||||||
|
// Retry download.
|
||||||
|
RETRY_DOWNLOAD -> UpdateDownloader(context).execute(intent.getStringExtra(FILE_LOCATION))
|
||||||
|
|
||||||
|
CANCEL_NOTIFICATION -> context.notificationManager.cancel(notificationId)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,109 @@
|
||||||
|
package eu.kanade.tachiyomi.data.updater
|
||||||
|
|
||||||
|
import android.app.AlarmManager
|
||||||
|
import android.app.PendingIntent
|
||||||
|
import android.content.BroadcastReceiver
|
||||||
|
import android.content.Context
|
||||||
|
import android.content.Intent
|
||||||
|
import android.os.SystemClock
|
||||||
|
import eu.kanade.tachiyomi.BuildConfig
|
||||||
|
import eu.kanade.tachiyomi.R
|
||||||
|
import eu.kanade.tachiyomi.data.preference.PreferencesHelper
|
||||||
|
import eu.kanade.tachiyomi.util.DeviceUtil
|
||||||
|
import eu.kanade.tachiyomi.util.alarmManager
|
||||||
|
import eu.kanade.tachiyomi.util.notification
|
||||||
|
import eu.kanade.tachiyomi.util.notificationManager
|
||||||
|
import rx.android.schedulers.AndroidSchedulers
|
||||||
|
import rx.schedulers.Schedulers
|
||||||
|
|
||||||
|
class UpdateDownloaderAlarm : BroadcastReceiver() {
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
const val CHECK_UPDATE_ACTION = "eu.kanade.CHECK_UPDATE"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the alarm to run the intent that checks for update
|
||||||
|
* @param context the application context.
|
||||||
|
* @param intervalInHours the time in hours when it will be executed.
|
||||||
|
*/
|
||||||
|
@JvmStatic
|
||||||
|
@JvmOverloads
|
||||||
|
fun startAlarm(context: Context, intervalInHours: Int = 12, isEnabled: Boolean = PreferencesHelper.getAutomaticUpdateStatus(context)) {
|
||||||
|
// Stop previous running alarms if needed, and do not restart it if the interval is 0.
|
||||||
|
UpdateDownloaderAlarm.stopAlarm(context)
|
||||||
|
if (intervalInHours == 0 || !isEnabled)
|
||||||
|
return
|
||||||
|
|
||||||
|
// Get the time the alarm should fire the event to update.
|
||||||
|
val intervalInMillis = intervalInHours * 60 * 60 * 1000
|
||||||
|
val nextRun = SystemClock.elapsedRealtime() + intervalInMillis
|
||||||
|
|
||||||
|
// Start the alarm.
|
||||||
|
val pendingIntent = getPendingIntent(context)
|
||||||
|
context.alarmManager.setInexactRepeating(AlarmManager.ELAPSED_REALTIME_WAKEUP,
|
||||||
|
nextRun, intervalInMillis.toLong(), pendingIntent)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Stops the alarm if it's running.
|
||||||
|
* @param context the application context.
|
||||||
|
*/
|
||||||
|
fun stopAlarm(context: Context) {
|
||||||
|
val pendingIntent = getPendingIntent(context)
|
||||||
|
context.alarmManager.cancel(pendingIntent)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns broadcast intent
|
||||||
|
* @param context the application context.
|
||||||
|
* @return broadcast intent
|
||||||
|
*/
|
||||||
|
fun getPendingIntent(context: Context): PendingIntent {
|
||||||
|
return PendingIntent.getBroadcast(context, 0,
|
||||||
|
Intent(context, UpdateDownloaderAlarm::class.java).apply {
|
||||||
|
this.action = CHECK_UPDATE_ACTION
|
||||||
|
}, PendingIntent.FLAG_UPDATE_CURRENT)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
override fun onReceive(context: Context, intent: Intent) {
|
||||||
|
when (intent.action) {
|
||||||
|
// Start the alarm when the system is booted.
|
||||||
|
Intent.ACTION_BOOT_COMPLETED -> startAlarm(context)
|
||||||
|
// Update the library when the alarm fires an event.
|
||||||
|
CHECK_UPDATE_ACTION -> checkVersion(context)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun checkVersion(context: Context) {
|
||||||
|
if (DeviceUtil.isNetworkConnected(context)) {
|
||||||
|
val updateChecker = GithubUpdateChecker(context)
|
||||||
|
updateChecker.checkForApplicationUpdate()
|
||||||
|
.subscribeOn(Schedulers.io())
|
||||||
|
.observeOn(AndroidSchedulers.mainThread())
|
||||||
|
.subscribe({ release ->
|
||||||
|
//Get version of latest release
|
||||||
|
var newVersion = release.version
|
||||||
|
newVersion = newVersion.replace("[^\\d.]".toRegex(), "")
|
||||||
|
|
||||||
|
//Check if latest version is different from current version
|
||||||
|
if (newVersion != BuildConfig.VERSION_NAME) {
|
||||||
|
val downloadLink = release.downloadLink
|
||||||
|
|
||||||
|
val n = context.notification() {
|
||||||
|
setContentTitle(context.getString(R.string.update_check_notification_update_available))
|
||||||
|
addAction(android.R.drawable.stat_sys_download_done, context.getString(eu.kanade.tachiyomi.R.string.action_download),
|
||||||
|
UpdateDownloader(context).getInstallOnReceivedIntent(UpdateDownloader.InstallOnReceived.RETRY_DOWNLOAD, downloadLink))
|
||||||
|
setSmallIcon(android.R.drawable.stat_sys_download_done)
|
||||||
|
}
|
||||||
|
// Displays the progress bar on notification
|
||||||
|
context.notificationManager.notify(0, n);
|
||||||
|
}
|
||||||
|
}, {
|
||||||
|
it.printStackTrace()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -73,5 +73,4 @@ open class BaseActivity : AppCompatActivity() {
|
||||||
snack.f()
|
snack.f()
|
||||||
snack.show()
|
snack.show()
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
|
||||||
|
|
|
@ -1,12 +1,14 @@
|
||||||
package eu.kanade.tachiyomi.ui.setting
|
package eu.kanade.tachiyomi.ui.setting
|
||||||
|
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
|
import android.support.v7.preference.SwitchPreferenceCompat
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import com.afollestad.materialdialogs.MaterialDialog
|
import com.afollestad.materialdialogs.MaterialDialog
|
||||||
import eu.kanade.tachiyomi.BuildConfig
|
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.UpdateDownloader
|
import eu.kanade.tachiyomi.data.updater.UpdateDownloader
|
||||||
|
import eu.kanade.tachiyomi.data.updater.UpdateDownloaderAlarm
|
||||||
import eu.kanade.tachiyomi.util.toast
|
import eu.kanade.tachiyomi.util.toast
|
||||||
import rx.Subscription
|
import rx.Subscription
|
||||||
import rx.android.schedulers.AndroidSchedulers
|
import rx.android.schedulers.AndroidSchedulers
|
||||||
|
@ -27,6 +29,10 @@ class SettingsAboutFragment : SettingsNestedFragment() {
|
||||||
*/
|
*/
|
||||||
private var releaseSubscription: Subscription? = null
|
private var releaseSubscription: Subscription? = null
|
||||||
|
|
||||||
|
val automaticUpdateToggle by lazy {
|
||||||
|
findPreference(getString(R.string.pref_enable_automatic_updates_key)) as SwitchPreferenceCompat
|
||||||
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
|
|
||||||
fun newInstance(resourcePreference: Int, resourceTitle: Int): SettingsNestedFragment {
|
fun newInstance(resourcePreference: Int, resourceTitle: Int): SettingsNestedFragment {
|
||||||
|
@ -45,11 +51,25 @@ class SettingsAboutFragment : SettingsNestedFragment() {
|
||||||
else
|
else
|
||||||
BuildConfig.VERSION_NAME
|
BuildConfig.VERSION_NAME
|
||||||
|
|
||||||
//Set onClickListener to check for new version
|
if (!BuildConfig.DEBUG && BuildConfig.INCLUDE_UPDATER) {
|
||||||
version.setOnPreferenceClickListener {
|
//Set onClickListener to check for new version
|
||||||
if (!BuildConfig.DEBUG && BuildConfig.INCLUDE_UPDATER)
|
version.setOnPreferenceClickListener {
|
||||||
checkVersion()
|
true
|
||||||
true
|
}
|
||||||
|
|
||||||
|
automaticUpdateToggle.isEnabled = true
|
||||||
|
automaticUpdateToggle.setOnPreferenceChangeListener { preference, any ->
|
||||||
|
val status = any as Boolean
|
||||||
|
UpdateDownloaderAlarm.startAlarm(activity, 12, status)
|
||||||
|
true
|
||||||
|
}
|
||||||
|
|
||||||
|
automaticUpdateToggle.isEnabled = true
|
||||||
|
automaticUpdateToggle.setOnPreferenceChangeListener { preference, any ->
|
||||||
|
val status = any as Boolean
|
||||||
|
UpdateDownloaderAlarm.startAlarm(activity, 12, status)
|
||||||
|
true
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
buildTime.summary = getFormattedBuildTime()
|
buildTime.summary = getFormattedBuildTime()
|
||||||
|
|
|
@ -2,6 +2,7 @@ package eu.kanade.tachiyomi.ui.setting
|
||||||
|
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
|
import android.support.v14.preference.MultiSelectListPreference
|
||||||
import android.support.v4.app.TaskStackBuilder
|
import android.support.v4.app.TaskStackBuilder
|
||||||
import android.support.v7.preference.Preference
|
import android.support.v7.preference.Preference
|
||||||
import android.view.View
|
import android.view.View
|
||||||
|
@ -33,13 +34,28 @@ class SettingsGeneralFragment : SettingsNestedFragment() {
|
||||||
findPreference(getString(R.string.pref_library_update_interval_key)) as IntListPreference
|
findPreference(getString(R.string.pref_library_update_interval_key)) as IntListPreference
|
||||||
}
|
}
|
||||||
|
|
||||||
|
val updateRestriction by lazy {
|
||||||
|
findPreference(getString(R.string.pref_library_update_restriction_key)) as MultiSelectListPreference
|
||||||
|
}
|
||||||
|
|
||||||
val themePreference by lazy {
|
val themePreference by lazy {
|
||||||
findPreference(getString(R.string.pref_theme_key)) as IntListPreference
|
findPreference(getString(R.string.pref_theme_key)) as IntListPreference
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var updateIntervalSubscription: Subscription? = null
|
||||||
|
|
||||||
var columnsSubscription: Subscription? = null
|
var columnsSubscription: Subscription? = null
|
||||||
|
|
||||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||||
|
updateIntervalSubscription = preferences.libraryUpdateInterval().asObservable()
|
||||||
|
.subscribe { updateRestriction.isVisible = it > 0 }
|
||||||
|
|
||||||
|
columnsSubscription = Observable.combineLatest(
|
||||||
|
preferences.portraitColumns().asObservable(),
|
||||||
|
preferences.landscapeColumns().asObservable())
|
||||||
|
{ portraitColumns, landscapeColumns -> Pair(portraitColumns, landscapeColumns) }
|
||||||
|
.subscribe { updateColumnsSummary(it.first, it.second) }
|
||||||
|
|
||||||
updateInterval.setOnPreferenceChangeListener { preference, newValue ->
|
updateInterval.setOnPreferenceChangeListener { preference, newValue ->
|
||||||
LibraryUpdateAlarm.startAlarm(activity, (newValue as String).toInt())
|
LibraryUpdateAlarm.startAlarm(activity, (newValue as String).toInt())
|
||||||
true
|
true
|
||||||
|
@ -57,17 +73,11 @@ class SettingsGeneralFragment : SettingsNestedFragment() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onResume() {
|
override fun onDestroyView() {
|
||||||
super.onResume()
|
updateIntervalSubscription?.unsubscribe()
|
||||||
columnsSubscription = Observable.combineLatest(preferences.portraitColumns().asObservable(),
|
|
||||||
preferences.landscapeColumns().asObservable(),
|
|
||||||
{ portraitColumns, landscapeColumns -> Pair(portraitColumns, landscapeColumns) })
|
|
||||||
.subscribe { updateColumnsSummary(it.first, it.second) }
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onPause() {
|
|
||||||
columnsSubscription?.unsubscribe()
|
columnsSubscription?.unsubscribe()
|
||||||
super.onPause()
|
super.onDestroyView()
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onDisplayPreferenceDialog(preference: Preference) {
|
override fun onDisplayPreferenceDialog(preference: Preference) {
|
||||||
|
|
|
@ -11,7 +11,7 @@ object DeviceUtil {
|
||||||
val intent = context.registerReceiver(null, IntentFilter(Intent.ACTION_BATTERY_CHANGED))
|
val intent = context.registerReceiver(null, IntentFilter(Intent.ACTION_BATTERY_CHANGED))
|
||||||
intent?.let {
|
intent?.let {
|
||||||
val plugged = it.getIntExtra(BatteryManager.EXTRA_PLUGGED, -1)
|
val plugged = it.getIntExtra(BatteryManager.EXTRA_PLUGGED, -1)
|
||||||
return plugged == BatteryManager.BATTERY_PLUGGED_AC || plugged == BatteryManager.BATTERY_PLUGGED_USB
|
return plugged == BatteryManager.BATTERY_PLUGGED_AC || plugged == BatteryManager.BATTERY_PLUGGED_USB || plugged == BatteryManager.BATTERY_PLUGGED_WIRELESS
|
||||||
}
|
}
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
Binary file not shown.
After Width: | Height: | Size: 717 B |
Binary file not shown.
After Width: | Height: | Size: 487 B |
Binary file not shown.
After Width: | Height: | Size: 871 B |
Binary file not shown.
After Width: | Height: | Size: 1.1 KiB |
Binary file not shown.
After Width: | Height: | Size: 1.3 KiB |
|
@ -126,4 +126,14 @@
|
||||||
<item>48</item>
|
<item>48</item>
|
||||||
</string-array>
|
</string-array>
|
||||||
|
|
||||||
|
<string-array name="library_update_restrictions">
|
||||||
|
<item>@string/wifi</item>
|
||||||
|
<item>@string/charging</item>
|
||||||
|
</string-array>
|
||||||
|
|
||||||
|
<string-array name="library_update_restrictions_values">
|
||||||
|
<item>wifi</item>
|
||||||
|
<item>ac</item>
|
||||||
|
</string-array>
|
||||||
|
|
||||||
</resources>
|
</resources>
|
|
@ -13,10 +13,10 @@
|
||||||
<string name="pref_library_columns_landscape_key">pref_library_columns_landscape_key</string>
|
<string name="pref_library_columns_landscape_key">pref_library_columns_landscape_key</string>
|
||||||
<string name="pref_library_update_interval_key">pref_library_update_interval_key</string>
|
<string name="pref_library_update_interval_key">pref_library_update_interval_key</string>
|
||||||
<string name="pref_update_only_non_completed_key">pref_update_only_non_completed_key</string>
|
<string name="pref_update_only_non_completed_key">pref_update_only_non_completed_key</string>
|
||||||
<string name="pref_update_only_when_charging_key">pref_update_only_when_charging_key</string>
|
|
||||||
<string name="pref_auto_update_manga_sync_key">pref_auto_update_manga_sync_key</string>
|
<string name="pref_auto_update_manga_sync_key">pref_auto_update_manga_sync_key</string>
|
||||||
<string name="pref_ask_update_manga_sync_key">pref_ask_update_manga_sync_key</string>
|
<string name="pref_ask_update_manga_sync_key">pref_ask_update_manga_sync_key</string>
|
||||||
<string name="pref_theme_key">pref_theme_key</string>
|
<string name="pref_theme_key">pref_theme_key</string>
|
||||||
|
<string name="pref_library_update_restriction_key">library_update_restriction</string>
|
||||||
|
|
||||||
<string name="pref_default_viewer_key">pref_default_viewer_key</string>
|
<string name="pref_default_viewer_key">pref_default_viewer_key</string>
|
||||||
<string name="pref_image_scale_type_key">pref_image_scale_type_key</string>
|
<string name="pref_image_scale_type_key">pref_image_scale_type_key</string>
|
||||||
|
@ -52,8 +52,10 @@
|
||||||
<string name="pref_clear_chapter_cache_key">pref_clear_chapter_cache_key</string>
|
<string name="pref_clear_chapter_cache_key">pref_clear_chapter_cache_key</string>
|
||||||
<string name="pref_clear_database_key">pref_clear_database_key</string>
|
<string name="pref_clear_database_key">pref_clear_database_key</string>
|
||||||
|
|
||||||
|
|
||||||
<string name="pref_version">pref_version</string>
|
<string name="pref_version">pref_version</string>
|
||||||
<string name="pref_build_time">pref_build_time</string>
|
<string name="pref_build_time">pref_build_time</string>
|
||||||
|
<string name="pref_enable_automatic_updates_key">pref_enable_automatic_updates_key</string>
|
||||||
|
|
||||||
<string name="pref_display_catalogue_as_list">pref_display_catalogue_as_list</string>
|
<string name="pref_display_catalogue_as_list">pref_display_catalogue_as_list</string>
|
||||||
<string name="pref_last_catalogue_source_key">pref_last_catalogue_source_key</string>
|
<string name="pref_last_catalogue_source_key">pref_last_catalogue_source_key</string>
|
||||||
|
|
|
@ -51,6 +51,7 @@
|
||||||
<string name="action_cancel">Cancel</string>
|
<string name="action_cancel">Cancel</string>
|
||||||
<string name="action_sort">Sort</string>
|
<string name="action_sort">Sort</string>
|
||||||
<string name="action_force">Force refresh</string>
|
<string name="action_force">Force refresh</string>
|
||||||
|
<string name="action_install">Install</string>
|
||||||
|
|
||||||
<!-- Operations -->
|
<!-- Operations -->
|
||||||
<string name="deleting">Deleting…</string>
|
<string name="deleting">Deleting…</string>
|
||||||
|
@ -72,8 +73,6 @@
|
||||||
<string name="landscape">Landscape</string>
|
<string name="landscape">Landscape</string>
|
||||||
<string name="default_columns">Default</string>
|
<string name="default_columns">Default</string>
|
||||||
<string name="pref_library_update_interval">Library update frequency</string>
|
<string name="pref_library_update_interval">Library update frequency</string>
|
||||||
<string name="pref_update_only_non_completed">Only update incomplete manga</string>
|
|
||||||
<string name="pref_update_only_when_charging">Only update when charging</string>
|
|
||||||
<string name="update_never">Manual</string>
|
<string name="update_never">Manual</string>
|
||||||
<string name="update_1hour">Hourly</string>
|
<string name="update_1hour">Hourly</string>
|
||||||
<string name="update_2hour">Every 2 hours</string>
|
<string name="update_2hour">Every 2 hours</string>
|
||||||
|
@ -82,13 +81,17 @@
|
||||||
<string name="update_12hour">Every 12 hours</string>
|
<string name="update_12hour">Every 12 hours</string>
|
||||||
<string name="update_24hour">Daily</string>
|
<string name="update_24hour">Daily</string>
|
||||||
<string name="update_48hour">Every 2 days</string>
|
<string name="update_48hour">Every 2 days</string>
|
||||||
|
<string name="pref_library_update_restriction">Library update restrictions</string>
|
||||||
|
<string name="pref_library_update_restriction_summary">Update only when the conditions are met</string>
|
||||||
|
<string name="wifi">Wi-Fi</string>
|
||||||
|
<string name="charging">Charging</string>
|
||||||
|
<string name="pref_update_only_non_completed">Only update incomplete manga</string>
|
||||||
<string name="pref_auto_update_manga_sync">Sync chapters after reading</string>
|
<string name="pref_auto_update_manga_sync">Sync chapters after reading</string>
|
||||||
<string name="pref_ask_update_manga_sync">Confirm before updating</string>
|
<string name="pref_ask_update_manga_sync">Confirm before updating</string>
|
||||||
<string name="pref_theme">Application theme</string>
|
<string name="pref_theme">Application theme</string>
|
||||||
<string name="light_theme">Main theme</string>
|
<string name="light_theme">Main theme</string>
|
||||||
<string name="dark_theme">Dark theme</string>
|
<string name="dark_theme">Dark theme</string>
|
||||||
|
|
||||||
|
|
||||||
<!-- Reader section -->
|
<!-- Reader section -->
|
||||||
<string name="pref_hide_status_bar">Hide status bar</string>
|
<string name="pref_hide_status_bar">Hide status bar</string>
|
||||||
<string name="pref_lock_orientation">Lock orientation</string>
|
<string name="pref_lock_orientation">Lock orientation</string>
|
||||||
|
@ -166,7 +169,8 @@
|
||||||
<!-- About section -->
|
<!-- About section -->
|
||||||
<string name="version">Version</string>
|
<string name="version">Version</string>
|
||||||
<string name="build_time">Build time</string>
|
<string name="build_time">Build time</string>
|
||||||
|
<string name="pref_enable_automatic_updates">Check for updates</string>
|
||||||
|
<string name="pref_enable_automatic_updates_summary">Automatically check for application updates</string>
|
||||||
<!-- ACRA -->
|
<!-- ACRA -->
|
||||||
<string name="pref_enable_acra">Send crash reports</string>
|
<string name="pref_enable_acra">Send crash reports</string>
|
||||||
<string name="pref_acra_summary">Helps fix any bugs. No sensitive data will be sent</string>
|
<string name="pref_acra_summary">Helps fix any bugs. No sensitive data will be sent</string>
|
||||||
|
@ -282,6 +286,13 @@
|
||||||
<string name="update_check_download_started">Download started</string>
|
<string name="update_check_download_started">Download started</string>
|
||||||
<string name="update_check_look_for_updates">Looking for updates</string>
|
<string name="update_check_look_for_updates">Looking for updates</string>
|
||||||
|
|
||||||
|
<!--UpdateCheck Notifications-->
|
||||||
|
<string name="update_check_notification_file_download">Download update</string>
|
||||||
|
<string name="update_check_notification_download_in_progress">Download in progress</string>
|
||||||
|
<string name="update_check_notification_download_complete">Download complete</string>
|
||||||
|
<string name="update_check_notification_download_error">Download error</string>
|
||||||
|
<string name="update_check_notification_update_available">Update available</string>
|
||||||
|
|
||||||
<!--Content Description-->
|
<!--Content Description-->
|
||||||
<string name="description_backdrop">Backdrop image of selected manga</string>
|
<string name="description_backdrop">Backdrop image of selected manga</string>
|
||||||
<string name="description_cover">Cover of selected manga</string>
|
<string name="description_cover">Cover of selected manga</string>
|
||||||
|
|
|
@ -3,19 +3,26 @@
|
||||||
xmlns:android="http://schemas.android.com/apk/res/android">
|
xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
|
||||||
<SwitchPreferenceCompat
|
<SwitchPreferenceCompat
|
||||||
|
android:defaultValue="true"
|
||||||
android:key="acra.enable"
|
android:key="acra.enable"
|
||||||
android:title="@string/pref_enable_acra"
|
|
||||||
android:summary="@string/pref_acra_summary"
|
android:summary="@string/pref_acra_summary"
|
||||||
android:defaultValue="true"/>
|
android:title="@string/pref_enable_acra"/>
|
||||||
|
|
||||||
|
<SwitchPreferenceCompat
|
||||||
|
android:defaultValue="false"
|
||||||
|
android:enabled="false"
|
||||||
|
android:key="@string/pref_enable_automatic_updates_key"
|
||||||
|
android:summary="@string/pref_enable_automatic_updates_summary"
|
||||||
|
android:title="@string/pref_enable_automatic_updates"/>
|
||||||
|
|
||||||
<Preference
|
<Preference
|
||||||
android:key="@string/pref_version"
|
android:key="@string/pref_version"
|
||||||
android:title="@string/version"
|
android:persistent="false"
|
||||||
android:persistent="false" />
|
android:title="@string/version"/>
|
||||||
|
|
||||||
<Preference
|
<Preference
|
||||||
android:key="@string/pref_build_time"
|
android:key="@string/pref_build_time"
|
||||||
android:title="@string/build_time"
|
android:persistent="false"
|
||||||
android:persistent="false" />
|
android:title="@string/build_time"/>
|
||||||
|
|
||||||
</android.support.v7.preference.PreferenceScreen>
|
</android.support.v7.preference.PreferenceScreen>
|
|
@ -3,13 +3,13 @@
|
||||||
xmlns:android="http://schemas.android.com/apk/res/android">
|
xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
|
||||||
<Preference
|
<Preference
|
||||||
android:title="@string/pref_clear_chapter_cache"
|
android:key="@string/pref_clear_chapter_cache_key"
|
||||||
android:key="@string/pref_clear_chapter_cache_key" />
|
android:title="@string/pref_clear_chapter_cache"/>
|
||||||
|
|
||||||
<Preference
|
<Preference
|
||||||
android:title="@string/pref_clear_database"
|
|
||||||
android:key="@string/pref_clear_database_key"
|
android:key="@string/pref_clear_database_key"
|
||||||
android:summary="@string/pref_clear_database_summary"/>
|
android:summary="@string/pref_clear_database_summary"
|
||||||
|
android:title="@string/pref_clear_database"/>
|
||||||
|
|
||||||
<SwitchPreferenceCompat
|
<SwitchPreferenceCompat
|
||||||
android:defaultValue="false"
|
android:defaultValue="false"
|
||||||
|
|
|
@ -24,14 +24,16 @@
|
||||||
android:summary="%s"
|
android:summary="%s"
|
||||||
android:title="@string/pref_library_update_interval"/>
|
android:title="@string/pref_library_update_interval"/>
|
||||||
|
|
||||||
|
<MultiSelectListPreference
|
||||||
|
android:entries="@array/library_update_restrictions"
|
||||||
|
android:entryValues="@array/library_update_restrictions_values"
|
||||||
|
android:key="@string/pref_library_update_restriction_key"
|
||||||
|
android:summary="@string/pref_library_update_restriction_summary"
|
||||||
|
android:title="@string/pref_library_update_restriction" />
|
||||||
|
|
||||||
<SwitchPreferenceCompat
|
<SwitchPreferenceCompat
|
||||||
android:defaultValue="false"
|
android:defaultValue="false"
|
||||||
android:key="@string/pref_update_only_non_completed_key"
|
android:key="@string/pref_update_only_non_completed_key"
|
||||||
android:title="@string/pref_update_only_non_completed"/>
|
android:title="@string/pref_update_only_non_completed"/>
|
||||||
|
|
||||||
<SwitchPreferenceCompat
|
|
||||||
android:defaultValue="false"
|
|
||||||
android:key="@string/pref_update_only_when_charging_key"
|
|
||||||
android:title="@string/pref_update_only_when_charging"/>
|
|
||||||
|
|
||||||
</android.support.v7.preference.PreferenceScreen>
|
</android.support.v7.preference.PreferenceScreen>
|
|
@ -101,7 +101,7 @@ public class LibraryUpdateAlarmTest {
|
||||||
@Test
|
@Test
|
||||||
public void testLibraryUpdateServiceIsStartedWhenUpdateIntentIsReceived() {
|
public void testLibraryUpdateServiceIsStartedWhenUpdateIntentIsReceived() {
|
||||||
Intent intent = new Intent(context, LibraryUpdateService.class);
|
Intent intent = new Intent(context, LibraryUpdateService.class);
|
||||||
intent.putExtra("is_forced", false);
|
intent.putExtra("is_manual", false);
|
||||||
assertThat(app.getNextStartedService()).isNotEqualTo(intent);
|
assertThat(app.getNextStartedService()).isNotEqualTo(intent);
|
||||||
|
|
||||||
LibraryUpdateAlarm alarm = new LibraryUpdateAlarm();
|
LibraryUpdateAlarm alarm = new LibraryUpdateAlarm();
|
||||||
|
|
Loading…
Reference in a new issue