mirror of
				https://github.com/mihonapp/mihon.git
				synced 2025-11-04 08:08:55 +01:00 
			
		
		
		
	Merge branch 'master' into update-kotlin-coroutines-gradle
This commit is contained in:
		@@ -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"
 | 
			
		||||
 
 | 
			
		||||
@@ -341,19 +341,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()
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
@@ -500,19 +503,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))
 | 
			
		||||
        }
 | 
			
		||||
 
 | 
			
		||||
@@ -316,9 +316,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()
 | 
			
		||||
 
 | 
			
		||||
@@ -19,7 +19,7 @@ class CatalogueSearchAdapter(val controller: CatalogueSearchController) :
 | 
			
		||||
     */
 | 
			
		||||
    private var bundle = Bundle()
 | 
			
		||||
 | 
			
		||||
    override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int, payloads: List<Any?>?) {
 | 
			
		||||
    override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int, payloads: List<Any?>) {
 | 
			
		||||
        super.onBindViewHolder(holder, position, payloads)
 | 
			
		||||
        restoreHolderState(holder)
 | 
			
		||||
    }
 | 
			
		||||
@@ -71,4 +71,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,6 +92,30 @@ 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(position: Int): Boolean {
 | 
			
		||||
        val extension = (adapter?.getItem(position) as? ExtensionItem)?.extension ?: return false
 | 
			
		||||
        if (extension is Extension.Installed) {
 | 
			
		||||
@@ -114,7 +146,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) {
 | 
			
		||||
 
 | 
			
		||||
@@ -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
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -6,11 +6,12 @@ import eu.davidea.flexibleadapter.items.AbstractHeaderItem
 | 
			
		||||
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.
 | 
			
		||||
@@ -38,13 +39,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
 | 
			
		||||
 
 | 
			
		||||
@@ -276,7 +276,7 @@ class MainActivity : BaseActivity() {
 | 
			
		||||
        const val INTENT_SEARCH_QUERY = "query"
 | 
			
		||||
        const val INTENT_SEARCH_FILTER = "filter"
 | 
			
		||||
 | 
			
		||||
        private const val URL_HELP = "https://github.com/inorichi/tachiyomi/wiki"
 | 
			
		||||
        private const val URL_HELP = "https://tachiyomi.org/help/"
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -382,11 +382,12 @@ class MangaInfoController : NucleusController<MangaInfoPresenter>(),
 | 
			
		||||
        toggleFavorite()
 | 
			
		||||
        if (manga.favorite) {
 | 
			
		||||
            val categories = presenter.getCategories()
 | 
			
		||||
            val defaultCategory = categories.find { it.id == preferences.defaultCategory() }
 | 
			
		||||
            val defaultCategoryId = preferences.defaultCategory()
 | 
			
		||||
            val defaultCategory = categories.find { it.id == defaultCategoryId }
 | 
			
		||||
            when {
 | 
			
		||||
                defaultCategory != null -> presenter.moveMangaToCategory(manga, defaultCategory)
 | 
			
		||||
                categories.size <= 1 -> // default or the one from the user
 | 
			
		||||
                    presenter.moveMangaToCategory(manga, categories.firstOrNull())
 | 
			
		||||
                defaultCategoryId == 0 || categories.isEmpty() -> // 'Default' or no category
 | 
			
		||||
                    presenter.moveMangaToCategory(manga, null)
 | 
			
		||||
                else -> {
 | 
			
		||||
                    val ids = presenter.getMangaCategoryIds(manga)
 | 
			
		||||
                    val preselected = ids.mapNotNull { id ->
 | 
			
		||||
@@ -413,9 +414,9 @@ class MangaInfoController : NucleusController<MangaInfoPresenter>(),
 | 
			
		||||
            activity?.toast(activity?.getString(R.string.manga_added_library))
 | 
			
		||||
        }
 | 
			
		||||
        val categories = presenter.getCategories()
 | 
			
		||||
        if (categories.size <= 1) {
 | 
			
		||||
            // default or the one from the user then just add to favorite.
 | 
			
		||||
            presenter.moveMangaToCategory(manga, categories.firstOrNull())
 | 
			
		||||
        if (categories.isEmpty()) {
 | 
			
		||||
            // no categories exist, display a message about adding categories
 | 
			
		||||
            activity?.toast(activity?.getString(R.string.action_add_category))
 | 
			
		||||
        } else {
 | 
			
		||||
            val ids = presenter.getMangaCategoryIds(manga)
 | 
			
		||||
            val preselected = ids.mapNotNull { id ->
 | 
			
		||||
 
 | 
			
		||||
@@ -130,9 +130,9 @@ class MangaInfoPresenter(
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 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()
 | 
			
		||||
 
 | 
			
		||||
@@ -5,7 +5,7 @@ import eu.davidea.flexibleadapter.FlexibleAdapter
 | 
			
		||||
import eu.davidea.flexibleadapter.items.AbstractHeaderItem
 | 
			
		||||
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.
 | 
			
		||||
@@ -36,7 +36,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)
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -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
 | 
			
		||||
                }
 | 
			
		||||
 
 | 
			
		||||
@@ -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
 | 
			
		||||
                }
 | 
			
		||||
 
 | 
			
		||||
@@ -8,6 +8,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
 | 
			
		||||
@@ -179,15 +180,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
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user