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" /> + +