diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/core/model/Manga.kt b/app/src/main/kotlin/org/koitharu/kotatsu/core/model/Manga.kt index 9afce24e4..10304ef2b 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/core/model/Manga.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/core/model/Manga.kt @@ -6,6 +6,7 @@ import android.text.SpannableStringBuilder import androidx.annotation.DrawableRes import androidx.annotation.StringRes import androidx.collection.MutableObjectIntMap +import androidx.core.net.toUri import androidx.core.os.LocaleListCompat import androidx.core.text.buildSpannedString import androidx.core.text.strikeThrough @@ -125,7 +126,8 @@ val Manga.isBroken: Boolean get() = source == UnknownMangaSource val Manga.appUrl: Uri - get() = Uri.parse("https://kotatsu.app/manga").buildUpon() + get() = "https://kotatsu.app/manga".toUri() + .buildUpon() .appendQueryParameter("source", source.name) .appendQueryParameter("name", title) .appendQueryParameter("url", url) diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/core/nav/AppRouter.kt b/app/src/main/kotlin/org/koitharu/kotatsu/core/nav/AppRouter.kt index 0cbefcb63..3f565b863 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/core/nav/AppRouter.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/core/nav/AppRouter.kt @@ -12,6 +12,8 @@ import android.provider.Settings import android.view.View import androidx.annotation.CheckResult import androidx.annotation.UiContext +import androidx.core.app.ShareCompat +import androidx.core.content.FileProvider import androidx.core.net.toUri import androidx.fragment.app.DialogFragment import androidx.fragment.app.Fragment @@ -29,7 +31,10 @@ import org.koitharu.kotatsu.browser.cloudflare.CloudFlareActivity import org.koitharu.kotatsu.core.exceptions.CloudFlareProtectedException import org.koitharu.kotatsu.core.model.FavouriteCategory import org.koitharu.kotatsu.core.model.MangaSourceInfo +import org.koitharu.kotatsu.core.model.appUrl import org.koitharu.kotatsu.core.model.getTitle +import org.koitharu.kotatsu.core.model.isBroken +import org.koitharu.kotatsu.core.model.isLocal import org.koitharu.kotatsu.core.model.parcelable.ParcelableManga import org.koitharu.kotatsu.core.model.parcelable.ParcelableMangaListFilter import org.koitharu.kotatsu.core.model.parcelable.ParcelableMangaPage @@ -43,6 +48,8 @@ import org.koitharu.kotatsu.core.ui.dialog.ErrorDetailsDialog import org.koitharu.kotatsu.core.ui.dialog.buildAlertDialog import org.koitharu.kotatsu.core.util.ext.connectivityManager import org.koitharu.kotatsu.core.util.ext.findActivity +import org.koitharu.kotatsu.core.util.ext.getThemeDrawable +import org.koitharu.kotatsu.core.util.ext.toFileOrNull import org.koitharu.kotatsu.core.util.ext.toUriOrNull import org.koitharu.kotatsu.core.util.ext.withArgs import org.koitharu.kotatsu.details.ui.DetailsActivity @@ -72,6 +79,7 @@ import org.koitharu.kotatsu.parsers.model.MangaPage import org.koitharu.kotatsu.parsers.model.MangaSource import org.koitharu.kotatsu.parsers.model.MangaTag import org.koitharu.kotatsu.parsers.model.SortOrder +import org.koitharu.kotatsu.parsers.util.ellipsize import org.koitharu.kotatsu.parsers.util.isNullOrEmpty import org.koitharu.kotatsu.parsers.util.mapToArray import org.koitharu.kotatsu.reader.ui.colorfilter.ColorFilterConfigActivity @@ -96,6 +104,8 @@ import org.koitharu.kotatsu.stats.ui.StatsActivity import org.koitharu.kotatsu.stats.ui.sheet.MangaStatsSheet import org.koitharu.kotatsu.suggestions.ui.SuggestionsActivity import org.koitharu.kotatsu.tracker.ui.updates.UpdatesActivity +import java.io.File +import com.google.android.material.R as materialR class AppRouter private constructor( private val activity: FragmentActivity?, @@ -391,6 +401,37 @@ class AppRouter private constructor( }.show() } + fun showShareDialog(manga: Manga) { + if (manga.isBroken) { + return + } + if (manga.isLocal) { + manga.url.toUri().toFileOrNull()?.let { + shareFile(it) + } + return + } + buildAlertDialog(contextOrNull() ?: return) { + setIcon(context.getThemeDrawable(materialR.attr.actionModeShareDrawable)) + setTitle(R.string.share) + setItems( + arrayOf( + context.getString(R.string.link_to_manga_in_app), + context.getString(R.string.link_to_manga_on_s, manga.source.getTitle(context)), + ), + ) { _, which -> + val link = when (which) { + 0 -> manga.appUrl.toString() + 1 -> manga.publicUrl + else -> return@setItems + } + shareLink(link, manga.title) + } + setNegativeButton(android.R.string.cancel, null) + setCancelable(true) + }.show() + } + fun showErrorDialog(error: Throwable, url: String? = null) { ErrorDetailsDialog().withArgs(2) { putSerializable(KEY_ERROR, error) @@ -565,6 +606,25 @@ class AppRouter private constructor( return fragment?.childFragmentManager ?: activity?.supportFragmentManager } + private fun shareLink(link: String, title: String) { + val context = contextOrNull() ?: return + ShareCompat.IntentBuilder(context) + .setText(link) + .setType(TYPE_TEXT) + .setChooserTitle(context.getString(R.string.share_s, title.ellipsize(12))) + .startChooser() + } + + private fun shareFile(file: File) { // TODO directory sharing support + val context = contextOrNull() ?: return + val intentBuilder = ShareCompat.IntentBuilder(context) + .setType(TYPE_CBZ) + val uri = FileProvider.getUriForFile(context, "${BuildConfig.APPLICATION_ID}.files", file) + intentBuilder.addStream(uri) + intentBuilder.setChooserTitle(context.getString(R.string.share_s, file.name)) + intentBuilder.startChooser() + } + @UiContext private fun contextOrNull(): Context? = activity ?: fragment?.context @@ -726,6 +786,10 @@ class AppRouter private constructor( private const val ACTION_ACCOUNT_SYNC_SETTINGS = "android.settings.ACCOUNT_SYNC_SETTINGS" private const val EXTRA_SHOW_FRAGMENT_ARGUMENTS = ":settings:show_fragment_args" + private const val TYPE_TEXT = "text/plain" + private const val TYPE_IMAGE = "image/*" + private const val TYPE_CBZ = "application/x-cbz" + private fun Class.fragmentTag() = name // TODO private inline fun fragmentTag() = F::class.java.fragmentTag() diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/core/util/ShareHelper.kt b/app/src/main/kotlin/org/koitharu/kotatsu/core/util/ShareHelper.kt index 3fa1b6e3f..4c08b9d22 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/core/util/ShareHelper.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/core/util/ShareHelper.kt @@ -15,6 +15,7 @@ private const val TYPE_TEXT = "text/plain" private const val TYPE_IMAGE = "image/*" private const val TYPE_CBZ = "application/x-cbz" +@Deprecated("") class ShareHelper(private val context: Context) { fun shareMangaLink(manga: Manga) { diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/details/ui/DetailsMenuProvider.kt b/app/src/main/kotlin/org/koitharu/kotatsu/details/ui/DetailsMenuProvider.kt index ceedcfdd8..9fcf94ce1 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/details/ui/DetailsMenuProvider.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/details/ui/DetailsMenuProvider.kt @@ -5,20 +5,16 @@ import android.view.MenuInflater import android.view.MenuItem import android.view.View import androidx.core.content.pm.ShortcutManagerCompat -import androidx.core.net.toFile -import androidx.core.net.toUri import androidx.core.view.MenuProvider import androidx.fragment.app.FragmentActivity import androidx.lifecycle.lifecycleScope -import com.google.android.material.dialog.MaterialAlertDialogBuilder import com.google.android.material.snackbar.Snackbar import kotlinx.coroutines.launch import org.koitharu.kotatsu.R import org.koitharu.kotatsu.core.model.LocalMangaSource -import org.koitharu.kotatsu.core.model.isLocal import org.koitharu.kotatsu.core.nav.router import org.koitharu.kotatsu.core.os.AppShortcutManager -import org.koitharu.kotatsu.core.util.ShareHelper +import org.koitharu.kotatsu.core.ui.dialog.buildAlertDialog class DetailsMenuProvider( private val activity: FragmentActivity, @@ -47,23 +43,16 @@ class DetailsMenuProvider( val manga = viewModel.getMangaOrNull() ?: return false when (menuItem.itemId) { R.id.action_share -> { - val shareHelper = ShareHelper(activity) - if (manga.isLocal) { - shareHelper.shareCbz(listOf(manga.url.toUri().toFile())) - } else { - shareHelper.shareMangaLink(manga) - } + activity.router.showShareDialog(manga) } R.id.action_delete -> { - MaterialAlertDialogBuilder(activity) - .setTitle(R.string.delete_manga) - .setMessage(activity.getString(R.string.text_delete_local_manga, manga.title)) - .setPositiveButton(R.string.delete) { _, _ -> - viewModel.deleteLocal() - } - .setNegativeButton(android.R.string.cancel, null) - .show() + buildAlertDialog(activity) { + setTitle(R.string.delete_manga) + setMessage(activity.getString(R.string.text_delete_local_manga, manga.title)) + setPositiveButton(R.string.delete) { _, _ -> viewModel.deleteLocal() } + setNegativeButton(android.R.string.cancel, null) + }.show() } R.id.action_save -> { diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index cbd3d233f..6c9a32a03 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -814,4 +814,6 @@ Try to open manga in a web browser to ensure it is available on its source. It looks like your version of Kotatsu is out of date. Please install the latest version to get all available fixes. You can submit a bug report to the developers. This will help us investigate and fix the issue. + Link to manga on %s + Link to manga in Kotatsu diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index da265fe5f..b31ca2c59 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -31,7 +31,7 @@ material = "1.13.0-alpha11" moshi = "1.15.2" okhttp = "4.12.0" okio = "3.10.2" -parsers = "d5a4cf68c6" +parsers = "e83636edc0" preference = "1.2.1" recyclerview = "1.4.0" room = "2.6.1"