mirror of
https://github.com/tachiyomiorg/tachiyomi.git
synced 2024-11-10 20:15:09 +01:00
Added option to save/share manga cover when zoomed in
This commit is contained in:
parent
7cc6bd6f0a
commit
99010acc24
@ -30,6 +30,7 @@ import androidx.appcompat.view.ActionMode
|
|||||||
import androidx.appcompat.widget.PopupMenu
|
import androidx.appcompat.widget.PopupMenu
|
||||||
import androidx.appcompat.widget.SearchView
|
import androidx.appcompat.widget.SearchView
|
||||||
import androidx.core.graphics.ColorUtils
|
import androidx.core.graphics.ColorUtils
|
||||||
|
import androidx.core.view.iterator
|
||||||
import androidx.palette.graphics.Palette
|
import androidx.palette.graphics.Palette
|
||||||
import androidx.recyclerview.widget.ItemTouchHelper
|
import androidx.recyclerview.widget.ItemTouchHelper
|
||||||
import androidx.recyclerview.widget.LinearLayoutManager
|
import androidx.recyclerview.widget.LinearLayoutManager
|
||||||
@ -177,6 +178,7 @@ class MangaDetailsController :
|
|||||||
private var currentAnimator: Animator? = null
|
private var currentAnimator: Animator? = null
|
||||||
|
|
||||||
var headerHeight = 0
|
var headerHeight = 0
|
||||||
|
var fullCoverActive = false
|
||||||
|
|
||||||
override fun getTitle(): String? {
|
override fun getTitle(): String? {
|
||||||
return null
|
return null
|
||||||
@ -190,6 +192,7 @@ class MangaDetailsController :
|
|||||||
override fun onViewCreated(view: View) {
|
override fun onViewCreated(view: View) {
|
||||||
super.onViewCreated(view)
|
super.onViewCreated(view)
|
||||||
coverColor = null
|
coverColor = null
|
||||||
|
fullCoverActive = false
|
||||||
|
|
||||||
setRecycler(view)
|
setRecycler(view)
|
||||||
setPaletteColor()
|
setPaletteColor()
|
||||||
@ -756,6 +759,19 @@ class MangaDetailsController :
|
|||||||
searchView.clearFocus()
|
searchView.clearFocus()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
val menuItems = menu.iterator()
|
||||||
|
while (menuItems.hasNext()) {
|
||||||
|
menuItems.next().isVisible = !fullCoverActive
|
||||||
|
}
|
||||||
|
val saveItem = menu.findItem(R.id.save)
|
||||||
|
val shareItem = menu.findItem(R.id.share)
|
||||||
|
saveItem.isVisible = fullCoverActive
|
||||||
|
shareItem.isVisible = fullCoverActive
|
||||||
|
if (fullCoverActive) {
|
||||||
|
saveItem.icon.setTint(Color.WHITE)
|
||||||
|
shareItem.icon.setTint(Color.WHITE)
|
||||||
|
}
|
||||||
|
|
||||||
setOnQueryTextChangeListener(searchView) {
|
setOnQueryTextChangeListener(searchView) {
|
||||||
query = it ?: ""
|
query = it ?: ""
|
||||||
if (query.isNotEmpty()) getHeader()?.collapse()
|
if (query.isNotEmpty()) getHeader()?.collapse()
|
||||||
@ -798,6 +814,28 @@ class MangaDetailsController :
|
|||||||
R.id.download_next, R.id.download_next_5, R.id.download_custom, R.id.download_unread, R.id.download_all -> downloadChapters(
|
R.id.download_next, R.id.download_next_5, R.id.download_custom, R.id.download_unread, R.id.download_all -> downloadChapters(
|
||||||
item.itemId
|
item.itemId
|
||||||
)
|
)
|
||||||
|
R.id.save -> {
|
||||||
|
if (presenter.saveCover()) {
|
||||||
|
activity?.toast(R.string.cover_saved)
|
||||||
|
} else {
|
||||||
|
activity?.toast(R.string.error_saving_cover)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
R.id.share -> {
|
||||||
|
val cover = presenter.shareCover()
|
||||||
|
if (cover != null) {
|
||||||
|
val stream = cover.getUriCompat(activity!!)
|
||||||
|
val intent = Intent(Intent.ACTION_SEND).apply {
|
||||||
|
putExtra(Intent.EXTRA_STREAM, stream)
|
||||||
|
flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_GRANT_READ_URI_PERMISSION
|
||||||
|
clipData = ClipData.newRawUri(null, stream)
|
||||||
|
type = "image/*"
|
||||||
|
}
|
||||||
|
startActivity(Intent.createChooser(intent, activity?.getString(R.string.share)))
|
||||||
|
} else {
|
||||||
|
activity?.toast(R.string.error_sharing_cover)
|
||||||
|
}
|
||||||
|
}
|
||||||
else -> return super.onOptionsItemSelected(item)
|
else -> return super.onOptionsItemSelected(item)
|
||||||
}
|
}
|
||||||
return true
|
return true
|
||||||
@ -1324,6 +1362,24 @@ class MangaDetailsController :
|
|||||||
}
|
}
|
||||||
expandedImageView.requestLayout()
|
expandedImageView.requestLayout()
|
||||||
|
|
||||||
|
val activity = activity as? MainActivity ?: return
|
||||||
|
val currTheme = activity.appbar.context.theme
|
||||||
|
val currColor = activity.drawerArrow?.color
|
||||||
|
if (!activity.isInNightMode()) {
|
||||||
|
activity.appbar.context.setTheme(R.style.ThemeOverlay_AppCompat_Dark_ActionBar)
|
||||||
|
|
||||||
|
val iconPrimary = Color.WHITE
|
||||||
|
activity.toolbar.setTitleTextColor(iconPrimary)
|
||||||
|
activity.drawerArrow?.color = iconPrimary
|
||||||
|
activity.toolbar.overflowIcon?.setTint(iconPrimary)
|
||||||
|
activity.window.decorView.systemUiVisibility =
|
||||||
|
activity.window.decorView.systemUiVisibility.rem(
|
||||||
|
View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR
|
||||||
|
)
|
||||||
|
}
|
||||||
|
fullCoverActive = true
|
||||||
|
activity.invalidateOptionsMenu()
|
||||||
|
|
||||||
expandedImageView.post {
|
expandedImageView.post {
|
||||||
val defMargin = 16.dpToPx
|
val defMargin = 16.dpToPx
|
||||||
expandedImageView.updateLayoutParams<ViewGroup.MarginLayoutParams> {
|
expandedImageView.updateLayoutParams<ViewGroup.MarginLayoutParams> {
|
||||||
@ -1374,6 +1430,8 @@ class MangaDetailsController :
|
|||||||
expandedImageView.setOnClickListener {
|
expandedImageView.setOnClickListener {
|
||||||
currentAnimator?.cancel()
|
currentAnimator?.cancel()
|
||||||
|
|
||||||
|
fullCoverActive = false
|
||||||
|
activity.invalidateOptionsMenu()
|
||||||
val rect2 = Rect()
|
val rect2 = Rect()
|
||||||
thumbView.getGlobalVisibleRect(rect2)
|
thumbView.getGlobalVisibleRect(rect2)
|
||||||
expandedImageView.updateLayoutParams<ViewGroup.MarginLayoutParams> {
|
expandedImageView.updateLayoutParams<ViewGroup.MarginLayoutParams> {
|
||||||
@ -1399,6 +1457,21 @@ class MangaDetailsController :
|
|||||||
play(ObjectAnimator.ofFloat(fullBackdrop, View.ALPHA, 0f))
|
play(ObjectAnimator.ofFloat(fullBackdrop, View.ALPHA, 0f))
|
||||||
duration = shortAnimationDuration.toLong()
|
duration = shortAnimationDuration.toLong()
|
||||||
interpolator = DecelerateInterpolator()
|
interpolator = DecelerateInterpolator()
|
||||||
|
|
||||||
|
if (!activity.isInNightMode()) {
|
||||||
|
activity.appbar.context.setTheme(
|
||||||
|
ThemeUtil.theme(presenter.preferences.theme())
|
||||||
|
)
|
||||||
|
|
||||||
|
val iconPrimary = currColor ?: Color.WHITE
|
||||||
|
activity.toolbar.setTitleTextColor(iconPrimary)
|
||||||
|
activity.drawerArrow?.color = iconPrimary
|
||||||
|
activity.toolbar.overflowIcon?.setTint(iconPrimary)
|
||||||
|
activity.window.decorView.systemUiVisibility =
|
||||||
|
activity.window.decorView.systemUiVisibility.or(
|
||||||
|
View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR
|
||||||
|
)
|
||||||
|
}
|
||||||
addListener(
|
addListener(
|
||||||
object : AnimatorListenerAdapter() {
|
object : AnimatorListenerAdapter() {
|
||||||
|
|
||||||
|
@ -3,6 +3,7 @@ package eu.kanade.tachiyomi.ui.manga
|
|||||||
import android.app.Application
|
import android.app.Application
|
||||||
import android.graphics.Bitmap
|
import android.graphics.Bitmap
|
||||||
import android.net.Uri
|
import android.net.Uri
|
||||||
|
import android.os.Environment
|
||||||
import eu.kanade.tachiyomi.R
|
import eu.kanade.tachiyomi.R
|
||||||
import eu.kanade.tachiyomi.data.cache.CoverCache
|
import eu.kanade.tachiyomi.data.cache.CoverCache
|
||||||
import eu.kanade.tachiyomi.data.database.DatabaseHelper
|
import eu.kanade.tachiyomi.data.database.DatabaseHelper
|
||||||
@ -35,6 +36,7 @@ import eu.kanade.tachiyomi.util.chapter.ChapterUtil
|
|||||||
import eu.kanade.tachiyomi.util.chapter.syncChaptersWithSource
|
import eu.kanade.tachiyomi.util.chapter.syncChaptersWithSource
|
||||||
import eu.kanade.tachiyomi.util.lang.trimOrNull
|
import eu.kanade.tachiyomi.util.lang.trimOrNull
|
||||||
import eu.kanade.tachiyomi.util.storage.DiskUtil
|
import eu.kanade.tachiyomi.util.storage.DiskUtil
|
||||||
|
import eu.kanade.tachiyomi.util.system.ImageUtil
|
||||||
import eu.kanade.tachiyomi.util.system.executeOnIO
|
import eu.kanade.tachiyomi.util.system.executeOnIO
|
||||||
import kotlinx.coroutines.CoroutineScope
|
import kotlinx.coroutines.CoroutineScope
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
@ -694,6 +696,49 @@ class MangaDetailsPresenter(
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun shareCover(): File? {
|
||||||
|
return try {
|
||||||
|
val destDir = File(coverCache.context.cacheDir, "shared_image")
|
||||||
|
val file = saveCover(destDir)
|
||||||
|
file
|
||||||
|
} catch (e: Exception) {
|
||||||
|
null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun saveCover(): Boolean {
|
||||||
|
return try {
|
||||||
|
val directory = File(
|
||||||
|
Environment.getExternalStorageDirectory().absolutePath +
|
||||||
|
File.separator + Environment.DIRECTORY_PICTURES +
|
||||||
|
File.separator + "Tachiyomi"
|
||||||
|
)
|
||||||
|
saveCover(directory)
|
||||||
|
true
|
||||||
|
} catch (e: Exception) {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun saveCover(directory: File): File {
|
||||||
|
val cover = coverCache.getCoverFile(manga)
|
||||||
|
val type = ImageUtil.findImageType(cover.inputStream())
|
||||||
|
?: throw Exception("Not an image")
|
||||||
|
|
||||||
|
directory.mkdirs()
|
||||||
|
|
||||||
|
// Build destination file.
|
||||||
|
val filename = DiskUtil.buildValidFilename("${manga.title}.${type.extension}")
|
||||||
|
|
||||||
|
val destFile = File(directory, filename)
|
||||||
|
cover.inputStream().use { input ->
|
||||||
|
destFile.outputStream().use { output ->
|
||||||
|
input.copyTo(output)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return destFile
|
||||||
|
}
|
||||||
|
|
||||||
fun isTracked(): Boolean =
|
fun isTracked(): Boolean =
|
||||||
loggedServices.any { service -> tracks.any { it.sync_id == service.id } }
|
loggedServices.any { service -> tracks.any { it.sync_id == service.id } }
|
||||||
|
|
||||||
|
@ -78,4 +78,17 @@
|
|||||||
android:title="@string/migrate"
|
android:title="@string/migrate"
|
||||||
app:showAsAction="never" />
|
app:showAsAction="never" />
|
||||||
|
|
||||||
|
<item
|
||||||
|
android:id="@+id/save"
|
||||||
|
android:icon="@drawable/ic_save_24dp"
|
||||||
|
android:title="@string/save"
|
||||||
|
android:visible="false"
|
||||||
|
app:showAsAction="ifRoom" />
|
||||||
|
|
||||||
|
<item
|
||||||
|
android:id="@+id/share"
|
||||||
|
android:icon="@drawable/ic_share_24dp"
|
||||||
|
android:title="@string/share"
|
||||||
|
android:visible="false"
|
||||||
|
app:showAsAction="ifRoom" />
|
||||||
</menu>
|
</menu>
|
||||||
|
@ -394,6 +394,9 @@
|
|||||||
<string name="must_be_in_library_to_edit">Manga must be in your library to edit</string>
|
<string name="must_be_in_library_to_edit">Manga must be in your library to edit</string>
|
||||||
<string name="remember_this_choice">Remember this choice</string>
|
<string name="remember_this_choice">Remember this choice</string>
|
||||||
<string name="source_not_installed">Source not installed</string>
|
<string name="source_not_installed">Source not installed</string>
|
||||||
|
<string name="cover_saved">Cover saved</string>
|
||||||
|
<string name="error_saving_cover">Error saving cover</string>
|
||||||
|
<string name="error_sharing_cover">Error sharing cover</string>
|
||||||
<plurals name="deleted_chapters">
|
<plurals name="deleted_chapters">
|
||||||
<item quantity="one">A chapter has been removed from the source:\n%2$s\nDelete
|
<item quantity="one">A chapter has been removed from the source:\n%2$s\nDelete
|
||||||
its download?</item>
|
its download?</item>
|
||||||
|
Loading…
Reference in New Issue
Block a user