Delete local manga

This commit is contained in:
Koitharu
2020-03-01 19:39:22 +02:00
parent fb0ce39b27
commit 4dff9f5bf1
13 changed files with 131 additions and 39 deletions

View File

@@ -27,7 +27,11 @@
android:name="android.app.default_searchable"
android:value=".ui.search.SearchActivity" />
</activity>
<activity android:name=".ui.details.MangaDetailsActivity" />
<activity android:name=".ui.details.MangaDetailsActivity" >
<intent-filter>
<action android:name="${applicationId}.action.VIEW_MANGA" />
</intent-filter>
</activity>
<activity android:name=".ui.reader.ReaderActivity" />
<activity
android:name=".ui.search.SearchActivity"

View File

@@ -1,4 +1,4 @@
package org.koitharu.kotatsu.ui.common
package org.koitharu.kotatsu.ui.common.dialog
import android.annotation.SuppressLint
import android.content.Context
@@ -71,7 +71,8 @@ class TextInputDialog private constructor(private val delegate: AlertDialog) :
return this
}
fun create() = TextInputDialog(delegate.create())
fun create() =
TextInputDialog(delegate.create())
}
}

View File

@@ -49,9 +49,9 @@ class ChaptersFragment : BaseFragment(R.layout.fragment_chapters), MangaDetailsV
progressBar.isVisible = isLoading
}
override fun onError(e: Exception) {
override fun onError(e: Exception) = Unit //handled in activity
}
override fun onMangaRemoved(manga: Manga) = Unit //handled in activity
override fun onHistoryChanged(history: MangaHistory?) {
adapter.currentChapterId = history?.chapterId

View File

@@ -6,6 +6,7 @@ import android.net.Uri
import android.os.Bundle
import android.view.Menu
import android.view.MenuItem
import android.widget.Toast
import androidx.core.content.pm.ShortcutManagerCompat
import androidx.core.net.toFile
import androidx.lifecycle.lifecycleScope
@@ -25,6 +26,7 @@ import org.koitharu.kotatsu.ui.download.DownloadService
import org.koitharu.kotatsu.utils.ShareHelper
import org.koitharu.kotatsu.utils.ShortcutUtils
import org.koitharu.kotatsu.utils.ext.getDisplayMessage
import org.koitharu.kotatsu.utils.ext.showDialog
class MangaDetailsActivity : BaseActivity(), MangaDetailsView {
@@ -58,6 +60,14 @@ class MangaDetailsActivity : BaseActivity(), MangaDetailsView {
override fun onLoadingStateChanged(isLoading: Boolean) = Unit
override fun onMangaRemoved(manga: Manga) {
Toast.makeText(
this, getString(R.string._s_deleted_from_local_storage, manga.title),
Toast.LENGTH_SHORT
).show()
finish()
}
override fun onError(e: Exception) {
Snackbar.make(pager, e.getDisplayMessage(resources), Snackbar.LENGTH_LONG).show()
}
@@ -68,9 +78,11 @@ class MangaDetailsActivity : BaseActivity(), MangaDetailsView {
}
override fun onPrepareOptionsMenu(menu: Menu): Boolean {
menu.findItem(R.id.action_save).isEnabled =
menu.findItem(R.id.action_save).isVisible =
manga?.source != null && manga?.source != MangaSource.LOCAL
menu.findItem(R.id.action_shortcut).isVisible = BuildConfig.DEBUG ||
menu.findItem(R.id.action_delete).isVisible =
manga?.source == MangaSource.LOCAL
menu.findItem(R.id.action_shortcut).isVisible = BuildConfig.DEBUG &&
ShortcutManagerCompat.isRequestPinShortcutSupported(this)
return super.onPrepareOptionsMenu(menu)
}
@@ -86,6 +98,19 @@ class MangaDetailsActivity : BaseActivity(), MangaDetailsView {
}
true
}
R.id.action_delete -> {
manga?.let { m ->
showDialog {
setTitle(R.string.delete_manga)
setMessage(getString(R.string.text_delete_local_manga, m.title))
setPositiveButton(R.string.delete) { _, _ ->
presenter.deleteLocal(m)
}
setNegativeButton(android.R.string.cancel, null)
}
}
true
}
R.id.action_save -> {
manga?.let {
DownloadService.start(this, it)
@@ -118,6 +143,8 @@ class MangaDetailsActivity : BaseActivity(), MangaDetailsView {
private const val EXTRA_MANGA = "manga"
const val ACTION_MANGA_VIEW = "${BuildConfig.APPLICATION_ID}.action.VIEW_MANGA"
fun newIntent(context: Context, manga: Manga) =
Intent(context, MangaDetailsActivity::class.java)
.putExtra(EXTRA_MANGA, manga)

View File

@@ -82,9 +82,9 @@ class MangaDetailsFragment : BaseFragment(R.layout.fragment_details), MangaDetai
progressBar.isVisible = isLoading
}
override fun onError(e: Exception) {
override fun onError(e: Exception) = Unit //handled in activity
}
override fun onMangaRemoved(manga: Manga) = Unit //handled in activity
private fun updateReadButton() {
if (manga?.chapters.isNullOrEmpty()) {

View File

@@ -1,5 +1,6 @@
package org.koitharu.kotatsu.ui.details
import kotlinx.coroutines.CancellationException
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
@@ -7,15 +8,20 @@ import moxy.InjectViewState
import moxy.presenterScope
import org.koitharu.kotatsu.BuildConfig
import org.koitharu.kotatsu.core.model.Manga
import org.koitharu.kotatsu.core.model.MangaSource
import org.koitharu.kotatsu.core.parser.LocalMangaRepository
import org.koitharu.kotatsu.domain.MangaProviderFactory
import org.koitharu.kotatsu.domain.favourites.FavouritesRepository
import org.koitharu.kotatsu.domain.favourites.OnFavouritesChangeListener
import org.koitharu.kotatsu.domain.history.HistoryRepository
import org.koitharu.kotatsu.domain.history.OnHistoryChangeListener
import org.koitharu.kotatsu.ui.common.BasePresenter
import org.koitharu.kotatsu.utils.ext.safe
import java.io.IOException
@InjectViewState
class MangaDetailsPresenter private constructor(): BasePresenter<MangaDetailsView>(), OnHistoryChangeListener,
class MangaDetailsPresenter private constructor() : BasePresenter<MangaDetailsView>(),
OnHistoryChangeListener,
OnFavouritesChangeListener {
private lateinit var historyRepository: HistoryRepository
@@ -57,6 +63,31 @@ class MangaDetailsPresenter private constructor(): BasePresenter<MangaDetailsVie
}
}
fun deleteLocal(manga: Manga) {
presenterScope.launch {
viewState.onLoadingStateChanged(true)
try {
withContext(Dispatchers.IO) {
val repository =
MangaProviderFactory.create(MangaSource.LOCAL) as LocalMangaRepository
repository.delete(manga) || throw IOException("Unable to delete file")
safe {
HistoryRepository().delete(manga)
}
}
viewState.onMangaRemoved(manga)
} catch (e: CancellationException) {
} catch (e: Exception) {
if (BuildConfig.DEBUG) {
e.printStackTrace()
}
viewState.onError(e)
} finally {
viewState.onLoadingStateChanged(false)
}
}
}
private fun loadHistory(manga: Manga) {
presenterScope.launch {
try {

View File

@@ -3,6 +3,7 @@ package org.koitharu.kotatsu.ui.details
import moxy.MvpView
import moxy.viewstate.strategy.alias.AddToEndSingle
import moxy.viewstate.strategy.alias.OneExecution
import moxy.viewstate.strategy.alias.SingleState
import org.koitharu.kotatsu.core.model.FavouriteCategory
import org.koitharu.kotatsu.core.model.Manga
import org.koitharu.kotatsu.core.model.MangaHistory
@@ -23,4 +24,7 @@ interface MangaDetailsView : MvpView {
@AddToEndSingle
fun onFavouriteChanged(categories: List<FavouriteCategory>)
@SingleState
fun onMangaRemoved(manga: Manga)
}

View File

@@ -49,7 +49,7 @@ class DownloadNotification(private val context: Context) {
chapter * PROGRESS_STEP + (page / pagesTotal.toFloat() * PROGRESS_STEP).roundToInt()
val percent = (progress / max.toFloat() * 100).roundToInt()
builder.setProgress(max, progress, false)
builder.setContentText(context.getString(R.string.downloading_d_percent, percent))
builder.setContentText("%d%%".format(percent))
}
fun setPostProcessing() {

View File

@@ -11,7 +11,7 @@ import org.koitharu.kotatsu.R
import org.koitharu.kotatsu.core.model.FavouriteCategory
import org.koitharu.kotatsu.core.model.Manga
import org.koitharu.kotatsu.ui.common.BaseBottomSheet
import org.koitharu.kotatsu.ui.common.TextInputDialog
import org.koitharu.kotatsu.ui.common.dialog.TextInputDialog
import org.koitharu.kotatsu.utils.ext.getDisplayMessage
import org.koitharu.kotatsu.utils.ext.withArgs

View File

@@ -14,6 +14,7 @@ import org.koitharu.kotatsu.R
import org.koitharu.kotatsu.core.model.Manga
import org.koitharu.kotatsu.ui.main.list.MangaListFragment
import org.koitharu.kotatsu.utils.ext.ellipsize
import org.koitharu.kotatsu.utils.ext.showDialog
import java.io.File
class LocalListFragment : MangaListFragment<File>() {
@@ -80,7 +81,14 @@ class LocalListFragment : MangaListFragment<File>() {
override fun onPopupMenuItemSelected(item: MenuItem, data: Manga): Boolean {
return when (item.itemId) {
R.id.action_delete -> {
presenter.delete(data)
context?.showDialog {
setTitle(R.string.delete_manga)
setMessage(getString(R.string.text_delete_local_manga, data.title))
setPositiveButton(R.string.delete) { _, _ ->
presenter.delete(data)
}
setNegativeButton(android.R.string.cancel, null)
}
true
}
else -> super.onPopupMenuItemSelected(item, data)

View File

@@ -1,6 +1,8 @@
package org.koitharu.kotatsu.utils
import android.app.ActivityManager
import android.content.Context
import androidx.core.content.getSystemService
import androidx.core.content.pm.ShortcutInfoCompat
import androidx.core.graphics.drawable.IconCompat
import androidx.core.graphics.drawable.toBitmap
@@ -11,23 +13,30 @@ import kotlinx.coroutines.withContext
import org.koitharu.kotatsu.R
import org.koitharu.kotatsu.core.model.Manga
import org.koitharu.kotatsu.ui.details.MangaDetailsActivity
import org.koitharu.kotatsu.utils.ext.safe
object ShortcutUtils {
@JvmStatic
suspend fun createShortcutInfo(context: Context, manga: Manga): ShortcutInfoCompat {
// val icon = withContext(Dispatchers.IO) {
// Coil.loader().get(manga.coverUrl) {
// context.getSystemService<ActivityManager>()?.let {
// size(it.launcherLargeIconSize)
// }
// }.toBitmap()
// }
val icon = safe {
withContext(Dispatchers.IO) {
Coil.loader().get(manga.coverUrl) {
context.getSystemService<ActivityManager>()?.let {
size(it.launcherLargeIconSize)
}
}.toBitmap()
}
}
return ShortcutInfoCompat.Builder(context, manga.id.toString())
.setShortLabel(manga.title)
.setLongLabel(manga.title)
.setIcon(IconCompat.createWithResource(context, R.drawable.ic_launcher_foreground))
.setIntent(MangaDetailsActivity.newIntent(context, manga))
.setIcon(icon?.let {
IconCompat.createWithBitmap(it)
} ?: IconCompat.createWithResource(context, R.drawable.ic_launcher_foreground))
.setIntent(
MangaDetailsActivity.newIntent(context, manga.copy(chapters = null))
.setAction(MangaDetailsActivity.ACTION_MANGA_VIEW)
)
.build()
}
}

View File

@@ -1,22 +1,28 @@
<?xml version="1.0" encoding="utf-8"?>
<menu
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<menu xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<item
android:id="@+id/action_share"
android:icon="@drawable/ic_share"
android:title="@string/share"
app:showAsAction="ifRoom" />
<item
android:id="@+id/action_share"
android:icon="@drawable/ic_share"
android:title="@string/share"
app:showAsAction="ifRoom" />
<item
android:id="@+id/action_save"
android:title="@string/save"
app:showAsAction="never" />
<item
android:id="@+id/action_save"
android:title="@string/save"
android:visible="false"
app:showAsAction="never" />
<item
android:id="@+id/action_shortcut"
android:title="@string/create_shortcut"
app:showAsAction="never" />
<item
android:id="@+id/action_delete"
android:title="@string/delete"
android:visible="false"
app:showAsAction="never" />
<item
android:id="@+id/action_shortcut"
android:title="@string/create_shortcut"
app:showAsAction="never" />
</menu>

View File

@@ -86,4 +86,6 @@
<string name="put_items_below_to_disable_it">Put items below to disable it</string>
<string name="grid_size">Grid size</string>
<string name="search_results_on_s">Search results on %s</string>
<string name="delete_manga">Delete manga</string>
<string name="text_delete_local_manga">Are you really want to delete \"%s\" from your phone\'s local storage? \nThis operation cannot be undone.</string>
</resources>