Merge branch 'master' into dev

This commit is contained in:
Stypox 2021-09-19 22:37:00 +02:00
commit 7e26748dc4
No known key found for this signature in database
GPG Key ID: 4BDF1B40A49FDD23
13 changed files with 120 additions and 63 deletions

View File

@ -17,8 +17,8 @@ android {
resValue "string", "app_name", "NewPipe" resValue "string", "app_name", "NewPipe"
minSdkVersion 19 minSdkVersion 19
targetSdkVersion 29 targetSdkVersion 29
versionCode 975 versionCode 976
versionName "0.21.9" versionName "0.21.10"
multiDexEnabled true multiDexEnabled true
@ -189,7 +189,7 @@ dependencies {
// name and the commit hash with the commit hash of the (pushed) commit you want to test // 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/ // This works thanks to JitPack: https://jitpack.io/
implementation 'com.github.TeamNewPipe:nanojson:1d9e1aea9049fc9f85e68b43ba39fe7be1c1f751' implementation 'com.github.TeamNewPipe:nanojson:1d9e1aea9049fc9f85e68b43ba39fe7be1c1f751'
implementation 'com.github.TeamNewPipe:NewPipeExtractor:62b87552f51022be76804f3bed65447aeb9fce9b' implementation 'com.github.TeamNewPipe:NewPipeExtractor:v0.21.10'
/** Checkstyle **/ /** Checkstyle **/
checkstyle "com.puppycrawl.tools:checkstyle:${checkstyleVersion}" checkstyle "com.puppycrawl.tools:checkstyle:${checkstyleVersion}"
@ -259,6 +259,9 @@ dependencies {
// Crash reporting // Crash reporting
implementation "ch.acra:acra-core:5.7.0" implementation "ch.acra:acra-core:5.7.0"
// Properly restarting
implementation 'com.jakewharton:process-phoenix:2.1.2'
// Reactive extensions for Java VM // Reactive extensions for Java VM
implementation "io.reactivex.rxjava3:rxjava:3.0.7" implementation "io.reactivex.rxjava3:rxjava:3.0.7"
implementation "io.reactivex.rxjava3:rxandroid:3.0.0" implementation "io.reactivex.rxjava3:rxandroid:3.0.0"

View File

@ -11,6 +11,8 @@ import androidx.core.app.NotificationManagerCompat;
import androidx.multidex.MultiDexApplication; import androidx.multidex.MultiDexApplication;
import androidx.preference.PreferenceManager; import androidx.preference.PreferenceManager;
import com.jakewharton.processphoenix.ProcessPhoenix;
import org.acra.ACRA; import org.acra.ACRA;
import org.acra.config.ACRAConfigurationException; import org.acra.config.ACRAConfigurationException;
import org.acra.config.CoreConfiguration; import org.acra.config.CoreConfiguration;
@ -86,6 +88,12 @@ public class App extends MultiDexApplication {
app = this; app = this;
if (ProcessPhoenix.isPhoenixProcess(this)) {
Log.i(TAG, "This is a phoenix process! "
+ "Aborting initialization of App[onCreate]");
return;
}
// Initialize settings first because others inits can use its values // Initialize settings first because others inits can use its values
NewPipeSettings.initSettings(this); NewPipeSettings.initSettings(this);

View File

@ -16,7 +16,7 @@ import leakcanary.AppWatcher;
public abstract class BaseFragment extends Fragment { public abstract class BaseFragment extends Fragment {
protected final String TAG = getClass().getSimpleName() + "@" + Integer.toHexString(hashCode()); protected final String TAG = getClass().getSimpleName() + "@" + Integer.toHexString(hashCode());
protected final boolean DEBUG = MainActivity.DEBUG; protected static final boolean DEBUG = MainActivity.DEBUG;
protected AppCompatActivity activity; protected AppCompatActivity activity;
//These values are used for controlling fragments when they are part of the frontpage //These values are used for controlling fragments when they are part of the frontpage
@State @State

View File

@ -170,6 +170,10 @@ class AboutActivity : AppCompatActivity() {
"PrettyTime", "2012 - 2020", "Lincoln Baxter, III", "PrettyTime", "2012 - 2020", "Lincoln Baxter, III",
"https://github.com/ocpsoft/prettytime", StandardLicenses.APACHE2 "https://github.com/ocpsoft/prettytime", StandardLicenses.APACHE2
), ),
SoftwareComponent(
"ProcessPhoenix", "2015", "Jake Wharton",
"https://github.com/JakeWharton/ProcessPhoenix", StandardLicenses.APACHE2
),
SoftwareComponent( SoftwareComponent(
"RxAndroid", "2015", "The RxAndroid authors", "RxAndroid", "2015", "The RxAndroid authors",
"https://github.com/ReactiveX/RxAndroid", StandardLicenses.APACHE2 "https://github.com/ReactiveX/RxAndroid", StandardLicenses.APACHE2

View File

@ -121,27 +121,14 @@ class ErrorPanelHelper(
ErrorActivity.reportError(context, errorInfo) ErrorActivity.reportError(context, errorInfo)
} }
errorTextView.setText( errorTextView.setText(getExceptionDescription(errorInfo.throwable))
when (errorInfo.throwable) {
is AgeRestrictedContentException -> R.string.restricted_video_no_stream if (errorInfo.throwable !is ContentNotAvailableException &&
is GeographicRestrictionException -> R.string.georestricted_content errorInfo.throwable !is ContentNotSupportedException
is PaidContentException -> R.string.paid_content ) {
is PrivateContentException -> R.string.private_content // show retry button only for content which is not unavailable or unsupported
is SoundCloudGoPlusContentException -> R.string.soundcloud_go_plus_content errorRetryButton.isVisible = true
is YoutubeMusicPremiumContentException -> R.string.youtube_music_premium_content }
is ContentNotAvailableException -> R.string.content_not_available
is ContentNotSupportedException -> R.string.content_not_supported
else -> {
// show retry button only for content which is not unavailable or unsupported
errorRetryButton.isVisible = true
if (errorInfo.throwable != null && errorInfo.throwable!!.isNetworkRelated) {
R.string.network_error
} else {
R.string.error_snackbar_message
}
}
}
)
} }
setRootVisible() setRootVisible()
@ -189,5 +176,27 @@ class ErrorPanelHelper(
companion object { companion object {
val TAG: String = ErrorPanelHelper::class.simpleName!! val TAG: String = ErrorPanelHelper::class.simpleName!!
val DEBUG: Boolean = MainActivity.DEBUG val DEBUG: Boolean = MainActivity.DEBUG
@StringRes
public fun getExceptionDescription(throwable: Throwable?): Int {
return when (throwable) {
is AgeRestrictedContentException -> R.string.restricted_video_no_stream
is GeographicRestrictionException -> R.string.georestricted_content
is PaidContentException -> R.string.paid_content
is PrivateContentException -> R.string.private_content
is SoundCloudGoPlusContentException -> R.string.soundcloud_go_plus_content
is YoutubeMusicPremiumContentException -> R.string.youtube_music_premium_content
is ContentNotAvailableException -> R.string.content_not_available
is ContentNotSupportedException -> R.string.content_not_supported
else -> {
// show retry button only for content which is not unavailable or unsupported
if (throwable != null && throwable.isNetworkRelated) {
R.string.network_error
} else {
R.string.error_snackbar_message
}
}
}
}
} }
} }

View File

@ -1,5 +1,6 @@
package org.schabi.newpipe.error; package org.schabi.newpipe.error;
import android.annotation.SuppressLint;
import android.content.Intent; import android.content.Intent;
import android.content.SharedPreferences; import android.content.SharedPreferences;
import android.os.Build; import android.os.Build;
@ -66,6 +67,7 @@ public class ReCaptchaActivity extends AppCompatActivity {
private ActivityRecaptchaBinding recaptchaBinding; private ActivityRecaptchaBinding recaptchaBinding;
private String foundCookies = ""; private String foundCookies = "";
@SuppressLint("SetJavaScriptEnabled")
@Override @Override
protected void onCreate(final Bundle savedInstanceState) { protected void onCreate(final Bundle savedInstanceState) {
ThemeHelper.setTheme(this); ThemeHelper.setTheme(this);

View File

@ -424,7 +424,7 @@ public final class VideoDetailFragment
showRelatedItems = sharedPreferences.getBoolean(key, true); showRelatedItems = sharedPreferences.getBoolean(key, true);
tabSettingsChanged = true; tabSettingsChanged = true;
} else if (key.equals(getString(R.string.show_description_key))) { } else if (key.equals(getString(R.string.show_description_key))) {
showComments = sharedPreferences.getBoolean(key, true); showDescription = sharedPreferences.getBoolean(key, true);
tabSettingsChanged = true; tabSettingsChanged = true;
} }
} }
@ -743,20 +743,19 @@ public final class VideoDetailFragment
&& player.getPlayQueue() != null && player.getPlayQueue() != null
&& player.videoPlayerSelected() && player.videoPlayerSelected()
&& player.getPlayQueue().previous()) { && player.getPlayQueue().previous()) {
return true; return true; // no code here, as previous() was used in the if
} }
// That means that we are on the start of the stack, // That means that we are on the start of the stack,
// return false to let the MainActivity handle the onBack
if (stack.size() <= 1) { if (stack.size() <= 1) {
restoreDefaultOrientation(); restoreDefaultOrientation();
return false; // let MainActivity handle the onBack (e.g. to minimize the mini player)
return false;
} }
// Remove top // Remove top
stack.pop(); stack.pop();
// Get stack item from the new top // Get stack item from the new top
assert stack.peek() != null; setupFromHistoryItem(Objects.requireNonNull(stack.peek()));
setupFromHistoryItem(stack.peek());
return true; return true;
} }
@ -1433,17 +1432,15 @@ public final class VideoDetailFragment
//////////////////////////////////////////////////////////////////////////*/ //////////////////////////////////////////////////////////////////////////*/
private void restoreDefaultOrientation() { private void restoreDefaultOrientation() {
if (!isPlayerAvailable() || !player.videoPlayerSelected() || activity == null) { if (isPlayerAvailable() && player.videoPlayerSelected()) {
return; toggleFullscreenIfInFullscreenMode();
} }
toggleFullscreenIfInFullscreenMode();
// This will show systemUI and pause the player. // This will show systemUI and pause the player.
// User can tap on Play button and video will be in fullscreen mode again // User can tap on Play button and video will be in fullscreen mode again
// Note for tablet: trying to avoid orientation changes since it's not easy // Note for tablet: trying to avoid orientation changes since it's not easy
// to physically rotate the tablet every time // to physically rotate the tablet every time
if (!DeviceUtils.isTablet(activity)) { if (activity != null && !DeviceUtils.isTablet(activity)) {
activity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED); activity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED);
} }
} }

