Updates To Snackbar logic

Removed experimnetal coroutines
Snackbar for removing mangas now remove downloads when they disappear.
Undo Snackbars now disappear when touching somewhere thats not them
Opening Tachiyomi from a url (ie mangadex) now automatically goes to the manga page once the search finishes
Fixed self made typo in "Update Library"
Removed Delete Mangas dialog as it is unused
This commit is contained in:
Jay 2019-11-10 17:16:39 -08:00
parent ae5ad2a9a6
commit 72e7b649a2
17 changed files with 188 additions and 165 deletions

View File

@ -258,12 +258,6 @@ repositories {
mavenCentral() mavenCentral()
} }
kotlin {
experimental {
coroutines 'enable'
}
}
androidExtensions { androidExtensions {
experimental = true experimental = true
} }

View File

@ -67,25 +67,4 @@ class CoverCache(private val context: Context) {
val file = getCoverFile(thumbnailUrl) val file = getCoverFile(thumbnailUrl)
return file.exists() && file.delete() return file.exists() && file.delete()
} }
/**
* Delete the cover file from the cache.
*
* @param thumbnailUrl the thumbnail url.
* @return status of deletion.
*/
fun deleteFromCache(manga: Manga, delayBy:Long) {
val thumbnailUrl = manga.thumbnail_url
// Check if url is empty.
if (thumbnailUrl.isNullOrEmpty()) return
launchUI {
delay(delayBy)
if (!manga.favorite) {
// Remove file.
val file = getCoverFile(thumbnailUrl)
if (file.exists()) file.delete()
}
}
}
} }

View File

