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"
minSdkVersion 19
targetSdkVersion 29
versionCode 975
versionName "0.21.9"
versionCode 976
versionName "0.21.10"
multiDexEnabled true
@ -189,7 +189,7 @@ dependencies {
// name and the commit hash with the commit hash of the (pushed) commit you want to test
// This works thanks to JitPack: https://jitpack.io/
implementation 'com.github.TeamNewPipe:nanojson:1d9e1aea9049fc9f85e68b43ba39fe7be1c1f751'
implementation 'com.github.TeamNewPipe:NewPipeExtractor:62b87552f51022be76804f3bed65447aeb9fce9b'
implementation 'com.github.TeamNewPipe:NewPipeExtractor:v0.21.10'
/** Checkstyle **/
checkstyle "com.puppycrawl.tools:checkstyle:${checkstyleVersion}"
@ -259,6 +259,9 @@ dependencies {
// Crash reporting
implementation "ch.acra:acra-core:5.7.0"
// Properly restarting
implementation 'com.jakewharton:process-phoenix:2.1.2'
// Reactive extensions for Java VM
implementation "io.reactivex.rxjava3:rxjava:3.0.7"
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.preference.PreferenceManager;
import com.jakewharton.processphoenix.ProcessPhoenix;
import org.acra.ACRA;
import org.acra.config.ACRAConfigurationException;
import org.acra.config.CoreConfiguration;
@ -86,6 +88,12 @@ public class App extends MultiDexApplication {
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
NewPipeSettings.initSettings(this);

View File

@ -16,7 +16,7 @@ import leakcanary.AppWatcher;
public abstract class BaseFragment extends Fragment {
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;
//These values are used for controlling fragments when they are part of the frontpage
@State

View File

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

View File

@ -121,27 +121,14 @@ class ErrorPanelHelper(
ErrorActivity.reportError(context, errorInfo)
}
errorTextView.setText(
when (errorInfo.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
errorRetryButton.isVisible = true
if (errorInfo.throwable != null && errorInfo.throwable!!.isNetworkRelated) {
R.string.network_error
} else {
R.string.error_snackbar_message
}
}
}
)
errorTextView.setText(getExceptionDescription(errorInfo.throwable))
if (errorInfo.throwable !is ContentNotAvailableException &&
errorInfo.throwable !is ContentNotSupportedException
) {
// show retry button only for content which is not unavailable or unsupported
errorRetryButton.isVisible = true
}
}
setRootVisible()
@ -189,5 +176,27 @@ class ErrorPanelHelper(
companion object {
val TAG: String = ErrorPanelHelper::class.simpleName!!
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;
import android.annotation.SuppressLint;
import android.content.Intent;
import android.content.SharedPreferences;
import android.os.Build;
@ -66,6 +67,7 @@ public class ReCaptchaActivity extends AppCompatActivity {
private ActivityRecaptchaBinding recaptchaBinding;
private String foundCookies = "";
@SuppressLint("SetJavaScriptEnabled")
@Override
protected void onCreate(final Bundle savedInstanceState) {
ThemeHelper.setTheme(this);

View File

@ -424,7 +424,7 @@ public final class VideoDetailFragment
showRelatedItems = sharedPreferences.getBoolean(key, true);
tabSettingsChanged = true;
} else if (key.equals(getString(R.string.show_description_key))) {
showComments = sharedPreferences.getBoolean(key, true);
showDescription = sharedPreferences.getBoolean(key, true);
tabSettingsChanged = true;
}
}
@ -743,20 +743,19 @@ public final class VideoDetailFragment
&& player.getPlayQueue() != null
&& player.videoPlayerSelected()
&& 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,
// return false to let the MainActivity handle the onBack
if (stack.size() <= 1) {
restoreDefaultOrientation();
return false;
return false; // let MainActivity handle the onBack (e.g. to minimize the mini player)
}
// Remove top
stack.pop();
// Get stack item from the new top
assert stack.peek() != null;
setupFromHistoryItem(stack.peek());
setupFromHistoryItem(Objects.requireNonNull(stack.peek()));
return true;
}
@ -1433,17 +1432,15 @@ public final class VideoDetailFragment
//////////////////////////////////////////////////////////////////////////*/
private void restoreDefaultOrientation() {
if (!isPlayerAvailable() || !player.videoPlayerSelected() || activity == null) {
return;
if (isPlayerAvailable() && player.videoPlayerSelected()) {
toggleFullscreenIfInFullscreenMode();
}
toggleFullscreenIfInFullscreenMode();
// This will show systemUI and pause the player.
// 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
// to physically rotate the tablet every time
if (!DeviceUtils.isTablet(activity)) {
if (activity != null && !DeviceUtils.isTablet(activity)) {
activity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED);
}
}

View File

@ -215,6 +215,7 @@ public class SearchFragment extends BaseListFragment<SearchInfo, ListExtractor.I
@Override
public void onViewCreated(@NonNull final View rootView, final Bundle savedInstanceState) {
searchBinding = FragmentSearchBinding.bind(rootView);
super.onViewCreated(rootView, savedInstanceState);
showSearchOnStart();
initSearchListeners();
@ -341,7 +342,6 @@ public class SearchFragment extends BaseListFragment<SearchInfo, ListExtractor.I
@Override
protected void initViews(final View rootView, final Bundle savedInstanceState) {
super.initViews(rootView, savedInstanceState);
searchBinding = FragmentSearchBinding.bind(rootView);
searchBinding.suggestionsList.setAdapter(suggestionListAdapter);
new ItemTouchHelper(new ItemTouchHelper.Callback() {
@ -807,18 +807,21 @@ public class SearchFragment extends BaseListFragment<SearchInfo, ListExtractor.I
})
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(listNotification -> {
if (listNotification.isOnNext()) {
if (listNotification.getValue() != null) {
handleSuggestions(listNotification.getValue());
}
} else if (listNotification.isOnError()
&& listNotification.getError() != null
&& !ExceptionUtils.isInterruptedCaused(listNotification.getError())) {
showSnackBarError(new ErrorInfo(listNotification.getError(),
UserAction.GET_SUGGESTIONS, searchString, serviceId));
}
});
.subscribe(
listNotification -> {
if (listNotification.isOnNext()) {
if (listNotification.getValue() != null) {
handleSuggestions(listNotification.getValue());
}
} else if (listNotification.isOnError()
&& listNotification.getError() != null
&& !ExceptionUtils.isInterruptedCaused(
listNotification.getError())) {
showSnackBarError(new ErrorInfo(listNotification.getError(),
UserAction.GET_SUGGESTIONS, searchString, serviceId));
}
}, throwable -> showSnackBarError(new ErrorInfo(
throwable, UserAction.GET_SUGGESTIONS, searchString, serviceId)));
}
@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 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_HIDE_TIME = 2000; // 2 Seconds
public static final int DPAD_CONTROLS_HIDE_TIME = 7000; // 7 Seconds
@ -611,7 +611,6 @@ public final class Player implements
// Resolve enqueue intents
if (intent.getBooleanExtra(ENQUEUE, false) && playQueue != null) {
playQueue.append(newQueue.getStreams());
return;
// Resolve enqueue next intents
@ -619,7 +618,6 @@ public final class Player implements
final int currentIndex = playQueue.getIndex();
playQueue.append(newQueue.getStreams());
playQueue.move(playQueue.size() - 1, currentIndex + 1);
return;
}
@ -2328,7 +2326,7 @@ public final class Player implements
Log.d(TAG, "ExoPlayer - onRepeatModeChanged() called with: "
+ "repeatMode = [" + repeatMode + "]");
}
setRepeatModeButton(((AppCompatImageButton) binding.repeatButton), repeatMode);
setRepeatModeButton(binding.repeatButton, repeatMode);
onShuffleOrRepeatModeChanged();
}
@ -3191,7 +3189,7 @@ public final class Player implements
private StreamSegmentAdapter.StreamSegmentListener getStreamSegmentListener() {
return (item, seconds) -> {
segmentAdapter.selectSegment(item);
seekTo(seconds * 1000);
seekTo(seconds * 1000L);
triggerProgressUpdate();
};
}
@ -3201,7 +3199,7 @@ public final class Player implements
final List<StreamSegment> segments = currentMetadata.getMetadata().getStreamSegments();
for (int i = 0; i < segments.size(); i++) {
if (segments.get(i).getStartTimeSeconds() * 1000 > playbackPosition) {
if (segments.get(i).getStartTimeSeconds() * 1000L > playbackPosition) {
break;
}
nearestPosition++;

View File

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

View File

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

View File

@ -1,9 +1,14 @@
package org.schabi.newpipe.util.external_communication;
import android.content.Context;
import android.util.Log;
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.StreamingService;
import org.schabi.newpipe.extractor.exceptions.ExtractionException;
@ -24,6 +29,9 @@ import io.reactivex.rxjava3.disposables.CompositeDisposable;
import io.reactivex.rxjava3.schedulers.Schedulers;
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 HASHTAG_TIMESTAMP_PATTERN =
Pattern.compile("(.*)#timestamp=(\\d+)");
@ -93,7 +101,12 @@ public final class InternalUrlsHandler {
return false;
}
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.LinkType linkType;
@ -146,8 +159,18 @@ public final class InternalUrlsHandler {
.observeOn(AndroidSchedulers.mainThread())
.subscribe(info -> {
final PlayQueue playQueue
= new SinglePlayQueue(info, seconds * 1000);
= new SinglePlayQueue(info, seconds * 1000L);
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;
}

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>