From de261e282509f7eba356c3ac469072c037131c46 Mon Sep 17 00:00:00 2001 From: Xilin Jia <6257601+XilinJia@users.noreply.github.com> Date: Sat, 16 Mar 2024 12:22:07 +0000 Subject: [PATCH] change on click operations --- app/build.gradle | 10 +--- .../ui/adapter/EpisodeItemListAdapter.kt | 32 ++++++++----- .../podcini/ui/adapter/SelectableAdapter.kt | 37 +++++++++++---- .../adapter/SubscriptionsRecyclerAdapter.kt | 46 +++++++++++-------- .../ui/fragment/EpisodesListFragment.kt | 28 ++++++----- .../EpisodeMultiSelectActionHandler.kt | 24 ++++------ .../ui/fragment/swipeactions/SwipeAction.kt | 1 - .../view/viewholder/EpisodeItemViewHolder.kt | 14 +++--- .../drawable/baseline_arrow_downward_24.xml | 5 ++ .../res/drawable/baseline_arrow_upward_24.xml | 5 ++ .../res/layout/feed_item_list_fragment.xml | 3 +- .../menu/episodes_apply_action_speeddial.xml | 8 ++-- .../main/res/menu/multi_select_options.xml | 10 ++++ app/src/main/res/values/strings.xml | 4 ++ changelog.md | 12 ++++- .../android/en-US/changelogs/3020108.txt | 10 ++++ 16 files changed, 164 insertions(+), 85 deletions(-) create mode 100644 app/src/main/res/drawable/baseline_arrow_downward_24.xml create mode 100644 app/src/main/res/drawable/baseline_arrow_upward_24.xml create mode 100644 fastlane/metadata/android/en-US/changelogs/3020108.txt diff --git a/app/build.gradle b/app/build.gradle index 8dee1e81..7a1ac1d5 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -149,8 +149,8 @@ android { // Version code schema (not used): // "1.2.3-beta4" -> 1020304 // "1.2.3" -> 1020395 - versionCode 3020107 - versionName "4.2.4" + versionCode 3020108 + versionName "4.2.5" def commit = "" try { @@ -164,12 +164,6 @@ android { } buildConfigField "String", "COMMIT_HASH", ('"' + (commit.isEmpty() ? "Unknown commit" : commit) + '"') -// javaCompileOptions { -// annotationProcessorOptions { -// arguments = [eventBusIndex: 'ac.mdiq.podcini.ApEventBusIndex'] -// } -// } - if (project.hasProperty("podcastindexApiKey")) { buildConfigField "String", "PODCASTINDEX_API_KEY", '"' + podcastindexApiKey + '"' buildConfigField "String", "PODCASTINDEX_API_SECRET", '"' + podcastindexApiSecret + '"' diff --git a/app/src/main/java/ac/mdiq/podcini/ui/adapter/EpisodeItemListAdapter.kt b/app/src/main/java/ac/mdiq/podcini/ui/adapter/EpisodeItemListAdapter.kt index dc4463cd..eb5d3505 100644 --- a/app/src/main/java/ac/mdiq/podcini/ui/adapter/EpisodeItemListAdapter.kt +++ b/app/src/main/java/ac/mdiq/podcini/ui/adapter/EpisodeItemListAdapter.kt @@ -71,23 +71,34 @@ open class EpisodeItemListAdapter(mainActivity: MainActivity) : val item: FeedItem = episodes[pos] holder.bind(item) - holder.infoCard.setOnCreateContextMenuListener(this) +// holder.infoCard.setOnCreateContextMenuListener(this) holder.infoCard.setOnLongClickListener { longPressedItem = item longPressedPosition = holder.bindingAdapterPosition startSelectMode(longPressedPosition) - false + true } holder.infoCard.setOnClickListener { - if (inActionMode()) { - toggleSelection(holder.bindingAdapterPosition) + val activity: MainActivity? = mainActivityRef.get() + if (!inActionMode()) { + val ids: LongArray = FeedItemUtil.getIds(episodes) + val position = ArrayUtils.indexOf(ids, item.id) + activity?.loadChildFragment(ItemPagerFragment.newInstance(ids, position)) } else { - longPressedItem = item - longPressedPosition = holder.bindingAdapterPosition - it.showContextMenu() + toggleSelection(holder.bindingAdapterPosition) } } +// holder.infoCard.setOnClickListener { +// if (inActionMode()) { +// toggleSelection(holder.bindingAdapterPosition) +// } else { +// longPressedItem = item +// longPressedPosition = holder.bindingAdapterPosition +// it.showContextMenu() +// } +// } + // holder.infoCard.setOnCreateContextMenuListener(this) // holder.infoCard.setOnLongClickListener { // longPressedItem = item @@ -180,11 +191,10 @@ open class EpisodeItemListAdapter(mainActivity: MainActivity) : @UnstableApi override fun onCreateContextMenu(menu: ContextMenu, v: View, menuInfo: ContextMenu.ContextMenuInfo?) { val inflater: MenuInflater = activity!!.menuInflater if (inActionMode()) { - inflater.inflate(R.menu.multi_select_context_popup, menu) +// inflater.inflate(R.menu.multi_select_context_popup, menu) } else { - if (longPressedItem == null) { - return - } + if (longPressedItem == null) return + inflater.inflate(R.menu.feeditemlist_context, menu) menu.setHeaderTitle(longPressedItem!!.title) FeedItemMenuHandler.onPrepareMenu(menu, longPressedItem, R.id.skip_episode_item) diff --git a/app/src/main/java/ac/mdiq/podcini/ui/adapter/SelectableAdapter.kt b/app/src/main/java/ac/mdiq/podcini/ui/adapter/SelectableAdapter.kt index ad073f6f..c36d012b 100644 --- a/app/src/main/java/ac/mdiq/podcini/ui/adapter/SelectableAdapter.kt +++ b/app/src/main/java/ac/mdiq/podcini/ui/adapter/SelectableAdapter.kt @@ -44,15 +44,27 @@ abstract class SelectableAdapter(private val activ } override fun onActionItemClicked(mode: ActionMode, item: MenuItem): Boolean { - if (item.itemId == R.id.select_toggle) { - val selectAll = selectedIds.size != itemCount - shouldSelectLazyLoadedItems = selectAll - setSelected(0, itemCount, selectAll) - toggleSelectAllIcon(item, selectAll) - updateTitle() - return true + when (item.itemId) { + R.id.select_toggle -> { + val selectAll = selectedIds.size != itemCount + shouldSelectLazyLoadedItems = selectAll + setSelected(0, itemCount, selectAll) + toggleSelectAllIcon(item, selectAll) + updateTitle() + return true + } + R.id.select_all_above -> { + shouldSelectLazyLoadedItems = true + toggleSelected(0, pos) + return true + } + R.id.select_all_below -> { + shouldSelectLazyLoadedItems = true + toggleSelected(pos + 1, itemCount) + return true + } + else -> return false } - return false } override fun onDestroyActionMode(mode: ActionMode) { @@ -113,6 +125,15 @@ abstract class SelectableAdapter(private val activ notifyItemRangeChanged(startPos, (endPos - startPos)) } + fun toggleSelected(startPos: Int, endPos: Int) { + var i = startPos + while (i < endPos && i < itemCount) { + toggleSelection(i) + i++ + } + notifyItemRangeChanged(startPos, (endPos - startPos)) + } + protected fun toggleSelection(pos: Int) { setSelected(pos, !isSelected(pos)) notifyItemChanged(pos) diff --git a/app/src/main/java/ac/mdiq/podcini/ui/adapter/SubscriptionsRecyclerAdapter.kt b/app/src/main/java/ac/mdiq/podcini/ui/adapter/SubscriptionsRecyclerAdapter.kt index 7333dff9..171f96a4 100644 --- a/app/src/main/java/ac/mdiq/podcini/ui/adapter/SubscriptionsRecyclerAdapter.kt +++ b/app/src/main/java/ac/mdiq/podcini/ui/adapter/SubscriptionsRecyclerAdapter.kt @@ -69,22 +69,31 @@ open class SubscriptionsRecyclerAdapter(mainActivity: MainActivity) : holder.coverImage.alpha = 1.0f } - holder.infoCard.setOnCreateContextMenuListener(this) - holder.infoCard.setOnLongClickListener { - longPressedPosition = holder.bindingAdapterPosition - selectedItem = drawerItem - startSelectMode(longPressedPosition) - false - } holder.infoCard.setOnClickListener { if (inActionMode()) { holder.selectCheckbox.setChecked(!isSelected(holder.bindingAdapterPosition)) } else { - longPressedPosition = holder.bindingAdapterPosition - selectedItem = drawerItem - it.showContextMenu() + val fragment: Fragment = FeedItemlistFragment.newInstance(drawerItem.feed.id) + mainActivityRef.get()?.loadChildFragment(fragment) } } +// holder.infoCard.setOnCreateContextMenuListener(this) + holder.infoCard.setOnLongClickListener { + longPressedPosition = holder.bindingAdapterPosition + selectedItem = drawerItem + startSelectMode(longPressedPosition) + true + } + +// holder.infoCard.setOnClickListener { +// if (inActionMode()) { +// holder.selectCheckbox.setChecked(!isSelected(holder.bindingAdapterPosition)) +// } else { +// longPressedPosition = holder.bindingAdapterPosition +// selectedItem = drawerItem +// it.showContextMenu() +// } +// } // holder.infoCard.setOnCreateContextMenuListener(this) // holder.infoCard.setOnLongClickListener { // if (!inActionMode()) { @@ -128,12 +137,11 @@ open class SubscriptionsRecyclerAdapter(mainActivity: MainActivity) : } override fun onCreateContextMenu(menu: ContextMenu, v: View, menuInfo: ContextMenu.ContextMenuInfo?) { - if (selectedItem == null) { - return - } - val inflater: MenuInflater = mainActivityRef.get()!!.menuInflater + if (selectedItem == null) return + val mainActRef = mainActivityRef.get() ?: return + val inflater: MenuInflater = mainActRef.menuInflater if (inActionMode()) { - inflater.inflate(R.menu.multi_select_context_popup, menu) +// inflater.inflate(R.menu.multi_select_context_popup, menu) // menu.findItem(R.id.multi_select).setVisible(true) } else { inflater.inflate(R.menu.nav_feed_context, menu) @@ -197,7 +205,9 @@ open class SubscriptionsRecyclerAdapter(mainActivity: MainActivity) : count.visibility = View.GONE } - val coverLoader = CoverLoader(mainActivityRef.get()!!) + val mainActRef = mainActivityRef.get() ?: return + + val coverLoader = CoverLoader(mainActRef) val feed: Feed = drawerItem.feed coverLoader.withUri(feed.imageUrl) errorIcon.visibility = if (feed.hasLastUpdateFailed()) View.VISIBLE else View.GONE @@ -205,8 +215,8 @@ open class SubscriptionsRecyclerAdapter(mainActivity: MainActivity) : coverLoader.withCoverView(coverImage) coverLoader.load() - val density: Float = mainActivityRef.get()!!.resources.displayMetrics.density - card.setCardBackgroundColor(SurfaceColors.getColorForElevation(mainActivityRef.get()!!, 1 * density)) + val density: Float = mainActRef.resources.displayMetrics.density + card.setCardBackgroundColor(SurfaceColors.getColorForElevation(mainActRef, 1 * density)) val textHPadding = 20 val textVPadding = 5 diff --git a/app/src/main/java/ac/mdiq/podcini/ui/fragment/EpisodesListFragment.kt b/app/src/main/java/ac/mdiq/podcini/ui/fragment/EpisodesListFragment.kt index 7fe9ad68..e6eb518f 100644 --- a/app/src/main/java/ac/mdiq/podcini/ui/fragment/EpisodesListFragment.kt +++ b/app/src/main/java/ac/mdiq/podcini/ui/fragment/EpisodesListFragment.kt @@ -106,18 +106,24 @@ abstract class EpisodesListFragment : Fragment(), SelectableAdapter.OnSelectMode override fun onContextItemSelected(item: MenuItem): Boolean { Log.d(TAG, "onContextItemSelected() called with: item = [$item]") - if (!userVisibleHint || !isVisible || !isMenuVisible) { - // The method is called on all fragments in a ViewPager, so this needs to be ignored in invisible ones. - // Apparently, none of the visibility check method works reliably on its own, so we just use all. - return false - } else if (listAdapter.longPressedItem == null) { - Log.i(TAG, "Selected item or listAdapter was null, ignoring selection") - return super.onContextItemSelected(item) - } else if (listAdapter.onContextItemSelected(item)) { - return true + when { + !userVisibleHint || !isVisible || !isMenuVisible -> { + // The method is called on all fragments in a ViewPager, so this needs to be ignored in invisible ones. + // Apparently, none of the visibility check method works reliably on its own, so we just use all. + return false + } + listAdapter.longPressedItem == null -> { + Log.i(TAG, "Selected item or listAdapter was null, ignoring selection") + return super.onContextItemSelected(item) + } + listAdapter.onContextItemSelected(item) -> { + return true + } + else -> { + val selectedItem: FeedItem = listAdapter.longPressedItem ?: return false + return FeedItemMenuHandler.onMenuItemClicked(this, item.itemId, selectedItem) + } } - val selectedItem: FeedItem = listAdapter.longPressedItem ?: return false - return FeedItemMenuHandler.onMenuItemClicked(this, item.itemId, selectedItem) } @UnstableApi override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View { diff --git a/app/src/main/java/ac/mdiq/podcini/ui/fragment/actions/EpisodeMultiSelectActionHandler.kt b/app/src/main/java/ac/mdiq/podcini/ui/fragment/actions/EpisodeMultiSelectActionHandler.kt index d03bd7bb..d49f4003 100644 --- a/app/src/main/java/ac/mdiq/podcini/ui/fragment/actions/EpisodeMultiSelectActionHandler.kt +++ b/app/src/main/java/ac/mdiq/podcini/ui/fragment/actions/EpisodeMultiSelectActionHandler.kt @@ -19,15 +19,15 @@ class EpisodeMultiSelectActionHandler(private val activity: MainActivity, privat fun handleAction(items: List) { when (actionId) { + R.id.add_to_favorite_batch -> { + markFavorite(items) + } R.id.add_to_queue_batch -> { queueChecked(items) } R.id.remove_from_queue_batch -> { removeFromQueueChecked(items) } -// R.id.remove_from_inbox_batch -> { -// removeFromInboxChecked(items) -// } R.id.mark_read_batch -> { markedCheckedPlayed(items) } @@ -64,17 +64,6 @@ class EpisodeMultiSelectActionHandler(private val activity: MainActivity, privat showMessage(R.plurals.removed_from_queue_batch_label, checkedIds.size) } -// private fun removeFromInboxChecked(items: List) { -// val markUnplayed = LongList() -// for (episode in items) { -// if (episode.isNew) { -// markUnplayed.add(episode.id) -// } -// } -// DBWriter.markItemPlayed(FeedItem.UNPLAYED, *markUnplayed.toArray()) -// showMessage(R.plurals.removed_from_inbox_batch_label, markUnplayed.size()) -// } - private fun markedCheckedPlayed(items: List) { val checkedIds = getSelectedIds(items) DBWriter.markItemPlayed(FeedItem.PLAYED, *checkedIds) @@ -87,6 +76,13 @@ class EpisodeMultiSelectActionHandler(private val activity: MainActivity, privat showMessage(R.plurals.marked_unread_batch_label, checkedIds.size) } + private fun markFavorite(items: List) { + for (item in items) { + DBWriter.addFavoriteItem(item) + } + showMessage(R.plurals.marked_favorite_batch_label, items.size) + } + private fun downloadChecked(items: List) { // download the check episodes in the same order as they are currently displayed for (episode in items) { diff --git a/app/src/main/java/ac/mdiq/podcini/ui/fragment/swipeactions/SwipeAction.kt b/app/src/main/java/ac/mdiq/podcini/ui/fragment/swipeactions/SwipeAction.kt index 8eb69124..b631bb3f 100644 --- a/app/src/main/java/ac/mdiq/podcini/ui/fragment/swipeactions/SwipeAction.kt +++ b/app/src/main/java/ac/mdiq/podcini/ui/fragment/swipeactions/SwipeAction.kt @@ -25,7 +25,6 @@ interface SwipeAction { companion object { const val ADD_TO_QUEUE: String = "ADD_TO_QUEUE" -// const val REMOVE_FROM_INBOX: String = "REMOVE_FROM_INBOX" const val START_DOWNLOAD: String = "START_DOWNLOAD" const val MARK_FAV: String = "MARK_FAV" const val TOGGLE_PLAYED: String = "MARK_PLAYED" diff --git a/app/src/main/java/ac/mdiq/podcini/ui/view/viewholder/EpisodeItemViewHolder.kt b/app/src/main/java/ac/mdiq/podcini/ui/view/viewholder/EpisodeItemViewHolder.kt index c37c7f5f..195b6b22 100644 --- a/app/src/main/java/ac/mdiq/podcini/ui/view/viewholder/EpisodeItemViewHolder.kt +++ b/app/src/main/java/ac/mdiq/podcini/ui/view/viewholder/EpisodeItemViewHolder.kt @@ -164,22 +164,22 @@ class EpisodeItemViewHolder(private val activity: MainActivity, parent: ViewGrou secondaryActionProgress.setIndeterminate(false) } - duration.text = ac.mdiq.podcini.util.Converter.getDurationStringLong(media.getDuration()) + duration.text = Converter.getDurationStringLong(media.getDuration()) duration.setContentDescription(activity.getString(R.string.chapter_duration, - ac.mdiq.podcini.util.Converter.getDurationStringLocalized(activity, media.getDuration().toLong()))) + Converter.getDurationStringLocalized(activity, media.getDuration().toLong()))) if (PlaybackStatus.isPlaying(item?.media) || item?.isInProgress == true) { val progress: Int = (100.0 * media.getPosition() / media.getDuration()).toInt() val remainingTime = max((media.getDuration() - media.getPosition()).toDouble(), 0.0).toInt() progressBar.progress = progress - position.text = ac.mdiq.podcini.util.Converter.getDurationStringLong(media.getPosition()) + position.text = Converter.getDurationStringLong(media.getPosition()) position.setContentDescription(activity.getString(R.string.position, - ac.mdiq.podcini.util.Converter.getDurationStringLocalized(activity, media.getPosition().toLong()))) + Converter.getDurationStringLocalized(activity, media.getPosition().toLong()))) progressBar.visibility = View.VISIBLE position.visibility = View.VISIBLE if (UserPreferences.shouldShowRemainingTime()) { - duration.text = (if ((remainingTime > 0)) "-" else "") + ac.mdiq.podcini.util.Converter.getDurationStringLong(remainingTime) + duration.text = (if ((remainingTime > 0)) "-" else "") + Converter.getDurationStringLong(remainingTime) duration.setContentDescription(activity.getString(R.string.chapter_duration, - ac.mdiq.podcini.util.Converter.getDurationStringLocalized(activity, (media.getDuration() - media.getPosition()).toLong()))) + Converter.getDurationStringLocalized(activity, (media.getDuration() - media.getPosition()).toLong()))) } } else { progressBar.visibility = View.GONE @@ -264,7 +264,7 @@ class EpisodeItemViewHolder(private val activity: MainActivity, parent: ViewGrou fun notifyPlaybackPositionUpdated(event: PlaybackPositionEvent) { progressBar.progress = (100.0 * event.position / event.duration).toInt() - position.text = ac.mdiq.podcini.util.Converter.getDurationStringLong(event.position) + position.text = Converter.getDurationStringLong(event.position) updateDuration(event) duration.visibility = View.VISIBLE // Even if the duration was previously unknown, it is now known } diff --git a/app/src/main/res/drawable/baseline_arrow_downward_24.xml b/app/src/main/res/drawable/baseline_arrow_downward_24.xml new file mode 100644 index 00000000..dcfb7840 --- /dev/null +++ b/app/src/main/res/drawable/baseline_arrow_downward_24.xml @@ -0,0 +1,5 @@ + + + diff --git a/app/src/main/res/drawable/baseline_arrow_upward_24.xml b/app/src/main/res/drawable/baseline_arrow_upward_24.xml new file mode 100644 index 00000000..46d5d587 --- /dev/null +++ b/app/src/main/res/drawable/baseline_arrow_upward_24.xml @@ -0,0 +1,5 @@ + + + diff --git a/app/src/main/res/layout/feed_item_list_fragment.xml b/app/src/main/res/layout/feed_item_list_fragment.xml index 6d9e93d6..32d4b726 100644 --- a/app/src/main/res/layout/feed_item_list_fragment.xml +++ b/app/src/main/res/layout/feed_item_list_fragment.xml @@ -15,8 +15,7 @@ android:id="@+id/collapsing_toolbar" android:layout_width="match_parent" android:layout_height="match_parent" - app:scrimAnimationDuration="200" - app:layout_scrollFlags="scroll|exitUntilCollapsed"> + app:scrimAnimationDuration="200"> - - - - + diff --git a/app/src/main/res/menu/multi_select_options.xml b/app/src/main/res/menu/multi_select_options.xml index 5cb2b760..33755cbb 100644 --- a/app/src/main/res/menu/multi_select_options.xml +++ b/app/src/main/res/menu/multi_select_options.xml @@ -1,6 +1,16 @@ + + Marked as unplayed Mark as read To jump to positions, you need to play the episode + + %d episode marked as favorite. + %d episodes marked as favorite. + %d episode marked as played. %d episodes marked as played. diff --git a/changelog.md b/changelog.md index 3b670b44..794c83e3 100644 --- a/changelog.md +++ b/changelog.md @@ -68,4 +68,14 @@ * fixed the "getValue() can not be null" bug * enabled ksp for Kotlin builds -* cleaned up build.gradle files \ No newline at end of file +* cleaned up build.gradle files + +## 4.2.5 + +* change in click operation + * click on title area opens the podcast/episode + * long-press on title area automatically enters in selection mode + * select all above or below are put to action bar together with select all + * operations are only on the selected (single or multiple) + * popup menus for single item operation are disabled +* in podcast view, the title bar no longer scrolls off screen \ No newline at end of file diff --git a/fastlane/metadata/android/en-US/changelogs/3020108.txt b/fastlane/metadata/android/en-US/changelogs/3020108.txt new file mode 100644 index 00000000..5f8c6494 --- /dev/null +++ b/fastlane/metadata/android/en-US/changelogs/3020108.txt @@ -0,0 +1,10 @@ + +Version 4.2.5 brings several changes: + +* change in click operation + * click on title area opens the podcast/episode + * long-press on title area automatically enters in selection mode + * select all above or below are put to action bar together with select all + * operations are only on the selected (single or multiple) + * popup menus for single item operation are disabled +* in podcast view, the title bar no longer scrolls off screen