diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/cache/ChapterCache.kt b/app/src/main/java/eu/kanade/tachiyomi/data/cache/ChapterCache.kt index 8225472bd8..b56537ba6d 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/cache/ChapterCache.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/cache/ChapterCache.kt @@ -7,6 +7,7 @@ import com.google.gson.reflect.TypeToken import com.jakewharton.disklrucache.DiskLruCache import eu.kanade.tachiyomi.data.source.model.Page import eu.kanade.tachiyomi.util.DiskUtils +import eu.kanade.tachiyomi.util.saveTo import okhttp3.Response import okio.Okio import rx.Observable @@ -194,10 +195,7 @@ class ChapterCache(private val context: Context) { editor = diskCache.edit(key) ?: throw IOException("Unable to edit key") // Get OutputStream and write image with Okio. - Okio.buffer(Okio.sink(editor.newOutputStream(0))).use { - it.writeAll(response.body().source()) - it.flush() - } + response.body().source().saveTo(editor.newOutputStream(0)) diskCache.flush() editor.commit() diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/cache/CoverCache.kt b/app/src/main/java/eu/kanade/tachiyomi/data/cache/CoverCache.kt index 7ff72605b7..3fbce3d06d 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/cache/CoverCache.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/cache/CoverCache.kt @@ -33,7 +33,7 @@ class CoverCache(private val context: Context) { * @param headers headers included in Glide request. * @param onReady function to call when the image is ready */ - fun save(thumbnailUrl: String?, headers: LazyHeaders, onReady: ((File) -> Unit)? = null) { + fun save(thumbnailUrl: String?, headers: LazyHeaders?, onReady: ((File) -> Unit)? = null) { // Check if url is empty. if (thumbnailUrl.isNullOrEmpty()) return @@ -62,7 +62,7 @@ class CoverCache(private val context: Context) { * @param headers headers included in Glide request. * @param onReady function to call when the image is ready */ - fun saveOrLoadFromCache(thumbnailUrl: String?, headers: LazyHeaders, onReady: ((File) -> Unit)?) { + fun saveOrLoadFromCache(thumbnailUrl: String?, headers: LazyHeaders?, onReady: ((File) -> Unit)?) { // Check if url is empty. if (thumbnailUrl.isNullOrEmpty()) return diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/database/models/Manga.java b/app/src/main/java/eu/kanade/tachiyomi/data/database/models/Manga.java index ad4e1eed4a..97c42c5528 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/database/models/Manga.java +++ b/app/src/main/java/eu/kanade/tachiyomi/data/database/models/Manga.java @@ -95,6 +95,13 @@ public class Manga implements Serializable { return m; } + public static Manga create(String pathUrl, int source) { + Manga m = new Manga(); + m.url = pathUrl; + m.source = source; + return m; + } + public void setUrl(String url) { this.url = UrlUtil.getPath(url); } diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/download/DownloadManager.kt b/app/src/main/java/eu/kanade/tachiyomi/data/download/DownloadManager.kt index f78738a3ae..ea0677a9ab 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/download/DownloadManager.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/download/DownloadManager.kt @@ -15,10 +15,7 @@ import eu.kanade.tachiyomi.data.source.SourceManager import eu.kanade.tachiyomi.data.source.base.Source import eu.kanade.tachiyomi.data.source.model.Page import eu.kanade.tachiyomi.event.DownloadChaptersEvent -import eu.kanade.tachiyomi.util.DiskUtils -import eu.kanade.tachiyomi.util.DynamicConcurrentMergeOperator -import eu.kanade.tachiyomi.util.UrlUtil -import eu.kanade.tachiyomi.util.toast +import eu.kanade.tachiyomi.util.* import rx.Observable import rx.Subscription import rx.android.schedulers.AndroidSchedulers @@ -27,9 +24,7 @@ import rx.subjects.BehaviorSubject import rx.subjects.PublishSubject import timber.log.Timber import java.io.File -import java.io.FileOutputStream import java.io.FileReader -import java.io.IOException import java.util.* class DownloadManager(private val context: Context, private val sourceManager: SourceManager, private val preferences: PreferencesHelper) { @@ -176,7 +171,7 @@ class DownloadManager(private val context: Context, private val sourceManager: S // Or if the page list already exists, start from the file Observable.just(download.pages) - return Observable.defer { pageListObservable + return Observable.defer { pageListObservable .doOnNext { pages -> download.downloadedImages = 0 download.status = Download.DOWNLOADING @@ -232,14 +227,11 @@ class DownloadManager(private val context: Context, private val sourceManager: S private fun downloadImage(page: Page, source: Source, directory: File, filename: String): Observable { page.status = Page.DOWNLOAD_IMAGE return source.getImageProgressResponse(page) - .flatMap({ resp -> - try { - DiskUtils.saveBufferedSourceToDirectory(resp.body().source(), directory, filename) - } finally { - resp.body().close() - } + .flatMap { + it.body().source().saveTo(File(directory, filename)) Observable.just(page) - }).retry(2) + } + .retry(2) } // Public method to get the image from the filesystem. It does NOT provide any way to download the image @@ -311,26 +303,14 @@ class DownloadManager(private val context: Context, private val sourceManager: S val chapterDir = getAbsoluteChapterDirectory(source, manga, chapter) val pagesFile = File(chapterDir, PAGE_LIST_FILE) - var reader: JsonReader? = null - try { - if (pagesFile.exists()) { - reader = JsonReader(FileReader(pagesFile.absolutePath)) - val collectionType = object : TypeToken>() { - - }.type - return gson.fromJson>(reader, collectionType) + return try { + JsonReader(FileReader(pagesFile)).use { + val collectionType = object : TypeToken>() {}.type + gson.fromJson(it, collectionType) } } catch (e: Exception) { - Timber.e(e.cause, e.message) - } finally { - if (reader != null) try { - reader.close() - } catch (e: IOException) { - /* Do nothing */ - } - + null } - return null } // Shortcut for the method above @@ -343,20 +323,13 @@ class DownloadManager(private val context: Context, private val sourceManager: S val chapterDir = getAbsoluteChapterDirectory(source, manga, chapter) val pagesFile = File(chapterDir, PAGE_LIST_FILE) - var out: FileOutputStream? = null - try { - out = FileOutputStream(pagesFile) - out.write(gson.toJson(pages).toByteArray()) - out.flush() - } catch (e: IOException) { - Timber.e(e.cause, e.message) - } finally { - if (out != null) try { - out.close() - } catch (e: IOException) { - /* Do nothing */ + pagesFile.outputStream().use { + try { + it.write(gson.toJson(pages).toByteArray()) + it.flush() + } catch (e: Exception) { + Timber.e(e, e.message) } - } } diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/network/NetworkHelper.kt b/app/src/main/java/eu/kanade/tachiyomi/data/network/NetworkHelper.kt index e65d8ee23e..e37905e1d9 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/network/NetworkHelper.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/network/NetworkHelper.kt @@ -10,10 +10,13 @@ import java.net.CookieStore class NetworkHelper(context: Context) { - private val client: OkHttpClient - private val forceCacheClient: OkHttpClient + private val cacheDir = File(context.cacheDir, "network_cache") - private val cookieManager: CookieManager + private val cacheSize = 5L * 1024 * 1024 // 5 MiB + + private val cookieManager = CookieManager().apply { + setCookiePolicy(CookiePolicy.ACCEPT_ALL) + } private val forceCacheInterceptor = { chain: Interceptor.Chain -> val originalResponse = chain.proceed(chain.request()) @@ -23,24 +26,17 @@ class NetworkHelper(context: Context) { .build() } - private val cacheSize = 5L * 1024 * 1024 // 5 MiB - private val cacheDir = "network_cache" + private val client = OkHttpClient.Builder() + .cookieJar(JavaNetCookieJar(cookieManager)) + .cache(Cache(cacheDir, cacheSize)) + .build() - init { - val cacheDir = File(context.cacheDir, cacheDir) + private val forceCacheClient = client.newBuilder() + .addNetworkInterceptor(forceCacheInterceptor) + .build() - cookieManager = CookieManager() - cookieManager.setCookiePolicy(CookiePolicy.ACCEPT_ALL) - - client = OkHttpClient.Builder() - .cookieJar(JavaNetCookieJar(cookieManager)) - .cache(Cache(cacheDir, cacheSize)) - .build() - - forceCacheClient = client.newBuilder() - .addNetworkInterceptor(forceCacheInterceptor) - .build() - } + val cookies: CookieStore + get() = cookieManager.cookieStore @JvmOverloads fun request(request: Request, forceCache: Boolean = false): Observable { @@ -59,22 +55,22 @@ class NetworkHelper(context: Context) { } fun requestBodyProgress(request: Request, listener: ProgressListener): Observable { - return Observable.fromCallable { - val progressClient = client.newBuilder() - .cache(null) - .addNetworkInterceptor { chain -> - val originalResponse = chain.proceed(chain.request()) - originalResponse.newBuilder() - .body(ProgressResponseBody(originalResponse.body(), listener)) - .build() - } - .build() - - progressClient.newCall(request).execute() - } + return Observable.fromCallable { requestBodyProgressBlocking(request, listener) } + } + + fun requestBodyProgressBlocking(request: Request, listener: ProgressListener): Response { + val progressClient = client.newBuilder() + .cache(null) + .addNetworkInterceptor { chain -> + val originalResponse = chain.proceed(chain.request()) + originalResponse.newBuilder() + .body(ProgressResponseBody(originalResponse.body(), listener)) + .build() + } + .build() + + return progressClient.newCall(request).execute() } - val cookies: CookieStore - get() = cookieManager.cookieStore } diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/source/Language.kt b/app/src/main/java/eu/kanade/tachiyomi/data/source/Language.kt index 4c5b862e73..77774e5e90 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/source/Language.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/source/Language.kt @@ -1,8 +1,8 @@ package eu.kanade.tachiyomi.data.source -class Language(val lang: String, val code: String) +class Language(val code: String, val lang: String) -val EN = Language("English", "EN") -val RU = Language("Russian", "RU") +val EN = Language("EN", "English") +val RU = Language("RU", "Russian") -fun getLanguages(): List = listOf(EN, RU) \ No newline at end of file +fun getLanguages() = listOf(EN, RU) \ No newline at end of file diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/source/model/Page.java b/app/src/main/java/eu/kanade/tachiyomi/data/source/model/Page.java index c3ec18d70e..ecd1bbe448 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/source/model/Page.java +++ b/app/src/main/java/eu/kanade/tachiyomi/data/source/model/Page.java @@ -1,6 +1,5 @@ package eu.kanade.tachiyomi.data.source.model; -import java.io.Serializable; import java.util.List; import eu.kanade.tachiyomi.data.database.models.Chapter; @@ -25,6 +24,14 @@ public class Page implements ProgressListener { public static final int READY = 3; public static final int ERROR = 4; + public Page(int pageNumber, String url) { + this(pageNumber, url, null, null); + } + + public Page(int pageNumber, String url, String imageUrl) { + this(pageNumber, url, imageUrl, null); + } + public Page(int pageNumber, String url, String imageUrl, String imagePath) { this.pageNumber = pageNumber; this.url = url; @@ -32,10 +39,6 @@ public class Page implements ProgressListener { this.imagePath = imagePath; } - public Page(int pageNumber, String url) { - this(pageNumber, url, null, null); - } - public int getPageNumber() { return pageNumber; } diff --git a/app/src/main/java/eu/kanade/tachiyomi/util/DiskUtils.java b/app/src/main/java/eu/kanade/tachiyomi/util/DiskUtils.java index 3f7f3a804e..53011786b2 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/util/DiskUtils.java +++ b/app/src/main/java/eu/kanade/tachiyomi/util/DiskUtils.java @@ -5,11 +5,6 @@ import java.io.IOException; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; -import okhttp3.internal.Util; -import okio.BufferedSink; -import okio.BufferedSource; -import okio.Okio; - public final class DiskUtils { private DiskUtils() { @@ -39,34 +34,7 @@ public final class DiskUtils { } return sb.toString(); } - - public static File saveBufferedSourceToDirectory(BufferedSource bufferedSource, File directory, String name) throws IOException { - createDirectory(directory); - - File writeFile = new File(directory, name); - if (writeFile.exists()) { - if (writeFile.delete()) { - writeFile = new File(directory, name); - } else { - throw new IOException("Failed Deleting Existing File for Overwrite"); - } - } - - BufferedSink bufferedSink = null; - try { - bufferedSink = Okio.buffer(Okio.sink(writeFile)); - bufferedSink.writeAll(bufferedSource); - Util.closeQuietly(bufferedSink); - } catch (Exception e) { - Util.closeQuietly(bufferedSink); - //noinspection ResultOfMethodCallIgnored - writeFile.delete(); - throw new IOException("Unable to save image"); - } - - return writeFile; - } - + public static void deleteFiles(File inputFile) { if (inputFile.isDirectory()) { for (File childFile : inputFile.listFiles()) { diff --git a/app/src/main/java/eu/kanade/tachiyomi/util/OkioExtensions.kt b/app/src/main/java/eu/kanade/tachiyomi/util/OkioExtensions.kt new file mode 100644 index 0000000000..728c6a3892 --- /dev/null +++ b/app/src/main/java/eu/kanade/tachiyomi/util/OkioExtensions.kt @@ -0,0 +1,39 @@ +package eu.kanade.tachiyomi.util + +import okio.BufferedSource +import okio.Okio +import java.io.File +import java.io.OutputStream + +/** + * Saves the given source to a file and closes it. Directories will be created if needed. + * + * @param file the file where the source is copied. + */ +fun BufferedSource.saveTo(file: File) { + try { + // Create parent dirs if needed + file.parentFile.mkdirs() + + // Copy to destination + saveTo(file.outputStream()) + } catch (e: Exception) { + close() + file.delete() + throw e + } +} + +/** + * Saves the given source to an output stream and closes both resources. + * + * @param stream the stream where the source is copied. + */ +fun BufferedSource.saveTo(stream: OutputStream) { + use { input -> + Okio.buffer(Okio.sink(stream)).use { + it.writeAll(input) + it.flush() + } + } +} \ No newline at end of file diff --git a/app/src/main/java/eu/kanade/tachiyomi/widget/preference/LoginDialogPreference.kt b/app/src/main/java/eu/kanade/tachiyomi/widget/preference/LoginDialogPreference.kt index 1d850a61ca..e2d254b486 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/widget/preference/LoginDialogPreference.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/widget/preference/LoginDialogPreference.kt @@ -11,8 +11,8 @@ import com.afollestad.materialdialogs.MaterialDialog import com.dd.processbutton.iml.ActionProcessButton import eu.kanade.tachiyomi.R import eu.kanade.tachiyomi.data.preference.PreferencesHelper -import eu.kanade.tachiyomi.ui.setting.SettingsActivity import eu.kanade.tachiyomi.ui.base.listener.SimpleTextWatcher +import eu.kanade.tachiyomi.ui.setting.SettingsActivity import kotlinx.android.synthetic.main.pref_account_login.view.* import rx.Subscription @@ -30,7 +30,7 @@ abstract class LoginDialogPreference : DialogFragment() { val dialog = MaterialDialog.Builder(activity) .customView(R.layout.pref_account_login, false) .negativeText(android.R.string.cancel) - .build(); + .build() onViewCreated(dialog.view, savedState)