From 9af8756d484fa21e94e90667358c3d72bd1a1e0a Mon Sep 17 00:00:00 2001 From: Isira Seneviratne Date: Sun, 23 Jun 2024 08:57:51 +0530 Subject: [PATCH] Replace CommentRepliesFragment with bottom sheet composable, improve previews --- .../java/org/schabi/newpipe/MainActivity.java | 103 ++---------------- .../fragments/list/comments/Comment.kt | 31 ++++-- .../list/comments/CommentRepliesFragment.kt | 56 ---------- .../fragments/list/comments/CommentSection.kt | 4 +- .../schabi/newpipe/util/NavigationHelper.java | 14 --- 5 files changed, 35 insertions(+), 173 deletions(-) delete mode 100644 app/src/main/java/org/schabi/newpipe/fragments/list/comments/CommentRepliesFragment.kt diff --git a/app/src/main/java/org/schabi/newpipe/MainActivity.java b/app/src/main/java/org/schabi/newpipe/MainActivity.java index d288ab4c6..6b95ea8a8 100644 --- a/app/src/main/java/org/schabi/newpipe/MainActivity.java +++ b/app/src/main/java/org/schabi/newpipe/MainActivity.java @@ -44,7 +44,6 @@ import android.widget.FrameLayout; import android.widget.Spinner; import androidx.annotation.NonNull; -import androidx.annotation.Nullable; import androidx.appcompat.app.ActionBar; import androidx.appcompat.app.ActionBarDrawerToggle; import androidx.appcompat.app.AppCompatActivity; @@ -52,7 +51,6 @@ import androidx.core.app.ActivityCompat; import androidx.core.view.GravityCompat; import androidx.drawerlayout.widget.DrawerLayout; import androidx.fragment.app.Fragment; -import androidx.fragment.app.FragmentContainerView; import androidx.fragment.app.FragmentManager; import androidx.preference.PreferenceManager; @@ -71,7 +69,6 @@ import org.schabi.newpipe.extractor.services.peertube.PeertubeInstance; import org.schabi.newpipe.fragments.BackPressable; import org.schabi.newpipe.fragments.MainFragment; import org.schabi.newpipe.fragments.detail.VideoDetailFragment; -import org.schabi.newpipe.fragments.list.comments.CommentRepliesFragment; import org.schabi.newpipe.fragments.list.search.SearchFragment; import org.schabi.newpipe.local.feed.notifications.NotificationWorker; import org.schabi.newpipe.player.Player; @@ -557,33 +554,22 @@ public class MainActivity extends AppCompatActivity { // interacts with a fragment inside fragment_holder so all back presses should be // handled by it if (bottomSheetHiddenOrCollapsed()) { - final FragmentManager fm = getSupportFragmentManager(); - final Fragment fragment = fm.findFragmentById(R.id.fragment_holder); + final var fm = getSupportFragmentManager(); + final var fragment = fm.findFragmentById(R.id.fragment_holder); // If current fragment implements BackPressable (i.e. can/wanna handle back press) // delegate the back press to it - if (fragment instanceof BackPressable) { - if (((BackPressable) fragment).onBackPressed()) { - return; - } - } else if (fragment instanceof CommentRepliesFragment) { - // expand DetailsFragment if CommentRepliesFragment was opened - // to show the top level comments again - // Expand DetailsFragment if CommentRepliesFragment was opened - // and no other CommentRepliesFragments are on top of the back stack - // to show the top level comments again. - openDetailFragmentFromCommentReplies(fm, false); + if (fragment instanceof BackPressable backPressable && backPressable.onBackPressed()) { + return; } - } else { - final Fragment fragmentPlayer = getSupportFragmentManager() + final var fragmentPlayer = getSupportFragmentManager() .findFragmentById(R.id.fragment_player_holder); // If current fragment implements BackPressable (i.e. can/wanna handle back press) // delegate the back press to it - if (fragmentPlayer instanceof BackPressable) { - if (!((BackPressable) fragmentPlayer).onBackPressed()) { - BottomSheetBehavior.from(mainBinding.fragmentPlayerHolder) - .setState(BottomSheetBehavior.STATE_COLLAPSED); - } + if (fragmentPlayer instanceof BackPressable backPressable + && !backPressable.onBackPressed()) { + BottomSheetBehavior.from(mainBinding.fragmentPlayerHolder) + .setState(BottomSheetBehavior.STATE_COLLAPSED); return; } } @@ -647,15 +633,9 @@ public class MainActivity extends AppCompatActivity { * */ private void onHomeButtonPressed() { - final FragmentManager fm = getSupportFragmentManager(); - final Fragment fragment = fm.findFragmentById(R.id.fragment_holder); + final var fm = getSupportFragmentManager(); - if (fragment instanceof CommentRepliesFragment) { - // Expand DetailsFragment if CommentRepliesFragment was opened - // and no other CommentRepliesFragments are on top of the back stack - // to show the top level comments again. - openDetailFragmentFromCommentReplies(fm, true); - } else if (!NavigationHelper.tryGotoSearchFragment(fm)) { + if (!NavigationHelper.tryGotoSearchFragment(fm)) { // If search fragment wasn't found in the backstack go to the main fragment NavigationHelper.gotoMainFragment(fm); } @@ -853,67 +833,6 @@ public class MainActivity extends AppCompatActivity { } } - private void openDetailFragmentFromCommentReplies( - @NonNull final FragmentManager fm, - final boolean popBackStack - ) { - // obtain the name of the fragment under the replies fragment that's going to be popped - @Nullable final String fragmentUnderEntryName; - if (fm.getBackStackEntryCount() < 2) { - fragmentUnderEntryName = null; - } else { - fragmentUnderEntryName = fm.getBackStackEntryAt(fm.getBackStackEntryCount() - 2) - .getName(); - } - - // the root comment is the comment for which the user opened the replies page - final var repliesFragment = (CommentRepliesFragment) - fm.findFragmentByTag(CommentRepliesFragment.TAG); - final var rootComment = repliesFragment == null ? null : repliesFragment.getComment(); - - // sometimes this function pops the backstack, other times it's handled by the system - if (popBackStack) { - fm.popBackStackImmediate(); - } - - // only expand the bottom sheet back if there are no more nested comment replies fragments - // stacked under the one that is currently being popped - if (CommentRepliesFragment.TAG.equals(fragmentUnderEntryName)) { - return; - } - - final BottomSheetBehavior behavior = BottomSheetBehavior - .from(mainBinding.fragmentPlayerHolder); - // do not return to the comment if the details fragment was closed - if (behavior.getState() == BottomSheetBehavior.STATE_HIDDEN) { - return; - } - - // scroll to the root comment once the bottom sheet expansion animation is finished - behavior.addBottomSheetCallback(new BottomSheetBehavior.BottomSheetCallback() { - @Override - public void onStateChanged(@NonNull final View bottomSheet, - final int newState) { - if (newState == BottomSheetBehavior.STATE_EXPANDED) { - final Fragment detailFragment = fm.findFragmentById( - R.id.fragment_player_holder); - if (detailFragment instanceof VideoDetailFragment && rootComment != null) { - // should always be the case - ((VideoDetailFragment) detailFragment).scrollToComment(rootComment); - } - behavior.removeBottomSheetCallback(this); - } - } - - @Override - public void onSlide(@NonNull final View bottomSheet, final float slideOffset) { - // not needed, listener is removed once the sheet is expanded - } - }); - - behavior.setState(BottomSheetBehavior.STATE_EXPANDED); - } - private boolean bottomSheetHiddenOrCollapsed() { final BottomSheetBehavior bottomSheetBehavior = BottomSheetBehavior.from(mainBinding.fragmentPlayerHolder); diff --git a/app/src/main/java/org/schabi/newpipe/fragments/list/comments/Comment.kt b/app/src/main/java/org/schabi/newpipe/fragments/list/comments/Comment.kt index ac254a5b3..bd00b7bd8 100644 --- a/app/src/main/java/org/schabi/newpipe/fragments/list/comments/Comment.kt +++ b/app/src/main/java/org/schabi/newpipe/fragments/list/comments/Comment.kt @@ -12,7 +12,9 @@ import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.size import androidx.compose.foundation.layout.width import androidx.compose.foundation.shape.CircleShape +import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.ModalBottomSheet import androidx.compose.material3.Surface import androidx.compose.material3.Text import androidx.compose.material3.TextButton @@ -38,6 +40,8 @@ import androidx.compose.ui.tooling.preview.PreviewParameter import androidx.compose.ui.tooling.preview.PreviewParameterProvider import androidx.compose.ui.unit.dp import androidx.fragment.app.FragmentActivity +import androidx.paging.Pager +import androidx.paging.PagingConfig import coil.compose.AsyncImage import org.schabi.newpipe.R import org.schabi.newpipe.extractor.Page @@ -60,10 +64,12 @@ fun rememberParsedText(commentText: Description): AnnotatedString { } } +@OptIn(ExperimentalMaterial3Api::class) @Composable fun Comment(comment: CommentsInfoItem) { val context = LocalContext.current var isExpanded by rememberSaveable { mutableStateOf(false) } + var showReplies by rememberSaveable { mutableStateOf(false) } Surface(color = MaterialTheme.colorScheme.background) { Row( @@ -139,22 +145,29 @@ fun Comment(comment: CommentsInfoItem) { } if (comment.replies != null) { - TextButton(onClick = { - NavigationHelper.openCommentRepliesFragment( - context as FragmentActivity, comment - ) - }) { - Text( - text = pluralStringResource( - R.plurals.replies, comment.replyCount, comment.replyCount.toString() - ) + TextButton(onClick = { showReplies = true }) { + val text = pluralStringResource( + R.plurals.replies, comment.replyCount, comment.replyCount.toString() ) + Text(text = text) } } } } } } + + if (showReplies) { + ModalBottomSheet(onDismissRequest = { showReplies = false }) { + val flow = remember(comment) { + Pager(PagingConfig(pageSize = 20, enablePlaceholders = false)) { + CommentsSource(comment.serviceId, comment.url, comment.replies) + }.flow + } + + CommentSection(parentComment = comment, flow = flow) + } + } } fun CommentsInfoItem( diff --git a/app/src/main/java/org/schabi/newpipe/fragments/list/comments/CommentRepliesFragment.kt b/app/src/main/java/org/schabi/newpipe/fragments/list/comments/CommentRepliesFragment.kt deleted file mode 100644 index e25b3a960..000000000 --- a/app/src/main/java/org/schabi/newpipe/fragments/list/comments/CommentRepliesFragment.kt +++ /dev/null @@ -1,56 +0,0 @@ -package org.schabi.newpipe.fragments.list.comments - -import android.os.Bundle -import android.view.LayoutInflater -import android.view.View -import android.view.ViewGroup -import androidx.appcompat.app.AppCompatActivity -import androidx.compose.runtime.remember -import androidx.compose.ui.platform.ComposeView -import androidx.compose.ui.platform.ViewCompositionStrategy -import androidx.fragment.app.Fragment -import androidx.paging.Pager -import androidx.paging.PagingConfig -import org.schabi.newpipe.extractor.comments.CommentsInfoItem -import org.schabi.newpipe.ktx.serializable -import org.schabi.newpipe.ui.theme.AppTheme -import org.schabi.newpipe.util.Localization - -class CommentRepliesFragment : Fragment() { - lateinit var comment: CommentsInfoItem - - override fun onCreateView( - inflater: LayoutInflater, - container: ViewGroup?, - savedInstanceState: Bundle? - ): View { - comment = requireArguments().serializable(COMMENT_KEY)!! - - val activity = requireActivity() as AppCompatActivity - val bar = activity.supportActionBar!! - bar.setDisplayShowTitleEnabled(true) - bar.title = Localization.replyCount(activity, comment.replyCount) - - return ComposeView(activity).apply { - setViewCompositionStrategy(ViewCompositionStrategy.DisposeOnViewTreeLifecycleDestroyed) - setContent { - val flow = remember(comment) { - Pager(PagingConfig(pageSize = 20, enablePlaceholders = false)) { - CommentsSource(comment.serviceId, comment.url, comment.replies) - }.flow - } - - AppTheme { - CommentSection(parentComment = comment, flow = flow) - } - } - } - } - - companion object { - @JvmField - val TAG = CommentRepliesFragment::class.simpleName!! - - const val COMMENT_KEY = "comment" - } -} diff --git a/app/src/main/java/org/schabi/newpipe/fragments/list/comments/CommentSection.kt b/app/src/main/java/org/schabi/newpipe/fragments/list/comments/CommentSection.kt index dc4104b9d..ed1969b68 100644 --- a/app/src/main/java/org/schabi/newpipe/fragments/list/comments/CommentSection.kt +++ b/app/src/main/java/org/schabi/newpipe/fragments/list/comments/CommentSection.kt @@ -45,7 +45,7 @@ fun CommentSection( @Preview(name = "Dark mode", uiMode = Configuration.UI_MODE_NIGHT_YES) @Composable private fun CommentSectionPreview() { - val comments = (0..100).map { + val comments = (1..100).map { CommentsInfoItem( commentText = Description("Comment $it", Description.PLAIN_TEXT), uploaderName = "Test" @@ -69,7 +69,7 @@ private fun CommentRepliesPreview() { isPinned = true, isHeartedByUploader = true ) - val replies = (0..100).map { + val replies = (1..100).map { CommentsInfoItem( commentText = Description("Reply $it", Description.PLAIN_TEXT), uploaderName = "Test" diff --git a/app/src/main/java/org/schabi/newpipe/util/NavigationHelper.java b/app/src/main/java/org/schabi/newpipe/util/NavigationHelper.java index 9accf22a5..e6af64ffe 100644 --- a/app/src/main/java/org/schabi/newpipe/util/NavigationHelper.java +++ b/app/src/main/java/org/schabi/newpipe/util/NavigationHelper.java @@ -9,7 +9,6 @@ import android.content.Context; import android.content.Intent; import android.net.Uri; import android.os.Build; -import android.os.Bundle; import android.util.Log; import android.widget.Toast; @@ -46,7 +45,6 @@ import org.schabi.newpipe.extractor.stream.VideoStream; import org.schabi.newpipe.fragments.MainFragment; import org.schabi.newpipe.fragments.detail.VideoDetailFragment; import org.schabi.newpipe.fragments.list.channel.ChannelFragment; -import org.schabi.newpipe.fragments.list.comments.CommentRepliesFragment; import org.schabi.newpipe.fragments.list.kiosk.KioskFragment; import org.schabi.newpipe.fragments.list.playlist.PlaylistFragment; import org.schabi.newpipe.fragments.list.search.SearchFragment; @@ -502,18 +500,6 @@ public final class NavigationHelper { } } - public static void openCommentRepliesFragment(@NonNull final FragmentActivity activity, - @NonNull final CommentsInfoItem comment) { - final var bundle = new Bundle(); - bundle.putSerializable(CommentRepliesFragment.COMMENT_KEY, comment); - - defaultTransaction(activity.getSupportFragmentManager()) - .replace(R.id.fragment_holder, CommentRepliesFragment.class, bundle, - CommentRepliesFragment.TAG) - .addToBackStack(CommentRepliesFragment.TAG) - .commit(); - } - public static void openPlaylistFragment(final FragmentManager fragmentManager, final int serviceId, final String url, @NonNull final String name) {