mirror of
https://github.com/TeamNewPipe/NewPipe
synced 2024-12-23 08:30:44 +00:00
Merge branch 'master' into dev
This commit is contained in:
commit
7e26748dc4
@ -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"
|
||||||
|
@ -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);
|
||||||
|
|
||||||
|
@ -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
|
||||||
|
@ -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
|
||||||
|
@ -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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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);
|
||||||
|
@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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
|
||||||
|
@ -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++;
|
||||||
|
@ -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);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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) {
|
||||||
|
@ -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;
|
||||||
}
|
}
|
||||||
|
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>
|
Loading…
Reference in New Issue
Block a user