Add Koharu (#3981)
* Add Koharu.to * Remove some useless things * Fixed Filters, and More - Fixed Filters/Search not working when query is empty - Changed image resolution default value to match website's - Use `when` instead of kotlin's reflect - Added search by "id", and url intent filter? * Apply Suggestions - Apply vetleledaal's suggestions - Fix UrlActivity? * Apply suggestions * Apply Suggestions * Add files via upload
This commit is contained in:
parent
7257dc89a8
commit
41812dd97b
|
@ -0,0 +1,23 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
|
||||||
|
<application>
|
||||||
|
<activity
|
||||||
|
android:name=".en.koharu.KoharuUrlActivity"
|
||||||
|
android:excludeFromRecents="true"
|
||||||
|
android:exported="true"
|
||||||
|
android:theme="@android:style/Theme.NoDisplay">
|
||||||
|
<intent-filter>
|
||||||
|
<action android:name="android.intent.action.VIEW" />
|
||||||
|
|
||||||
|
<category android:name="android.intent.category.DEFAULT" />
|
||||||
|
<category android:name="android.intent.category.BROWSABLE" />
|
||||||
|
|
||||||
|
<data
|
||||||
|
android:host="koharu.to"
|
||||||
|
android:pathPattern="/g/..*/..*"
|
||||||
|
android:scheme="https" />
|
||||||
|
</intent-filter>
|
||||||
|
</activity>
|
||||||
|
</application>
|
||||||
|
</manifest>
|
|
@ -0,0 +1,8 @@
|
||||||
|
ext {
|
||||||
|
extName = 'Koharu'
|
||||||
|
extClass = '.Koharu'
|
||||||
|
extVersionCode = 1
|
||||||
|
isNsfw = true
|
||||||
|
}
|
||||||
|
|
||||||
|
apply from: "$rootDir/common.gradle"
|
Binary file not shown.
After Width: | Height: | Size: 2.7 KiB |
Binary file not shown.
After Width: | Height: | Size: 1.4 KiB |
Binary file not shown.
After Width: | Height: | Size: 3.6 KiB |
Binary file not shown.
After Width: | Height: | Size: 6.7 KiB |
Binary file not shown.
After Width: | Height: | Size: 9.6 KiB |
|
@ -0,0 +1,305 @@
|
||||||
|
package eu.kanade.tachiyomi.extension.en.koharu
|
||||||
|
|
||||||
|
import android.app.Application
|
||||||
|
import android.content.SharedPreferences
|
||||||
|
import androidx.preference.ListPreference
|
||||||
|
import androidx.preference.PreferenceScreen
|
||||||
|
import eu.kanade.tachiyomi.network.GET
|
||||||
|
import eu.kanade.tachiyomi.network.POST
|
||||||
|
import eu.kanade.tachiyomi.network.interceptor.rateLimit
|
||||||
|
import eu.kanade.tachiyomi.source.ConfigurableSource
|
||||||
|
import eu.kanade.tachiyomi.source.model.FilterList
|
||||||
|
import eu.kanade.tachiyomi.source.model.MangasPage
|
||||||
|
import eu.kanade.tachiyomi.source.model.Page
|
||||||
|
import eu.kanade.tachiyomi.source.model.SChapter
|
||||||
|
import eu.kanade.tachiyomi.source.model.SManga
|
||||||
|
import eu.kanade.tachiyomi.source.model.UpdateStrategy
|
||||||
|
import eu.kanade.tachiyomi.source.online.HttpSource
|
||||||
|
import kotlinx.serialization.decodeFromString
|
||||||
|
import kotlinx.serialization.json.Json
|
||||||
|
import okhttp3.HttpUrl.Companion.toHttpUrl
|
||||||
|
import okhttp3.OkHttpClient
|
||||||
|
import okhttp3.Request
|
||||||
|
import okhttp3.Response
|
||||||
|
import rx.Observable
|
||||||
|
import uy.kohesive.injekt.Injekt
|
||||||
|
import uy.kohesive.injekt.api.get
|
||||||
|
import uy.kohesive.injekt.injectLazy
|
||||||
|
import java.text.SimpleDateFormat
|
||||||
|
import java.util.Locale
|
||||||
|
|
||||||
|
class Koharu : HttpSource(), ConfigurableSource {
|
||||||
|
override val name = "Koharu"
|
||||||
|
|
||||||
|
override val baseUrl = "https://koharu.to"
|
||||||
|
|
||||||
|
private val apiUrl = baseUrl.replace("://", "://api.")
|
||||||
|
|
||||||
|
private val apiBooksUrl = "$apiUrl/books"
|
||||||
|
|
||||||
|
override val lang = "en"
|
||||||
|
|
||||||
|
override val supportsLatest = true
|
||||||
|
|
||||||
|
override val client: OkHttpClient = network.cloudflareClient.newBuilder()
|
||||||
|
.rateLimit(1)
|
||||||
|
.build()
|
||||||
|
|
||||||
|
private val json: Json by injectLazy()
|
||||||
|
|
||||||
|
private val preferences: SharedPreferences by lazy {
|
||||||
|
Injekt.get<Application>().getSharedPreferences("source_$id", 0x0000)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun quality() = preferences.getString(PREF_IMAGERES, "1280")!!
|
||||||
|
|
||||||
|
override fun headersBuilder() = super.headersBuilder()
|
||||||
|
.add("Referer", "$baseUrl/")
|
||||||
|
.add("Origin", baseUrl)
|
||||||
|
|
||||||
|
private fun getManga(book: Entry) = SManga.create().apply {
|
||||||
|
setUrlWithoutDomain("${book.id}/${book.public_key}")
|
||||||
|
title = book.title
|
||||||
|
thumbnail_url = book.thumbnail.path
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun getImagesByMangaEntry(entry: MangaEntry): ImagesInfo {
|
||||||
|
val data = entry.data
|
||||||
|
val dataKey = when (quality()) {
|
||||||
|
"1600" -> data.`1600` ?: data.`1280` ?: data.`0`
|
||||||
|
"1280" -> data.`1280` ?: data.`1600` ?: data.`0`
|
||||||
|
"980" -> data.`980` ?: data.`1280` ?: data.`0`
|
||||||
|
"780" -> data.`780` ?: data.`980` ?: data.`0`
|
||||||
|
else -> data.`0`
|
||||||
|
}
|
||||||
|
|
||||||
|
val imagesResponse = client.newCall(POST("$apiBooksUrl/data/${entry.id}/${entry.public_key}/${dataKey.id}/${dataKey.public_key}", headers)).execute()
|
||||||
|
val images = imagesResponse.parseAs<ImagesInfo>()
|
||||||
|
return images
|
||||||
|
}
|
||||||
|
|
||||||
|
// Latest
|
||||||
|
|
||||||
|
override fun latestUpdatesRequest(page: Int) = GET("$apiBooksUrl?page=$page", headers)
|
||||||
|
override fun latestUpdatesParse(response: Response) = popularMangaParse(response)
|
||||||
|
|
||||||
|
// Popular
|
||||||
|
|
||||||
|
override fun popularMangaRequest(page: Int) = GET("$apiBooksUrl?sort=6&page=$page", headers)
|
||||||
|
override fun popularMangaParse(response: Response): MangasPage {
|
||||||
|
val data = response.parseAs<Books>()
|
||||||
|
|
||||||
|
return MangasPage(data.entries.map(::getManga), data.page * data.limit < data.total)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Search
|
||||||
|
|
||||||
|
override fun getFilterList(): FilterList = getFilters()
|
||||||
|
|
||||||
|
override fun fetchSearchManga(page: Int, query: String, filters: FilterList): Observable<MangasPage> {
|
||||||
|
return when {
|
||||||
|
query.startsWith(PREFIX_ID_KEY_SEARCH) -> {
|
||||||
|
val ipk = query.removePrefix(PREFIX_ID_KEY_SEARCH)
|
||||||
|
val response = client.newCall(GET("$apiBooksUrl/detail/$ipk", headers)).execute()
|
||||||
|
Observable.just(searchMangaParse2(response))
|
||||||
|
}
|
||||||
|
else -> super.fetchSearchManga(page, query, filters)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun searchMangaRequest(page: Int, query: String, filters: FilterList): Request {
|
||||||
|
val url = apiBooksUrl.toHttpUrl().newBuilder().apply {
|
||||||
|
val terms = mutableListOf(query.trim())
|
||||||
|
|
||||||
|
filters.forEach { filter ->
|
||||||
|
when (filter) {
|
||||||
|
is SortFilter -> addQueryParameter("sort", filter.getValue())
|
||||||
|
|
||||||
|
is CategoryFilter -> {
|
||||||
|
val activeFilter = filter.state.filter { it.state }
|
||||||
|
if (activeFilter.isNotEmpty()) {
|
||||||
|
addQueryParameter("cat", activeFilter.sumOf { it.value }.toString())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
is TextFilter -> {
|
||||||
|
if (filter.state.isNotEmpty()) {
|
||||||
|
terms += filter.state.split(",").filter(String::isNotBlank).map { tag ->
|
||||||
|
val trimmed = tag.trim()
|
||||||
|
buildString {
|
||||||
|
if (trimmed.startsWith('-')) {
|
||||||
|
append("-")
|
||||||
|
}
|
||||||
|
append(filter.type)
|
||||||
|
append("!:")
|
||||||
|
append("\"")
|
||||||
|
append(trimmed.lowercase().removePrefix("-"))
|
||||||
|
append("\"")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else -> {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (query.isNotEmpty()) terms.add("title:\"$query\"")
|
||||||
|
if (terms.isNotEmpty()) addQueryParameter("s", terms.joinToString(" "))
|
||||||
|
addQueryParameter("page", page.toString())
|
||||||
|
}.build()
|
||||||
|
|
||||||
|
return GET(url, headers)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun searchMangaParse(response: Response) = popularMangaParse(response)
|
||||||
|
|
||||||
|
private fun searchMangaParse2(response: Response): MangasPage {
|
||||||
|
val entry = response.parseAs<MangaEntry>()
|
||||||
|
|
||||||
|
return MangasPage(
|
||||||
|
listOf(
|
||||||
|
SManga.create().apply {
|
||||||
|
setUrlWithoutDomain("${entry.id}/${entry.public_key}")
|
||||||
|
title = entry.title
|
||||||
|
thumbnail_url = entry.thumbnails.base + entry.thumbnails.main.path
|
||||||
|
},
|
||||||
|
),
|
||||||
|
false,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
// Details
|
||||||
|
|
||||||
|
override fun mangaDetailsRequest(manga: SManga): Request {
|
||||||
|
return GET("$apiBooksUrl/detail/${manga.url}", headers)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun mangaDetailsParse(response: Response): SManga {
|
||||||
|
return response.parseAs<MangaEntry>().toSManga()
|
||||||
|
}
|
||||||
|
|
||||||
|
private val dateReformat = SimpleDateFormat("EEEE, d MMM yyyy HH:mm (z)", Locale.ENGLISH)
|
||||||
|
private fun MangaEntry.toSManga() = SManga.create().apply {
|
||||||
|
val artists = mutableListOf<String>()
|
||||||
|
val circles = mutableListOf<String>()
|
||||||
|
val parodies = mutableListOf<String>()
|
||||||
|
val magazines = mutableListOf<String>()
|
||||||
|
val characters = mutableListOf<String>()
|
||||||
|
val cosplayers = mutableListOf<String>()
|
||||||
|
val females = mutableListOf<String>()
|
||||||
|
val males = mutableListOf<String>()
|
||||||
|
val mixed = mutableListOf<String>()
|
||||||
|
val other = mutableListOf<String>()
|
||||||
|
val uploaders = mutableListOf<String>()
|
||||||
|
val tags = mutableListOf<String>()
|
||||||
|
for (tag in this@toSManga.tags) {
|
||||||
|
when (tag.namespace) {
|
||||||
|
1 -> artists.add(tag.name)
|
||||||
|
2 -> circles.add(tag.name)
|
||||||
|
3 -> parodies.add(tag.name)
|
||||||
|
4 -> magazines.add(tag.name)
|
||||||
|
5 -> characters.add(tag.name)
|
||||||
|
6 -> cosplayers.add(tag.name)
|
||||||
|
7 -> uploaders.add(tag.name)
|
||||||
|
8 -> males.add(tag.name + " ♂")
|
||||||
|
9 -> females.add(tag.name + " ♀")
|
||||||
|
10 -> mixed.add(tag.name)
|
||||||
|
12 -> other.add(tag.name)
|
||||||
|
else -> tags.add(tag.name)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
author = (circles.emptyToNull() ?: artists).joinToString()
|
||||||
|
artist = artists.joinToString()
|
||||||
|
genre = (tags + males + females + mixed).joinToString()
|
||||||
|
description = buildString {
|
||||||
|
circles.emptyToNull()?.joinToString()?.let {
|
||||||
|
append("Circles: ", it, "\n")
|
||||||
|
}
|
||||||
|
uploaders.emptyToNull()?.joinToString()?.let {
|
||||||
|
append("Uploaders: ", it, "\n")
|
||||||
|
}
|
||||||
|
magazines.emptyToNull()?.joinToString()?.let {
|
||||||
|
append("Magazines: ", it, "\n")
|
||||||
|
}
|
||||||
|
cosplayers.emptyToNull()?.joinToString()?.let {
|
||||||
|
append("Cosplayers: ", it, "\n")
|
||||||
|
}
|
||||||
|
parodies.emptyToNull()?.joinToString()?.let {
|
||||||
|
append("Parodies: ", it, "\n")
|
||||||
|
}
|
||||||
|
characters.emptyToNull()?.joinToString()?.let {
|
||||||
|
append("Characters: ", it, "\n")
|
||||||
|
}
|
||||||
|
append("Pages: ", thumbnails.entries.size, "\n\n")
|
||||||
|
|
||||||
|
try {
|
||||||
|
append("Added: ", dateReformat.format(((updated_at ?: created_at))), "\n")
|
||||||
|
} catch (_: Exception) {}
|
||||||
|
}
|
||||||
|
status = SManga.COMPLETED
|
||||||
|
update_strategy = UpdateStrategy.ONLY_FETCH_ONCE
|
||||||
|
initialized = true
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun <T> Collection<T>.emptyToNull(): Collection<T>? {
|
||||||
|
return this.ifEmpty { null }
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getMangaUrl(manga: SManga) = "$baseUrl/g/${manga.url}"
|
||||||
|
|
||||||
|
// Chapter
|
||||||
|
|
||||||
|
override fun chapterListRequest(manga: SManga): Request {
|
||||||
|
return GET("$apiBooksUrl/detail/${manga.url}", headers)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun chapterListParse(response: Response): List<SChapter> {
|
||||||
|
val manga = response.parseAs<MangaEntry>()
|
||||||
|
return listOf(
|
||||||
|
SChapter.create().apply {
|
||||||
|
name = "Chapter"
|
||||||
|
url = "${manga.id}/${manga.public_key}"
|
||||||
|
date_upload = (manga.updated_at ?: manga.created_at)
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getChapterUrl(chapter: SChapter) = "$baseUrl/g/${chapter.url}"
|
||||||
|
|
||||||
|
// Page List
|
||||||
|
|
||||||
|
override fun pageListRequest(chapter: SChapter): Request {
|
||||||
|
return GET("$apiBooksUrl/detail/${chapter.url}", headers)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun pageListParse(response: Response): List<Page> {
|
||||||
|
val mangaEntry = response.parseAs<MangaEntry>()
|
||||||
|
val imagesInfo = getImagesByMangaEntry(mangaEntry)
|
||||||
|
|
||||||
|
return imagesInfo.entries.mapIndexed { index, image ->
|
||||||
|
Page(index, imageUrl = "${imagesInfo.base}/${image.path}")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun imageUrlParse(response: Response) = throw UnsupportedOperationException()
|
||||||
|
|
||||||
|
// Settings
|
||||||
|
|
||||||
|
override fun setupPreferenceScreen(screen: PreferenceScreen) {
|
||||||
|
ListPreference(screen.context).apply {
|
||||||
|
key = PREF_IMAGERES
|
||||||
|
title = "Image Resolution"
|
||||||
|
entries = arrayOf("780x", "980x", "1280x", "1600x", "Original")
|
||||||
|
entryValues = arrayOf("780", "980", "1280", "1600", "0")
|
||||||
|
summary = "%s"
|
||||||
|
setDefaultValue("1280")
|
||||||
|
}.also(screen::addPreference)
|
||||||
|
}
|
||||||
|
|
||||||
|
private inline fun <reified T> Response.parseAs(): T {
|
||||||
|
return json.decodeFromString(body.string())
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
const val PREFIX_ID_KEY_SEARCH = "id:"
|
||||||
|
private const val PREF_IMAGERES = "pref_image_quality"
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,75 @@
|
||||||
|
package eu.kanade.tachiyomi.extension.en.koharu
|
||||||
|
|
||||||
|
import kotlinx.serialization.Serializable
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
class Tag(
|
||||||
|
var name: String,
|
||||||
|
var namespace: Int = 0,
|
||||||
|
)
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
class Books(
|
||||||
|
val entries: List<Entry> = emptyList(),
|
||||||
|
val total: Int = 0,
|
||||||
|
val limit: Int = 0,
|
||||||
|
val page: Int,
|
||||||
|
)
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
class Entry(
|
||||||
|
val id: Int,
|
||||||
|
val public_key: String,
|
||||||
|
val title: String,
|
||||||
|
val thumbnail: Thumbnail,
|
||||||
|
)
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
class MangaEntry(
|
||||||
|
val id: Int,
|
||||||
|
val title: String,
|
||||||
|
val public_key: String,
|
||||||
|
val created_at: Long = 0L,
|
||||||
|
val updated_at: Long?,
|
||||||
|
val thumbnails: Thumbnails,
|
||||||
|
val tags: List<Tag> = emptyList(),
|
||||||
|
val data: Data,
|
||||||
|
)
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
class Thumbnails(
|
||||||
|
val base: String,
|
||||||
|
val main: Thumbnail,
|
||||||
|
val entries: List<Thumbnail>,
|
||||||
|
)
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
class Thumbnail(
|
||||||
|
val path: String,
|
||||||
|
)
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
class Data(
|
||||||
|
val `0`: DataKey,
|
||||||
|
val `780`: DataKey? = null,
|
||||||
|
val `980`: DataKey? = null,
|
||||||
|
val `1280`: DataKey? = null,
|
||||||
|
val `1600`: DataKey? = null,
|
||||||
|
)
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
class DataKey(
|
||||||
|
val id: Int,
|
||||||
|
val public_key: String,
|
||||||
|
)
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
class ImagesInfo(
|
||||||
|
val base: String,
|
||||||
|
val entries: List<ImagePath>,
|
||||||
|
)
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
class ImagePath(
|
||||||
|
val path: String,
|
||||||
|
)
|
|
@ -0,0 +1,51 @@
|
||||||
|
package eu.kanade.tachiyomi.extension.en.koharu
|
||||||
|
|
||||||
|
import eu.kanade.tachiyomi.source.model.Filter
|
||||||
|
import eu.kanade.tachiyomi.source.model.FilterList
|
||||||
|
|
||||||
|
fun getFilters(): FilterList {
|
||||||
|
return FilterList(
|
||||||
|
SortFilter("Sort by", getSortsList),
|
||||||
|
CategoryFilter("Category"),
|
||||||
|
Filter.Separator(),
|
||||||
|
Filter.Header("Separate tags with commas (,)"),
|
||||||
|
Filter.Header("Prepend with dash (-) to exclude"),
|
||||||
|
TextFilter("Artists", "artist"),
|
||||||
|
TextFilter("Magazines", "magazine"),
|
||||||
|
TextFilter("Publishers", "publisher"),
|
||||||
|
TextFilter("Characters", "character"),
|
||||||
|
TextFilter("Cosplayers", "cosplayer"),
|
||||||
|
TextFilter("Parodies", "parody"),
|
||||||
|
TextFilter("Circles", "circle"),
|
||||||
|
TextFilter("Male Tags", "male"),
|
||||||
|
TextFilter("Female Tags", "female"),
|
||||||
|
TextFilter("Tags ( Universal )", "tag"),
|
||||||
|
Filter.Header("Filter by pages, for example: (>20)"),
|
||||||
|
TextFilter("Pages", "pages"),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
internal open class TextFilter(name: String, val type: String) : Filter.Text(name)
|
||||||
|
internal open class SortFilter(name: String, private val vals: List<Pair<String, String>>, state: Int = 0) :
|
||||||
|
Filter.Select<String>(name, vals.map { it.first }.toTypedArray(), state) {
|
||||||
|
fun getValue() = vals[state].second
|
||||||
|
}
|
||||||
|
|
||||||
|
internal class CategoryFilter(name: String) :
|
||||||
|
Filter.Group<CheckBoxFilter>(
|
||||||
|
name,
|
||||||
|
listOf(
|
||||||
|
Pair("Manga", 2),
|
||||||
|
Pair("Doujinshi", 4),
|
||||||
|
Pair("Illustration", 8),
|
||||||
|
).map { CheckBoxFilter(it.first, it.second, true) },
|
||||||
|
)
|
||||||
|
internal open class CheckBoxFilter(name: String, val value: Int, state: Boolean) : Filter.CheckBox(name, state)
|
||||||
|
|
||||||
|
private val getSortsList: List<Pair<String, String>> = listOf(
|
||||||
|
Pair("Title", "1"),
|
||||||
|
Pair("Pages", "2"),
|
||||||
|
Pair("Recently Posted", ""),
|
||||||
|
Pair("Most Viewed", "6"),
|
||||||
|
Pair("Most Favorited", "8"),
|
||||||
|
)
|
|
@ -0,0 +1,34 @@
|
||||||
|
package eu.kanade.tachiyomi.extension.en.koharu
|
||||||
|
|
||||||
|
import android.app.Activity
|
||||||
|
import android.content.ActivityNotFoundException
|
||||||
|
import android.content.Intent
|
||||||
|
import android.os.Bundle
|
||||||
|
import android.util.Log
|
||||||
|
import kotlin.system.exitProcess
|
||||||
|
|
||||||
|
class KoharuUrlActivity : Activity() {
|
||||||
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
|
super.onCreate(savedInstanceState)
|
||||||
|
val pathSegments = intent?.data?.pathSegments
|
||||||
|
if (pathSegments != null && pathSegments.size > 2) {
|
||||||
|
val id = "${pathSegments[1]}/${pathSegments[2]}"
|
||||||
|
val mainIntent = Intent().apply {
|
||||||
|
action = "eu.kanade.tachiyomi.SEARCH"
|
||||||
|
putExtra("query", "${Koharu.PREFIX_ID_KEY_SEARCH}$id")
|
||||||
|
putExtra("filter", packageName)
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
startActivity(mainIntent)
|
||||||
|
} catch (e: ActivityNotFoundException) {
|
||||||
|
Log.e("KoharuUrlActivity", "Could not start activity", e)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Log.e("KoharuUrlActivity", "Could not parse URI from intent $intent")
|
||||||
|
}
|
||||||
|
|
||||||
|
finish()
|
||||||
|
exitProcess(0)
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue