Some minor stuff from upstream (#547)

* local manga changes from stable

* use device pin instead of biometric when biometric cant be used for some reason

* corektx changes

Co-authored-by: arkon <arkon@users.noreply.github.com>
This commit is contained in:
Carlos 2020-08-09 14:52:45 -04:00 committed by GitHub
parent 45eab517d0
commit c0228f06f8
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
20 changed files with 85 additions and 65 deletions

View File

@ -90,7 +90,7 @@ dependencies {
implementation("com.github.inorichi:subsampling-scale-image-view:ac0dae7") implementation("com.github.inorichi:subsampling-scale-image-view:ac0dae7")
implementation("com.github.inorichi:junrar-android:634c1f5") implementation("com.github.inorichi:junrar-android:634c1f5")
// Android support library // Android X libraries
implementation("androidx.appcompat:appcompat:1.1.0") implementation("androidx.appcompat:appcompat:1.1.0")
implementation("androidx.cardview:cardview:1.0.0") implementation("androidx.cardview:cardview:1.0.0")
implementation("com.google.android.material:material:1.1.0") implementation("com.google.android.material:material:1.1.0")
@ -100,12 +100,13 @@ dependencies {
implementation("androidx.browser:browser:1.2.0") implementation("androidx.browser:browser:1.2.0")
implementation("androidx.biometric:biometric:1.0.1") implementation("androidx.biometric:biometric:1.0.1")
implementation("androidx.palette:palette:1.0.0") implementation("androidx.palette:palette:1.0.0")
implementation ("androidx.core:core-ktx:$1.3.1")
implementation("androidx.constraintlayout:constraintlayout:1.1.3") implementation("androidx.constraintlayout:constraintlayout:1.1.3")
implementation("androidx.multidex:multidex:2.0.1") implementation("androidx.multidex:multidex:2.0.1")
implementation("com.google.firebase:firebase-core:17.3.0") implementation("com.google.firebase:firebase-core:17.4.4")
val lifecycleVersion = "2.2.0" val lifecycleVersion = "2.2.0"
implementation("androidx.lifecycle:lifecycle-extensions:$lifecycleVersion") implementation("androidx.lifecycle:lifecycle-extensions:$lifecycleVersion")

View File

@ -7,6 +7,7 @@ import android.net.Uri
import android.os.Build import android.os.Build
import android.os.IBinder import android.os.IBinder
import android.os.PowerManager import android.os.PowerManager
import androidx.core.net.toUri
import com.hippo.unifile.UniFile import com.hippo.unifile.UniFile
import eu.kanade.tachiyomi.data.notification.Notifications import eu.kanade.tachiyomi.data.notification.Notifications
import eu.kanade.tachiyomi.util.system.isServiceRunning import eu.kanade.tachiyomi.util.system.isServiceRunning
@ -108,7 +109,7 @@ class BackupCreateService : Service() {
val backupFlags = intent.getIntExtra(BackupConst.EXTRA_FLAGS, 0) val backupFlags = intent.getIntExtra(BackupConst.EXTRA_FLAGS, 0)
backupManager = BackupManager(this) backupManager = BackupManager(this)
val backupFileUri = Uri.parse(backupManager.createBackup(uri, backupFlags, false)) val backupFileUri = backupManager.createBackup(uri, backupFlags, false)?.toUri()
val unifile = UniFile.fromUri(this, backupFileUri) val unifile = UniFile.fromUri(this, backupFileUri)
notifier.showBackupComplete(unifile) notifier.showBackupComplete(unifile)
} catch (e: Exception) { } catch (e: Exception) {

View File

@ -2,6 +2,7 @@ package eu.kanade.tachiyomi.data.backup
import android.content.Context import android.content.Context
import android.net.Uri import android.net.Uri
import androidx.core.net.toUri
import androidx.work.ExistingPeriodicWorkPolicy import androidx.work.ExistingPeriodicWorkPolicy
import androidx.work.PeriodicWorkRequestBuilder import androidx.work.PeriodicWorkRequestBuilder
import androidx.work.WorkManager import androidx.work.WorkManager
@ -19,7 +20,7 @@ class BackupCreatorJob(private val context: Context, workerParams: WorkerParamet
override fun doWork(): Result { override fun doWork(): Result {
val preferences = Injekt.get<PreferencesHelper>() val preferences = Injekt.get<PreferencesHelper>()
val backupManager = BackupManager(context) val backupManager = BackupManager(context)
val uri = Uri.parse(preferences.backupsDirectory().getOrDefault()) val uri = preferences.backupsDirectory().getOrDefault().toUri()
val flags = BackupCreateService.BACKUP_ALL val flags = BackupCreateService.BACKUP_ALL
return try { return try {
backupManager.createBackup(uri, flags, true) backupManager.createBackup(uri, flags, true)

View File

@ -1,7 +1,7 @@
package eu.kanade.tachiyomi.data.download package eu.kanade.tachiyomi.data.download
import android.content.Context import android.content.Context
import android.net.Uri import androidx.core.net.toUri
import com.hippo.unifile.UniFile import com.hippo.unifile.UniFile
import eu.kanade.tachiyomi.data.database.DatabaseHelper import eu.kanade.tachiyomi.data.database.DatabaseHelper
import eu.kanade.tachiyomi.data.database.models.Chapter import eu.kanade.tachiyomi.data.database.models.Chapter
@ -57,7 +57,7 @@ class DownloadCache(
*/ */
private fun getDirectoryFromPreference(): UniFile { private fun getDirectoryFromPreference(): UniFile {
val dir = preferences.downloadsDirectory().getOrDefault() val dir = preferences.downloadsDirectory().getOrDefault()
return UniFile.fromUri(context, Uri.parse(dir)) return UniFile.fromUri(context, dir.toUri())
} }
/** /**

View File

@ -2,6 +2,7 @@ package eu.kanade.tachiyomi.data.download
import android.content.Context import android.content.Context
import android.net.Uri import android.net.Uri
import androidx.core.net.toUri
import com.hippo.unifile.UniFile import com.hippo.unifile.UniFile
import eu.kanade.tachiyomi.R import eu.kanade.tachiyomi.R
import eu.kanade.tachiyomi.data.database.DatabaseHelper import eu.kanade.tachiyomi.data.database.DatabaseHelper
@ -31,14 +32,14 @@ class DownloadProvider(private val context: Context) {
* The root directory for downloads. * The root directory for downloads.
*/ */
private var downloadsDir = preferences.downloadsDirectory().getOrDefault().let { private var downloadsDir = preferences.downloadsDirectory().getOrDefault().let {
val dir = UniFile.fromUri(context, Uri.parse(it)) val dir = UniFile.fromUri(context, it.toUri())
DiskUtil.createNoMediaFile(dir, context) DiskUtil.createNoMediaFile(dir, context)
dir dir
} }
init { init {
preferences.downloadsDirectory().asObservable().skip(1) preferences.downloadsDirectory().asObservable().skip(1)
.subscribe { downloadsDir = UniFile.fromUri(context, Uri.parse(it)) } .subscribe { downloadsDir = UniFile.fromUri(context, it.toUri()) }
} }
/** /**

View File

@ -1,6 +1,7 @@
package eu.kanade.tachiyomi.data.track.anilist package eu.kanade.tachiyomi.data.track.anilist
import android.net.Uri import android.net.Uri
import androidx.core.net.toUri
import com.github.salomonbrys.kotson.array import com.github.salomonbrys.kotson.array
import com.github.salomonbrys.kotson.get import com.github.salomonbrys.kotson.get
import com.github.salomonbrys.kotson.jsonObject import com.github.salomonbrys.kotson.jsonObject
@ -232,7 +233,7 @@ class AnilistApi(val client: OkHttpClient, interceptor: AnilistInterceptor) {
return baseMangaUrl + mediaId return baseMangaUrl + mediaId
} }
fun authUrl() = Uri.parse("${baseUrl}oauth/authorize").buildUpon() fun authUrl() = "${baseUrl.toUri()}oauth/authorize".toUri().buildUpon()
.appendQueryParameter("client_id", clientId) .appendQueryParameter("client_id", clientId)
.appendQueryParameter("response_type", "token") .appendQueryParameter("response_type", "token")
.build()!! .build()!!

View File

@ -1,6 +1,7 @@
package eu.kanade.tachiyomi.data.track.bangumi package eu.kanade.tachiyomi.data.track.bangumi
import android.net.Uri import android.net.Uri
import androidx.core.net.toUri
import com.github.salomonbrys.kotson.array import com.github.salomonbrys.kotson.array
import com.github.salomonbrys.kotson.obj import com.github.salomonbrys.kotson.obj
import com.google.gson.Gson import com.google.gson.Gson
@ -56,9 +57,8 @@ class BangumiApi(private val client: OkHttpClient, interceptor: BangumiIntercept
suspend fun search(search: String): List<TrackSearch> { suspend fun search(search: String): List<TrackSearch> {
return withContext(Dispatchers.IO) { return withContext(Dispatchers.IO) {
val url = Uri.parse( val url = "$apiUrl/search/subject/${URLEncoder.encode(search, Charsets.UTF_8.name())}"
"$apiUrl/search/subject/${URLEncoder.encode(search, Charsets.UTF_8.name())}" .toUri().buildUpon().appendQueryParameter("max_results", "20").build()
).buildUpon().appendQueryParameter("max_results", "20").build()
val request = Request.Builder().url(url.toString()).get().build() val request = Request.Builder().url(url.toString()).get().build()
val netResponse = authClient.newCall(request).await() val netResponse = authClient.newCall(request).await()
@ -159,7 +159,7 @@ class BangumiApi(private val client: OkHttpClient, interceptor: BangumiIntercept
return "$baseMangaUrl/$remoteId" return "$baseMangaUrl/$remoteId"
} }
fun authUrl() = Uri.parse(loginUrl).buildUpon().appendQueryParameter("client_id", clientId) fun authUrl() = loginUrl.toUri().buildUpon().appendQueryParameter("client_id", clientId)
.appendQueryParameter("response_type", "code") .appendQueryParameter("response_type", "code")
.appendQueryParameter("redirect_uri", redirectUrl).build() .appendQueryParameter("redirect_uri", redirectUrl).build()

View File

@ -1,6 +1,7 @@
package eu.kanade.tachiyomi.data.track.myanimelist package eu.kanade.tachiyomi.data.track.myanimelist
import android.net.Uri import android.net.Uri
import androidx.core.net.toUri
import eu.kanade.tachiyomi.data.database.models.Track import eu.kanade.tachiyomi.data.database.models.Track
import eu.kanade.tachiyomi.data.track.TrackManager import eu.kanade.tachiyomi.data.track.TrackManager
import eu.kanade.tachiyomi.data.track.model.TrackSearch import eu.kanade.tachiyomi.data.track.model.TrackSearch
@ -182,31 +183,31 @@ class MyAnimeListApi(private val client: OkHttpClient, interceptor: MyAnimeListI
private fun mangaUrl(remoteId: Int) = baseMangaUrl + remoteId private fun mangaUrl(remoteId: Int) = baseMangaUrl + remoteId
private fun loginUrl() = Uri.parse(baseUrl).buildUpon().appendPath("login.php").toString() private fun loginUrl() = baseUrl.toUri().buildUpon().appendPath("login.php").toString()
private fun searchUrl(query: String): String { private fun searchUrl(query: String): String {
val col = "c[]" val col = "c[]"
return Uri.parse(baseUrl).buildUpon().appendPath("manga.php") return baseUrl.toUri().buildUpon().appendPath("manga.php")
.appendQueryParameter("q", query).appendQueryParameter(col, "a") .appendQueryParameter("q", query).appendQueryParameter(col, "a")
.appendQueryParameter(col, "b").appendQueryParameter(col, "c") .appendQueryParameter(col, "b").appendQueryParameter(col, "c")
.appendQueryParameter(col, "d").appendQueryParameter(col, "e") .appendQueryParameter(col, "d").appendQueryParameter(col, "e")
.appendQueryParameter(col, "g").toString() .appendQueryParameter(col, "g").toString()
} }
private fun exportListUrl() = Uri.parse(baseUrl).buildUpon().appendPath("panel.php") private fun exportListUrl() = baseUrl.toUri().buildUpon().appendPath("panel.php")
.appendQueryParameter("go", "export").toString() .appendQueryParameter("go", "export").toString()
private fun updateUrl() = private fun updateUrl() =
Uri.parse(baseModifyListUrl).buildUpon().appendPath("edit.json").toString() baseModifyListUrl.toUri().buildUpon().appendPath("edit.json").toString()
private fun removeUrl(mediaId: Int) = Uri.parse(baseModifyListUrl).buildUpon().appendPath(mediaId.toString()) private fun removeUrl(mediaId: Int) = baseModifyListUrl.toUri().buildUpon().appendPath(mediaId.toString())
.appendPath("delete").toString() .appendPath("delete").toString()
private fun addUrl() = private fun addUrl() =
Uri.parse(baseModifyListUrl).buildUpon().appendPath("add.json").toString() baseModifyListUrl.toUri().buildUpon().appendPath("add.json").toString()
private fun listEntryUrl(mediaId: Int) = private fun listEntryUrl(mediaId: Int) =
Uri.parse(baseModifyListUrl).buildUpon().appendPath(mediaId.toString()) baseModifyListUrl.toUri().buildUpon().appendPath(mediaId.toString())
.appendPath("edit").toString() .appendPath("edit").toString()
private fun loginPostBody(username: String, password: String, csrf: String): RequestBody { private fun loginPostBody(username: String, password: String, csrf: String): RequestBody {

View File

@ -1,6 +1,7 @@
package eu.kanade.tachiyomi.data.track.shikimori package eu.kanade.tachiyomi.data.track.shikimori
import android.net.Uri import android.net.Uri
import androidx.core.net.toUri
import com.github.salomonbrys.kotson.array import com.github.salomonbrys.kotson.array
import com.github.salomonbrys.kotson.jsonObject import com.github.salomonbrys.kotson.jsonObject
import com.github.salomonbrys.kotson.nullString import com.github.salomonbrys.kotson.nullString
@ -52,7 +53,7 @@ class ShikimoriApi(private val client: OkHttpClient, interceptor: ShikimoriInter
suspend fun search(search: String): List<TrackSearch> { suspend fun search(search: String): List<TrackSearch> {
return withContext(Dispatchers.IO) { return withContext(Dispatchers.IO) {
val url = val url =
Uri.parse("$apiUrl/mangas").buildUpon().appendQueryParameter("order", "popularity") "$apiUrl/mangas".toUri().buildUpon().appendQueryParameter("order", "popularity")
.appendQueryParameter("search", search).appendQueryParameter("limit", "20") .appendQueryParameter("search", search).appendQueryParameter("limit", "20")
.build() .build()
val request = Request.Builder().url(url.toString()).get().build() val request = Request.Builder().url(url.toString()).get().build()
@ -96,14 +97,13 @@ class ShikimoriApi(private val client: OkHttpClient, interceptor: ShikimoriInter
suspend fun findLibManga(track: Track, user_id: String): Track? { suspend fun findLibManga(track: Track, user_id: String): Track? {
return withContext(Dispatchers.IO) { return withContext(Dispatchers.IO) {
val url = Uri.parse("$apiUrl/v2/user_rates").buildUpon() val url = "$apiUrl/v2/user_rates".toUri().buildUpon()
.appendQueryParameter("user_id", user_id) .appendQueryParameter("user_id", user_id)
.appendQueryParameter("target_id", track.media_id.toString()) .appendQueryParameter("target_id", track.media_id.toString())
.appendQueryParameter("target_type", "Manga").build() .appendQueryParameter("target_type", "Manga").build()
val request = Request.Builder().url(url.toString()).get().build() val request = Request.Builder().url(url.toString()).get().build()
val urlMangas = val urlMangas ="$apiUrl/mangas".toUri().buildUpon().appendPath(track.media_id.toString())
Uri.parse("$apiUrl/mangas").buildUpon().appendPath(track.media_id.toString())
.build() .build()
val requestMangas = Request.Builder().url(urlMangas.toString()).get().build() val requestMangas = Request.Builder().url(urlMangas.toString()).get().build()
@ -171,7 +171,7 @@ class ShikimoriApi(private val client: OkHttpClient, interceptor: ShikimoriInter
return "$baseMangaUrl/$remoteId" return "$baseMangaUrl/$remoteId"
} }
fun authUrl() = Uri.parse(loginUrl).buildUpon().appendQueryParameter("client_id", clientId) fun authUrl() = loginUrl.toUri().buildUpon().appendQueryParameter("client_id", clientId)
.appendQueryParameter("redirect_uri", redirectUrl) .appendQueryParameter("redirect_uri", redirectUrl)
.appendQueryParameter("response_type", "code").build() .appendQueryParameter("response_type", "code").build()

View File

@ -7,6 +7,7 @@ import android.content.Intent
import android.content.IntentFilter import android.content.IntentFilter
import android.net.Uri import android.net.Uri
import android.os.Environment import android.os.Environment
import androidx.core.net.toUri
import com.jakewharton.rxrelay.PublishRelay import com.jakewharton.rxrelay.PublishRelay
import eu.kanade.tachiyomi.extension.model.Extension import eu.kanade.tachiyomi.extension.model.Extension
import eu.kanade.tachiyomi.extension.model.InstallStep import eu.kanade.tachiyomi.extension.model.InstallStep
@ -63,7 +64,7 @@ internal class ExtensionInstaller(private val context: Context) {
// Register the receiver after removing (and unregistering) the previous download // Register the receiver after removing (and unregistering) the previous download
downloadReceiver.register() downloadReceiver.register()
val downloadUri = Uri.parse(url) val downloadUri = url.toUri()
val request = DownloadManager.Request(downloadUri) val request = DownloadManager.Request(downloadUri)
.setTitle(extension.name) .setTitle(extension.name)
.setMimeType(APK_MIME) .setMimeType(APK_MIME)
@ -141,7 +142,7 @@ internal class ExtensionInstaller(private val context: Context) {
* @param pkgName The package name of the extension to uninstall * @param pkgName The package name of the extension to uninstall
*/ */
fun uninstallApk(pkgName: String) { fun uninstallApk(pkgName: String) {
val packageUri = Uri.parse("package:$pkgName") val packageUri = "package:$pkgName".toUri()
val intent = Intent(Intent.ACTION_UNINSTALL_PACKAGE, packageUri) val intent = Intent(Intent.ACTION_UNINSTALL_PACKAGE, packageUri)
.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK) .setFlags(Intent.FLAG_ACTIVITY_NEW_TASK)

View File

@ -31,17 +31,19 @@ import java.util.zip.ZipFile
class LocalSource(private val context: Context) : CatalogueSource { class LocalSource(private val context: Context) : CatalogueSource {
companion object { companion object {
const val ID = 0L
const val HELP_URL = "https://tachiyomi.org/help/guides/reading-local-manga/" const val HELP_URL = "https://tachiyomi.org/help/guides/reading-local-manga/"
private const val COVER_NAME = "cover.jpg" private const val COVER_NAME = "cover.jpg"
private val SUPPORTED_ARCHIVE_TYPES = setOf("zip", "rar", "cbr", "cbz", "epub")
private val POPULAR_FILTERS = FilterList(OrderBy()) private val POPULAR_FILTERS = FilterList(OrderBy())
private val LATEST_FILTERS = private val LATEST_FILTERS =
FilterList(OrderBy().apply { state = Filter.Sort.Selection(1, false) }) FilterList(OrderBy().apply { state = Filter.Sort.Selection(1, false) })
private val LATEST_THRESHOLD = TimeUnit.MILLISECONDS.convert(7, TimeUnit.DAYS) private val LATEST_THRESHOLD = TimeUnit.MILLISECONDS.convert(7, TimeUnit.DAYS)
const val ID = 0L
fun updateCover(context: Context, manga: SManga, input: InputStream): File? { fun updateCover(context: Context, manga: SManga, input: InputStream): File? {
val dir = getBaseDirectories(context).firstOrNull() val dir = getBaseDirectories(context).asSequence().firstOrNull()
if (dir == null) { if (dir == null) {
input.close() input.close()
return null return null
@ -85,12 +87,12 @@ class LocalSource(private val context: Context) : CatalogueSource {
val time = val time =
if (filters === LATEST_FILTERS) System.currentTimeMillis() - LATEST_THRESHOLD else 0L if (filters === LATEST_FILTERS) System.currentTimeMillis() - LATEST_THRESHOLD else 0L
var mangaDirs = baseDirs.mapNotNull { it.listFiles()?.toList() }.flatten().filter { var mangaDirs = baseDirs
it.isDirectory && if (time == 0L) it.name.contains( .asSequence()
query, .mapNotNull { it.listFiles()?.toList() }.flatten()
ignoreCase = true .filter { it.isDirectory }
) else it.lastModified() >= time .filter { if (time == 0L) it.name.contains(query, ignoreCase = true) else it.lastModified() >= time }
}.distinctBy { it.name } .distinctBy { it.name }
val state = ((if (filters.isEmpty()) POPULAR_FILTERS else filters)[0] as OrderBy).state val state = ((if (filters.isEmpty()) POPULAR_FILTERS else filters)[0] as OrderBy).state
when (state?.index) { when (state?.index) {
@ -134,15 +136,17 @@ class LocalSource(private val context: Context) : CatalogueSource {
} }
} }
} }
return Observable.just(MangasPage(mangas, false)) return Observable.just(MangasPage(mangas.toList(), false))
} }
override fun fetchLatestUpdates(page: Int) = fetchSearchManga(page, "", LATEST_FILTERS) override fun fetchLatestUpdates(page: Int) = fetchSearchManga(page, "", LATEST_FILTERS)
override fun fetchMangaDetails(manga: SManga): Observable<SManga> { override fun fetchMangaDetails(manga: SManga): Observable<SManga> {
val baseDirs = getBaseDirectories(context) val baseDirs = getBaseDirectories(context)
baseDirs.mapNotNull { File(it, manga.url).listFiles()?.toList() } baseDirs
.flatten().filter { it.extension == "json" }.firstOrNull()?.apply { .mapNotNull { File(it, manga.url).listFiles()?.toList() }
.flatten()
.filter { it.extension == "json" }.firstOrNull()?.apply {
val json = Gson().fromJson( val json = Gson().fromJson(
Scanner(this).useDelimiter("\\Z").next(), Scanner(this).useDelimiter("\\Z").next(),
JsonObject::class.java JsonObject::class.java
@ -155,6 +159,7 @@ class LocalSource(private val context: Context) : CatalogueSource {
?: manga.genre ?: manga.genre
manga.status = json["status"]?.asInt ?: manga.status manga.status = json["status"]?.asInt ?: manga.status
} }
val url = manga.url val url = manga.url
// Try to find the cover // Try to find the cover
for (dir in baseDirs) { for (dir in baseDirs) {
@ -249,7 +254,7 @@ class LocalSource(private val context: Context) : CatalogueSource {
} }
private fun isSupportedFile(extension: String): Boolean { private fun isSupportedFile(extension: String): Boolean {
return extension.toLowerCase() in setOf("zip", "rar", "cbr", "cbz", "epub") return extension.toLowerCase() in SUPPORTED_ARCHIVE_TYPES
} }
fun getFormat(chapter: SChapter): Format { fun getFormat(chapter: SChapter): Format {
@ -283,22 +288,23 @@ class LocalSource(private val context: Context) : CatalogueSource {
val format = getFormat(chapter) val format = getFormat(chapter)
return when (format) { return when (format) {
is Format.Directory -> { is Format.Directory -> {
val entry = format.file.listFiles().sortedWith(Comparator<File> { f1, f2 -> val entry = format.file.listFiles()
?.sortedWith(Comparator<File> { f1, f2 ->
f1.name.compareToCaseInsensitiveNaturalOrder(f2.name) f1.name.compareToCaseInsensitiveNaturalOrder(f2.name)
}) })
.find { !it.isDirectory && ImageUtil.isImage(it.name) { FileInputStream(it) } } ?.find { !it.isDirectory && ImageUtil.isImage(it.name) { FileInputStream(it) } }
entry?.let { updateCover(context, manga, it.inputStream()) } entry?.let { updateCover(context, manga, it.inputStream()) }
} }
is Format.Zip -> { is Format.Zip -> {
ZipFile(format.file).use { zip -> ZipFile(format.file).use { zip ->
val entry = zip.entries().toList().sortedWith(Comparator<ZipEntry> { f1, f2 -> val entry = zip.entries().toList().sortedWith(Comparator<ZipEntry> { f1, f2 ->
f1.name.compareToCaseInsensitiveNaturalOrder(f2.name) f1.name.compareToCaseInsensitiveNaturalOrder(f2.name)
}).find { }).find {
!it.isDirectory && ImageUtil.isImage(it.name) { !it.isDirectory && ImageUtil.isImage(it.name) {
zip.getInputStream(it) zip.getInputStream(it)
}
} }
}
entry?.let { updateCover(context, manga, zip.getInputStream(it)) } entry?.let { updateCover(context, manga, zip.getInputStream(it)) }
} }
@ -306,12 +312,12 @@ class LocalSource(private val context: Context) : CatalogueSource {
is Format.Rar -> { is Format.Rar -> {
Archive(format.file).use { archive -> Archive(format.file).use { archive ->
val entry = archive.fileHeaders.sortedWith(Comparator<FileHeader> { f1, f2 -> val entry = archive.fileHeaders.sortedWith(Comparator<FileHeader> { f1, f2 ->
f1.fileNameString.compareToCaseInsensitiveNaturalOrder(f2.fileNameString) f1.fileNameString.compareToCaseInsensitiveNaturalOrder(f2.fileNameString)
}).find { }).find {
!it.isDirectory && ImageUtil.isImage(it.fileNameString) { !it.isDirectory && ImageUtil.isImage(it.fileNameString) {
archive.getInputStream(it) archive.getInputStream(it)
}
} }
}
entry?.let { updateCover(context, manga, archive.getInputStream(it)) } entry?.let { updateCover(context, manga, archive.getInputStream(it)) }
} }

View File

@ -5,6 +5,7 @@ import android.net.Uri
import android.os.Bundle import android.os.Bundle
import android.view.View import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
import androidx.core.net.toUri
import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.LinearLayoutManager
import com.google.android.material.bottomsheet.BottomSheetBehavior import com.google.android.material.bottomsheet.BottomSheetBehavior
import com.google.android.material.bottomsheet.BottomSheetDialog import com.google.android.material.bottomsheet.BottomSheetDialog
@ -117,7 +118,7 @@ class TrackingBottomSheet(private val controller: MangaDetailsController) : Bott
if (track.tracking_url.isBlank()) { if (track.tracking_url.isBlank()) {
activity.toast(R.string.url_not_set_click_again) activity.toast(R.string.url_not_set_click_again)
} else { } else {
activity.startActivity(Intent(Intent.ACTION_VIEW, Uri.parse(track.tracking_url))) activity.startActivity(Intent(Intent.ACTION_VIEW, track.tracking_url.toUri()))
controller.refreshTracker = position controller.refreshTracker = position
} }
} }

View File

@ -18,6 +18,7 @@ import android.widget.FrameLayout
import android.widget.ImageView import android.widget.ImageView
import android.widget.LinearLayout import android.widget.LinearLayout
import android.widget.TextView import android.widget.TextView
import androidx.core.net.toUri
import coil.api.loadAny import coil.api.loadAny
import coil.request.CachePolicy import coil.request.CachePolicy
import com.davemorrissey.labs.subscaleview.ImageSource import com.davemorrissey.labs.subscaleview.ImageSource
@ -465,7 +466,7 @@ class PagerPageHolder(
} }
setText(R.string.open_in_browser) setText(R.string.open_in_browser)
setOnClickListener { setOnClickListener {
val intent = Intent(Intent.ACTION_VIEW, Uri.parse(imageUrl)) val intent = Intent(Intent.ACTION_VIEW, imageUrl.toUri())
context.startActivity(intent) context.startActivity(intent)
} }

View File

@ -15,6 +15,7 @@ import android.widget.LinearLayout
import android.widget.TextView import android.widget.TextView
import androidx.appcompat.widget.AppCompatButton import androidx.appcompat.widget.AppCompatButton
import androidx.appcompat.widget.AppCompatImageView import androidx.appcompat.widget.AppCompatImageView
import androidx.core.net.toUri
import coil.api.clear import coil.api.clear
import coil.api.loadAny import coil.api.loadAny
import coil.request.CachePolicy import coil.request.CachePolicy
@ -458,7 +459,7 @@ class WebtoonPageHolder(
} }
setText(R.string.open_in_browser) setText(R.string.open_in_browser)
setOnClickListener { setOnClickListener {
val intent = Intent(Intent.ACTION_VIEW, Uri.parse(imageUrl)) val intent = Intent(Intent.ACTION_VIEW, imageUrl.toUri())
context.startActivity(intent) context.startActivity(intent)
} }

View File

@ -33,7 +33,7 @@ class BiometricActivity : BaseActivity() {
val promptInfo = BiometricPrompt.PromptInfo.Builder() val promptInfo = BiometricPrompt.PromptInfo.Builder()
.setTitle(getString(R.string.unlock_library)) .setTitle(getString(R.string.unlock_library))
.setNegativeButtonText(getString(android.R.string.cancel)) .setDeviceCredentialAllowed(true)
.build() .build()
biometricPrompt.authenticate(promptInfo) biometricPrompt.authenticate(promptInfo)

View File

@ -4,6 +4,7 @@ import android.app.Dialog
import android.content.Intent import android.content.Intent
import android.net.Uri import android.net.Uri
import android.os.Bundle import android.os.Bundle
import androidx.core.net.toUri
import androidx.preference.PreferenceScreen import androidx.preference.PreferenceScreen
import com.afollestad.materialdialogs.MaterialDialog import com.afollestad.materialdialogs.MaterialDialog
import com.google.android.gms.oss.licenses.OssLicensesMenuActivity import com.google.android.gms.oss.licenses.OssLicensesMenuActivity
@ -58,7 +59,7 @@ class AboutController : SettingsController() {
val url = "https://tachiyomi.org" val url = "https://tachiyomi.org"
summary = url summary = url
onClick { onClick {
val intent = Intent(Intent.ACTION_VIEW, Uri.parse(url)) val intent = Intent(Intent.ACTION_VIEW, url.toUri())
startActivity(intent) startActivity(intent)
} }
} }
@ -68,7 +69,7 @@ class AboutController : SettingsController() {
val url = "https://discord.gg/tachiyomi" val url = "https://discord.gg/tachiyomi"
summary = url summary = url
onClick { onClick {
val intent = Intent(Intent.ACTION_VIEW, Uri.parse(url)) val intent = Intent(Intent.ACTION_VIEW, url.toUri())
startActivity(intent) startActivity(intent)
} }
} }
@ -77,7 +78,7 @@ class AboutController : SettingsController() {
val url = "https://github.com/Jays2Kings/tachiyomiJ2K" val url = "https://github.com/Jays2Kings/tachiyomiJ2K"
summary = url summary = url
onClick { onClick {
val intent = Intent(Intent.ACTION_VIEW, Uri.parse(url)) val intent = Intent(Intent.ACTION_VIEW, url.toUri())
startActivity(intent) startActivity(intent)
} }
} }
@ -86,13 +87,12 @@ class AboutController : SettingsController() {
titleRes = R.string.whats_new titleRes = R.string.whats_new
onClick { onClick {
val intent = Intent( val intent = Intent(
Intent.ACTION_VIEW, Uri.parse( Intent.ACTION_VIEW,
if (BuildConfig.DEBUG) { if (BuildConfig.DEBUG) {
"https://github.com/Jays2Kings/tachiyomiJ2K/commits/master" "https://github.com/Jays2Kings/tachiyomiJ2K/commits/master"
} else { } else {
"https://github.com/Jays2Kings/tachiyomiJ2K/releases/tag/v${BuildConfig.VERSION_NAME}" "https://github.com/Jays2Kings/tachiyomiJ2K/releases/tag/v${BuildConfig.VERSION_NAME}"
} }.toUri()
)
) )
startActivity(intent) startActivity(intent)
} }

View File

@ -10,6 +10,7 @@ import android.os.Bundle
import android.os.PowerManager import android.os.PowerManager
import android.provider.Settings import android.provider.Settings
import android.widget.Toast import android.widget.Toast
import androidx.core.net.toUri
import androidx.preference.PreferenceScreen import androidx.preference.PreferenceScreen
import com.afollestad.materialdialogs.MaterialDialog import com.afollestad.materialdialogs.MaterialDialog
import eu.kanade.tachiyomi.R import eu.kanade.tachiyomi.R
@ -122,7 +123,7 @@ class SettingsAdvancedController : SettingsController() {
if (!pm.isIgnoringBatteryOptimizations(packageName)) { if (!pm.isIgnoringBatteryOptimizations(packageName)) {
val intent = Intent().apply { val intent = Intent().apply {
action = Settings.ACTION_REQUEST_IGNORE_BATTERY_OPTIMIZATIONS action = Settings.ACTION_REQUEST_IGNORE_BATTERY_OPTIMIZATIONS
data = Uri.parse("package:$packageName") data = "package:$packageName".toUri()
} }
startActivity(intent) startActivity(intent)
} else { } else {

View File

@ -8,6 +8,7 @@ import android.content.Intent
import android.net.Uri import android.net.Uri
import android.os.Bundle import android.os.Bundle
import android.view.View import android.view.View
import androidx.core.net.toUri
import androidx.preference.PreferenceScreen import androidx.preference.PreferenceScreen
import com.afollestad.materialdialogs.MaterialDialog import com.afollestad.materialdialogs.MaterialDialog
import com.afollestad.materialdialogs.list.listItemsMultiChoice import com.afollestad.materialdialogs.list.listItemsMultiChoice
@ -102,7 +103,7 @@ class SettingsBackupController : SettingsController() {
preferences.backupsDirectory().asObservable() preferences.backupsDirectory().asObservable()
.subscribeUntilDestroy { path -> .subscribeUntilDestroy { path ->
val dir = UniFile.fromUri(context, Uri.parse(path)) val dir = UniFile.fromUri(context, path.toUri())
summary = dir.filePath + "/automatic" summary = dir.filePath + "/automatic"
} }
} }

View File

@ -8,6 +8,7 @@ import android.net.Uri
import android.os.Bundle import android.os.Bundle
import android.os.Environment import android.os.Environment
import androidx.core.content.ContextCompat import androidx.core.content.ContextCompat
import androidx.core.net.toUri
import androidx.preference.PreferenceScreen import androidx.preference.PreferenceScreen
import com.afollestad.materialdialogs.MaterialDialog import com.afollestad.materialdialogs.MaterialDialog
import com.afollestad.materialdialogs.list.listItemsSingleChoice import com.afollestad.materialdialogs.list.listItemsSingleChoice
@ -42,7 +43,7 @@ class SettingsDownloadController : SettingsController() {
preferences.downloadsDirectory().asObservable() preferences.downloadsDirectory().asObservable()
.subscribeUntilDestroy { path -> .subscribeUntilDestroy { path ->
val dir = UniFile.fromUri(context, Uri.parse(path)) val dir = UniFile.fromUri(context, path.toUri())
summary = dir.filePath ?: path summary = dir.filePath ?: path
} }
} }

View File

@ -25,6 +25,7 @@ import androidx.annotation.StringRes
import androidx.browser.customtabs.CustomTabsIntent import androidx.browser.customtabs.CustomTabsIntent
import androidx.core.app.NotificationCompat import androidx.core.app.NotificationCompat
import androidx.core.content.ContextCompat import androidx.core.content.ContextCompat
import androidx.core.net.toUri
import com.nononsenseapps.filepicker.FilePickerActivity import com.nononsenseapps.filepicker.FilePickerActivity
import eu.kanade.tachiyomi.R import eu.kanade.tachiyomi.R
import eu.kanade.tachiyomi.widget.CustomLayoutPickerActivity import eu.kanade.tachiyomi.widget.CustomLayoutPickerActivity
@ -223,7 +224,7 @@ fun Context.isServiceRunning(serviceClass: Class<*>): Boolean {
*/ */
fun Context.openInBrowser(url: String) { fun Context.openInBrowser(url: String) {
try { try {
val parsedUrl = Uri.parse(url) val parsedUrl = url.toUri()
val intent = CustomTabsIntent.Builder() val intent = CustomTabsIntent.Builder()
.setToolbarColor(getResourceColor(R.attr.colorPrimaryVariant)) .setToolbarColor(getResourceColor(R.attr.colorPrimaryVariant))
.build() .build()