mirror of
				https://github.com/TeamNewPipe/NewPipe
				synced 2025-11-04 09:13:00 +00:00 
			
		
		
		
	Merge branch 'master' into dev
This commit is contained in:
		@@ -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"
 | 
			
		||||
 
 | 
			
		||||
@@ -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);
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -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
 | 
			
		||||
 
 | 
			
		||||
@@ -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
 | 
			
		||||
 
 | 
			
		||||
@@ -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
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -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);
 | 
			
		||||
 
 | 
			
		||||
@@ -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);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 
 | 
			
		||||
@@ -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
 | 
			
		||||
 
 | 
			
		||||
@@ -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++;
 | 
			
		||||
 
 | 
			
		||||
@@ -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());
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -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) {
 | 
			
		||||
 
 | 
			
		||||
@@ -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;
 | 
			
		||||
    }
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										9
									
								
								app/src/main/res/values-night-v21/styles.xml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										9
									
								
								app/src/main/res/values-night-v21/styles.xml
									
									
									
									
									
										Normal 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>
 | 
			
		||||
		Reference in New Issue
	
	Block a user