Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Migrate playlist fragment to Jetpack Compose #11259

Draft
wants to merge 72 commits into
base: refactor
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
72 commits
Select commit Hold shift + click to select a range
92a7f22
Load notification icons using Coil
Isira-Seneviratne Jun 19, 2024
844b4ed
Migrate to Coil from Picasso
Isira-Seneviratne Jun 22, 2024
e6302cc
Clean up Picasso leftovers
Isira-Seneviratne Jun 22, 2024
4d3b4a7
Enable RGB-565 for low-end devices
Isira-Seneviratne Jun 22, 2024
f74402b
Added Coil helper method
Isira-Seneviratne Jun 22, 2024
e3b7bf4
Add annotation
Isira-Seneviratne Jun 26, 2024
8aa2590
Simplify newImageLoader implementation
Isira-Seneviratne Jun 26, 2024
71361de
Use Coil's default disk and memory cache config
Isira-Seneviratne Jun 26, 2024
39d0691
Enable crossfade animation
Isira-Seneviratne Jun 27, 2024
c4ada7f
Correct method name
Isira-Seneviratne Jul 3, 2024
348a79f
Fix thumbnail not being displayed in media notification
Isira-Seneviratne Jul 3, 2024
da83646
Update Coil
Isira-Seneviratne Jul 22, 2024
4ec7532
Addressed code review comments
Isira-Seneviratne Jul 22, 2024
08f0338
Convert comment replies views to Jetpack Compose
Isira-Seneviratne May 12, 2024
c429deb
Rename .java to .kt
Isira-Seneviratne May 12, 2024
e193265
Use reply header composable in fragment
Isira-Seneviratne May 12, 2024
fc8a748
Added like count
Isira-Seneviratne May 12, 2024
b302c1b
Added missing comment features, fixed theming
Isira-Seneviratne May 17, 2024
b14da3a
Add comment ellipsis
Isira-Seneviratne Jun 16, 2024
65fce6e
Update replies fragment to use the comment composable as well
Isira-Seneviratne Jun 18, 2024
f056edd
Improve previews, display date of comment
Isira-Seneviratne Jun 18, 2024
e75eb2d
Fixed some comment issues
Isira-Seneviratne Jun 18, 2024
c29fa70
Use AnnotatedString to handle HTML parsing
Isira-Seneviratne Jun 19, 2024
e71fc38
Add replies button
Isira-Seneviratne Jun 19, 2024
b6a0f5d
Set view strategy
Isira-Seneviratne Jun 20, 2024
552ea50
Fixed like count display
Isira-Seneviratne Jun 20, 2024
cf3fb0b
Fixed fragment title
Isira-Seneviratne Jun 21, 2024
f27273e
Rename .java to .kt
Isira-Seneviratne Jun 21, 2024
03bc4e2
Migrate comments fragment to Jetpack Compose
Isira-Seneviratne Jun 21, 2024
ff88184
Added scrollbar to comment section
Isira-Seneviratne Jun 21, 2024
754bf45
Replace CommentRepliesFragment with bottom sheet composable, improve …
Isira-Seneviratne Jun 23, 2024
cc6f1ff
Replace Spacers with the horizontalArrangement parameter
Isira-Seneviratne Jun 23, 2024
8d4c608
Handle no comments and comments disabled scenarios
Isira-Seneviratne Jun 23, 2024
e87a2e0
Rm redundant Surface
Isira-Seneviratne Jun 26, 2024
219da28
Improve code organization
Isira-Seneviratne Jun 28, 2024
975a341
Cache paging data using the cachedIn() extension
Isira-Seneviratne Jun 30, 2024
94ef79c
Add comment view model
Isira-Seneviratne Jul 2, 2024
21b22d3
Rm extra padding in header
Isira-Seneviratne Jul 5, 2024
4b13e30
Replace padding modifier with verticalArrangement in comment header
Isira-Seneviratne Jul 5, 2024
1c503ce
Added loading indicator
Isira-Seneviratne Jul 6, 2024
ddbfcf8
Rm unused method
Isira-Seneviratne Jul 8, 2024
3c55d95
Improve comment loading smoothness
Isira-Seneviratne Jul 8, 2024
f438ba4
Animate comment expand/collapse
Isira-Seneviratne Jul 9, 2024
b83f643
Make parsed links clickable, visible
Isira-Seneviratne Jul 10, 2024
0d9c4aa
Fix alignment of comment message
Isira-Seneviratne Jul 12, 2024
f285bc0
Fix some modifiers
Isira-Seneviratne Jul 16, 2024
f1591ab
Added DescriptionText composable
Isira-Seneviratne Jul 25, 2024
b2748cc
Improved component organisation
Isira-Seneviratne Jul 28, 2024
10dd571
Update Kotlin to 2.0, update dependencies and fix issues
Isira-Seneviratne Jul 28, 2024
68b3dd5
Create playlist header composable
Isira-Seneviratne Jun 28, 2024
8603b0d
Start implementing full playlist view, add view model
Isira-Seneviratne Jun 28, 2024
bf1c9ba
Start implementing stream composable, grid layout
Isira-Seneviratne Jul 1, 2024
72bbe0e
Implement card and list layouts, check for preferred layout from sett…
Isira-Seneviratne Jul 2, 2024
37e4064
Cache total duration calculation
Isira-Seneviratne Jul 2, 2024
462ed5c
Added loading indicator to playlist view
Isira-Seneviratne Jul 6, 2024
5e33b69
Moved stream display to separate composable for reusability
Isira-Seneviratne Jul 8, 2024
bbdff4b
Show dropdown menu on long click, make some adjustments
Isira-Seneviratne Jul 9, 2024
7501f2f
Animate playlist description expand/collapse
Isira-Seneviratne Jul 9, 2024
c978fb7
Fix stream thumbnail text color
Isira-Seneviratne Jul 12, 2024
3da4aee
Rename .java to .kt
Isira-Seneviratne Jul 13, 2024
9dfd064
Remove old playlist fragment
Isira-Seneviratne Jul 13, 2024
b9556a1
Improved PlaylistHeader
Isira-Seneviratne Jul 16, 2024
82e5b6b
Remove playlist preview dependency on external HTTP calls
Isira-Seneviratne Jul 22, 2024
d7de38c
Remove TextEllipsizer
Isira-Seneviratne Jul 25, 2024
b443abb
Improved component organisation
Isira-Seneviratne Jul 28, 2024
06a5828
Improved stream components
Isira-Seneviratne Jul 28, 2024
8e8f627
Dismiss popup menu on clicking an option
Isira-Seneviratne Jul 28, 2024
4453061
Added stream grid mini preview
Isira-Seneviratne Jul 28, 2024
2f9364a
Fix crash when opening YouTube mixes
Isira-Seneviratne Jul 29, 2024
75475da
Improve StreamThumbnail composable
Isira-Seneviratne Jul 29, 2024
a885a88
Add duration comment
Isira-Seneviratne Jul 29, 2024
aec18c7
Move item view mode composable
Isira-Seneviratne Aug 1, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 10 additions & 9 deletions app/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ plugins {
id "kotlin-parcelize"
id "checkstyle"
id "org.sonarqube" version "4.0.0.2929"
id "org.jetbrains.kotlin.plugin.compose" version "${kotlin_version}"
}

