Updates to stuff once more

More manga type handling
Fixed download progress colors
Manga controller now listens to global updates
Removed search activity layout, using main activty with navbar hidden
Empty library while using filters now has new text
This commit is contained in:
Jay 2020-03-03 20:31:17 -08:00
parent 0c81459143
commit 52e6a7fc95
20 changed files with 152 additions and 323 deletions

View File

@ -3,7 +3,8 @@ package eu.kanade.tachiyomi.data.database.models
import eu.kanade.tachiyomi.source.SourceManager
import eu.kanade.tachiyomi.source.model.SManga
import eu.kanade.tachiyomi.ui.reader.ReaderActivity
import uy.kohesive.injekt.injectLazy
import uy.kohesive.injekt.Injekt
import uy.kohesive.injekt.api.get
import java.util.Locale
interface Manga : SManga {
@ -35,47 +36,62 @@ interface Manga : SManga {
}
fun mangaType(): Int {
val sourceManager: SourceManager by injectLazy()
val sourceName = Injekt.get<SourceManager>().getOrStub(source).name
val currentTags = currentGenres()?.split(",")?.map { it.trim().toLowerCase(Locale.US) }
return if (currentTags?.any
{ tag ->
tag.startsWith("english") || tag == "comic"
} == true)
} == true || isComicSource(sourceName))
TYPE_COMIC
else if (currentTags?.any
{ tag ->
tag.startsWith("chinese") || tag == "manhua"
} == true)
} == true ||
sourceName.contains("manhua", true))
TYPE_MANHUA
else if (currentTags?.any
{ tag ->
tag == "long strip" || tag == "manhwa" ||
tag.contains("webtoon")
} == true ||
sourceManager.getOrStub(source).name.contains("webtoon", true))
} == true || isWebtoonSource(sourceName))
TYPE_MANHWA
else TYPE_MANGA
}
fun defaultReaderType(): Int {
val sourceManager: SourceManager by injectLazy()
val sourceName = Injekt.get<SourceManager>().getOrStub(source).name
val currentTags = currentGenres()?.split(",")?.map { it.trim().toLowerCase(Locale.US) }
return if (currentTags?.any
{ tag ->
tag == "long strip" || tag == "manhwa" ||
tag.contains("webtoon")
} == true ||
sourceManager.getOrStub(source).name.contains("webtoon", true))
} == true || isWebtoonSource(sourceName))
ReaderActivity.WEBTOON
else if (currentTags?.any
{ tag ->
tag.startsWith("chinese") || tag == "manhua" ||
tag.startsWith("english") || tag == "comic"
} == true)
} == true || isComicSource(sourceName) ||
sourceName.contains("manhua", true) )
ReaderActivity.LEFT_TO_RIGHT
else 0
}
fun isWebtoonSource(sourceName: String): Boolean {
return sourceName.contains("webtoon", true) ||
sourceName.contains("manwha", true) ||
sourceName.contains("toonily", true)
}
fun isComicSource(sourceName: String): Boolean {
return sourceName.contains("gunnerkrigg", true) ||
sourceName.contains("gunnerkrigg", true) ||
sourceName.contains("dilbert", true) ||
sourceName.contains("cyanide", true) ||
sourceName.contains("xkcd", true) ||
sourceName.contains("tapastic", true)
}
// Used to display the chapter's title one way or another
var displayMode: Int
get() = chapter_flags and DISPLAY_MASK

View File

@ -196,8 +196,9 @@ class LibraryUpdateService(
this.listener = listener
}
fun removeListener() {
listener = null
fun removeListener(listener: LibraryServiceListener) {
if (this.listener == listener)
this.listener = null
}
}

View File

