mirror of
https://github.com/mihonapp/mihon.git
synced 2025-11-05 00:28:56 +01:00
add tristate library filters, update to dev
This commit is contained in:
@@ -60,7 +60,7 @@ class CoverCache(private val context: Context) {
|
||||
return false
|
||||
|
||||
// Remove file.
|
||||
val file = getCoverFile(thumbnailUrl!!)
|
||||
val file = getCoverFile(thumbnailUrl)
|
||||
return file.exists() && file.delete()
|
||||
}
|
||||
|
||||
|
||||
@@ -28,7 +28,9 @@ class DownloadProvider(private val context: Context) {
|
||||
* The root directory for downloads.
|
||||
*/
|
||||
private var downloadsDir = preferences.downloadsDirectory().getOrDefault().let {
|
||||
UniFile.fromUri(context, Uri.parse(it))
|
||||
val dir = UniFile.fromUri(context, Uri.parse(it))
|
||||
DiskUtil.createNoMediaFile(dir, context)
|
||||
dir
|
||||
}
|
||||
|
||||
init {
|
||||
|
||||
@@ -132,7 +132,7 @@ class DownloadService : Service() {
|
||||
.subscribeOn(Schedulers.io())
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribe({ state -> onNetworkStateChanged(state)
|
||||
}, { _ ->
|
||||
}, {
|
||||
toast(R.string.download_queue_error)
|
||||
stopSelf()
|
||||
})
|
||||
|
||||
@@ -427,6 +427,8 @@ class Downloader(
|
||||
if (download.status == Download.DOWNLOADED) {
|
||||
tmpDir.renameTo(dirname)
|
||||
cache.addChapter(dirname, mangaDir, download.manga)
|
||||
|
||||
DiskUtil.createNoMediaFile(tmpDir, context)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -150,11 +150,12 @@ class PreferencesHelper(val context: Context) {
|
||||
|
||||
fun downloadBadge() = rxPrefs.getBoolean(Keys.downloadBadge, false)
|
||||
|
||||
fun filterDownloaded() = rxPrefs.getBoolean(Keys.filterDownloaded, false)
|
||||
// J2K converted from boolean to integer
|
||||
fun filterDownloaded() = rxPrefs.getInteger(Keys.filterDownloaded, 0)
|
||||
|
||||
fun filterUnread() = rxPrefs.getBoolean(Keys.filterUnread, false)
|
||||
fun filterUnread() = rxPrefs.getInteger(Keys.filterUnread, 0)
|
||||
|
||||
fun filterCompleted() = rxPrefs.getBoolean(Keys.filterCompleted, false)
|
||||
fun filterCompleted() = rxPrefs.getInteger(Keys.filterCompleted, 0)
|
||||
|
||||
fun librarySortingMode() = rxPrefs.getInteger(Keys.librarySortingMode, 0)
|
||||
|
||||
@@ -178,6 +179,20 @@ class PreferencesHelper(val context: Context) {
|
||||
|
||||
fun trustedSignatures() = rxPrefs.getStringSet("trusted_signatures", emptySet())
|
||||
|
||||
// --> AZ J2K CHERRYPICKING
|
||||
|
||||
fun upgradeFilters() {
|
||||
val filterDl = rxPrefs.getBoolean(Keys.filterDownloaded, false).getOrDefault()
|
||||
val filterUn = rxPrefs.getBoolean(Keys.filterUnread, false).getOrDefault()
|
||||
val filterCm = rxPrefs.getBoolean(Keys.filterCompleted, false).getOrDefault()
|
||||
filterDownloaded().set(if (filterDl) 1 else 0)
|
||||
filterUnread().set(if (filterUn) 1 else 0)
|
||||
filterCompleted().set(if (filterCm) 1 else 0)
|
||||
}
|
||||
|
||||
// <--
|
||||
|
||||
|
||||
// --> EH
|
||||
fun enableExhentai() = rxPrefs.getBoolean(Keys.eh_enableExHentai, false)
|
||||
|
||||
|
||||
@@ -45,11 +45,11 @@ class SharedPreferencesDataStore(private val prefs: SharedPreferences) : Prefere
|
||||
prefs.edit().putString(key, value).apply()
|
||||
}
|
||||
|
||||
override fun getStringSet(key: String?, defValues: MutableSet<String>?): MutableSet<String> {
|
||||
override fun getStringSet(key: String?, defValues: MutableSet<String>?): MutableSet<String>? {
|
||||
return prefs.getStringSet(key, defValues)
|
||||
}
|
||||
|
||||
override fun putStringSet(key: String?, values: MutableSet<String>?) {
|
||||
prefs.edit().putStringSet(key, values).apply()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,13 +21,15 @@ class AnilistApi(val client: OkHttpClient, interceptor: AnilistInterceptor) {
|
||||
private val jsonMime = MediaType.parse("application/json; charset=utf-8")
|
||||
private val authClient = client.newBuilder().addInterceptor(interceptor).build()
|
||||
|
||||
|
||||
fun addLibManga(track: Track): Observable<Track> {
|
||||
val query = """
|
||||
mutation AddManga(${'$'}mangaId: Int, ${'$'}progress: Int, ${'$'}status: MediaListStatus) {
|
||||
SaveMediaListEntry (mediaId: ${'$'}mangaId, progress: ${'$'}progress, status: ${'$'}status)
|
||||
{ id status } }
|
||||
"""
|
||||
|mutation AddManga(${'$'}mangaId: Int, ${'$'}progress: Int, ${'$'}status: MediaListStatus) {
|
||||
|SaveMediaListEntry (mediaId: ${'$'}mangaId, progress: ${'$'}progress, status: ${'$'}status) {
|
||||
| id
|
||||
| status
|
||||
|}
|
||||
|}
|
||||
|""".trimMargin()
|
||||
val variables = jsonObject(
|
||||
"mangaId" to track.media_id,
|
||||
"progress" to track.last_chapter_read,
|
||||
@@ -58,14 +60,14 @@ class AnilistApi(val client: OkHttpClient, interceptor: AnilistInterceptor) {
|
||||
|
||||
fun updateLibManga(track: Track): Observable<Track> {
|
||||
val query = """
|
||||
mutation UpdateManga(${'$'}listId: Int, ${'$'}progress: Int, ${'$'}status: MediaListStatus, ${'$'}score: Int) {
|
||||
SaveMediaListEntry (id: ${'$'}listId, progress: ${'$'}progress, status: ${'$'}status, scoreRaw: ${'$'}score) {
|
||||
id
|
||||
status
|
||||
progress
|
||||
}
|
||||
}
|
||||
"""
|
||||
|mutation UpdateManga(${'$'}listId: Int, ${'$'}progress: Int, ${'$'}status: MediaListStatus, ${'$'}score: Int) {
|
||||
|SaveMediaListEntry (id: ${'$'}listId, progress: ${'$'}progress, status: ${'$'}status, scoreRaw: ${'$'}score) {
|
||||
|id
|
||||
|status
|
||||
|progress
|
||||
|}
|
||||
|}
|
||||
|""".trimMargin()
|
||||
val variables = jsonObject(
|
||||
"listId" to track.library_id,
|
||||
"progress" to track.last_chapter_read,
|
||||
@@ -90,29 +92,29 @@ class AnilistApi(val client: OkHttpClient, interceptor: AnilistInterceptor) {
|
||||
|
||||
fun search(search: String): Observable<List<TrackSearch>> {
|
||||
val query = """
|
||||
query Search(${'$'}query: String) {
|
||||
Page (perPage: 50) {
|
||||
media(search: ${'$'}query, type: MANGA, format_not_in: [NOVEL]) {
|
||||
id
|
||||
title {
|
||||
romaji
|
||||
}
|
||||
coverImage {
|
||||
large
|
||||
}
|
||||
type
|
||||
status
|
||||
chapters
|
||||
description
|
||||
startDate {
|
||||
year
|
||||
month
|
||||
day
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
"""
|
||||
|query Search(${'$'}query: String) {
|
||||
|Page (perPage: 50) {
|
||||
|media(search: ${'$'}query, type: MANGA, format_not_in: [NOVEL]) {
|
||||
|id
|
||||
|title {
|
||||
|romaji
|
||||
|}
|
||||
|coverImage {
|
||||
|large
|
||||
|}
|
||||
|type
|
||||
|status
|
||||
|chapters
|
||||
|description
|
||||
|startDate {
|
||||
|year
|
||||
|month
|
||||
|day
|
||||
|}
|
||||
|}
|
||||
|}
|
||||
|}
|
||||
|""".trimMargin()
|
||||
val variables = jsonObject(
|
||||
"query" to search
|
||||
)
|
||||
@@ -142,37 +144,37 @@ class AnilistApi(val client: OkHttpClient, interceptor: AnilistInterceptor) {
|
||||
}
|
||||
|
||||
|
||||
fun findLibManga(track: Track, userid: Int) : Observable<Track?> {
|
||||
fun findLibManga(track: Track, userid: Int): Observable<Track?> {
|
||||
val query = """
|
||||
query (${'$'}id: Int!, ${'$'}manga_id: Int!) {
|
||||
Page {
|
||||
mediaList(userId: ${'$'}id, type: MANGA, mediaId: ${'$'}manga_id) {
|
||||
id
|
||||
status
|
||||
scoreRaw: score(format: POINT_100)
|
||||
progress
|
||||
media{
|
||||
id
|
||||
title {
|
||||
romaji
|
||||
}
|
||||
coverImage {
|
||||
large
|
||||
}
|
||||
type
|
||||
status
|
||||
chapters
|
||||
description
|
||||
startDate {
|
||||
year
|
||||
month
|
||||
day
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
"""
|
||||
|query (${'$'}id: Int!, ${'$'}manga_id: Int!) {
|
||||
|Page {
|
||||
|mediaList(userId: ${'$'}id, type: MANGA, mediaId: ${'$'}manga_id) {
|
||||
|id
|
||||
|status
|
||||
|scoreRaw: score(format: POINT_100)
|
||||
|progress
|
||||
|media {
|
||||
|id
|
||||
|title {
|
||||
|romaji
|
||||
|}
|
||||
|coverImage {
|
||||
|large
|
||||
|}
|
||||
|type
|
||||
|status
|
||||
|chapters
|
||||
|description
|
||||
|startDate {
|
||||
|year
|
||||
|month
|
||||
|day
|
||||
|}
|
||||
|}
|
||||
|}
|
||||
|}
|
||||
|}
|
||||
|""".trimMargin()
|
||||
val variables = jsonObject(
|
||||
"id" to userid,
|
||||
"manga_id" to track.media_id
|
||||
@@ -214,16 +216,15 @@ class AnilistApi(val client: OkHttpClient, interceptor: AnilistInterceptor) {
|
||||
|
||||
fun getCurrentUser(): Observable<Pair<Int, String>> {
|
||||
val query = """
|
||||
query User
|
||||
{
|
||||
Viewer {
|
||||
id
|
||||
mediaListOptions {
|
||||
scoreFormat
|
||||
}
|
||||
}
|
||||
}
|
||||
"""
|
||||
|query User {
|
||||
|Viewer {
|
||||
|id
|
||||
|mediaListOptions {
|
||||
|scoreFormat
|
||||
|}
|
||||
|}
|
||||
|}
|
||||
|""".trimMargin()
|
||||
val payload = jsonObject(
|
||||
"query" to query
|
||||
)
|
||||
@@ -246,7 +247,7 @@ class AnilistApi(val client: OkHttpClient, interceptor: AnilistInterceptor) {
|
||||
}
|
||||
}
|
||||
|
||||
fun jsonToALManga(struct: JsonObject): ALManga{
|
||||
private fun jsonToALManga(struct: JsonObject): ALManga {
|
||||
val date = try {
|
||||
val date = Calendar.getInstance()
|
||||
date.set(struct["startDate"]["year"].nullInt ?: 0, (struct["startDate"]["month"].nullInt ?: 0) - 1,
|
||||
@@ -261,11 +262,10 @@ class AnilistApi(val client: OkHttpClient, interceptor: AnilistInterceptor) {
|
||||
date, struct["chapters"].nullInt ?: 0)
|
||||
}
|
||||
|
||||
fun jsonToALUserManga(struct: JsonObject): ALUserManga{
|
||||
return ALUserManga(struct["id"].asLong, struct["status"].asString, struct["scoreRaw"].asInt, struct["progress"].asInt, jsonToALManga(struct["media"].obj) )
|
||||
private fun jsonToALUserManga(struct: JsonObject): ALUserManga {
|
||||
return ALUserManga(struct["id"].asLong, struct["status"].asString, struct["scoreRaw"].asInt, struct["progress"].asInt, jsonToALManga(struct["media"].obj))
|
||||
}
|
||||
|
||||
|
||||
companion object {
|
||||
private const val clientId = "385"
|
||||
private const val clientUrl = "tachiyomi://anilist-auth"
|
||||
|
||||
@@ -18,13 +18,12 @@ class KitsuSearchManga(obj: JsonObject) {
|
||||
private val synopsis by obj.byString
|
||||
private var startDate = obj.get("startDate").nullString?.let {
|
||||
val outputDf = SimpleDateFormat("yyyy-MM-dd", Locale.US)
|
||||
outputDf.format(Date(it!!.toLong() * 1000))
|
||||
outputDf.format(Date(it.toLong() * 1000))
|
||||
}
|
||||
private val endDate = obj.get("endDate").nullString
|
||||
|
||||
|
||||
@CallSuper
|
||||
open fun toTrack() = TrackSearch.create(TrackManager.KITSU).apply {
|
||||
fun toTrack() = TrackSearch.create(TrackManager.KITSU).apply {
|
||||
media_id = this@KitsuSearchManga.id
|
||||
title = canonicalTitle
|
||||
total_chapters = chapterCount ?: 0
|
||||
@@ -55,7 +54,7 @@ class KitsuLibManga(obj: JsonObject, manga: JsonObject) {
|
||||
private val ratingTwenty = obj["attributes"].obj.get("ratingTwenty").nullString
|
||||
val progress by obj["attributes"].byInt
|
||||
|
||||
open fun toTrack() = TrackSearch.create(TrackManager.KITSU).apply {
|
||||
fun toTrack() = TrackSearch.create(TrackManager.KITSU).apply {
|
||||
media_id = libraryId
|
||||
title = canonicalTitle
|
||||
total_chapters = chapterCount ?: 0
|
||||
|
||||
@@ -112,11 +112,7 @@ class Myanimelist(private val context: Context, id: Int) : TrackService(id) {
|
||||
.toCompletable()
|
||||
}
|
||||
|
||||
// Attempt to login again if cookies have been cleared but credentials are still filled
|
||||
fun ensureLoggedIn() {
|
||||
if (isAuthorized) return
|
||||
if (!isLogged) throw Exception("MAL Login Credentials not found")
|
||||
|
||||
fun refreshLogin() {
|
||||
val username = getUsername()
|
||||
val password = getPassword()
|
||||
logout()
|
||||
@@ -131,6 +127,14 @@ class Myanimelist(private val context: Context, id: Int) : TrackService(id) {
|
||||
}
|
||||
}
|
||||
|
||||
// Attempt to login again if cookies have been cleared but credentials are still filled
|
||||
fun ensureLoggedIn() {
|
||||
if (isAuthorized) return
|
||||
if (!isLogged) throw Exception("MAL Login Credentials not found")
|
||||
|
||||
refreshLogin()
|
||||
}
|
||||
|
||||
override fun logout() {
|
||||
super.logout()
|
||||
preferences.trackToken(this).delete()
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package eu.kanade.tachiyomi.data.track.myanimelist
|
||||
|
||||
import okhttp3.Interceptor
|
||||
import okhttp3.Request
|
||||
import okhttp3.RequestBody
|
||||
import okhttp3.Response
|
||||
import okio.Buffer
|
||||
@@ -11,18 +12,27 @@ class MyAnimeListInterceptor(private val myanimelist: Myanimelist): Interceptor
|
||||
override fun intercept(chain: Interceptor.Chain): Response {
|
||||
myanimelist.ensureLoggedIn()
|
||||
|
||||
var request = chain.request()
|
||||
request.body()?.let {
|
||||
val request = chain.request()
|
||||
var response = chain.proceed(updateRequest(request))
|
||||
|
||||
if (response.code() == 400){
|
||||
myanimelist.refreshLogin()
|
||||
response = chain.proceed(updateRequest(request))
|
||||
}
|
||||
|
||||
return response
|
||||
}
|
||||
|
||||
private fun updateRequest(request: Request): Request {
|
||||
return request.body()?.let {
|
||||
val contentType = it.contentType().toString()
|
||||
val updatedBody = when {
|
||||
contentType.contains("x-www-form-urlencoded") -> updateFormBody(it)
|
||||
contentType.contains("json") -> updateJsonBody(it)
|
||||
else -> it
|
||||
}
|
||||
request = request.newBuilder().post(updatedBody).build()
|
||||
}
|
||||
|
||||
return chain.proceed(request)
|
||||
request.newBuilder().post(updatedBody).build()
|
||||
} ?: request
|
||||
}
|
||||
|
||||
private fun bodyToString(requestBody: RequestBody): String {
|
||||
|
||||
@@ -39,7 +39,7 @@ class ExtensionInstallActivity : Activity() {
|
||||
}
|
||||
|
||||
private fun checkInstallationResult(resultCode: Int) {
|
||||
val downloadId = intent.extras.getLong(ExtensionInstaller.EXTRA_DOWNLOAD_ID)
|
||||
val downloadId = intent.extras!!.getLong(ExtensionInstaller.EXTRA_DOWNLOAD_ID)
|
||||
val success = resultCode == RESULT_OK
|
||||
|
||||
val extensionManager = Injekt.get<ExtensionManager>()
|
||||
|
||||
@@ -7,8 +7,11 @@ import android.content.IntentFilter
|
||||
import eu.kanade.tachiyomi.extension.model.Extension
|
||||
import eu.kanade.tachiyomi.extension.model.LoadResult
|
||||
import eu.kanade.tachiyomi.util.launchNow
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.withContext
|
||||
import kotlinx.coroutines.CoroutineStart
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.GlobalScope
|
||||
import kotlinx.coroutines.async
|
||||
|
||||
/**
|
||||
* Broadcast receiver that listens for the system's packages installed, updated or removed, and only
|
||||
|
||||
@@ -121,7 +121,7 @@ internal object ExtensionLoader {
|
||||
|
||||
val classLoader = PathClassLoader(appInfo.sourceDir, null, context.classLoader)
|
||||
|
||||
val sources = appInfo.metaData.getString(METADATA_SOURCE_CLASS)
|
||||
val sources = appInfo.metaData.getString(METADATA_SOURCE_CLASS)!!
|
||||
.split(";")
|
||||
.map {
|
||||
val sourceClass = it.trim()
|
||||
|
||||
@@ -2,20 +2,24 @@ package eu.kanade.tachiyomi.source
|
||||
|
||||
import android.content.Context
|
||||
import eu.kanade.tachiyomi.R
|
||||
import eu.kanade.tachiyomi.source.model.*
|
||||
import eu.kanade.tachiyomi.source.model.Filter
|
||||
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.util.ChapterRecognition
|
||||
import eu.kanade.tachiyomi.util.ComparatorUtil.CaseInsensitiveNaturalComparator
|
||||
import eu.kanade.tachiyomi.util.DiskUtil
|
||||
import eu.kanade.tachiyomi.util.EpubFile
|
||||
import eu.kanade.tachiyomi.util.ImageUtil
|
||||
import junrar.Archive
|
||||
import junrar.rarfile.FileHeader
|
||||
import net.greypanther.natsort.CaseInsensitiveSimpleNaturalComparator
|
||||
import rx.Observable
|
||||
import timber.log.Timber
|
||||
import java.io.File
|
||||
import java.io.FileInputStream
|
||||
import java.io.InputStream
|
||||
import java.util.Comparator
|
||||
import java.util.Locale
|
||||
import java.util.concurrent.TimeUnit
|
||||
import java.util.zip.ZipEntry
|
||||
@@ -125,7 +129,6 @@ class LocalSource(private val context: Context) : CatalogueSource {
|
||||
override fun fetchMangaDetails(manga: SManga) = Observable.just(manga)
|
||||
|
||||
override fun fetchChapterList(manga: SManga): Observable<List<SChapter>> {
|
||||
val comparator = CaseInsensitiveSimpleNaturalComparator.getInstance<String>()
|
||||
val chapters = getBaseDirectories(context)
|
||||
.mapNotNull { File(it, manga.url).listFiles()?.toList() }
|
||||
.flatten()
|
||||
@@ -146,7 +149,7 @@ class LocalSource(private val context: Context) : CatalogueSource {
|
||||
}
|
||||
.sortedWith(Comparator { c1, c2 ->
|
||||
val c = c2.chapter_number.compareTo(c1.chapter_number)
|
||||
if (c == 0) comparator.compare(c2.name, c1.name) else c
|
||||
if (c == 0) CaseInsensitiveNaturalComparator.compare(c2.name, c1.name) else c
|
||||
})
|
||||
|
||||
return Observable.just(chapters)
|
||||
@@ -189,20 +192,19 @@ class LocalSource(private val context: Context) : CatalogueSource {
|
||||
|
||||
private fun updateCover(chapter: SChapter, manga: SManga): File? {
|
||||
val format = getFormat(chapter)
|
||||
val comparator = CaseInsensitiveSimpleNaturalComparator.getInstance<String>()
|
||||
return when (format) {
|
||||
is Format.Directory -> {
|
||||
val entry = format.file.listFiles()
|
||||
.sortedWith(Comparator<File> { f1, f2 -> comparator.compare(f1.name, f2.name) })
|
||||
.find { !it.isDirectory && ImageUtil.isImage(it.name, { FileInputStream(it) }) }
|
||||
.sortedWith(Comparator<File> { f1, f2 -> CaseInsensitiveNaturalComparator.compare(f1.name, f2.name) })
|
||||
.find { !it.isDirectory && ImageUtil.isImage(it.name) { FileInputStream(it) } }
|
||||
|
||||
entry?.let { updateCover(context, manga, it.inputStream())}
|
||||
}
|
||||
is Format.Zip -> {
|
||||
ZipFile(format.file).use { zip ->
|
||||
val entry = zip.entries().toList()
|
||||
.sortedWith(Comparator<ZipEntry> { f1, f2 -> comparator.compare(f1.name, f2.name) })
|
||||
.find { !it.isDirectory && ImageUtil.isImage(it.name, { zip.getInputStream(it) }) }
|
||||
.sortedWith(Comparator<ZipEntry> { f1, f2 -> CaseInsensitiveNaturalComparator.compare(f1.name, f2.name) })
|
||||
.find { !it.isDirectory && ImageUtil.isImage(it.name) { zip.getInputStream(it) } }
|
||||
|
||||
entry?.let { updateCover(context, manga, zip.getInputStream(it) )}
|
||||
}
|
||||
@@ -210,8 +212,8 @@ class LocalSource(private val context: Context) : CatalogueSource {
|
||||
is Format.Rar -> {
|
||||
Archive(format.file).use { archive ->
|
||||
val entry = archive.fileHeaders
|
||||
.sortedWith(Comparator<FileHeader> { f1, f2 -> comparator.compare(f1.fileNameString, f2.fileNameString) })
|
||||
.find { !it.isDirectory && ImageUtil.isImage(it.fileNameString, { archive.getInputStream(it) }) }
|
||||
.sortedWith(Comparator<FileHeader> { f1, f2 -> CaseInsensitiveNaturalComparator.compare(f1.fileNameString, f2.fileNameString) })
|
||||
.find { !it.isDirectory && ImageUtil.isImage(it.fileNameString) { archive.getInputStream(it) } }
|
||||
|
||||
entry?.let { updateCover(context, manga, archive.getInputStream(it) )}
|
||||
}
|
||||
|
||||
@@ -13,7 +13,7 @@ class SourceDividerItemDecoration(context: Context) : RecyclerView.ItemDecoratio
|
||||
|
||||
init {
|
||||
val a = context.obtainStyledAttributes(intArrayOf(android.R.attr.listDivider))
|
||||
divider = a.getDrawable(0)
|
||||
divider = a.getDrawable(0)!!
|
||||
a.recycle()
|
||||
}
|
||||
|
||||
|
||||
@@ -271,6 +271,7 @@ open class BrowseCatalogueController(bundle: Bundle) :
|
||||
RecyclerView(view.context).apply {
|
||||
id = R.id.recycler
|
||||
layoutManager = LinearLayoutManager(context)
|
||||
layoutParams = RecyclerView.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT)
|
||||
addItemDecoration(DividerItemDecoration(context, DividerItemDecoration.VERTICAL))
|
||||
}
|
||||
} else {
|
||||
@@ -441,19 +442,22 @@ open class BrowseCatalogueController(bundle: Bundle) :
|
||||
adapter.onLoadMoreComplete(null)
|
||||
hideProgressBar()
|
||||
|
||||
val message = if (error is NoResultsException) "No results found" else (error.message ?: "")
|
||||
|
||||
snack?.dismiss()
|
||||
snack = catalogue_view?.snack(message, Snackbar.LENGTH_INDEFINITE) {
|
||||
setAction(R.string.action_retry) {
|
||||
// If not the first page, show bottom progress bar.
|
||||
if (adapter.mainItemCount > 0) {
|
||||
val item = progressItem ?: return@setAction
|
||||
adapter.addScrollableFooterWithDelay(item, 0, true)
|
||||
} else {
|
||||
showProgressBar()
|
||||
|
||||
if (catalogue_view != null) {
|
||||
val message = if (error is NoResultsException) catalogue_view.context.getString(R.string.no_results_found) else (error.message ?: "")
|
||||
|
||||
snack = catalogue_view.snack(message, Snackbar.LENGTH_INDEFINITE) {
|
||||
setAction(R.string.action_retry) {
|
||||
// If not the first page, show bottom progress bar.
|
||||
if (adapter.mainItemCount > 0) {
|
||||
val item = progressItem ?: return@setAction
|
||||
adapter.addScrollableFooterWithDelay(item, 0, true)
|
||||
} else {
|
||||
showProgressBar()
|
||||
}
|
||||
presenter.requestNext()
|
||||
}
|
||||
presenter.requestNext()
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -602,19 +606,21 @@ open class BrowseCatalogueController(bundle: Bundle) :
|
||||
adapter?.notifyItemChanged(position)
|
||||
|
||||
val categories = presenter.getCategories()
|
||||
val defaultCategory = categories.find { it.id == preferences.defaultCategory() }
|
||||
if (defaultCategory != null) {
|
||||
presenter.moveMangaToCategory(manga, defaultCategory)
|
||||
} else if (categories.size <= 1) { // default or the one from the user
|
||||
presenter.moveMangaToCategory(manga, categories.firstOrNull())
|
||||
} else {
|
||||
val ids = presenter.getMangaCategoryIds(manga)
|
||||
val preselected = ids.mapNotNull { id ->
|
||||
categories.indexOfFirst { it.id == id }.takeIf { it != -1 }
|
||||
}.toTypedArray()
|
||||
val defaultCategoryId = preferences.defaultCategory()
|
||||
val defaultCategory = categories.find { it.id == defaultCategoryId }
|
||||
when {
|
||||
defaultCategory != null -> presenter.moveMangaToCategory(manga, defaultCategory)
|
||||
defaultCategoryId == 0 || categories.isEmpty() -> // 'Default' or no category
|
||||
presenter.moveMangaToCategory(manga, null)
|
||||
else -> {
|
||||
val ids = presenter.getMangaCategoryIds(manga)
|
||||
val preselected = ids.mapNotNull { id ->
|
||||
categories.indexOfFirst { it.id == id }.takeIf { it != -1 }
|
||||
}.toTypedArray()
|
||||
|
||||
ChangeMangaCategoriesDialog(this, listOf(manga), categories, preselected)
|
||||
.showDialog(router)
|
||||
ChangeMangaCategoriesDialog(this, listOf(manga), categories, preselected)
|
||||
.showDialog(router)
|
||||
}
|
||||
}
|
||||
activity?.toast(activity?.getString(R.string.manga_added_library))
|
||||
}
|
||||
|
||||
@@ -328,9 +328,9 @@ open class BrowseCataloguePresenter(
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the default, and user categories.
|
||||
* Get user categories.
|
||||
*
|
||||
* @return List of categories, default plus user categories
|
||||
* @return List of categories, not including the default category
|
||||
*/
|
||||
fun getCategories(): List<Category> {
|
||||
return db.getCategories().executeAsBlocking()
|
||||
|
||||
@@ -86,4 +86,4 @@ class CatalogueNavigationView @JvmOverloads constructor(context: Context, attrs:
|
||||
}
|
||||
// EXH <--
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -44,7 +44,7 @@ class CatalogueSearchAdapter(val controller: CatalogueSearchController) :
|
||||
|
||||
override fun onRestoreInstanceState(savedInstanceState: Bundle) {
|
||||
super.onRestoreInstanceState(savedInstanceState)
|
||||
bundle = savedInstanceState.getBundle(HOLDER_BUNDLE_KEY)
|
||||
bundle = savedInstanceState.getBundle(HOLDER_BUNDLE_KEY)!!
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -81,4 +81,4 @@ class CatalogueSearchAdapter(val controller: CatalogueSearchController) :
|
||||
private companion object {
|
||||
const val HOLDER_BUNDLE_KEY = "holder_bundle"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,10 +1,14 @@
|
||||
package eu.kanade.tachiyomi.ui.extension
|
||||
|
||||
import android.support.v7.widget.LinearLayoutManager
|
||||
import android.support.v7.widget.SearchView
|
||||
import android.view.LayoutInflater
|
||||
import android.view.Menu
|
||||
import android.view.MenuInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import com.jakewharton.rxbinding.support.v4.widget.refreshes
|
||||
import com.jakewharton.rxbinding.support.v7.widget.queryTextChanges
|
||||
import eu.davidea.flexibleadapter.FlexibleAdapter
|
||||
import eu.davidea.flexibleadapter.items.IFlexible
|
||||
import eu.kanade.tachiyomi.R
|
||||
@@ -28,6 +32,10 @@ open class ExtensionController : NucleusController<ExtensionPresenter>(),
|
||||
*/
|
||||
private var adapter: FlexibleAdapter<IFlexible<*>>? = null
|
||||
|
||||
private var extensions: List<ExtensionItem> = emptyList()
|
||||
|
||||
private var query = ""
|
||||
|
||||
init {
|
||||
setHasOptionsMenu(true)
|
||||
}
|
||||
@@ -84,7 +92,32 @@ open class ExtensionController : NucleusController<ExtensionPresenter>(),
|
||||
}
|
||||
}
|
||||
|
||||
override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) {
|
||||
inflater.inflate(R.menu.extension_main, menu)
|
||||
|
||||
val searchItem = menu.findItem(R.id.action_search)
|
||||
val searchView = searchItem.actionView as SearchView
|
||||
searchView.maxWidth = Int.MAX_VALUE
|
||||
|
||||
if (!query.isEmpty()) {
|
||||
searchItem.expandActionView()
|
||||
searchView.setQuery(query, true)
|
||||
searchView.clearFocus()
|
||||
}
|
||||
|
||||
searchView.queryTextChanges()
|
||||
.filter { router.backstack.lastOrNull()?.controller() == this }
|
||||
.subscribeUntilDestroy {
|
||||
query = it.toString()
|
||||
drawExtensions()
|
||||
}
|
||||
|
||||
// Fixes problem with the overflow icon showing up in lieu of search
|
||||
searchItem.fixExpand()
|
||||
}
|
||||
|
||||
override fun onItemClick(view: View, position: Int): Boolean {
|
||||
// override fun onItemClick(position: Int): Boolean {
|
||||
val extension = (adapter?.getItem(position) as? ExtensionItem)?.extension ?: return false
|
||||
if (extension is Extension.Installed) {
|
||||
openDetails(extension)
|
||||
@@ -114,7 +147,19 @@ open class ExtensionController : NucleusController<ExtensionPresenter>(),
|
||||
|
||||
fun setExtensions(extensions: List<ExtensionItem>) {
|
||||
ext_swipe_refresh?.isRefreshing = false
|
||||
adapter?.updateDataSet(extensions)
|
||||
this.extensions = extensions
|
||||
drawExtensions()
|
||||
}
|
||||
|
||||
fun drawExtensions() {
|
||||
if (!query.isBlank()) {
|
||||
adapter?.updateDataSet(
|
||||
extensions.filter {
|
||||
it.extension.name.contains(query, ignoreCase = true)
|
||||
})
|
||||
} else {
|
||||
adapter?.updateDataSet(extensions)
|
||||
}
|
||||
}
|
||||
|
||||
fun downloadUpdate(item: ExtensionItem) {
|
||||
|
||||
@@ -53,7 +53,7 @@ class ExtensionDetailsController(bundle: Bundle? = null) :
|
||||
}
|
||||
|
||||
override fun createPresenter(): ExtensionDetailsPresenter {
|
||||
return ExtensionDetailsPresenter(args.getString(PKGNAME_KEY))
|
||||
return ExtensionDetailsPresenter(args.getString(PKGNAME_KEY)!!)
|
||||
}
|
||||
|
||||
override fun getTitle(): String? {
|
||||
@@ -134,7 +134,7 @@ class ExtensionDetailsController(bundle: Bundle? = null) :
|
||||
private fun addPreferencesForSource(screen: PreferenceScreen, source: Source, multiSource: Boolean) {
|
||||
val context = screen.context
|
||||
|
||||
// TODO
|
||||
// TODO
|
||||
val dataStore = SharedPreferencesDataStore(/*if (source is HttpSource) {
|
||||
source.preferences
|
||||
} else {*/
|
||||
|
||||
@@ -13,7 +13,7 @@ class ExtensionDividerItemDecoration(context: Context) : RecyclerView.ItemDecora
|
||||
|
||||
init {
|
||||
val a = context.obtainStyledAttributes(intArrayOf(android.R.attr.listDivider))
|
||||
divider = a.getDrawable(0)
|
||||
divider = a.getDrawable(0)!!
|
||||
a.recycle()
|
||||
}
|
||||
|
||||
|
||||
@@ -3,18 +3,14 @@ package eu.kanade.tachiyomi.ui.extension
|
||||
import android.annotation.SuppressLint
|
||||
import android.view.View
|
||||
import eu.davidea.flexibleadapter.FlexibleAdapter
|
||||
import eu.kanade.tachiyomi.R
|
||||
import eu.kanade.tachiyomi.ui.base.holder.BaseFlexibleViewHolder
|
||||
import kotlinx.android.synthetic.main.extension_card_header.*
|
||||
import kotlinx.android.synthetic.main.extension_card_header.title
|
||||
|
||||
class ExtensionGroupHolder(view: View, adapter: FlexibleAdapter<*>) :
|
||||
BaseFlexibleViewHolder(view, adapter) {
|
||||
|
||||
@SuppressLint("SetTextI18n")
|
||||
fun bind(item: ExtensionGroupItem) {
|
||||
title.text = when {
|
||||
item.installed -> itemView.context.getString(R.string.ext_installed)
|
||||
else -> itemView.context.getString(R.string.ext_available)
|
||||
} + " (" + item.size + ")"
|
||||
title.text = item.name
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,11 +8,12 @@ import eu.davidea.flexibleadapter.items.IFlexible
|
||||
import eu.kanade.tachiyomi.R
|
||||
|
||||
/**
|
||||
* Item that contains the language header.
|
||||
* Item that contains the group header.
|
||||
*
|
||||
* @param code The lang code.
|
||||
* @param name The header name.
|
||||
* @param size The number of items in the group.
|
||||
*/
|
||||
data class ExtensionGroupItem(val installed: Boolean, val size: Int) : AbstractHeaderItem<ExtensionGroupHolder>() {
|
||||
data class ExtensionGroupItem(val name: String, val size: Int) : AbstractHeaderItem<ExtensionGroupHolder>() {
|
||||
|
||||
/**
|
||||
* Returns the layout resource of this item.
|
||||
@@ -40,13 +41,13 @@ data class ExtensionGroupItem(val installed: Boolean, val size: Int) : AbstractH
|
||||
override fun equals(other: Any?): Boolean {
|
||||
if (this === other) return true
|
||||
if (other is ExtensionGroupItem) {
|
||||
return installed == other.installed
|
||||
return name == other.name
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
override fun hashCode(): Int {
|
||||
return installed.hashCode()
|
||||
return name.hashCode()
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,10 +1,13 @@
|
||||
package eu.kanade.tachiyomi.ui.extension
|
||||
|
||||
import android.app.Application
|
||||
import android.os.Bundle
|
||||
import eu.kanade.tachiyomi.R
|
||||
import eu.kanade.tachiyomi.extension.ExtensionManager
|
||||
import eu.kanade.tachiyomi.extension.model.Extension
|
||||
import eu.kanade.tachiyomi.extension.model.InstallStep
|
||||
import eu.kanade.tachiyomi.ui.base.presenter.BasePresenter
|
||||
import eu.kanade.tachiyomi.util.LocaleHelper
|
||||
import rx.Observable
|
||||
import rx.Subscription
|
||||
import rx.android.schedulers.AndroidSchedulers
|
||||
@@ -49,6 +52,8 @@ open class ExtensionPresenter(
|
||||
|
||||
@Synchronized
|
||||
private fun toItems(tuple: ExtensionTuple): List<ExtensionItem> {
|
||||
val context = Injekt.get<Application>()
|
||||
|
||||
val (installed, untrusted, available) = tuple
|
||||
|
||||
val items = mutableListOf<ExtensionItem>()
|
||||
@@ -62,7 +67,7 @@ open class ExtensionPresenter(
|
||||
.sortedBy { it.pkgName }
|
||||
|
||||
if (installedSorted.isNotEmpty() || untrustedSorted.isNotEmpty()) {
|
||||
val header = ExtensionGroupItem(true, installedSorted.size + untrustedSorted.size)
|
||||
val header = ExtensionGroupItem(context.getString(R.string.ext_installed), installedSorted.size + untrustedSorted.size)
|
||||
items += installedSorted.map { extension ->
|
||||
ExtensionItem(extension, header, currentDownloads[extension.pkgName])
|
||||
}
|
||||
@@ -71,10 +76,17 @@ open class ExtensionPresenter(
|
||||
}
|
||||
}
|
||||
if (availableSorted.isNotEmpty()) {
|
||||
val header = ExtensionGroupItem(false, availableSorted.size)
|
||||
items += availableSorted.map { extension ->
|
||||
ExtensionItem(extension, header, currentDownloads[extension.pkgName])
|
||||
}
|
||||
val availableGroupedByLang = availableSorted
|
||||
.groupBy { LocaleHelper.getDisplayName(it.lang, context) }
|
||||
.toSortedMap()
|
||||
|
||||
availableGroupedByLang
|
||||
.forEach {
|
||||
val header = ExtensionGroupItem(it.key, it.value.size)
|
||||
items += it.value.map { extension ->
|
||||
ExtensionItem(extension, header, currentDownloads[extension.pkgName])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
this.extensions = items
|
||||
|
||||
@@ -24,10 +24,10 @@ class ExtensionTrustDialog<T>(bundle: Bundle? = null) : DialogController(bundle)
|
||||
.positiveText(R.string.ext_trust)
|
||||
.negativeText(R.string.ext_uninstall)
|
||||
.onPositive { _, _ ->
|
||||
(targetController as? Listener)?.trustSignature(args.getString(SIGNATURE_KEY))
|
||||
(targetController as? Listener)?.trustSignature(args.getString(SIGNATURE_KEY)!!)
|
||||
}
|
||||
.onNegative { _, _ ->
|
||||
(targetController as? Listener)?.uninstallExtension(args.getString(PKGNAME_KEY))
|
||||
(targetController as? Listener)?.uninstallExtension(args.getString(PKGNAME_KEY)!!)
|
||||
}
|
||||
.build()
|
||||
}
|
||||
|
||||
@@ -5,10 +5,14 @@ import android.util.AttributeSet
|
||||
import eu.kanade.tachiyomi.R
|
||||
import eu.kanade.tachiyomi.data.preference.PreferencesHelper
|
||||
import eu.kanade.tachiyomi.data.preference.getOrDefault
|
||||
import eu.kanade.tachiyomi.ui.catalogue.filter.TriStateItem
|
||||
import eu.kanade.tachiyomi.widget.ExtendedNavigationView
|
||||
import eu.kanade.tachiyomi.widget.ExtendedNavigationView.Item.MultiSort.Companion.SORT_ASC
|
||||
import eu.kanade.tachiyomi.widget.ExtendedNavigationView.Item.MultiSort.Companion.SORT_DESC
|
||||
import eu.kanade.tachiyomi.widget.ExtendedNavigationView.Item.MultiSort.Companion.SORT_NONE
|
||||
import eu.kanade.tachiyomi.widget.ExtendedNavigationView.Item.TriStateGroup.Companion.STATE_IGNORE
|
||||
import eu.kanade.tachiyomi.widget.ExtendedNavigationView.Item.TriStateGroup.Companion.STATE_INCLUDE
|
||||
import eu.kanade.tachiyomi.widget.ExtendedNavigationView.Item.TriStateGroup.Companion.STATE_EXCLUDE
|
||||
import uy.kohesive.injekt.injectLazy
|
||||
|
||||
/**
|
||||
@@ -48,7 +52,7 @@ class LibraryNavigationView @JvmOverloads constructor(context: Context, attrs: A
|
||||
* Returns true if there's at least one filter from [FilterGroup] active.
|
||||
*/
|
||||
fun hasActiveFilters(): Boolean {
|
||||
return (groups[0] as FilterGroup).items.any { it.checked }
|
||||
return (groups[0] as FilterGroup).items.any { it.state != STATE_IGNORE } //j2k it.checked -> this
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -69,11 +73,11 @@ class LibraryNavigationView @JvmOverloads constructor(context: Context, attrs: A
|
||||
*/
|
||||
inner class FilterGroup : Group {
|
||||
|
||||
private val downloaded = Item.CheckboxGroup(R.string.action_filter_downloaded, this)
|
||||
private val downloaded = Item.TriStateGroup(R.string.action_filter_downloaded, this)
|
||||
|
||||
private val unread = Item.CheckboxGroup(R.string.action_filter_unread, this)
|
||||
private val unread = Item.TriStateGroup(R.string.action_filter_unread, this)
|
||||
|
||||
private val completed = Item.CheckboxGroup(R.string.completed, this)
|
||||
private val completed = Item.TriStateGroup(R.string.completed, this)
|
||||
|
||||
override val items = listOf(downloaded, unread, completed)
|
||||
|
||||
@@ -81,19 +85,28 @@ class LibraryNavigationView @JvmOverloads constructor(context: Context, attrs: A
|
||||
|
||||
override val footer = Item.Separator()
|
||||
|
||||
override fun initModels() {
|
||||
downloaded.checked = preferences.filterDownloaded().getOrDefault()
|
||||
unread.checked = preferences.filterUnread().getOrDefault()
|
||||
completed.checked = preferences.filterCompleted().getOrDefault()
|
||||
override fun initModels() { //j2k changes
|
||||
try {
|
||||
downloaded.state = preferences.filterDownloaded().getOrDefault()
|
||||
unread.state = preferences.filterUnread().getOrDefault()
|
||||
completed.state = preferences.filterCompleted().getOrDefault()
|
||||
} catch (e: Exception) {
|
||||
preferences.upgradeFilters()
|
||||
}
|
||||
}
|
||||
|
||||
override fun onItemClicked(item: Item) {
|
||||
item as Item.CheckboxGroup
|
||||
item.checked = !item.checked
|
||||
override fun onItemClicked(item: Item) { //j2k changes
|
||||
item as Item.TriStateGroup
|
||||
val newState = when (item.state) {
|
||||
STATE_IGNORE -> STATE_INCLUDE
|
||||
STATE_INCLUDE -> STATE_EXCLUDE
|
||||
else -> STATE_IGNORE
|
||||
}
|
||||
item.state = newState
|
||||
when (item) {
|
||||
downloaded -> preferences.filterDownloaded().set(item.checked)
|
||||
unread -> preferences.filterUnread().set(item.checked)
|
||||
completed -> preferences.filterCompleted().set(item.checked)
|
||||
downloaded -> preferences.filterDownloaded().set(item.state)
|
||||
unread -> preferences.filterUnread().set(item.state)
|
||||
completed -> preferences.filterCompleted().set(item.state)
|
||||
}
|
||||
|
||||
adapter.notifyItemChanged(item)
|
||||
@@ -214,4 +227,4 @@ class LibraryNavigationView @JvmOverloads constructor(context: Context, attrs: A
|
||||
item.group.items.forEach { adapter.notifyItemChanged(it) }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,6 +14,9 @@ import eu.kanade.tachiyomi.source.LocalSource
|
||||
import eu.kanade.tachiyomi.source.SourceManager
|
||||
import eu.kanade.tachiyomi.source.model.SManga
|
||||
import eu.kanade.tachiyomi.source.online.HttpSource
|
||||
import eu.kanade.tachiyomi.widget.ExtendedNavigationView.Item.TriStateGroup.Companion.STATE_EXCLUDE
|
||||
import eu.kanade.tachiyomi.widget.ExtendedNavigationView.Item.TriStateGroup.Companion.STATE_INCLUDE
|
||||
import eu.kanade.tachiyomi.widget.ExtendedNavigationView.Item.TriStateGroup.Companion.STATE_IGNORE
|
||||
import eu.kanade.tachiyomi.ui.base.presenter.BasePresenter
|
||||
import eu.kanade.tachiyomi.util.combineLatest
|
||||
import eu.kanade.tachiyomi.util.isNullOrUnsubscribed
|
||||
@@ -121,26 +124,22 @@ class LibraryPresenter(
|
||||
|
||||
val filterFn: (LibraryItem) -> Boolean = f@ { item ->
|
||||
// Filter when there isn't unread chapters.
|
||||
if (filterUnread && item.manga.unread == 0) {
|
||||
if (filterUnread == STATE_INCLUDE && item.manga.unread == 0) {return@f false}
|
||||
if (filterUnread == STATE_EXCLUDE && item.manga.unread > 0) {return@f false}
|
||||
if (filterCompleted == STATE_INCLUDE && item.manga.status != SManga.COMPLETED) {
|
||||
return@f false
|
||||
}
|
||||
|
||||
if (filterCompleted && item.manga.status != SManga.COMPLETED) {
|
||||
if (filterCompleted == STATE_EXCLUDE && item.manga.status == SManga.COMPLETED) {
|
||||
return@f false
|
||||
}
|
||||
|
||||
// Filter when there are no downloads.
|
||||
if (filterDownloaded) {
|
||||
// Local manga are always downloaded
|
||||
if (item.manga.source == LocalSource.ID) {
|
||||
return@f true
|
||||
if (filterDownloaded != STATE_IGNORE) {
|
||||
val isDownloaded = when {
|
||||
item.manga.source == LocalSource.ID -> true
|
||||
item.downloadCount != -1 -> item.downloadCount > 0
|
||||
else -> downloadManager.getDownloadCount(item.manga) > 0
|
||||
}
|
||||
// Don't bother with directory checking if download count has been set.
|
||||
if (item.downloadCount != -1) {
|
||||
return@f item.downloadCount > 0
|
||||
}
|
||||
|
||||
return@f downloadManager.getDownloadCount(item.manga) > 0
|
||||
return@f if (filterDownloaded == STATE_INCLUDE) isDownloaded else !isDownloaded
|
||||
}
|
||||
true
|
||||
}
|
||||
|
||||
@@ -31,6 +31,7 @@ import eu.kanade.tachiyomi.ui.manga.MangaController
|
||||
import eu.kanade.tachiyomi.ui.recent_updates.RecentChaptersController
|
||||
import eu.kanade.tachiyomi.ui.recently_read.RecentlyReadController
|
||||
import eu.kanade.tachiyomi.ui.setting.SettingsMainController
|
||||
import eu.kanade.tachiyomi.util.openInBrowser
|
||||
import exh.uconfig.WarnConfigureDialogController
|
||||
import exh.ui.batchadd.BatchAddController
|
||||
import exh.ui.lock.LockChangeHandler
|
||||
@@ -145,6 +146,9 @@ class MainActivity : BaseActivity() {
|
||||
R.id.nav_drawer_settings -> {
|
||||
router.pushController(SettingsMainController().withFadeTransaction())
|
||||
}
|
||||
R.id.nav_drawer_help -> {
|
||||
openInBrowser(URL_HELP)
|
||||
}
|
||||
}
|
||||
}
|
||||
drawer.closeDrawer(GravityCompat.START)
|
||||
@@ -467,6 +471,8 @@ class MainActivity : BaseActivity() {
|
||||
const val INTENT_SEARCH = "eu.kanade.tachiyomi.SEARCH"
|
||||
const val INTENT_SEARCH_QUERY = "query"
|
||||
const val INTENT_SEARCH_FILTER = "filter"
|
||||
|
||||
private const val URL_HELP = "https://tachiyomi.org/help/"
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -204,7 +204,6 @@ class MangaController : RxController, TabbedController {
|
||||
}
|
||||
|
||||
companion object {
|
||||
|
||||
// EXH -->
|
||||
const val UPDATE_EXTRA = "update"
|
||||
const val SMART_SEARCH_CONFIG_EXTRA = "smartSearchConfig"
|
||||
@@ -220,5 +219,4 @@ class MangaController : RxController, TabbedController {
|
||||
.apply { isAccessible = true }
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -235,3 +235,4 @@ class MangaInfoPresenter(
|
||||
return toInsert
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,9 +1,7 @@
|
||||
package eu.kanade.tachiyomi.ui.manga.info
|
||||
|
||||
import android.os.Bundle
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.view.*
|
||||
import android.webkit.WebView
|
||||
import eu.kanade.tachiyomi.R
|
||||
import eu.kanade.tachiyomi.source.SourceManager
|
||||
@@ -16,6 +14,10 @@ class MangaWebViewController(bundle: Bundle? = null) : BaseController(bundle) {
|
||||
|
||||
private val sourceManager by injectLazy<SourceManager>()
|
||||
|
||||
init {
|
||||
setHasOptionsMenu(true)
|
||||
}
|
||||
|
||||
constructor(sourceId: Long, url: String) : this(Bundle().apply {
|
||||
putLong(SOURCE_KEY, sourceId)
|
||||
putString(URL_KEY, url)
|
||||
@@ -43,6 +45,40 @@ class MangaWebViewController(bundle: Bundle? = null) : BaseController(bundle) {
|
||||
web.loadUrl(url, headers)
|
||||
}
|
||||
|
||||
override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) {
|
||||
inflater.inflate(R.menu.web_view, menu)
|
||||
}
|
||||
|
||||
override fun onPrepareOptionsMenu(menu: Menu) {
|
||||
val web = view as WebView
|
||||
menu.findItem(R.id.action_forward).isVisible = web.canGoForward()
|
||||
}
|
||||
|
||||
override fun onOptionsItemSelected(item: MenuItem): Boolean {
|
||||
when (item.itemId) {
|
||||
R.id.action_forward -> {
|
||||
val web = view as WebView
|
||||
if (web.canGoForward()) web.goForward()
|
||||
}
|
||||
R.id.action_refresh -> {
|
||||
val web = view as WebView
|
||||
web.reload()
|
||||
}
|
||||
R.id.action_close -> router.popController(this)
|
||||
else -> return super.onOptionsItemSelected(item)
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
override fun handleBack(): Boolean {
|
||||
val web = view as WebView
|
||||
if (web.canGoBack()) {
|
||||
web.goBack()
|
||||
return true
|
||||
}
|
||||
return super.handleBack()
|
||||
}
|
||||
|
||||
override fun onDestroyView(view: View) {
|
||||
val web = view as WebView
|
||||
web.stopLoading()
|
||||
|
||||
@@ -7,7 +7,7 @@ import eu.davidea.flexibleadapter.items.AbstractHeaderItem
|
||||
import eu.davidea.flexibleadapter.items.IFlexible
|
||||
import eu.kanade.tachiyomi.R
|
||||
import eu.kanade.tachiyomi.ui.base.holder.BaseFlexibleViewHolder
|
||||
import kotlinx.android.synthetic.main.catalogue_main_controller_card.*
|
||||
import kotlinx.android.synthetic.main.catalogue_main_controller_card.title
|
||||
|
||||
/**
|
||||
* Item that contains the selection header.
|
||||
@@ -38,7 +38,7 @@ class SelectionHeader : AbstractHeaderItem<SelectionHeader.Holder>() {
|
||||
|
||||
class Holder(view: View, adapter: FlexibleAdapter<*>) : BaseFlexibleViewHolder(view, adapter) {
|
||||
init {
|
||||
title.text = "Please select a source to migrate from"
|
||||
title.text = view.context.getString(R.string.migration_selection_prompt)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -141,8 +141,8 @@ class ReaderActivity : BaseRxActivity<ReaderPresenter>() {
|
||||
setContentView(R.layout.reader_activity)
|
||||
|
||||
if (presenter.needsInit()) {
|
||||
val manga = intent.extras.getLong("manga", -1)
|
||||
val chapter = intent.extras.getLong("chapter", -1)
|
||||
val manga = intent.extras!!.getLong("manga", -1)
|
||||
val chapter = intent.extras!!.getLong("chapter", -1)
|
||||
|
||||
if (manga == -1L || chapter == -1L) {
|
||||
finish()
|
||||
|
||||
@@ -513,7 +513,7 @@ class ReaderPresenter(
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribeFirst(
|
||||
{ view, file -> view.onShareImageResult(file) },
|
||||
{ view, error -> /* Empty */ }
|
||||
{ _, _ -> /* Empty */ }
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -2,8 +2,8 @@ package eu.kanade.tachiyomi.ui.reader.loader
|
||||
|
||||
import eu.kanade.tachiyomi.source.model.Page
|
||||
import eu.kanade.tachiyomi.ui.reader.model.ReaderPage
|
||||
import eu.kanade.tachiyomi.util.ComparatorUtil.CaseInsensitiveNaturalComparator
|
||||
import eu.kanade.tachiyomi.util.ImageUtil
|
||||
import net.greypanther.natsort.CaseInsensitiveSimpleNaturalComparator
|
||||
import rx.Observable
|
||||
import java.io.File
|
||||
import java.io.FileInputStream
|
||||
@@ -18,11 +18,9 @@ class DirectoryPageLoader(val file: File) : PageLoader() {
|
||||
* comparator.
|
||||
*/
|
||||
override fun getPages(): Observable<List<ReaderPage>> {
|
||||
val comparator = CaseInsensitiveSimpleNaturalComparator.getInstance<String>()
|
||||
|
||||
return file.listFiles()
|
||||
.filter { !it.isDirectory && ImageUtil.isImage(it.name) { FileInputStream(it) } }
|
||||
.sortedWith(Comparator<File> { f1, f2 -> comparator.compare(f1.name, f2.name) })
|
||||
.sortedWith(Comparator<File> { f1, f2 -> CaseInsensitiveNaturalComparator.compare(f1.name, f2.name) })
|
||||
.mapIndexed { i, file ->
|
||||
val streamFn = { FileInputStream(file) }
|
||||
ReaderPage(i).apply {
|
||||
|
||||
@@ -32,9 +32,9 @@ class DownloadPageLoader(
|
||||
return downloadManager.buildPageList(source, manga, chapter.chapter)
|
||||
.map { pages ->
|
||||
pages.map { page ->
|
||||
ReaderPage(page.index, page.url, page.imageUrl, {
|
||||
ReaderPage(page.index, page.url, page.imageUrl) {
|
||||
context.contentResolver.openInputStream(page.uri)
|
||||
}).apply {
|
||||
}.apply {
|
||||
status = Page.READY
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,10 +2,10 @@ package eu.kanade.tachiyomi.ui.reader.loader
|
||||
|
||||
import eu.kanade.tachiyomi.source.model.Page
|
||||
import eu.kanade.tachiyomi.ui.reader.model.ReaderPage
|
||||
import eu.kanade.tachiyomi.util.ComparatorUtil.CaseInsensitiveNaturalComparator
|
||||
import eu.kanade.tachiyomi.util.ImageUtil
|
||||
import junrar.Archive
|
||||
import junrar.rarfile.FileHeader
|
||||
import net.greypanther.natsort.CaseInsensitiveSimpleNaturalComparator
|
||||
import rx.Observable
|
||||
import java.io.File
|
||||
import java.io.InputStream
|
||||
@@ -42,11 +42,9 @@ class RarPageLoader(file: File) : PageLoader() {
|
||||
* comparator.
|
||||
*/
|
||||
override fun getPages(): Observable<List<ReaderPage>> {
|
||||
val comparator = CaseInsensitiveSimpleNaturalComparator.getInstance<String>()
|
||||
|
||||
return archive.fileHeaders
|
||||
.filter { !it.isDirectory && ImageUtil.isImage(it.fileNameString) { archive.getInputStream(it) } }
|
||||
.sortedWith(Comparator<FileHeader> { f1, f2 -> comparator.compare(f1.fileNameString, f2.fileNameString) })
|
||||
.sortedWith(Comparator<FileHeader> { f1, f2 -> CaseInsensitiveNaturalComparator.compare(f1.fileNameString, f2.fileNameString) })
|
||||
.mapIndexed { i, header ->
|
||||
val streamFn = { getStream(header) }
|
||||
|
||||
|
||||
@@ -2,8 +2,8 @@ package eu.kanade.tachiyomi.ui.reader.loader
|
||||
|
||||
import eu.kanade.tachiyomi.source.model.Page
|
||||
import eu.kanade.tachiyomi.ui.reader.model.ReaderPage
|
||||
import eu.kanade.tachiyomi.util.ComparatorUtil.CaseInsensitiveNaturalComparator
|
||||
import eu.kanade.tachiyomi.util.ImageUtil
|
||||
import net.greypanther.natsort.CaseInsensitiveSimpleNaturalComparator
|
||||
import rx.Observable
|
||||
import java.io.File
|
||||
import java.util.zip.ZipEntry
|
||||
@@ -32,11 +32,9 @@ class ZipPageLoader(file: File) : PageLoader() {
|
||||
* comparator.
|
||||
*/
|
||||
override fun getPages(): Observable<List<ReaderPage>> {
|
||||
val comparator = CaseInsensitiveSimpleNaturalComparator.getInstance<String>()
|
||||
|
||||
return zip.entries().toList()
|
||||
.filter { !it.isDirectory && ImageUtil.isImage(it.name) { zip.getInputStream(it) } }
|
||||
.sortedWith(Comparator<ZipEntry> { f1, f2 -> comparator.compare(f1.name, f2.name) })
|
||||
.sortedWith(Comparator<ZipEntry> { f1, f2 -> CaseInsensitiveNaturalComparator.compare(f1.name, f2.name) })
|
||||
.mapIndexed { i, entry ->
|
||||
val streamFn = { zip.getInputStream(entry) }
|
||||
ReaderPage(i).apply {
|
||||
|
||||
@@ -19,6 +19,7 @@ import com.bumptech.glide.load.DataSource
|
||||
import com.bumptech.glide.load.engine.DiskCacheStrategy
|
||||
import com.bumptech.glide.load.engine.GlideException
|
||||
import com.bumptech.glide.load.resource.drawable.DrawableTransitionOptions
|
||||
import com.bumptech.glide.load.resource.gif.GifDrawable
|
||||
import com.bumptech.glide.request.RequestListener
|
||||
import com.bumptech.glide.request.target.Target
|
||||
import com.bumptech.glide.request.transition.NoTransition
|
||||
@@ -457,6 +458,9 @@ class PagerPageHolder(
|
||||
dataSource: DataSource?,
|
||||
isFirstResource: Boolean
|
||||
): Boolean {
|
||||
if (resource is GifDrawable) {
|
||||
resource.setLoopCount(GifDrawable.LOOP_INTRINSIC)
|
||||
}
|
||||
onImageDecoded()
|
||||
return false
|
||||
}
|
||||
|
||||
@@ -46,6 +46,7 @@ class PagerTransitionHolder(
|
||||
* Text view used to display the text of the current and next/prev chapters.
|
||||
*/
|
||||
private var textView = TextView(context).apply {
|
||||
textSize = 17.5F
|
||||
wrapContent()
|
||||
}
|
||||
|
||||
|
||||
@@ -18,6 +18,7 @@ import com.bumptech.glide.load.DataSource
|
||||
import com.bumptech.glide.load.engine.DiskCacheStrategy
|
||||
import com.bumptech.glide.load.engine.GlideException
|
||||
import com.bumptech.glide.load.resource.drawable.DrawableTransitionOptions
|
||||
import com.bumptech.glide.load.resource.gif.GifDrawable
|
||||
import com.bumptech.glide.request.RequestListener
|
||||
import com.bumptech.glide.request.target.Target
|
||||
import com.bumptech.glide.request.transition.NoTransition
|
||||
@@ -497,6 +498,9 @@ class WebtoonPageHolder(
|
||||
dataSource: DataSource?,
|
||||
isFirstResource: Boolean
|
||||
): Boolean {
|
||||
if (resource is GifDrawable) {
|
||||
resource.setLoopCount(GifDrawable.LOOP_INTRINSIC)
|
||||
}
|
||||
onImageDecoded()
|
||||
return false
|
||||
}
|
||||
|
||||
@@ -62,12 +62,10 @@ open class WebtoonRecyclerView @JvmOverloads constructor(
|
||||
override fun onScrollStateChanged(state: Int) {
|
||||
super.onScrollStateChanged(state)
|
||||
val layoutManager = layoutManager
|
||||
if(layoutManager != null) {
|
||||
val visibleItemCount = layoutManager.childCount
|
||||
val totalItemCount = layoutManager.itemCount
|
||||
atLastPosition = visibleItemCount > 0 && lastVisibleItemPosition == totalItemCount - 1
|
||||
atFirstPosition = firstVisibleItemPosition == 0
|
||||
}
|
||||
val visibleItemCount = layoutManager?.childCount ?: 0
|
||||
val totalItemCount = layoutManager?.itemCount ?: 0
|
||||
atLastPosition = visibleItemCount > 0 && lastVisibleItemPosition == totalItemCount - 1
|
||||
atFirstPosition = firstVisibleItemPosition == 0
|
||||
}
|
||||
|
||||
private fun getPositionX(positionX: Float): Float {
|
||||
|
||||
@@ -111,7 +111,7 @@ class WebtoonViewer(val activity: ReaderActivity) : BaseViewer {
|
||||
recycler.longTapListener = f@ { event ->
|
||||
if (activity.menuVisible || config.longTapEnabled) {
|
||||
val child = recycler.findChildViewUnder(event.x, event.y)
|
||||
if(child != null) {
|
||||
if (child != null) {
|
||||
val position = recycler.getChildAdapterPosition(child)
|
||||
val item = adapter.items.getOrNull(position)
|
||||
if (item is ReaderPage) {
|
||||
|
||||
@@ -135,14 +135,14 @@ class SettingsAboutController : SettingsController() {
|
||||
override fun onCreateDialog(savedViewState: Bundle?): Dialog {
|
||||
return MaterialDialog.Builder(activity!!)
|
||||
.title(R.string.update_check_title)
|
||||
.content(args.getString(BODY_KEY))
|
||||
.content(args.getString(BODY_KEY) ?: "")
|
||||
.positiveText(R.string.update_check_confirm)
|
||||
.negativeText(R.string.update_check_ignore)
|
||||
.onPositive { _, _ ->
|
||||
val appContext = applicationContext
|
||||
if (appContext != null) {
|
||||
// Start download
|
||||
val url = args.getString(URL_KEY)
|
||||
val url = args.getString(URL_KEY) ?: ""
|
||||
UpdaterService.downloadUpdate(appContext, url)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,7 +17,6 @@ import eu.kanade.tachiyomi.data.database.DatabaseHelper
|
||||
import eu.kanade.tachiyomi.data.preference.PreferencesHelper
|
||||
import eu.kanade.tachiyomi.data.preference.getOrDefault
|
||||
import eu.kanade.tachiyomi.ui.base.controller.DialogController
|
||||
import eu.kanade.tachiyomi.util.DiskUtil
|
||||
import eu.kanade.tachiyomi.util.getFilePicker
|
||||
import uy.kohesive.injekt.Injekt
|
||||
import uy.kohesive.injekt.api.get
|
||||
@@ -45,15 +44,6 @@ class SettingsDownloadController : SettingsController() {
|
||||
.subscribeUntilDestroy { path ->
|
||||
val dir = UniFile.fromUri(context, Uri.parse(path))
|
||||
summary = dir.filePath ?: path
|
||||
|
||||
// Don't display downloaded chapters in gallery apps creating .nomedia
|
||||
if (dir != null && dir.exists()) {
|
||||
val nomedia = dir.findFile(".nomedia")
|
||||
if (nomedia == null) {
|
||||
dir.createFile(".nomedia")
|
||||
applicationContext?.let { DiskUtil.scanMedia(it, dir.uri) }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
switchPreference {
|
||||
|
||||
@@ -9,6 +9,7 @@ import android.view.View
|
||||
import com.afollestad.materialdialogs.MaterialDialog
|
||||
import eu.kanade.tachiyomi.R
|
||||
import eu.kanade.tachiyomi.data.database.DatabaseHelper
|
||||
import eu.kanade.tachiyomi.data.database.models.Category
|
||||
import eu.kanade.tachiyomi.data.library.LibraryUpdateJob
|
||||
import eu.kanade.tachiyomi.data.preference.PreferencesHelper
|
||||
import eu.kanade.tachiyomi.data.preference.getOrDefault
|
||||
@@ -182,15 +183,17 @@ class SettingsGeneralController : SettingsController() {
|
||||
key = Keys.defaultCategory
|
||||
titleRes = R.string.default_category
|
||||
|
||||
val selectedCategory = dbCategories.find { it.id == preferences.defaultCategory() }
|
||||
val categories = listOf(Category.createDefault()) + dbCategories
|
||||
|
||||
val selectedCategory = categories.find { it.id == preferences.defaultCategory() }
|
||||
entries = arrayOf(context.getString(R.string.default_category_summary)) +
|
||||
dbCategories.map { it.name }.toTypedArray()
|
||||
entryValues = arrayOf("-1") + dbCategories.map { it.id.toString() }.toTypedArray()
|
||||
categories.map { it.name }.toTypedArray()
|
||||
entryValues = arrayOf("-1") + categories.map { it.id.toString() }.toTypedArray()
|
||||
defaultValue = "-1"
|
||||
summary = selectedCategory?.name ?: context.getString(R.string.default_category_summary)
|
||||
|
||||
onChange { newValue ->
|
||||
summary = dbCategories.find {
|
||||
summary = categories.find {
|
||||
it.id == (newValue as String).toInt()
|
||||
}?.name ?: context.getString(R.string.default_category_summary)
|
||||
true
|
||||
|
||||
@@ -91,7 +91,7 @@ object ChapterRecognition {
|
||||
* @param chapter chapter object
|
||||
* @return true if volume is found
|
||||
*/
|
||||
fun updateChapter(match: MatchResult?, chapter: SChapter): Boolean {
|
||||
private fun updateChapter(match: MatchResult?, chapter: SChapter): Boolean {
|
||||
match?.let {
|
||||
val initial = it.groups[1]?.value?.toFloat()!!
|
||||
val subChapterDecimal = it.groups[2]?.value
|
||||
@@ -109,12 +109,12 @@ object ChapterRecognition {
|
||||
* @param alpha alpha value of regex
|
||||
* @return decimal/alpha float value
|
||||
*/
|
||||
fun checkForDecimal(decimal: String?, alpha: String?): Float {
|
||||
private fun checkForDecimal(decimal: String?, alpha: String?): Float {
|
||||
if (!decimal.isNullOrEmpty())
|
||||
return decimal?.toFloat()!!
|
||||
return decimal.toFloat()
|
||||
|
||||
if (!alpha.isNullOrEmpty()) {
|
||||
if (alpha!!.contains("extra"))
|
||||
if (alpha.contains("extra"))
|
||||
return .99f
|
||||
|
||||
if (alpha.contains("omake"))
|
||||
@@ -138,7 +138,7 @@ object ChapterRecognition {
|
||||
* x.a -> x.1, x.b -> x.2, etc
|
||||
*/
|
||||
private fun parseAlphaPostFix(alpha: Char): Float {
|
||||
return ("0." + Integer.toString(alpha.toInt() - 96)).toFloat()
|
||||
return ("0." + (alpha.toInt() - 96).toString()).toFloat()
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
package eu.kanade.tachiyomi.util
|
||||
|
||||
object ComparatorUtil {
|
||||
val CaseInsensitiveNaturalComparator = compareBy<String, String>(String.CASE_INSENSITIVE_ORDER) { it }.then(naturalOrder())
|
||||
}
|
||||
@@ -199,11 +199,11 @@ fun Context.isServiceRunning(serviceClass: Class<*>): Boolean {
|
||||
*/
|
||||
fun Context.openInBrowser(url: String) {
|
||||
try {
|
||||
val url = Uri.parse(url)
|
||||
val parsedUrl = Uri.parse(url)
|
||||
val intent = CustomTabsIntent.Builder()
|
||||
.setToolbarColor(getResourceColor(R.attr.colorPrimary))
|
||||
.build()
|
||||
intent.launchUrl(this, url)
|
||||
intent.launchUrl(this, parsedUrl)
|
||||
} catch (e: Exception) {
|
||||
toast(e.message)
|
||||
}
|
||||
|
||||
@@ -5,5 +5,6 @@ import kotlinx.coroutines.*
|
||||
fun launchUI(block: suspend CoroutineScope.() -> Unit): Job =
|
||||
GlobalScope.launch(Dispatchers.Main, CoroutineStart.DEFAULT, block)
|
||||
|
||||
@UseExperimental(ExperimentalCoroutinesApi::class)
|
||||
fun launchNow(block: suspend CoroutineScope.() -> Unit): Job =
|
||||
GlobalScope.launch(Dispatchers.Main, CoroutineStart.UNDISPATCHED, block)
|
||||
|
||||
@@ -7,6 +7,7 @@ import android.os.Build
|
||||
import android.os.Environment
|
||||
import android.support.v4.content.ContextCompat
|
||||
import android.support.v4.os.EnvironmentCompat
|
||||
import com.hippo.unifile.UniFile
|
||||
import java.io.File
|
||||
|
||||
object DiskUtil {
|
||||
@@ -54,6 +55,19 @@ object DiskUtil {
|
||||
return directories
|
||||
}
|
||||
|
||||
/**
|
||||
* Don't display downloaded chapters in gallery apps creating `.nomedia`.
|
||||
*/
|
||||
fun createNoMediaFile(dir: UniFile?, context: Context?) {
|
||||
if (dir != null && dir.exists()) {
|
||||
val nomedia = dir.findFile(".nomedia")
|
||||
if (nomedia == null) {
|
||||
dir.createFile(".nomedia")
|
||||
context?.let { scanMedia(it, dir.uri) }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Scans the given file so that it can be shown in gallery apps, for example.
|
||||
*/
|
||||
|
||||
@@ -77,6 +77,17 @@ open class ExtendedNavigationView @JvmOverloads constructor(
|
||||
setTint(context.getResourceColor(R.attr.colorAccent))
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Creates a vector tinted with the accent color.
|
||||
*
|
||||
* @param context any context.
|
||||
* @param resId the vector resource to load and tint
|
||||
*/
|
||||
fun tintVector(context: Context, resId: Int, colorId: Int): Drawable {
|
||||
return VectorDrawableCompat.create(context.resources, resId, context.theme)!!.apply {
|
||||
setTint(context.getResourceColor(colorId))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -107,6 +118,25 @@ open class ExtendedNavigationView @JvmOverloads constructor(
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class TriStateGroup(resId: Int, group: Group) : MultiStateGroup(resId, group) {
|
||||
|
||||
companion object {
|
||||
const val STATE_IGNORE = 0
|
||||
const val STATE_INCLUDE = 1
|
||||
const val STATE_EXCLUDE = 2
|
||||
}
|
||||
|
||||
override fun getStateDrawable(context: Context): Drawable? {
|
||||
return when(state) {
|
||||
STATE_INCLUDE -> tintVector(context, R.drawable.ic_check_box_24dp)
|
||||
STATE_EXCLUDE -> tintVector(context, R.drawable.ic_check_box_x_24dp,
|
||||
android.R.attr.textColorSecondary)
|
||||
else -> tintVector(context, R.drawable.ic_check_box_outline_blank_24dp,
|
||||
android.R.attr.textColorSecondary)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -236,4 +266,4 @@ open class ExtendedNavigationView @JvmOverloads constructor(
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user