android {
Expand Down Expand Up @@ -104,10 +105,6 @@ android {
'META-INF/COPYRIGHT']
}
}

composeOptions {
kotlinCompilerExtensionVersion = "1.5.3"
}
}

ext {
Expand Down Expand Up @@ -203,7 +200,7 @@ dependencies {
// name and the commit hash with the commit hash of the (pushed) commit you want to test
// This works thanks to JitPack: https://jitpack.io/
implementation 'com.github.TeamNewPipe:nanojson:1d9e1aea9049fc9f85e68b43ba39fe7be1c1f751'
implementation 'com.github.TeamNewPipe:NewPipeExtractor:v0.24.0'
implementation 'com.github.TeamNewPipe:NewPipeExtractor:v0.24.2'
implementation 'com.github.TeamNewPipe:NoNonsense-FilePicker:5.0.0'

/** Checkstyle **/
Expand Down Expand Up @@ -267,8 +264,7 @@ dependencies {
implementation "com.github.lisawray.groupie:groupie-viewbinding:${groupieVersion}"

// Image loading
//noinspection GradleDependency --> 2.8 is the last version, not 2.71828!
implementation "com.squareup.picasso:picasso:2.8"
implementation 'io.coil-kt:coil-compose:2.7.0'

// Markdown library for Android
implementation "io.noties.markwon:core:${markwonVersion}"
Expand All @@ -290,10 +286,15 @@ dependencies {
implementation "org.ocpsoft.prettytime:prettytime:5.0.8.Final"

// Jetpack Compose
implementation(platform('androidx.compose:compose-bom:2024.02.01'))
implementation 'androidx.compose.material3:material3'
implementation(platform('androidx.compose:compose-bom:2024.06.00'))
implementation 'androidx.compose.material3:material3:1.3.0-beta05'
implementation 'androidx.compose.material3.adaptive:adaptive:1.0.0-beta04'
implementation 'androidx.activity:activity-compose'
implementation 'androidx.compose.ui:ui-tooling-preview'
implementation 'androidx.compose.ui:ui-text:1.7.0-beta06' // Needed for parsing HTML to AnnotatedString
implementation 'androidx.lifecycle:lifecycle-viewmodel-compose'
implementation 'androidx.paging:paging-compose:3.3.1'
implementation 'com.github.nanihadesuka:LazyColumnScrollbar:2.2.0'

/** Debugging **/
// Memory leak detection
Expand Down
24 changes: 15 additions & 9 deletions app/src/main/java/org/schabi/newpipe/App.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package org.schabi.newpipe;

import android.app.ActivityManager;
import android.app.Application;
import android.content.Context;
import android.content.SharedPreferences;
Expand All @@ -8,6 +9,7 @@
import androidx.annotation.NonNull;
import androidx.core.app.NotificationChannelCompat;
import androidx.core.app.NotificationManagerCompat;
import androidx.core.content.ContextCompat;
import androidx.preference.PreferenceManager;

import com.jakewharton.processphoenix.ProcessPhoenix;
Expand All @@ -20,10 +22,9 @@
import org.schabi.newpipe.ktx.ExceptionUtils;
import org.schabi.newpipe.settings.NewPipeSettings;
import org.schabi.newpipe.util.Localization;
import org.schabi.newpipe.util.image.ImageStrategy;
import org.schabi.newpipe.util.image.PicassoHelper;
import org.schabi.newpipe.util.ServiceHelper;
import org.schabi.newpipe.util.StateSaver;
import org.schabi.newpipe.util.image.ImageStrategy;
import org.schabi.newpipe.util.image.PreferredImageQuality;

import java.io.IOException;
Expand All @@ -32,6 +33,9 @@
import java.util.List;
import java.util.Objects;

import coil.ImageLoader;
import coil.ImageLoaderFactory;
import coil.util.DebugLogger;
import io.reactivex.rxjava3.exceptions.CompositeException;
import io.reactivex.rxjava3.exceptions.MissingBackpressureException;
import io.reactivex.rxjava3.exceptions.OnErrorNotImplementedException;
Expand All @@ -57,7 +61,7 @@
* along with NewPipe. If not, see <http://www.gnu.org/licenses/>.
*/

public class App extends Application {
public class App extends Application implements ImageLoaderFactory {
public static final String PACKAGE_NAME = BuildConfig.APPLICATION_ID;
private static final String TAG = App.class.toString();

Expand Down Expand Up @@ -108,20 +112,22 @@ public void onCreate() {

// Initialize image loader
final SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this);
PicassoHelper.init(this);
ImageStrategy.setPreferredImageQuality(PreferredImageQuality.fromPreferenceKey(this,
prefs.getString(getString(R.string.image_quality_key),
getString(R.string.image_quality_default))));
PicassoHelper.setIndicatorsEnabled(MainActivity.DEBUG
&& prefs.getBoolean(getString(R.string.show_image_indicators_key), false));

configureRxJavaErrorHandler();
}

@NonNull
@Override
public void onTerminate() {
super.onTerminate();
PicassoHelper.terminate();
public ImageLoader newImageLoader() {
return new ImageLoader.Builder(this)
.allowRgb565(ContextCompat.getSystemService(this, ActivityManager.class)
.isLowRamDevice())
.logger(BuildConfig.DEBUG ? new DebugLogger() : null)
.crossfade(true)
.build();
}

protected Downloader getDownloader() {
Expand Down
109 changes: 13 additions & 96 deletions app/src/main/java/org/schabi/newpipe/MainActivity.java
Original file line number Diff line number Diff line change
Expand Up @@ -44,15 +44,13 @@
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;
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;

Expand All @@ -66,13 +64,11 @@
import org.schabi.newpipe.error.ErrorUtil;
import org.schabi.newpipe.extractor.NewPipe;
import org.schabi.newpipe.extractor.StreamingService;
import org.schabi.newpipe.extractor.comments.CommentsInfoItem;
import org.schabi.newpipe.extractor.exceptions.ExtractionException;
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;
Expand Down Expand Up @@ -558,33 +554,22 @@ public void onBackPressed() {
// 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;
}
}
Expand Down Expand Up @@ -648,15 +633,9 @@ public void onRequestPermissionsResult(final int requestCode,
* </pre>
*/
private void onHomeButtonPressed() {
final FragmentManager fm = getSupportFragmentManager();
final Fragment fragment = fm.findFragmentById(R.id.fragment_holder);

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)) {
final var fm = getSupportFragmentManager();

if (!NavigationHelper.tryGotoSearchFragment(fm)) {
// If search fragment wasn't found in the backstack go to the main fragment
NavigationHelper.gotoMainFragment(fm);
}
Expand Down Expand Up @@ -789,7 +768,7 @@ private void handleIntent(final Intent intent) {
break;
case PLAYLIST:
NavigationHelper.openPlaylistFragment(getSupportFragmentManager(),
serviceId, url, title);
serviceId, url);
break;
}
} else if (intent.hasExtra(Constants.KEY_OPEN_SEARCH)) {
Expand Down Expand Up @@ -854,68 +833,6 @@ public void onReceive(final Context context, final Intent intent) {
}
}

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
@Nullable final CommentRepliesFragment repliesFragment =
(CommentRepliesFragment) fm.findFragmentByTag(CommentRepliesFragment.TAG);
@Nullable final CommentsInfoItem rootComment =
repliesFragment == null ? null : repliesFragment.getCommentsInfoItem();

// 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<FragmentContainerView> 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<FrameLayout> bottomSheetBehavior =
BottomSheetBehavior.from(mainBinding.fragmentPlayerHolder);
Expand Down
4 changes: 2 additions & 2 deletions app/src/main/java/org/schabi/newpipe/about/AboutActivity.kt
Original file line number Diff line number Diff line change
Expand Up @@ -167,8 +167,8 @@ class AboutActivity : AppCompatActivity() {
"https://square.github.io/okhttp/", StandardLicenses.APACHE2
),
SoftwareComponent(
"Picasso", "2013", "Square, Inc.",
"https://square.github.io/picasso/", StandardLicenses.APACHE2
"Coil", "2023", "Coil Contributors",
"https://coil-kt.github.io/coil/", StandardLicenses.APACHE2
),
SoftwareComponent(
"PrettyTime", "2012 - 2020", "Lincoln Baxter, III",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,6 @@
import org.schabi.newpipe.extractor.utils.Utils;
import org.schabi.newpipe.util.ThemeHelper;

import java.io.UnsupportedEncodingException;

/*
* Created by beneth <[email protected]> on 06.12.16.
*
Expand Down Expand Up @@ -187,14 +185,11 @@ private void handleCookiesFromUrl(@Nullable final String url) {
final int abuseEnd = url.indexOf("+path");

try {
String abuseCookie = url.substring(abuseStart + 13, abuseEnd);
abuseCookie = Utils.decodeUrlUtf8(abuseCookie);
handleCookies(abuseCookie);
} catch (UnsupportedEncodingException | StringIndexOutOfBoundsException e) {
handleCookies(Utils.decodeUrlUtf8(url.substring(abuseStart + 13, abuseEnd)));
} catch (final StringIndexOutOfBoundsException e) {
if (MainActivity.DEBUG) {
e.printStackTrace();
Log.d(TAG, "handleCookiesFromUrl: invalid google abuse starting at "
+ abuseStart + " and ending at " + abuseEnd + " for url " + url);
+ abuseStart + " and ending at " + abuseEnd + " for url " + url, e);
}
}
}
Expand Down
Loading
Loading