@ -190,22 +190,6 @@ class DownloadManager(context: Context) {
cache.removeManga(manga) cache.removeManga(manga)
} }
/**
* Deletes the directory of a downloaded manga.
*
* @param manga the manga to delete.
* @param source the source of the manga.
*/
fun deleteManga(manga: Manga, source: Source, delayBy: Long) {
launchUI {
delay(delayBy)
if (!manga.favorite) {
deleteManga(manga, source)
}
}
}
/** /**
* Adds a list of chapters to be deleted later. * Adds a list of chapters to be deleted later.
* *

View File

@ -64,7 +64,7 @@ class NotificationReceiver : BroadcastReceiver() {
ACTION_DELETE_IMAGE -> deleteImage(context, intent.getStringExtra(EXTRA_FILE_LOCATION), ACTION_DELETE_IMAGE -> deleteImage(context, intent.getStringExtra(EXTRA_FILE_LOCATION),
intent.getIntExtra(EXTRA_NOTIFICATION_ID, -1)) intent.getIntExtra(EXTRA_NOTIFICATION_ID, -1))
// Cancel library update and dismiss notification // Cancel library update and dismiss notification
ACTION_CANCEL_LIBRARY_UPDATE -> cancelLibraryUpdate(context, Notifications.ID_LIBRARY_PROGRESS) ACTION_CANCEL_LIBRARY_UPDATE -> cancelLibraryUpdate(context)
// Open reader activity // Open reader activity
ACTION_OPEN_CHAPTER -> { ACTION_OPEN_CHAPTER -> {
openChapter(context, intent.getLongExtra(EXTRA_MANGA_ID, -1), openChapter(context, intent.getLongExtra(EXTRA_MANGA_ID, -1),
@ -76,7 +76,8 @@ class NotificationReceiver : BroadcastReceiver() {
context, notificationId, intent.getStringExtra(EXTRA_GROUP_ID) context, notificationId, intent.getStringExtra(EXTRA_GROUP_ID)
) )
val url = intent.getStringExtra(EXTRA_CHAPTER_URL) ?: return val url = intent.getStringExtra(EXTRA_CHAPTER_URL) ?: return
markAsRead(url) val mangaId = intent.getLongExtra(EXTRA_MANGA_ID, -1)
markAsRead(url, mangaId)
} }
} }
} }
@ -157,9 +158,9 @@ class NotificationReceiver : BroadcastReceiver() {
* @param context context of application * @param context context of application
* @param notificationId id of notification * @param notificationId id of notification
*/ */
private fun cancelLibraryUpdate(context: Context, notificationId: Int) { private fun cancelLibraryUpdate(context: Context) {
LibraryUpdateService.stop(context) LibraryUpdateService.stop(context)
Handler().post { dismissNotification(context, notificationId) } Handler().post { dismissNotification(context, Notifications.ID_LIBRARY_PROGRESS) }
} }
/** /**
@ -168,9 +169,9 @@ class NotificationReceiver : BroadcastReceiver() {
* @param context context of application * @param context context of application
* @param notificationId id of notification * @param notificationId id of notification
*/ */
private fun markAsRead(chapterUrl: String) { private fun markAsRead(chapterUrl: String, mangaaId: Long) {
val db: DatabaseHelper = Injekt.get() val db: DatabaseHelper = Injekt.get()
val chapter = db.getChapter(chapterUrl).executeAsBlocking() ?: return val chapter = db.getChapter(chapterUrl, mangaaId).executeAsBlocking() ?: return
chapter.read = true chapter.read = true
db.updateChapterProgress(chapter).executeAsBlocking() db.updateChapterProgress(chapter).executeAsBlocking()
val preferences: PreferencesHelper = Injekt.get() val preferences: PreferencesHelper = Injekt.get()
@ -398,6 +399,7 @@ class NotificationReceiver : BroadcastReceiver() {
val newIntent = Intent(context, NotificationReceiver::class.java).apply { val newIntent = Intent(context, NotificationReceiver::class.java).apply {
action = ACTION_MARK_AS_READ action = ACTION_MARK_AS_READ
putExtra(EXTRA_CHAPTER_URL, chapter.url) putExtra(EXTRA_CHAPTER_URL, chapter.url)
putExtra(EXTRA_MANGA_ID, manga.id)
putExtra(EXTRA_NOTIFICATION_ID, manga.id.hashCode()) putExtra(EXTRA_NOTIFICATION_ID, manga.id.hashCode())
putExtra(EXTRA_GROUP_ID, groupId) putExtra(EXTRA_GROUP_ID, groupId)
} }

View File

@ -9,6 +9,7 @@ import android.view.*
import androidx.core.view.GravityCompat import androidx.core.view.GravityCompat
import com.afollestad.materialdialogs.MaterialDialog import com.afollestad.materialdialogs.MaterialDialog
import com.f2prateek.rx.preferences.Preference import com.f2prateek.rx.preferences.Preference
import com.google.android.material.snackbar.BaseTransientBottomBar
import com.jakewharton.rxbinding.support.v7.widget.queryTextChangeEvents import com.jakewharton.rxbinding.support.v7.widget.queryTextChangeEvents
import eu.davidea.flexibleadapter.FlexibleAdapter import eu.davidea.flexibleadapter.FlexibleAdapter
import eu.davidea.flexibleadapter.items.IFlexible import eu.davidea.flexibleadapter.items.IFlexible
@ -24,6 +25,7 @@ import eu.kanade.tachiyomi.ui.base.controller.SecondaryDrawerController
import eu.kanade.tachiyomi.ui.base.controller.withFadeTransaction import eu.kanade.tachiyomi.ui.base.controller.withFadeTransaction
import eu.kanade.tachiyomi.ui.library.ChangeMangaCategoriesDialog import eu.kanade.tachiyomi.ui.library.ChangeMangaCategoriesDialog
import eu.kanade.tachiyomi.ui.library.HeightTopWindowInsetsListener import eu.kanade.tachiyomi.ui.library.HeightTopWindowInsetsListener
import eu.kanade.tachiyomi.ui.main.MainActivity
import eu.kanade.tachiyomi.ui.manga.MangaController import eu.kanade.tachiyomi.ui.manga.MangaController
import eu.kanade.tachiyomi.ui.manga.info.MangaWebViewController import eu.kanade.tachiyomi.ui.manga.info.MangaWebViewController
import eu.kanade.tachiyomi.util.* import eu.kanade.tachiyomi.util.*
@ -501,18 +503,23 @@ open class BrowseCatalogueController(bundle: Bundle) :
* @param position the position of the element clicked. * @param position the position of the element clicked.
*/ */
override fun onItemLongClick(position: Int) { override fun onItemLongClick(position: Int) {
val activity = activity ?: return
val manga = (adapter?.getItem(position) as? CatalogueItem?)?.manga ?: return val manga = (adapter?.getItem(position) as? CatalogueItem?)?.manga ?: return
snack?.dismiss() snack?.dismiss()
if (manga.favorite) { if (manga.favorite) {
presenter.changeMangaFavorite(manga) presenter.changeMangaFavorite(manga)
adapter?.notifyItemChanged(position) adapter?.notifyItemChanged(position)
snack = snack = catalouge_layout?.snack(R.string.manga_removed_library, Snackbar.LENGTH_INDEFINITE) {
catalouge_layout?.snack(R.string.manga_removed_library, 5000) { setAction(R.string.action_undo) {
setAction(R.string.action_undo) { if (!manga.favorite) addManga(manga, position)
if (!manga.favorite) addManga(manga, position)
}
} }
addCallback(object : BaseTransientBottomBar.BaseCallback<Snackbar>() {
override fun onDismissed(transientBottomBar: Snackbar?, event: Int) {
super.onDismissed(transientBottomBar, event)
if (!manga.favorite) presenter.confirmDeletion(manga)
}
})
}
(activity as? MainActivity)?.setUndoSnackBar(snack)
} else { } else {
addManga(manga, position) addManga(manga, position)
snack = catalouge_layout?.snack(R.string.manga_added_library) snack = catalouge_layout?.snack(R.string.manga_added_library)

View File

@ -254,14 +254,15 @@ open class BrowseCataloguePresenter(
*/ */
fun changeMangaFavorite(manga: Manga) { fun changeMangaFavorite(manga: Manga) {
manga.favorite = !manga.favorite manga.favorite = !manga.favorite
if (!manga.favorite) {
coverCache.deleteFromCache(manga, 5000)
val downloadManager: DownloadManager = Injekt.get()
downloadManager.deleteManga(manga,source,5000)
}
db.insertManga(manga).executeAsBlocking() db.insertManga(manga).executeAsBlocking()
} }
fun confirmDeletion(manga: Manga) {
coverCache.deleteFromCache(manga.thumbnail_url)
val downloadManager: DownloadManager = Injekt.get()
downloadManager.deleteManga(manga,source)
}
/** /**
* Changes the active display mode. * Changes the active display mode.
*/ */

View File

@ -171,12 +171,29 @@ open class CatalogueSearchController(
return null return null
} }
override fun handleBack(): Boolean {
return if (extensionFilter != null) {
activity?.finishAffinity()
true
} else super.handleBack()
}
/** /**
* Add search result to adapter. * Add search result to adapter.
* *
* @param searchResult result of search. * @param searchResult result of search.
*/ */
fun setItems(searchResult: List<CatalogueSearchItem>) { fun setItems(searchResult: List<CatalogueSearchItem>) {
if (extensionFilter != null) {
val results = searchResult.first().results
if (results != null && results.size == 1) {
val manga = results.first().manga
router.pushController(MangaController(manga,true,fromExtension = true)
.withFadeTransaction()
)
return
}
}
adapter?.updateDataSet(searchResult) adapter?.updateDataSet(searchResult)
} }

View File

@ -1,43 +0,0 @@
package eu.kanade.tachiyomi.ui.library
import android.app.Dialog
import android.os.Bundle
import com.afollestad.materialdialogs.MaterialDialog
import com.bluelinelabs.conductor.Controller
import eu.kanade.tachiyomi.R
import eu.kanade.tachiyomi.data.database.models.Manga
import eu.kanade.tachiyomi.ui.base.controller.DialogController
import eu.kanade.tachiyomi.widget.DialogCheckboxView
class DeleteLibraryMangasDialog<T>(bundle: Bundle? = null) :
DialogController(bundle) where T : Controller, T: DeleteLibraryMangasDialog.Listener {
private var mangas = emptyList<Manga>()
constructor(target: T, mangas: List<Manga>) : this() {
this.mangas = mangas
targetController = target
}
override fun onCreateDialog(savedViewState: Bundle?): Dialog {
val view = DialogCheckboxView(activity!!).apply {
setDescription(R.string.confirm_delete_manga)
setOptionDescription(R.string.also_delete_chapters)
}
return MaterialDialog.Builder(activity!!)
.title(R.string.action_remove)
.customView(view, true)
.positiveText(android.R.string.yes)
.negativeText(android.R.string.no)
.onPositive { _, _ ->
val deleteChapters = view.isChecked()
(targetController as? Listener)?.deleteMangasFromLibrary(mangas, deleteChapters)
}
.build()
}
interface Listener {
fun deleteMangasFromLibrary(mangas: List<Manga>, deleteChapters: Boolean)
}
}

View File

@ -21,6 +21,7 @@ import androidx.core.view.GravityCompat
import com.bluelinelabs.conductor.ControllerChangeHandler import com.bluelinelabs.conductor.ControllerChangeHandler
import com.bluelinelabs.conductor.ControllerChangeType import com.bluelinelabs.conductor.ControllerChangeType
import com.f2prateek.rx.preferences.Preference import com.f2prateek.rx.preferences.Preference
import com.google.android.material.snackbar.BaseTransientBottomBar
import com.google.android.material.snackbar.Snackbar import com.google.android.material.snackbar.Snackbar
import com.google.android.material.tabs.TabLayout import com.google.android.material.tabs.TabLayout
import com.jakewharton.rxbinding.support.v4.view.pageSelections import com.jakewharton.rxbinding.support.v4.view.pageSelections
@ -66,7 +67,6 @@ class LibraryController(
SecondaryDrawerController, SecondaryDrawerController,
ActionMode.Callback, ActionMode.Callback,
ChangeMangaCategoriesDialog.Listener, ChangeMangaCategoriesDialog.Listener,
DeleteLibraryMangasDialog.Listener,
MigrationInterface { MigrationInterface {
/** /**
@ -530,14 +530,24 @@ class LibraryController(
private fun deleteMangasFromLibrary() { private fun deleteMangasFromLibrary() {
val mangas = selectedMangas.toList() val mangas = selectedMangas.toList()
presenter.removeMangaFromLibrary(mangas, true) presenter.removeMangaFromLibrary(mangas)
destroyActionModeIfNeeded() destroyActionModeIfNeeded()
snack?.dismiss() snack?.dismiss()
snack = view?.snack(activity?.getString(R.string.manga_removed_library) ?: "", 5000) { snack = view?.snack(activity?.getString(R.string.manga_removed_library) ?: "", Snackbar.LENGTH_INDEFINITE) {
var undoing = false
setAction(R.string.action_undo) { setAction(R.string.action_undo) {
presenter.addMangas(mangas) presenter.addMangas(mangas)
undoing = true
} }
addCallback(object : BaseTransientBottomBar.BaseCallback<Snackbar>() {
override fun onDismissed(transientBottomBar: Snackbar?, event: Int) {
super.onDismissed(transientBottomBar, event)
if (!undoing)
presenter.confirmDeletion(mangas)
}
})
} }
(activity as? MainActivity)?.setUndoSnackBar(snack)
} }
override fun updateCategoriesForMangas(mangas: List<Manga>, categories: List<Category>) { override fun updateCategoriesForMangas(mangas: List<Manga>, categories: List<Category>) {
@ -545,11 +555,6 @@ class LibraryController(
destroyActionModeIfNeeded() destroyActionModeIfNeeded()
} }
override fun deleteMangasFromLibrary(mangas: List<Manga>, deleteChapters: Boolean) {
presenter.removeMangaFromLibrary(mangas, deleteChapters)
destroyActionModeIfNeeded()
}
/** /**
* Changes the cover for the selected manga. * Changes the cover for the selected manga.
*/ */

View File

@ -305,7 +305,7 @@ class LibraryPresenter(
* @param mangas the list of manga to delete. * @param mangas the list of manga to delete.
* @param deleteChapters whether to also delete downloaded chapters. * @param deleteChapters whether to also delete downloaded chapters.
*/ */
fun removeMangaFromLibrary(mangas: List<Manga>, deleteChapters: Boolean) { fun removeMangaFromLibrary(mangas: List<Manga>) {
// Create a set of the list // Create a set of the list
val mangaToDelete = mangas.distinctBy { it.id } val mangaToDelete = mangas.distinctBy { it.id }
mangaToDelete.forEach { it.favorite = false } mangaToDelete.forEach { it.favorite = false }
@ -314,20 +314,18 @@ class LibraryPresenter(
.onErrorResumeNext { Observable.empty() } .onErrorResumeNext { Observable.empty() }
.subscribeOn(Schedulers.io()) .subscribeOn(Schedulers.io())
.subscribe() .subscribe()
}
fun confirmDeletion(mangas: List<Manga>) {
Observable.fromCallable { Observable.fromCallable {
val mangaToDelete = mangas.distinctBy { it.id }
mangaToDelete.forEach { manga -> mangaToDelete.forEach { manga ->
coverCache.deleteFromCache(manga, 5000) coverCache.deleteFromCache(manga.thumbnail_url)
if (deleteChapters) { val source = sourceManager.get(manga.source) as? HttpSource
val source = sourceManager.get(manga.source) as? HttpSource if (source != null)
if (source != null) { downloadManager.deleteManga(manga, source)
downloadManager.deleteManga(manga, source, 5000)
}
}
} }
} }.subscribeOn(Schedulers.io()).subscribe()
.subscribeOn(Schedulers.io())
.subscribe()
} }
fun addMangas(mangas: List<Manga>) { fun addMangas(mangas: List<Manga>) {

View File

@ -5,25 +5,19 @@ import android.app.SearchManager
import android.content.Intent import android.content.Intent
import android.content.res.Configuration import android.content.res.Configuration
import android.graphics.Color import android.graphics.Color
import android.graphics.Rect
import android.os.Build import android.os.Build
import android.os.Bundle import android.os.Bundle
import androidx.annotation.NonNull import android.view.MotionEvent
import androidx.annotation.Px
import androidx.annotation.RequiresApi
import androidx.core.view.GravityCompat import androidx.core.view.GravityCompat
import androidx.core.view.ViewCompat
import androidx.core.view.WindowInsetsCompat
import androidx.drawerlayout.widget.DrawerLayout
import androidx.appcompat.app.AppCompatDelegate
import androidx.appcompat.app.AppCompatDelegate.* import androidx.appcompat.app.AppCompatDelegate.*
import androidx.appcompat.graphics.drawable.DrawerArrowDrawable import androidx.appcompat.graphics.drawable.DrawerArrowDrawable
import android.view.View import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
import android.view.WindowInsets
import android.view.WindowManager
import android.widget.FrameLayout import android.widget.FrameLayout
import android.widget.LinearLayout import android.widget.LinearLayout
import com.bluelinelabs.conductor.* import com.bluelinelabs.conductor.*
import com.google.android.material.snackbar.Snackbar
import eu.kanade.tachiyomi.Migrations import eu.kanade.tachiyomi.Migrations
import eu.kanade.tachiyomi.R import eu.kanade.tachiyomi.R
import eu.kanade.tachiyomi.data.notification.NotificationReceiver import eu.kanade.tachiyomi.data.notification.NotificationReceiver
@ -34,7 +28,6 @@ import eu.kanade.tachiyomi.ui.catalogue.CatalogueController
import eu.kanade.tachiyomi.ui.catalogue.global_search.CatalogueSearchController import eu.kanade.tachiyomi.ui.catalogue.global_search.CatalogueSearchController
import eu.kanade.tachiyomi.ui.download.DownloadController import eu.kanade.tachiyomi.ui.download.DownloadController
import eu.kanade.tachiyomi.ui.extension.ExtensionController import eu.kanade.tachiyomi.ui.extension.ExtensionController
import eu.kanade.tachiyomi.ui.library.HeightTopWindowInsetsListener
import eu.kanade.tachiyomi.ui.library.LibraryController import eu.kanade.tachiyomi.ui.library.LibraryController
import eu.kanade.tachiyomi.ui.manga.MangaController import eu.kanade.tachiyomi.ui.manga.MangaController
import eu.kanade.tachiyomi.ui.recent_updates.RecentChaptersController import eu.kanade.tachiyomi.ui.recent_updates.RecentChaptersController
@ -42,17 +35,17 @@ import eu.kanade.tachiyomi.ui.recently_read.RecentlyReadController
import eu.kanade.tachiyomi.ui.setting.SettingsMainController import eu.kanade.tachiyomi.ui.setting.SettingsMainController
import eu.kanade.tachiyomi.util.NoopWindowInsetsListener import eu.kanade.tachiyomi.util.NoopWindowInsetsListener
import eu.kanade.tachiyomi.util.doOnApplyWindowInsets import eu.kanade.tachiyomi.util.doOnApplyWindowInsets
import eu.kanade.tachiyomi.util.launchUI
import eu.kanade.tachiyomi.util.marginBottom import eu.kanade.tachiyomi.util.marginBottom
import eu.kanade.tachiyomi.util.marginTop import eu.kanade.tachiyomi.util.marginTop
import eu.kanade.tachiyomi.util.openInBrowser import eu.kanade.tachiyomi.util.openInBrowser
import eu.kanade.tachiyomi.util.updateLayoutParams import eu.kanade.tachiyomi.util.updateLayoutParams
import eu.kanade.tachiyomi.util.updatePadding import eu.kanade.tachiyomi.util.updatePadding
import eu.kanade.tachiyomi.util.updatePaddingRelative import eu.kanade.tachiyomi.util.updatePaddingRelative
import kotlinx.android.synthetic.main.chapters_controller.view.*
import kotlinx.android.synthetic.main.main_activity.* import kotlinx.android.synthetic.main.main_activity.*
import kotlinx.coroutines.delay
import uy.kohesive.injekt.injectLazy import uy.kohesive.injekt.injectLazy
class MainActivity : BaseActivity() { class MainActivity : BaseActivity() {
private lateinit var router: Router private lateinit var router: Router
@ -63,6 +56,25 @@ class MainActivity : BaseActivity() {
private var secondaryDrawer: ViewGroup? = null private var secondaryDrawer: ViewGroup? = null
private var snackBar:Snackbar? = null
var extraRectForUndo:Rect? = null
private var canDismissSnackBar = false
fun setUndoSnackBar(snackBar: Snackbar?, extraViewToCheck: View? = null) {
this.snackBar = snackBar
canDismissSnackBar = false
launchUI {
delay(1000)
canDismissSnackBar = true
}
if (extraViewToCheck != null) {
extraRectForUndo = Rect()
extraViewToCheck.getGlobalVisibleRect(extraRectForUndo)
}
else
extraRectForUndo = null
}
private val startScreenId by lazy { private val startScreenId by lazy {
when (preferences.startScreen()) { when (preferences.startScreen()) {
2 -> R.id.nav_drawer_recently_read 2 -> R.id.nav_drawer_recently_read
@ -296,6 +308,28 @@ class MainActivity : BaseActivity() {
router.setRoot(controller.withFadeTransaction().tag(id.toString())) router.setRoot(controller.withFadeTransaction().tag(id.toString()))
} }
override fun dispatchTouchEvent(ev: MotionEvent?): Boolean {
if (ev?.action == MotionEvent.ACTION_DOWN) {
if (snackBar != null && snackBar!!.isShown) {
val sRect = Rect()
snackBar!!.view.getGlobalVisibleRect(sRect)
//This way the snackbar will only be dismissed if
//the user clicks outside it.
if (canDismissSnackBar && !sRect.contains(ev.x.toInt(), ev.y.toInt())
&& (extraRectForUndo == null ||
!extraRectForUndo!!.contains(ev.x.toInt(), ev.y.toInt()))) {
snackBar?.dismiss()
snackBar = null
extraRectForUndo = null
}
}
else if (snackBar != null)
snackBar = null
}
return super.dispatchTouchEvent(ev)
}
private fun syncActivityViewWithController(to: Controller?, from: Controller? = null) { private fun syncActivityViewWithController(to: Controller?, from: Controller? = null) {
if (from is DialogController || to is DialogController) { if (from is DialogController || to is DialogController) {
return return

View File

@ -1,22 +1,20 @@
package eu.kanade.tachiyomi.ui.manga package eu.kanade.tachiyomi.ui.manga
import android.Manifest.permission.WRITE_EXTERNAL_STORAGE import android.Manifest.permission.WRITE_EXTERNAL_STORAGE
import android.app.NotificationManager import android.app.Activity
import android.content.Context
import android.content.Intent
import android.os.Bundle import android.os.Bundle
import com.google.android.material.tabs.TabLayout
import androidx.vectordrawable.graphics.drawable.VectorDrawableCompat
import android.view.LayoutInflater import android.view.LayoutInflater
import android.view.View import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
import android.widget.LinearLayout import android.widget.LinearLayout
import android.widget.TextView import android.widget.TextView
import androidx.vectordrawable.graphics.drawable.VectorDrawableCompat
import com.bluelinelabs.conductor.ControllerChangeHandler import com.bluelinelabs.conductor.ControllerChangeHandler
import com.bluelinelabs.conductor.ControllerChangeType import com.bluelinelabs.conductor.ControllerChangeType
import com.bluelinelabs.conductor.Router import com.bluelinelabs.conductor.Router
import com.bluelinelabs.conductor.RouterTransaction import com.bluelinelabs.conductor.RouterTransaction
import com.bluelinelabs.conductor.support.RouterPagerAdapter import com.bluelinelabs.conductor.support.RouterPagerAdapter
import com.google.android.material.tabs.TabLayout
import com.jakewharton.rxrelay.BehaviorRelay import com.jakewharton.rxrelay.BehaviorRelay
import com.jakewharton.rxrelay.PublishRelay import com.jakewharton.rxrelay.PublishRelay
import eu.kanade.tachiyomi.R import eu.kanade.tachiyomi.R
@ -42,7 +40,10 @@ import java.util.Date
class MangaController : RxController, TabbedController { class MangaController : RxController, TabbedController {
constructor(manga: Manga?, fromCatalogue: Boolean = false) : super(Bundle().apply { constructor(manga: Manga?, fromCatalogue: Boolean = false, fromExtension: Boolean = false) :
super
(Bundle()
.apply {
putLong(MANGA_EXTRA, manga?.id ?: 0) putLong(MANGA_EXTRA, manga?.id ?: 0)
putBoolean(FROM_CATALOGUE_EXTRA, fromCatalogue) putBoolean(FROM_CATALOGUE_EXTRA, fromCatalogue)
}) { }) {
@ -50,6 +51,7 @@ class MangaController : RxController, TabbedController {
if (manga != null) { if (manga != null) {
source = Injekt.get<SourceManager>().getOrStub(manga.source) source = Injekt.get<SourceManager>().getOrStub(manga.source)
} }
backClosesApp = fromExtension
} }
constructor(manga: Manga?, startY:Float?) : super(Bundle().apply { constructor(manga: Manga?, startY:Float?) : super(Bundle().apply {
@ -74,12 +76,32 @@ class MangaController : RxController, TabbedController {
) )
} }
override fun onRestoreInstanceState(savedInstanceState: Bundle) {
backClosesApp = false
super.onRestoreInstanceState(savedInstanceState)
}
override fun onActivityResumed(activity: Activity) {
backClosesApp = false
super.onActivityResumed(activity)
}
override fun handleBack(): Boolean {
return if (backClosesApp) {
activity?.finishAffinity()
true
} else super.handleBack()
}
var manga: Manga? = null var manga: Manga? = null
private set private set
var source: Source? = null var source: Source? = null
private set private set
var backClosesApp = false
private set
var startingChapterYPos:Float? = null var startingChapterYPos:Float? = null
private var adapter: MangaDetailAdapter? = null private var adapter: MangaDetailAdapter? = null

View File

@ -34,6 +34,7 @@ import android.content.Context
import android.util.AttributeSet import android.util.AttributeSet
import androidx.core.view.ViewCompat import androidx.core.view.ViewCompat
import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView
import eu.kanade.tachiyomi.ui.main.MainActivity
import kotlin.math.* import kotlin.math.*
class ChaptersController() : NucleusController<ChaptersPresenter>(), class ChaptersController() : NucleusController<ChaptersPresenter>(),
@ -411,19 +412,20 @@ class ChaptersController() : NucleusController<ChaptersPresenter>(),
val view = view val view = view
destroyActionModeIfNeeded() destroyActionModeIfNeeded()
presenter.downloadChapters(chapters) presenter.downloadChapters(chapters)
if (view != null && !presenter.manga.favorite && (snack == null || snack?.getText() != view.context.getString(R.string.snack_add_to_library))) { if (view != null && !presenter.manga.favorite && (snack == null ||
snack = snack?.getText() != view.context.getString(R.string.snack_add_to_library))) {
view.snack(view.context.getString(R.string.snack_add_to_library), Snackbar.LENGTH_INDEFINITE) { snack = view.snack(view.context.getString(R.string.snack_add_to_library), Snackbar.LENGTH_INDEFINITE) {
setAction(R.string.action_add) { setAction(R.string.action_add) {
presenter.addToLibrary() presenter.addToLibrary()
}
addCallback(object : BaseTransientBottomBar.BaseCallback<Snackbar>() {
override fun onDismissed(transientBottomBar: Snackbar?, event: Int) {
super.onDismissed(transientBottomBar, event)
if (snack == transientBottomBar) snack = null
}
})
} }
addCallback(object : BaseTransientBottomBar.BaseCallback<Snackbar>() {
override fun onDismissed(transientBottomBar: Snackbar?, event: Int) {
super.onDismissed(transientBottomBar, event)
if (snack == transientBottomBar) snack = null
}
})
}
(activity as? MainActivity)?.setUndoSnackBar(snack)
} }
} }

View File

@ -11,18 +11,22 @@ import android.graphics.Bitmap
import android.graphics.drawable.Drawable import android.graphics.drawable.Drawable
import android.os.Build import android.os.Build
import android.os.Bundle import android.os.Bundle
import com.google.android.material.snackbar.Snackbar import android.view.LayoutInflater
import android.view.Menu
import android.view.MenuInflater
import android.view.MenuItem
import android.view.View
import android.view.ViewGroup
import androidx.core.content.pm.ShortcutInfoCompat import androidx.core.content.pm.ShortcutInfoCompat
import androidx.core.content.pm.ShortcutManagerCompat import androidx.core.content.pm.ShortcutManagerCompat
import androidx.core.graphics.drawable.IconCompat import androidx.core.graphics.drawable.IconCompat
import android.view.*
import android.widget.Toast
import com.afollestad.materialdialogs.MaterialDialog import com.afollestad.materialdialogs.MaterialDialog
import com.bumptech.glide.load.engine.DiskCacheStrategy import com.bumptech.glide.load.engine.DiskCacheStrategy
import com.bumptech.glide.load.resource.bitmap.RoundedCorners import com.bumptech.glide.load.resource.bitmap.RoundedCorners
import com.bumptech.glide.request.target.CustomTarget import com.bumptech.glide.request.target.CustomTarget
import com.bumptech.glide.request.target.SimpleTarget
import com.bumptech.glide.request.transition.Transition import com.bumptech.glide.request.transition.Transition
import com.google.android.material.snackbar.BaseTransientBottomBar
import com.google.android.material.snackbar.Snackbar
import com.jakewharton.rxbinding.support.v4.widget.refreshes import com.jakewharton.rxbinding.support.v4.widget.refreshes
import com.jakewharton.rxbinding.view.clicks import com.jakewharton.rxbinding.view.clicks
import com.jakewharton.rxbinding.view.longClicks import com.jakewharton.rxbinding.view.longClicks
@ -42,7 +46,14 @@ import eu.kanade.tachiyomi.ui.catalogue.global_search.CatalogueSearchController
import eu.kanade.tachiyomi.ui.library.ChangeMangaCategoriesDialog import eu.kanade.tachiyomi.ui.library.ChangeMangaCategoriesDialog
import eu.kanade.tachiyomi.ui.main.MainActivity import eu.kanade.tachiyomi.ui.main.MainActivity
import eu.kanade.tachiyomi.ui.manga.MangaController import eu.kanade.tachiyomi.ui.manga.MangaController
import eu.kanade.tachiyomi.util.* import eu.kanade.tachiyomi.util.doOnApplyWindowInsets
import eu.kanade.tachiyomi.util.getUriCompat
import eu.kanade.tachiyomi.util.marginBottom
import eu.kanade.tachiyomi.util.openInBrowser
import eu.kanade.tachiyomi.util.snack
import eu.kanade.tachiyomi.util.toast
import eu.kanade.tachiyomi.util.updateLayoutParams
import eu.kanade.tachiyomi.util.updatePaddingRelative
import jp.wasabeef.glide.transformations.CropSquareTransformation import jp.wasabeef.glide.transformations.CropSquareTransformation
import jp.wasabeef.glide.transformations.MaskTransformation import jp.wasabeef.glide.transformations.MaskTransformation
import kotlinx.android.synthetic.main.manga_info_controller.* import kotlinx.android.synthetic.main.manga_info_controller.*
@ -460,11 +471,19 @@ class MangaInfoController : NucleusController<MangaInfoPresenter>(),
val view = container val view = container
snack?.dismiss() snack?.dismiss()
if (view != null) { if (view != null) {
snack = view.snack(view.context.getString(R.string.manga_removed_library), 5000) { snack = view.snack(view.context.getString(R.string.manga_removed_library), Snackbar.LENGTH_INDEFINITE) {
setAction(R.string.action_undo) { setAction(R.string.action_undo) {
presenter.setFavorite(true) presenter.setFavorite(true)
} }
addCallback(object : BaseTransientBottomBar.BaseCallback<Snackbar>() {
override fun onDismissed(transientBottomBar: Snackbar?, event: Int) {
super.onDismissed(transientBottomBar, event)
if (!presenter.manga.favorite)
presenter.confirmDeletion()
}
})
} }
(activity as? MainActivity)?.setUndoSnackBar(snack, fab_favorite)
} }
} }
@ -480,7 +499,7 @@ class MangaInfoController : NucleusController<MangaInfoPresenter>(),
val categories = presenter.getCategories() val categories = presenter.getCategories()
if (categories.isEmpty()) { if (categories.isEmpty()) {
// no categories exist, display a message about adding categories // no categories exist, display a message about adding categories
activity?.toast(activity?.getString(R.string.action_add_category)) snack = container?.snack(R.string.action_add_category)
} else { } else {
val ids = presenter.getMangaCategoryIds(manga) val ids = presenter.getMangaCategoryIds(manga)
val preselected = ids.mapNotNull { id -> val preselected = ids.mapNotNull { id ->

View File

@ -109,15 +109,16 @@ class MangaInfoPresenter(
*/ */
fun toggleFavorite(): Boolean { fun toggleFavorite(): Boolean {
manga.favorite = !manga.favorite manga.favorite = !manga.favorite
if (!manga.favorite) {
coverCache.deleteFromCache(manga, 5000)
downloadManager.deleteManga(manga, source, 5000)
}
db.insertManga(manga).executeAsBlocking() db.insertManga(manga).executeAsBlocking()
sendMangaToView() sendMangaToView()
return manga.favorite return manga.favorite
} }
fun confirmDeletion() {
coverCache.deleteFromCache(manga.thumbnail_url)
downloadManager.deleteManga(manga, source)
}
fun setFavorite(favorite: Boolean) { fun setFavorite(favorite: Boolean) {
if (manga.favorite == favorite) { if (manga.favorite == favorite) {
return return

View File

@ -89,7 +89,7 @@ class RecentChaptersController : NucleusController<RecentChaptersPresenter>(),
swipe_refresh.refreshes().subscribeUntilDestroy { swipe_refresh.refreshes().subscribeUntilDestroy {
if (!LibraryUpdateService.isRunning(view.context)) { if (!LibraryUpdateService.isRunning(view.context)) {
LibraryUpdateService.start(view.context) LibraryUpdateService.start(view.context)
view.snack(R.string.action_update_library) view.snack(R.string.updating_library)
} }
// It can be a very long operation, so we disable swipe refresh and show a snackbar. // It can be a very long operation, so we disable swipe refresh and show a snackbar.
swipe_refresh.isRefreshing = false swipe_refresh.isRefreshing = false

View File

@ -50,7 +50,7 @@
<string name="action_remove_bookmark">Remove bookmark</string> <string name="action_remove_bookmark">Remove bookmark</string>
<string name="action_delete">Delete</string> <string name="action_delete">Delete</string>
<string name="action_update">Update</string> <string name="action_update">Update</string>
<string name="action_update_library">Updating library</string> <string name="action_update_library">Update library</string>
<string name="action_edit">Edit</string> <string name="action_edit">Edit</string>
<string name="action_add">Add</string> <string name="action_add">Add</string>
<string name="action_add_category">Add category</string> <string name="action_add_category">Add category</string>
@ -317,6 +317,7 @@
<!-- Library fragment --> <!-- Library fragment -->
<string name="library_search_hint">Title or author…</string> <string name="library_search_hint">Title or author…</string>
<string name="updating_library">Updating library</string>
<string name="updating_category">Updating category</string> <string name="updating_category">Updating category</string>
<string name="local_source_badge">Local</string> <string name="local_source_badge">Local</string>
<string name="confirm_delete_manga">Are you sure you want to remove selected manga?</string> <string name="confirm_delete_manga">Are you sure you want to remove selected manga?</string>