mirror of
				https://github.com/mihonapp/mihon.git
				synced 2025-10-30 22:07:57 +01: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:
		| @@ -1,6 +1,6 @@ | ||||
| <?xml version="1.0" encoding="utf-8"?> | ||||
| <manifest package="eu.kanade.tachiyomi" | ||||
|           xmlns:android="http://schemas.android.com/apk/res/android"> | ||||
| <manifest 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.ACCESS_NETWORK_STATE" /> | ||||
| @@ -68,7 +68,19 @@ | ||||
|         </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 | ||||
| @@ -79,6 +91,15 @@ | ||||
|             </intent-filter> | ||||
|         </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 | ||||
|             android:name="eu.kanade.tachiyomi.data.cache.CoverGlideModule" | ||||
|             android:value="GlideModule" /> | ||||
|   | ||||
| @@ -9,6 +9,8 @@ import android.os.IBinder | ||||
| import android.os.PowerManager | ||||
| import android.support.v4.app.NotificationCompat | ||||
| 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.R | ||||
| 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.source.SourceManager | ||||
| import eu.kanade.tachiyomi.ui.main.MainActivity | ||||
| import eu.kanade.tachiyomi.util.AndroidComponentUtil | ||||
| import eu.kanade.tachiyomi.util.DeviceUtil | ||||
| import eu.kanade.tachiyomi.util.notification | ||||
| import eu.kanade.tachiyomi.util.notificationManager | ||||
| import eu.kanade.tachiyomi.util.* | ||||
| import rx.Observable | ||||
| import rx.Subscription | ||||
| import rx.schedulers.Schedulers | ||||
| import timber.log.Timber | ||||
| import java.util.* | ||||
| import java.util.concurrent.atomic.AtomicInteger | ||||
| 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 | ||||
|  * started calling the [start] method. If it's already running, it won't do anything. | ||||
| @@ -77,6 +51,30 @@ class LibraryUpdateService : Service() { | ||||
|     companion object { | ||||
|         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 | ||||
|          * 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 | ||||
|      * is irrelevant, because everything required is fetched in [updateLibrary]. | ||||
|      * @param intent the intent from [start]. | ||||
|      * Method called when the service receives an intent. | ||||
|      * @param intent the start intent from. | ||||
|      * @param flags the flags of the command. | ||||
|      * @param startId the start id of this command. | ||||
|      * @return the start value of the command. | ||||
|      */ | ||||
|     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. | ||||
|         if (!DeviceUtil.isNetworkConnected(this)) { | ||||
|             Timber.i("Sync canceled, connection not available") | ||||
|             showWarningNotification(getString(R.string.notification_no_connection_title), | ||||
|                     getString(R.string.notification_no_connection_body)) | ||||
|  | ||||
|         // Get connectivity status | ||||
|         val connection = ReactiveNetwork().getConnectivityStatus(this, true) | ||||
|  | ||||
|         // Get library update restrictions | ||||
|         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) | ||||
|             cancelUpdate = true | ||||
|         } | ||||
|         if (!isManualUpdate && "ac" in restrictions && !DeviceUtil.isPowerConnected(this)) { | ||||
|             AndroidComponentUtil.toggleComponent(this, SyncOnPowerConnected::class.java, true) | ||||
|             cancelUpdate = true | ||||
|         } | ||||
|  | ||||
|         if (cancelUpdate) { | ||||
|             stopSelf(startId) | ||||
|             return Service.START_NOT_STICKY | ||||
|         } | ||||
|  | ||||
|         // If user doesn't want to update while phone is not charging, cancel sync | ||||
|         else if (preferences.updateOnlyWhenCharging() && !(intent?.getBooleanExtra(UPDATE_IS_FORCED, false) ?: false) && !DeviceUtil.isPowerConnected(this)) { | ||||
|             Timber.i("Sync canceled, not connected to ac power") | ||||
|             // 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 | ||||
|         } | ||||
|         // Stop enabled components. | ||||
|         AndroidComponentUtil.toggleComponent(this, SyncOnConnectionAvailable::class.java, false) | ||||
|         AndroidComponentUtil.toggleComponent(this, SyncOnPowerConnected::class.java, false) | ||||
|  | ||||
|         // Unsubscribe from any previous subscription if needed. | ||||
|         subscription?.unsubscribe() | ||||
| @@ -168,20 +182,11 @@ class LibraryUpdateService : Service() { | ||||
|                             stopSelf(startId) | ||||
|                         }, { | ||||
|                             stopSelf(startId) | ||||
|                 }) | ||||
|                         }) | ||||
|  | ||||
|         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 | ||||
|      * heavy operations or network calls here. | ||||
| @@ -195,7 +200,8 @@ class LibraryUpdateService : Service() { | ||||
|         val newUpdates = 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. | ||||
|         val allLibraryMangas = db.getFavoriteMangas().executeAsBlocking() | ||||
| @@ -299,12 +305,11 @@ class LibraryUpdateService : Service() { | ||||
|      * @param body the body of the notification. | ||||
|      */ | ||||
|     private fun showNotification(title: String, body: String) { | ||||
|         val n = notification() { | ||||
|         notificationManager.notify(UPDATE_NOTIFICATION_ID, notification() { | ||||
|             setSmallIcon(R.drawable.ic_refresh_white_24dp_img) | ||||
|             setContentTitle(title) | ||||
|             setContentText(body) | ||||
|         } | ||||
|         notificationManager.notify(UPDATE_NOTIFICATION_ID, n) | ||||
|         }) | ||||
|     } | ||||
|  | ||||
|     /** | ||||
| @@ -314,35 +319,15 @@ class LibraryUpdateService : Service() { | ||||
|      * @param total the total progress. | ||||
|      */ | ||||
|     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) | ||||
|             setContentTitle(manga.title) | ||||
|             setProgress(total, current, false) | ||||
|             setOngoing(true) | ||||
|             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. | ||||
| @@ -353,14 +338,13 @@ class LibraryUpdateService : Service() { | ||||
|         val title = getString(R.string.notification_update_completed) | ||||
|         val body = getUpdatedMangasBody(updates, failed) | ||||
|  | ||||
|         val n = notification() { | ||||
|         notificationManager.notify(UPDATE_NOTIFICATION_ID, notification() { | ||||
|             setSmallIcon(R.drawable.ic_refresh_white_24dp_img) | ||||
|             setContentTitle(title) | ||||
|             setStyle(NotificationCompat.BigTextStyle().bigText(body)) | ||||
|             setContentIntent(notificationIntent) | ||||
|             setAutoCancel(true) | ||||
|         } | ||||
|         notificationManager.notify(UPDATE_NOTIFICATION_ID, n) | ||||
|         }) | ||||
|     } | ||||
|  | ||||
|     /** | ||||
| @@ -385,7 +369,6 @@ class LibraryUpdateService : Service() { | ||||
|      * network changes. | ||||
|      */ | ||||
|     class SyncOnConnectionAvailable : BroadcastReceiver() { | ||||
|  | ||||
|         /** | ||||
|          * Method called when a network change occurs. | ||||
|          * @param context the application context. | ||||
| @@ -394,35 +377,38 @@ class LibraryUpdateService : Service() { | ||||
|         override fun onReceive(context: Context, intent: Intent) { | ||||
|             if (DeviceUtil.isNetworkConnected(context)) { | ||||
|                 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() { | ||||
|         companion object { | ||||
|             // Cancel library update action | ||||
|             val CANCEL_LIBRARY_UPDATE = "eu.kanade.CANCEL_LIBRARY_UPDATE" | ||||
|             // Force library update | ||||
|             val FORCE_LIBRARY_UPDATE = "eu.kanade.FORCE_LIBRARY_UPDATE" | ||||
|     class SyncOnPowerConnected: BroadcastReceiver() { | ||||
|         /** | ||||
|          * Method called when AC is connected. | ||||
|          * @param context the application context. | ||||
|          * @param intent the intent received. | ||||
|          */ | ||||
|         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. | ||||
|          * @param context the application context. | ||||
|          * @param intent the intent received. | ||||
|          */ | ||||
|         override fun onReceive(context: Context, intent: Intent) { | ||||
|             when (intent.action) { | ||||
|                 CANCEL_LIBRARY_UPDATE -> { | ||||
|                     LibraryUpdateService.stop(context) | ||||
|                     context.notificationManager.cancel(UPDATE_NOTIFICATION_ID) | ||||
|                 } | ||||
|                 FORCE_LIBRARY_UPDATE -> LibraryUpdateService.start(context, true) | ||||
|             } | ||||
|             LibraryUpdateService.stop(context) | ||||
|             context.notificationManager.cancel(UPDATE_NOTIFICATION_ID) | ||||
|         } | ||||
|     } | ||||
|  | ||||
|   | ||||
| @@ -70,16 +70,17 @@ class PreferenceKeys(context: Context) { | ||||
|  | ||||
|     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 libraryUpdateRestriction = context.getString(R.string.pref_library_update_restriction_key) | ||||
|  | ||||
|     val filterDownloaded = context.getString(R.string.pref_filter_downloaded_key) | ||||
|  | ||||
|     val filterUnread = context.getString(R.string.pref_filter_unread_key) | ||||
|  | ||||
|     fun sourceUsername(sourceId: Int) = "pref_source_username_$sourceId" | ||||
|  | ||||
|  | ||||
|     fun sourcePassword(sourceId: Int) = "pref_source_password_$sourceId" | ||||
|  | ||||
|     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) | ||||
|         } | ||||
|  | ||||
|         fun getAutomaticUpdateStatus(context: Context): Boolean { | ||||
|             return PreferenceManager.getDefaultSharedPreferences(context).getBoolean( | ||||
|                     context.getString(R.string.pref_enable_automatic_updates), false) | ||||
|         } | ||||
|  | ||||
|         @JvmStatic | ||||
|         fun getTheme(context: Context): Int { | ||||
|             return PreferenceManager.getDefaultSharedPreferences(context).getInt( | ||||
| @@ -132,10 +137,10 @@ class PreferencesHelper(private val context: Context) { | ||||
|  | ||||
|     fun removeAfterMarkedAsRead() = prefs.getBoolean(keys.removeAfterMarkedAsRead, false) | ||||
|  | ||||
|     fun updateOnlyWhenCharging() = prefs.getBoolean(keys.updateOnlyWhenCharging, false) | ||||
|  | ||||
|     fun libraryUpdateInterval() = rxPrefs.getInteger(keys.libraryUpdateInterval, 0) | ||||
|  | ||||
|     fun libraryUpdateRestriction() = prefs.getStringSet(keys.libraryUpdateRestriction, emptySet()) | ||||
|  | ||||
|     fun filterDownloaded() = rxPrefs.getBoolean(keys.filterDownloaded, 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.show() | ||||
|     } | ||||
|  | ||||
| } | ||||
| } | ||||
|   | ||||
| @@ -1,12 +1,14 @@ | ||||
| package eu.kanade.tachiyomi.ui.setting | ||||
|  | ||||
| import android.os.Bundle | ||||
| import android.support.v7.preference.SwitchPreferenceCompat | ||||
| import android.view.View | ||||
| import com.afollestad.materialdialogs.MaterialDialog | ||||
| import eu.kanade.tachiyomi.BuildConfig | ||||
| import eu.kanade.tachiyomi.R | ||||
| import eu.kanade.tachiyomi.data.updater.GithubUpdateChecker | ||||
| import eu.kanade.tachiyomi.data.updater.UpdateDownloader | ||||
| import eu.kanade.tachiyomi.data.updater.UpdateDownloaderAlarm | ||||
| import eu.kanade.tachiyomi.util.toast | ||||
| import rx.Subscription | ||||
| import rx.android.schedulers.AndroidSchedulers | ||||
| @@ -27,6 +29,10 @@ class SettingsAboutFragment : SettingsNestedFragment() { | ||||
|      */ | ||||
|     private var releaseSubscription: Subscription? = null | ||||
|  | ||||
|     val automaticUpdateToggle by lazy { | ||||
|         findPreference(getString(R.string.pref_enable_automatic_updates_key)) as SwitchPreferenceCompat | ||||
|     } | ||||
|  | ||||
|     companion object { | ||||
|  | ||||
|         fun newInstance(resourcePreference: Int, resourceTitle: Int): SettingsNestedFragment { | ||||
| @@ -45,11 +51,25 @@ class SettingsAboutFragment : SettingsNestedFragment() { | ||||
|         else | ||||
|             BuildConfig.VERSION_NAME | ||||
|  | ||||
|         //Set onClickListener to check for new version | ||||
|         version.setOnPreferenceClickListener { | ||||
|             if (!BuildConfig.DEBUG && BuildConfig.INCLUDE_UPDATER) | ||||
|                 checkVersion() | ||||
|             true | ||||
|         if (!BuildConfig.DEBUG && BuildConfig.INCLUDE_UPDATER) { | ||||
|             //Set onClickListener to check for new version | ||||
|             version.setOnPreferenceClickListener { | ||||
|                 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() | ||||
|   | ||||
| @@ -2,6 +2,7 @@ package eu.kanade.tachiyomi.ui.setting | ||||
|  | ||||
| import android.content.Intent | ||||
| import android.os.Bundle | ||||
| import android.support.v14.preference.MultiSelectListPreference | ||||
| import android.support.v4.app.TaskStackBuilder | ||||
| import android.support.v7.preference.Preference | ||||
| import android.view.View | ||||
| @@ -33,13 +34,28 @@ class SettingsGeneralFragment : SettingsNestedFragment() { | ||||
|         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 { | ||||
|         findPreference(getString(R.string.pref_theme_key)) as IntListPreference | ||||
|     } | ||||
|  | ||||
|     var updateIntervalSubscription: Subscription? = null | ||||
|  | ||||
|     var columnsSubscription: Subscription? = null | ||||
|  | ||||
|     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 -> | ||||
|             LibraryUpdateAlarm.startAlarm(activity, (newValue as String).toInt()) | ||||
|             true | ||||
| @@ -57,17 +73,11 @@ class SettingsGeneralFragment : SettingsNestedFragment() { | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     override fun onResume() { | ||||
|         super.onResume() | ||||
|         columnsSubscription = Observable.combineLatest(preferences.portraitColumns().asObservable(), | ||||
|                 preferences.landscapeColumns().asObservable(), | ||||
|                 { portraitColumns, landscapeColumns -> Pair(portraitColumns, landscapeColumns) }) | ||||
|                 .subscribe { updateColumnsSummary(it.first, it.second) } | ||||
|     } | ||||
|  | ||||
|     override fun onPause() { | ||||
|     override fun onDestroyView() { | ||||
|         updateIntervalSubscription?.unsubscribe() | ||||
|         columnsSubscription?.unsubscribe() | ||||
|         super.onPause() | ||||
|         super.onDestroyView() | ||||
|  | ||||
|     } | ||||
|  | ||||
|     override fun onDisplayPreferenceDialog(preference: Preference) { | ||||
|   | ||||
| @@ -11,7 +11,7 @@ object DeviceUtil { | ||||
|         val intent = context.registerReceiver(null, IntentFilter(Intent.ACTION_BATTERY_CHANGED)) | ||||
|         intent?.let { | ||||
|             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 | ||||
|     } | ||||
|   | ||||
										
											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> | ||||
|     </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> | ||||
| @@ -13,10 +13,10 @@ | ||||
|     <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_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_ask_update_manga_sync_key">pref_ask_update_manga_sync_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_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_database_key">pref_clear_database_key</string> | ||||
|  | ||||
|  | ||||
|     <string name="pref_version">pref_version</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_last_catalogue_source_key">pref_last_catalogue_source_key</string> | ||||
|   | ||||
| @@ -51,6 +51,7 @@ | ||||
|     <string name="action_cancel">Cancel</string> | ||||
|     <string name="action_sort">Sort</string> | ||||
|     <string name="action_force">Force refresh</string> | ||||
|     <string name="action_install">Install</string> | ||||
|  | ||||
|     <!-- Operations --> | ||||
|     <string name="deleting">Deleting…</string> | ||||
| @@ -72,8 +73,6 @@ | ||||
|     <string name="landscape">Landscape</string> | ||||
|     <string name="default_columns">Default</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_1hour">Hourly</string> | ||||
|     <string name="update_2hour">Every 2 hours</string> | ||||
| @@ -82,13 +81,17 @@ | ||||
|     <string name="update_12hour">Every 12 hours</string> | ||||
|     <string name="update_24hour">Daily</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_ask_update_manga_sync">Confirm before updating</string> | ||||
|     <string name="pref_theme">Application theme</string> | ||||
|     <string name="light_theme">Main theme</string> | ||||
|     <string name="dark_theme">Dark theme</string> | ||||
|  | ||||
|  | ||||
|       <!-- Reader section --> | ||||
|     <string name="pref_hide_status_bar">Hide status bar</string> | ||||
|     <string name="pref_lock_orientation">Lock orientation</string> | ||||
| @@ -166,7 +169,8 @@ | ||||
|       <!-- About section --> | ||||
|     <string name="version">Version</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 --> | ||||
|     <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> | ||||
| @@ -282,6 +286,13 @@ | ||||
|     <string name="update_check_download_started">Download started</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--> | ||||
|     <string name="description_backdrop">Backdrop image 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"> | ||||
|  | ||||
|     <SwitchPreferenceCompat | ||||
|         android:defaultValue="true" | ||||
|         android:key="acra.enable" | ||||
|         android:title="@string/pref_enable_acra" | ||||
|         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 | ||||
|         android:key="@string/pref_version" | ||||
|         android:title="@string/version" | ||||
|         android:persistent="false" /> | ||||
|         android:persistent="false" | ||||
|         android:title="@string/version"/> | ||||
|  | ||||
|     <Preference | ||||
|         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> | ||||
| @@ -3,13 +3,13 @@ | ||||
|     xmlns:android="http://schemas.android.com/apk/res/android"> | ||||
|  | ||||
|     <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 | ||||
|         android:title="@string/pref_clear_database" | ||||
|         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 | ||||
|         android:defaultValue="false" | ||||
|   | ||||
| @@ -24,14 +24,16 @@ | ||||
|         android:summary="%s" | ||||
|         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 | ||||
|         android:defaultValue="false" | ||||
|         android:key="@string/pref_update_only_non_completed_key" | ||||
|         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> | ||||
		Reference in New Issue
	
	Block a user