View File

@ -215,6 +215,7 @@ public class SearchFragment extends BaseListFragment<SearchInfo, ListExtractor.I
@Override @Override
public void onViewCreated(@NonNull final View rootView, final Bundle savedInstanceState) { public void onViewCreated(@NonNull final View rootView, final Bundle savedInstanceState) {
searchBinding = FragmentSearchBinding.bind(rootView);
super.onViewCreated(rootView, savedInstanceState); super.onViewCreated(rootView, savedInstanceState);
showSearchOnStart(); showSearchOnStart();
initSearchListeners(); initSearchListeners();
@ -341,7 +342,6 @@ public class SearchFragment extends BaseListFragment<SearchInfo, ListExtractor.I
@Override @Override
protected void initViews(final View rootView, final Bundle savedInstanceState) { protected void initViews(final View rootView, final Bundle savedInstanceState) {
super.initViews(rootView, savedInstanceState); super.initViews(rootView, savedInstanceState);
searchBinding = FragmentSearchBinding.bind(rootView);
searchBinding.suggestionsList.setAdapter(suggestionListAdapter); searchBinding.suggestionsList.setAdapter(suggestionListAdapter);
new ItemTouchHelper(new ItemTouchHelper.Callback() { new ItemTouchHelper(new ItemTouchHelper.Callback() {
@ -807,18 +807,21 @@ public class SearchFragment extends BaseListFragment<SearchInfo, ListExtractor.I
}) })
.subscribeOn(Schedulers.io()) .subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread()) .observeOn(AndroidSchedulers.mainThread())
.subscribe(listNotification -> { .subscribe(
if (listNotification.isOnNext()) { listNotification -> {
if (listNotification.getValue() != null) { if (listNotification.isOnNext()) {
handleSuggestions(listNotification.getValue()); if (listNotification.getValue() != null) {
} handleSuggestions(listNotification.getValue());
} else if (listNotification.isOnError() }
&& listNotification.getError() != null } else if (listNotification.isOnError()
&& !ExceptionUtils.isInterruptedCaused(listNotification.getError())) { && listNotification.getError() != null
showSnackBarError(new ErrorInfo(listNotification.getError(), && !ExceptionUtils.isInterruptedCaused(
UserAction.GET_SUGGESTIONS, searchString, serviceId)); listNotification.getError())) {
} showSnackBarError(new ErrorInfo(listNotification.getError(),
}); UserAction.GET_SUGGESTIONS, searchString, serviceId));
}
}, throwable -> showSnackBarError(new ErrorInfo(
throwable, UserAction.GET_SUGGESTIONS, searchString, serviceId)));
} }
@Override @Override

View File

@ -238,7 +238,7 @@ public final class Player implements
//////////////////////////////////////////////////////////////////////////*/ //////////////////////////////////////////////////////////////////////////*/
public static final int PLAY_PREV_ACTIVATION_LIMIT_MILLIS = 5000; // 5 seconds public static final int PLAY_PREV_ACTIVATION_LIMIT_MILLIS = 5000; // 5 seconds
public static final int PROGRESS_LOOP_INTERVAL_MILLIS = 500; // 500 millis public static final int PROGRESS_LOOP_INTERVAL_MILLIS = 1000; // 1 second
public static final int DEFAULT_CONTROLS_DURATION = 300; // 300 millis public static final int DEFAULT_CONTROLS_DURATION = 300; // 300 millis
public static final int DEFAULT_CONTROLS_HIDE_TIME = 2000; // 2 Seconds public static final int DEFAULT_CONTROLS_HIDE_TIME = 2000; // 2 Seconds
public static final int DPAD_CONTROLS_HIDE_TIME = 7000; // 7 Seconds public static final int DPAD_CONTROLS_HIDE_TIME = 7000; // 7 Seconds
@ -611,7 +611,6 @@ public final class Player implements
// Resolve enqueue intents // Resolve enqueue intents
if (intent.getBooleanExtra(ENQUEUE, false) && playQueue != null) { if (intent.getBooleanExtra(ENQUEUE, false) && playQueue != null) {
playQueue.append(newQueue.getStreams()); playQueue.append(newQueue.getStreams());
return; return;
// Resolve enqueue next intents // Resolve enqueue next intents
@ -619,7 +618,6 @@ public final class Player implements
final int currentIndex = playQueue.getIndex(); final int currentIndex = playQueue.getIndex();
playQueue.append(newQueue.getStreams()); playQueue.append(newQueue.getStreams());
playQueue.move(playQueue.size() - 1, currentIndex + 1); playQueue.move(playQueue.size() - 1, currentIndex + 1);
return; return;
} }
@ -2328,7 +2326,7 @@ public final class Player implements
Log.d(TAG, "ExoPlayer - onRepeatModeChanged() called with: " Log.d(TAG, "ExoPlayer - onRepeatModeChanged() called with: "
+ "repeatMode = [" + repeatMode + "]"); + "repeatMode = [" + repeatMode + "]");
} }
setRepeatModeButton(((AppCompatImageButton) binding.repeatButton), repeatMode); setRepeatModeButton(binding.repeatButton, repeatMode);
onShuffleOrRepeatModeChanged(); onShuffleOrRepeatModeChanged();
} }
@ -3191,7 +3189,7 @@ public final class Player implements
private StreamSegmentAdapter.StreamSegmentListener getStreamSegmentListener() { private StreamSegmentAdapter.StreamSegmentListener getStreamSegmentListener() {
return (item, seconds) -> { return (item, seconds) -> {
segmentAdapter.selectSegment(item); segmentAdapter.selectSegment(item);
seekTo(seconds * 1000); seekTo(seconds * 1000L);
triggerProgressUpdate(); triggerProgressUpdate();
}; };
} }
@ -3201,7 +3199,7 @@ public final class Player implements
final List<StreamSegment> segments = currentMetadata.getMetadata().getStreamSegments(); final List<StreamSegment> segments = currentMetadata.getMetadata().getStreamSegments();
for (int i = 0; i < segments.size(); i++) { for (int i = 0; i < segments.size(); i++) {
if (segments.get(i).getStartTimeSeconds() * 1000 > playbackPosition) { if (segments.get(i).getStartTimeSeconds() * 1000L > playbackPosition) {
break; break;
} }
nearestPosition++; nearestPosition++;

View File

@ -60,6 +60,8 @@ import java.util.ArrayList;
import static org.schabi.newpipe.util.external_communication.ShareUtils.installApp; import static org.schabi.newpipe.util.external_communication.ShareUtils.installApp;
import com.jakewharton.processphoenix.ProcessPhoenix;
public final class NavigationHelper { public final class NavigationHelper {
public static final String MAIN_FRAGMENT_TAG = "main_fragment_tag"; public static final String MAIN_FRAGMENT_TAG = "main_fragment_tag";
public static final String SEARCH_FRAGMENT_TAG = "search_fragment_tag"; public static final String SEARCH_FRAGMENT_TAG = "search_fragment_tag";
@ -348,7 +350,7 @@ public final class NavigationHelper {
autoPlay = false; autoPlay = false;
} }
final RunnableWithVideoDetailFragment onVideoDetailFragmentReady = (detailFragment) -> { final RunnableWithVideoDetailFragment onVideoDetailFragmentReady = detailFragment -> {
expandMainPlayer(detailFragment.requireActivity()); expandMainPlayer(detailFragment.requireActivity());
detailFragment.setAutoPlay(autoPlay); detailFragment.setAutoPlay(autoPlay);
if (switchingPlayers) { if (switchingPlayers) {
@ -595,8 +597,7 @@ public final class NavigationHelper {
*/ */
public static void restartApp(final Activity activity) { public static void restartApp(final Activity activity) {
NewPipeDatabase.close(); NewPipeDatabase.close();
activity.finishAffinity();
final Intent intent = new Intent(activity, MainActivity.class); ProcessPhoenix.triggerRebirth(activity.getApplicationContext());
activity.startActivity(intent);
} }
} }

View File

@ -119,7 +119,7 @@ public final class PermissionHelper {
public static boolean isPopupEnabled(final Context context) { public static boolean isPopupEnabled(final Context context) {
return Build.VERSION.SDK_INT < Build.VERSION_CODES.M return Build.VERSION.SDK_INT < Build.VERSION_CODES.M
|| PermissionHelper.checkSystemAlertWindowPermission(context); || checkSystemAlertWindowPermission(context);
} }
public static void showPopupEnablementToast(final Context context) { public static void showPopupEnablementToast(final Context context) {

View File

@ -1,9 +1,14 @@
package org.schabi.newpipe.util.external_communication; package org.schabi.newpipe.util.external_communication;
import android.content.Context; import android.content.Context;
import android.util.Log;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import androidx.appcompat.app.AlertDialog;
import org.schabi.newpipe.MainActivity;
import org.schabi.newpipe.R;
import org.schabi.newpipe.error.ErrorPanelHelper;
import org.schabi.newpipe.extractor.NewPipe; import org.schabi.newpipe.extractor.NewPipe;
import org.schabi.newpipe.extractor.StreamingService; import org.schabi.newpipe.extractor.StreamingService;
import org.schabi.newpipe.extractor.exceptions.ExtractionException; import org.schabi.newpipe.extractor.exceptions.ExtractionException;
@ -24,6 +29,9 @@ import io.reactivex.rxjava3.disposables.CompositeDisposable;
import io.reactivex.rxjava3.schedulers.Schedulers; import io.reactivex.rxjava3.schedulers.Schedulers;
public final class InternalUrlsHandler { public final class InternalUrlsHandler {
private static final String TAG = InternalUrlsHandler.class.getSimpleName();
private static final boolean DEBUG = MainActivity.DEBUG;
private static final Pattern AMPERSAND_TIMESTAMP_PATTERN = Pattern.compile("(.*)&t=(\\d+)"); private static final Pattern AMPERSAND_TIMESTAMP_PATTERN = Pattern.compile("(.*)&t=(\\d+)");
private static final Pattern HASHTAG_TIMESTAMP_PATTERN = private static final Pattern HASHTAG_TIMESTAMP_PATTERN =
Pattern.compile("(.*)#timestamp=(\\d+)"); Pattern.compile("(.*)#timestamp=(\\d+)");
@ -93,7 +101,12 @@ public final class InternalUrlsHandler {
return false; return false;
} }
final String matchedUrl = matcher.group(1); final String matchedUrl = matcher.group(1);
final int seconds = Integer.parseInt(matcher.group(2)); final int seconds;
if (matcher.group(2) == null) {
seconds = -1;
} else {
seconds = Integer.parseInt(matcher.group(2));
}
final StreamingService service; final StreamingService service;
final StreamingService.LinkType linkType; final StreamingService.LinkType linkType;
@ -146,8 +159,18 @@ public final class InternalUrlsHandler {
.observeOn(AndroidSchedulers.mainThread()) .observeOn(AndroidSchedulers.mainThread())
.subscribe(info -> { .subscribe(info -> {
final PlayQueue playQueue final PlayQueue playQueue
= new SinglePlayQueue(info, seconds * 1000); = new SinglePlayQueue(info, seconds * 1000L);
NavigationHelper.playOnPopupPlayer(context, playQueue, false); NavigationHelper.playOnPopupPlayer(context, playQueue, false);
}, throwable -> {
if (DEBUG) {
Log.e(TAG, "Could not play on popup: " + url, throwable);
}
new AlertDialog.Builder(context)
.setTitle(R.string.player_stream_failure)
.setMessage(
ErrorPanelHelper.Companion.getExceptionDescription(throwable))
.setPositiveButton(R.string.ok, (v, b) -> { })
.show();
})); }));
return true; return true;
} }

View File

@ -0,0 +1,9 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<!-- Opening Theme -->
<style name="Base.V21.OpeningTheme" parent="Base.V19.OpeningTheme">
<item name="android:navigationBarColor">@color/dark_youtube_primary_color</item>
</style>
</resources>