@ -17,6 +17,8 @@ class DownloadButton @JvmOverloads constructor(context: Context, attrs: Attribut
: FrameLayout(context, attrs) {
private val activeColor = context.getResourceColor(R.attr.colorAccent)
private val progressBGColor = ContextCompat.getColor(context,
R.color.divider)
private val disabledColor = ContextCompat.getColor(context,
R.color.material_on_surface_disabled)
private val downloadedColor = ContextCompat.getColor(context,
@ -60,7 +62,7 @@ class DownloadButton @JvmOverloads constructor(context: Context, attrs: Attribut
download_border.setImageDrawable(borderCircle)
download_progress.isIndeterminate = false
download_progress.progress = progress
download_border.drawable.setTint(disabledColor)
download_border.drawable.setTint(progressBGColor)
download_progress.progressDrawable?.setTint(downloadedColor)
download_icon.drawable.setTint(disabledColor)
if (!isAnimating) {

View File

@ -299,7 +299,7 @@ open class LibraryController(
override fun onDestroyView(view: View) {
pagerAdapter?.onDestroy()
DownloadService.removeListener(this)
LibraryUpdateService.removeListener()
LibraryUpdateService.removeListener(this)
pagerAdapter = null
actionMode = null
tabsVisibilitySubscription?.unsubscribe()

View File

@ -222,7 +222,11 @@ class LibraryListController(bundle: Bundle? = null) : LibraryController(bundle),
if (mangaMap.isNotEmpty()) {
empty_view?.hide()
} else {
empty_view?.show(R.drawable.ic_book_black_128dp, R.string.information_empty_library)
empty_view?.show(
R.drawable.ic_book_black_128dp,
if (bottom_sheet.hasActiveFilters()) R.string.information_empty_library_filtered
else R.string.information_empty_library
)
}
adapter.setItems(mangaMap)

View File

@ -170,14 +170,9 @@ class LibraryPresenter(
if (filterUnread == STATE_REALLY_EXCLUDE && item.manga.unread > 0) return@f false
if (filterMangaType > 0) {
val mangaType = item.manga.mangaType()
if ((filterMangaType == Manga.TYPE_MANHUA) && mangaType != Manga.TYPE_MANHUA)
return@f false
if ((filterMangaType == Manga.TYPE_COMIC) && mangaType != Manga.TYPE_COMIC) return@f false
if ((filterMangaType == Manga.TYPE_MANHWA) && mangaType != Manga.TYPE_MANHWA) return@f false
if (filterMangaType != item.manga.mangaType()) return@f false
}
if (filterCompleted == STATE_INCLUDE && item.manga.status != SManga.COMPLETED)
return@f false
if (filterCompleted == STATE_EXCLUDE && item.manga.status == SManga.COMPLETED)

View File

@ -80,8 +80,6 @@ open class MainActivity : BaseActivity(), DownloadServiceListener {
private var currentGestureDelegate:SwipeGestureInterface? = null
private lateinit var gestureDetector:GestureDetectorCompat
protected open var trulyGoBack = false
private var secondaryDrawer: ViewGroup? = null
private var snackBar:Snackbar? = null
@ -112,7 +110,6 @@ open class MainActivity : BaseActivity(), DownloadServiceListener {
Timber.e(e, "Exception when creating webview at start")
}
super.onCreate(savedInstanceState)
if (trulyGoBack) return
// Do not let the launcher create a new activity http://stackoverflow.com/questions/16283079
if (!isTaskRoot) {
@ -314,8 +311,6 @@ open class MainActivity : BaseActivity(), DownloadServiceListener {
container: ViewGroup, handler: ControllerChangeHandler) {
syncActivityViewWithController(to, from)
if (to !is DialogController)
navigationView.visibility = if (router.backstackSize > 1) View.GONE else View.VISIBLE
}
override fun onChangeCompleted(to: Controller?, from: Controller?, isPush: Boolean,
@ -394,7 +389,6 @@ open class MainActivity : BaseActivity(), DownloadServiceListener {
override fun onResume() {
super.onResume()
// setting in case someone comes from the search activity to main
usingBottomNav = true
getExtensionUpdates()
DownloadService.callListeners()
}
@ -501,10 +495,10 @@ open class MainActivity : BaseActivity(), DownloadServiceListener {
}
override fun onBackPressed() {
if (trulyGoBack) {
/*if (trulyGoBack) {
super.onBackPressed()
return
}
}*/
/*if (drawer.isDrawerOpen(GravityCompat.START) || drawer.isDrawerOpen(GravityCompat.END)) {
drawer.closeDrawers()
} else {*/
@ -560,17 +554,6 @@ open class MainActivity : BaseActivity(), DownloadServiceListener {
}
drawerArrow?.progress = 1f
/* if (from is TabbedController) {
from.cleanupTabs(tabs)
}
if (to is TabbedController) {
tabAnimator.expand()
to.configureTabs(tabs)
} else {
tabAnimator.collapse()
tabs.setupWithViewPager(null)
}*/
currentGestureDelegate = to as? SwipeGestureInterface
/*if (from is SecondaryDrawerController) {
@ -593,6 +576,9 @@ open class MainActivity : BaseActivity(), DownloadServiceListener {
} else {
appbar.enableElevation()
}
if (to !is DialogController)
navigationView.visibility = if (router.backstackSize > 1) View.GONE else View.VISIBLE
}
override fun downloadStatusChanged(downloading: Boolean) {
@ -672,8 +658,6 @@ open class MainActivity : BaseActivity(), DownloadServiceListener {
const val INTENT_SEARCH_QUERY = "query"
const val INTENT_SEARCH_FILTER = "filter"
var usingBottomNav = true
internal set
}
}

View File

@ -2,148 +2,24 @@ package eu.kanade.tachiyomi.ui.main
import android.app.SearchManager
import android.content.Intent
import android.content.res.Configuration
import android.graphics.Color
import android.os.Build
import android.os.Bundle
import android.view.View
import android.view.ViewGroup
import android.widget.FrameLayout
import android.widget.LinearLayout
import androidx.appcompat.graphics.drawable.DrawerArrowDrawable
import androidx.core.graphics.ColorUtils
import com.bluelinelabs.conductor.Conductor
import com.bluelinelabs.conductor.Controller
import com.bluelinelabs.conductor.ControllerChangeHandler
import eu.kanade.tachiyomi.R
import eu.kanade.tachiyomi.data.notification.NotificationReceiver
import eu.kanade.tachiyomi.ui.base.controller.DialogController
import eu.kanade.tachiyomi.ui.base.controller.NoToolbarElevationController
import eu.kanade.tachiyomi.ui.base.controller.withFadeTransaction
import eu.kanade.tachiyomi.ui.catalogue.global_search.CatalogueSearchController
import eu.kanade.tachiyomi.ui.security.SecureActivityDelegate
import eu.kanade.tachiyomi.util.system.getResourceColor
import eu.kanade.tachiyomi.util.view.updateLayoutParams
import eu.kanade.tachiyomi.util.view.updatePadding
import kotlinx.android.synthetic.main.search_activity.*
import eu.kanade.tachiyomi.util.view.gone
import kotlinx.android.synthetic.main.main_activity.*
class SearchActivity: MainActivity() {
override var trulyGoBack = true
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
usingBottomNav = false
setContentView(R.layout.search_activity)
setSupportActionBar(sToolbar)
drawerArrow = DrawerArrowDrawable(this)
drawerArrow?.color = getResourceColor(R.attr.actionBarTintColor)
sToolbar.navigationIcon = drawerArrow
//tabAnimator = TabsAnimator(sTabs)
val container: ViewGroup = findViewById(R.id.controller_container)
val content: LinearLayout = findViewById(R.id.main_content)
container.systemUiVisibility = View.SYSTEM_UI_FLAG_LAYOUT_STABLE or
View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN or
View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
content.systemUiVisibility = View.SYSTEM_UI_FLAG_LAYOUT_STABLE or
View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN or
View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
content.setOnApplyWindowInsetsListener { v, insets ->
window.navigationBarColor =
// if the os does not support light nav bar and is portrait, draw a dark translucent
// nav bar
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M &&
(v.rootWindowInsets.systemWindowInsetLeft > 0 ||
v.rootWindowInsets.systemWindowInsetRight > 0))
// For lollipop, draw opaque nav bar
Color.BLACK
else Color.argb(179, 0, 0, 0)
}
// if the android q+ device has gesture nav, transparent nav bar
else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q
&& (v.rootWindowInsets.systemWindowInsetBottom != v.rootWindowInsets
.tappableElementInsets.bottom)) {
getColor(android.R.color.transparent)
}
// if in landscape with 2/3 button mode, fully opaque nav bar
else if (v.rootWindowInsets.systemWindowInsetLeft > 0
|| v.rootWindowInsets.systemWindowInsetRight > 0) {
getResourceColor(android.R.attr.colorBackground)
}
// if in portrait with 2/3 button mode, translucent nav bar
else {
ColorUtils.setAlphaComponent(
getResourceColor(android.R.attr.colorBackground), 179)
}
v.setPadding(insets.systemWindowInsetLeft, insets.systemWindowInsetTop,
insets.systemWindowInsetRight, 0)
insets
}
val currentNightMode = resources.configuration.uiMode and Configuration.UI_MODE_NIGHT_MASK
if (Build.VERSION.SDK_INT >= 26 && currentNightMode == Configuration.UI_MODE_NIGHT_NO &&
preferences.theme() >= 8) {
content.systemUiVisibility = content.systemUiVisibility.or(View
.SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR)
}
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && currentNightMode == Configuration
.UI_MODE_NIGHT_NO && preferences.theme() >= 8)
content.systemUiVisibility = content.systemUiVisibility.or(View
.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR)
val searchContainer: FrameLayout = findViewById(R.id.search_container)
searchContainer.setOnApplyWindowInsetsListener { v, insets ->
window.statusBarColor = getResourceColor(R.attr.colorPrimary)
val contextView = window?.decorView?.findViewById<View>(R.id.action_mode_bar)
contextView?.updateLayoutParams<ViewGroup.MarginLayoutParams> {
leftMargin = insets.systemWindowInsetLeft
rightMargin = insets.systemWindowInsetRight
}
// Consume any horizontal insets and pad all content in. There's not much we can do
// with horizontal insets
v.updatePadding(
left = insets.systemWindowInsetLeft,
right = insets.systemWindowInsetRight
)
insets.replaceSystemWindowInsets(
0, insets.systemWindowInsetTop,
0, insets.systemWindowInsetBottom
)
}
router = Conductor.attachRouter(this, container, savedInstanceState)
if (!router.hasRootController()) {
// Set start screen
handleIntentAction(intent)
}
sToolbar.setNavigationOnClickListener {
toolbar.setNavigationOnClickListener {
popToRoot()
}
router.addChangeListener(object : ControllerChangeHandler.ControllerChangeListener {
override fun onChangeStarted(to: Controller?, from: Controller?, isPush: Boolean,
container: ViewGroup, handler: ControllerChangeHandler
) {
syncActivityViewWithController(to, from)
}
override fun onChangeCompleted(to: Controller?, from: Controller?, isPush: Boolean,
container: ViewGroup, handler: ControllerChangeHandler
) {
}
})
syncActivityViewWithController(router.backstack.lastOrNull()?.controller())
}
override fun onBackPressed() {
@ -167,29 +43,15 @@ class SearchActivity: MainActivity() {
if (from is DialogController || to is DialogController) {
return
}
toolbar.navigationIcon = drawerArrow
drawerArrow?.progress = 1f
/*if (from is TabbedController) {
from.cleanupTabs(sTabs)
}
if (to is TabbedController) {
tabAnimator.expand()
to.configureTabs(sTabs)
} else {
tabAnimator.collapse()
sTabs.setupWithViewPager(null)
}*/
if (to is NoToolbarElevationController) {
appbar.disableElevation()
} else {
appbar.enableElevation()
}
}
override fun onResume() {
super.onResume()
usingBottomNav = false
navigationView.gone()
}
override fun handleIntentAction(intent: Intent): Boolean {

View File

@ -44,6 +44,7 @@ import eu.kanade.tachiyomi.data.notification.NotificationReceiver
import eu.kanade.tachiyomi.source.Source
import eu.kanade.tachiyomi.source.SourceManager
import eu.kanade.tachiyomi.ui.base.controller.BaseController
import eu.kanade.tachiyomi.ui.base.controller.NoToolbarElevationController
import eu.kanade.tachiyomi.ui.catalogue.CatalogueController
import eu.kanade.tachiyomi.ui.library.ChangeMangaCategoriesDialog
import eu.kanade.tachiyomi.ui.library.LibraryController
@ -75,7 +76,8 @@ class MangaChaptersController : BaseController,
FlexibleAdapter.OnItemClickListener,
FlexibleAdapter.OnItemLongClickListener,
ChaptersAdapter.MangaHeaderInterface,
ChangeMangaCategoriesDialog.Listener {
ChangeMangaCategoriesDialog.Listener,
NoToolbarElevationController {
constructor(manga: Manga?,
fromCatalogue: Boolean = false,
@ -148,11 +150,11 @@ class MangaChaptersController : BaseController,
val array = view.context.obtainStyledAttributes(attrsArray)
val appbarHeight = array.getDimensionPixelSize(0, 0)
array.recycle()
val offset = 20.dpToPx
val offset = 10.dpToPx
recycler.doOnApplyWindowInsets { v, insets, _ ->
headerHeight = appbarHeight + insets.systemWindowInsetTop + offset
swipe_refresh.setProgressViewOffset(false, (-40).dpToPx, headerHeight)
headerHeight = appbarHeight + insets.systemWindowInsetTop
swipe_refresh.setProgressViewOffset(false, (-40).dpToPx, headerHeight + offset)
(recycler.findViewHolderForAdapterPosition(0) as? MangaHeaderHolder)
?.setTopHeight(headerHeight)
fast_scroller?.updateLayoutParams<ViewGroup.MarginLayoutParams> {
@ -169,15 +171,18 @@ class MangaChaptersController : BaseController,
val atTop = !recycler.canScrollVertically(-1)
if ((!atTop && !toolbarIsColored) || (atTop && toolbarIsColored)) {
toolbarIsColored = !atTop
colorAnimator?.cancel()
val color =
coverColor ?: activity!!.getResourceColor(android.R.attr.colorPrimary)
val colorFrom = ColorUtils.setAlphaComponent(
color, if (toolbarIsColored) 0 else 255
)
val colorFrom =
if (colorAnimator?.isRunning == true) activity?.window?.statusBarColor
?: color
else ColorUtils.setAlphaComponent(
color, if (toolbarIsColored) 0 else 255
)
val colorTo = ColorUtils.setAlphaComponent(
color, if (toolbarIsColored) 255 else 0
)
colorAnimator?.cancel()
colorAnimator = ValueAnimator.ofObject(
ArgbEvaluator(), colorFrom, colorTo
)
@ -192,6 +197,15 @@ class MangaChaptersController : BaseController,
}
}
}
setPaletteColor()
swipe_refresh.setOnRefreshListener {
presenter.refreshAll()
}
}
fun setPaletteColor() {
val view = view ?: return
GlideApp.with(view.context).load(manga)
.diskCacheStrategy(DiskCacheStrategy.AUTOMATIC)
.signature(ObjectKey(MangaImpl.getLastCoverFetch(manga!!.id!!).toString()))
@ -224,19 +238,17 @@ class MangaChaptersController : BaseController,
override fun onLoadCleared(placeholder: Drawable?) { }
})
swipe_refresh.setOnRefreshListener {
presenter.refreshAll()
}
}
override fun onActivityResumed(activity: Activity) {
super.onActivityResumed(activity)
presenter.isLockedFromSearch = SecureActivityDelegate.shouldBeLocked()
presenter.headerItem.isLocked = presenter.isLockedFromSearch
presenter.fetchChapters()
}
fun showError(message: String) {
swipe_refresh?.isRefreshing = false
swipe_refresh?.isRefreshing = presenter.isLoading
view?.snack(message)
}
@ -275,25 +287,25 @@ class MangaChaptersController : BaseController,
fun updateHeader() {
if (presenter.chapters.isEmpty()) {
adapter?.updateDataSet(listOf(ChapterItem(Chapter.createH(), presenter.manga)))
adapter?.updateDataSet(listOf(presenter.headerItem))
}
else {
swipe_refresh?.isRefreshing = false
swipe_refresh?.isRefreshing = presenter.isLoading
adapter?.updateDataSet(
listOf(ChapterItem(Chapter.createH(), presenter.manga)) + presenter.chapters
listOf(ChapterItem(presenter.headerItem, presenter.manga)) + presenter.chapters
)
}
}
fun updateChapters(chapters: List<ChapterItem>) {
swipe_refresh?.isRefreshing = false
swipe_refresh?.isRefreshing = presenter.isLoading
if (presenter.chapters.isEmpty() && fromCatalogue && !presenter.hasRequested) {
launchUI { swipe_refresh?.isRefreshing = true }
presenter.fetchChaptersFromSource()
}
adapter?.updateDataSet(listOf(ChapterItem(Chapter.createH(), presenter.manga)) + chapters)
}
adapter?.updateDataSet(listOf(presenter.headerItem) + chapters)
}
override fun onItemClick(view: View?, position: Int): Boolean {
val adapter = adapter ?: return false
@ -442,10 +454,15 @@ class MangaChaptersController : BaseController,
val adapter = adapter ?: return
val chapter = adapter.getItem(position) ?: return
if (chapter.isHeader) return
if (chapter.status != Download.NOT_DOWNLOADED) {
if (chapter.status != Download.NOT_DOWNLOADED && chapter.status != Download.ERROR) {
presenter.deleteChapters(listOf(chapter))
}
else presenter.downloadChapters(listOf(chapter))
else {
val isError = chapter.status == Download.ERROR
presenter.downloadChapters(listOf(chapter))
if (isError)
presenter.restartDownloads()
}
}
override fun tagClicked(text: String) {
@ -463,6 +480,10 @@ class MangaChaptersController : BaseController,
override fun chapterCount():Int = presenter.chapters.size
override fun favoriteManga(longPress: Boolean) {
if (presenter.isLockedFromSearch) {
SecureActivityDelegate.promptLockIfNeeded(activity)
return
}
val manga = presenter.manga
if (longPress) {
if (!manga.favorite) {

View File

@ -35,9 +35,7 @@ import eu.kanade.tachiyomi.ui.migration.manga.process.MigrationListController
import eu.kanade.tachiyomi.ui.security.SecureActivityDelegate
import eu.kanade.tachiyomi.util.system.toast
import eu.kanade.tachiyomi.util.view.applyWindowInsetsForController
import kotlinx.android.synthetic.main.main_activity.*
import kotlinx.android.synthetic.main.manga_controller.*
import kotlinx.android.synthetic.main.search_activity.*
import rx.Subscription
import uy.kohesive.injekt.Injekt
import uy.kohesive.injekt.api.get
@ -178,8 +176,7 @@ class MangaController : RxController, TabbedController, BottomNavBarInterface {
}
fun tabLayout():TabLayout? {
return if (activity is SearchActivity) activity?.sTabs
else activity?.tabs
return null
}
fun updateTitle(manga: Manga) {

View File

@ -162,6 +162,7 @@ class MangaHeaderHolder(
}))
manga_source.text = adapter.coverListener?.mangaSource()?.toString()
if (!manga.initialized) return
GlideApp.with(view.context).load(manga)
.diskCacheStrategy(DiskCacheStrategy.AUTOMATIC)
.signature(ObjectKey(MangaImpl.getLastCoverFetch(manga.id!!).toString()))

View File

@ -5,12 +5,15 @@ import eu.kanade.tachiyomi.data.cache.CoverCache
import eu.kanade.tachiyomi.data.database.DatabaseHelper
import eu.kanade.tachiyomi.data.database.models.Category
import eu.kanade.tachiyomi.data.database.models.Chapter
import eu.kanade.tachiyomi.data.database.models.LibraryManga
import eu.kanade.tachiyomi.data.database.models.Manga
import eu.kanade.tachiyomi.data.database.models.MangaCategory
import eu.kanade.tachiyomi.data.database.models.MangaImpl
import eu.kanade.tachiyomi.data.download.DownloadManager
import eu.kanade.tachiyomi.data.download.model.Download
import eu.kanade.tachiyomi.data.download.model.DownloadQueue
import eu.kanade.tachiyomi.data.library.LibraryServiceListener
import eu.kanade.tachiyomi.data.library.LibraryUpdateService
import eu.kanade.tachiyomi.data.preference.PreferencesHelper
import eu.kanade.tachiyomi.source.LocalSource
import eu.kanade.tachiyomi.source.Source
@ -21,7 +24,6 @@ import eu.kanade.tachiyomi.util.chapter.syncChaptersWithSource
import eu.kanade.tachiyomi.util.system.launchUI
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.Job
import kotlinx.coroutines.async
import kotlinx.coroutines.launch
@ -39,19 +41,25 @@ class MangaPresenter(private val controller: MangaChaptersController,
private val db: DatabaseHelper = Injekt.get(),
private val downloadManager: DownloadManager = Injekt.get()):
CoroutineScope,
DownloadQueue.DownloadListener {
DownloadQueue.DownloadListener,
LibraryServiceListener {
override var coroutineContext:CoroutineContext = Job() + Dispatchers.Default
var isLockedFromSearch = false
var hasRequested = false
var isLoading = false
var chapters:List<ChapterItem> = emptyList()
private set
var headerItem = ChapterItem(Chapter.createH(), manga)
fun onCreate() {
isLockedFromSearch = SecureActivityDelegate.shouldBeLocked()
headerItem.isLocked = isLockedFromSearch
downloadManager.addListener(this)
LibraryUpdateService.setListener(this)
if (!manga.initialized) {
controller.updateHeader()
launchUI {
@ -67,31 +75,7 @@ class MangaPresenter(private val controller: MangaChaptersController,
fun onDestroy() {
downloadManager.removeListener(this)
}
fun fetchMangaFromSource() {
GlobalScope.launch(Dispatchers.IO) {
withContext(Dispatchers.Main) {
controller.setRefresh(true)
}
val thumbnailUrl = manga.thumbnail_url
val networkManga = try {
source.fetchMangaDetails(manga).toBlocking().single()
} catch (e: java.lang.Exception) {
controller.showError(trimException(e))
return@launch
}
if (networkManga != null) {
manga.copyFrom(networkManga)
manga.initialized = true
db.insertManga(manga).executeAsBlocking()
if (thumbnailUrl != networkManga.thumbnail_url)
MangaImpl.setLastCoverFetch(manga.id!!, Date().time)
withContext(Dispatchers.Main) {
controller.updateHeader()
}
}
}
LibraryUpdateService.removeListener(this)
}
fun fetchChapters() {
@ -275,6 +259,11 @@ class MangaPresenter(private val controller: MangaChaptersController,
downloadManager.downloadChapters(manga, chapters)
}
fun restartDownloads() {
if (downloadManager.isPaused())
downloadManager.startDownloads()
}
/**
* Deletes the given list of chapter.
* @param chapters the list of chapters to delete.
@ -304,6 +293,7 @@ class MangaPresenter(private val controller: MangaChaptersController,
fun refreshAll() {
launch {
isLoading = true
var mangaError: java.lang.Exception? = null
var chapterError: java.lang.Exception? = null
val chapters = async(Dispatchers.IO) {
@ -338,6 +328,7 @@ class MangaPresenter(private val controller: MangaChaptersController,
syncChaptersWithSource(db, finChapters, manga, source)
withContext(Dispatchers.IO) { updateChapters() }
}
isLoading = false
if (chapterError == null)
withContext(Dispatchers.Main) { controller.updateChapters(this@MangaPresenter.chapters) }
if (mangaError != null)
@ -350,6 +341,7 @@ class MangaPresenter(private val controller: MangaChaptersController,
*/
fun fetchChaptersFromSource() {
hasRequested = true
isLoading = true
launch(Dispatchers.IO) {
val chapters = try {
@ -359,6 +351,7 @@ class MangaPresenter(private val controller: MangaChaptersController,
withContext(Dispatchers.Main) { controller.showError(trimException(e)) }
return@launch
} ?: listOf()
isLoading = false
try {
syncChaptersWithSource(db, chapters, manga, source)
@ -480,4 +473,10 @@ class MangaPresenter(private val controller: MangaChaptersController,
}
toggleFavorite()
}
override fun onUpdateManga(manga: LibraryManga) {
if (manga.id == this.manga.id) {
fetchChapters()
}
}
}

View File

@ -200,8 +200,6 @@ class MangaInfoController : NucleusController<MangaInfoPresenter>(),
setFullCoverToThumb()
}
container?.setOnApplyWindowInsetsListener { _, insets ->
if (MainActivity.usingBottomNav)
return@setOnApplyWindowInsetsListener insets
if (resources?.configuration?.orientation == Configuration.ORIENTATION_LANDSCAPE) {
fab_favorite?.updateLayoutParams<ViewGroup.MarginLayoutParams> {
bottomMargin = fabBaseMarginBottom + insets.systemWindowInsetBottom

View File

@ -13,12 +13,14 @@ class BiometricActivity : BaseActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val fromSearch = intent.getBooleanExtra("fromSearch", false)
val biometricPrompt = BiometricPrompt(this, executor, object : BiometricPrompt
.AuthenticationCallback() {
override fun onAuthenticationError(errorCode: Int, errString: CharSequence) {
super.onAuthenticationError(errorCode, errString)
finishAffinity()
if (fromSearch) finish()
else finishAffinity()
}
override fun onAuthenticationSucceeded(result: BiometricPrompt.AuthenticationResult) {

View File

@ -6,6 +6,7 @@ import android.view.WindowManager
import androidx.biometric.BiometricManager
import eu.kanade.tachiyomi.data.preference.PreferencesHelper
import eu.kanade.tachiyomi.data.preference.getOrDefault
import eu.kanade.tachiyomi.ui.main.SearchActivity
import uy.kohesive.injekt.injectLazy
import java.util.Date
@ -33,6 +34,7 @@ object SecureActivityDelegate {
if (lockApp && BiometricManager.from(activity).canAuthenticate() == BiometricManager.BIOMETRIC_SUCCESS) {
if (isAppLocked()) {
val intent = Intent(activity, BiometricActivity::class.java)
intent.putExtra("fromSearch", (activity is SearchActivity))
activity.startActivity(intent)
activity.overridePendingTransition(0, 0)
}

View File

@ -12,13 +12,12 @@ import android.content.res.Resources
import android.net.ConnectivityManager
import android.net.Uri
import android.os.PowerManager
import android.widget.Toast
import androidx.annotation.AttrRes
import androidx.annotation.StringRes
import androidx.browser.customtabs.CustomTabsIntent
import androidx.core.app.NotificationCompat
import androidx.core.content.ContextCompat
import androidx.localbroadcastmanager.content.LocalBroadcastManager
import android.widget.Toast
import com.nononsenseapps.filepicker.FilePickerActivity
import eu.kanade.tachiyomi.R
import eu.kanade.tachiyomi.widget.CustomLayoutPickerActivity
@ -102,6 +101,9 @@ val Int.pxToDp: Int
val Int.dpToPx: Int
get() = (this * Resources.getSystem().displayMetrics.density).toInt()
val Float.dpToPx: Float
get() = (this * Resources.getSystem().displayMetrics.density)
/**
* Property to get the notification manager from the context.
*/

View File

@ -1,10 +1,8 @@
package eu.kanade.tachiyomi.widget
import android.animation.ObjectAnimator
import android.animation.StateListAnimator
import android.content.Context
import android.util.AttributeSet
import com.google.android.material.R
import com.google.android.material.appbar.AppBarLayout
class ElevationAppBarLayout @JvmOverloads constructor(
@ -13,29 +11,24 @@ class ElevationAppBarLayout @JvmOverloads constructor(
) : AppBarLayout(context, attrs) {
private var origStateAnimator: StateListAnimator? = null
private var origElevation: Float
init {
origStateAnimator = stateListAnimator
origElevation = elevation
}
fun enableElevation() {
stateListAnimator = origStateAnimator
}
fun disableElevation() {
stateListAnimator = StateListAnimator().apply {
val objAnimator = ObjectAnimator.ofFloat(this, "elevation", 0f)
// Enabled and collapsible, but not collapsed means not elevated
addState(intArrayOf(android.R.attr.enabled, R.attr.state_collapsible, -R.attr.state_collapsed),
objAnimator)
// Default enabled state
addState(intArrayOf(android.R.attr.enabled), objAnimator)
// Disabled state
addState(IntArray(0), objAnimator)
if (stateListAnimator == null) {
stateListAnimator = origStateAnimator
elevation = origElevation
}
}
fun disableElevation() {
stateListAnimator = null
elevation = 0f
//translationZ = 0.1f.dpToPx
}
}

View File

@ -6,12 +6,21 @@
android:layout_height="match_parent"
android:orientation="vertical">
<com.bluelinelabs.conductor.ChangeHandlerFrameLayout
android:id="@+id/controller_container"
android:layout_width="match_parent"
android:layout_height="0dp"
app:layout_constraintBottom_toTopOf="@+id/navigationView"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent">
</com.bluelinelabs.conductor.ChangeHandlerFrameLayout>
<eu.kanade.tachiyomi.widget.ElevationAppBarLayout
android:id="@+id/appbar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:stateListAnimator="@null"
android:translationZ="0.1dp"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent">
@ -40,17 +49,6 @@
</eu.kanade.tachiyomi.widget.ElevationAppBarLayout>
<com.bluelinelabs.conductor.ChangeHandlerFrameLayout
android:id="@+id/controller_container"
android:layout_width="match_parent"
android:layout_height="0dp"
app:layout_constraintBottom_toTopOf="@+id/navigationView"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent">
</com.bluelinelabs.conductor.ChangeHandlerFrameLayout>
<com.google.android.material.bottomnavigation.BottomNavigationView
android:id="@+id/navigationView"
android:layout_width="match_parent"

View File

@ -1,50 +0,0 @@
<FrameLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/search_container"
android:layout_width="match_parent"
android:layout_height="match_parent">
<LinearLayout
android:orientation="vertical"
android:id="@+id/main_content"
android:layout_width="match_parent"
android:fitsSystemWindows="true"
android:layout_height="match_parent">
<eu.kanade.tachiyomi.widget.ElevationAppBarLayout
android:id="@+id/appbar"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<androidx.appcompat.widget.Toolbar
android:id="@+id/sToolbar"
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
android:background="?attr/colorPrimary"
android:theme="?attr/actionBarTheme"/>
<com.google.android.material.tabs.TabLayout
android:id="@+id/sTabs"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:theme="@style/Theme.ActionBar.Tab"
android:background="?colorPrimary"
app:tabRippleColor="@color/rippleColor"
app:tabIndicatorColor="?attr/actionBarTintColor"
app:tabTextColor="?attr/actionBarTintColor"
app:tabInlineLabel="true"
app:tabGravity="center"
app:tabMode="auto"
app:tabMinWidth="75dp"/>
</eu.kanade.tachiyomi.widget.ElevationAppBarLayout>
<com.bluelinelabs.conductor.ChangeHandlerFrameLayout
android:id="@+id/controller_container"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</LinearLayout>
</FrameLayout>

View File

@ -677,6 +677,8 @@
<string name="information_no_recent">No recent chapters</string>
<string name="information_no_recent_manga">No recently read manga</string>
<string name="information_empty_library">Your library is empty, add series to your library from the catalogues.</string>
<string name="information_empty_library_filtered">No matches found for your current
filters</string>
<string name="information_empty_category">You have no categories. Hit the plus button to create one for organizing your library.</string>
<!-- Download Notification -->