diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index 75f44383e..b665da264 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -95,7 +95,12 @@
android:label="@string/search" />
+ android:exported="true"
+ android:label="@string/manga_list">
+
+
+
+
@@ -138,8 +143,8 @@
android:windowSoftInputMode="adjustResize" />
= Build.VERSION_CODES.N_MR1 &&
+ context.getSystemService(ShortcutManager::class.java).maxShortcutCountPerActivity > 0
+ }
+
private suspend fun updateShortcutsImpl() = runCatchingCancellable {
val maxShortcuts = ShortcutManagerCompat.getMaxShortcutCountPerActivity(context).coerceAtLeast(5)
val shortcuts = historyRepository.getList(0, maxShortcuts)
@@ -132,8 +144,25 @@ class AppShortcutManager @Inject constructor(
.build()
}
- fun isDynamicShortcutsAvailable(): Boolean {
- return Build.VERSION.SDK_INT >= Build.VERSION_CODES.N_MR1 &&
- context.getSystemService(ShortcutManager::class.java).maxShortcutCountPerActivity > 0
+ private suspend fun buildShortcutInfo(source: MangaSource): ShortcutInfoCompat {
+ val icon = runCatchingCancellable {
+ coil.execute(
+ ImageRequest.Builder(context)
+ .data(source.faviconUri())
+ .size(iconSize)
+ .scale(Scale.FIT)
+ .build(),
+ ).getDrawableOrThrow().toBitmap()
+ }.fold(
+ onSuccess = { IconCompat.createWithAdaptiveBitmap(it) },
+ onFailure = { IconCompat.createWithResource(context, R.drawable.ic_shortcut_default) },
+ )
+ return ShortcutInfoCompat.Builder(context, source.name)
+ .setShortLabel(source.title)
+ .setLongLabel(source.title)
+ .setIcon(icon)
+ .setLongLived(true)
+ .setIntent(MangaListActivity.newIntent(context, source))
+ .build()
}
}
diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/explore/ui/ExploreFragment.kt b/app/src/main/kotlin/org/koitharu/kotatsu/explore/ui/ExploreFragment.kt
index 01e48676e..49350c2b2 100644
--- a/app/src/main/kotlin/org/koitharu/kotatsu/explore/ui/ExploreFragment.kt
+++ b/app/src/main/kotlin/org/koitharu/kotatsu/explore/ui/ExploreFragment.kt
@@ -7,6 +7,7 @@ import android.view.MenuItem
import android.view.View
import android.view.ViewGroup
import androidx.appcompat.widget.PopupMenu
+import androidx.core.content.pm.ShortcutManagerCompat
import androidx.core.graphics.Insets
import androidx.core.view.updatePadding
import androidx.fragment.app.viewModels
@@ -15,9 +16,11 @@ import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import coil.ImageLoader
import dagger.hilt.android.AndroidEntryPoint
+import kotlinx.coroutines.launch
import org.koitharu.kotatsu.R
import org.koitharu.kotatsu.bookmarks.ui.BookmarksActivity
import org.koitharu.kotatsu.core.exceptions.resolve.SnackbarErrorObserver
+import org.koitharu.kotatsu.core.os.AppShortcutManager
import org.koitharu.kotatsu.core.ui.BaseFragment
import org.koitharu.kotatsu.core.ui.dialog.TwoButtonsAlertDialog
import org.koitharu.kotatsu.core.ui.list.OnListItemClickListener
@@ -28,6 +31,7 @@ import org.koitharu.kotatsu.core.ui.widgets.TipView
import org.koitharu.kotatsu.core.util.ext.addMenuProvider
import org.koitharu.kotatsu.core.util.ext.observe
import org.koitharu.kotatsu.core.util.ext.observeEvent
+import org.koitharu.kotatsu.core.util.ext.viewLifecycleScope
import org.koitharu.kotatsu.databinding.FragmentExploreBinding
import org.koitharu.kotatsu.details.ui.DetailsActivity
import org.koitharu.kotatsu.download.ui.list.DownloadsActivity
@@ -55,6 +59,9 @@ class ExploreFragment :
@Inject
lateinit var coil: ImageLoader
+ @Inject
+ lateinit var shortcutManager: AppShortcutManager
+
private val viewModel by viewModels()
private var exploreAdapter: ExploreAdapter? = null
@@ -141,6 +148,8 @@ class ExploreFragment :
override fun onItemLongClick(item: MangaSourceItem, view: View): Boolean {
val menu = PopupMenu(view.context, view)
menu.inflate(R.menu.popup_source)
+ menu.menu.findItem(R.id.action_shortcut)
+ ?.isVisible = ShortcutManagerCompat.isRequestPinShortcutSupported(view.context)
menu.setOnMenuItemClickListener(SourceMenuListener(item))
menu.show()
return true
@@ -195,6 +204,12 @@ class ExploreFragment :
viewModel.hideSource(sourceItem.source)
}
+ R.id.action_shortcut -> {
+ viewLifecycleScope.launch {
+ shortcutManager.requestPinShortcut(sourceItem.source)
+ }
+ }
+
else -> return false
}
return true
diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/search/ui/MangaListActivity.kt b/app/src/main/kotlin/org/koitharu/kotatsu/search/ui/MangaListActivity.kt
index 074154d8c..9c7511751 100644
--- a/app/src/main/kotlin/org/koitharu/kotatsu/search/ui/MangaListActivity.kt
+++ b/app/src/main/kotlin/org/koitharu/kotatsu/search/ui/MangaListActivity.kt
@@ -17,14 +17,15 @@ import dagger.hilt.android.AndroidEntryPoint
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.flowOn
import kotlinx.coroutines.flow.map
+import org.koitharu.kotatsu.BuildConfig
import org.koitharu.kotatsu.R
+import org.koitharu.kotatsu.core.model.MangaSource
import org.koitharu.kotatsu.core.model.parcelable.ParcelableManga
import org.koitharu.kotatsu.core.model.parcelable.ParcelableMangaTags
import org.koitharu.kotatsu.core.parser.MangaIntent
import org.koitharu.kotatsu.core.ui.BaseActivity
import org.koitharu.kotatsu.core.ui.model.titleRes
import org.koitharu.kotatsu.core.util.ext.getParcelableExtraCompat
-import org.koitharu.kotatsu.core.util.ext.getSerializableExtraCompat
import org.koitharu.kotatsu.core.util.ext.getThemeColor
import org.koitharu.kotatsu.core.util.ext.observe
import org.koitharu.kotatsu.core.util.ext.setTextAndVisible
@@ -64,7 +65,7 @@ class MangaListActivity :
if (viewBinding.containerFilterHeader != null) {
viewBinding.appbar.addOnOffsetChangedListener(this)
}
- val source = intent.getSerializableExtraCompat(EXTRA_SOURCE) ?: tags?.firstOrNull()?.source
+ val source = intent.getStringExtra(EXTRA_SOURCE)?.let(::MangaSource) ?: tags?.firstOrNull()?.source
if (source == null) {
finishAfterTransition()
return
@@ -186,11 +187,14 @@ class MangaListActivity :
private const val EXTRA_TAGS = "tags"
private const val EXTRA_SOURCE = "source"
+ const val ACTION_MANGA_EXPLORE = "${BuildConfig.APPLICATION_ID}.action.EXPLORE_MANGA"
fun newIntent(context: Context, tags: Set) = Intent(context, MangaListActivity::class.java)
+ .setAction(ACTION_MANGA_EXPLORE)
.putExtra(EXTRA_TAGS, ParcelableMangaTags(tags))
fun newIntent(context: Context, source: MangaSource) = Intent(context, MangaListActivity::class.java)
- .putExtra(EXTRA_SOURCE, source)
+ .setAction(ACTION_MANGA_EXPLORE)
+ .putExtra(EXTRA_SOURCE, source.name)
}
}
diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/settings/newsources/NewSourcesDialogFragment.kt b/app/src/main/kotlin/org/koitharu/kotatsu/settings/newsources/NewSourcesDialogFragment.kt
index 906c42fb4..99443f6cc 100644
--- a/app/src/main/kotlin/org/koitharu/kotatsu/settings/newsources/NewSourcesDialogFragment.kt
+++ b/app/src/main/kotlin/org/koitharu/kotatsu/settings/newsources/NewSourcesDialogFragment.kt
@@ -59,6 +59,8 @@ class NewSourcesDialogFragment :
override fun onItemLiftClick(item: SourceConfigItem.SourceItem) = Unit
+ override fun onItemShortcutClick(item: SourceConfigItem.SourceItem) = Unit
+
override fun onItemEnabledChanged(item: SourceConfigItem.SourceItem, isEnabled: Boolean) {
viewModel.onItemEnabledChanged(item, isEnabled)
}
diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/settings/sources/SourcesManageFragment.kt b/app/src/main/kotlin/org/koitharu/kotatsu/settings/sources/SourcesManageFragment.kt
index 2da4fc4d5..30053898b 100644
--- a/app/src/main/kotlin/org/koitharu/kotatsu/settings/sources/SourcesManageFragment.kt
+++ b/app/src/main/kotlin/org/koitharu/kotatsu/settings/sources/SourcesManageFragment.kt
@@ -15,7 +15,9 @@ import androidx.recyclerview.widget.ItemTouchHelper
import androidx.recyclerview.widget.RecyclerView
import coil.ImageLoader
import dagger.hilt.android.AndroidEntryPoint
+import kotlinx.coroutines.launch
import org.koitharu.kotatsu.R
+import org.koitharu.kotatsu.core.os.AppShortcutManager
import org.koitharu.kotatsu.core.prefs.AppSettings
import org.koitharu.kotatsu.core.ui.BaseFragment
import org.koitharu.kotatsu.core.ui.util.RecyclerViewOwner
@@ -24,6 +26,7 @@ import org.koitharu.kotatsu.core.util.ext.addMenuProvider
import org.koitharu.kotatsu.core.util.ext.getItem
import org.koitharu.kotatsu.core.util.ext.observe
import org.koitharu.kotatsu.core.util.ext.observeEvent
+import org.koitharu.kotatsu.core.util.ext.viewLifecycleScope
import org.koitharu.kotatsu.databinding.FragmentSettingsSourcesBinding
import org.koitharu.kotatsu.main.ui.owners.AppBarOwner
import org.koitharu.kotatsu.settings.SettingsActivity
@@ -44,6 +47,9 @@ class SourcesManageFragment :
@Inject
lateinit var settings: AppSettings
+ @Inject
+ lateinit var shortcutManager: AppShortcutManager
+
private var reorderHelper: ItemTouchHelper? = null
private val viewModel by viewModels()
@@ -103,6 +109,12 @@ class SourcesManageFragment :
viewModel.bringToTop(item.source)
}
+ override fun onItemShortcutClick(item: SourceConfigItem.SourceItem) {
+ viewLifecycleScope.launch {
+ shortcutManager.requestPinShortcut(item.source)
+ }
+ }
+
override fun onItemEnabledChanged(item: SourceConfigItem.SourceItem, isEnabled: Boolean) {
viewModel.setEnabled(item.source, isEnabled)
}
diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/settings/sources/adapter/SourceConfigAdapterDelegates.kt b/app/src/main/kotlin/org/koitharu/kotatsu/settings/sources/adapter/SourceConfigAdapterDelegates.kt
index 5682876df..f727175a9 100644
--- a/app/src/main/kotlin/org/koitharu/kotatsu/settings/sources/adapter/SourceConfigAdapterDelegates.kt
+++ b/app/src/main/kotlin/org/koitharu/kotatsu/settings/sources/adapter/SourceConfigAdapterDelegates.kt
@@ -8,6 +8,7 @@ import android.text.style.RelativeSizeSpan
import android.text.style.SuperscriptSpan
import android.view.View
import androidx.appcompat.widget.PopupMenu
+import androidx.core.content.pm.ShortcutManagerCompat
import androidx.core.text.buildSpannedString
import androidx.core.text.inSpans
import androidx.core.view.isGone
@@ -39,7 +40,7 @@ fun sourceConfigHeaderDelegate() =
ItemFilterHeaderBinding.inflate(
layoutInflater,
parent,
- false
+ false,
)
},
) {
@@ -76,7 +77,7 @@ fun sourceConfigItemCheckableDelegate(
ItemSourceConfigCheckableBinding.inflate(
layoutInflater,
parent,
- false
+ false,
)
},
) {
@@ -121,7 +122,7 @@ fun sourceConfigItemDelegate2(
ItemSourceConfigBinding.inflate(
layoutInflater,
parent,
- false
+ false,
)
},
) {
@@ -189,8 +190,8 @@ fun SpannableStringBuilder.appendNsfwLabel(context: Context) = inSpans(
ForegroundColorSpan(
context.getThemeColor(
com.google.android.material.R.attr.colorError,
- Color.RED
- )
+ Color.RED,
+ ),
),
RelativeSizeSpan(0.74f),
SuperscriptSpan(),
@@ -205,10 +206,13 @@ private fun showSourceMenu(
) {
val menu = PopupMenu(anchor.context, anchor)
menu.inflate(R.menu.popup_source_config)
+ menu.menu.findItem(R.id.action_shortcut)
+ ?.isVisible = ShortcutManagerCompat.isRequestPinShortcutSupported(anchor.context)
menu.setOnMenuItemClickListener {
when (it.itemId) {
R.id.action_settings -> listener.onItemSettingsClick(item)
R.id.action_lift -> listener.onItemLiftClick(item)
+ R.id.action_shortcut -> listener.onItemShortcutClick(item)
}
true
}
diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/settings/sources/adapter/SourceConfigListener.kt b/app/src/main/kotlin/org/koitharu/kotatsu/settings/sources/adapter/SourceConfigListener.kt
index 1ad142653..d97027bc2 100644
--- a/app/src/main/kotlin/org/koitharu/kotatsu/settings/sources/adapter/SourceConfigListener.kt
+++ b/app/src/main/kotlin/org/koitharu/kotatsu/settings/sources/adapter/SourceConfigListener.kt
@@ -9,6 +9,8 @@ interface SourceConfigListener : OnTipCloseListener {
fun onItemLiftClick(item: SourceConfigItem.SourceItem)
+ fun onItemShortcutClick(item: SourceConfigItem.SourceItem)
+
fun onItemEnabledChanged(item: SourceConfigItem.SourceItem, isEnabled: Boolean)
fun onHeaderClick(header: SourceConfigItem.LocaleGroup)
diff --git a/app/src/main/res/menu/popup_source.xml b/app/src/main/res/menu/popup_source.xml
index 60e497a39..54eded2ed 100644
--- a/app/src/main/res/menu/popup_source.xml
+++ b/app/src/main/res/menu/popup_source.xml
@@ -6,6 +6,10 @@
android:id="@+id/action_settings"
android:title="@string/settings" />
+
+
diff --git a/app/src/main/res/menu/popup_source_config.xml b/app/src/main/res/menu/popup_source_config.xml
index 688868d1a..3c868da7c 100644
--- a/app/src/main/res/menu/popup_source_config.xml
+++ b/app/src/main/res/menu/popup_source_config.xml
@@ -6,6 +6,10 @@
android:id="@+id/action_lift"
android:title="@string/to_top" />
+
+