From 34ab93c9bd214c02882386db09392e02b59b0540 Mon Sep 17 00:00:00 2001 From: ktprograms Date: Mon, 1 Nov 2021 11:48:20 +0800 Subject: [PATCH 001/107] Fix player controls not hiding if resumed from media button --- app/src/main/java/org/schabi/newpipe/player/Player.java | 3 +++ .../org/schabi/newpipe/player/playback/PlayerMediaSession.java | 1 + 2 files changed, 4 insertions(+) diff --git a/app/src/main/java/org/schabi/newpipe/player/Player.java b/app/src/main/java/org/schabi/newpipe/player/Player.java index 22e66e793..4e2a9d065 100644 --- a/app/src/main/java/org/schabi/newpipe/player/Player.java +++ b/app/src/main/java/org/schabi/newpipe/player/Player.java @@ -3754,6 +3754,9 @@ public final class Player implements case KeyEvent.KEYCODE_SPACE: if (isFullscreen) { playPause(); + if (isPlaying()) { + hideControls(0, 0); + } } break; case KeyEvent.KEYCODE_BACK: diff --git a/app/src/main/java/org/schabi/newpipe/player/playback/PlayerMediaSession.java b/app/src/main/java/org/schabi/newpipe/player/playback/PlayerMediaSession.java index 9dcb12344..fbe12efb1 100644 --- a/app/src/main/java/org/schabi/newpipe/player/playback/PlayerMediaSession.java +++ b/app/src/main/java/org/schabi/newpipe/player/playback/PlayerMediaSession.java @@ -88,6 +88,7 @@ public class PlayerMediaSession implements MediaSessionCallback { @Override public void play() { player.play(); + player.hideControls(0, 0); } @Override From 29348411520f56f07865c6deef722ae107b9a205 Mon Sep 17 00:00:00 2001 From: ktprograms Date: Wed, 3 Nov 2021 08:26:13 +0800 Subject: [PATCH 002/107] Enable play/pause with space key even when not in fullscreen player --- app/src/main/java/org/schabi/newpipe/player/Player.java | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/app/src/main/java/org/schabi/newpipe/player/Player.java b/app/src/main/java/org/schabi/newpipe/player/Player.java index 4e2a9d065..2a88445bb 100644 --- a/app/src/main/java/org/schabi/newpipe/player/Player.java +++ b/app/src/main/java/org/schabi/newpipe/player/Player.java @@ -3752,11 +3752,9 @@ public final class Player implements default: break; case KeyEvent.KEYCODE_SPACE: - if (isFullscreen) { - playPause(); - if (isPlaying()) { - hideControls(0, 0); - } + playPause(); + if (isPlaying()) { + hideControls(0, 0); } break; case KeyEvent.KEYCODE_BACK: From 55146163729dcb45f5d8d4277b41c75425d096de Mon Sep 17 00:00:00 2001 From: martin Date: Tue, 21 Dec 2021 18:17:48 +0100 Subject: [PATCH 003/107] Change pitch by semitones --- .../helper/PlaybackParameterDialog.java | 243 +++++++++++++++++- .../res/layout/dialog_playback_parameter.xml | 125 ++++++++- app/src/main/res/values/settings_keys.xml | 1 + app/src/main/res/values/strings.xml | 2 + 4 files changed, 360 insertions(+), 11 deletions(-) diff --git a/app/src/main/java/org/schabi/newpipe/player/helper/PlaybackParameterDialog.java b/app/src/main/java/org/schabi/newpipe/player/helper/PlaybackParameterDialog.java index 5139ef9cd..222a95afb 100644 --- a/app/src/main/java/org/schabi/newpipe/player/helper/PlaybackParameterDialog.java +++ b/app/src/main/java/org/schabi/newpipe/player/helper/PlaybackParameterDialog.java @@ -9,6 +9,7 @@ import android.os.Bundle; import android.util.Log; import android.view.View; import android.widget.CheckBox; +import android.widget.RelativeLayout; import android.widget.SeekBar; import android.widget.TextView; @@ -37,6 +38,7 @@ public class PlaybackParameterDialog extends DialogFragment { private static final double DEFAULT_TEMPO = 1.00f; private static final double DEFAULT_PITCH = 1.00f; + private static final int DEFAULT_SEMITONES = 0; private static final double DEFAULT_STEP = STEP_TWENTY_FIVE_PERCENT_VALUE; private static final boolean DEFAULT_SKIP_SILENCE = false; @@ -64,9 +66,11 @@ public class PlaybackParameterDialog extends DialogFragment { private double initialTempo = DEFAULT_TEMPO; private double initialPitch = DEFAULT_PITCH; + private int initialSemitones = DEFAULT_SEMITONES; private boolean initialSkipSilence = DEFAULT_SKIP_SILENCE; private double tempo = DEFAULT_TEMPO; private double pitch = DEFAULT_PITCH; + private int semitones = DEFAULT_SEMITONES; private double stepSize = DEFAULT_STEP; @Nullable @@ -78,6 +82,8 @@ public class PlaybackParameterDialog extends DialogFragment { @Nullable private TextView tempoStepUpText; @Nullable + private RelativeLayout pitchControl; + @Nullable private SeekBar pitchSlider; @Nullable private TextView pitchCurrentText; @@ -86,9 +92,23 @@ public class PlaybackParameterDialog extends DialogFragment { @Nullable private TextView pitchStepUpText; @Nullable + private RelativeLayout semitoneControl; + @Nullable + private SeekBar semitoneSlider; + @Nullable + private TextView semitoneCurrentText; + @Nullable + private TextView semitoneStepDownText; + @Nullable + private TextView semitoneStepUpText; + @Nullable + private View separatorStepSizeSelector; + @Nullable private CheckBox unhookingCheckbox; @Nullable private CheckBox skipSilenceCheckbox; + @Nullable + private CheckBox adjustBySemitonesCheckbox; public static PlaybackParameterDialog newInstance(final double playbackTempo, final double playbackPitch, @@ -101,6 +121,7 @@ public class PlaybackParameterDialog extends DialogFragment { dialog.tempo = playbackTempo; dialog.pitch = playbackPitch; + dialog.semitones = dialog.percentToSemitones(playbackPitch); dialog.initialSkipSilence = playbackSkipSilence; return dialog; @@ -127,9 +148,11 @@ public class PlaybackParameterDialog extends DialogFragment { if (savedInstanceState != null) { initialTempo = savedInstanceState.getDouble(INITIAL_TEMPO_KEY, DEFAULT_TEMPO); initialPitch = savedInstanceState.getDouble(INITIAL_PITCH_KEY, DEFAULT_PITCH); + initialSemitones = percentToSemitones(initialPitch); tempo = savedInstanceState.getDouble(TEMPO_KEY, DEFAULT_TEMPO); pitch = savedInstanceState.getDouble(PITCH_KEY, DEFAULT_PITCH); + semitones = percentToSemitones(pitch); stepSize = savedInstanceState.getDouble(STEP_SIZE_KEY, DEFAULT_STEP); } } @@ -160,9 +183,11 @@ public class PlaybackParameterDialog extends DialogFragment { .setView(view) .setCancelable(true) .setNegativeButton(R.string.cancel, (dialogInterface, i) -> - setPlaybackParameters(initialTempo, initialPitch, initialSkipSilence)) + setPlaybackParameters(initialTempo, initialPitch, + initialSemitones, initialSkipSilence)) .setNeutralButton(R.string.playback_reset, (dialogInterface, i) -> - setPlaybackParameters(DEFAULT_TEMPO, DEFAULT_PITCH, DEFAULT_SKIP_SILENCE)) + setPlaybackParameters(DEFAULT_TEMPO, DEFAULT_PITCH, + DEFAULT_SEMITONES, DEFAULT_SKIP_SILENCE)) .setPositiveButton(R.string.ok, (dialogInterface, i) -> setCurrentPlaybackParameters()); @@ -176,12 +201,47 @@ public class PlaybackParameterDialog extends DialogFragment { private void setupControlViews(@NonNull final View rootView) { setupHookingControl(rootView); setupSkipSilenceControl(rootView); + setupAdjustBySemitonesControl(rootView); setupTempoControl(rootView); setupPitchControl(rootView); + setupSemitoneControl(rootView); + + togglePitchSliderType(rootView); setStepSize(stepSize); - setupStepSizeSelector(rootView); + } + + private void togglePitchSliderType(@NonNull final View rootView) { + + pitchControl = rootView.findViewById(R.id.pitchControl); + semitoneControl = rootView.findViewById(R.id.semitoneControl); + + separatorStepSizeSelector = rootView.findViewById(R.id.separatorStepSizeSelector); + final RelativeLayout.LayoutParams params = + (RelativeLayout.LayoutParams) separatorStepSizeSelector.getLayoutParams(); + if (pitchControl != null && semitoneControl != null && unhookingCheckbox != null) { + if (getCurrentAdjustBySemitones()) { + // replaces pitchControl slider with semitoneControl slider + pitchControl.setVisibility(View.GONE); + semitoneControl.setVisibility(View.VISIBLE); + params.addRule(RelativeLayout.BELOW, R.id.semitoneControl); + + // forces unhook for semitones + unhookingCheckbox.setChecked(true); + unhookingCheckbox.setEnabled(false); + + setupTempoStepSizeSelector(rootView); + } else { + semitoneControl.setVisibility(View.GONE); + pitchControl.setVisibility(View.VISIBLE); + params.addRule(RelativeLayout.BELOW, R.id.pitchControl); + + // (re)enables hooking selection + unhookingCheckbox.setEnabled(true); + setupCombinedStepSizeSelector(rootView); + } + } } private void setupTempoControl(@NonNull final View rootView) { @@ -234,23 +294,40 @@ public class PlaybackParameterDialog extends DialogFragment { } } + private void setupSemitoneControl(@NonNull final View rootView) { + semitoneSlider = rootView.findViewById(R.id.semitoneSeekbar); + semitoneCurrentText = rootView.findViewById(R.id.semitoneCurrentText); + semitoneStepDownText = rootView.findViewById(R.id.semitoneStepDown); + semitoneStepUpText = rootView.findViewById(R.id.semitoneStepUp); + + if (semitoneCurrentText != null) { + semitoneCurrentText.setText(getSignedSemitonesString(semitones)); + } + + if (semitoneSlider != null) { + setSemitoneSlider(semitones); + semitoneSlider.setOnSeekBarChangeListener(getOnSemitoneChangedListener()); + } + + } + private void setupHookingControl(@NonNull final View rootView) { unhookingCheckbox = rootView.findViewById(R.id.unhookCheckbox); if (unhookingCheckbox != null) { - // restore whether pitch and tempo are unhooked or not + // restores whether pitch and tempo are unhooked or not unhookingCheckbox.setChecked(PreferenceManager .getDefaultSharedPreferences(requireContext()) .getBoolean(getString(R.string.playback_unhook_key), true)); unhookingCheckbox.setOnCheckedChangeListener((compoundButton, isChecked) -> { - // save whether pitch and tempo are unhooked or not + // saves whether pitch and tempo are unhooked or not PreferenceManager.getDefaultSharedPreferences(requireContext()) .edit() .putBoolean(getString(R.string.playback_unhook_key), isChecked) .apply(); if (!isChecked) { - // when unchecked, slide back to the minimum of current tempo or pitch + // when unchecked, slides back to the minimum of current tempo or pitch final double minimum = Math.min(getCurrentPitch(), getCurrentTempo()); setSliders(minimum); setCurrentPlaybackParameters(); @@ -268,6 +345,42 @@ public class PlaybackParameterDialog extends DialogFragment { } } + private void setupAdjustBySemitonesControl(@NonNull final View rootView) { + adjustBySemitonesCheckbox = rootView.findViewById(R.id.adjustBySemitonesCheckbox); + if (adjustBySemitonesCheckbox != null) { + // restores whether semitone adjustment is used or not + adjustBySemitonesCheckbox.setChecked(PreferenceManager + .getDefaultSharedPreferences(requireContext()) + .getBoolean(getString(R.string.playback_adjust_by_semitones_key), true)); + + // stores whether semitone adjustment is used or not + adjustBySemitonesCheckbox.setOnCheckedChangeListener((compoundButton, isChecked) -> { + PreferenceManager.getDefaultSharedPreferences(requireContext()) + .edit() + .putBoolean(getString(R.string.playback_adjust_by_semitones_key), isChecked) + .apply(); + togglePitchSliderType(rootView); + if (isChecked) { + setPlaybackParameters( + getCurrentTempo(), + getCurrentPitch(), + percentToSemitones(getCurrentPitch()), + getCurrentSkipSilence() + ); + setSemitoneSlider(percentToSemitones(getCurrentPitch())); + } else { + setPlaybackParameters( + getCurrentTempo(), + semitonesToPercent(getCurrentSemitones()), + getCurrentSemitones(), + getCurrentSkipSilence() + ); + setPitchSlider(semitonesToPercent(getCurrentSemitones())); + } + }); + } + } + private void setupStepSizeSelector(@NonNull final View rootView) { final TextView stepSizeOnePercentText = rootView.findViewById(R.id.stepSizeOnePercent); final TextView stepSizeFivePercentText = rootView.findViewById(R.id.stepSizeFivePercent); @@ -310,6 +423,22 @@ public class PlaybackParameterDialog extends DialogFragment { } } + private void setupTempoStepSizeSelector(@NonNull final View rootView) { + final TextView playbackStepTypeText = rootView.findViewById(R.id.playback_step_type); + if (playbackStepTypeText != null) { + playbackStepTypeText.setText(R.string.playback_tempo_step); + } + setupStepSizeSelector(rootView); + } + + private void setupCombinedStepSizeSelector(@NonNull final View rootView) { + final TextView playbackStepTypeText = rootView.findViewById(R.id.playback_step_type); + if (playbackStepTypeText != null) { + playbackStepTypeText.setText(R.string.playback_step); + } + setupStepSizeSelector(rootView); + } + private void setStepSize(final double stepSize) { this.stepSize = stepSize; @@ -344,6 +473,20 @@ public class PlaybackParameterDialog extends DialogFragment { setCurrentPlaybackParameters(); }); } + + if (semitoneStepDownText != null) { + semitoneStepDownText.setOnClickListener(view -> { + onSemitoneSliderUpdated(getCurrentSemitones() - 1); + setCurrentPlaybackParameters(); + }); + } + + if (semitoneStepUpText != null) { + semitoneStepUpText.setOnClickListener(view -> { + onSemitoneSliderUpdated(getCurrentSemitones() + 1); + setCurrentPlaybackParameters(); + }); + } } /*////////////////////////////////////////////////////////////////////////// @@ -398,6 +541,33 @@ public class PlaybackParameterDialog extends DialogFragment { }; } + private SeekBar.OnSeekBarChangeListener getOnSemitoneChangedListener() { + return new SeekBar.OnSeekBarChangeListener() { + @Override + public void onProgressChanged(final SeekBar seekBar, final int progress, + final boolean fromUser) { + // semitone slider supplies values 0 to 25, subtraction by 12 is required + final int currentSemitones = progress - 12; + if (fromUser) { // this change is first in chain + onSemitoneSliderUpdated(currentSemitones); + // line below also saves semitones as pitch percentages + onPitchSliderUpdated(semitonesToPercent(currentSemitones)); + setCurrentPlaybackParameters(); + } + } + + @Override + public void onStartTrackingTouch(final SeekBar seekBar) { + // Do Nothing. + } + + @Override + public void onStopTrackingTouch(final SeekBar seekBar) { + // Do Nothing. + } + }; + } + private void onTempoSliderUpdated(final double newTempo) { if (unhookingCheckbox == null) { return; @@ -420,6 +590,13 @@ public class PlaybackParameterDialog extends DialogFragment { } } + private void onSemitoneSliderUpdated(final int newSemitone) { + if (unhookingCheckbox == null) { + return; + } + setSemitoneSlider(newSemitone); + } + private void setSliders(final double newValue) { setTempoSlider(newValue); setPitchSlider(newValue); @@ -439,25 +616,49 @@ public class PlaybackParameterDialog extends DialogFragment { pitchSlider.setProgress(strategy.progressOf(newPitch)); } + private void setSemitoneSlider(final int newSemitone) { + if (semitoneSlider == null) { + return; + } + semitoneSlider.setProgress(newSemitone + 12); + } + /*////////////////////////////////////////////////////////////////////////// // Helper //////////////////////////////////////////////////////////////////////////*/ private void setCurrentPlaybackParameters() { - setPlaybackParameters(getCurrentTempo(), getCurrentPitch(), getCurrentSkipSilence()); + if (getCurrentAdjustBySemitones()) { + setPlaybackParameters( + getCurrentTempo(), + semitonesToPercent(getCurrentSemitones()), + getCurrentSemitones(), + getCurrentSkipSilence() + ); + } else { + setPlaybackParameters( + getCurrentTempo(), + getCurrentPitch(), + percentToSemitones(getCurrentPitch()), + getCurrentSkipSilence() + ); + } } private void setPlaybackParameters(final double newTempo, final double newPitch, - final boolean skipSilence) { - if (callback != null && tempoCurrentText != null && pitchCurrentText != null) { + final int newSemitones, final boolean skipSilence) { + if (callback != null && tempoCurrentText != null + && pitchCurrentText != null && semitoneCurrentText != null) { if (DEBUG) { Log.d(TAG, "Setting playback parameters to " + "tempo=[" + newTempo + "], " - + "pitch=[" + newPitch + "]"); + + "pitch=[" + newPitch + "], " + + "semitones=[" + newSemitones + "]"); } tempoCurrentText.setText(PlayerHelper.formatSpeed(newTempo)); pitchCurrentText.setText(PlayerHelper.formatPitch(newPitch)); + semitoneCurrentText.setText(getSignedSemitonesString(newSemitones)); callback.onPlaybackParameterChanged((float) newTempo, (float) newPitch, skipSilence); } } @@ -470,6 +671,11 @@ public class PlaybackParameterDialog extends DialogFragment { return pitchSlider == null ? pitch : strategy.valueOf(pitchSlider.getProgress()); } + private int getCurrentSemitones() { + // semitoneSlider is absolute, that's why - 12 + return semitoneSlider == null ? semitones : semitoneSlider.getProgress() - 12; + } + private double getCurrentStepSize() { return stepSize; } @@ -478,6 +684,10 @@ public class PlaybackParameterDialog extends DialogFragment { return skipSilenceCheckbox != null && skipSilenceCheckbox.isChecked(); } + private boolean getCurrentAdjustBySemitones() { + return adjustBySemitonesCheckbox != null && adjustBySemitonesCheckbox.isChecked(); + } + @NonNull private static String getStepUpPercentString(final double percent) { return STEP_UP_SIGN + getPercentString(percent); @@ -493,8 +703,21 @@ public class PlaybackParameterDialog extends DialogFragment { return PlayerHelper.formatPitch(percent); } + @NonNull + private static String getSignedSemitonesString(final int semitones) { + return semitones > 0 ? "+" + semitones : "" + semitones; + } + public interface Callback { void onPlaybackParameterChanged(float playbackTempo, float playbackPitch, boolean playbackSkipSilence); } + + public double semitonesToPercent(final int inSemitones) { + return Math.pow(2, inSemitones / 12.0); + } + + public int percentToSemitones(final double inPercent) { + return (int) Math.round(12 * Math.log(inPercent) / Math.log(2)); + } } diff --git a/app/src/main/res/layout/dialog_playback_parameter.xml b/app/src/main/res/layout/dialog_playback_parameter.xml index 40db90675..fd63cbd79 100644 --- a/app/src/main/res/layout/dialog_playback_parameter.xml +++ b/app/src/main/res/layout/dialog_playback_parameter.xml @@ -261,11 +261,121 @@ tools:text="+5%" /> + + + + + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/values/settings_keys.xml b/app/src/main/res/values/settings_keys.xml index b6f76fce2..a0f03a5b6 100644 --- a/app/src/main/res/values/settings_keys.xml +++ b/app/src/main/res/values/settings_keys.xml @@ -258,6 +258,7 @@ enable_playback_resume enable_playback_state_lists playback_unhook_key + playback_adjust_by_semitones_key playback_speed_key playback_pitch_key playback_skip_silence_key diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index b4d46a3e2..0f297006e 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -503,7 +503,9 @@ Pitch Unhook (may cause distortion) Fast-forward during silence + Adjust pitch by musical semitones Step + Tempo step Reset In order to comply with the European General Data Protection Regulation (GDPR), we herby draw your attention to NewPipe\'s privacy policy. Please read it carefully.\nYou must accept it to send us the bug report. From 3a9cdb28ab460949156d78ec477c81e24d65d87c Mon Sep 17 00:00:00 2001 From: Atemu Date: Thu, 3 Feb 2022 13:58:17 +0100 Subject: [PATCH 004/107] app/build.grade: compileSdk 30 -> 31 Required for newer versions of some dependencies --- app/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/build.gradle b/app/build.gradle index 4cebcb7f5..3e476441f 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -8,7 +8,7 @@ plugins { } android { - compileSdk 30 + compileSdk 31 buildToolsVersion '30.0.3' defaultConfig { From 67b2503062277da11fef1450743e8807c6bf4541 Mon Sep 17 00:00:00 2001 From: Atemu Date: Thu, 3 Feb 2022 13:58:40 +0100 Subject: [PATCH 005/107] app/build.grade: androidxRoomVersion 2.3.0 -> 2.4.1 This version of Room includes a fix for building dependant apps such as NewPipe on Apple Silicon devices (aarch64-darwin) --- app/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/build.gradle b/app/build.gradle index 3e476441f..bb4e547a1 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -101,7 +101,7 @@ ext { checkstyleVersion = '9.2.1' androidxLifecycleVersion = '2.3.1' - androidxRoomVersion = '2.3.0' + androidxRoomVersion = '2.4.1' icepickVersion = '3.2.0' exoPlayerVersion = '2.14.2' From 4049abf2c05f3e41202c863f8ea7f978bd45a458 Mon Sep 17 00:00:00 2001 From: martin Date: Fri, 4 Feb 2022 16:15:55 +0100 Subject: [PATCH 006/107] Addressed comment in PR --- .../helper/PlaybackParameterDialog.java | 35 ++++++-------- .../res/layout/dialog_playback_parameter.xml | 46 ++++++------------- 2 files changed, 28 insertions(+), 53 deletions(-) diff --git a/app/src/main/java/org/schabi/newpipe/player/helper/PlaybackParameterDialog.java b/app/src/main/java/org/schabi/newpipe/player/helper/PlaybackParameterDialog.java index 222a95afb..74604eda0 100644 --- a/app/src/main/java/org/schabi/newpipe/player/helper/PlaybackParameterDialog.java +++ b/app/src/main/java/org/schabi/newpipe/player/helper/PlaybackParameterDialog.java @@ -82,8 +82,6 @@ public class PlaybackParameterDialog extends DialogFragment { @Nullable private TextView tempoStepUpText; @Nullable - private RelativeLayout pitchControl; - @Nullable private SeekBar pitchSlider; @Nullable private TextView pitchCurrentText; @@ -92,8 +90,6 @@ public class PlaybackParameterDialog extends DialogFragment { @Nullable private TextView pitchStepUpText; @Nullable - private RelativeLayout semitoneControl; - @Nullable private SeekBar semitoneSlider; @Nullable private TextView semitoneCurrentText; @@ -102,8 +98,6 @@ public class PlaybackParameterDialog extends DialogFragment { @Nullable private TextView semitoneStepUpText; @Nullable - private View separatorStepSizeSelector; - @Nullable private CheckBox unhookingCheckbox; @Nullable private CheckBox skipSilenceCheckbox; @@ -213,11 +207,13 @@ public class PlaybackParameterDialog extends DialogFragment { } private void togglePitchSliderType(@NonNull final View rootView) { + @Nullable + final RelativeLayout pitchControl = rootView.findViewById(R.id.pitchControl); + @Nullable + final RelativeLayout semitoneControl = rootView.findViewById(R.id.semitoneControl); - pitchControl = rootView.findViewById(R.id.pitchControl); - semitoneControl = rootView.findViewById(R.id.semitoneControl); - - separatorStepSizeSelector = rootView.findViewById(R.id.separatorStepSizeSelector); + @Nullable + final View separatorStepSizeSelector = rootView.findViewById(R.id.separatorStepSizeSelector); final RelativeLayout.LayoutParams params = (RelativeLayout.LayoutParams) separatorStepSizeSelector.getLayoutParams(); if (pitchControl != null && semitoneControl != null && unhookingCheckbox != null) { @@ -364,10 +360,14 @@ public class PlaybackParameterDialog extends DialogFragment { setPlaybackParameters( getCurrentTempo(), getCurrentPitch(), - percentToSemitones(getCurrentPitch()), + Integer.min(12, + Integer.max(-12, percentToSemitones(getCurrentPitch()) + )), getCurrentSkipSilence() ); - setSemitoneSlider(percentToSemitones(getCurrentPitch())); + setSemitoneSlider(Integer.min(12, + Integer.max(-12, percentToSemitones(getCurrentPitch())) + )); } else { setPlaybackParameters( getCurrentTempo(), @@ -546,7 +546,7 @@ public class PlaybackParameterDialog extends DialogFragment { @Override public void onProgressChanged(final SeekBar seekBar, final int progress, final boolean fromUser) { - // semitone slider supplies values 0 to 25, subtraction by 12 is required + // semitone slider supplies values 0 to 24, subtraction by 12 is required final int currentSemitones = progress - 12; if (fromUser) { // this change is first in chain onSemitoneSliderUpdated(currentSemitones); @@ -569,9 +569,6 @@ public class PlaybackParameterDialog extends DialogFragment { } private void onTempoSliderUpdated(final double newTempo) { - if (unhookingCheckbox == null) { - return; - } if (!unhookingCheckbox.isChecked()) { setSliders(newTempo); } else { @@ -580,9 +577,6 @@ public class PlaybackParameterDialog extends DialogFragment { } private void onPitchSliderUpdated(final double newPitch) { - if (unhookingCheckbox == null) { - return; - } if (!unhookingCheckbox.isChecked()) { setSliders(newPitch); } else { @@ -591,9 +585,6 @@ public class PlaybackParameterDialog extends DialogFragment { } private void onSemitoneSliderUpdated(final int newSemitone) { - if (unhookingCheckbox == null) { - return; - } setSemitoneSlider(newSemitone); } diff --git a/app/src/main/res/layout/dialog_playback_parameter.xml b/app/src/main/res/layout/dialog_playback_parameter.xml index fd63cbd79..27ff0b2a3 100644 --- a/app/src/main/res/layout/dialog_playback_parameter.xml +++ b/app/src/main/res/layout/dialog_playback_parameter.xml @@ -48,8 +48,7 @@ android:text="--%" android:textColor="?attr/colorAccent" android:textStyle="bold" - tools:ignore="HardcodedText" - tools:text="-5%" /> + tools:ignore="HardcodedText"/> + tools:ignore="HardcodedText"/> + tools:ignore="HardcodedText"/> + tools:ignore="HardcodedText"/> + tools:ignore="HardcodedText"/> + tools:ignore="HardcodedText"/> + tools:ignore="HardcodedText"/> + tools:ignore="HardcodedText"/> + tools:ignore="HardcodedText"/> + tools:ignore="HardcodedText"/> + tools:ignore="HardcodedText"/> + tools:ignore="HardcodedText"/> + tools:ignore="HardcodedText"/> + tools:ignore="HardcodedText"/> + tools:ignore="HardcodedText"/> Date: Sat, 5 Feb 2022 12:31:07 +0100 Subject: [PATCH 007/107] Fixed checkstyle violation --- .../newpipe/player/helper/PlaybackParameterDialog.java | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/app/src/main/java/org/schabi/newpipe/player/helper/PlaybackParameterDialog.java b/app/src/main/java/org/schabi/newpipe/player/helper/PlaybackParameterDialog.java index 74604eda0..0fe500965 100644 --- a/app/src/main/java/org/schabi/newpipe/player/helper/PlaybackParameterDialog.java +++ b/app/src/main/java/org/schabi/newpipe/player/helper/PlaybackParameterDialog.java @@ -207,13 +207,11 @@ public class PlaybackParameterDialog extends DialogFragment { } private void togglePitchSliderType(@NonNull final View rootView) { - @Nullable final RelativeLayout pitchControl = rootView.findViewById(R.id.pitchControl); - @Nullable final RelativeLayout semitoneControl = rootView.findViewById(R.id.semitoneControl); - @Nullable - final View separatorStepSizeSelector = rootView.findViewById(R.id.separatorStepSizeSelector); + final View separatorStepSizeSelector = + rootView.findViewById(R.id.separatorStepSizeSelector); final RelativeLayout.LayoutParams params = (RelativeLayout.LayoutParams) separatorStepSizeSelector.getLayoutParams(); if (pitchControl != null && semitoneControl != null && unhookingCheckbox != null) { From ed2967ec7d418c186841a1db41a8304c143160e0 Mon Sep 17 00:00:00 2001 From: martin Date: Thu, 17 Feb 2022 10:28:50 +0100 Subject: [PATCH 008/107] Addressing layout comments --- .../res/layout/dialog_playback_parameter.xml | 38 ++++++++++++------- 1 file changed, 24 insertions(+), 14 deletions(-) diff --git a/app/src/main/res/layout/dialog_playback_parameter.xml b/app/src/main/res/layout/dialog_playback_parameter.xml index 27ff0b2a3..862b2ea67 100644 --- a/app/src/main/res/layout/dialog_playback_parameter.xml +++ b/app/src/main/res/layout/dialog_playback_parameter.xml @@ -48,7 +48,8 @@ android:text="--%" android:textColor="?attr/colorAccent" android:textStyle="bold" - tools:ignore="HardcodedText"/> + tools:ignore="HardcodedText" + tools:text="-5%" /> + tools:ignore="HardcodedText" + tools:text="1.00x" /> + tools:ignore="HardcodedText" + tools:text="100%" /> + tools:ignore="HardcodedText" + tools:text="300%" /> + tools:ignore="HardcodedText" + tools:text="+5%" /> + tools:ignore="HardcodedText" + tools:text="-5%" /> + tools:ignore="HardcodedText" + tools:text="25%" /> + tools:ignore="HardcodedText" + tools:text="100%" /> + tools:ignore="HardcodedText" + tools:text="300%" /> + tools:ignore="HardcodedText" + tools:text="+5%" /> + tools:ignore="HardcodedText" /> + tools:text="0" /> + tools:ignore="HardcodedText" /> + tools:ignore="HardcodedText" /> Date: Sat, 15 Jan 2022 17:05:40 +0100 Subject: [PATCH 009/107] Load enough initial data into BaseListFragment --- .../fragments/list/BaseListFragment.java | 63 ++++++++++++++++++- .../fragments/list/BaseListInfoFragment.java | 19 ++++-- .../fragments/list/search/SearchFragment.java | 14 ++++- 3 files changed, 85 insertions(+), 11 deletions(-) diff --git a/app/src/main/java/org/schabi/newpipe/fragments/list/BaseListFragment.java b/app/src/main/java/org/schabi/newpipe/fragments/list/BaseListFragment.java index 3c2e65bb7..d0a07ffc4 100644 --- a/app/src/main/java/org/schabi/newpipe/fragments/list/BaseListFragment.java +++ b/app/src/main/java/org/schabi/newpipe/fragments/list/BaseListFragment.java @@ -271,7 +271,7 @@ public abstract class BaseListFragment extends BaseStateFragment @Override protected void initListeners() { super.initListeners(); - infoListAdapter.setOnStreamSelectedListener(new OnClickGesture() { + infoListAdapter.setOnStreamSelectedListener(new OnClickGesture<>() { @Override public void selected(final StreamInfoItem selectedItem) { onStreamSelected(selectedItem); @@ -418,7 +418,66 @@ public abstract class BaseListFragment extends BaseStateFragment // Load and handle //////////////////////////////////////////////////////////////////////////*/ - protected abstract void loadMoreItems(); + /** + * If more items are loadable and the itemList is not scrollable -> load more data. + *
+ * Should be called once the initial items inside {@link #startLoading(boolean)} + * has been loaded and added to the {@link #itemsList}. + *
+ * Otherwise the loading indicator is always shown but no data can be loaded + * because the view is not scrollable; see also #1974. + */ + protected void ifMoreItemsLoadableLoadUntilScrollable() { + ifMoreItemsLoadableLoadUntilScrollable(0); + } + + /** + * If more items are loadable and the itemList is not scrollable -> load more data. + * + * @param recursiveCallCount Amount of recursive calls that occurred + * @see #ifMoreItemsLoadableLoadUntilScrollable() + */ + protected void ifMoreItemsLoadableLoadUntilScrollable(final int recursiveCallCount) { + // Try to prevent malfunction / stackoverflow + if (recursiveCallCount > 100) { + Log.w(TAG, "loadEnoughInitialData - Too many recursive calls - Aborting"); + return; + } + if (!hasMoreItems()) { + if (DEBUG) { + Log.d(TAG, "loadEnoughInitialData - OK: No more items to load"); + } + return; + } + if (itemsList.canScrollVertically(1) + || itemsList.canScrollVertically(-1)) { + if (DEBUG) { + Log.d(TAG, "loadEnoughInitial - OK: itemList is scrollable"); + } + return; + } + if (DEBUG) { + Log.d(TAG, "loadEnoughInitialData - View is not scrollable " + + "but it could load more items -> Loading more"); + } + loadMoreItems(() -> + ifMoreItemsLoadableLoadUntilScrollable(recursiveCallCount + 1)); + } + + /** + * Loads more items. + * @param initialDataLoadCallback + * Callback used in {@link #ifMoreItemsLoadableLoadUntilScrollable()}. + *
+ * Execute it once the data was loaded and added to the {@link #itemsList}. + *
+ * Might be null. + */ + protected abstract void loadMoreItems(@Nullable Runnable initialDataLoadCallback); + + protected void loadMoreItems() { + loadMoreItems(null); + } protected abstract boolean hasMoreItems(); diff --git a/app/src/main/java/org/schabi/newpipe/fragments/list/BaseListInfoFragment.java b/app/src/main/java/org/schabi/newpipe/fragments/list/BaseListInfoFragment.java index e98dc9fda..87f031c12 100644 --- a/app/src/main/java/org/schabi/newpipe/fragments/list/BaseListInfoFragment.java +++ b/app/src/main/java/org/schabi/newpipe/fragments/list/BaseListInfoFragment.java @@ -6,6 +6,7 @@ import android.util.Log; import android.view.View; import androidx.annotation.NonNull; +import androidx.annotation.Nullable; import org.schabi.newpipe.error.ErrorInfo; import org.schabi.newpipe.error.UserAction; @@ -65,7 +66,7 @@ public abstract class BaseListInfoFragment super.onResume(); // Check if it was loading when the fragment was stopped/paused, if (wasLoading.getAndSet(false)) { - if (hasMoreItems() && infoListAdapter.getItemsList().size() > 0) { + if (hasMoreItems() && !infoListAdapter.getItemsList().isEmpty()) { loadMoreItems(); } else { doInitialLoadLogic(); @@ -105,6 +106,7 @@ public abstract class BaseListInfoFragment // Load and handle //////////////////////////////////////////////////////////////////////////*/ + @Override protected void doInitialLoadLogic() { if (DEBUG) { Log.d(TAG, "doInitialLoadLogic() called"); @@ -144,6 +146,7 @@ public abstract class BaseListInfoFragment currentInfo = result; currentNextPage = result.getNextPage(); handleResult(result); + ifMoreItemsLoadableLoadUntilScrollable(); }, throwable -> showError(new ErrorInfo(throwable, errorUserAction, "Start loading: " + url, serviceId))); @@ -158,7 +161,8 @@ public abstract class BaseListInfoFragment */ protected abstract Single loadMoreItemsLogic(); - protected void loadMoreItems() { + @Override + protected void loadMoreItems(@Nullable final Runnable initialDataLoadCallback) { isLoading.set(true); if (currentWorker != null) { @@ -171,9 +175,12 @@ public abstract class BaseListInfoFragment .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) .doFinally(this::allowDownwardFocusScroll) - .subscribe((@NonNull ListExtractor.InfoItemsPage InfoItemsPage) -> { + .subscribe(infoItemsPage -> { isLoading.set(false); - handleNextItems(InfoItemsPage); + handleNextItems(infoItemsPage); + if (initialDataLoadCallback != null) { + initialDataLoadCallback.run(); + } }, (@NonNull Throwable throwable) -> dynamicallyShowErrorPanelOrSnackbar(new ErrorInfo(throwable, errorUserAction, "Loading more items: " + url, serviceId))); @@ -223,7 +230,7 @@ public abstract class BaseListInfoFragment setTitle(name); if (infoListAdapter.getItemsList().isEmpty()) { - if (result.getRelatedItems().size() > 0) { + if (!result.getRelatedItems().isEmpty()) { infoListAdapter.addInfoItemList(result.getRelatedItems()); showListFooter(hasMoreItems()); } else { @@ -240,7 +247,7 @@ public abstract class BaseListInfoFragment final List errors = new ArrayList<>(result.getErrors()); // handling ContentNotSupportedException not to show the error but an appropriate string // so that crashes won't be sent uselessly and the user will understand what happened - errors.removeIf(throwable -> throwable instanceof ContentNotSupportedException); + errors.removeIf(ContentNotSupportedException.class::isInstance); if (!errors.isEmpty()) { dynamicallyShowErrorPanelOrSnackbar(new ErrorInfo(result.getErrors(), diff --git a/app/src/main/java/org/schabi/newpipe/fragments/list/search/SearchFragment.java b/app/src/main/java/org/schabi/newpipe/fragments/list/search/SearchFragment.java index 055c27733..35bb2c349 100644 --- a/app/src/main/java/org/schabi/newpipe/fragments/list/search/SearchFragment.java +++ b/app/src/main/java/org/schabi/newpipe/fragments/list/search/SearchFragment.java @@ -868,12 +868,15 @@ public class SearchFragment extends BaseListFragment isLoading.set(false)) - .subscribe(this::handleResult, this::onItemError); + .subscribe(result -> { + handleResult(result); + ifMoreItemsLoadableLoadUntilScrollable(); + }, this::onItemError); } @Override - protected void loadMoreItems() { + protected void loadMoreItems(@Nullable final Runnable initialDataLoadCallback) { if (!Page.isValid(nextPage)) { return; } @@ -891,7 +894,12 @@ public class SearchFragment extends BaseListFragment isLoading.set(false)) - .subscribe(this::handleNextItems, this::onItemError); + .subscribe(itemsPage -> { + handleNextItems(itemsPage); + if (initialDataLoadCallback != null) { + initialDataLoadCallback.run(); + } + }, this::onItemError); } @Override From 2c51a7970d0bf3174a25b0c4cde03a4878e7cefa Mon Sep 17 00:00:00 2001 From: litetex <40789489+litetex@users.noreply.github.com> Date: Sat, 15 Jan 2022 17:28:03 +0100 Subject: [PATCH 010/107] Improved InfoListAdapter * Removed unused code * Cleaned it up * Made code more readable --- .../newpipe/info_list/InfoListAdapter.java | 70 ++++--------------- 1 file changed, 12 insertions(+), 58 deletions(-) diff --git a/app/src/main/java/org/schabi/newpipe/info_list/InfoListAdapter.java b/app/src/main/java/org/schabi/newpipe/info_list/InfoListAdapter.java index 56bc63384..410a65449 100644 --- a/app/src/main/java/org/schabi/newpipe/info_list/InfoListAdapter.java +++ b/app/src/main/java/org/schabi/newpipe/info_list/InfoListAdapter.java @@ -145,43 +145,6 @@ public class InfoListAdapter extends RecyclerView.Adapter data) { - infoItemList.clear(); - infoItemList.addAll(data); - notifyDataSetChanged(); - } - - public void addInfoItem(@Nullable final InfoItem data) { - if (data == null) { - return; - } - if (DEBUG) { - Log.d(TAG, "addInfoItem() before > infoItemList.size() = " - + infoItemList.size() + ", thread = " + Thread.currentThread()); - } - - final int positionInserted = sizeConsideringHeaderOffset(); - infoItemList.add(data); - - if (DEBUG) { - Log.d(TAG, "addInfoItem() after > position = " + positionInserted + ", " - + "infoItemList.size() = " + infoItemList.size() + ", " - + "header = " + header + ", footer = " + footer + ", " - + "showFooter = " + showFooter); - } - notifyItemInserted(positionInserted); - - if (footer != null && showFooter) { - final int footerNow = sizeConsideringHeaderOffset(); - notifyItemMoved(positionInserted, footerNow); - - if (DEBUG) { - Log.d(TAG, "addInfoItem() footer from " + positionInserted - + " to " + footerNow); - } - } - } - public void clearStreamItemList() { if (infoItemList.isEmpty()) { return; @@ -226,7 +189,7 @@ public class InfoListAdapter extends RecyclerView.Adapter getItemsList() { + public List getItemsList() { return infoItemList; } @@ -335,29 +298,23 @@ public class InfoListAdapter extends RecyclerView.Adapter payloads) { - if (!payloads.isEmpty() && holder instanceof InfoItemHolder) { - for (final Object payload : payloads) { - if (payload instanceof StreamStateEntity) { - ((InfoItemHolder) holder).updateState(infoItemList - .get(header == null ? position : position - 1), recordManager); - } else if (payload instanceof Boolean) { - ((InfoItemHolder) holder).updateState(infoItemList - .get(header == null ? position : position - 1), recordManager); - } - } - } else { + if (payloads.isEmpty() || !(holder instanceof InfoItemHolder)) { onBindViewHolder(holder, position); + return; + } + + for (final Object payload : payloads) { + if (payload instanceof StreamStateEntity || payload instanceof Boolean) { + ((InfoItemHolder) holder).updateState(infoItemList + .get(header == null ? position : position - 1), recordManager); + } } } @@ -372,11 +329,8 @@ public class InfoListAdapter extends RecyclerView.Adapter Date: Sat, 15 Jan 2022 17:40:08 +0100 Subject: [PATCH 011/107] Removed InfoListAdapter from checkstyle-suppressions because if you modify something in the code the suppressions-file no longer matches --- .../java/org/schabi/newpipe/info_list/InfoListAdapter.java | 2 ++ checkstyle-suppressions.xml | 4 ---- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/app/src/main/java/org/schabi/newpipe/info_list/InfoListAdapter.java b/app/src/main/java/org/schabi/newpipe/info_list/InfoListAdapter.java index 410a65449..57e6fa380 100644 --- a/app/src/main/java/org/schabi/newpipe/info_list/InfoListAdapter.java +++ b/app/src/main/java/org/schabi/newpipe/info_list/InfoListAdapter.java @@ -212,6 +212,7 @@ public class InfoListAdapter extends RecyclerView.Adapter - - From 91c67b085bd6a5553b44a08a296e2c2f72f761c8 Mon Sep 17 00:00:00 2001 From: litetex <40789489+litetex@users.noreply.github.com> Date: Sat, 15 Jan 2022 23:55:19 +0100 Subject: [PATCH 012/107] Code improvements Removed - partial - stupid code. --- .../fragments/list/BaseListFragment.java | 5 ---- .../list/channel/ChannelFragment.java | 23 ++++++++----------- .../list/playlist/PlaylistFragment.java | 2 +- .../list/videos/RelatedItemsFragment.java | 14 +++-------- 4 files changed, 13 insertions(+), 31 deletions(-) diff --git a/app/src/main/java/org/schabi/newpipe/fragments/list/BaseListFragment.java b/app/src/main/java/org/schabi/newpipe/fragments/list/BaseListFragment.java index d0a07ffc4..741729d5b 100644 --- a/app/src/main/java/org/schabi/newpipe/fragments/list/BaseListFragment.java +++ b/app/src/main/java/org/schabi/newpipe/fragments/list/BaseListFragment.java @@ -79,11 +79,6 @@ public abstract class BaseListFragment extends BaseStateFragment } } - @Override - public void onDetach() { - super.onDetach(); - } - @Override public void onCreate(final Bundle savedInstanceState) { super.onCreate(savedInstanceState); diff --git a/app/src/main/java/org/schabi/newpipe/fragments/list/channel/ChannelFragment.java b/app/src/main/java/org/schabi/newpipe/fragments/list/channel/ChannelFragment.java index 37954478d..66eb64b82 100644 --- a/app/src/main/java/org/schabi/newpipe/fragments/list/channel/ChannelFragment.java +++ b/app/src/main/java/org/schabi/newpipe/fragments/list/channel/ChannelFragment.java @@ -183,13 +183,6 @@ public class ChannelFragment extends BaseListInfoFragment } } - private void openRssFeed() { - final ChannelInfo info = currentInfo; - if (info != null) { - ShareUtils.openUrlInBrowser(requireContext(), info.getFeedUrl(), false); - } - } - @Override public boolean onOptionsItemSelected(final MenuItem item) { switch (item.getItemId()) { @@ -197,7 +190,10 @@ public class ChannelFragment extends BaseListInfoFragment NavigationHelper.openSettings(requireContext()); break; case R.id.menu_item_rss: - openRssFeed(); + if (currentInfo != null) { + ShareUtils.openUrlInBrowser( + requireContext(), currentInfo.getFeedUrl(), false); + } break; case R.id.menu_item_openInBrowser: if (currentInfo != null) { @@ -516,12 +512,11 @@ public class ChannelFragment extends BaseListInfoFragment } private PlayQueue getPlayQueue(final int index) { - final List streamItems = new ArrayList<>(); - for (final InfoItem i : infoListAdapter.getItemsList()) { - if (i instanceof StreamInfoItem) { - streamItems.add((StreamInfoItem) i); - } - } + final List streamItems = infoListAdapter.getItemsList().stream() + .filter(StreamInfoItem.class::isInstance) + .map(StreamInfoItem.class::cast) + .collect(Collectors.toList()); + return new ChannelPlayQueue(currentInfo.getServiceId(), currentInfo.getUrl(), currentInfo.getNextPage(), streamItems, index); } diff --git a/app/src/main/java/org/schabi/newpipe/fragments/list/playlist/PlaylistFragment.java b/app/src/main/java/org/schabi/newpipe/fragments/list/playlist/PlaylistFragment.java index 640d08064..89ee3980e 100644 --- a/app/src/main/java/org/schabi/newpipe/fragments/list/playlist/PlaylistFragment.java +++ b/app/src/main/java/org/schabi/newpipe/fragments/list/playlist/PlaylistFragment.java @@ -413,7 +413,7 @@ public class PlaylistFragment extends BaseListInfoFragment { } private Subscriber> getPlaylistBookmarkSubscriber() { - return new Subscriber>() { + return new Subscriber<>() { @Override public void onSubscribe(final Subscription s) { if (bookmarkReactor != null) { diff --git a/app/src/main/java/org/schabi/newpipe/fragments/list/videos/RelatedItemsFragment.java b/app/src/main/java/org/schabi/newpipe/fragments/list/videos/RelatedItemsFragment.java index 6532417c0..285024484 100644 --- a/app/src/main/java/org/schabi/newpipe/fragments/list/videos/RelatedItemsFragment.java +++ b/app/src/main/java/org/schabi/newpipe/fragments/list/videos/RelatedItemsFragment.java @@ -26,12 +26,11 @@ import org.schabi.newpipe.util.RelatedItemInfo; import java.io.Serializable; import io.reactivex.rxjava3.core.Single; -import io.reactivex.rxjava3.disposables.CompositeDisposable; public class RelatedItemsFragment extends BaseListInfoFragment implements SharedPreferences.OnSharedPreferenceChangeListener { private static final String INFO_KEY = "related_info_key"; - private final CompositeDisposable disposables = new CompositeDisposable(); + private RelatedItemInfo relatedItemInfo; /*////////////////////////////////////////////////////////////////////////// @@ -54,11 +53,6 @@ public class RelatedItemsFragment extends BaseListInfoFragment // LifeCycle //////////////////////////////////////////////////////////////////////////*/ - @Override - public void onAttach(@NonNull final Context context) { - super.onAttach(context); - } - @Override public View onCreateView(@NonNull final LayoutInflater inflater, @Nullable final ViewGroup container, @@ -67,9 +61,6 @@ public class RelatedItemsFragment extends BaseListInfoFragment } @Override - public void onDestroy() { - super.onDestroy(); - disposables.clear(); } @Override @@ -128,7 +119,6 @@ public class RelatedItemsFragment extends BaseListInfoFragment } ViewUtils.slideUp(requireView(), 120, 96, 0.06f); - disposables.clear(); } /*////////////////////////////////////////////////////////////////////////// @@ -137,11 +127,13 @@ public class RelatedItemsFragment extends BaseListInfoFragment @Override public void setTitle(final String title) { + // Nothing to do - override parent } @Override public void onCreateOptionsMenu(@NonNull final Menu menu, @NonNull final MenuInflater inflater) { + // Nothing to do - override parent } private void setInitialData(final StreamInfo info) { From d3cd3d62b4ab542888669492f6da025cc2db2cfd Mon Sep 17 00:00:00 2001 From: litetex <40789489+litetex@users.noreply.github.com> Date: Sun, 16 Jan 2022 17:08:13 +0100 Subject: [PATCH 013/107] Tried to repair #4475 and #3368 * Always recreate the footer so that it's not possible to attach the same instance twice * Removed support for creating a custom footer as it's never used * Supply the header with an supplier * This might not fix the problem completely as we currently can only create the header once inside Channel, Playlist and RelatedItems-Fragment - allowing creation of multiple headers might be done in the future if the issues still arise * Other minor fixes --- .../fragments/list/BaseListFragment.java | 18 ++---- .../list/channel/ChannelFragment.java | 19 +++---- .../list/playlist/PlaylistFragment.java | 20 +++---- .../list/videos/RelatedItemsFragment.java | 42 +++++++------- .../newpipe/info_list/InfoListAdapter.java | 57 +++++++++++-------- 5 files changed, 78 insertions(+), 78 deletions(-) diff --git a/app/src/main/java/org/schabi/newpipe/fragments/list/BaseListFragment.java b/app/src/main/java/org/schabi/newpipe/fragments/list/BaseListFragment.java index 741729d5b..d1685c5af 100644 --- a/app/src/main/java/org/schabi/newpipe/fragments/list/BaseListFragment.java +++ b/app/src/main/java/org/schabi/newpipe/fragments/list/BaseListFragment.java @@ -17,10 +17,8 @@ import androidx.appcompat.app.ActionBar; import androidx.preference.PreferenceManager; import androidx.recyclerview.widget.GridLayoutManager; import androidx.recyclerview.widget.RecyclerView; -import androidx.viewbinding.ViewBinding; import org.schabi.newpipe.R; -import org.schabi.newpipe.databinding.PignateFooterBinding; import org.schabi.newpipe.error.ErrorUtil; import org.schabi.newpipe.extractor.InfoItem; import org.schabi.newpipe.extractor.channel.ChannelInfoItem; @@ -44,6 +42,7 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.List; import java.util.Queue; +import java.util.function.Supplier; import static org.schabi.newpipe.extractor.utils.Utils.isNullOrEmpty; import static org.schabi.newpipe.ktx.ViewUtils.animate; @@ -215,14 +214,10 @@ public abstract class BaseListFragment extends BaseStateFragment //////////////////////////////////////////////////////////////////////////*/ @Nullable - protected ViewBinding getListHeader() { + protected Supplier getListHeaderSupplier() { return null; } - protected ViewBinding getListFooter() { - return PignateFooterBinding.inflate(activity.getLayoutInflater(), itemsList, false); - } - protected RecyclerView.LayoutManager getListLayoutManager() { return new SuperScrollLayoutManager(activity); } @@ -247,11 +242,10 @@ public abstract class BaseListFragment extends BaseStateFragment itemsList.setLayoutManager(useGrid ? getGridLayoutManager() : getListLayoutManager()); infoListAdapter.setUseGridVariant(useGrid); - infoListAdapter.setFooter(getListFooter().getRoot()); - final ViewBinding listHeader = getListHeader(); - if (listHeader != null) { - infoListAdapter.setHeader(listHeader.getRoot()); + final Supplier listHeaderSupplier = getListHeaderSupplier(); + if (listHeaderSupplier != null) { + infoListAdapter.setHeaderSupplier(listHeaderSupplier); } itemsList.setAdapter(infoListAdapter); @@ -447,7 +441,7 @@ public abstract class BaseListFragment extends BaseStateFragment if (itemsList.canScrollVertically(1) || itemsList.canScrollVertically(-1)) { if (DEBUG) { - Log.d(TAG, "loadEnoughInitial - OK: itemList is scrollable"); + Log.d(TAG, "loadEnoughInitialData - OK: itemList is scrollable"); } return; } diff --git a/app/src/main/java/org/schabi/newpipe/fragments/list/channel/ChannelFragment.java b/app/src/main/java/org/schabi/newpipe/fragments/list/channel/ChannelFragment.java index 66eb64b82..918facc4e 100644 --- a/app/src/main/java/org/schabi/newpipe/fragments/list/channel/ChannelFragment.java +++ b/app/src/main/java/org/schabi/newpipe/fragments/list/channel/ChannelFragment.java @@ -1,5 +1,9 @@ package org.schabi.newpipe.fragments.list.channel; +import static org.schabi.newpipe.ktx.TextViewUtils.animateTextColor; +import static org.schabi.newpipe.ktx.ViewUtils.animate; +import static org.schabi.newpipe.ktx.ViewUtils.animateBackgroundColor; + import android.content.Context; import android.os.Bundle; import android.text.TextUtils; @@ -17,7 +21,6 @@ import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.appcompat.app.ActionBar; import androidx.core.content.ContextCompat; -import androidx.viewbinding.ViewBinding; import com.jakewharton.rxbinding4.view.RxView; @@ -29,7 +32,6 @@ import org.schabi.newpipe.databinding.PlaylistControlBinding; import org.schabi.newpipe.error.ErrorInfo; import org.schabi.newpipe.error.ErrorUtil; import org.schabi.newpipe.error.UserAction; -import org.schabi.newpipe.extractor.InfoItem; import org.schabi.newpipe.extractor.ListExtractor; import org.schabi.newpipe.extractor.channel.ChannelInfo; import org.schabi.newpipe.extractor.exceptions.ContentNotSupportedException; @@ -43,13 +45,14 @@ import org.schabi.newpipe.player.playqueue.PlayQueue; import org.schabi.newpipe.util.ExtractorHelper; import org.schabi.newpipe.util.Localization; import org.schabi.newpipe.util.NavigationHelper; -import org.schabi.newpipe.util.external_communication.ShareUtils; import org.schabi.newpipe.util.PicassoHelper; import org.schabi.newpipe.util.ThemeHelper; +import org.schabi.newpipe.util.external_communication.ShareUtils; -import java.util.ArrayList; import java.util.List; import java.util.concurrent.TimeUnit; +import java.util.function.Supplier; +import java.util.stream.Collectors; import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers; import io.reactivex.rxjava3.core.Observable; @@ -61,10 +64,6 @@ import io.reactivex.rxjava3.functions.Consumer; import io.reactivex.rxjava3.functions.Function; import io.reactivex.rxjava3.schedulers.Schedulers; -import static org.schabi.newpipe.ktx.TextViewUtils.animateTextColor; -import static org.schabi.newpipe.ktx.ViewUtils.animate; -import static org.schabi.newpipe.ktx.ViewUtils.animateBackgroundColor; - public class ChannelFragment extends BaseListInfoFragment implements View.OnClickListener { @@ -145,12 +144,12 @@ public class ChannelFragment extends BaseListInfoFragment //////////////////////////////////////////////////////////////////////////*/ @Override - protected ViewBinding getListHeader() { + protected Supplier getListHeaderSupplier() { headerBinding = ChannelHeaderBinding .inflate(activity.getLayoutInflater(), itemsList, false); playlistControlBinding = headerBinding.playlistControl; - return headerBinding; + return headerBinding::getRoot; } @Override diff --git a/app/src/main/java/org/schabi/newpipe/fragments/list/playlist/PlaylistFragment.java b/app/src/main/java/org/schabi/newpipe/fragments/list/playlist/PlaylistFragment.java index 89ee3980e..84dcb4fd9 100644 --- a/app/src/main/java/org/schabi/newpipe/fragments/list/playlist/PlaylistFragment.java +++ b/app/src/main/java/org/schabi/newpipe/fragments/list/playlist/PlaylistFragment.java @@ -1,5 +1,9 @@ package org.schabi.newpipe.fragments.list.playlist; +import static org.schabi.newpipe.extractor.utils.Utils.isNullOrEmpty; +import static org.schabi.newpipe.ktx.ViewUtils.animate; +import static org.schabi.newpipe.ktx.ViewUtils.animateHideRecyclerViewAllowingScrolling; + import android.app.Activity; import android.content.Context; import android.os.Bundle; @@ -15,7 +19,6 @@ import android.view.ViewGroup; import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.appcompat.content.res.AppCompatResources; -import androidx.viewbinding.ViewBinding; import org.reactivestreams.Subscriber; import org.reactivestreams.Subscription; @@ -42,17 +45,18 @@ import org.schabi.newpipe.player.helper.PlayerHolder; import org.schabi.newpipe.player.playqueue.PlayQueue; import org.schabi.newpipe.player.playqueue.PlaylistPlayQueue; import org.schabi.newpipe.util.ExtractorHelper; -import org.schabi.newpipe.util.PicassoHelper; -import org.schabi.newpipe.util.external_communication.KoreUtils; import org.schabi.newpipe.util.Localization; import org.schabi.newpipe.util.NavigationHelper; -import org.schabi.newpipe.util.external_communication.ShareUtils; +import org.schabi.newpipe.util.PicassoHelper; import org.schabi.newpipe.util.StreamDialogEntry; +import org.schabi.newpipe.util.external_communication.KoreUtils; +import org.schabi.newpipe.util.external_communication.ShareUtils; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import java.util.concurrent.atomic.AtomicBoolean; +import java.util.function.Supplier; import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers; import io.reactivex.rxjava3.core.Flowable; @@ -60,10 +64,6 @@ import io.reactivex.rxjava3.core.Single; import io.reactivex.rxjava3.disposables.CompositeDisposable; import io.reactivex.rxjava3.disposables.Disposable; -import static org.schabi.newpipe.extractor.utils.Utils.isNullOrEmpty; -import static org.schabi.newpipe.ktx.ViewUtils.animate; -import static org.schabi.newpipe.ktx.ViewUtils.animateHideRecyclerViewAllowingScrolling; - public class PlaylistFragment extends BaseListInfoFragment { private static final String PICASSO_PLAYLIST_TAG = "PICASSO_PLAYLIST_TAG"; @@ -120,12 +120,12 @@ public class PlaylistFragment extends BaseListInfoFragment { //////////////////////////////////////////////////////////////////////////*/ @Override - protected ViewBinding getListHeader() { + protected Supplier getListHeaderSupplier() { headerBinding = PlaylistHeaderBinding .inflate(activity.getLayoutInflater(), itemsList, false); playlistControlBinding = headerBinding.playlistControl; - return headerBinding; + return headerBinding::getRoot; } @Override diff --git a/app/src/main/java/org/schabi/newpipe/fragments/list/videos/RelatedItemsFragment.java b/app/src/main/java/org/schabi/newpipe/fragments/list/videos/RelatedItemsFragment.java index 285024484..7ba6aa2ab 100644 --- a/app/src/main/java/org/schabi/newpipe/fragments/list/videos/RelatedItemsFragment.java +++ b/app/src/main/java/org/schabi/newpipe/fragments/list/videos/RelatedItemsFragment.java @@ -1,6 +1,5 @@ package org.schabi.newpipe.fragments.list.videos; -import android.content.Context; import android.content.SharedPreferences; import android.os.Bundle; import android.view.LayoutInflater; @@ -12,7 +11,6 @@ import android.view.ViewGroup; import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.preference.PreferenceManager; -import androidx.viewbinding.ViewBinding; import org.schabi.newpipe.R; import org.schabi.newpipe.databinding.RelatedItemsHeaderBinding; @@ -24,6 +22,7 @@ import org.schabi.newpipe.ktx.ViewUtils; import org.schabi.newpipe.util.RelatedItemInfo; import java.io.Serializable; +import java.util.function.Supplier; import io.reactivex.rxjava3.core.Single; @@ -60,9 +59,6 @@ public class RelatedItemsFragment extends BaseListInfoFragment return inflater.inflate(R.layout.fragment_related_items, container, false); } - @Override - } - @Override public void onDestroyView() { headerBinding = null; @@ -70,22 +66,23 @@ public class RelatedItemsFragment extends BaseListInfoFragment } @Override - protected ViewBinding getListHeader() { - if (relatedItemInfo != null && relatedItemInfo.getRelatedItems() != null) { - headerBinding = RelatedItemsHeaderBinding - .inflate(activity.getLayoutInflater(), itemsList, false); - - final SharedPreferences pref = PreferenceManager - .getDefaultSharedPreferences(requireContext()); - final boolean autoplay = pref.getBoolean(getString(R.string.auto_queue_key), false); - headerBinding.autoplaySwitch.setChecked(autoplay); - headerBinding.autoplaySwitch.setOnCheckedChangeListener((compoundButton, b) -> - PreferenceManager.getDefaultSharedPreferences(requireContext()).edit() - .putBoolean(getString(R.string.auto_queue_key), b).apply()); - return headerBinding; - } else { + protected Supplier getListHeaderSupplier() { + if (relatedItemInfo == null || relatedItemInfo.getRelatedItems() == null) { return null; } + + headerBinding = RelatedItemsHeaderBinding + .inflate(activity.getLayoutInflater(), itemsList, false); + + final SharedPreferences pref = PreferenceManager + .getDefaultSharedPreferences(requireContext()); + final boolean autoplay = pref.getBoolean(getString(R.string.auto_queue_key), false); + headerBinding.autoplaySwitch.setChecked(autoplay); + headerBinding.autoplaySwitch.setOnCheckedChangeListener((compoundButton, b) -> + PreferenceManager.getDefaultSharedPreferences(requireContext()).edit() + .putBoolean(getString(R.string.auto_queue_key), b).apply()); + + return headerBinding::getRoot; } @Override @@ -161,11 +158,10 @@ public class RelatedItemsFragment extends BaseListInfoFragment @Override public void onSharedPreferenceChanged(final SharedPreferences sharedPreferences, final String s) { - final SharedPreferences pref = - PreferenceManager.getDefaultSharedPreferences(requireContext()); - final boolean autoplay = pref.getBoolean(getString(R.string.auto_queue_key), false); if (headerBinding != null) { - headerBinding.autoplaySwitch.setChecked(autoplay); + headerBinding.autoplaySwitch.setChecked( + sharedPreferences.getBoolean( + getString(R.string.auto_queue_key), false)); } } diff --git a/app/src/main/java/org/schabi/newpipe/info_list/InfoListAdapter.java b/app/src/main/java/org/schabi/newpipe/info_list/InfoListAdapter.java index 57e6fa380..839125fd1 100644 --- a/app/src/main/java/org/schabi/newpipe/info_list/InfoListAdapter.java +++ b/app/src/main/java/org/schabi/newpipe/info_list/InfoListAdapter.java @@ -2,6 +2,7 @@ package org.schabi.newpipe.info_list; import android.content.Context; import android.util.Log; +import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; @@ -11,6 +12,7 @@ import androidx.recyclerview.widget.GridLayoutManager; import androidx.recyclerview.widget.RecyclerView; import org.schabi.newpipe.database.stream.model.StreamStateEntity; +import org.schabi.newpipe.databinding.PignateFooterBinding; import org.schabi.newpipe.extractor.InfoItem; import org.schabi.newpipe.extractor.channel.ChannelInfoItem; import org.schabi.newpipe.extractor.comments.CommentsInfoItem; @@ -34,6 +36,7 @@ import org.schabi.newpipe.util.OnClickGesture; import java.util.ArrayList; import java.util.List; +import java.util.function.Supplier; /* * Created by Christian Schabesberger on 01.08.16. @@ -74,6 +77,7 @@ public class InfoListAdapter extends RecyclerView.Adapter infoItemList; private final HistoryRecordManager recordManager; @@ -81,11 +85,12 @@ public class InfoListAdapter extends RecyclerView.Adapter headerSupplier = null; public InfoListAdapter(final Context context) { - this.recordManager = new HistoryRecordManager(context); + layoutInflater = LayoutInflater.from(context); + recordManager = new HistoryRecordManager(context); infoItemBuilder = new InfoItemBuilder(context); infoItemList = new ArrayList<>(); } @@ -129,12 +134,12 @@ public class InfoListAdapter extends RecyclerView.Adapter offsetStart = " + offsetStart + ", " + "infoItemList.size() = " + infoItemList.size() + ", " - + "header = " + header + ", footer = " + footer + ", " + + "header = " + hasHeader() + ", " + "showFooter = " + showFooter); } notifyItemRangeInserted(offsetStart, data.size()); - if (footer != null && showFooter) { + if (showFooter) { final int footerNow = sizeConsideringHeaderOffset(); notifyItemMoved(offsetStart, footerNow); @@ -153,16 +158,16 @@ public class InfoListAdapter extends RecyclerView.Adapter headerSupplier) { + final boolean changed = headerSupplier != this.headerSupplier; + this.headerSupplier = headerSupplier; if (changed) { notifyDataSetChanged(); } } - public void setFooter(final View view) { - this.footer = view; + protected boolean hasHeader() { + return this.headerSupplier != null; } public void showFooter(final boolean show) { @@ -182,7 +187,7 @@ public class InfoListAdapter extends RecyclerView.Adapter Date: Sun, 16 Jan 2022 19:05:51 +0100 Subject: [PATCH 014/107] Reverted to loading behavior of #7638 and improved it The previous/reverted behavior caused unwanted data transmission: * Removed loading via handleResults/loadMoreItems-callback because the RecyclerView is apparently not immediately updated in the UI when the data is set which causes one load of data to much. --- .../fragments/list/BaseListFragment.java | 127 +++++++++--------- .../fragments/list/BaseListInfoFragment.java | 7 +- .../fragments/list/search/SearchFragment.java | 14 +- 3 files changed, 68 insertions(+), 80 deletions(-) diff --git a/app/src/main/java/org/schabi/newpipe/fragments/list/BaseListFragment.java b/app/src/main/java/org/schabi/newpipe/fragments/list/BaseListFragment.java index d1685c5af..279d6d563 100644 --- a/app/src/main/java/org/schabi/newpipe/fragments/list/BaseListFragment.java +++ b/app/src/main/java/org/schabi/newpipe/fragments/list/BaseListFragment.java @@ -312,14 +312,74 @@ public abstract class BaseListFragment extends BaseStateFragment }); itemsList.clearOnScrollListeners(); - itemsList.addOnScrollListener(new OnScrollBelowItemsListener() { + + /* + * Add initial scroll listener - which tries to load more items when not enough + * are in the view (not scrollable) and more are available. + * + * Note: This method only works because "This callback will also be called if visible + * item range changes after a layout calculation. In that case, dx and dy will be 0." + * - which might be unexpected because no actual scrolling occurs... + * + * This listener will be replaced by DefaultItemListOnScrolledDownListener when + * * the view was actually scrolled + * * the view is scrollable + * * No more items can be loaded + */ + itemsList.addOnScrollListener(new DefaultItemListOnScrolledDownListener() { @Override - public void onScrolledDown(final RecyclerView recyclerView) { - onScrollToBottom(); + public void onScrolled(final RecyclerView recyclerView, final int dx, final int dy) { + super.onScrolled(recyclerView, dx, dy); + + if (dy != 0) { + log("Vertical scroll occurred"); + + useNormalScrollListener(); + return; + } + if (isLoading.get()) { + log("Still loading data -> Skipping"); + return; + } + if (!hasMoreItems()) { + log("No more items to load"); + + useNormalScrollListener(); + return; + } + if (itemsList.canScrollVertically(1) + || itemsList.canScrollVertically(-1)) { + log("View is scrollable"); + + useNormalScrollListener(); + return; + } + + log("Loading more data"); + loadMoreItems(); + } + + private void useNormalScrollListener() { + log("Unregistering and using normal listener"); + itemsList.removeOnScrollListener(this); + itemsList.addOnScrollListener(new DefaultItemListOnScrolledDownListener()); + } + + private void log(final String msg) { + if (DEBUG) { + Log.d(TAG, "itemListInitScrollListener - " + msg); + } } }); } + class DefaultItemListOnScrolledDownListener extends OnScrollBelowItemsListener { + @Override + public void onScrolledDown(final RecyclerView recyclerView) { + onScrollToBottom(); + } + } + private void onStreamSelected(final StreamInfoItem selectedItem) { onItemSelected(selectedItem); NavigationHelper.openVideoDetailFragment(requireContext(), getFM(), @@ -407,66 +467,7 @@ public abstract class BaseListFragment extends BaseStateFragment // Load and handle //////////////////////////////////////////////////////////////////////////*/ - /** - * If more items are loadable and the itemList is not scrollable -> load more data. - *
- * Should be called once the initial items inside {@link #startLoading(boolean)} - * has been loaded and added to the {@link #itemsList}. - *
- * Otherwise the loading indicator is always shown but no data can be loaded - * because the view is not scrollable; see also #1974. - */ - protected void ifMoreItemsLoadableLoadUntilScrollable() { - ifMoreItemsLoadableLoadUntilScrollable(0); - } - - /** - * If more items are loadable and the itemList is not scrollable -> load more data. - * - * @param recursiveCallCount Amount of recursive calls that occurred - * @see #ifMoreItemsLoadableLoadUntilScrollable() - */ - protected void ifMoreItemsLoadableLoadUntilScrollable(final int recursiveCallCount) { - // Try to prevent malfunction / stackoverflow - if (recursiveCallCount > 100) { - Log.w(TAG, "loadEnoughInitialData - Too many recursive calls - Aborting"); - return; - } - if (!hasMoreItems()) { - if (DEBUG) { - Log.d(TAG, "loadEnoughInitialData - OK: No more items to load"); - } - return; - } - if (itemsList.canScrollVertically(1) - || itemsList.canScrollVertically(-1)) { - if (DEBUG) { - Log.d(TAG, "loadEnoughInitialData - OK: itemList is scrollable"); - } - return; - } - if (DEBUG) { - Log.d(TAG, "loadEnoughInitialData - View is not scrollable " - + "but it could load more items -> Loading more"); - } - loadMoreItems(() -> - ifMoreItemsLoadableLoadUntilScrollable(recursiveCallCount + 1)); - } - - /** - * Loads more items. - * @param initialDataLoadCallback - * Callback used in {@link #ifMoreItemsLoadableLoadUntilScrollable()}. - *
- * Execute it once the data was loaded and added to the {@link #itemsList}. - *
- * Might be null. - */ - protected abstract void loadMoreItems(@Nullable Runnable initialDataLoadCallback); - - protected void loadMoreItems() { - loadMoreItems(null); - } + protected abstract void loadMoreItems(); protected abstract boolean hasMoreItems(); diff --git a/app/src/main/java/org/schabi/newpipe/fragments/list/BaseListInfoFragment.java b/app/src/main/java/org/schabi/newpipe/fragments/list/BaseListInfoFragment.java index 87f031c12..ebd586e35 100644 --- a/app/src/main/java/org/schabi/newpipe/fragments/list/BaseListInfoFragment.java +++ b/app/src/main/java/org/schabi/newpipe/fragments/list/BaseListInfoFragment.java @@ -6,7 +6,6 @@ import android.util.Log; import android.view.View; import androidx.annotation.NonNull; -import androidx.annotation.Nullable; import org.schabi.newpipe.error.ErrorInfo; import org.schabi.newpipe.error.UserAction; @@ -146,7 +145,6 @@ public abstract class BaseListInfoFragment currentInfo = result; currentNextPage = result.getNextPage(); handleResult(result); - ifMoreItemsLoadableLoadUntilScrollable(); }, throwable -> showError(new ErrorInfo(throwable, errorUserAction, "Start loading: " + url, serviceId))); @@ -162,7 +160,7 @@ public abstract class BaseListInfoFragment protected abstract Single loadMoreItemsLogic(); @Override - protected void loadMoreItems(@Nullable final Runnable initialDataLoadCallback) { + protected void loadMoreItems() { isLoading.set(true); if (currentWorker != null) { @@ -178,9 +176,6 @@ public abstract class BaseListInfoFragment .subscribe(infoItemsPage -> { isLoading.set(false); handleNextItems(infoItemsPage); - if (initialDataLoadCallback != null) { - initialDataLoadCallback.run(); - } }, (@NonNull Throwable throwable) -> dynamicallyShowErrorPanelOrSnackbar(new ErrorInfo(throwable, errorUserAction, "Loading more items: " + url, serviceId))); diff --git a/app/src/main/java/org/schabi/newpipe/fragments/list/search/SearchFragment.java b/app/src/main/java/org/schabi/newpipe/fragments/list/search/SearchFragment.java index 35bb2c349..055c27733 100644 --- a/app/src/main/java/org/schabi/newpipe/fragments/list/search/SearchFragment.java +++ b/app/src/main/java/org/schabi/newpipe/fragments/list/search/SearchFragment.java @@ -868,15 +868,12 @@ public class SearchFragment extends BaseListFragment isLoading.set(false)) - .subscribe(result -> { - handleResult(result); - ifMoreItemsLoadableLoadUntilScrollable(); - }, this::onItemError); + .subscribe(this::handleResult, this::onItemError); } @Override - protected void loadMoreItems(@Nullable final Runnable initialDataLoadCallback) { + protected void loadMoreItems() { if (!Page.isValid(nextPage)) { return; } @@ -894,12 +891,7 @@ public class SearchFragment extends BaseListFragment isLoading.set(false)) - .subscribe(itemsPage -> { - handleNextItems(itemsPage); - if (initialDataLoadCallback != null) { - initialDataLoadCallback.run(); - } - }, this::onItemError); + .subscribe(this::handleNextItems, this::onItemError); } @Override From 85f701b94e8ce730e77bd91024d382d2e0753939 Mon Sep 17 00:00:00 2001 From: litetex <40789489+litetex@users.noreply.github.com> Date: Sun, 16 Jan 2022 19:42:30 +0100 Subject: [PATCH 015/107] Fixed listener not re-registering after e.g. a new search is started --- .../fragments/list/BaseListFragment.java | 45 ++++++++++++------- 1 file changed, 29 insertions(+), 16 deletions(-) diff --git a/app/src/main/java/org/schabi/newpipe/fragments/list/BaseListFragment.java b/app/src/main/java/org/schabi/newpipe/fragments/list/BaseListFragment.java index 279d6d563..5b503cb8e 100644 --- a/app/src/main/java/org/schabi/newpipe/fragments/list/BaseListFragment.java +++ b/app/src/main/java/org/schabi/newpipe/fragments/list/BaseListFragment.java @@ -304,28 +304,36 @@ public abstract class BaseListFragment extends BaseStateFragment } }); - infoListAdapter.setOnCommentsSelectedListener(new OnClickGesture() { + infoListAdapter.setOnCommentsSelectedListener(new OnClickGesture<>() { @Override public void selected(final CommentsInfoItem selectedItem) { onItemSelected(selectedItem); } }); + } + /** + * Remove all listeners and add the initial scroll listener to the {@link #itemsList}. + *
+ * Which tries to load more items when not enough are in the view (not scrollable) + * and more are available. + *
+ * Note: This method only works because "This callback will also be called if visible + * item range changes after a layout calculation. In that case, dx and dy will be 0." + * - which might be unexpected because no actual scrolling occurs... + *
+ * This listener will be replaced by DefaultItemListOnScrolledDownListener when + *
    + *
  • the view was actually scrolled
  • + *
  • the view is scrollable
  • + *
  • no more items can be loaded
  • + *
+ */ + protected void setItemsListInitialScrollListener() { + if (DEBUG) { + Log.d(TAG, "setItemsListInitialScrollListener called"); + } itemsList.clearOnScrollListeners(); - - /* - * Add initial scroll listener - which tries to load more items when not enough - * are in the view (not scrollable) and more are available. - * - * Note: This method only works because "This callback will also be called if visible - * item range changes after a layout calculation. In that case, dx and dy will be 0." - * - which might be unexpected because no actual scrolling occurs... - * - * This listener will be replaced by DefaultItemListOnScrolledDownListener when - * * the view was actually scrolled - * * the view is scrollable - * * No more items can be loaded - */ itemsList.addOnScrollListener(new DefaultItemListOnScrolledDownListener() { @Override public void onScrolled(final RecyclerView recyclerView, final int dx, final int dy) { @@ -360,7 +368,6 @@ public abstract class BaseListFragment extends BaseStateFragment } private void useNormalScrollListener() { - log("Unregistering and using normal listener"); itemsList.removeOnScrollListener(this); itemsList.addOnScrollListener(new DefaultItemListOnScrolledDownListener()); } @@ -467,6 +474,12 @@ public abstract class BaseListFragment extends BaseStateFragment // Load and handle //////////////////////////////////////////////////////////////////////////*/ + @Override + protected void startLoading(final boolean forceLoad) { + setItemsListInitialScrollListener(); + super.startLoading(forceLoad); + } + protected abstract void loadMoreItems(); protected abstract boolean hasMoreItems(); From 01683aa816b7792bab769b18b0509275b7eed6cc Mon Sep 17 00:00:00 2001 From: litetex <40789489+litetex@users.noreply.github.com> Date: Mon, 24 Jan 2022 20:57:23 +0100 Subject: [PATCH 016/107] Code improvements --- .../newpipe/info_list/InfoListAdapter.java | 20 +++++++++---------- 1 file changed, 9 insertions(+), 11 deletions(-) diff --git a/app/src/main/java/org/schabi/newpipe/info_list/InfoListAdapter.java b/app/src/main/java/org/schabi/newpipe/info_list/InfoListAdapter.java index 839125fd1..718f7004c 100644 --- a/app/src/main/java/org/schabi/newpipe/info_list/InfoListAdapter.java +++ b/app/src/main/java/org/schabi/newpipe/info_list/InfoListAdapter.java @@ -79,7 +79,7 @@ public class InfoListAdapter extends RecyclerView.Adapter infoItemList; + private final List infoItemList; private final HistoryRecordManager recordManager; private boolean useMiniVariant = false; @@ -134,7 +134,7 @@ public class InfoListAdapter extends RecyclerView.Adapter offsetStart = " + offsetStart + ", " + "infoItemList.size() = " + infoItemList.size() + ", " - + "header = " + hasHeader() + ", " + + "hasHeader = " + hasHeader() + ", " + "showFooter = " + showFooter); } notifyItemRangeInserted(offsetStart, data.size()); @@ -211,7 +211,7 @@ public class InfoListAdapter extends RecyclerView.Adapter payloads) { + // an empty payload requires a full update (see RecyclerView javadoc) if (payloads.isEmpty() || !(holder instanceof InfoItemHolder)) { onBindViewHolder(holder, position); return; From 9c2cdd251390fab9e4949befe306fd4c7b589a2b Mon Sep 17 00:00:00 2001 From: litetex <40789489+litetex@users.noreply.github.com> Date: Thu, 27 Jan 2022 20:41:58 +0100 Subject: [PATCH 017/107] Removed useless code MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit → https://github.com/TeamNewPipe/NewPipe/pull/7659#discussion_r793752985 --- .../newpipe/info_list/InfoListAdapter.java | 19 ------------------- 1 file changed, 19 deletions(-) diff --git a/app/src/main/java/org/schabi/newpipe/info_list/InfoListAdapter.java b/app/src/main/java/org/schabi/newpipe/info_list/InfoListAdapter.java index 718f7004c..fb27593e7 100644 --- a/app/src/main/java/org/schabi/newpipe/info_list/InfoListAdapter.java +++ b/app/src/main/java/org/schabi/newpipe/info_list/InfoListAdapter.java @@ -11,7 +11,6 @@ import androidx.annotation.Nullable; import androidx.recyclerview.widget.GridLayoutManager; import androidx.recyclerview.widget.RecyclerView; -import org.schabi.newpipe.database.stream.model.StreamStateEntity; import org.schabi.newpipe.databinding.PignateFooterBinding; import org.schabi.newpipe.extractor.InfoItem; import org.schabi.newpipe.extractor.channel.ChannelInfoItem; @@ -311,24 +310,6 @@ public class InfoListAdapter extends RecyclerView.Adapter payloads) { - // an empty payload requires a full update (see RecyclerView javadoc) - if (payloads.isEmpty() || !(holder instanceof InfoItemHolder)) { - onBindViewHolder(holder, position); - return; - } - - for (final Object payload : payloads) { - if (payload instanceof StreamStateEntity || payload instanceof Boolean) { - ((InfoItemHolder) holder).updateState( - infoItemList.get(hasHeader() ? position - 1 : position), recordManager); - } - } - } - public GridLayoutManager.SpanSizeLookup getSpanSizeLookup(final int spanCount) { return new GridLayoutManager.SpanSizeLookup() { @Override From 2acaefdb2adce73da19875ec30bb468731ba8904 Mon Sep 17 00:00:00 2001 From: litetex <40789489+litetex@users.noreply.github.com> Date: Thu, 17 Feb 2022 20:58:53 +0100 Subject: [PATCH 018/107] Fixed scrolling not working when rotating device --- .../fragments/list/BaseListFragment.java | 35 ++++++++++++------- 1 file changed, 22 insertions(+), 13 deletions(-) diff --git a/app/src/main/java/org/schabi/newpipe/fragments/list/BaseListFragment.java b/app/src/main/java/org/schabi/newpipe/fragments/list/BaseListFragment.java index 5b503cb8e..6ea0a8a0d 100644 --- a/app/src/main/java/org/schabi/newpipe/fragments/list/BaseListFragment.java +++ b/app/src/main/java/org/schabi/newpipe/fragments/list/BaseListFragment.java @@ -310,10 +310,24 @@ public abstract class BaseListFragment extends BaseStateFragment onItemSelected(selectedItem); } }); + + // Ensure that there is always a scroll listener (e.g. when rotating the device) + useNormalItemListScrollListener(); } /** - * Remove all listeners and add the initial scroll listener to the {@link #itemsList}. + * Removes all listeners and adds the normal scroll listener to the {@link #itemsList}. + */ + protected void useNormalItemListScrollListener() { + if (DEBUG) { + Log.d(TAG, "useNormalItemListScrollListener called"); + } + itemsList.clearOnScrollListeners(); + itemsList.addOnScrollListener(new DefaultItemListOnScrolledDownListener()); + } + + /** + * Removes all listeners and adds the initial scroll listener to the {@link #itemsList}. *
* Which tries to load more items when not enough are in the view (not scrollable) * and more are available. @@ -329,9 +343,9 @@ public abstract class BaseListFragment extends BaseStateFragment *
  • no more items can be loaded
  • * */ - protected void setItemsListInitialScrollListener() { + protected void useInitialItemListLoadScrollListener() { if (DEBUG) { - Log.d(TAG, "setItemsListInitialScrollListener called"); + Log.d(TAG, "useInitialItemListLoadScrollListener called"); } itemsList.clearOnScrollListeners(); itemsList.addOnScrollListener(new DefaultItemListOnScrolledDownListener() { @@ -342,7 +356,7 @@ public abstract class BaseListFragment extends BaseStateFragment if (dy != 0) { log("Vertical scroll occurred"); - useNormalScrollListener(); + useNormalItemListScrollListener(); return; } if (isLoading.get()) { @@ -352,14 +366,14 @@ public abstract class BaseListFragment extends BaseStateFragment if (!hasMoreItems()) { log("No more items to load"); - useNormalScrollListener(); + useNormalItemListScrollListener(); return; } if (itemsList.canScrollVertically(1) || itemsList.canScrollVertically(-1)) { log("View is scrollable"); - useNormalScrollListener(); + useNormalItemListScrollListener(); return; } @@ -367,14 +381,9 @@ public abstract class BaseListFragment extends BaseStateFragment loadMoreItems(); } - private void useNormalScrollListener() { - itemsList.removeOnScrollListener(this); - itemsList.addOnScrollListener(new DefaultItemListOnScrolledDownListener()); - } - private void log(final String msg) { if (DEBUG) { - Log.d(TAG, "itemListInitScrollListener - " + msg); + Log.d(TAG, "initItemListLoadScrollListener - " + msg); } } }); @@ -476,7 +485,7 @@ public abstract class BaseListFragment extends BaseStateFragment @Override protected void startLoading(final boolean forceLoad) { - setItemsListInitialScrollListener(); + useInitialItemListLoadScrollListener(); super.startLoading(forceLoad); } From 7f846429cf8f6b25e75be257e482b79133e6c552 Mon Sep 17 00:00:00 2001 From: Stypox Date: Fri, 18 Feb 2022 14:05:34 +0100 Subject: [PATCH 019/107] Remove large-land player layout: not actually used --- app/src/main/res/layout-large-land/player.xml | 756 ------------------ 1 file changed, 756 deletions(-) delete mode 100644 app/src/main/res/layout-large-land/player.xml diff --git a/app/src/main/res/layout-large-land/player.xml b/app/src/main/res/layout-large-land/player.xml deleted file mode 100644 index 71a325cf3..000000000 --- a/app/src/main/res/layout-large-land/player.xml +++ /dev/null @@ -1,756 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -