This commit is contained in:
CypherpunkSamurai 2023-03-09 23:41:39 +05:30
parent ac6060fe0d
commit d929aa6652
556 changed files with 15082 additions and 13976 deletions

View File

@ -3,10 +3,13 @@ import com.android.tools.profgen.ArtProfileSerializer
import com.android.tools.profgen.DexFile
plugins {
id 'java'
id 'maven-publish'
id "com.android.application"
id "kotlin-android"
id "kotlin-kapt"
id "kotlin-parcelize"
id("org.openrewrite.rewrite") version("5.37.0")
id "checkstyle"
id "org.sonarqube" version "3.5.0.2730"
}

View File

@ -4,14 +4,17 @@ import android.app.Application;
import android.content.Context;
import android.content.SharedPreferences;
import android.util.Log;
import androidx.annotation.NonNull;
import androidx.core.app.NotificationChannelCompat;
import androidx.core.app.NotificationManagerCompat;
import androidx.preference.PreferenceManager;
import com.jakewharton.processphoenix.ProcessPhoenix;
import io.reactivex.rxjava3.exceptions.CompositeException;
import io.reactivex.rxjava3.exceptions.MissingBackpressureException;
import io.reactivex.rxjava3.exceptions.OnErrorNotImplementedException;
import io.reactivex.rxjava3.exceptions.UndeliverableException;
import io.reactivex.rxjava3.functions.Consumer;
import io.reactivex.rxjava3.plugins.RxJavaPlugins;
import org.acra.ACRA;
import org.acra.config.CoreConfigurationBuilder;
import org.schabi.newpipe.error.ReCaptchaActivity;
@ -30,13 +33,6 @@ import java.net.SocketException;
import java.util.List;
import java.util.Objects;
import io.reactivex.rxjava3.exceptions.CompositeException;
import io.reactivex.rxjava3.exceptions.MissingBackpressureException;
import io.reactivex.rxjava3.exceptions.OnErrorNotImplementedException;
import io.reactivex.rxjava3.exceptions.UndeliverableException;
import io.reactivex.rxjava3.functions.Consumer;
import io.reactivex.rxjava3.plugins.RxJavaPlugins;
/*
* Copyright (C) Hans-Christoph Steiner 2016 <hans@eds.org>
* App.java is part of NewPipe.
@ -87,8 +83,8 @@ public class App extends Application {
NewPipeSettings.initSettings(this);
NewPipe.init(getDownloader(),
Localization.getPreferredLocalization(this),
Localization.getPreferredContentCountry(this));
Localization.getPreferredLocalization(this),
Localization.getPreferredContentCountry(this));
Localization.initPrettyTime(Localization.resolvePrettyTime(getApplicationContext()));
StateSaver.init(this);

View File

@ -4,19 +4,17 @@ import android.content.Context;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;
import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentManager;
import icepick.Icepick;
import icepick.State;
import leakcanary.AppWatcher;
public abstract class BaseFragment extends Fragment {
protected final String TAG = getClass().getSimpleName() + "@" + Integer.toHexString(hashCode());
protected static final boolean DEBUG = MainActivity.DEBUG;
protected final String TAG = getClass().getSimpleName() + "@" + Integer.toHexString(hashCode());
protected AppCompatActivity activity;
//These values are used for controlling fragments when they are part of the frontpage
@State

View File

@ -1,11 +1,12 @@
package org.schabi.newpipe;
import android.content.Context;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.preference.PreferenceManager;
import okhttp3.OkHttpClient;
import okhttp3.RequestBody;
import okhttp3.ResponseBody;
import org.schabi.newpipe.error.ReCaptchaActivity;
import org.schabi.newpipe.extractor.downloader.Downloader;
import org.schabi.newpipe.extractor.downloader.Request;
@ -14,19 +15,11 @@ import org.schabi.newpipe.extractor.exceptions.ReCaptchaException;
import org.schabi.newpipe.util.InfoCache;
import java.io.IOException;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import okhttp3.OkHttpClient;
import okhttp3.RequestBody;
import okhttp3.ResponseBody;
public final class DownloaderImpl extends Downloader {
public static final String USER_AGENT =
"Mozilla/5.0 (Windows NT 10.0; rv:91.0) Gecko/20100101 Firefox/91.0";

View File

@ -4,7 +4,6 @@ import android.annotation.SuppressLint;
import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
import org.schabi.newpipe.util.NavigationHelper;
/*

View File

@ -20,29 +20,17 @@
package org.schabi.newpipe;
import static org.schabi.newpipe.util.Localization.assureCorrectAppLanguage;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.SharedPreferences;
import android.content.*;
import android.content.pm.PackageManager;
import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
import android.util.Log;
import android.view.KeyEvent;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
import android.view.*;
import android.widget.AdapterView;
import android.widget.ArrayAdapter;
import android.widget.FrameLayout;
import android.widget.Spinner;
import androidx.annotation.NonNull;
import androidx.appcompat.app.ActionBar;
import androidx.appcompat.app.ActionBarDrawerToggle;
@ -53,14 +41,8 @@ import androidx.drawerlayout.widget.DrawerLayout;
import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentManager;
import androidx.preference.PreferenceManager;
import com.google.android.material.bottomsheet.BottomSheetBehavior;
import org.schabi.newpipe.databinding.ActivityMainBinding;
import org.schabi.newpipe.databinding.DrawerHeaderBinding;
import org.schabi.newpipe.databinding.DrawerLayoutBinding;
import org.schabi.newpipe.databinding.InstanceSpinnerLayoutBinding;
import org.schabi.newpipe.databinding.ToolbarLayoutBinding;
import org.schabi.newpipe.databinding.*;
import org.schabi.newpipe.error.ErrorUtil;
import org.schabi.newpipe.extractor.NewPipe;
import org.schabi.newpipe.extractor.StreamingService;
@ -75,39 +57,19 @@ import org.schabi.newpipe.player.Player;
import org.schabi.newpipe.player.event.OnKeyDownListener;
import org.schabi.newpipe.player.helper.PlayerHolder;
import org.schabi.newpipe.player.playqueue.PlayQueue;
import org.schabi.newpipe.util.Constants;
import org.schabi.newpipe.util.DeviceUtils;
import org.schabi.newpipe.util.KioskTranslator;
import org.schabi.newpipe.util.Localization;
import org.schabi.newpipe.util.NavigationHelper;
import org.schabi.newpipe.util.PeertubeHelper;
import org.schabi.newpipe.util.PermissionHelper;
import org.schabi.newpipe.util.SerializedCache;
import org.schabi.newpipe.util.ServiceHelper;
import org.schabi.newpipe.util.StateSaver;
import org.schabi.newpipe.util.ThemeHelper;
import org.schabi.newpipe.util.*;
import org.schabi.newpipe.views.FocusOverlayView;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import static org.schabi.newpipe.util.Localization.assureCorrectAppLanguage;
public class MainActivity extends AppCompatActivity {
private static final String TAG = "MainActivity";
@SuppressWarnings("ConstantConditions")
public static final boolean DEBUG = !BuildConfig.BUILD_TYPE.equals("release");
private ActivityMainBinding mainBinding;
private DrawerHeaderBinding drawerHeaderBinding;
private DrawerLayoutBinding drawerLayoutBinding;
private ToolbarLayoutBinding toolbarLayoutBinding;
private ActionBarDrawerToggle toggle;
private boolean servicesShown = false;
private BroadcastReceiver broadcastReceiver;
private static final String TAG = "MainActivity";
private static final int ITEM_ID_SUBSCRIPTIONS = -1;
private static final int ITEM_ID_FEED = -2;
private static final int ITEM_ID_BOOKMARKS = -3;
@ -115,8 +77,14 @@ public class MainActivity extends AppCompatActivity {
private static final int ITEM_ID_HISTORY = -5;
private static final int ITEM_ID_SETTINGS = 0;
private static final int ITEM_ID_ABOUT = 1;
private static final int ORDER = 0;
private ActivityMainBinding mainBinding;
private DrawerHeaderBinding drawerHeaderBinding;
private DrawerLayoutBinding drawerLayoutBinding;
private ToolbarLayoutBinding toolbarLayoutBinding;
private ActionBarDrawerToggle toggle;
private boolean servicesShown = false;
private BroadcastReceiver broadcastReceiver;
/*//////////////////////////////////////////////////////////////////////////
// Activity's LifeCycle

View File

@ -1,20 +1,14 @@
package org.schabi.newpipe;
import static org.schabi.newpipe.database.AppDatabase.DATABASE_NAME;
import static org.schabi.newpipe.database.Migrations.MIGRATION_1_2;
import static org.schabi.newpipe.database.Migrations.MIGRATION_2_3;
import static org.schabi.newpipe.database.Migrations.MIGRATION_3_4;
import static org.schabi.newpipe.database.Migrations.MIGRATION_4_5;
import static org.schabi.newpipe.database.Migrations.MIGRATION_5_6;
import android.content.Context;
import android.database.Cursor;
import androidx.annotation.NonNull;
import androidx.room.Room;
import org.schabi.newpipe.database.AppDatabase;
import static org.schabi.newpipe.database.AppDatabase.DATABASE_NAME;
import static org.schabi.newpipe.database.Migrations.*;
public final class NewPipeDatabase {
private static volatile AppDatabase databaseInstance;

View File

@ -1,15 +1,10 @@
package org.schabi.newpipe;
import static org.schabi.newpipe.util.SparseItemUtil.fetchStreamInfoAndSaveToDatabase;
import static org.schabi.newpipe.util.external_communication.ShareUtils.shareText;
import android.content.Context;
import android.view.ContextThemeWrapper;
import android.view.View;
import android.widget.PopupMenu;
import androidx.fragment.app.FragmentManager;
import org.schabi.newpipe.database.stream.model.StreamEntity;
import org.schabi.newpipe.download.DownloadDialog;
import org.schabi.newpipe.local.dialog.PlaylistDialog;
@ -20,6 +15,9 @@ import org.schabi.newpipe.util.SparseItemUtil;
import java.util.List;
import static org.schabi.newpipe.util.SparseItemUtil.fetchStreamInfoAndSaveToDatabase;
import static org.schabi.newpipe.util.external_communication.ShareUtils.shareText;
public final class QueueItemMenuUtil {
private QueueItemMenuUtil() {
}

View File

@ -1,8 +1,5 @@
package org.schabi.newpipe;
import static org.schabi.newpipe.extractor.StreamingService.ServiceInfo.MediaCapability.AUDIO;
import static org.schabi.newpipe.extractor.StreamingService.ServiceInfo.MediaCapability.VIDEO;
import android.annotation.SuppressLint;
import android.app.IntentService;
import android.content.Context;
@ -13,16 +10,11 @@ import android.content.pm.PackageManager;
import android.os.Build;
import android.os.Bundle;
import android.text.TextUtils;
import android.view.ContextThemeWrapper;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.view.WindowManager;
import android.view.*;
import android.widget.Button;
import android.widget.RadioButton;
import android.widget.RadioGroup;
import android.widget.Toast;
import androidx.annotation.DrawableRes;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
@ -40,7 +32,14 @@ import androidx.lifecycle.DefaultLifecycleObserver;
import androidx.lifecycle.Lifecycle;
import androidx.lifecycle.LifecycleOwner;
import androidx.preference.PreferenceManager;
import icepick.Icepick;
import icepick.State;
import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers;
import io.reactivex.rxjava3.core.Observable;
import io.reactivex.rxjava3.core.Single;
import io.reactivex.rxjava3.disposables.CompositeDisposable;
import io.reactivex.rxjava3.disposables.Disposable;
import io.reactivex.rxjava3.schedulers.Schedulers;
import org.schabi.newpipe.database.stream.model.StreamEntity;
import org.schabi.newpipe.databinding.ListRadioIconItemBinding;
import org.schabi.newpipe.databinding.SingleChoiceDialogViewBinding;
@ -54,16 +53,7 @@ import org.schabi.newpipe.extractor.NewPipe;
import org.schabi.newpipe.extractor.StreamingService;
import org.schabi.newpipe.extractor.StreamingService.LinkType;
import org.schabi.newpipe.extractor.channel.ChannelInfo;
import org.schabi.newpipe.extractor.exceptions.AgeRestrictedContentException;
import org.schabi.newpipe.extractor.exceptions.ContentNotAvailableException;
import org.schabi.newpipe.extractor.exceptions.ContentNotSupportedException;
import org.schabi.newpipe.extractor.exceptions.ExtractionException;
import org.schabi.newpipe.extractor.exceptions.GeographicRestrictionException;
import org.schabi.newpipe.extractor.exceptions.PaidContentException;
import org.schabi.newpipe.extractor.exceptions.PrivateContentException;
import org.schabi.newpipe.extractor.exceptions.ReCaptchaException;
import org.schabi.newpipe.extractor.exceptions.SoundCloudGoPlusContentException;
import org.schabi.newpipe.extractor.exceptions.YoutubeMusicPremiumContentException;
import org.schabi.newpipe.extractor.exceptions.*;
import org.schabi.newpipe.extractor.playlist.PlaylistInfo;
import org.schabi.newpipe.extractor.stream.StreamInfo;
import org.schabi.newpipe.ktx.ExceptionUtils;
@ -75,13 +65,7 @@ import org.schabi.newpipe.player.playqueue.ChannelPlayQueue;
import org.schabi.newpipe.player.playqueue.PlayQueue;
import org.schabi.newpipe.player.playqueue.PlaylistPlayQueue;
import org.schabi.newpipe.player.playqueue.SinglePlayQueue;
import org.schabi.newpipe.util.Constants;
import org.schabi.newpipe.util.DeviceUtils;
import org.schabi.newpipe.util.ExtractorHelper;
import org.schabi.newpipe.util.Localization;
import org.schabi.newpipe.util.NavigationHelper;
import org.schabi.newpipe.util.PermissionHelper;
import org.schabi.newpipe.util.ThemeHelper;
import org.schabi.newpipe.util.*;
import org.schabi.newpipe.util.external_communication.ShareUtils;
import org.schabi.newpipe.util.urlfinder.UrlFinder;
import org.schabi.newpipe.views.FocusOverlayView;
@ -95,14 +79,8 @@ import java.util.List;
import java.util.Optional;
import java.util.function.Consumer;
import icepick.Icepick;
import icepick.State;
import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers;
import io.reactivex.rxjava3.core.Observable;
import io.reactivex.rxjava3.core.Single;
import io.reactivex.rxjava3.disposables.CompositeDisposable;
import io.reactivex.rxjava3.disposables.Disposable;
import io.reactivex.rxjava3.schedulers.Schedulers;
import static org.schabi.newpipe.extractor.StreamingService.ServiceInfo.MediaCapability.AUDIO;
import static org.schabi.newpipe.extractor.StreamingService.ServiceInfo.MediaCapability.VIDEO;
/**
* Get the url from the intent and open it in the chosen preferred player.
@ -123,6 +101,53 @@ public class RouterActivity extends AppCompatActivity {
private AlertDialog alertDialogChoice = null;
private FragmentManager.FragmentLifecycleCallbacks dismissListener = null;
/**
* @param context the context. It will be {@code finish()}ed at the end of the handling if it is
* an instance of {@link RouterActivity}.
* @param errorInfo the error information
*/
private static void handleError(final Context context, final ErrorInfo errorInfo) {
if (errorInfo.getThrowable() != null) {
errorInfo.getThrowable().printStackTrace();
}
if (errorInfo.getThrowable() instanceof ReCaptchaException) {
Toast.makeText(context, R.string.recaptcha_request_toast, Toast.LENGTH_LONG).show();
// Starting ReCaptcha Challenge Activity
final Intent intent = new Intent(context, ReCaptchaActivity.class);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
context.startActivity(intent);
} else if (errorInfo.getThrowable() != null
&& ExceptionUtils.isNetworkRelated(errorInfo.getThrowable())) {
Toast.makeText(context, R.string.network_error, Toast.LENGTH_LONG).show();
} else if (errorInfo.getThrowable() instanceof AgeRestrictedContentException) {
Toast.makeText(context, R.string.restricted_video_no_stream,
Toast.LENGTH_LONG).show();
} else if (errorInfo.getThrowable() instanceof GeographicRestrictionException) {
Toast.makeText(context, R.string.georestricted_content, Toast.LENGTH_LONG).show();
} else if (errorInfo.getThrowable() instanceof PaidContentException) {
Toast.makeText(context, R.string.paid_content, Toast.LENGTH_LONG).show();
} else if (errorInfo.getThrowable() instanceof PrivateContentException) {
Toast.makeText(context, R.string.private_content, Toast.LENGTH_LONG).show();
} else if (errorInfo.getThrowable() instanceof SoundCloudGoPlusContentException) {
Toast.makeText(context, R.string.soundcloud_go_plus_content,
Toast.LENGTH_LONG).show();
} else if (errorInfo.getThrowable() instanceof YoutubeMusicPremiumContentException) {
Toast.makeText(context, R.string.youtube_music_premium_content,
Toast.LENGTH_LONG).show();
} else if (errorInfo.getThrowable() instanceof ContentNotAvailableException) {
Toast.makeText(context, R.string.content_not_available, Toast.LENGTH_LONG).show();
} else if (errorInfo.getThrowable() instanceof ContentNotSupportedException) {
Toast.makeText(context, R.string.content_not_supported, Toast.LENGTH_LONG).show();
} else {
ErrorUtil.createNotification(context, errorInfo);
}
if (context instanceof RouterActivity) {
((RouterActivity) context).finish();
}
}
@Override
protected void onCreate(final Bundle savedInstanceState) {
ThemeHelper.setDayNightMode(this);
@ -261,53 +286,6 @@ public class RouterActivity extends AppCompatActivity {
UserAction.SHARE_TO_NEWPIPE, "Getting service from url: " + url))));
}
/**
* @param context the context. It will be {@code finish()}ed at the end of the handling if it is
* an instance of {@link RouterActivity}.
* @param errorInfo the error information
*/
private static void handleError(final Context context, final ErrorInfo errorInfo) {
if (errorInfo.getThrowable() != null) {
errorInfo.getThrowable().printStackTrace();
}
if (errorInfo.getThrowable() instanceof ReCaptchaException) {
Toast.makeText(context, R.string.recaptcha_request_toast, Toast.LENGTH_LONG).show();
// Starting ReCaptcha Challenge Activity
final Intent intent = new Intent(context, ReCaptchaActivity.class);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
context.startActivity(intent);
} else if (errorInfo.getThrowable() != null
&& ExceptionUtils.isNetworkRelated(errorInfo.getThrowable())) {
Toast.makeText(context, R.string.network_error, Toast.LENGTH_LONG).show();
} else if (errorInfo.getThrowable() instanceof AgeRestrictedContentException) {
Toast.makeText(context, R.string.restricted_video_no_stream,
Toast.LENGTH_LONG).show();
} else if (errorInfo.getThrowable() instanceof GeographicRestrictionException) {
Toast.makeText(context, R.string.georestricted_content, Toast.LENGTH_LONG).show();
} else if (errorInfo.getThrowable() instanceof PaidContentException) {
Toast.makeText(context, R.string.paid_content, Toast.LENGTH_LONG).show();
} else if (errorInfo.getThrowable() instanceof PrivateContentException) {
Toast.makeText(context, R.string.private_content, Toast.LENGTH_LONG).show();
} else if (errorInfo.getThrowable() instanceof SoundCloudGoPlusContentException) {
Toast.makeText(context, R.string.soundcloud_go_plus_content,
Toast.LENGTH_LONG).show();
} else if (errorInfo.getThrowable() instanceof YoutubeMusicPremiumContentException) {
Toast.makeText(context, R.string.youtube_music_premium_content,
Toast.LENGTH_LONG).show();
} else if (errorInfo.getThrowable() instanceof ContentNotAvailableException) {
Toast.makeText(context, R.string.content_not_available, Toast.LENGTH_LONG).show();
} else if (errorInfo.getThrowable() instanceof ContentNotSupportedException) {
Toast.makeText(context, R.string.content_not_supported, Toast.LENGTH_LONG).show();
} else {
ErrorUtil.createNotification(context, errorInfo);
}
if (context instanceof RouterActivity) {
((RouterActivity) context).finish();
}
}
protected void showUnsupportedUrlDialog(final String url) {
final Context context = getThemeWrapperContext();
new AlertDialog.Builder(context)
@ -396,43 +374,6 @@ public class RouterActivity extends AppCompatActivity {
}
}
/**
* This is a helper class for checking if the choices are available and/or selected.
*/
class ChoiceAvailabilityChecker {
private final List<AdapterChoiceItem> availableChoices;
private final String selectedChoiceKey;
ChoiceAvailabilityChecker(
@NonNull final List<AdapterChoiceItem> availableChoices,
@NonNull final String selectedChoiceKey) {
this.availableChoices = availableChoices;
this.selectedChoiceKey = selectedChoiceKey;
}
public List<AdapterChoiceItem> getAvailableChoices() {
return availableChoices;
}
public String getSelectedChoiceKey() {
return selectedChoiceKey;
}
public boolean isAvailableAndSelected(@StringRes final int... wantedKeys) {
return Arrays.stream(wantedKeys).anyMatch(this::isAvailableAndSelected);
}
public boolean isAvailableAndSelected(@StringRes final int wantedKey) {
final String wanted = getString(wantedKey);
// Check if the wanted option is selected
if (!selectedChoiceKey.equals(wanted)) {
return false;
}
// Check if it's available
return availableChoices.stream().anyMatch(item -> wanted.equals(item.key));
}
}
private void showDialog(final List<AdapterChoiceItem> choices) {
final SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(this);
@ -701,9 +642,61 @@ public class RouterActivity extends AppCompatActivity {
return playerType == null || playerType == PlayerType.MAIN;
}
private void openAddToPlaylistDialog() {
getPersistFragment().openAddToPlaylistDialog(currentServiceId, currentUrl);
}
private void openDownloadDialog() {
getPersistFragment().openDownloadDialog(currentServiceId, currentUrl);
}
private PersistentFragment getPersistFragment() {
final FragmentManager fm = getSupportFragmentManager();
PersistentFragment persistFragment =
(PersistentFragment) fm.findFragmentByTag("PERSIST_FRAGMENT");
if (persistFragment == null) {
persistFragment = new PersistentFragment();
fm.beginTransaction()
.add(persistFragment, "PERSIST_FRAGMENT")
.commitNow();
}
return persistFragment;
}
@Override
public void onRequestPermissionsResult(final int requestCode,
@NonNull final String[] permissions,
@NonNull final int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
for (final int i : grantResults) {
if (i == PackageManager.PERMISSION_DENIED) {
finish();
return;
}
}
if (requestCode == PermissionHelper.DOWNLOAD_DIALOG_REQUEST_CODE) {
openDownloadDialog();
}
}
@Nullable
private String getUrl(final Intent intent) {
String foundUrl = null;
if (intent.getData() != null) {
// Called from another app
foundUrl = intent.getData().toString();
} else if (intent.getStringExtra(Intent.EXTRA_TEXT) != null) {
// Called from the share menu
final String extraText = intent.getStringExtra(Intent.EXTRA_TEXT);
foundUrl = UrlFinder.firstUrlFromInput(extraText);
}
return foundUrl;
}
public static class PersistentFragment extends Fragment {
private WeakReference<AppCompatActivity> weakContext;
private final CompositeDisposable disposables = new CompositeDisposable();
private WeakReference<AppCompatActivity> weakContext;
private int running = 0;
private synchronized void inFlight(final boolean started) {
@ -770,10 +763,10 @@ public class RouterActivity extends AppCompatActivity {
public void onResume(@NonNull final LifecycleOwner owner) {
getLifecycle().removeObserver(this);
getActivityContext().ifPresentOrElse(context ->
context.runOnUiThread(() -> {
runnable.accept(context);
inFlight(false);
}),
context.runOnUiThread(() -> {
runnable.accept(context);
inFlight(false);
}),
() -> inFlight(false)
);
}
@ -806,7 +799,7 @@ public class RouterActivity extends AppCompatActivity {
Toast.LENGTH_LONG);
toast.show();
emitter.setCancellable(toast::cancel);
}))));
}))));
}
@SuppressLint("CheckResult")
@ -817,13 +810,13 @@ public class RouterActivity extends AppCompatActivity {
.observeOn(AndroidSchedulers.mainThread())
.compose(this::pleaseWait)
.subscribe(result ->
runOnVisible(ctx -> {
final FragmentManager fm = ctx.getSupportFragmentManager();
final DownloadDialog downloadDialog = new DownloadDialog(ctx, result);
// dismiss listener to be handled by FragmentManager
downloadDialog.show(fm, "downloadDialog");
}
), throwable -> runOnVisible(ctx ->
runOnVisible(ctx -> {
final FragmentManager fm = ctx.getSupportFragmentManager();
final DownloadDialog downloadDialog = new DownloadDialog(ctx, result);
// dismiss listener to be handled by FragmentManager
downloadDialog.show(fm, "downloadDialog");
}
), throwable -> runOnVisible(ctx ->
((RouterActivity) ctx).showUnsupportedUrlDialog(currentUrl))));
}
@ -855,43 +848,6 @@ public class RouterActivity extends AppCompatActivity {
}
}
private void openAddToPlaylistDialog() {
getPersistFragment().openAddToPlaylistDialog(currentServiceId, currentUrl);
}
private void openDownloadDialog() {
getPersistFragment().openDownloadDialog(currentServiceId, currentUrl);
}
private PersistentFragment getPersistFragment() {
final FragmentManager fm = getSupportFragmentManager();
PersistentFragment persistFragment =
(PersistentFragment) fm.findFragmentByTag("PERSIST_FRAGMENT");
if (persistFragment == null) {
persistFragment = new PersistentFragment();
fm.beginTransaction()
.add(persistFragment, "PERSIST_FRAGMENT")
.commitNow();
}
return persistFragment;
}
@Override
public void onRequestPermissionsResult(final int requestCode,
@NonNull final String[] permissions,
@NonNull final int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
for (final int i : grantResults) {
if (i == PackageManager.PERMISSION_DENIED) {
finish();
return;
}
}
if (requestCode == PermissionHelper.DOWNLOAD_DIALOG_REQUEST_CODE) {
openDownloadDialog();
}
}
private static class AdapterChoiceItem {
final String description;
final String key;
@ -949,10 +905,9 @@ public class RouterActivity extends AppCompatActivity {
}
final Serializable serializable = intent.getSerializableExtra(KEY_CHOICE);
if (!(serializable instanceof Choice)) {
if (!(serializable instanceof final Choice playerChoice)) {
return;
}
final Choice playerChoice = (Choice) serializable;
handleChoice(playerChoice);
}
@ -1058,18 +1013,40 @@ public class RouterActivity extends AppCompatActivity {
// Utils
//////////////////////////////////////////////////////////////////////////*/
@Nullable
private String getUrl(final Intent intent) {
String foundUrl = null;
if (intent.getData() != null) {
// Called from another app
foundUrl = intent.getData().toString();
} else if (intent.getStringExtra(Intent.EXTRA_TEXT) != null) {
// Called from the share menu
final String extraText = intent.getStringExtra(Intent.EXTRA_TEXT);
foundUrl = UrlFinder.firstUrlFromInput(extraText);
/**
* This is a helper class for checking if the choices are available and/or selected.
*/
class ChoiceAvailabilityChecker {
private final List<AdapterChoiceItem> availableChoices;
private final String selectedChoiceKey;
ChoiceAvailabilityChecker(
@NonNull final List<AdapterChoiceItem> availableChoices,
@NonNull final String selectedChoiceKey) {
this.availableChoices = availableChoices;
this.selectedChoiceKey = selectedChoiceKey;
}
return foundUrl;
public List<AdapterChoiceItem> getAvailableChoices() {
return availableChoices;
}
public String getSelectedChoiceKey() {
return selectedChoiceKey;
}
public boolean isAvailableAndSelected(@StringRes final int... wantedKeys) {
return Arrays.stream(wantedKeys).anyMatch(this::isAvailableAndSelected);
}
public boolean isAvailableAndSelected(@StringRes final int wantedKey) {
final String wanted = getString(wantedKey);
// Check if the wanted option is selected
if (!selectedChoiceKey.equals(wanted)) {
return false;
}
// Check if it's available
return availableChoices.stream().anyMatch(item -> wanted.equals(item.key));
}
}
}

View File

@ -1,11 +1,8 @@
package org.schabi.newpipe.database;
import static org.schabi.newpipe.database.Migrations.DB_VER_6;
import androidx.room.Database;
import androidx.room.RoomDatabase;
import androidx.room.TypeConverters;
import org.schabi.newpipe.database.feed.dao.FeedDAO;
import org.schabi.newpipe.database.feed.dao.FeedGroupDAO;
import org.schabi.newpipe.database.feed.model.FeedEntity;
@ -29,6 +26,8 @@ import org.schabi.newpipe.database.stream.model.StreamStateEntity;
import org.schabi.newpipe.database.subscription.SubscriptionDAO;
import org.schabi.newpipe.database.subscription.SubscriptionEntity;
import static org.schabi.newpipe.database.Migrations.DB_VER_6;
@TypeConverters({Converters.class})
@Database(
entities = {

View File

@ -4,12 +4,11 @@ import androidx.room.Dao;
import androidx.room.Delete;
import androidx.room.Insert;
import androidx.room.Update;
import io.reactivex.rxjava3.core.Flowable;
import java.util.Collection;
import java.util.List;
import io.reactivex.rxjava3.core.Flowable;
@Dao
public interface BasicDAO<Entity> {
/* Inserts */

View File

@ -1,11 +1,9 @@
package org.schabi.newpipe.database;
import android.util.Log;
import androidx.annotation.NonNull;
import androidx.room.migration.Migration;
import androidx.sqlite.db.SupportSQLiteDatabase;
import org.schabi.newpipe.MainActivity;
public final class Migrations {
@ -24,10 +22,86 @@ public final class Migrations {
public static final int DB_VER_4 = 4;
public static final int DB_VER_5 = 5;
public static final int DB_VER_6 = 6;
private static final String TAG = Migrations.class.getName();
public static final boolean DEBUG = MainActivity.DEBUG;
public static final Migration MIGRATION_2_3 = new Migration(DB_VER_2, DB_VER_3) {
@Override
public void migrate(@NonNull final SupportSQLiteDatabase database) {
// Add NOT NULLs and new fields
database.execSQL("CREATE TABLE IF NOT EXISTS streams_new "
+ "(uid INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, "
+ "service_id INTEGER NOT NULL, url TEXT NOT NULL, title TEXT NOT NULL, "
+ "stream_type TEXT NOT NULL, duration INTEGER NOT NULL, "
+ "uploader TEXT NOT NULL, thumbnail_url TEXT, view_count INTEGER, "
+ "textual_upload_date TEXT, upload_date INTEGER, "
+ "is_upload_date_approximation INTEGER)");
database.execSQL("INSERT INTO streams_new (uid, service_id, url, title, stream_type, "
+ "duration, uploader, thumbnail_url, view_count, textual_upload_date, "
+ "upload_date, is_upload_date_approximation) "
+ "SELECT uid, service_id, url, ifnull(title, ''), "
+ "ifnull(stream_type, 'VIDEO_STREAM'), ifnull(duration, 0), "
+ "ifnull(uploader, ''), ifnull(thumbnail_url, ''), NULL, NULL, NULL, NULL "
+ "FROM streams WHERE url IS NOT NULL");
database.execSQL("DROP TABLE streams");
database.execSQL("ALTER TABLE streams_new RENAME TO streams");
database.execSQL("CREATE UNIQUE INDEX index_streams_service_id_url "
+ "ON streams (service_id, url)");
// Tables for feed feature
database.execSQL("CREATE TABLE IF NOT EXISTS feed "
+ "(stream_id INTEGER NOT NULL, subscription_id INTEGER NOT NULL, "
+ "PRIMARY KEY(stream_id, subscription_id), "
+ "FOREIGN KEY(stream_id) REFERENCES streams(uid) "
+ "ON UPDATE CASCADE ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED, "
+ "FOREIGN KEY(subscription_id) REFERENCES subscriptions(uid) "
+ "ON UPDATE CASCADE ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED)");
database.execSQL("CREATE INDEX index_feed_subscription_id ON feed (subscription_id)");
database.execSQL("CREATE TABLE IF NOT EXISTS feed_group "
+ "(uid INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, name TEXT NOT NULL, "
+ "icon_id INTEGER NOT NULL, sort_order INTEGER NOT NULL)");
database.execSQL("CREATE INDEX index_feed_group_sort_order ON feed_group (sort_order)");
database.execSQL("CREATE TABLE IF NOT EXISTS feed_group_subscription_join "
+ "(group_id INTEGER NOT NULL, subscription_id INTEGER NOT NULL, "
+ "PRIMARY KEY(group_id, subscription_id), "
+ "FOREIGN KEY(group_id) REFERENCES feed_group(uid) "
+ "ON UPDATE CASCADE ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED, "
+ "FOREIGN KEY(subscription_id) REFERENCES subscriptions(uid) "
+ "ON UPDATE CASCADE ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED)");
database.execSQL("CREATE INDEX index_feed_group_subscription_join_subscription_id "
+ "ON feed_group_subscription_join (subscription_id)");
database.execSQL("CREATE TABLE IF NOT EXISTS feed_last_updated "
+ "(subscription_id INTEGER NOT NULL, last_updated INTEGER, "
+ "PRIMARY KEY(subscription_id), "
+ "FOREIGN KEY(subscription_id) REFERENCES subscriptions(uid) "
+ "ON UPDATE CASCADE ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED)");
}
};
public static final Migration MIGRATION_3_4 = new Migration(DB_VER_3, DB_VER_4) {
@Override
public void migrate(@NonNull final SupportSQLiteDatabase database) {
database.execSQL(
"ALTER TABLE streams ADD COLUMN uploader_url TEXT"
);
}
};
public static final Migration MIGRATION_4_5 = new Migration(DB_VER_4, DB_VER_5) {
@Override
public void migrate(@NonNull final SupportSQLiteDatabase database) {
database.execSQL("ALTER TABLE `subscriptions` ADD COLUMN `notification_mode` "
+ "INTEGER NOT NULL DEFAULT 0");
}
};
public static final Migration MIGRATION_5_6 = new Migration(DB_VER_5, DB_VER_6) {
@Override
public void migrate(@NonNull final SupportSQLiteDatabase database) {
database.execSQL("ALTER TABLE `playlists` ADD COLUMN `is_thumbnail_permanent` "
+ "INTEGER NOT NULL DEFAULT 0");
}
};
private static final String TAG = Migrations.class.getName();
public static final Migration MIGRATION_1_2 = new Migration(DB_VER_1, DB_VER_2) {
@Override
public void migrate(@NonNull final SupportSQLiteDatabase database) {
@ -115,88 +189,6 @@ public final class Migrations {
}
};
public static final Migration MIGRATION_2_3 = new Migration(DB_VER_2, DB_VER_3) {
@Override
public void migrate(@NonNull final SupportSQLiteDatabase database) {
// Add NOT NULLs and new fields
database.execSQL("CREATE TABLE IF NOT EXISTS streams_new "
+ "(uid INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, "
+ "service_id INTEGER NOT NULL, url TEXT NOT NULL, title TEXT NOT NULL, "
+ "stream_type TEXT NOT NULL, duration INTEGER NOT NULL, "
+ "uploader TEXT NOT NULL, thumbnail_url TEXT, view_count INTEGER, "
+ "textual_upload_date TEXT, upload_date INTEGER, "
+ "is_upload_date_approximation INTEGER)");
database.execSQL("INSERT INTO streams_new (uid, service_id, url, title, stream_type, "
+ "duration, uploader, thumbnail_url, view_count, textual_upload_date, "
+ "upload_date, is_upload_date_approximation) "
+ "SELECT uid, service_id, url, ifnull(title, ''), "
+ "ifnull(stream_type, 'VIDEO_STREAM'), ifnull(duration, 0), "
+ "ifnull(uploader, ''), ifnull(thumbnail_url, ''), NULL, NULL, NULL, NULL "
+ "FROM streams WHERE url IS NOT NULL");
database.execSQL("DROP TABLE streams");
database.execSQL("ALTER TABLE streams_new RENAME TO streams");
database.execSQL("CREATE UNIQUE INDEX index_streams_service_id_url "
+ "ON streams (service_id, url)");
// Tables for feed feature
database.execSQL("CREATE TABLE IF NOT EXISTS feed "
+ "(stream_id INTEGER NOT NULL, subscription_id INTEGER NOT NULL, "
+ "PRIMARY KEY(stream_id, subscription_id), "
+ "FOREIGN KEY(stream_id) REFERENCES streams(uid) "
+ "ON UPDATE CASCADE ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED, "
+ "FOREIGN KEY(subscription_id) REFERENCES subscriptions(uid) "
+ "ON UPDATE CASCADE ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED)");
database.execSQL("CREATE INDEX index_feed_subscription_id ON feed (subscription_id)");
database.execSQL("CREATE TABLE IF NOT EXISTS feed_group "
+ "(uid INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, name TEXT NOT NULL, "
+ "icon_id INTEGER NOT NULL, sort_order INTEGER NOT NULL)");
database.execSQL("CREATE INDEX index_feed_group_sort_order ON feed_group (sort_order)");
database.execSQL("CREATE TABLE IF NOT EXISTS feed_group_subscription_join "
+ "(group_id INTEGER NOT NULL, subscription_id INTEGER NOT NULL, "
+ "PRIMARY KEY(group_id, subscription_id), "
+ "FOREIGN KEY(group_id) REFERENCES feed_group(uid) "
+ "ON UPDATE CASCADE ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED, "
+ "FOREIGN KEY(subscription_id) REFERENCES subscriptions(uid) "
+ "ON UPDATE CASCADE ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED)");
database.execSQL("CREATE INDEX index_feed_group_subscription_join_subscription_id "
+ "ON feed_group_subscription_join (subscription_id)");
database.execSQL("CREATE TABLE IF NOT EXISTS feed_last_updated "
+ "(subscription_id INTEGER NOT NULL, last_updated INTEGER, "
+ "PRIMARY KEY(subscription_id), "
+ "FOREIGN KEY(subscription_id) REFERENCES subscriptions(uid) "
+ "ON UPDATE CASCADE ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED)");
}
};
public static final Migration MIGRATION_3_4 = new Migration(DB_VER_3, DB_VER_4) {
@Override
public void migrate(@NonNull final SupportSQLiteDatabase database) {
database.execSQL(
"ALTER TABLE streams ADD COLUMN uploader_url TEXT"
);
}
};
public static final Migration MIGRATION_4_5 = new Migration(DB_VER_4, DB_VER_5) {
@Override
public void migrate(@NonNull final SupportSQLiteDatabase database) {
database.execSQL("ALTER TABLE `subscriptions` ADD COLUMN `notification_mode` "
+ "INTEGER NOT NULL DEFAULT 0");
}
};
public static final Migration MIGRATION_5_6 = new Migration(DB_VER_5, DB_VER_6) {
@Override
public void migrate(@NonNull final SupportSQLiteDatabase database) {
database.execSQL("ALTER TABLE `playlists` ADD COLUMN `is_thumbnail_permanent` "
+ "INTEGER NOT NULL DEFAULT 0");
}
};
private Migrations() {
}
}

View File

@ -3,18 +3,12 @@ package org.schabi.newpipe.database.history.dao;
import androidx.annotation.Nullable;
import androidx.room.Dao;
import androidx.room.Query;
import io.reactivex.rxjava3.core.Flowable;
import org.schabi.newpipe.database.history.model.SearchHistoryEntry;
import java.util.List;
import io.reactivex.rxjava3.core.Flowable;
import static org.schabi.newpipe.database.history.model.SearchHistoryEntry.CREATION_DATE;
import static org.schabi.newpipe.database.history.model.SearchHistoryEntry.ID;
import static org.schabi.newpipe.database.history.model.SearchHistoryEntry.SEARCH;
import static org.schabi.newpipe.database.history.model.SearchHistoryEntry.SERVICE_ID;
import static org.schabi.newpipe.database.history.model.SearchHistoryEntry.TABLE_NAME;
import static org.schabi.newpipe.database.history.model.SearchHistoryEntry.*;
@Dao
public interface SearchHistoryDAO extends HistoryDAO<SearchHistoryEntry> {

View File

@ -4,26 +4,20 @@ import androidx.annotation.Nullable;
import androidx.room.Dao;
import androidx.room.Query;
import androidx.room.RewriteQueriesToDropUnusedColumns;
import io.reactivex.rxjava3.core.Flowable;
import org.schabi.newpipe.database.history.model.StreamHistoryEntity;
import org.schabi.newpipe.database.history.model.StreamHistoryEntry;
import org.schabi.newpipe.database.stream.StreamStatisticsEntry;
import java.util.List;
import io.reactivex.rxjava3.core.Flowable;
import static org.schabi.newpipe.database.history.model.StreamHistoryEntity.JOIN_STREAM_ID;
import static org.schabi.newpipe.database.history.model.StreamHistoryEntity.STREAM_ACCESS_DATE;
import static org.schabi.newpipe.database.history.model.StreamHistoryEntity.STREAM_HISTORY_TABLE;
import static org.schabi.newpipe.database.history.model.StreamHistoryEntity.STREAM_REPEAT_COUNT;
import static org.schabi.newpipe.database.history.model.StreamHistoryEntity.*;
import static org.schabi.newpipe.database.stream.StreamStatisticsEntry.STREAM_LATEST_DATE;
import static org.schabi.newpipe.database.stream.StreamStatisticsEntry.STREAM_WATCH_COUNT;
import static org.schabi.newpipe.database.stream.model.StreamEntity.STREAM_ID;
import static org.schabi.newpipe.database.stream.model.StreamEntity.STREAM_TABLE;
import static org.schabi.newpipe.database.stream.model.StreamStateEntity.JOIN_STREAM_ID_ALIAS;
import static org.schabi.newpipe.database.stream.model.StreamStateEntity.STREAM_PROGRESS_MILLIS;
import static org.schabi.newpipe.database.stream.model.StreamStateEntity.STREAM_STATE_TABLE;
import static org.schabi.newpipe.database.stream.model.StreamStateEntity.*;
@Dao
public abstract class StreamHistoryDAO implements HistoryDAO<StreamHistoryEntity> {

View File

@ -5,15 +5,12 @@ import androidx.room.ColumnInfo;
import androidx.room.Entity;
import androidx.room.ForeignKey;
import androidx.room.Index;
import org.schabi.newpipe.database.stream.model.StreamEntity;
import java.time.OffsetDateTime;
import static androidx.room.ForeignKey.CASCADE;
import static org.schabi.newpipe.database.history.model.StreamHistoryEntity.JOIN_STREAM_ID;
import static org.schabi.newpipe.database.history.model.StreamHistoryEntity.STREAM_ACCESS_DATE;
import static org.schabi.newpipe.database.history.model.StreamHistoryEntity.STREAM_HISTORY_TABLE;
import static org.schabi.newpipe.database.history.model.StreamHistoryEntity.*;
@Entity(tableName = STREAM_HISTORY_TABLE,
primaryKeys = {JOIN_STREAM_ID, STREAM_ACCESS_DATE},
@ -42,8 +39,8 @@ public class StreamHistoryEntity {
private long repeatCount;
/**
* @param streamUid the stream id this history item will refer to
* @param accessDate the last time the stream was accessed
* @param streamUid the stream id this history item will refer to
* @param accessDate the last time the stream was accessed
* @param repeatCount the total number of views this stream received
*/
public StreamHistoryEntity(final long streamUid,

View File

@ -9,8 +9,6 @@ import java.util.stream.Collectors;
import java.util.stream.Stream;
public interface PlaylistLocalItem extends LocalItem {
String getOrderingName();
static List<PlaylistLocalItem> merge(
final List<PlaylistMetadataEntry> localPlaylists,
final List<PlaylistRemoteEntity> remotePlaylists) {
@ -19,4 +17,6 @@ public interface PlaylistLocalItem extends LocalItem {
Comparator.nullsLast(String.CASE_INSENSITIVE_ORDER)))
.collect(Collectors.toList());
}
String getOrderingName();
}

View File

@ -2,9 +2,7 @@ package org.schabi.newpipe.database.playlist;
import androidx.room.ColumnInfo;
import static org.schabi.newpipe.database.playlist.model.PlaylistEntity.PLAYLIST_ID;
import static org.schabi.newpipe.database.playlist.model.PlaylistEntity.PLAYLIST_NAME;
import static org.schabi.newpipe.database.playlist.model.PlaylistEntity.PLAYLIST_THUMBNAIL_URL;
import static org.schabi.newpipe.database.playlist.model.PlaylistEntity.*;
public class PlaylistMetadataEntry implements PlaylistLocalItem {
public static final String PLAYLIST_STREAM_COUNT = "streamCount";

View File

@ -2,14 +2,12 @@ package org.schabi.newpipe.database.playlist.dao;
import androidx.room.Dao;
import androidx.room.Query;
import io.reactivex.rxjava3.core.Flowable;
import org.schabi.newpipe.database.BasicDAO;
import org.schabi.newpipe.database.playlist.model.PlaylistEntity;
import java.util.List;
import io.reactivex.rxjava3.core.Flowable;
import static org.schabi.newpipe.database.playlist.model.PlaylistEntity.PLAYLIST_ID;
import static org.schabi.newpipe.database.playlist.model.PlaylistEntity.PLAYLIST_TABLE;

View File

@ -3,18 +3,13 @@ package org.schabi.newpipe.database.playlist.dao;
import androidx.room.Dao;
import androidx.room.Query;
import androidx.room.Transaction;
import io.reactivex.rxjava3.core.Flowable;
import org.schabi.newpipe.database.BasicDAO;
import org.schabi.newpipe.database.playlist.model.PlaylistRemoteEntity;
import java.util.List;
import io.reactivex.rxjava3.core.Flowable;
import static org.schabi.newpipe.database.playlist.model.PlaylistRemoteEntity.REMOTE_PLAYLIST_ID;
import static org.schabi.newpipe.database.playlist.model.PlaylistRemoteEntity.REMOTE_PLAYLIST_SERVICE_ID;
import static org.schabi.newpipe.database.playlist.model.PlaylistRemoteEntity.REMOTE_PLAYLIST_TABLE;
import static org.schabi.newpipe.database.playlist.model.PlaylistRemoteEntity.REMOTE_PLAYLIST_URL;
import static org.schabi.newpipe.database.playlist.model.PlaylistRemoteEntity.*;
@Dao
public interface PlaylistRemoteDAO extends BasicDAO<PlaylistRemoteEntity> {

View File

@ -4,7 +4,7 @@ import androidx.room.Dao;
import androidx.room.Query;
import androidx.room.RewriteQueriesToDropUnusedColumns;
import androidx.room.Transaction;
import io.reactivex.rxjava3.core.Flowable;
import org.schabi.newpipe.database.BasicDAO;
import org.schabi.newpipe.database.playlist.PlaylistMetadataEntry;
import org.schabi.newpipe.database.playlist.PlaylistStreamEntry;
@ -12,23 +12,12 @@ import org.schabi.newpipe.database.playlist.model.PlaylistStreamEntity;
import java.util.List;
import io.reactivex.rxjava3.core.Flowable;
import static org.schabi.newpipe.database.playlist.PlaylistMetadataEntry.PLAYLIST_STREAM_COUNT;
import static org.schabi.newpipe.database.playlist.model.PlaylistEntity.PLAYLIST_ID;
import static org.schabi.newpipe.database.playlist.model.PlaylistEntity.PLAYLIST_NAME;
import static org.schabi.newpipe.database.playlist.model.PlaylistEntity.PLAYLIST_TABLE;
import static org.schabi.newpipe.database.playlist.model.PlaylistEntity.PLAYLIST_THUMBNAIL_URL;
import static org.schabi.newpipe.database.playlist.model.PlaylistStreamEntity.JOIN_INDEX;
import static org.schabi.newpipe.database.playlist.model.PlaylistStreamEntity.JOIN_PLAYLIST_ID;
import static org.schabi.newpipe.database.playlist.model.PlaylistEntity.*;
import static org.schabi.newpipe.database.playlist.model.PlaylistStreamEntity.JOIN_STREAM_ID;
import static org.schabi.newpipe.database.playlist.model.PlaylistStreamEntity.PLAYLIST_STREAM_JOIN_TABLE;
import static org.schabi.newpipe.database.stream.model.StreamEntity.STREAM_ID;
import static org.schabi.newpipe.database.stream.model.StreamEntity.STREAM_TABLE;
import static org.schabi.newpipe.database.stream.model.StreamEntity.STREAM_THUMBNAIL_URL;
import static org.schabi.newpipe.database.stream.model.StreamStateEntity.JOIN_STREAM_ID_ALIAS;
import static org.schabi.newpipe.database.stream.model.StreamStateEntity.STREAM_PROGRESS_MILLIS;
import static org.schabi.newpipe.database.stream.model.StreamStateEntity.STREAM_STATE_TABLE;
import static org.schabi.newpipe.database.playlist.model.PlaylistStreamEntity.*;
import static org.schabi.newpipe.database.stream.model.StreamEntity.*;
import static org.schabi.newpipe.database.stream.model.StreamStateEntity.*;
@Dao
public interface PlaylistStreamDAO extends BasicDAO<PlaylistStreamEntity> {

View File

@ -1,22 +1,13 @@
package org.schabi.newpipe.database.playlist.model;
import android.text.TextUtils;
import androidx.room.ColumnInfo;
import androidx.room.Entity;
import androidx.room.Ignore;
import androidx.room.Index;
import androidx.room.PrimaryKey;
import androidx.room.*;
import org.schabi.newpipe.database.playlist.PlaylistLocalItem;
import org.schabi.newpipe.extractor.playlist.PlaylistInfo;
import org.schabi.newpipe.util.Constants;
import static org.schabi.newpipe.database.LocalItem.LocalItemType.PLAYLIST_REMOTE_ITEM;
import static org.schabi.newpipe.database.playlist.model.PlaylistRemoteEntity.REMOTE_PLAYLIST_NAME;
import static org.schabi.newpipe.database.playlist.model.PlaylistRemoteEntity.REMOTE_PLAYLIST_SERVICE_ID;
import static org.schabi.newpipe.database.playlist.model.PlaylistRemoteEntity.REMOTE_PLAYLIST_TABLE;
import static org.schabi.newpipe.database.playlist.model.PlaylistRemoteEntity.REMOTE_PLAYLIST_URL;
import static org.schabi.newpipe.database.playlist.model.PlaylistRemoteEntity.*;
@Entity(tableName = REMOTE_PLAYLIST_TABLE,
indices = {

View File

@ -4,14 +4,10 @@ import androidx.room.ColumnInfo;
import androidx.room.Entity;
import androidx.room.ForeignKey;
import androidx.room.Index;
import org.schabi.newpipe.database.stream.model.StreamEntity;
import static androidx.room.ForeignKey.CASCADE;
import static org.schabi.newpipe.database.playlist.model.PlaylistStreamEntity.JOIN_INDEX;
import static org.schabi.newpipe.database.playlist.model.PlaylistStreamEntity.JOIN_PLAYLIST_ID;
import static org.schabi.newpipe.database.playlist.model.PlaylistStreamEntity.JOIN_STREAM_ID;
import static org.schabi.newpipe.database.playlist.model.PlaylistStreamEntity.PLAYLIST_STREAM_JOIN_TABLE;
import static org.schabi.newpipe.database.playlist.model.PlaylistStreamEntity.*;
@Entity(tableName = PLAYLIST_STREAM_JOIN_TABLE,
primaryKeys = {JOIN_PLAYLIST_ID, JOIN_INDEX},

View File

@ -1,18 +1,12 @@
package org.schabi.newpipe.database.stream.dao;
import androidx.room.Dao;
import androidx.room.Insert;
import androidx.room.OnConflictStrategy;
import androidx.room.Query;
import androidx.room.Transaction;
import androidx.room.*;
import io.reactivex.rxjava3.core.Flowable;
import org.schabi.newpipe.database.BasicDAO;
import org.schabi.newpipe.database.stream.model.StreamStateEntity;
import java.util.List;
import io.reactivex.rxjava3.core.Flowable;
import static org.schabi.newpipe.database.stream.model.StreamStateEntity.JOIN_STREAM_ID;
import static org.schabi.newpipe.database.stream.model.StreamStateEntity.STREAM_STATE_TABLE;

View File

@ -26,21 +26,19 @@ public class StreamStateEntity {
// for some other joins already
public static final String JOIN_STREAM_ID_ALIAS = "stream_id_alias";
public static final String STREAM_PROGRESS_MILLIS = "progress_time";
/**
* Playback state will not be saved, if playback time is less than this threshold (5000ms = 5s).
*/
private static final long PLAYBACK_SAVE_THRESHOLD_START_MILLISECONDS = 5000;
/**
* Stream will be considered finished if the playback time left exceeds this threshold
* (60000ms = 60s).
*
* @see #isFinished(long)
* @see org.schabi.newpipe.database.feed.dao.FeedDAO#getLiveOrNotPlayedStreams()
* @see org.schabi.newpipe.database.feed.dao.FeedDAO#getLiveOrNotPlayedStreamsForGroup(long)
*/
public static final long PLAYBACK_FINISHED_END_MILLISECONDS = 60000;
/**
* Playback state will not be saved, if playback time is less than this threshold (5000ms = 5s).
*/
private static final long PLAYBACK_SAVE_THRESHOLD_START_MILLISECONDS = 5000;
@ColumnInfo(name = JOIN_STREAM_ID)
private long streamUid;
@ -71,6 +69,7 @@ public class StreamStateEntity {
/**
* The state will be considered valid, and thus be saved, if the progress is more than {@link
* #PLAYBACK_SAVE_THRESHOLD_START_MILLISECONDS} or at least 1/4 of the video length.
*
* @param durationInSeconds the duration of the stream connected with this state, in seconds
* @return whether this stream state entity should be saved or not
*/
@ -85,10 +84,11 @@ public class StreamStateEntity {
* The state will be saved anyway, so that it can be shown under stream info items, but the
* player will not resume if a state is considered as finished. Finished streams are also the
* ones that can be filtered out in the feed fragment.
* @see org.schabi.newpipe.database.feed.dao.FeedDAO#getLiveOrNotPlayedStreams()
* @see org.schabi.newpipe.database.feed.dao.FeedDAO#getLiveOrNotPlayedStreamsForGroup(long)
*
* @param durationInSeconds the duration of the stream connected with this state, in seconds
* @return whether the stream is finished or not
* @see org.schabi.newpipe.database.feed.dao.FeedDAO#getLiveOrNotPlayedStreams()
* @see org.schabi.newpipe.database.feed.dao.FeedDAO#getLiveOrNotPlayedStreamsForGroup(long)
*/
public boolean isFinished(final long durationInSeconds) {
return progressMillis >= durationInSeconds * 1000 - PLAYBACK_FINISHED_END_MILLISECONDS

View File

@ -1,6 +1,7 @@
package org.schabi.newpipe.database.subscription;
import androidx.annotation.IntDef;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;

View File

@ -1,19 +1,12 @@
package org.schabi.newpipe.database.subscription;
import androidx.annotation.NonNull;
import androidx.room.ColumnInfo;
import androidx.room.Entity;
import androidx.room.Ignore;
import androidx.room.Index;
import androidx.room.PrimaryKey;
import androidx.room.*;
import org.schabi.newpipe.extractor.channel.ChannelInfo;
import org.schabi.newpipe.extractor.channel.ChannelInfoItem;
import org.schabi.newpipe.util.Constants;
import static org.schabi.newpipe.database.subscription.SubscriptionEntity.SUBSCRIPTION_SERVICE_ID;
import static org.schabi.newpipe.database.subscription.SubscriptionEntity.SUBSCRIPTION_TABLE;
import static org.schabi.newpipe.database.subscription.SubscriptionEntity.SUBSCRIPTION_URL;
import static org.schabi.newpipe.database.subscription.SubscriptionEntity.*;
@Entity(tableName = SUBSCRIPTION_TABLE,
indices = {@Index(value = {SUBSCRIPTION_SERVICE_ID, SUBSCRIPTION_URL}, unique = true)})
@ -26,7 +19,7 @@ public class SubscriptionEntity {
public static final String SUBSCRIPTION_AVATAR_URL = "avatar_url";
public static final String SUBSCRIPTION_SUBSCRIBER_COUNT = "subscriber_count";
public static final String SUBSCRIPTION_DESCRIPTION = "description";
public static final String SUBSCRIPTION_NOTIFICATION_MODE = "notification_mode";
public static final String SUBSCRIPTION_NOTIFICATION_MODE = "notification_mode";
@PrimaryKey(autoGenerate = true)
private long uid = 0;
@ -167,20 +160,16 @@ public class SubscriptionEntity {
if (!url.equals(that.url)) {
return false;
}
if (name != null ? !name.equals(that.name) : that.name != null) {
if (!java.util.Objects.equals(name, that.name)) {
return false;
}
if (avatarUrl != null ? !avatarUrl.equals(that.avatarUrl) : that.avatarUrl != null) {
if (!java.util.Objects.equals(avatarUrl, that.avatarUrl)) {
return false;
}
if (subscriberCount != null
? !subscriberCount.equals(that.subscriberCount)
: that.subscriberCount != null) {
if (!java.util.Objects.equals(subscriberCount, that.subscriberCount)) {
return false;
}
return description != null
? description.equals(that.description)
: that.description == null;
return java.util.Objects.equals(description, that.description);
}
@Override

View File

@ -6,17 +6,14 @@ import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.ViewTreeObserver;
import androidx.appcompat.app.ActionBar;
import androidx.appcompat.app.AppCompatActivity;
import androidx.fragment.app.FragmentTransaction;
import org.schabi.newpipe.R;
import org.schabi.newpipe.databinding.ActivityDownloaderBinding;
import org.schabi.newpipe.util.DeviceUtils;
import org.schabi.newpipe.util.ThemeHelper;
import org.schabi.newpipe.views.FocusOverlayView;
import us.shandian.giga.service.DownloadManagerService;
import us.shandian.giga.ui.fragment.MissionsFragment;
@ -53,12 +50,12 @@ public class DownloadActivity extends AppCompatActivity {
getWindow().getDecorView().getViewTreeObserver()
.addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
@Override
public void onGlobalLayout() {
updateFragments();
getWindow().getDecorView().getViewTreeObserver().removeOnGlobalLayoutListener(this);
}
});
@Override
public void onGlobalLayout() {
updateFragments();
getWindow().getDecorView().getViewTreeObserver().removeOnGlobalLayoutListener(this);
}
});
if (DeviceUtils.isTv(this)) {
FocusOverlayView.setupFocusObserver(this);

View File

@ -1,17 +1,8 @@
package org.schabi.newpipe.download;
import static org.schabi.newpipe.extractor.stream.DeliveryMethod.PROGRESSIVE_HTTP;
import static org.schabi.newpipe.util.ListHelper.getStreamsOfSpecifiedDelivery;
import static org.schabi.newpipe.util.Localization.assureCorrectAppLanguage;
import android.app.Activity;
import android.content.ComponentName;
import android.content.Context;
import android.content.DialogInterface;
import android.content.*;
import android.content.DialogInterface.OnDismissListener;
import android.content.Intent;
import android.content.ServiceConnection;
import android.content.SharedPreferences;
import android.net.Uri;
import android.os.Bundle;
import android.os.Environment;
@ -24,7 +15,6 @@ import android.widget.AdapterView;
import android.widget.RadioGroup;
import android.widget.SeekBar;
import android.widget.Toast;
import androidx.activity.result.ActivityResult;
import androidx.activity.result.ActivityResultLauncher;
import androidx.activity.result.contract.ActivityResultContracts.StartActivityForResult;
@ -39,9 +29,10 @@ import androidx.collection.SparseArrayCompat;
import androidx.documentfile.provider.DocumentFile;
import androidx.fragment.app.DialogFragment;
import androidx.preference.PreferenceManager;
import com.nononsenseapps.filepicker.Utils;
import icepick.Icepick;
import icepick.State;
import io.reactivex.rxjava3.disposables.CompositeDisposable;
import org.schabi.newpipe.MainActivity;
import org.schabi.newpipe.R;
import org.schabi.newpipe.databinding.DownloadDialogBinding;
@ -51,24 +42,19 @@ import org.schabi.newpipe.error.UserAction;
import org.schabi.newpipe.extractor.MediaFormat;
import org.schabi.newpipe.extractor.NewPipe;
import org.schabi.newpipe.extractor.localization.Localization;
import org.schabi.newpipe.extractor.stream.AudioStream;
import org.schabi.newpipe.extractor.stream.Stream;
import org.schabi.newpipe.extractor.stream.StreamInfo;
import org.schabi.newpipe.extractor.stream.SubtitlesStream;
import org.schabi.newpipe.extractor.stream.VideoStream;
import org.schabi.newpipe.extractor.stream.*;
import org.schabi.newpipe.settings.NewPipeSettings;
import org.schabi.newpipe.streams.io.NoFileManagerSafeGuard;
import org.schabi.newpipe.streams.io.StoredDirectoryHelper;
import org.schabi.newpipe.streams.io.StoredFileHelper;
import org.schabi.newpipe.util.FilePickerActivityHelper;
import org.schabi.newpipe.util.FilenameUtils;
import org.schabi.newpipe.util.ListHelper;
import org.schabi.newpipe.util.PermissionHelper;
import org.schabi.newpipe.util.SecondaryStreamHelper;
import org.schabi.newpipe.util.SimpleOnSeekBarChangeListener;
import org.schabi.newpipe.util.StreamItemAdapter;
import org.schabi.newpipe.util.*;
import org.schabi.newpipe.util.StreamItemAdapter.StreamSizeWrapper;
import org.schabi.newpipe.util.ThemeHelper;
import us.shandian.giga.get.MissionRecoveryInfo;
import us.shandian.giga.postprocessing.Postprocessing;
import us.shandian.giga.service.DownloadManager;
import us.shandian.giga.service.DownloadManagerService;
import us.shandian.giga.service.DownloadManagerService.DownloadManagerBinder;
import us.shandian.giga.service.MissionState;
import java.io.File;
import java.io.IOException;
@ -77,21 +63,15 @@ import java.util.Locale;
import java.util.Objects;
import java.util.Optional;
import icepick.Icepick;
import icepick.State;
import io.reactivex.rxjava3.disposables.CompositeDisposable;
import us.shandian.giga.get.MissionRecoveryInfo;
import us.shandian.giga.postprocessing.Postprocessing;
import us.shandian.giga.service.DownloadManager;
import us.shandian.giga.service.DownloadManagerService;
import us.shandian.giga.service.DownloadManagerService.DownloadManagerBinder;
import us.shandian.giga.service.MissionState;
import static org.schabi.newpipe.extractor.stream.DeliveryMethod.PROGRESSIVE_HTTP;
import static org.schabi.newpipe.util.ListHelper.getStreamsOfSpecifiedDelivery;
import static org.schabi.newpipe.util.Localization.assureCorrectAppLanguage;
public class DownloadDialog extends DialogFragment
implements RadioGroup.OnCheckedChangeListener, AdapterView.OnItemSelectedListener {
private static final String TAG = "DialogFragment";
private static final boolean DEBUG = MainActivity.DEBUG;
private final CompositeDisposable disposables = new CompositeDisposable();
@State
StreamInfo currentInfo;
@State
@ -106,34 +86,25 @@ public class DownloadDialog extends DialogFragment
int selectedAudioIndex = 0; // default to the first item
@State
int selectedSubtitleIndex = 0; // default to the first item
@Nullable
private OnDismissListener onDismissListener = null;
private StoredDirectoryHelper mainStorageAudio = null;
private StoredDirectoryHelper mainStorageVideo = null;
private DownloadManager downloadManager = null;
private ActionMenuItemView okButton = null;
private Context context;
private boolean askForSavePath;
private StreamItemAdapter<AudioStream, Stream> audioStreamsAdapter;
private StreamItemAdapter<VideoStream, AudioStream> videoStreamsAdapter;
private StreamItemAdapter<SubtitlesStream, Stream> subtitleStreamsAdapter;
private final CompositeDisposable disposables = new CompositeDisposable();
private DownloadDialogBinding dialogBinding;
private SharedPreferences prefs;
// Variables for file name and MIME type when picking new folder because it's not set yet
private String filenameTmp;
private String mimeTmp;
private final ActivityResultLauncher<Intent> requestDownloadSaveAsLauncher =
registerForActivityResult(
new StartActivityForResult(), this::requestDownloadSaveAsResult);
private SharedPreferences prefs;
// Variables for file name and MIME type when picking new folder because it's not set yet
private String filenameTmp;
private String mimeTmp;
private final ActivityResultLauncher<Intent> requestDownloadPickAudioFolderLauncher =
registerForActivityResult(
new StartActivityForResult(), this::requestDownloadPickAudioFolderResult);
@ -1031,7 +1002,7 @@ public class DownloadDialog extends DialogFragment
if (selectedStream.getFormat() == MediaFormat.TTML) {
psName = Postprocessing.ALGORITHM_TTML_CONVERTER;
psArgs = new String[] {
psArgs = new String[]{
selectedStream.getFormat().getSuffix(),
"false" // ignore empty frames
};
@ -1042,10 +1013,10 @@ public class DownloadDialog extends DialogFragment
}
if (secondaryStream == null) {
urls = new String[] {
urls = new String[]{
selectedStream.getContent()
};
recoveryInfo = new MissionRecoveryInfo[] {
recoveryInfo = new MissionRecoveryInfo[]{
new MissionRecoveryInfo(selectedStream)
};
} else {
@ -1054,10 +1025,10 @@ public class DownloadDialog extends DialogFragment
+ secondaryStream.getDeliveryMethod());
}
urls = new String[] {
urls = new String[]{
selectedStream.getContent(), secondaryStream.getContent()
};
recoveryInfo = new MissionRecoveryInfo[] {new MissionRecoveryInfo(selectedStream),
recoveryInfo = new MissionRecoveryInfo[]{new MissionRecoveryInfo(selectedStream),
new MissionRecoveryInfo(secondaryStream)};
}

View File

@ -1,9 +1,7 @@
package org.schabi.newpipe.error;
import android.content.Context;
import androidx.annotation.NonNull;
import org.acra.ReportField;
import org.acra.data.CrashReportData;
import org.acra.sender.ReportSender;

View File

@ -1,11 +1,8 @@
package org.schabi.newpipe.error;
import android.content.Context;
import androidx.annotation.NonNull;
import com.google.auto.service.AutoService;
import org.acra.config.CoreConfiguration;
import org.acra.sender.ReportSender;
import org.acra.sender.ReportSenderFactory;

View File

@ -1,7 +1,5 @@
package org.schabi.newpipe.error;
import static org.schabi.newpipe.util.Localization.assureCorrectAppLanguage;
import android.app.Activity;
import android.content.Context;
import android.content.Intent;
@ -12,14 +10,11 @@ import android.util.Log;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import androidx.annotation.Nullable;
import androidx.appcompat.app.ActionBar;
import androidx.appcompat.app.AlertDialog;
import androidx.appcompat.app.AppCompatActivity;
import com.grack.nanojson.JsonWriter;
import org.schabi.newpipe.BuildConfig;
import org.schabi.newpipe.MainActivity;
import org.schabi.newpipe.R;
@ -33,6 +28,8 @@ import java.time.format.DateTimeFormatter;
import java.util.Arrays;
import java.util.stream.Collectors;
import static org.schabi.newpipe.util.Localization.assureCorrectAppLanguage;
/*
* Created by Christian Schabesberger on 24.10.15.
*
@ -83,6 +80,25 @@ public class ErrorActivity extends AppCompatActivity {
// Activity lifecycle
////////////////////////////////////////////////////////////////////////
/**
* Get the checked activity.
*
* @param returnActivity the activity to return to
* @return the casted return activity or null
*/
@Nullable
static Class<? extends Activity> getReturnActivity(final Class<?> returnActivity) {
Class<? extends Activity> checkedReturnActivity = null;
if (returnActivity != null) {
if (Activity.class.isAssignableFrom(returnActivity)) {
checkedReturnActivity = returnActivity.asSubclass(Activity.class);
} else {
checkedReturnActivity = MainActivity.class;
}
}
return checkedReturnActivity;
}
@Override
protected void onCreate(final Bundle savedInstanceState) {
assureCorrectAppLanguage(this);
@ -188,25 +204,6 @@ public class ErrorActivity extends AppCompatActivity {
.collect(Collectors.joining(separator + "\n", separator + "\n", separator));
}
/**
* Get the checked activity.
*
* @param returnActivity the activity to return to
* @return the casted return activity or null
*/
@Nullable
static Class<? extends Activity> getReturnActivity(final Class<?> returnActivity) {
Class<? extends Activity> checkedReturnActivity = null;
if (returnActivity != null) {
if (Activity.class.isAssignableFrom(returnActivity)) {
checkedReturnActivity = returnActivity.asSubclass(Activity.class);
} else {
checkedReturnActivity = MainActivity.class;
}
}
return checkedReturnActivity;
}
private void buildInfo(final ErrorInfo info) {
String text = "";
@ -267,7 +264,7 @@ public class ErrorActivity extends AppCompatActivity {
htmlErrorReport
.append("## Exception")
.append("\n* __User Action:__ ")
.append(getUserActionString(errorInfo.getUserAction()))
.append(getUserActionString(errorInfo.getUserAction()))
.append("\n* __Request:__ ").append(errorInfo.getRequest())
.append("\n* __Content Country:__ ").append(getContentCountryString())
.append("\n* __Content Language:__ ").append(getContentLanguageString())

View File

@ -7,19 +7,13 @@ import android.os.Bundle;
import android.util.Log;
import android.view.Menu;
import android.view.MenuItem;
import android.webkit.CookieManager;
import android.webkit.WebResourceRequest;
import android.webkit.WebSettings;
import android.webkit.WebView;
import android.webkit.WebViewClient;
import android.webkit.*;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.appcompat.app.ActionBar;
import androidx.appcompat.app.AppCompatActivity;
import androidx.core.app.NavUtils;
import androidx.preference.PreferenceManager;
import org.schabi.newpipe.DownloaderImpl;
import org.schabi.newpipe.MainActivity;
import org.schabi.newpipe.R;
@ -54,6 +48,8 @@ public class ReCaptchaActivity extends AppCompatActivity {
public static final String TAG = ReCaptchaActivity.class.toString();
public static final String YT_URL = "https://www.youtube.com";
public static final String RECAPTCHA_COOKIES_KEY = "recaptcha_cookies";
private ActivityRecaptchaBinding recaptchaBinding;
private String foundCookies = "";
public static String sanitizeRecaptchaUrl(@Nullable final String url) {
if (url == null || url.trim().isEmpty()) {
@ -64,9 +60,6 @@ public class ReCaptchaActivity extends AppCompatActivity {
}
}
private ActivityRecaptchaBinding recaptchaBinding;
private String foundCookies = "";
@SuppressLint("SetJavaScriptEnabled")
@Override
protected void onCreate(final Bundle savedInstanceState) {

View File

@ -4,11 +4,10 @@ import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.ProgressBar;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.fragment.app.Fragment;
import icepick.State;
import org.schabi.newpipe.BaseFragment;
import org.schabi.newpipe.R;
import org.schabi.newpipe.error.ErrorInfo;
@ -18,24 +17,20 @@ import org.schabi.newpipe.util.InfoCache;
import java.util.concurrent.atomic.AtomicBoolean;
import icepick.State;
import static org.schabi.newpipe.ktx.ViewUtils.animate;
public abstract class BaseStateFragment<I> extends BaseFragment implements ViewContract<I> {
@State
protected AtomicBoolean wasLoading = new AtomicBoolean();
protected AtomicBoolean isLoading = new AtomicBoolean();
@Nullable
@State
protected ErrorInfo lastPanelError = null;
@Nullable
private View emptyStateView;
@Nullable
private ProgressBar loadingProgressBar;
private ErrorPanelHelper errorPanelHelper;
@Nullable
@State
protected ErrorInfo lastPanelError = null;
@Override
public void onViewCreated(@NonNull final View rootView, final Bundle savedInstanceState) {

View File

@ -4,9 +4,7 @@ import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import androidx.annotation.Nullable;
import org.schabi.newpipe.BaseFragment;
import org.schabi.newpipe.R;

View File

@ -4,9 +4,7 @@ import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import androidx.annotation.Nullable;
import org.schabi.newpipe.BaseFragment;
import org.schabi.newpipe.R;

View File

@ -3,13 +3,7 @@ package org.schabi.newpipe.fragments;
import android.content.Context;
import android.os.Bundle;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
import android.view.*;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.appcompat.app.ActionBar;
@ -17,9 +11,7 @@ import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentManager;
import androidx.fragment.app.FragmentStatePagerAdapterMenuWorkaround;
import androidx.preference.PreferenceManager;
import com.google.android.material.tabs.TabLayout;
import org.schabi.newpipe.BaseFragment;
import org.schabi.newpipe.R;
import org.schabi.newpipe.databinding.FragmentMainBinding;
@ -34,10 +26,9 @@ import java.util.ArrayList;
import java.util.List;
public class MainFragment extends BaseFragment implements TabLayout.OnTabSelectedListener {
private final List<Tab> tabsList = new ArrayList<>();
private FragmentMainBinding binding;
private SelectedTabsPagerAdapter pagerAdapter;
private final List<Tab> tabsList = new ArrayList<>();
private TabsManager tabsManager;
private boolean hasTabsChanged = false;
@ -199,7 +190,8 @@ public class MainFragment extends BaseFragment implements TabLayout.OnTabSelecte
}
@Override
public void onTabUnselected(final TabLayout.Tab tab) { }
public void onTabUnselected(final TabLayout.Tab tab) {
}
@Override
public void onTabReselected(final TabLayout.Tab tab) {

View File

@ -1,25 +1,18 @@
package org.schabi.newpipe.fragments.detail;
import static android.text.TextUtils.isEmpty;
import static org.schabi.newpipe.extractor.stream.StreamExtractor.NO_AGE_LIMIT;
import static org.schabi.newpipe.extractor.utils.Utils.isBlank;
import static org.schabi.newpipe.util.Localization.getAppLocale;
import static org.schabi.newpipe.util.text.TextLinkifier.SET_LINK_MOVEMENT_METHOD;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.LinearLayout;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.StringRes;
import androidx.appcompat.widget.TooltipCompat;
import androidx.core.text.HtmlCompat;
import com.google.android.material.chip.Chip;
import icepick.State;
import io.reactivex.rxjava3.disposables.CompositeDisposable;
import org.schabi.newpipe.BaseFragment;
import org.schabi.newpipe.R;
import org.schabi.newpipe.databinding.FragmentDescriptionBinding;
@ -32,14 +25,17 @@ import org.schabi.newpipe.util.NavigationHelper;
import org.schabi.newpipe.util.external_communication.ShareUtils;
import org.schabi.newpipe.util.text.TextLinkifier;
import icepick.State;
import io.reactivex.rxjava3.disposables.CompositeDisposable;
import static android.text.TextUtils.isEmpty;
import static org.schabi.newpipe.extractor.stream.StreamExtractor.NO_AGE_LIMIT;
import static org.schabi.newpipe.extractor.utils.Utils.isBlank;
import static org.schabi.newpipe.util.Localization.getAppLocale;
import static org.schabi.newpipe.util.text.TextLinkifier.SET_LINK_MOVEMENT_METHOD;
public class DescriptionFragment extends BaseFragment {
final CompositeDisposable descriptionDisposables = new CompositeDisposable();
@State
StreamInfo streamInfo = null;
final CompositeDisposable descriptionDisposables = new CompositeDisposable();
FragmentDescriptionBinding binding;
public DescriptionFragment() {

View File

@ -1,7 +1,6 @@
package org.schabi.newpipe.fragments.detail;
import androidx.annotation.NonNull;
import org.schabi.newpipe.player.playqueue.PlayQueue;
import java.io.Serializable;
@ -20,14 +19,6 @@ class StackItem implements Serializable {
this.playQueue = playQueue;
}
public void setUrl(final String url) {
this.url = url;
}
public void setPlayQueue(final PlayQueue queue) {
this.playQueue = queue;
}
public int getServiceId() {
return serviceId;
}
@ -44,10 +35,18 @@ class StackItem implements Serializable {
return url;
}
public void setUrl(final String url) {
this.url = url;
}
public PlayQueue getPlayQueue() {
return playQueue;
}
public void setPlayQueue(final PlayQueue queue) {
this.playQueue = queue;
}
@NonNull
@Override
public String toString() {

View File

@ -1,7 +1,6 @@
package org.schabi.newpipe.fragments.detail;
import android.view.ViewGroup;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.fragment.app.Fragment;

View File

@ -1,26 +1,9 @@
package org.schabi.newpipe.fragments.detail;
import static android.text.TextUtils.isEmpty;
import static org.schabi.newpipe.extractor.StreamingService.ServiceInfo.MediaCapability.COMMENTS;
import static org.schabi.newpipe.extractor.stream.StreamExtractor.NO_AGE_LIMIT;
import static org.schabi.newpipe.ktx.ViewUtils.animate;
import static org.schabi.newpipe.ktx.ViewUtils.animateRotation;
import static org.schabi.newpipe.player.helper.PlayerHelper.globalScreenOrientationLocked;
import static org.schabi.newpipe.player.helper.PlayerHelper.isClearingQueueConfirmationRequired;
import static org.schabi.newpipe.player.playqueue.PlayQueueItem.RECOVERY_UNSET;
import static org.schabi.newpipe.util.ExtractorHelper.showMetaInfoInTextView;
import static org.schabi.newpipe.util.ListHelper.getUrlAndNonTorrentStreams;
import static org.schabi.newpipe.util.NavigationHelper.openPlayQueue;
import static org.schabi.newpipe.util.NavigationHelper.playWithKore;
import android.animation.ValueAnimator;
import android.annotation.SuppressLint;
import android.app.Activity;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.SharedPreferences;
import android.content.*;
import android.content.pm.ActivityInfo;
import android.database.ContentObserver;
import android.graphics.Color;
@ -34,17 +17,11 @@ import android.provider.Settings;
import android.util.DisplayMetrics;
import android.util.Log;
import android.util.TypedValue;
import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import android.view.ViewTreeObserver;
import android.view.WindowManager;
import android.view.*;
import android.view.animation.DecelerateInterpolator;
import android.widget.FrameLayout;
import android.widget.RelativeLayout;
import android.widget.Toast;
import androidx.annotation.AttrRes;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
@ -58,14 +35,17 @@ import androidx.core.view.WindowCompat;
import androidx.core.view.WindowInsetsCompat;
import androidx.core.view.WindowInsetsControllerCompat;
import androidx.preference.PreferenceManager;
import com.google.android.exoplayer2.PlaybackException;
import com.google.android.exoplayer2.PlaybackParameters;
import com.google.android.material.appbar.AppBarLayout;
import com.google.android.material.bottomsheet.BottomSheetBehavior;
import com.google.android.material.tabs.TabLayout;
import com.squareup.picasso.Callback;
import icepick.State;
import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers;
import io.reactivex.rxjava3.disposables.CompositeDisposable;
import io.reactivex.rxjava3.disposables.Disposable;
import io.reactivex.rxjava3.schedulers.Schedulers;
import org.schabi.newpipe.App;
import org.schabi.newpipe.R;
import org.schabi.newpipe.database.stream.model.StreamEntity;
@ -79,11 +59,7 @@ import org.schabi.newpipe.extractor.InfoItem;
import org.schabi.newpipe.extractor.NewPipe;
import org.schabi.newpipe.extractor.exceptions.ContentNotSupportedException;
import org.schabi.newpipe.extractor.exceptions.ExtractionException;
import org.schabi.newpipe.extractor.stream.AudioStream;
import org.schabi.newpipe.extractor.stream.Stream;
import org.schabi.newpipe.extractor.stream.StreamInfo;
import org.schabi.newpipe.extractor.stream.StreamType;
import org.schabi.newpipe.extractor.stream.VideoStream;
import org.schabi.newpipe.extractor.stream.*;
import org.schabi.newpipe.fragments.BackPressable;
import org.schabi.newpipe.fragments.BaseStateFragment;
import org.schabi.newpipe.fragments.EmptyFragment;
@ -104,33 +80,26 @@ import org.schabi.newpipe.player.playqueue.PlayQueueItem;
import org.schabi.newpipe.player.playqueue.SinglePlayQueue;
import org.schabi.newpipe.player.ui.MainPlayerUi;
import org.schabi.newpipe.player.ui.VideoPlayerUi;
import org.schabi.newpipe.util.Constants;
import org.schabi.newpipe.util.DeviceUtils;
import org.schabi.newpipe.util.ExtractorHelper;
import org.schabi.newpipe.util.ListHelper;
import org.schabi.newpipe.util.Localization;
import org.schabi.newpipe.util.NavigationHelper;
import org.schabi.newpipe.util.PermissionHelper;
import org.schabi.newpipe.util.PicassoHelper;
import org.schabi.newpipe.util.StreamTypeUtil;
import org.schabi.newpipe.util.ThemeHelper;
import org.schabi.newpipe.util.*;
import org.schabi.newpipe.util.external_communication.KoreUtils;
import org.schabi.newpipe.util.external_communication.ShareUtils;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Objects;
import java.util.Optional;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.function.Consumer;
import icepick.State;
import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers;
import io.reactivex.rxjava3.disposables.CompositeDisposable;
import io.reactivex.rxjava3.disposables.Disposable;
import io.reactivex.rxjava3.schedulers.Schedulers;
import static android.text.TextUtils.isEmpty;
import static org.schabi.newpipe.extractor.StreamingService.ServiceInfo.MediaCapability.COMMENTS;
import static org.schabi.newpipe.extractor.stream.StreamExtractor.NO_AGE_LIMIT;
import static org.schabi.newpipe.ktx.ViewUtils.animate;
import static org.schabi.newpipe.ktx.ViewUtils.animateRotation;
import static org.schabi.newpipe.player.helper.PlayerHelper.globalScreenOrientationLocked;
import static org.schabi.newpipe.player.helper.PlayerHelper.isClearingQueueConfirmationRequired;
import static org.schabi.newpipe.player.playqueue.PlayQueueItem.RECOVERY_UNSET;
import static org.schabi.newpipe.util.ExtractorHelper.showMetaInfoInTextView;
import static org.schabi.newpipe.util.ListHelper.getUrlAndNonTorrentStreams;
import static org.schabi.newpipe.util.NavigationHelper.openPlayQueue;
import static org.schabi.newpipe.util.NavigationHelper.playWithKore;
public final class VideoDetailFragment
extends BaseStateFragment<StreamInfo>
@ -138,10 +107,6 @@ public final class VideoDetailFragment
PlayerServiceExtendedEventListener,
OnKeyDownListener {
public static final String KEY_SWITCHING_PLAYERS = "switching_players";
private static final float MAX_OVERLAY_ALPHA = 0.9f;
private static final float MAX_PLAYER_HEIGHT = 0.7f;
public static final String ACTION_SHOW_MAIN_PLAYER =
App.PACKAGE_NAME + ".VideoDetailFragment.ACTION_SHOW_MAIN_PLAYER";
public static final String ACTION_HIDE_MAIN_PLAYER =
@ -152,24 +117,50 @@ public final class VideoDetailFragment
App.PACKAGE_NAME + ".VideoDetailFragment.ACTION_VIDEO_FRAGMENT_RESUMED";
public static final String ACTION_VIDEO_FRAGMENT_STOPPED =
App.PACKAGE_NAME + ".VideoDetailFragment.ACTION_VIDEO_FRAGMENT_STOPPED";
private static final float MAX_OVERLAY_ALPHA = 0.9f;
private static final float MAX_PLAYER_HEIGHT = 0.7f;
private static final String COMMENTS_TAB_TAG = "COMMENTS";
private static final String RELATED_TAB_TAG = "NEXT VIDEO";
private static final String DESCRIPTION_TAB_TAG = "DESCRIPTION TAB";
private static final String EMPTY_TAB_TAG = "EMPTY TAB";
private static final String PICASSO_VIDEO_DETAILS_TAG = "PICASSO_VIDEO_DETAILS_TAG";
/**
* Stack that contains the "navigation history".<br>
* The peek is the current video.
*/
private static LinkedList<StackItem> stack = new LinkedList<>();
@AttrRes
@NonNull
final List<Integer> tabIcons = new ArrayList<>();
@StringRes
@NonNull
final List<Integer> tabContentDescriptions = new ArrayList<>();
@NonNull
private final CompositeDisposable disposables = new CompositeDisposable();
private final PlayerHolder playerHolder = PlayerHolder.getInstance();
@State
private int serviceId = Constants.NO_SERVICE_ID;
@State
@NonNull
private String title = "";
@State
@Nullable
private String url = null;
@Nullable
private PlayQueue playQueue = null;
@State
private boolean autoPlayEnabled = true;
@State
int bottomSheetState = BottomSheetBehavior.STATE_EXPANDED;
@State
int lastStableBottomSheetState = BottomSheetBehavior.STATE_EXPANDED;
// tabs
private boolean showComments;
private boolean showRelatedItems;
private boolean showDescription;
private String selectedTabTag;
@AttrRes @NonNull final List<Integer> tabIcons = new ArrayList<>();
@StringRes @NonNull final List<Integer> tabContentDescriptions = new ArrayList<>();
private boolean tabSettingsChanged = false;
private int lastAppBarVerticalOffset = Integer.MAX_VALUE; // prevents useless updates
private final SharedPreferences.OnSharedPreferenceChangeListener preferenceChangeListener =
(sharedPreferences, key) -> {
if (getString(R.string.show_comments_key).equals(key)) {
@ -183,49 +174,43 @@ public final class VideoDetailFragment
tabSettingsChanged = true;
}
};
@State
protected int serviceId = Constants.NO_SERVICE_ID;
@State
@NonNull
protected String title = "";
@State
@Nullable
protected String url = null;
@Nullable
protected PlayQueue playQueue = null;
@State
int bottomSheetState = BottomSheetBehavior.STATE_EXPANDED;
@State
int lastStableBottomSheetState = BottomSheetBehavior.STATE_EXPANDED;
@State
protected boolean autoPlayEnabled = true;
private int lastAppBarVerticalOffset = Integer.MAX_VALUE; // prevents useless updates
@Nullable
private StreamInfo currentInfo = null;
private Disposable currentWorker;
@NonNull
private final CompositeDisposable disposables = new CompositeDisposable();
@Nullable
private Disposable positionSubscriber = null;
private BottomSheetBehavior<FrameLayout> bottomSheetBehavior;
private BottomSheetBehavior.BottomSheetCallback bottomSheetCallback;
private BroadcastReceiver broadcastReceiver;
/*//////////////////////////////////////////////////////////////////////////
// Views
//////////////////////////////////////////////////////////////////////////*/
private BottomSheetBehavior.BottomSheetCallback bottomSheetCallback;
private BroadcastReceiver broadcastReceiver;
private FragmentVideoDetailBinding binding;
private TabAdapter pageAdapter;
private ContentObserver settingsContentObserver;
@Nullable
private PlayerService playerService;
private Player player;
private final PlayerHolder playerHolder = PlayerHolder.getInstance();
public static VideoDetailFragment getInstance(final int serviceId,
@Nullable final String videoUrl,
@NonNull final String name,
@Nullable final PlayQueue queue) {
final VideoDetailFragment instance = new VideoDetailFragment();
instance.setInitialData(serviceId, videoUrl, name, queue);
return instance;
}
/*////////////////////////////////////////////////////////////////////////*/
public static VideoDetailFragment getInstanceInCollapsedState() {
final VideoDetailFragment instance = new VideoDetailFragment();
instance.updateBottomSheetState(BottomSheetBehavior.STATE_COLLAPSED);
return instance;
}
/*//////////////////////////////////////////////////////////////////////////
// Service management
@ -267,6 +252,11 @@ public final class VideoDetailFragment
updateOverlayPlayQueueButtonVisibility();
}
/*//////////////////////////////////////////////////////////////////////////
// Fragment's Lifecycle
//////////////////////////////////////////////////////////////////////////*/
@Override
public void onServiceDisconnected() {
playerService = null;
@ -274,29 +264,6 @@ public final class VideoDetailFragment
restoreDefaultBrightness();
}
/*////////////////////////////////////////////////////////////////////////*/
public static VideoDetailFragment getInstance(final int serviceId,
@Nullable final String videoUrl,
@NonNull final String name,
@Nullable final PlayQueue queue) {
final VideoDetailFragment instance = new VideoDetailFragment();
instance.setInitialData(serviceId, videoUrl, name, queue);
return instance;
}
public static VideoDetailFragment getInstanceInCollapsedState() {
final VideoDetailFragment instance = new VideoDetailFragment();
instance.updateBottomSheetState(BottomSheetBehavior.STATE_COLLAPSED);
return instance;
}
/*//////////////////////////////////////////////////////////////////////////
// Fragment's Lifecycle
//////////////////////////////////////////////////////////////////////////*/
@Override
public void onCreate(final Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
@ -422,28 +389,25 @@ public final class VideoDetailFragment
binding = null;
}
@Override
public void onActivityResult(final int requestCode, final int resultCode, final Intent data) {
super.onActivityResult(requestCode, resultCode, data);
switch (requestCode) {
case ReCaptchaActivity.RECAPTCHA_REQUEST:
if (resultCode == Activity.RESULT_OK) {
NavigationHelper.openVideoDetailFragment(requireContext(), getFM(),
serviceId, url, title, null, false);
} else {
Log.e(TAG, "ReCaptcha failed");
}
break;
default:
Log.e(TAG, "Request code from activity not supported [" + requestCode + "]");
break;
}
}
/*//////////////////////////////////////////////////////////////////////////
// OnClick
//////////////////////////////////////////////////////////////////////////*/
@Override
public void onActivityResult(final int requestCode, final int resultCode, final Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (requestCode == ReCaptchaActivity.RECAPTCHA_REQUEST) {
if (resultCode == Activity.RESULT_OK) {
NavigationHelper.openVideoDetailFragment(requireContext(), getFM(),
serviceId, url, title, null, false);
} else {
Log.e(TAG, "ReCaptcha failed");
}
} else {
Log.e(TAG, "Request code from activity not supported [" + requestCode + "]");
}
}
private void setOnClickListeners() {
binding.detailTitleRootLayout.setOnClickListener(v -> toggleTitleAndSecondaryControls());
binding.detailUploaderRootLayout.setOnClickListener(makeOnClickListener(info -> {
@ -574,6 +538,10 @@ public final class VideoDetailFragment
}
}
/*//////////////////////////////////////////////////////////////////////////
// Init
//////////////////////////////////////////////////////////////////////////*/
private void toggleTitleAndSecondaryControls() {
if (binding.detailSecondaryControlPanel.getVisibility() == View.GONE) {
binding.detailVideoTitleView.setMaxLines(10);
@ -590,10 +558,6 @@ public final class VideoDetailFragment
updateTabLayoutVisibility();
}
/*//////////////////////////////////////////////////////////////////////////
// Init
//////////////////////////////////////////////////////////////////////////*/
@Override // called from onViewCreated in {@link BaseFragment#onViewCreated}
protected void initViews(final View rootView, final Bundle savedInstanceState) {
super.initViews(rootView, savedInstanceState);
@ -628,7 +592,7 @@ public final class VideoDetailFragment
final View.OnTouchListener controlsTouchListener = (view, motionEvent) -> {
if (motionEvent.getAction() == MotionEvent.ACTION_DOWN
&& PreferenceManager.getDefaultSharedPreferences(activity)
&& PreferenceManager.getDefaultSharedPreferences(activity)
.getBoolean(getString(R.string.show_hold_to_append_key), true)) {
animate(binding.touchAppendDetail, true, 250, AnimationType.ALPHA, 0, () ->
@ -656,6 +620,10 @@ public final class VideoDetailFragment
}
}
/*//////////////////////////////////////////////////////////////////////////
// OwnStack
//////////////////////////////////////////////////////////////////////////*/
private void initThumbnailViews(@NonNull final StreamInfo info) {
PicassoHelper.loadDetailsThumbnail(info.getThumbnailUrl()).tag(PICASSO_VIDEO_DETAILS_TAG)
.into(binding.detailThumbnailImageView, new Callback() {
@ -677,16 +645,6 @@ public final class VideoDetailFragment
.into(binding.detailUploaderThumbnailView);
}
/*//////////////////////////////////////////////////////////////////////////
// OwnStack
//////////////////////////////////////////////////////////////////////////*/
/**
* Stack that contains the "navigation history".<br>
* The peek is the current video.
*/
private static LinkedList<StackItem> stack = new LinkedList<>();
@Override
public boolean onKeyDown(final int keyCode) {
return isPlayerAvailable()
@ -817,7 +775,7 @@ public final class VideoDetailFragment
}
protected void prepareAndLoadInfo() {
private void prepareAndLoadInfo() {
scrollToTop();
startLoading(false);
}
@ -1286,23 +1244,6 @@ public final class VideoDetailFragment
binding.playerPlaceholder.requestLayout();
}
private final ViewTreeObserver.OnPreDrawListener preDrawListener =
new ViewTreeObserver.OnPreDrawListener() {
@Override
public boolean onPreDraw() {
final DisplayMetrics metrics = getResources().getDisplayMetrics();
if (getView() != null) {
final int height = (DeviceUtils.isInMultiWindow(activity)
? requireView()
: activity.getWindow().getDecorView()).getHeight();
setHeightThumbnail(height, metrics);
getView().getViewTreeObserver().removeOnPreDrawListener(preDrawListener);
}
return false;
}
};
/**
* Method which controls the size of thumbnail and the size of main player inside
* a layout with thumbnail. It decides what height the player should have in both
@ -1331,7 +1272,22 @@ public final class VideoDetailFragment
: metrics.heightPixels / 2.0f);
setHeightThumbnail(height, metrics);
}
}
} private final ViewTreeObserver.OnPreDrawListener preDrawListener =
new ViewTreeObserver.OnPreDrawListener() {
@Override
public boolean onPreDraw() {
final DisplayMetrics metrics = getResources().getDisplayMetrics();
if (getView() != null) {
final int height = (DeviceUtils.isInMultiWindow(activity)
? requireView()
: activity.getWindow().getDecorView()).getHeight();
setHeightThumbnail(height, metrics);
getView().getViewTreeObserver().removeOnPreDrawListener(preDrawListener);
}
return false;
}
};
private void setHeightThumbnail(final int newHeight, final DisplayMetrics metrics) {
binding.detailThumbnailImageView.setLayoutParams(
@ -1350,10 +1306,10 @@ public final class VideoDetailFragment
binding.detailContentRootHiding.setVisibility(View.VISIBLE);
}
protected void setInitialData(final int newServiceId,
@Nullable final String newUrl,
@NonNull final String newTitle,
@Nullable final PlayQueue newPlayQueue) {
private void setInitialData(final int newServiceId,
@Nullable final String newUrl,
@NonNull final String newTitle,
@Nullable final PlayQueue newPlayQueue) {
this.serviceId = newServiceId;
this.url = newUrl;
this.title = newTitle;
@ -1422,11 +1378,6 @@ public final class VideoDetailFragment
activity.registerReceiver(broadcastReceiver, intentFilter);
}
/*//////////////////////////////////////////////////////////////////////////
// Orientation listener
//////////////////////////////////////////////////////////////////////////*/
private void restoreDefaultOrientation() {
if (isPlayerAvailable() && player.videoPlayerSelected()) {
toggleFullscreenIfInFullscreenMode();
@ -1441,8 +1392,9 @@ public final class VideoDetailFragment
}
}
/*//////////////////////////////////////////////////////////////////////////
// Contract
// Orientation listener
//////////////////////////////////////////////////////////////////////////*/
@Override
@ -1482,6 +1434,10 @@ public final class VideoDetailFragment
binding.detailSubChannelThumbnailView.setImageBitmap(null);
}
/*//////////////////////////////////////////////////////////////////////////
// Contract
//////////////////////////////////////////////////////////////////////////*/
@Override
public void handleResult(@NonNull final StreamInfo info) {
super.handleResult(info);
@ -1670,10 +1626,6 @@ public final class VideoDetailFragment
}
}
/*//////////////////////////////////////////////////////////////////////////
// Stream Results
//////////////////////////////////////////////////////////////////////////*/
private void updateProgressInfo(@NonNull final StreamInfo info) {
if (positionSubscriber != null) {
positionSubscriber.dispose();
@ -1726,6 +1678,10 @@ public final class VideoDetailFragment
});
}
/*//////////////////////////////////////////////////////////////////////////
// Stream Results
//////////////////////////////////////////////////////////////////////////*/
private void showPlaybackProgress(final long progress, final long duration) {
final int progressSeconds = (int) TimeUnit.MILLISECONDS.toSeconds(progress);
final int durationSeconds = (int) TimeUnit.MILLISECONDS.toSeconds(duration);
@ -1749,15 +1705,15 @@ public final class VideoDetailFragment
}
}
/*//////////////////////////////////////////////////////////////////////////
// Player event listener
//////////////////////////////////////////////////////////////////////////*/
@Override
public void onViewCreated() {
tryAddVideoPlayerView();
}
/*//////////////////////////////////////////////////////////////////////////
// Player event listener
//////////////////////////////////////////////////////////////////////////*/
@Override
public void onQueueUpdate(final PlayQueue queue) {
playQueue = queue;
@ -1806,16 +1762,14 @@ public final class VideoDetailFragment
final PlaybackParameters parameters) {
setOverlayPlayPauseImage(player != null && player.isPlaying());
switch (state) {
case Player.STATE_PLAYING:
if (binding.positionView.getAlpha() != 1.0f
&& player.getPlayQueue() != null
&& player.getPlayQueue().getItem() != null
&& player.getPlayQueue().getItem().getUrl().equals(url)) {
animate(binding.positionView, true, 100);
animate(binding.detailPositionView, true, 100);
}
break;
if (state == Player.STATE_PLAYING) {
if (binding.positionView.getAlpha() != 1.0f
&& player.getPlayQueue() != null
&& player.getPlayQueue().getItem() != null
&& player.getPlayQueue().getItem().getUrl().equals(url)) {
animate(binding.positionView, true, 100);
animate(binding.detailPositionView, true, 100);
}
}
}
@ -1948,10 +1902,6 @@ public final class VideoDetailFragment
valueAnimator.start();
}
/*//////////////////////////////////////////////////////////////////////////
// Player related utils
//////////////////////////////////////////////////////////////////////////*/
private void showSystemUi() {
if (DEBUG) {
Log.d(TAG, "showSystemUi() called");
@ -1974,6 +1924,10 @@ public final class VideoDetailFragment
android.R.attr.colorPrimary));
}
/*//////////////////////////////////////////////////////////////////////////
// Player related utils
//////////////////////////////////////////////////////////////////////////*/
private void hideSystemUi() {
if (DEBUG) {
Log.d(TAG, "hideSystemUi() called");
@ -2197,10 +2151,6 @@ public final class VideoDetailFragment
updateOverlayData(null, null, null);
}
/*//////////////////////////////////////////////////////////////////////////
// Bottom mini player
//////////////////////////////////////////////////////////////////////////*/
/**
* That's for Android TV support. Move focus from main fragment to the player or back
* based on what is currently selected
@ -2235,6 +2185,10 @@ public final class VideoDetailFragment
}
}
/*//////////////////////////////////////////////////////////////////////////
// Bottom mini player
//////////////////////////////////////////////////////////////////////////*/
/**
* When the mini player exists the view underneath it is not touchable.
* Bottom padding should be equal to the mini player's height in this case
@ -2443,4 +2397,6 @@ public final class VideoDetailFragment
lastStableBottomSheetState = newState;
}
}
}

View File

@ -1,9 +1,5 @@
package org.schabi.newpipe.fragments.detail;
import static com.google.android.exoplayer2.PlaybackException.ERROR_CODE_BEHIND_LIVE_WINDOW;
import static com.google.android.exoplayer2.PlaybackException.ERROR_CODE_DECODING_FAILED;
import static com.google.android.exoplayer2.PlaybackException.ERROR_CODE_UNSPECIFIED;
import android.content.Context;
import android.util.Log;
import android.util.Pair;
@ -13,15 +9,12 @@ import android.view.ViewGroup;
import android.widget.RadioButton;
import android.widget.RadioGroup;
import android.widget.Toast;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.appcompat.app.AlertDialog;
import com.google.android.exoplayer2.C;
import com.google.android.exoplayer2.ExoPlaybackException;
import com.google.android.exoplayer2.PlaybackException;
import org.schabi.newpipe.R;
import org.schabi.newpipe.databinding.ListRadioIconItemBinding;
import org.schabi.newpipe.databinding.SingleChoiceDialogViewBinding;
@ -32,6 +25,8 @@ import java.io.IOException;
import java.util.List;
import java.util.function.Supplier;
import static com.google.android.exoplayer2.PlaybackException.*;
/**
* Outsourced logic for crashing the player in the {@link VideoDetailFragment}.
*/
@ -46,25 +41,25 @@ public final class VideoDetailPlayerCrasher {
private static final List<Pair<String, Supplier<ExoPlaybackException>>>
AVAILABLE_EXCEPTION_TYPES = List.of(
new Pair<>("Source", () -> ExoPlaybackException.createForSource(
new IOException(DEFAULT_MSG),
ERROR_CODE_BEHIND_LIVE_WINDOW
)),
new Pair<>("Renderer", () -> ExoPlaybackException.createForRenderer(
new Exception(DEFAULT_MSG),
"Dummy renderer",
0,
null,
C.FORMAT_HANDLED,
/*isRecoverable=*/false,
ERROR_CODE_DECODING_FAILED
)),
new Pair<>("Unexpected", () -> ExoPlaybackException.createForUnexpected(
new RuntimeException(DEFAULT_MSG),
ERROR_CODE_UNSPECIFIED
)),
new Pair<>("Remote", () -> ExoPlaybackException.createForRemote(DEFAULT_MSG))
);
new Pair<>("Source", () -> ExoPlaybackException.createForSource(
new IOException(DEFAULT_MSG),
ERROR_CODE_BEHIND_LIVE_WINDOW
)),
new Pair<>("Renderer", () -> ExoPlaybackException.createForRenderer(
new Exception(DEFAULT_MSG),
"Dummy renderer",
0,
null,
C.FORMAT_HANDLED,
/*isRecoverable=*/false,
ERROR_CODE_DECODING_FAILED
)),
new Pair<>("Unexpected", () -> ExoPlaybackException.createForUnexpected(
new RuntimeException(DEFAULT_MSG),
ERROR_CODE_UNSPECIFIED
)),
new Pair<>("Remote", () -> ExoPlaybackException.createForRemote(DEFAULT_MSG))
);
private VideoDetailPlayerCrasher() {
// No impls
@ -127,6 +122,7 @@ public final class VideoDetailPlayerCrasher {
/**
* Note that this method does not crash the underlying exoplayer directly (it's not possible).
* It simply supplies a Exception to {@link Player#onPlayerError(PlaybackException)}.
*
* @param player
* @param exception
*/

View File

@ -1,8 +1,5 @@
package org.schabi.newpipe.fragments.list;
import static org.schabi.newpipe.ktx.ViewUtils.animate;
import static org.schabi.newpipe.ktx.ViewUtils.animateHideRecyclerViewAllowingScrolling;
import android.content.Context;
import android.content.SharedPreferences;
import android.content.res.Resources;
@ -11,14 +8,12 @@ import android.util.Log;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.View;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.appcompat.app.ActionBar;
import androidx.preference.PreferenceManager;
import androidx.recyclerview.widget.GridLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import org.schabi.newpipe.R;
import org.schabi.newpipe.error.ErrorUtil;
import org.schabi.newpipe.extractor.InfoItem;
@ -38,21 +33,22 @@ import java.util.List;
import java.util.Queue;
import java.util.function.Supplier;
import static org.schabi.newpipe.ktx.ViewUtils.animate;
import static org.schabi.newpipe.ktx.ViewUtils.animateHideRecyclerViewAllowingScrolling;
public abstract class BaseListFragment<I, N> extends BaseStateFragment<I>
implements ListViewContract<I, N>, StateSaver.WriteRead,
SharedPreferences.OnSharedPreferenceChangeListener {
private static final int LIST_MODE_UPDATE_FLAG = 0x32;
protected org.schabi.newpipe.util.SavedState savedState;
private boolean useDefaultStateSaving = true;
private int updateFlags = 0;
protected InfoListAdapter infoListAdapter;
protected RecyclerView itemsList;
/*//////////////////////////////////////////////////////////////////////////
// Views
//////////////////////////////////////////////////////////////////////////*/
protected InfoListAdapter infoListAdapter;
protected RecyclerView itemsList;
private boolean useDefaultStateSaving = true;
private int updateFlags = 0;
private int focusedPosition = -1;
/*//////////////////////////////////////////////////////////////////////////
@ -366,13 +362,6 @@ public abstract class BaseListFragment<I, N> extends BaseStateFragment<I>
});
}
class DefaultItemListOnScrolledDownListener extends OnScrollBelowItemsListener {
@Override
public void onScrolledDown(final RecyclerView recyclerView) {
onScrollToBottom();
}
}
private void onStreamSelected(final StreamInfoItem selectedItem) {
onItemSelected(selectedItem);
NavigationHelper.openVideoDetailFragment(requireContext(), getFM(),
@ -394,10 +383,6 @@ public abstract class BaseListFragment<I, N> extends BaseStateFragment<I>
}
}
/*//////////////////////////////////////////////////////////////////////////
// Menu
//////////////////////////////////////////////////////////////////////////*/
@Override
public void onCreateOptionsMenu(@NonNull final Menu menu,
@NonNull final MenuInflater inflater) {
@ -414,7 +399,7 @@ public abstract class BaseListFragment<I, N> extends BaseStateFragment<I>
}
/*//////////////////////////////////////////////////////////////////////////
// Load and handle
// Menu
//////////////////////////////////////////////////////////////////////////*/
@Override
@ -423,20 +408,24 @@ public abstract class BaseListFragment<I, N> extends BaseStateFragment<I>
super.startLoading(forceLoad);
}
/*//////////////////////////////////////////////////////////////////////////
// Load and handle
//////////////////////////////////////////////////////////////////////////*/
protected abstract void loadMoreItems();
protected abstract boolean hasMoreItems();
/*//////////////////////////////////////////////////////////////////////////
// Contract
//////////////////////////////////////////////////////////////////////////*/
@Override
public void showLoading() {
super.showLoading();
animateHideRecyclerViewAllowingScrolling(itemsList);
}
/*//////////////////////////////////////////////////////////////////////////
// Contract
//////////////////////////////////////////////////////////////////////////*/
@Override
public void hideLoading() {
super.hideLoading();
@ -481,9 +470,17 @@ public abstract class BaseListFragment<I, N> extends BaseStateFragment<I>
/**
* Returns preferred item view mode.
*
* @return ItemViewMode
*/
protected ItemViewMode getItemViewMode() {
return ThemeHelper.getItemViewMode(requireContext());
}
class DefaultItemListOnScrolledDownListener extends OnScrollBelowItemsListener {
@Override
public void onScrolledDown(final RecyclerView recyclerView) {
onScrollToBottom();
}
}
}

View File

@ -4,9 +4,12 @@ import android.os.Bundle;
import android.text.TextUtils;
import android.util.Log;
import android.view.View;
import androidx.annotation.NonNull;
import icepick.State;
import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers;
import io.reactivex.rxjava3.core.Single;
import io.reactivex.rxjava3.disposables.Disposable;
import io.reactivex.rxjava3.schedulers.Schedulers;
import org.schabi.newpipe.error.ErrorInfo;
import org.schabi.newpipe.error.UserAction;
import org.schabi.newpipe.extractor.InfoItem;
@ -22,22 +25,15 @@ import java.util.ArrayList;
import java.util.List;
import java.util.Queue;
import icepick.State;
import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers;
import io.reactivex.rxjava3.core.Single;
import io.reactivex.rxjava3.disposables.Disposable;
import io.reactivex.rxjava3.schedulers.Schedulers;
public abstract class BaseListInfoFragment<I extends InfoItem, L extends ListInfo<I>>
extends BaseListFragment<L, ListExtractor.InfoItemsPage<I>> {
private final UserAction errorUserAction;
@State
protected int serviceId = Constants.NO_SERVICE_ID;
@State
protected String name;
@State
protected String url;
private final UserAction errorUserAction;
protected L currentInfo;
protected Page currentNextPage;
protected Disposable currentWorker;

View File

@ -1,31 +1,28 @@
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.graphics.Color;
import android.os.Bundle;
import android.text.TextUtils;
import android.util.Log;
import android.util.TypedValue;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
import android.view.*;
import android.widget.Button;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.appcompat.app.ActionBar;
import androidx.core.content.ContextCompat;
import com.google.android.material.snackbar.Snackbar;
import com.jakewharton.rxbinding4.view.RxView;
import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers;
import io.reactivex.rxjava3.core.Observable;
import io.reactivex.rxjava3.core.Single;
import io.reactivex.rxjava3.disposables.CompositeDisposable;
import io.reactivex.rxjava3.disposables.Disposable;
import io.reactivex.rxjava3.functions.Action;
import io.reactivex.rxjava3.functions.Consumer;
import io.reactivex.rxjava3.functions.Function;
import io.reactivex.rxjava3.schedulers.Schedulers;
import org.schabi.newpipe.R;
import org.schabi.newpipe.database.subscription.NotificationMode;
import org.schabi.newpipe.database.subscription.SubscriptionEntity;
@ -41,16 +38,12 @@ import org.schabi.newpipe.extractor.exceptions.ContentNotSupportedException;
import org.schabi.newpipe.extractor.stream.StreamInfoItem;
import org.schabi.newpipe.fragments.list.BaseListInfoFragment;
import org.schabi.newpipe.ktx.AnimationType;
import org.schabi.newpipe.local.subscription.SubscriptionManager;
import org.schabi.newpipe.local.feed.notifications.NotificationHelper;
import org.schabi.newpipe.local.subscription.SubscriptionManager;
import org.schabi.newpipe.player.PlayerType;
import org.schabi.newpipe.player.playqueue.ChannelPlayQueue;
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.PicassoHelper;
import org.schabi.newpipe.util.ThemeHelper;
import org.schabi.newpipe.util.*;
import org.schabi.newpipe.util.external_communication.ShareUtils;
import java.util.List;
@ -58,15 +51,9 @@ 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;
import io.reactivex.rxjava3.core.Single;
import io.reactivex.rxjava3.disposables.CompositeDisposable;
import io.reactivex.rxjava3.disposables.Disposable;
import io.reactivex.rxjava3.functions.Action;
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<StreamInfoItem, ChannelInfo>
implements View.OnClickListener {
@ -92,6 +79,10 @@ public class ChannelFragment extends BaseListInfoFragment<StreamInfoItem, Channe
private MenuItem menuRssButton;
private MenuItem menuNotifyButton;
public ChannelFragment() {
super(UserAction.REQUESTED_CHANNEL);
}
public static ChannelFragment getInstance(final int serviceId, final String url,
final String name) {
final ChannelFragment instance = new ChannelFragment();
@ -99,10 +90,6 @@ public class ChannelFragment extends BaseListInfoFragment<StreamInfoItem, Channe
return instance;
}
public ChannelFragment() {
super(UserAction.REQUESTED_CHANNEL);
}
@Override
public void onResume() {
super.onResume();

View File

@ -1,16 +1,12 @@
package org.schabi.newpipe.fragments.list.comments;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.View;
import android.view.ViewGroup;
import android.view.*;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import io.reactivex.rxjava3.core.Single;
import io.reactivex.rxjava3.disposables.CompositeDisposable;
import org.schabi.newpipe.R;
import org.schabi.newpipe.error.UserAction;
import org.schabi.newpipe.extractor.ListExtractor;
@ -21,14 +17,15 @@ import org.schabi.newpipe.info_list.ItemViewMode;
import org.schabi.newpipe.ktx.ViewUtils;
import org.schabi.newpipe.util.ExtractorHelper;
import io.reactivex.rxjava3.core.Single;
import io.reactivex.rxjava3.disposables.CompositeDisposable;
public class CommentsFragment extends BaseListInfoFragment<CommentsInfoItem, CommentsInfo> {
private final CompositeDisposable disposables = new CompositeDisposable();
private TextView emptyStateDesc;
public CommentsFragment() {
super(UserAction.REQUESTED_COMMENTS);
}
public static CommentsFragment getInstance(final int serviceId, final String url,
final String name) {
final CommentsFragment instance = new CommentsFragment();
@ -36,10 +33,6 @@ public class CommentsFragment extends BaseListInfoFragment<CommentsInfoItem, Com
return instance;
}
public CommentsFragment() {
super(UserAction.REQUESTED_COMMENTS);
}
@Override
protected void initViews(final View rootView, final Bundle savedInstanceState) {
super.initViews(rootView, savedInstanceState);
@ -100,11 +93,13 @@ public class CommentsFragment extends BaseListInfoFragment<CommentsInfoItem, Com
//////////////////////////////////////////////////////////////////////////*/
@Override
public void setTitle(final String title) { }
public void setTitle(final String title) {
}
@Override
public void onCreateOptionsMenu(@NonNull final Menu menu,
@NonNull final MenuInflater inflater) { }
@NonNull final MenuInflater inflater) {
}
@Override
protected ItemViewMode getItemViewMode() {

View File

@ -1,7 +1,6 @@
package org.schabi.newpipe.fragments.list.kiosk;
import android.os.Bundle;
import org.schabi.newpipe.error.ErrorInfo;
import org.schabi.newpipe.error.UserAction;
import org.schabi.newpipe.extractor.NewPipe;

View File

@ -1,16 +1,12 @@
package org.schabi.newpipe.fragments.list.kiosk;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.View;
import android.view.ViewGroup;
import android.view.*;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.appcompat.app.ActionBar;
import icepick.State;
import io.reactivex.rxjava3.core.Single;
import org.schabi.newpipe.R;
import org.schabi.newpipe.error.ErrorInfo;
import org.schabi.newpipe.error.UserAction;
@ -27,9 +23,6 @@ import org.schabi.newpipe.util.ExtractorHelper;
import org.schabi.newpipe.util.KioskTranslator;
import org.schabi.newpipe.util.Localization;
import icepick.State;
import io.reactivex.rxjava3.core.Single;
/**
* Created by Christian Schabesberger on 23.09.17.
* <p>
@ -65,6 +58,10 @@ public class KioskFragment extends BaseListInfoFragment<StreamInfoItem, KioskInf
// Views
//////////////////////////////////////////////////////////////////////////*/
public KioskFragment() {
super(UserAction.REQUESTED_KIOSK);
}
public static KioskFragment getInstance(final int serviceId) throws ExtractionException {
return getInstance(serviceId, NewPipe.getService(serviceId)
.getKioskList().getDefaultKioskId());
@ -82,10 +79,6 @@ public class KioskFragment extends BaseListInfoFragment<StreamInfoItem, KioskInf
return instance;
}
public KioskFragment() {
super(UserAction.REQUESTED_KIOSK);
}
/*//////////////////////////////////////////////////////////////////////////
// LifeCycle
//////////////////////////////////////////////////////////////////////////*/

View File

@ -1,26 +1,20 @@
package org.schabi.newpipe.fragments.list.playlist;
import static org.schabi.newpipe.ktx.ViewUtils.animate;
import static org.schabi.newpipe.ktx.ViewUtils.animateHideRecyclerViewAllowingScrolling;
import android.content.Context;
import android.os.Bundle;
import android.text.TextUtils;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
import android.view.*;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.appcompat.content.res.AppCompatResources;
import com.google.android.material.shape.CornerFamily;
import com.google.android.material.shape.ShapeAppearanceModel;
import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers;
import io.reactivex.rxjava3.core.Flowable;
import io.reactivex.rxjava3.core.Single;
import io.reactivex.rxjava3.disposables.CompositeDisposable;
import io.reactivex.rxjava3.disposables.Disposable;
import org.reactivestreams.Subscriber;
import org.reactivestreams.Subscription;
import org.schabi.newpipe.NewPipeDatabase;
@ -58,11 +52,8 @@ import java.util.concurrent.atomic.AtomicBoolean;
import java.util.function.Supplier;
import java.util.stream.Collectors;
import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers;
import io.reactivex.rxjava3.core.Flowable;
import io.reactivex.rxjava3.core.Single;
import io.reactivex.rxjava3.disposables.CompositeDisposable;
import io.reactivex.rxjava3.disposables.Disposable;
import static org.schabi.newpipe.ktx.ViewUtils.animate;
import static org.schabi.newpipe.ktx.ViewUtils.animateHideRecyclerViewAllowingScrolling;
public class PlaylistFragment extends BaseListInfoFragment<StreamInfoItem, PlaylistInfo> {
@ -84,6 +75,10 @@ public class PlaylistFragment extends BaseListInfoFragment<StreamInfoItem, Playl
private MenuItem playlistBookmarkButton;
public PlaylistFragment() {
super(UserAction.REQUESTED_PLAYLIST);
}
public static PlaylistFragment getInstance(final int serviceId, final String url,
final String name) {
final PlaylistFragment instance = new PlaylistFragment();
@ -91,10 +86,6 @@ public class PlaylistFragment extends BaseListInfoFragment<StreamInfoItem, Playl
return instance;
}
public PlaylistFragment() {
super(UserAction.REQUESTED_PLAYLIST);
}
/*//////////////////////////////////////////////////////////////////////////
// LifeCycle
//////////////////////////////////////////////////////////////////////////*/
@ -311,7 +302,7 @@ public class PlaylistFragment extends BaseListInfoFragment<StreamInfoItem, Playl
.getColorStateList(requireContext(), R.color.transparent_background_color));
headerBinding.uploaderAvatarView.setImageDrawable(
AppCompatResources.getDrawable(requireContext(),
R.drawable.ic_radio)
R.drawable.ic_radio)
);
} else {
PicassoHelper.loadAvatar(avatarUrl).tag(PICASSO_PLAYLIST_TAG)
@ -420,7 +411,8 @@ public class PlaylistFragment extends BaseListInfoFragment<StreamInfoItem, Playl
}
@Override
public void onComplete() { }
public void onComplete() {
}
};
}

View File

@ -1,10 +1,5 @@
package org.schabi.newpipe.fragments.list.search;
import static androidx.recyclerview.widget.ItemTouchHelper.Callback.makeMovementFlags;
import static org.schabi.newpipe.ktx.ViewUtils.animate;
import static org.schabi.newpipe.util.ExtractorHelper.showMetaInfoInTextView;
import static java.util.Arrays.asList;
import android.app.Activity;
import android.content.Context;
import android.content.Intent;
@ -16,18 +11,11 @@ import android.text.TextUtils;
import android.text.TextWatcher;
import android.text.style.CharacterStyle;
import android.util.Log;
import android.view.KeyEvent;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
import android.view.*;
import android.view.animation.DecelerateInterpolator;
import android.view.inputmethod.EditorInfo;
import android.widget.EditText;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.appcompat.app.ActionBar;
@ -38,19 +26,21 @@ import androidx.core.text.HtmlCompat;
import androidx.preference.PreferenceManager;
import androidx.recyclerview.widget.ItemTouchHelper;
import androidx.recyclerview.widget.RecyclerView;
import icepick.State;
import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers;
import io.reactivex.rxjava3.core.Observable;
import io.reactivex.rxjava3.core.Single;
import io.reactivex.rxjava3.disposables.CompositeDisposable;
import io.reactivex.rxjava3.disposables.Disposable;
import io.reactivex.rxjava3.schedulers.Schedulers;
import io.reactivex.rxjava3.subjects.PublishSubject;
import org.schabi.newpipe.R;
import org.schabi.newpipe.databinding.FragmentSearchBinding;
import org.schabi.newpipe.error.ErrorInfo;
import org.schabi.newpipe.error.ErrorUtil;
import org.schabi.newpipe.error.ReCaptchaActivity;
import org.schabi.newpipe.error.UserAction;
import org.schabi.newpipe.extractor.InfoItem;
import org.schabi.newpipe.extractor.ListExtractor;
import org.schabi.newpipe.extractor.MetaInfo;
import org.schabi.newpipe.extractor.NewPipe;
import org.schabi.newpipe.extractor.Page;
import org.schabi.newpipe.extractor.StreamingService;
import org.schabi.newpipe.extractor.*;
import org.schabi.newpipe.extractor.search.SearchExtractor;
import org.schabi.newpipe.extractor.search.SearchInfo;
import org.schabi.newpipe.extractor.services.peertube.linkHandler.PeertubeSearchQueryHandlerFactory;
@ -61,29 +51,16 @@ import org.schabi.newpipe.ktx.AnimationType;
import org.schabi.newpipe.ktx.ExceptionUtils;
import org.schabi.newpipe.local.history.HistoryRecordManager;
import org.schabi.newpipe.settings.NewPipeSettings;
import org.schabi.newpipe.util.Constants;
import org.schabi.newpipe.util.DeviceUtils;
import org.schabi.newpipe.util.ExtractorHelper;
import org.schabi.newpipe.util.KeyboardUtil;
import org.schabi.newpipe.util.NavigationHelper;
import org.schabi.newpipe.util.ServiceHelper;
import org.schabi.newpipe.util.*;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Queue;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;
import icepick.State;
import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers;
import io.reactivex.rxjava3.core.Observable;
import io.reactivex.rxjava3.core.Single;
import io.reactivex.rxjava3.disposables.CompositeDisposable;
import io.reactivex.rxjava3.disposables.Disposable;
import io.reactivex.rxjava3.schedulers.Schedulers;
import io.reactivex.rxjava3.subjects.PublishSubject;
import static androidx.recyclerview.widget.ItemTouchHelper.Callback.makeMovementFlags;
import static java.util.Arrays.asList;
import static org.schabi.newpipe.ktx.ViewUtils.animate;
import static org.schabi.newpipe.util.ExtractorHelper.showMetaInfoInTextView;
public class SearchFragment extends BaseListFragment<SearchInfo, ListExtractor.InfoItemsPage<?>>
implements BackPressable {
@ -103,53 +80,40 @@ public class SearchFragment extends BaseListFragment<SearchInfo, ListExtractor.I
*/
private static final int SUGGESTIONS_DEBOUNCE = 120; //ms
private final PublishSubject<String> suggestionPublisher = PublishSubject.create();
@State
int filterItemCheckedId = -1;
private final SparseArrayCompat<String> menuItemToFilterName = new SparseArrayCompat<>();
private final CompositeDisposable disposables = new CompositeDisposable();
@State
protected int serviceId = Constants.NO_SERVICE_ID;
@State
int filterItemCheckedId = -1;
// these three represents the current search query
@State
String searchString;
/**
* No content filter should add like contentFilter = all
* be aware of this when implementing an extractor.
*/
@State
String[] contentFilter = new String[0];
@State
String sortFilter;
// these represents the last search
@State
String lastSearchedString;
@State
String searchSuggestion;
@State
boolean isCorrectedSearch;
@State
MetaInfo[] metaInfo;
@State
boolean wasSearchFocused = false;
private final SparseArrayCompat<String> menuItemToFilterName = new SparseArrayCompat<>();
private StreamingService service;
private Page nextPage;
private boolean showLocalSuggestions = true;
private boolean showRemoteSuggestions = true;
private Disposable searchDisposable;
private Disposable suggestionDisposable;
private final CompositeDisposable disposables = new CompositeDisposable();
private SuggestionListAdapter suggestionListAdapter;
private HistoryRecordManager historyRecordManager;
@ -717,9 +681,9 @@ public class SearchFragment extends BaseListFragment<SearchInfo, ListExtractor.I
.getRelatedSearches(query, similarQueryLimit, 25)
.toObservable()
.map(searchHistoryEntries ->
searchHistoryEntries.stream()
.map(entry -> new SuggestionItem(true, entry))
.collect(Collectors.toList()));
searchHistoryEntries.stream()
.map(entry -> new SuggestionItem(true, entry))
.collect(Collectors.toList()));
}
private Observable<List<SuggestionItem>> getRemoteSuggestionsObservable(final String query) {
@ -754,14 +718,14 @@ public class SearchFragment extends BaseListFragment<SearchInfo, ListExtractor.I
if (showLocalSuggestions && shallShowRemoteSuggestionsNow) {
return Observable.zip(
getLocalSuggestionsObservable(query, 3),
getRemoteSuggestionsObservable(query),
(local, remote) -> {
remote.removeIf(remoteItem -> local.stream().anyMatch(
localItem -> localItem.equals(remoteItem)));
local.addAll(remote);
return local;
})
getLocalSuggestionsObservable(query, 3),
getRemoteSuggestionsObservable(query),
(local, remote) -> {
remote.removeIf(remoteItem -> local.stream().anyMatch(
localItem -> localItem.equals(remoteItem)));
local.addAll(remote);
return local;
})
.materialize();
} else if (showLocalSuggestions) {
return getLocalSuggestionsObservable(query, 25)
@ -786,12 +750,12 @@ public class SearchFragment extends BaseListFragment<SearchInfo, ListExtractor.I
} else if (listNotification.isOnError()
&& listNotification.getError() != null
&& !ExceptionUtils.isInterruptedCaused(
listNotification.getError())) {
listNotification.getError())) {
showSnackBarError(new ErrorInfo(listNotification.getError(),
UserAction.GET_SUGGESTIONS, searchString, serviceId));
}
}, throwable -> showSnackBarError(new ErrorInfo(
throwable, UserAction.GET_SUGGESTIONS, searchString, serviceId)));
throwable, UserAction.GET_SUGGESTIONS, searchString, serviceId)));
}
@Override
@ -839,7 +803,8 @@ public class SearchFragment extends BaseListFragment<SearchInfo, ListExtractor.I
disposables.add(historyRecordManager.onSearched(serviceId, theSearchString)
.observeOn(AndroidSchedulers.mainThread())
.subscribe(
ignored -> { },
ignored -> {
},
throwable -> showSnackBarError(new ErrorInfo(throwable, UserAction.SEARCHED,
theSearchString, serviceId))
));
@ -855,9 +820,9 @@ public class SearchFragment extends BaseListFragment<SearchInfo, ListExtractor.I
searchDisposable.dispose();
}
searchDisposable = ExtractorHelper.searchFor(serviceId,
searchString,
Arrays.asList(contentFilter),
sortFilter)
searchString,
Arrays.asList(contentFilter),
sortFilter)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.doOnEvent((searchResult, throwable) -> isLoading.set(false))
@ -876,11 +841,11 @@ public class SearchFragment extends BaseListFragment<SearchInfo, ListExtractor.I
searchDisposable.dispose();
}
searchDisposable = ExtractorHelper.getMoreSearchItems(
serviceId,
searchString,
asList(contentFilter),
sortFilter,
nextPage)
serviceId,
searchString,
asList(contentFilter),
sortFilter,
nextPage)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.doOnEvent((nextItemsResult, throwable) -> isLoading.set(false))

View File

@ -3,8 +3,8 @@ package org.schabi.newpipe.fragments.list.search;
import androidx.annotation.NonNull;
public class SuggestionItem {
final boolean fromHistory;
public final String query;
final boolean fromHistory;
public SuggestionItem(final boolean fromHistory, final String query) {
this.fromHistory = fromHistory;

View File

@ -2,12 +2,10 @@ package org.schabi.newpipe.fragments.list.search;
import android.view.LayoutInflater;
import android.view.ViewGroup;
import androidx.annotation.NonNull;
import androidx.recyclerview.widget.DiffUtil;
import androidx.recyclerview.widget.ListAdapter;
import androidx.recyclerview.widget.RecyclerView;
import org.schabi.newpipe.R;
import org.schabi.newpipe.databinding.ItemSearchSuggestionBinding;

View File

@ -2,16 +2,11 @@ package org.schabi.newpipe.fragments.list.videos;
import android.content.SharedPreferences;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.View;
import android.view.ViewGroup;
import android.view.*;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.preference.PreferenceManager;
import io.reactivex.rxjava3.core.Single;
import org.schabi.newpipe.R;
import org.schabi.newpipe.databinding.RelatedItemsHeaderBinding;
import org.schabi.newpipe.error.UserAction;
@ -26,8 +21,6 @@ import org.schabi.newpipe.util.RelatedItemInfo;
import java.io.Serializable;
import java.util.function.Supplier;
import io.reactivex.rxjava3.core.Single;
public class RelatedItemsFragment extends BaseListInfoFragment<InfoItem, RelatedItemInfo>
implements SharedPreferences.OnSharedPreferenceChangeListener {
private static final String INFO_KEY = "related_info_key";
@ -40,16 +33,16 @@ public class RelatedItemsFragment extends BaseListInfoFragment<InfoItem, Related
private RelatedItemsHeaderBinding headerBinding;
public RelatedItemsFragment() {
super(UserAction.REQUESTED_STREAM);
}
public static RelatedItemsFragment getInstance(final StreamInfo info) {
final RelatedItemsFragment instance = new RelatedItemsFragment();
instance.setInitialData(info);
return instance;
}
public RelatedItemsFragment() {
super(UserAction.REQUESTED_STREAM);
}
/*//////////////////////////////////////////////////////////////////////////
// LifeCycle
//////////////////////////////////////////////////////////////////////////*/

View File

@ -3,23 +3,13 @@ package org.schabi.newpipe.info_list;
import android.content.Context;
import android.view.View;
import android.view.ViewGroup;
import androidx.annotation.NonNull;
import org.schabi.newpipe.extractor.InfoItem;
import org.schabi.newpipe.extractor.channel.ChannelInfoItem;
import org.schabi.newpipe.extractor.comments.CommentsInfoItem;
import org.schabi.newpipe.extractor.playlist.PlaylistInfoItem;
import org.schabi.newpipe.extractor.stream.StreamInfoItem;
import org.schabi.newpipe.info_list.holder.ChannelInfoItemHolder;
import org.schabi.newpipe.info_list.holder.ChannelMiniInfoItemHolder;
import org.schabi.newpipe.info_list.holder.CommentsInfoItemHolder;
import org.schabi.newpipe.info_list.holder.CommentsMiniInfoItemHolder;
import org.schabi.newpipe.info_list.holder.InfoItemHolder;
import org.schabi.newpipe.info_list.holder.PlaylistInfoItemHolder;
import org.schabi.newpipe.info_list.holder.PlaylistMiniInfoItemHolder;
import org.schabi.newpipe.info_list.holder.StreamInfoItemHolder;
import org.schabi.newpipe.info_list.holder.StreamMiniInfoItemHolder;
import org.schabi.newpipe.info_list.holder.*;
import org.schabi.newpipe.local.history.HistoryRecordManager;
import org.schabi.newpipe.util.OnClickGesture;

View File

@ -5,32 +5,17 @@ import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.recyclerview.widget.GridLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
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;
import org.schabi.newpipe.extractor.playlist.PlaylistInfoItem;
import org.schabi.newpipe.extractor.stream.StreamInfoItem;
import org.schabi.newpipe.info_list.holder.ChannelGridInfoItemHolder;
import org.schabi.newpipe.info_list.holder.ChannelInfoItemHolder;
import org.schabi.newpipe.info_list.holder.ChannelMiniInfoItemHolder;
import org.schabi.newpipe.info_list.holder.CommentsInfoItemHolder;
import org.schabi.newpipe.info_list.holder.CommentsMiniInfoItemHolder;
import org.schabi.newpipe.info_list.holder.InfoItemHolder;
import org.schabi.newpipe.info_list.holder.PlaylistCardInfoItemHolder;
import org.schabi.newpipe.info_list.holder.PlaylistGridInfoItemHolder;
import org.schabi.newpipe.info_list.holder.PlaylistInfoItemHolder;
import org.schabi.newpipe.info_list.holder.PlaylistMiniInfoItemHolder;
import org.schabi.newpipe.info_list.holder.StreamCardInfoItemHolder;
import org.schabi.newpipe.info_list.holder.StreamGridInfoItemHolder;
import org.schabi.newpipe.info_list.holder.StreamInfoItemHolder;
import org.schabi.newpipe.info_list.holder.StreamMiniInfoItemHolder;
import org.schabi.newpipe.info_list.holder.*;
import org.schabi.newpipe.local.history.HistoryRecordManager;
import org.schabi.newpipe.util.FallbackViewHolder;
import org.schabi.newpipe.util.OnClickGesture;

View File

@ -1,7 +1,5 @@
package org.schabi.newpipe.info_list.dialog;
import static org.schabi.newpipe.MainActivity.DEBUG;
import android.app.Activity;
import android.content.Context;
import android.content.DialogInterface;
@ -9,12 +7,10 @@ import android.os.Build;
import android.util.Log;
import android.view.View;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.appcompat.app.AlertDialog;
import androidx.fragment.app.Fragment;
import androidx.preference.PreferenceManager;
import org.schabi.newpipe.App;
import org.schabi.newpipe.R;
import org.schabi.newpipe.error.ErrorInfo;
@ -31,6 +27,8 @@ import java.util.ArrayList;
import java.util.List;
import java.util.stream.Stream;
import static org.schabi.newpipe.MainActivity.DEBUG;
/**
* Dialog for a {@link StreamInfoItem}.
* The dialog's content are actions that can be performed on the {@link StreamInfoItem}.
@ -73,7 +71,7 @@ public final class InfoItemDialog {
// Call an entry's action / onClick method when the entry is selected.
final DialogInterface.OnClickListener action = (d, index) ->
entries.get(index).action.onClick(fragment, info);
entries.get(index).action.onClick(fragment, info);
dialog = new AlertDialog.Builder(activity)
.setCustomTitle(bannerView)
@ -95,11 +93,16 @@ public final class InfoItemDialog {
* {@link #setAction(StreamDialogDefaultEntry, StreamDialogEntry.StreamDialogEntryAction)}.
*/
public static class Builder {
@NonNull private final Activity activity;
@NonNull private final Context context;
@NonNull private final StreamInfoItem infoItem;
@NonNull private final Fragment fragment;
@NonNull private final List<StreamDialogEntry> entries = new ArrayList<>();
@NonNull
private final Activity activity;
@NonNull
private final Context context;
@NonNull
private final StreamInfoItem infoItem;
@NonNull
private final Fragment fragment;
@NonNull
private final List<StreamDialogEntry> entries = new ArrayList<>();
private final boolean addDefaultEntriesAutomatically;
/**
@ -132,9 +135,9 @@ public final class InfoItemDialog {
* @param context
* @param fragment
* @param infoItem the item for this dialog; all entries and their actions work with
* this {@link StreamInfoItem}
* this {@link StreamInfoItem}
* @throws IllegalArgumentException if <code>activity, context</code>
* or resources is <code>null</code>
* or resources is <code>null</code>
*/
public Builder(final Activity activity,
final Context context,
@ -173,15 +176,14 @@ public final class InfoItemDialog {
* @param context
* @param fragment
* @param infoItem
* @param addDefaultEntriesAutomatically
* whether default entries added with {@link #addDefaultBeginningEntries()}
* and {@link #addDefaultEndEntries()} are added automatically when generating
* the {@link InfoItemDialog}.
* <br/>
* Entries added with {@link #addEntry(StreamDialogDefaultEntry)} and
* {@link #addAllEntries(StreamDialogDefaultEntry...)} are added in between.
* @param addDefaultEntriesAutomatically whether default entries added with {@link #addDefaultBeginningEntries()}
* and {@link #addDefaultEndEntries()} are added automatically when generating
* the {@link InfoItemDialog}.
* <br/>
* Entries added with {@link #addEntry(StreamDialogDefaultEntry)} and
* {@link #addAllEntries(StreamDialogDefaultEntry...)} are added in between.
* @throws IllegalArgumentException if <code>activity, context</code>
* or resources is <code>null</code>
* or resources is <code>null</code>
*/
public Builder(final Activity activity,
final Context context,
@ -205,8 +207,18 @@ public final class InfoItemDialog {
}
}
public static void reportErrorDuringInitialization(final Throwable throwable,
final InfoItem item) {
ErrorUtil.showSnackbar(App.getApp().getBaseContext(), new ErrorInfo(
throwable,
UserAction.OPEN_INFO_ITEM_DIALOG,
"none",
item.getServiceId()));
}
/**
* Adds a new entry and appends it to the current entry list.
*
* @param entry the entry to add
* @return the current {@link Builder} instance
*/
@ -217,6 +229,7 @@ public final class InfoItemDialog {
/**
* Adds new entries. These are appended to the current entry list.
*
* @param newEntries the entries to add
* @return the current {@link Builder} instance
*/
@ -230,12 +243,13 @@ public final class InfoItemDialog {
* <p><strong>Warning:</strong> Only use this method when the entry has been already added.
* Changing the action of an entry which has not been added to the Builder yet
* does not have an effect.</p>
* @param entry the entry to change
*
* @param entry the entry to change
* @param action the action to perform when the entry is selected
* @return the current {@link Builder} instance
*/
public Builder setAction(@NonNull final StreamDialogDefaultEntry entry,
@NonNull final StreamDialogEntry.StreamDialogEntryAction action) {
@NonNull final StreamDialogEntry.StreamDialogEntryAction action) {
for (int i = 0; i < entries.size(); i++) {
if (entries.get(i).resource == entry.resource) {
entries.set(i, new StreamDialogEntry(entry.resource, action));
@ -249,6 +263,7 @@ public final class InfoItemDialog {
* Adds {@link StreamDialogDefaultEntry#ENQUEUE} if the player is open and
* {@link StreamDialogDefaultEntry#ENQUEUE_NEXT} if there are multiple streams
* in the play queue.
*
* @return the current {@link Builder} instance
*/
public Builder addEnqueueEntriesIfNeeded() {
@ -267,6 +282,7 @@ public final class InfoItemDialog {
* Adds the {@link StreamDialogDefaultEntry#START_HERE_ON_BACKGROUND}.
* If the {@link #infoItem} is not a pure audio (live) stream,
* {@link StreamDialogDefaultEntry#START_HERE_ON_POPUP} is added, too.
*
* @return the current {@link Builder} instance
*/
public Builder addStartHereEntries() {
@ -280,6 +296,7 @@ public final class InfoItemDialog {
/**
* Adds {@link StreamDialogDefaultEntry#MARK_AS_WATCHED} if the watch history is enabled
* and the stream is not a livestream.
*
* @return the current {@link Builder} instance
*/
public Builder addMarkAsWatchedEntryIfNeeded() {
@ -294,6 +311,7 @@ public final class InfoItemDialog {
/**
* Adds the {@link StreamDialogDefaultEntry#PLAY_WITH_KODI} entry if it is needed.
*
* @return the current {@link Builder} instance
*/
public Builder addPlayWithKodiEntryIfNeeded() {
@ -308,6 +326,7 @@ public final class InfoItemDialog {
* <br/>
* This method adds the "enqueue" (see {@link #addEnqueueEntriesIfNeeded()})
* and "start here" (see {@link #addStartHereEntries()} entries.
*
* @return the current {@link Builder} instance
*/
public Builder addDefaultBeginningEntries() {
@ -318,6 +337,7 @@ public final class InfoItemDialog {
/**
* Add the entries which are usually at the bottom of the action list.
*
* @return the current {@link Builder} instance
*/
public Builder addDefaultEndEntries() {
@ -335,6 +355,7 @@ public final class InfoItemDialog {
/**
* Creates the {@link InfoItemDialog}.
*
* @return a new instance of {@link InfoItemDialog}
*/
public InfoItemDialog create() {
@ -343,14 +364,5 @@ public final class InfoItemDialog {
}
return new InfoItemDialog(this.activity, this.fragment, this.infoItem, this.entries);
}
public static void reportErrorDuringInitialization(final Throwable throwable,
final InfoItem item) {
ErrorUtil.showSnackbar(App.getApp().getBaseContext(), new ErrorInfo(
throwable,
UserAction.OPEN_INFO_ITEM_DIALOG,
"none",
item.getServiceId()));
}
}
}

View File

@ -1,15 +1,9 @@
package org.schabi.newpipe.info_list.dialog;
import static org.schabi.newpipe.util.NavigationHelper.openChannelFragment;
import static org.schabi.newpipe.util.SparseItemUtil.fetchItemInfoIfSparse;
import static org.schabi.newpipe.util.SparseItemUtil.fetchStreamInfoAndSaveToDatabase;
import static org.schabi.newpipe.util.SparseItemUtil.fetchUploaderUrlIfSparse;
import android.net.Uri;
import androidx.annotation.NonNull;
import androidx.annotation.StringRes;
import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers;
import org.schabi.newpipe.R;
import org.schabi.newpipe.database.stream.model.StreamEntity;
import org.schabi.newpipe.download.DownloadDialog;
@ -22,22 +16,23 @@ import org.schabi.newpipe.util.external_communication.ShareUtils;
import java.util.List;
import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers;
import static org.schabi.newpipe.util.NavigationHelper.openChannelFragment;
import static org.schabi.newpipe.util.SparseItemUtil.*;
/**
* <p>
* This enum provides entries that are accepted
* by the {@link InfoItemDialog.Builder}.
* This enum provides entries that are accepted
* by the {@link InfoItemDialog.Builder}.
* </p>
* <p>
* These entries contain a String {@link #resource} which is displayed in the dialog and
* a default {@link #action} that is executed
* when the entry is selected (via <code>onClick()</code>).
* <br/>
* They action can be overridden by using the Builder's
* {@link InfoItemDialog.Builder#setAction(
* StreamDialogDefaultEntry, StreamDialogEntry.StreamDialogEntryAction)}
* method.
* These entries contain a String {@link #resource} which is displayed in the dialog and
* a default {@link #action} that is executed
* when the entry is selected (via <code>onClick()</code>).
* <br/>
* They action can be overridden by using the Builder's
* {@link InfoItemDialog.Builder#setAction(
*StreamDialogDefaultEntry, StreamDialogEntry.StreamDialogEntryAction)}
* method.
* </p>
*/
public enum StreamDialogDefaultEntry {
@ -51,7 +46,7 @@ public enum StreamDialogDefaultEntry {
*/
ENQUEUE(R.string.enqueue_stream, (fragment, item) ->
fetchItemInfoIfSparse(fragment.requireContext(), item, singlePlayQueue ->
NavigationHelper.enqueueOnPlayer(fragment.getContext(), singlePlayQueue))
NavigationHelper.enqueueOnPlayer(fragment.getContext(), singlePlayQueue))
),
/**
@ -60,17 +55,17 @@ public enum StreamDialogDefaultEntry {
*/
ENQUEUE_NEXT(R.string.enqueue_next_stream, (fragment, item) ->
fetchItemInfoIfSparse(fragment.requireContext(), item, singlePlayQueue ->
NavigationHelper.enqueueNextOnPlayer(fragment.getContext(), singlePlayQueue))
NavigationHelper.enqueueNextOnPlayer(fragment.getContext(), singlePlayQueue))
),
START_HERE_ON_BACKGROUND(R.string.start_here_on_background, (fragment, item) ->
fetchItemInfoIfSparse(fragment.requireContext(), item, singlePlayQueue ->
NavigationHelper.playOnBackgroundPlayer(
fragment.getContext(), singlePlayQueue, true))),
NavigationHelper.playOnBackgroundPlayer(
fragment.getContext(), singlePlayQueue, true))),
START_HERE_ON_POPUP(R.string.start_here_on_popup, (fragment, item) ->
fetchItemInfoIfSparse(fragment.requireContext(), item, singlePlayQueue ->
NavigationHelper.playOnPopupPlayer(fragment.getContext(), singlePlayQueue, true))),
NavigationHelper.playOnPopupPlayer(fragment.getContext(), singlePlayQueue, true))),
SET_AS_PLAYLIST_THUMBNAIL(R.string.set_as_playlist_thumbnail, (fragment, item) -> {
throw new UnsupportedOperationException("This needs to be implemented manually "
@ -87,16 +82,16 @@ public enum StreamDialogDefaultEntry {
* or create a new playlist if there are no local playlists.
*/
APPEND_PLAYLIST(R.string.add_to_playlist, (fragment, item) ->
PlaylistDialog.createCorrespondingDialog(
fragment.getContext(),
List.of(new StreamEntity(item)),
dialog -> dialog.show(
fragment.getParentFragmentManager(),
"StreamDialogEntry@"
+ (dialog instanceof PlaylistAppendDialog ? "append" : "create")
+ "_playlist"
)
)
PlaylistDialog.createCorrespondingDialog(
fragment.getContext(),
List.of(new StreamEntity(item)),
dialog -> dialog.show(
fragment.getParentFragmentManager(),
"StreamDialogEntry@"
+ (dialog instanceof PlaylistAppendDialog ? "append" : "create")
+ "_playlist"
)
)
),
PLAY_WITH_KODI(R.string.play_with_kodi_title, (fragment, item) -> {
@ -133,11 +128,11 @@ public enum StreamDialogDefaultEntry {
MARK_AS_WATCHED(R.string.mark_as_watched, (fragment, item) ->
new HistoryRecordManager(fragment.getContext())
.markAsWatched(item)
.onErrorComplete()
.observeOn(AndroidSchedulers.mainThread())
.subscribe()
new HistoryRecordManager(fragment.getContext())
.markAsWatched(item)
.onErrorComplete()
.observeOn(AndroidSchedulers.mainThread())
.subscribe()
);

View File

@ -1,11 +1,9 @@
package org.schabi.newpipe.info_list.dialog;
import android.content.Context;
import androidx.annotation.NonNull;
import androidx.annotation.StringRes;
import androidx.fragment.app.Fragment;
import org.schabi.newpipe.extractor.stream.StreamInfoItem;
public class StreamDialogEntry {

View File

@ -1,7 +1,6 @@
package org.schabi.newpipe.info_list.holder;
import android.view.ViewGroup;
import org.schabi.newpipe.R;
import org.schabi.newpipe.info_list.InfoItemBuilder;

View File

@ -1,7 +1,6 @@
package org.schabi.newpipe.info_list.holder;
import android.view.ViewGroup;
import org.schabi.newpipe.R;
import org.schabi.newpipe.info_list.InfoItemBuilder;

View File

@ -4,17 +4,15 @@ import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.TextView;
import androidx.annotation.Nullable;
import org.schabi.newpipe.R;
import org.schabi.newpipe.extractor.InfoItem;
import org.schabi.newpipe.extractor.channel.ChannelInfoItem;
import org.schabi.newpipe.extractor.utils.Utils;
import org.schabi.newpipe.info_list.InfoItemBuilder;
import org.schabi.newpipe.local.history.HistoryRecordManager;
import org.schabi.newpipe.util.PicassoHelper;
import org.schabi.newpipe.util.Localization;
import org.schabi.newpipe.util.PicassoHelper;
public class ChannelMiniInfoItemHolder extends InfoItemHolder {
private final ImageView itemThumbnailView;
@ -40,10 +38,9 @@ public class ChannelMiniInfoItemHolder extends InfoItemHolder {
@Override
public void updateFromItem(final InfoItem infoItem,
final HistoryRecordManager historyRecordManager) {
if (!(infoItem instanceof ChannelInfoItem)) {
if (!(infoItem instanceof final ChannelInfoItem item)) {
return;
}
final ChannelInfoItem item = (ChannelInfoItem) infoItem;
itemTitleView.setText(item.getName());

View File

@ -4,7 +4,6 @@ import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.TextView;
import org.schabi.newpipe.R;
import org.schabi.newpipe.extractor.InfoItem;
import org.schabi.newpipe.extractor.comments.CommentsInfoItem;
@ -49,10 +48,9 @@ public class CommentsInfoItemHolder extends CommentsMiniInfoItemHolder {
final HistoryRecordManager historyRecordManager) {
super.updateFromItem(infoItem, historyRecordManager);
if (!(infoItem instanceof CommentsInfoItem)) {
if (!(infoItem instanceof final CommentsInfoItem item)) {
return;
}
final CommentsInfoItem item = (CommentsInfoItem) infoItem;
itemTitleView.setText(item.getUploaderName());

View File

@ -11,11 +11,10 @@ import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.RelativeLayout;
import android.widget.TextView;
import androidx.annotation.Nullable;
import androidx.appcompat.app.AppCompatActivity;
import androidx.core.text.HtmlCompat;
import io.reactivex.rxjava3.disposables.CompositeDisposable;
import org.schabi.newpipe.R;
import org.schabi.newpipe.error.ErrorUtil;
import org.schabi.newpipe.extractor.InfoItem;
@ -37,8 +36,6 @@ import org.schabi.newpipe.util.text.TextLinkifier;
import java.util.function.Consumer;
import io.reactivex.rxjava3.disposables.CompositeDisposable;
public class CommentsMiniInfoItemHolder extends InfoItemHolder {
private static final String TAG = "CommentsMiniIIHolder";
private static final String ELLIPSIS = "";
@ -91,10 +88,9 @@ public class CommentsMiniInfoItemHolder extends InfoItemHolder {
@Override
public void updateFromItem(final InfoItem infoItem,
final HistoryRecordManager historyRecordManager) {
if (!(infoItem instanceof CommentsInfoItem)) {
if (!(infoItem instanceof final CommentsInfoItem item)) {
return;
}
final CommentsInfoItem item = (CommentsInfoItem) infoItem;
PicassoHelper.loadAvatar(item.getUploaderAvatarUrl()).into(itemThumbnailView);
if (PicassoHelper.getShouldLoadImages()) {

View File

@ -2,9 +2,7 @@ package org.schabi.newpipe.info_list.holder;
import android.view.LayoutInflater;
import android.view.ViewGroup;
import androidx.recyclerview.widget.RecyclerView;
import org.schabi.newpipe.extractor.InfoItem;
import org.schabi.newpipe.info_list.InfoItemBuilder;
import org.schabi.newpipe.local.history.HistoryRecordManager;
@ -42,5 +40,6 @@ public abstract class InfoItemHolder extends RecyclerView.ViewHolder {
HistoryRecordManager historyRecordManager);
public void updateState(final InfoItem infoItem,
final HistoryRecordManager historyRecordManager) { }
final HistoryRecordManager historyRecordManager) {
}
}

View File

@ -1,7 +1,6 @@
package org.schabi.newpipe.info_list.holder;
import android.view.ViewGroup;
import org.schabi.newpipe.R;
import org.schabi.newpipe.info_list.InfoItemBuilder;

View File

@ -1,7 +1,6 @@
package org.schabi.newpipe.info_list.holder;
import android.view.ViewGroup;
import org.schabi.newpipe.R;
import org.schabi.newpipe.info_list.InfoItemBuilder;

View File

@ -1,7 +1,6 @@
package org.schabi.newpipe.info_list.holder;
import android.view.ViewGroup;
import org.schabi.newpipe.R;
import org.schabi.newpipe.info_list.InfoItemBuilder;

View File

@ -3,20 +3,19 @@ package org.schabi.newpipe.info_list.holder;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.TextView;
import org.schabi.newpipe.R;
import org.schabi.newpipe.extractor.InfoItem;
import org.schabi.newpipe.extractor.playlist.PlaylistInfoItem;
import org.schabi.newpipe.info_list.InfoItemBuilder;
import org.schabi.newpipe.local.history.HistoryRecordManager;
import org.schabi.newpipe.util.PicassoHelper;
import org.schabi.newpipe.util.Localization;
import org.schabi.newpipe.util.PicassoHelper;
public class PlaylistMiniInfoItemHolder extends InfoItemHolder {
public final ImageView itemThumbnailView;
private final TextView itemStreamCountView;
public final TextView itemTitleView;
public final TextView itemUploaderView;
private final TextView itemStreamCountView;
public PlaylistMiniInfoItemHolder(final InfoItemBuilder infoItemBuilder, final int layoutId,
final ViewGroup parent) {
@ -36,10 +35,9 @@ public class PlaylistMiniInfoItemHolder extends InfoItemHolder {
@Override
public void updateFromItem(final InfoItem infoItem,
final HistoryRecordManager historyRecordManager) {
if (!(infoItem instanceof PlaylistInfoItem)) {
if (!(infoItem instanceof final PlaylistInfoItem item)) {
return;
}
final PlaylistInfoItem item = (PlaylistInfoItem) infoItem;
itemTitleView.setText(item.getName());
itemStreamCountView.setText(Localization

View File

@ -1,7 +1,6 @@
package org.schabi.newpipe.info_list.holder;
import android.view.ViewGroup;
import org.schabi.newpipe.R;
import org.schabi.newpipe.info_list.InfoItemBuilder;

View File

@ -1,7 +1,6 @@
package org.schabi.newpipe.info_list.holder;
import android.view.ViewGroup;
import org.schabi.newpipe.R;
import org.schabi.newpipe.info_list.InfoItemBuilder;

View File

@ -3,7 +3,7 @@ package org.schabi.newpipe.info_list.holder;
import android.text.TextUtils;
import android.view.ViewGroup;
import android.widget.TextView;
import androidx.preference.PreferenceManager;
import org.schabi.newpipe.R;
import org.schabi.newpipe.extractor.InfoItem;
import org.schabi.newpipe.extractor.stream.StreamInfoItem;
@ -12,8 +12,6 @@ import org.schabi.newpipe.info_list.InfoItemBuilder;
import org.schabi.newpipe.local.history.HistoryRecordManager;
import org.schabi.newpipe.util.Localization;
import androidx.preference.PreferenceManager;
import static org.schabi.newpipe.MainActivity.DEBUG;
/*
@ -58,10 +56,9 @@ public class StreamInfoItemHolder extends StreamMiniInfoItemHolder {
final HistoryRecordManager historyRecordManager) {
super.updateFromItem(infoItem, historyRecordManager);
if (!(infoItem instanceof StreamInfoItem)) {
if (!(infoItem instanceof final StreamInfoItem item)) {
return;
}
final StreamInfoItem item = (StreamInfoItem) infoItem;
itemAdditionalDetails.setText(getStreamInfoDetailLine(item));
}

View File

@ -4,9 +4,7 @@ import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.TextView;
import androidx.core.content.ContextCompat;
import org.schabi.newpipe.R;
import org.schabi.newpipe.database.stream.model.StreamStateEntity;
import org.schabi.newpipe.extractor.InfoItem;
@ -46,10 +44,9 @@ public class StreamMiniInfoItemHolder extends InfoItemHolder {
@Override
public void updateFromItem(final InfoItem infoItem,
final HistoryRecordManager historyRecordManager) {
if (!(infoItem instanceof StreamInfoItem)) {
if (!(infoItem instanceof final StreamInfoItem item)) {
return;
}
final StreamInfoItem item = (StreamInfoItem) infoItem;
itemVideoTitleView.setText(item.getName());
itemUploaderView.setText(item.getUploaderName());

View File

@ -7,7 +7,6 @@ import android.util.Log;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.View;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.appcompat.app.ActionBar;
@ -17,7 +16,6 @@ import androidx.recyclerview.widget.GridLayoutManager;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import androidx.viewbinding.ViewBinding;
import org.schabi.newpipe.R;
import org.schabi.newpipe.databinding.PignateFooterBinding;
import org.schabi.newpipe.fragments.BaseStateFragment;
@ -48,10 +46,10 @@ public abstract class BaseLocalListFragment<I, N> extends BaseStateFragment<I>
//////////////////////////////////////////////////////////////////////////*/
private static final int LIST_MODE_UPDATE_FLAG = 0x32;
private ViewBinding headerRootBinding;
private ViewBinding footerRootBinding;
protected LocalItemListAdapter itemListAdapter;
protected RecyclerView itemsList;
private ViewBinding headerRootBinding;
private ViewBinding footerRootBinding;
private int updateFlags = 0;
/*//////////////////////////////////////////////////////////////////////////

View File

@ -1,7 +1,6 @@
package org.schabi.newpipe.local;
import android.view.View;
import androidx.recyclerview.widget.RecyclerView;
public class HeaderFooterHolder extends RecyclerView.ViewHolder {

View File

@ -1,7 +1,6 @@
package org.schabi.newpipe.local;
import android.content.Context;
import org.schabi.newpipe.database.LocalItem;
import org.schabi.newpipe.util.OnClickGesture;

View File

@ -4,29 +4,15 @@ import android.content.Context;
import android.util.Log;
import android.view.View;
import android.view.ViewGroup;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.recyclerview.widget.GridLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import org.schabi.newpipe.database.LocalItem;
import org.schabi.newpipe.database.stream.model.StreamStateEntity;
import org.schabi.newpipe.info_list.ItemViewMode;
import org.schabi.newpipe.local.history.HistoryRecordManager;
import org.schabi.newpipe.local.holder.LocalItemHolder;
import org.schabi.newpipe.local.holder.LocalPlaylistCardItemHolder;
import org.schabi.newpipe.local.holder.LocalPlaylistGridItemHolder;
import org.schabi.newpipe.local.holder.LocalPlaylistItemHolder;
import org.schabi.newpipe.local.holder.LocalPlaylistStreamCardItemHolder;
import org.schabi.newpipe.local.holder.LocalPlaylistStreamGridItemHolder;
import org.schabi.newpipe.local.holder.LocalPlaylistStreamItemHolder;
import org.schabi.newpipe.local.holder.LocalStatisticStreamCardItemHolder;
import org.schabi.newpipe.local.holder.LocalStatisticStreamGridItemHolder;
import org.schabi.newpipe.local.holder.LocalStatisticStreamItemHolder;
import org.schabi.newpipe.local.holder.RemotePlaylistCardItemHolder;
import org.schabi.newpipe.local.holder.RemotePlaylistGridItemHolder;
import org.schabi.newpipe.local.holder.RemotePlaylistItemHolder;
import org.schabi.newpipe.local.holder.*;
import org.schabi.newpipe.util.FallbackViewHolder;
import org.schabi.newpipe.util.Localization;
import org.schabi.newpipe.util.OnClickGesture;

View File

@ -8,12 +8,16 @@ import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.appcompat.app.AlertDialog;
import androidx.fragment.app.FragmentManager;
import icepick.State;
import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers;
import io.reactivex.rxjava3.core.Flowable;
import io.reactivex.rxjava3.core.Single;
import io.reactivex.rxjava3.disposables.CompositeDisposable;
import io.reactivex.rxjava3.disposables.Disposable;
import org.reactivestreams.Subscriber;
import org.reactivestreams.Subscription;
import org.schabi.newpipe.NewPipeDatabase;
@ -35,16 +39,9 @@ import org.schabi.newpipe.util.OnClickGesture;
import java.util.ArrayList;
import java.util.List;
import icepick.State;
import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers;
import io.reactivex.rxjava3.core.Flowable;
import io.reactivex.rxjava3.core.Single;
import io.reactivex.rxjava3.disposables.CompositeDisposable;
import io.reactivex.rxjava3.disposables.Disposable;
public final class BookmarkFragment extends BaseLocalListFragment<List<PlaylistLocalItem>, Void> {
@State
protected Parcelable itemsListState;
private Parcelable itemsListState;
private Subscription databaseSubscription;
private CompositeDisposable disposables = new CompositeDisposable();
@ -105,13 +102,11 @@ public final class BookmarkFragment extends BaseLocalListFragment<List<PlaylistL
public void selected(final LocalItem selectedItem) {
final FragmentManager fragmentManager = getFM();
if (selectedItem instanceof PlaylistMetadataEntry) {
final PlaylistMetadataEntry entry = ((PlaylistMetadataEntry) selectedItem);
if (selectedItem instanceof final PlaylistMetadataEntry entry) {
NavigationHelper.openLocalPlaylistFragment(fragmentManager, entry.uid,
entry.name);
} else if (selectedItem instanceof PlaylistRemoteEntity) {
final PlaylistRemoteEntity entry = ((PlaylistRemoteEntity) selectedItem);
} else if (selectedItem instanceof final PlaylistRemoteEntity entry) {
NavigationHelper.openPlaylistFragment(
fragmentManager,
entry.getServiceId(),
@ -140,7 +135,7 @@ public final class BookmarkFragment extends BaseLocalListFragment<List<PlaylistL
super.startLoading(forceLoad);
Flowable.combineLatest(localPlaylistManager.getPlaylists(),
remotePlaylistManager.getPlaylists(), PlaylistLocalItem::merge)
remotePlaylistManager.getPlaylists(), PlaylistLocalItem::merge)
.onBackpressureLatest()
.observeOn(AndroidSchedulers.mainThread())
.subscribe(getPlaylistsSubscriber());
@ -214,7 +209,8 @@ public final class BookmarkFragment extends BaseLocalListFragment<List<PlaylistL
}
@Override
public void onComplete() { }
public void onComplete() {
}
};
}

View File

@ -5,12 +5,12 @@ import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Toast;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers;
import io.reactivex.rxjava3.disposables.CompositeDisposable;
import org.schabi.newpipe.NewPipeDatabase;
import org.schabi.newpipe.R;
import org.schabi.newpipe.database.playlist.PlaylistMetadataEntry;
@ -20,21 +20,16 @@ import org.schabi.newpipe.local.playlist.LocalPlaylistManager;
import java.util.List;
import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers;
import io.reactivex.rxjava3.disposables.CompositeDisposable;
public final class PlaylistAppendDialog extends PlaylistDialog {
private static final String TAG = PlaylistAppendDialog.class.getCanonicalName();
private final CompositeDisposable playlistDisposables = new CompositeDisposable();
private RecyclerView playlistRecyclerView;
private LocalItemListAdapter playlistAdapter;
private final CompositeDisposable playlistDisposables = new CompositeDisposable();
/**
* Create a new instance of {@link PlaylistAppendDialog}.
*
* @param streamEntities a list of {@link StreamEntity} to be added to playlists
* @param streamEntities a list of {@link StreamEntity} to be added to playlists
* @return a new instance of {@link PlaylistAppendDialog}
*/
public static PlaylistAppendDialog newInstance(final List<StreamEntity> streamEntities) {
@ -101,7 +96,9 @@ public final class PlaylistAppendDialog extends PlaylistDialog {
// Helper
//////////////////////////////////////////////////////////////////////////*/
/** Display create playlist dialog. */
/**
* Display create playlist dialog.
*/
public void openCreatePlaylistDialog() {
if (getStreamEntities() == null || !isAdded()) {
return;

View File

@ -4,11 +4,10 @@ import android.app.Dialog;
import android.os.Bundle;
import android.text.InputType;
import android.widget.Toast;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.appcompat.app.AlertDialog.Builder;
import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers;
import org.schabi.newpipe.NewPipeDatabase;
import org.schabi.newpipe.R;
import org.schabi.newpipe.database.stream.model.StreamEntity;
@ -18,14 +17,12 @@ import org.schabi.newpipe.util.ThemeHelper;
import java.util.List;
import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers;
public final class PlaylistCreationDialog extends PlaylistDialog {
/**
* Create a new instance of {@link PlaylistCreationDialog}.
*
* @param streamEntities a list of {@link StreamEntity} to be added to playlists
* @param streamEntities a list of {@link StreamEntity} to be added to playlists
* @return a new instance of {@link PlaylistCreationDialog}
*/
public static PlaylistCreationDialog newInstance(final List<StreamEntity> streamEntities) {

View File

@ -5,12 +5,12 @@ import android.content.Context;
import android.content.DialogInterface;
import android.os.Bundle;
import android.view.Window;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.fragment.app.DialogFragment;
import androidx.fragment.app.FragmentManager;
import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers;
import io.reactivex.rxjava3.disposables.Disposable;
import org.schabi.newpipe.NewPipeDatabase;
import org.schabi.newpipe.database.stream.model.StreamEntity;
import org.schabi.newpipe.local.playlist.LocalPlaylistManager;
@ -24,9 +24,6 @@ import java.util.function.Consumer;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers;
import io.reactivex.rxjava3.disposables.Disposable;
public abstract class PlaylistDialog extends DialogFragment implements StateSaver.WriteRead {
@Nullable
@ -40,95 +37,6 @@ public abstract class PlaylistDialog extends DialogFragment implements StateSave
// LifeCycle
//////////////////////////////////////////////////////////////////////////*/
@Override
public void onCreate(@Nullable final Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
savedState = StateSaver.tryToRestore(savedInstanceState, this);
}
@Override
public void onDestroy() {
super.onDestroy();
StateSaver.onDestroy(savedState);
}
public List<StreamEntity> getStreamEntities() {
return streamEntities;
}
@NonNull
@Override
public Dialog onCreateDialog(final Bundle savedInstanceState) {
final Dialog dialog = super.onCreateDialog(savedInstanceState);
//remove title
final Window window = dialog.getWindow();
if (window != null) {
window.requestFeature(Window.FEATURE_NO_TITLE);
}
return dialog;
}
@Override
public void onDismiss(@NonNull final DialogInterface dialog) {
super.onDismiss(dialog);
if (onDismissListener != null) {
onDismissListener.onDismiss(dialog);
}
}
/*//////////////////////////////////////////////////////////////////////////
// State Saving
//////////////////////////////////////////////////////////////////////////*/
@Override
public String generateSuffix() {
final int size = streamEntities == null ? 0 : streamEntities.size();
return "." + size + ".list";
}
@Override
public void writeTo(final Queue<Object> objectsToSave) {
objectsToSave.add(streamEntities);
}
@Override
@SuppressWarnings("unchecked")
public void readFrom(@NonNull final Queue<Object> savedObjects) {
streamEntities = (List<StreamEntity>) savedObjects.poll();
}
@Override
public void onSaveInstanceState(@NonNull final Bundle outState) {
super.onSaveInstanceState(outState);
if (getActivity() != null) {
savedState = StateSaver.tryToSave(getActivity().isChangingConfigurations(),
savedState, outState, this);
}
}
/*//////////////////////////////////////////////////////////////////////////
// Getter + Setter
//////////////////////////////////////////////////////////////////////////*/
@Nullable
public DialogInterface.OnDismissListener getOnDismissListener() {
return onDismissListener;
}
public void setOnDismissListener(
@Nullable final DialogInterface.OnDismissListener onDismissListener
) {
this.onDismissListener = onDismissListener;
}
protected void setStreamEntities(final List<StreamEntity> streamEntities) {
this.streamEntities = streamEntities;
}
/*//////////////////////////////////////////////////////////////////////////
// Dialog creation
//////////////////////////////////////////////////////////////////////////*/
/**
* Creates a {@link PlaylistAppendDialog} when playlists exists,
* otherwise a {@link PlaylistCreationDialog}.
@ -178,4 +86,93 @@ public abstract class PlaylistDialog extends DialogFragment implements StateSave
return PlaylistDialog.createCorrespondingDialog(player.getContext(), streamEntities,
dialog -> dialog.show(fragmentManager, "PlaylistDialog"));
}
@Override
public void onCreate(@Nullable final Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
savedState = StateSaver.tryToRestore(savedInstanceState, this);
}
@Override
public void onDestroy() {
super.onDestroy();
StateSaver.onDestroy(savedState);
}
public List<StreamEntity> getStreamEntities() {
return streamEntities;
}
/*//////////////////////////////////////////////////////////////////////////
// State Saving
//////////////////////////////////////////////////////////////////////////*/
protected void setStreamEntities(final List<StreamEntity> streamEntities) {
this.streamEntities = streamEntities;
}
@NonNull
@Override
public Dialog onCreateDialog(final Bundle savedInstanceState) {
final Dialog dialog = super.onCreateDialog(savedInstanceState);
//remove title
final Window window = dialog.getWindow();
if (window != null) {
window.requestFeature(Window.FEATURE_NO_TITLE);
}
return dialog;
}
@Override
public void onDismiss(@NonNull final DialogInterface dialog) {
super.onDismiss(dialog);
if (onDismissListener != null) {
onDismissListener.onDismiss(dialog);
}
}
@Override
public String generateSuffix() {
final int size = streamEntities == null ? 0 : streamEntities.size();
return "." + size + ".list";
}
/*//////////////////////////////////////////////////////////////////////////
// Getter + Setter
//////////////////////////////////////////////////////////////////////////*/
@Override
public void writeTo(final Queue<Object> objectsToSave) {
objectsToSave.add(streamEntities);
}
@Override
@SuppressWarnings("unchecked")
public void readFrom(@NonNull final Queue<Object> savedObjects) {
streamEntities = (List<StreamEntity>) savedObjects.poll();
}
@Override
public void onSaveInstanceState(@NonNull final Bundle outState) {
super.onSaveInstanceState(outState);
if (getActivity() != null) {
savedState = StateSaver.tryToSave(getActivity().isChangingConfigurations(),
savedState, outState, this);
}
}
/*//////////////////////////////////////////////////////////////////////////
// Dialog creation
//////////////////////////////////////////////////////////////////////////*/
@Nullable
public DialogInterface.OnDismissListener getOnDismissListener() {
return onDismissListener;
}
public void setOnDismissListener(
@Nullable final DialogInterface.OnDismissListener onDismissListener
) {
this.onDismissListener = onDismissListener;
}
}

View File

@ -1,11 +1,9 @@
package org.schabi.newpipe.local.history;
import android.content.Context;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.recyclerview.widget.RecyclerView;
import org.schabi.newpipe.util.Localization;
import java.text.DateFormat;

View File

@ -20,10 +20,13 @@ package org.schabi.newpipe.local.history;
import android.content.Context;
import android.content.SharedPreferences;
import androidx.annotation.NonNull;
import androidx.preference.PreferenceManager;
import io.reactivex.rxjava3.core.Completable;
import io.reactivex.rxjava3.core.Flowable;
import io.reactivex.rxjava3.core.Maybe;
import io.reactivex.rxjava3.core.Single;
import io.reactivex.rxjava3.schedulers.Schedulers;
import org.schabi.newpipe.NewPipeDatabase;
import org.schabi.newpipe.R;
import org.schabi.newpipe.database.AppDatabase;
@ -52,12 +55,6 @@ import java.time.ZoneOffset;
import java.util.ArrayList;
import java.util.List;
import io.reactivex.rxjava3.core.Completable;
import io.reactivex.rxjava3.core.Flowable;
import io.reactivex.rxjava3.core.Maybe;
import io.reactivex.rxjava3.core.Single;
import io.reactivex.rxjava3.schedulers.Schedulers;
public class HistoryRecordManager {
private final AppDatabase database;
private final StreamDAO streamTable;
@ -87,9 +84,9 @@ public class HistoryRecordManager {
* Marks a stream item as watched such that it is hidden from the feed if watched videos are
* hidden. Adds a history entry and updates the stream progress to 100%.
*
* @see FeedViewModel#togglePlayedItems
* @param info the item to mark as watched
* @return a Maybe containing the ID of the item if successful
* @see FeedViewModel#togglePlayedItems
*/
public Maybe<Long> markAsWatched(final StreamInfoItem info) {
if (!isStreamHistoryEnabled()) {
@ -103,10 +100,10 @@ public class HistoryRecordManager {
// Duration will not exist if the item was loaded with fast mode, so fetch it if empty
if (info.getDuration() < 0) {
final StreamInfo completeInfo = ExtractorHelper.getStreamInfo(
info.getServiceId(),
info.getUrl(),
false
)
info.getServiceId(),
info.getUrl(),
false
)
.subscribeOn(Schedulers.io())
.blockingGet();
duration = completeInfo.getDuration();

View File

@ -3,20 +3,16 @@ package org.schabi.newpipe.local.history;
import android.content.Context;
import android.os.Bundle;
import android.os.Parcelable;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
import android.view.*;
import android.widget.Toast;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.viewbinding.ViewBinding;
import com.google.android.material.snackbar.Snackbar;
import icepick.State;
import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers;
import io.reactivex.rxjava3.disposables.CompositeDisposable;
import io.reactivex.rxjava3.disposables.Disposable;
import org.reactivestreams.Subscriber;
import org.reactivestreams.Subscription;
import org.schabi.newpipe.R;
@ -29,24 +25,15 @@ import org.schabi.newpipe.error.ErrorInfo;
import org.schabi.newpipe.error.UserAction;
import org.schabi.newpipe.extractor.stream.StreamInfoItem;
import org.schabi.newpipe.info_list.dialog.InfoItemDialog;
import org.schabi.newpipe.info_list.dialog.StreamDialogDefaultEntry;
import org.schabi.newpipe.local.BaseLocalListFragment;
import org.schabi.newpipe.player.playqueue.PlayQueue;
import org.schabi.newpipe.player.playqueue.SinglePlayQueue;
import org.schabi.newpipe.settings.HistorySettingsFragment;
import org.schabi.newpipe.util.NavigationHelper;
import org.schabi.newpipe.util.OnClickGesture;
import org.schabi.newpipe.info_list.dialog.StreamDialogDefaultEntry;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.Objects;
import icepick.State;
import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers;
import io.reactivex.rxjava3.disposables.CompositeDisposable;
import io.reactivex.rxjava3.disposables.Disposable;
import java.util.*;
public class StatisticsPlaylistFragment
extends BaseLocalListFragment<List<StreamStatisticsEntry>, Void> {
@ -313,7 +300,7 @@ public class StatisticsPlaylistFragment
sortMode = StatisticSortMode.LAST_PLAYED;
setTitle(getString(R.string.title_last_played));
headerBinding.sortButtonIcon.setImageResource(
R.drawable.ic_filter_list);
R.drawable.ic_filter_list);
headerBinding.sortButtonText.setText(R.string.title_most_played);
}
startLoading(true);
@ -351,8 +338,7 @@ public class StatisticsPlaylistFragment
private void deleteEntry(final int index) {
final LocalItem infoItem = itemListAdapter.getItemsList().get(index);
if (infoItem instanceof StreamStatisticsEntry) {
final StreamStatisticsEntry entry = (StreamStatisticsEntry) infoItem;
if (infoItem instanceof final StreamStatisticsEntry entry) {
final Disposable onDelete = recordManager
.deleteStreamHistoryAndState(entry.getStreamId())
.observeOn(AndroidSchedulers.mainThread())

View File

@ -2,9 +2,7 @@ package org.schabi.newpipe.local.holder;
import android.view.LayoutInflater;
import android.view.ViewGroup;
import androidx.recyclerview.widget.RecyclerView;
import org.schabi.newpipe.database.LocalItem;
import org.schabi.newpipe.local.LocalItemBuilder;
import org.schabi.newpipe.local.history.HistoryRecordManager;
@ -44,5 +42,6 @@ public abstract class LocalItemHolder extends RecyclerView.ViewHolder {
DateTimeFormatter dateTimeFormatter);
public void updateState(final LocalItem localItem,
final HistoryRecordManager historyRecordManager) { }
final HistoryRecordManager historyRecordManager) {
}
}

View File

@ -1,7 +1,6 @@
package org.schabi.newpipe.local.holder;
import android.view.ViewGroup;
import org.schabi.newpipe.R;
import org.schabi.newpipe.local.LocalItemBuilder;

View File

@ -1,7 +1,6 @@
package org.schabi.newpipe.local.holder;
import android.view.ViewGroup;
import org.schabi.newpipe.R;
import org.schabi.newpipe.local.LocalItemBuilder;

View File

@ -2,13 +2,12 @@ package org.schabi.newpipe.local.holder;
import android.view.View;
import android.view.ViewGroup;
import org.schabi.newpipe.database.LocalItem;
import org.schabi.newpipe.database.playlist.PlaylistMetadataEntry;
import org.schabi.newpipe.local.LocalItemBuilder;
import org.schabi.newpipe.local.history.HistoryRecordManager;
import org.schabi.newpipe.util.PicassoHelper;
import org.schabi.newpipe.util.Localization;
import org.schabi.newpipe.util.PicassoHelper;
import java.time.format.DateTimeFormatter;
@ -26,10 +25,9 @@ public class LocalPlaylistItemHolder extends PlaylistItemHolder {
public void updateFromItem(final LocalItem localItem,
final HistoryRecordManager historyRecordManager,
final DateTimeFormatter dateTimeFormatter) {
if (!(localItem instanceof PlaylistMetadataEntry)) {
if (!(localItem instanceof final PlaylistMetadataEntry item)) {
return;
}
final PlaylistMetadataEntry item = (PlaylistMetadataEntry) localItem;
itemTitleView.setText(item.name);
itemStreamCountView.setText(Localization.localizeStreamCountMini(

View File

@ -1,7 +1,6 @@
package org.schabi.newpipe.local.holder;
import android.view.ViewGroup;
import org.schabi.newpipe.R;
import org.schabi.newpipe.local.LocalItemBuilder;

View File

@ -1,7 +1,6 @@
package org.schabi.newpipe.local.holder;
import android.view.ViewGroup;
import org.schabi.newpipe.R;
import org.schabi.newpipe.local.LocalItemBuilder;

View File

@ -5,9 +5,7 @@ import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.TextView;
import androidx.core.content.ContextCompat;
import org.schabi.newpipe.R;
import org.schabi.newpipe.database.LocalItem;
import org.schabi.newpipe.database.playlist.PlaylistStreamEntry;
@ -25,8 +23,8 @@ import java.util.concurrent.TimeUnit;
public class LocalPlaylistStreamItemHolder extends LocalItemHolder {
public final ImageView itemThumbnailView;
public final TextView itemVideoTitleView;
private final TextView itemAdditionalDetailsView;
public final TextView itemDurationView;
private final TextView itemAdditionalDetailsView;
private final View itemHandleView;
private final AnimatedProgressBar itemProgressView;
@ -51,10 +49,9 @@ public class LocalPlaylistStreamItemHolder extends LocalItemHolder {
public void updateFromItem(final LocalItem localItem,
final HistoryRecordManager historyRecordManager,
final DateTimeFormatter dateTimeFormatter) {
if (!(localItem instanceof PlaylistStreamEntry)) {
if (!(localItem instanceof final PlaylistStreamEntry item)) {
return;
}
final PlaylistStreamEntry item = (PlaylistStreamEntry) localItem;
itemVideoTitleView.setText(item.getStreamEntity().getTitle());
itemAdditionalDetailsView.setText(Localization
@ -104,10 +101,9 @@ public class LocalPlaylistStreamItemHolder extends LocalItemHolder {
@Override
public void updateState(final LocalItem localItem,
final HistoryRecordManager historyRecordManager) {
if (!(localItem instanceof PlaylistStreamEntry)) {
if (!(localItem instanceof final PlaylistStreamEntry item)) {
return;
}
final PlaylistStreamEntry item = (PlaylistStreamEntry) localItem;
if (item.getProgressMillis() > 0 && item.getStreamEntity().getDuration() > 0) {
itemProgressView.setMax((int) item.getStreamEntity().getDuration());

View File

@ -1,7 +1,6 @@
package org.schabi.newpipe.local.holder;
import android.view.ViewGroup;
import org.schabi.newpipe.R;
import org.schabi.newpipe.local.LocalItemBuilder;

View File

@ -1,7 +1,6 @@
package org.schabi.newpipe.local.holder;
import android.view.ViewGroup;
import org.schabi.newpipe.R;
import org.schabi.newpipe.local.LocalItemBuilder;

View File

@ -4,10 +4,8 @@ import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.TextView;
import androidx.annotation.Nullable;
import androidx.core.content.ContextCompat;
import org.schabi.newpipe.R;
import org.schabi.newpipe.database.LocalItem;
import org.schabi.newpipe.database.stream.StreamStatisticsEntry;
@ -82,10 +80,9 @@ public class LocalStatisticStreamItemHolder extends LocalItemHolder {
public void updateFromItem(final LocalItem localItem,
final HistoryRecordManager historyRecordManager,
final DateTimeFormatter dateTimeFormatter) {
if (!(localItem instanceof StreamStatisticsEntry)) {
if (!(localItem instanceof final StreamStatisticsEntry item)) {
return;
}
final StreamStatisticsEntry item = (StreamStatisticsEntry) localItem;
itemVideoTitleView.setText(item.getStreamEntity().getTitle());
itemUploaderView.setText(item.getStreamEntity().getUploader());
@ -136,10 +133,9 @@ public class LocalStatisticStreamItemHolder extends LocalItemHolder {
@Override
public void updateState(final LocalItem localItem,
final HistoryRecordManager historyRecordManager) {
if (!(localItem instanceof StreamStatisticsEntry)) {
if (!(localItem instanceof final StreamStatisticsEntry item)) {
return;
}
final StreamStatisticsEntry item = (StreamStatisticsEntry) localItem;
if (item.getProgressMillis() > 0 && item.getStreamEntity().getDuration() > 0) {
itemProgressView.setMax((int) item.getStreamEntity().getDuration());

View File

@ -3,7 +3,6 @@ package org.schabi.newpipe.local.holder;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.TextView;
import org.schabi.newpipe.R;
import org.schabi.newpipe.database.LocalItem;
import org.schabi.newpipe.local.LocalItemBuilder;
@ -13,9 +12,9 @@ import java.time.format.DateTimeFormatter;
public abstract class PlaylistItemHolder extends LocalItemHolder {
public final ImageView itemThumbnailView;
final TextView itemStreamCountView;
public final TextView itemTitleView;
public final TextView itemUploaderView;
final TextView itemStreamCountView;
public PlaylistItemHolder(final LocalItemBuilder infoItemBuilder, final int layoutId,
final ViewGroup parent) {

View File

@ -1,7 +1,6 @@
package org.schabi.newpipe.local.holder;
import android.view.ViewGroup;
import org.schabi.newpipe.R;
import org.schabi.newpipe.local.LocalItemBuilder;

View File

@ -1,7 +1,6 @@
package org.schabi.newpipe.local.holder;
import android.view.ViewGroup;
import org.schabi.newpipe.R;
import org.schabi.newpipe.local.LocalItemBuilder;

View File

@ -2,7 +2,6 @@ package org.schabi.newpipe.local.holder;
import android.text.TextUtils;
import android.view.ViewGroup;
import org.schabi.newpipe.database.LocalItem;
import org.schabi.newpipe.database.playlist.model.PlaylistRemoteEntity;
import org.schabi.newpipe.local.LocalItemBuilder;
@ -28,10 +27,9 @@ public class RemotePlaylistItemHolder extends PlaylistItemHolder {
public void updateFromItem(final LocalItem localItem,
final HistoryRecordManager historyRecordManager,
final DateTimeFormatter dateTimeFormatter) {
if (!(localItem instanceof PlaylistRemoteEntity)) {
if (!(localItem instanceof final PlaylistRemoteEntity item)) {
return;
}
final PlaylistRemoteEntity item = (PlaylistRemoteEntity) localItem;
itemTitleView.setText(item.getName());
itemStreamCountView.setText(Localization.localizeStreamCountMini(

View File

@ -1,9 +1,5 @@
package org.schabi.newpipe.local.playlist;
import static org.schabi.newpipe.error.ErrorUtil.showUiErrorSnackbar;
import static org.schabi.newpipe.ktx.ViewUtils.animate;
import static org.schabi.newpipe.util.ThemeHelper.shouldUseGridLayout;
import android.content.Context;
import android.content.DialogInterface;
import android.os.Bundle;
@ -12,14 +8,8 @@ import android.text.InputType;
import android.text.TextUtils;
import android.util.Log;
import android.util.Pair;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
import android.view.*;
import android.widget.Toast;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.appcompat.app.AlertDialog;
@ -27,7 +17,13 @@ import androidx.preference.PreferenceManager;
import androidx.recyclerview.widget.ItemTouchHelper;
import androidx.recyclerview.widget.RecyclerView;
import androidx.viewbinding.ViewBinding;
import icepick.State;
import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers;
import io.reactivex.rxjava3.core.Single;
import io.reactivex.rxjava3.disposables.CompositeDisposable;
import io.reactivex.rxjava3.disposables.Disposable;
import io.reactivex.rxjava3.schedulers.Schedulers;
import io.reactivex.rxjava3.subjects.PublishSubject;
import org.reactivestreams.Subscriber;
import org.reactivestreams.Subscription;
import org.schabi.newpipe.NewPipeDatabase;
@ -61,13 +57,9 @@ import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.stream.Collectors;
import icepick.State;
import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers;
import io.reactivex.rxjava3.core.Single;
import io.reactivex.rxjava3.disposables.CompositeDisposable;
import io.reactivex.rxjava3.disposables.Disposable;
import io.reactivex.rxjava3.schedulers.Schedulers;
import io.reactivex.rxjava3.subjects.PublishSubject;
import static org.schabi.newpipe.error.ErrorUtil.showUiErrorSnackbar;
import static org.schabi.newpipe.ktx.ViewUtils.animate;
import static org.schabi.newpipe.util.ThemeHelper.shouldUseGridLayout;
public class LocalPlaylistFragment extends BaseLocalListFragment<List<PlaylistStreamEntry>, Void> {
// Save the list 10 seconds after the last change occurred

View File

@ -1,7 +1,11 @@
package org.schabi.newpipe.local.playlist;
import androidx.annotation.Nullable;
import io.reactivex.rxjava3.core.Completable;
import io.reactivex.rxjava3.core.Flowable;
import io.reactivex.rxjava3.core.Maybe;
import io.reactivex.rxjava3.core.Single;
import io.reactivex.rxjava3.schedulers.Schedulers;
import org.schabi.newpipe.R;
import org.schabi.newpipe.database.AppDatabase;
import org.schabi.newpipe.database.playlist.PlaylistMetadataEntry;
@ -16,12 +20,6 @@ import org.schabi.newpipe.database.stream.model.StreamEntity;
import java.util.ArrayList;
import java.util.List;
import io.reactivex.rxjava3.core.Completable;
import io.reactivex.rxjava3.core.Flowable;
import io.reactivex.rxjava3.core.Maybe;
import io.reactivex.rxjava3.core.Single;
import io.reactivex.rxjava3.schedulers.Schedulers;
public class LocalPlaylistManager {
private final AppDatabase database;
private final StreamDAO streamTable;

View File

@ -1,5 +1,8 @@
package org.schabi.newpipe.local.playlist;
import io.reactivex.rxjava3.core.Flowable;
import io.reactivex.rxjava3.core.Single;
import io.reactivex.rxjava3.schedulers.Schedulers;
import org.schabi.newpipe.database.AppDatabase;
import org.schabi.newpipe.database.playlist.dao.PlaylistRemoteDAO;
import org.schabi.newpipe.database.playlist.model.PlaylistRemoteEntity;
@ -7,10 +10,6 @@ import org.schabi.newpipe.extractor.playlist.PlaylistInfo;
import java.util.List;
import io.reactivex.rxjava3.core.Flowable;
import io.reactivex.rxjava3.core.Single;
import io.reactivex.rxjava3.schedulers.Schedulers;
public class RemotePlaylistManager {
private final PlaylistRemoteDAO playlistRemoteTable;

View File

@ -3,17 +3,14 @@ package org.schabi.newpipe.local.subscription;
import android.app.Dialog;
import android.content.Intent;
import android.os.Bundle;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.appcompat.app.AlertDialog;
import androidx.fragment.app.DialogFragment;
import androidx.fragment.app.Fragment;
import org.schabi.newpipe.R;
import icepick.Icepick;
import icepick.State;
import org.schabi.newpipe.R;
import static org.schabi.newpipe.util.Localization.assureCorrectAppLanguage;
@ -25,7 +22,7 @@ public class ImportConfirmationDialog extends DialogFragment {
@NonNull final Intent resultServiceIntent) {
final ImportConfirmationDialog confirmationDialog = new ImportConfirmationDialog();
confirmationDialog.setResultServiceIntent(resultServiceIntent);
confirmationDialog.show(fragment.getParentFragmentManager(), null);
show(fragment.getParentFragmentManager(), null);
}
public void setResultServiceIntent(final Intent resultServiceIntent) {

Some files were not shown because too many files have changed in this diff Show More