Merge remote-tracking branch 'origin/dev' into notifications-1
This commit is contained in:
commit
1130aba7ca
106 changed files with 706 additions and 441 deletions
|
|
@ -51,8 +51,12 @@ import java.util.ArrayList;
|
|||
* <li>{@link #saveState()}</li>
|
||||
* <li>{@link #restoreState(Parcelable, ClassLoader)}</li>
|
||||
* </ul>
|
||||
*
|
||||
* @deprecated Switch to {@link androidx.viewpager2.widget.ViewPager2} and use
|
||||
* {@link androidx.viewpager2.adapter.FragmentStateAdapter} instead.
|
||||
*/
|
||||
@SuppressWarnings("deprecation")
|
||||
@Deprecated
|
||||
public abstract class FragmentStatePagerAdapterMenuWorkaround extends PagerAdapter {
|
||||
private static final String TAG = "FragmentStatePagerAdapt";
|
||||
private static final boolean DEBUG = false;
|
||||
|
|
@ -86,9 +90,10 @@ public abstract class FragmentStatePagerAdapterMenuWorkaround extends PagerAdapt
|
|||
private final int mBehavior;
|
||||
private FragmentTransaction mCurTransaction = null;
|
||||
|
||||
private final ArrayList<Fragment.SavedState> mSavedState = new ArrayList<Fragment.SavedState>();
|
||||
private final ArrayList<Fragment> mFragments = new ArrayList<Fragment>();
|
||||
private final ArrayList<Fragment.SavedState> mSavedState = new ArrayList<>();
|
||||
private final ArrayList<Fragment> mFragments = new ArrayList<>();
|
||||
private Fragment mCurrentPrimaryItem = null;
|
||||
private boolean mExecutingFinishUpdate;
|
||||
|
||||
/**
|
||||
* Constructor for {@link FragmentStatePagerAdapterMenuWorkaround}
|
||||
|
|
@ -208,7 +213,7 @@ public abstract class FragmentStatePagerAdapterMenuWorkaround extends PagerAdapt
|
|||
mFragments.set(position, null);
|
||||
|
||||
mCurTransaction.remove(fragment);
|
||||
if (fragment == mCurrentPrimaryItem) {
|
||||
if (fragment.equals(mCurrentPrimaryItem)) {
|
||||
mCurrentPrimaryItem = null;
|
||||
}
|
||||
}
|
||||
|
|
@ -247,7 +252,19 @@ public abstract class FragmentStatePagerAdapterMenuWorkaround extends PagerAdapt
|
|||
@Override
|
||||
public void finishUpdate(@NonNull final ViewGroup container) {
|
||||
if (mCurTransaction != null) {
|
||||
mCurTransaction.commitNowAllowingStateLoss();
|
||||
// We drop any transactions that attempt to be committed
|
||||
// from a re-entrant call to finishUpdate(). We need to
|
||||
// do this as a workaround for Robolectric running measure/layout
|
||||
// calls inline rather than allowing them to be posted
|
||||
// as they would on a real device.
|
||||
if (!mExecutingFinishUpdate) {
|
||||
try {
|
||||
mExecutingFinishUpdate = true;
|
||||
mCurTransaction.commitNowAllowingStateLoss();
|
||||
} finally {
|
||||
mExecutingFinishUpdate = false;
|
||||
}
|
||||
}
|
||||
mCurTransaction = null;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -262,4 +262,5 @@ public class App extends MultiDexApplication {
|
|||
protected boolean isDisposedRxExceptionsReported() {
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -21,7 +21,6 @@ public abstract class BaseFragment extends Fragment {
|
|||
//These values are used for controlling fragments when they are part of the frontpage
|
||||
@State
|
||||
protected boolean useAsFrontPage = false;
|
||||
private boolean mIsVisibleToUser = false;
|
||||
|
||||
public void useAsFrontPage(final boolean value) {
|
||||
useAsFrontPage = value;
|
||||
|
|
@ -85,12 +84,6 @@ public abstract class BaseFragment extends Fragment {
|
|||
AppWatcher.INSTANCE.getObjectWatcher().watch(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setUserVisibleHint(final boolean isVisibleToUser) {
|
||||
super.setUserVisibleHint(isVisibleToUser);
|
||||
mIsVisibleToUser = isVisibleToUser;
|
||||
}
|
||||
|
||||
/*//////////////////////////////////////////////////////////////////////////
|
||||
// Init
|
||||
//////////////////////////////////////////////////////////////////////////*/
|
||||
|
|
@ -109,8 +102,7 @@ public abstract class BaseFragment extends Fragment {
|
|||
if (DEBUG) {
|
||||
Log.d(TAG, "setTitle() called with: title = [" + title + "]");
|
||||
}
|
||||
if ((!useAsFrontPage || mIsVisibleToUser)
|
||||
&& (activity != null && activity.getSupportActionBar() != null)) {
|
||||
if (!useAsFrontPage && activity != null && activity.getSupportActionBar() != null) {
|
||||
activity.getSupportActionBar().setDisplayShowTitleEnabled(true);
|
||||
activity.getSupportActionBar().setTitle(title);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -7,7 +7,6 @@ import android.content.Intent;
|
|||
import android.content.SharedPreferences;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.content.pm.Signature;
|
||||
import android.net.ConnectivityManager;
|
||||
import android.net.Uri;
|
||||
import android.util.Log;
|
||||
|
||||
|
|
@ -15,7 +14,6 @@ import androidx.annotation.NonNull;
|
|||
import androidx.annotation.Nullable;
|
||||
import androidx.core.app.NotificationCompat;
|
||||
import androidx.core.app.NotificationManagerCompat;
|
||||
import androidx.core.content.ContextCompat;
|
||||
import androidx.core.content.pm.PackageInfoCompat;
|
||||
import androidx.preference.PreferenceManager;
|
||||
|
||||
|
|
@ -48,7 +46,8 @@ public final class CheckForNewAppVersion extends IntentService {
|
|||
private static final boolean DEBUG = MainActivity.DEBUG;
|
||||
private static final String TAG = CheckForNewAppVersion.class.getSimpleName();
|
||||
|
||||
private static final String GITHUB_APK_SHA1
|
||||
// Public key of the certificate that is used in NewPipe release versions
|
||||
private static final String RELEASE_CERT_PUBLIC_KEY_SHA1
|
||||
= "B0:2E:90:7C:1C:D6:FC:57:C3:35:F0:88:D0:8F:50:5F:94:E4:D2:15";
|
||||
private static final String NEWPIPE_API_URL = "https://newpipe.net/api/data.json";
|
||||
|
||||
|
|
@ -129,44 +128,37 @@ public final class CheckForNewAppVersion extends IntentService {
|
|||
final String versionName,
|
||||
final String apkLocationUrl,
|
||||
final int versionCode) {
|
||||
final int notificationId = 2000;
|
||||
|
||||
if (BuildConfig.VERSION_CODE < versionCode) {
|
||||
// A pending intent to open the apk location url in the browser.
|
||||
final Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(apkLocationUrl));
|
||||
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
|
||||
final PendingIntent pendingIntent
|
||||
= PendingIntent.getActivity(application, 0, intent, 0);
|
||||
|
||||
final String channelId = application
|
||||
.getString(R.string.app_update_notification_channel_id);
|
||||
final NotificationCompat.Builder notificationBuilder
|
||||
= new NotificationCompat.Builder(application, channelId)
|
||||
.setSmallIcon(R.drawable.ic_newpipe_update)
|
||||
.setVisibility(NotificationCompat.VISIBILITY_PUBLIC)
|
||||
.setContentIntent(pendingIntent)
|
||||
.setAutoCancel(true)
|
||||
.setContentTitle(application
|
||||
.getString(R.string.app_update_notification_content_title))
|
||||
.setContentText(application
|
||||
.getString(R.string.app_update_notification_content_text)
|
||||
+ " " + versionName);
|
||||
|
||||
final NotificationManagerCompat notificationManager
|
||||
= NotificationManagerCompat.from(application);
|
||||
notificationManager.notify(notificationId, notificationBuilder.build());
|
||||
if (BuildConfig.VERSION_CODE >= versionCode) {
|
||||
return;
|
||||
}
|
||||
|
||||
// A pending intent to open the apk location url in the browser.
|
||||
final Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(apkLocationUrl));
|
||||
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
|
||||
final PendingIntent pendingIntent
|
||||
= PendingIntent.getActivity(application, 0, intent, 0);
|
||||
|
||||
final String channelId = application
|
||||
.getString(R.string.app_update_notification_channel_id);
|
||||
final NotificationCompat.Builder notificationBuilder
|
||||
= new NotificationCompat.Builder(application, channelId)
|
||||
.setSmallIcon(R.drawable.ic_newpipe_update)
|
||||
.setVisibility(NotificationCompat.VISIBILITY_PUBLIC)
|
||||
.setContentIntent(pendingIntent)
|
||||
.setAutoCancel(true)
|
||||
.setContentTitle(application
|
||||
.getString(R.string.app_update_notification_content_title))
|
||||
.setContentText(application
|
||||
.getString(R.string.app_update_notification_content_text)
|
||||
+ " " + versionName);
|
||||
|
||||
final NotificationManagerCompat notificationManager
|
||||
= NotificationManagerCompat.from(application);
|
||||
notificationManager.notify(2000, notificationBuilder.build());
|
||||
}
|
||||
|
||||
private static boolean isConnected(@NonNull final App app) {
|
||||
final ConnectivityManager connectivityManager =
|
||||
ContextCompat.getSystemService(app, ConnectivityManager.class);
|
||||
return connectivityManager != null && connectivityManager.getActiveNetworkInfo() != null
|
||||
&& connectivityManager.getActiveNetworkInfo().isConnected();
|
||||
}
|
||||
|
||||
public static boolean isGithubApk(@NonNull final App app) {
|
||||
return getCertificateSHA1Fingerprint(app).equals(GITHUB_APK_SHA1);
|
||||
public static boolean isReleaseApk(@NonNull final App app) {
|
||||
return getCertificateSHA1Fingerprint(app).equals(RELEASE_CERT_PUBLIC_KEY_SHA1);
|
||||
}
|
||||
|
||||
private void checkNewVersion() throws IOException, ReCaptchaException {
|
||||
|
|
@ -175,9 +167,8 @@ public final class CheckForNewAppVersion extends IntentService {
|
|||
final SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(app);
|
||||
final NewVersionManager manager = new NewVersionManager();
|
||||
|
||||
// Check if user has enabled/disabled update checking
|
||||
// and if the current apk is a github one or not.
|
||||
if (!prefs.getBoolean(app.getString(R.string.update_app_key), true) || !isGithubApk(app)) {
|
||||
// Check if the current apk is a github one or not.
|
||||
if (!isReleaseApk(app)) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -213,6 +204,7 @@ public final class CheckForNewAppVersion extends IntentService {
|
|||
|
||||
// Parse the json from the response.
|
||||
try {
|
||||
|
||||
final JsonObject githubStableObject = JsonParser.object()
|
||||
.from(response.responseBody()).getObject("flavors")
|
||||
.getObject("github").getObject("stable");
|
||||
|
|
@ -235,6 +227,23 @@ public final class CheckForNewAppVersion extends IntentService {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Start a new service which
|
||||
* checks if all conditions for performing a version check are met,
|
||||
* fetches the API endpoint {@link #NEWPIPE_API_URL} containing info
|
||||
* about the latest NewPipe version
|
||||
* and displays a notification about ana available update.
|
||||
* <br>
|
||||
* Following conditions need to be met, before data is request from the server:
|
||||
* <ul>
|
||||
* <li> The app is signed with the correct signing key (by TeamNewPipe / schabi).
|
||||
* If the signing key differs from the one used upstream, the update cannot be installed.</li>
|
||||
* <li>The user enabled searching for and notifying about updates in the settings.</li>
|
||||
* <li>The app did not recently check for updates.
|
||||
* We do not want to make unnecessary connections and DOS our servers.</li>
|
||||
* </ul>
|
||||
* <b>Must not be executed</b> when the app is in background.
|
||||
*/
|
||||
public static void startNewVersionCheckService() {
|
||||
final Intent intent = new Intent(App.getApp().getApplicationContext(),
|
||||
CheckForNewAppVersion.class);
|
||||
|
|
|
|||
|
|
@ -165,13 +165,25 @@ public class MainActivity extends AppCompatActivity {
|
|||
}
|
||||
openMiniPlayerUponPlayerStarted();
|
||||
|
||||
// Check for new version
|
||||
startNewVersionCheckService();
|
||||
|
||||
// shedule worker for checking for new streans and creating corresponding notifications
|
||||
NotificationWorker.schedule(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onPostCreate(final Bundle savedInstanceState) {
|
||||
super.onPostCreate(savedInstanceState);
|
||||
|
||||
final App app = App.getApp();
|
||||
final SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(app);
|
||||
|
||||
if (prefs.getBoolean(app.getString(R.string.update_app_key), true)) {
|
||||
// Start the service which is checking all conditions
|
||||
// and eventually searching for a new version.
|
||||
// The service searching for a new NewPipe version must not be started in background.
|
||||
startNewVersionCheckService();
|
||||
}
|
||||
}
|
||||
|
||||
private void setupDrawer() throws ExtractionException {
|
||||
addDrawerMenuForCurrentService();
|
||||
|
||||
|
|
|
|||
|
|
@ -18,6 +18,9 @@ import org.schabi.newpipe.util.NavigationHelper;
|
|||
import java.util.Collections;
|
||||
|
||||
public final class QueueItemMenuUtil {
|
||||
private QueueItemMenuUtil() {
|
||||
}
|
||||
|
||||
public static void openPopupMenu(final PlayQueue playQueue,
|
||||
final PlayQueueItem item,
|
||||
final View view,
|
||||
|
|
@ -57,6 +60,13 @@ public final class QueueItemMenuUtil {
|
|||
);
|
||||
|
||||
return true;
|
||||
case R.id.menu_item_channel_details:
|
||||
// An intent must be used here.
|
||||
// Opening with FragmentManager transactions is not working,
|
||||
// as PlayQueueActivity doesn't use fragments.
|
||||
NavigationHelper.openChannelFragmentUsingIntent(context, item.getServiceId(),
|
||||
item.getUploaderUrl(), item.getUploader());
|
||||
return true;
|
||||
case R.id.menu_item_share:
|
||||
shareText(context, item.getTitle(), item.getUrl(),
|
||||
item.getThumbnailUrl());
|
||||
|
|
@ -67,6 +77,4 @@ public final class QueueItemMenuUtil {
|
|||
|
||||
popupMenu.show();
|
||||
}
|
||||
|
||||
private QueueItemMenuUtil() { }
|
||||
}
|
||||
|
|
|
|||
|
|
@ -20,8 +20,8 @@ public class BlankFragment extends BaseFragment {
|
|||
}
|
||||
|
||||
@Override
|
||||
public void setUserVisibleHint(final boolean isVisibleToUser) {
|
||||
super.setUserVisibleHint(isVisibleToUser);
|
||||
public void onResume() {
|
||||
super.onResume();
|
||||
setTitle("NewPipe");
|
||||
// leave this inline. Will make it harder for copy cats.
|
||||
// If you are a Copy cat FUCK YOU.
|
||||
|
|
|
|||
|
|
@ -1181,7 +1181,7 @@ public final class VideoDetailFragment
|
|||
addVideoPlayerView();
|
||||
|
||||
final Intent playerIntent = NavigationHelper.getPlayerIntent(requireContext(),
|
||||
MainPlayer.class, queue, autoPlayEnabled);
|
||||
MainPlayer.class, queue, true, autoPlayEnabled);
|
||||
ContextCompat.startForegroundService(activity, playerIntent);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -143,7 +143,7 @@ public abstract class BaseListFragment<I, N> extends BaseStateFragment<I>
|
|||
final View focusedItem = itemsList.getFocusedChild();
|
||||
final RecyclerView.ViewHolder itemHolder =
|
||||
itemsList.findContainingViewHolder(focusedItem);
|
||||
return itemHolder.getAdapterPosition();
|
||||
return itemHolder.getBindingAdapterPosition();
|
||||
} catch (final NullPointerException e) {
|
||||
return -1;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -103,11 +103,9 @@ public class ChannelFragment extends BaseListInfoFragment<ChannelInfo>
|
|||
}
|
||||
|
||||
@Override
|
||||
public void setUserVisibleHint(final boolean isVisibleToUser) {
|
||||
super.setUserVisibleHint(isVisibleToUser);
|
||||
if (activity != null
|
||||
&& useAsFrontPage
|
||||
&& isVisibleToUser) {
|
||||
public void onResume() {
|
||||
super.onResume();
|
||||
if (activity != null && useAsFrontPage) {
|
||||
setTitle(currentInfo != null ? currentInfo.getName() : name);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -99,9 +99,12 @@ public class KioskFragment extends BaseListInfoFragment<KioskInfo> {
|
|||
}
|
||||
|
||||
@Override
|
||||
public void setUserVisibleHint(final boolean isVisibleToUser) {
|
||||
super.setUserVisibleHint(isVisibleToUser);
|
||||
if (useAsFrontPage && isVisibleToUser && activity != null) {
|
||||
public void onResume() {
|
||||
super.onResume();
|
||||
if (!Localization.getPreferredContentCountry(requireContext()).equals(contentCountry)) {
|
||||
reloadContent();
|
||||
}
|
||||
if (useAsFrontPage && activity != null) {
|
||||
try {
|
||||
setTitle(kioskTranslatedName);
|
||||
} catch (final Exception e) {
|
||||
|
|
@ -117,15 +120,6 @@ public class KioskFragment extends BaseListInfoFragment<KioskInfo> {
|
|||
return inflater.inflate(R.layout.fragment_kiosk, container, false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onResume() {
|
||||
super.onResume();
|
||||
|
||||
if (!Localization.getPreferredContentCountry(requireContext()).equals(contentCountry)) {
|
||||
reloadContent();
|
||||
}
|
||||
}
|
||||
|
||||
/*//////////////////////////////////////////////////////////////////////////
|
||||
// Menu
|
||||
//////////////////////////////////////////////////////////////////////////*/
|
||||
|
|
|
|||
|
|
@ -1088,7 +1088,7 @@ public class SearchFragment extends BaseListFragment<SearchInfo, ListExtractor.I
|
|||
//////////////////////////////////////////////////////////////////////////*/
|
||||
|
||||
public int getSuggestionMovementFlags(@NonNull final RecyclerView.ViewHolder viewHolder) {
|
||||
final int position = viewHolder.getAdapterPosition();
|
||||
final int position = viewHolder.getBindingAdapterPosition();
|
||||
if (position == RecyclerView.NO_POSITION) {
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -1099,7 +1099,7 @@ public class SearchFragment extends BaseListFragment<SearchInfo, ListExtractor.I
|
|||
}
|
||||
|
||||
public void onSuggestionItemSwiped(@NonNull final RecyclerView.ViewHolder viewHolder) {
|
||||
final int position = viewHolder.getAdapterPosition();
|
||||
final int position = viewHolder.getBindingAdapterPosition();
|
||||
final String query = suggestionListAdapter.getItem(position).query;
|
||||
final Disposable onDelete = historyRecordManager.deleteSearchHistory(query)
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
|
|
|
|||
|
|
@ -78,9 +78,9 @@ public final class BookmarkFragment extends BaseLocalListFragment<List<PlaylistL
|
|||
}
|
||||
|
||||
@Override
|
||||
public void setUserVisibleHint(final boolean isVisibleToUser) {
|
||||
super.setUserVisibleHint(isVisibleToUser);
|
||||
if (activity != null && isVisibleToUser) {
|
||||
public void onResume() {
|
||||
super.onResume();
|
||||
if (activity != null) {
|
||||
setTitle(activity.getString(R.string.tab_bookmarks));
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -101,9 +101,9 @@ public class StatisticsPlaylistFragment
|
|||
}
|
||||
|
||||
@Override
|
||||
public void setUserVisibleHint(final boolean isVisibleToUser) {
|
||||
super.setUserVisibleHint(isVisibleToUser);
|
||||
if (activity != null && isVisibleToUser) {
|
||||
public void onResume() {
|
||||
super.onResume();
|
||||
if (activity != null) {
|
||||
setTitle(activity.getString(R.string.title_activity_history));
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -709,8 +709,8 @@ public class LocalPlaylistFragment extends BaseLocalListFragment<List<PlaylistSt
|
|||
return false;
|
||||
}
|
||||
|
||||
final int sourceIndex = source.getAdapterPosition();
|
||||
final int targetIndex = target.getAdapterPosition();
|
||||
final int sourceIndex = source.getBindingAdapterPosition();
|
||||
final int targetIndex = target.getBindingAdapterPosition();
|
||||
final boolean isSwapped = itemListAdapter.swapItems(sourceIndex, targetIndex);
|
||||
if (isSwapped) {
|
||||
saveChanges();
|
||||
|
|
|
|||
|
|
@ -97,11 +97,9 @@ public class SubscriptionsImportFragment extends BaseFragment {
|
|||
}
|
||||
|
||||
@Override
|
||||
public void setUserVisibleHint(final boolean isVisibleToUser) {
|
||||
super.setUserVisibleHint(isVisibleToUser);
|
||||
if (isVisibleToUser) {
|
||||
setTitle(getString(R.string.import_title));
|
||||
}
|
||||
public void onResume() {
|
||||
super.onResume();
|
||||
setTitle(getString(R.string.import_title));
|
||||
}
|
||||
|
||||
@Nullable
|
||||
|
|
|
|||
|
|
@ -112,8 +112,8 @@ class FeedGroupReorderDialog : DialogFragment() {
|
|||
source: RecyclerView.ViewHolder,
|
||||
target: RecyclerView.ViewHolder
|
||||
): Boolean {
|
||||
val sourceIndex = source.adapterPosition
|
||||
val targetIndex = target.adapterPosition
|
||||
val sourceIndex = source.bindingAdapterPosition
|
||||
val targetIndex = target.bindingAdapterPosition
|
||||
|
||||
groupAdapter.notifyItemMoved(sourceIndex, targetIndex)
|
||||
Collections.swap(groupOrderedIdList, sourceIndex, targetIndex)
|
||||
|
|
|
|||
|
|
@ -51,6 +51,6 @@ public abstract class PlayQueueItemTouchCallback extends ItemTouchHelper.SimpleC
|
|||
|
||||
@Override
|
||||
public void onSwiped(final RecyclerView.ViewHolder viewHolder, final int swipeDir) {
|
||||
onSwiped(viewHolder.getAdapterPosition());
|
||||
onSwiped(viewHolder.getBindingAdapterPosition());
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -16,8 +16,9 @@ public class MainSettingsFragment extends BasePreferenceFragment {
|
|||
public void onCreatePreferences(final Bundle savedInstanceState, final String rootKey) {
|
||||
addPreferencesFromResource(R.xml.main_settings);
|
||||
|
||||
if (!CheckForNewAppVersion.isGithubApk(App.getApp())) {
|
||||
final Preference update = findPreference(getString(R.string.update_pref_screen_key));
|
||||
if (!CheckForNewAppVersion.isReleaseApk(App.getApp())) {
|
||||
final Preference update
|
||||
= findPreference(getString(R.string.update_pref_screen_key));
|
||||
getPreferenceScreen().removePreference(update);
|
||||
|
||||
defaultPreferences.edit().putBoolean(getString(R.string.update_app_key), false).apply();
|
||||
|
|
|
|||
|
|
@ -303,8 +303,8 @@ public class PeertubeInstanceListFragment extends Fragment {
|
|||
return false;
|
||||
}
|
||||
|
||||
final int sourceIndex = source.getAdapterPosition();
|
||||
final int targetIndex = target.getAdapterPosition();
|
||||
final int sourceIndex = source.getBindingAdapterPosition();
|
||||
final int targetIndex = target.getBindingAdapterPosition();
|
||||
instanceListAdapter.swapItems(sourceIndex, targetIndex);
|
||||
return true;
|
||||
}
|
||||
|
|
@ -322,7 +322,7 @@ public class PeertubeInstanceListFragment extends Fragment {
|
|||
@Override
|
||||
public void onSwiped(@NonNull final RecyclerView.ViewHolder viewHolder,
|
||||
final int swipeDir) {
|
||||
final int position = viewHolder.getAdapterPosition();
|
||||
final int position = viewHolder.getBindingAdapterPosition();
|
||||
// do not allow swiping the selected instance
|
||||
if (instanceList.get(position).getUrl().equals(selectedInstance.getUrl())) {
|
||||
instanceListAdapter.notifyItemChanged(position);
|
||||
|
|
|
|||
|
|
@ -1,34 +1,48 @@
|
|||
package org.schabi.newpipe.settings;
|
||||
|
||||
import static org.schabi.newpipe.CheckForNewAppVersion.startNewVersionCheckService;
|
||||
|
||||
import android.os.Bundle;
|
||||
import android.widget.Toast;
|
||||
|
||||
import androidx.preference.Preference;
|
||||
|
||||
import org.schabi.newpipe.R;
|
||||
|
||||
import static org.schabi.newpipe.CheckForNewAppVersion.startNewVersionCheckService;
|
||||
|
||||
public class UpdateSettingsFragment extends BasePreferenceFragment {
|
||||
private final Preference.OnPreferenceChangeListener updatePreferenceChange
|
||||
= (preference, checkForUpdates) -> {
|
||||
defaultPreferences.edit()
|
||||
.putBoolean(getString(R.string.update_app_key), (boolean) checkForUpdates).apply();
|
||||
|
||||
if ((boolean) checkForUpdates) {
|
||||
// Search for updates immediately when update checks are enabled.
|
||||
// Reset the expire time. This is necessary to check for an update immediately.
|
||||
defaultPreferences.edit()
|
||||
.putLong(getString(R.string.update_expiry_key), 0).apply();
|
||||
startNewVersionCheckService();
|
||||
}
|
||||
if ((boolean) checkForUpdates) {
|
||||
checkNewVersionNow();
|
||||
}
|
||||
return true;
|
||||
};
|
||||
|
||||
private final Preference.OnPreferenceClickListener manualUpdateClick
|
||||
= preference -> {
|
||||
Toast.makeText(getContext(), R.string.checking_updates_toast, Toast.LENGTH_SHORT).show();
|
||||
checkNewVersionNow();
|
||||
return true;
|
||||
};
|
||||
|
||||
private void checkNewVersionNow() {
|
||||
// Search for updates immediately when update checks are enabled.
|
||||
// Reset the expire time. This is necessary to check for an update immediately.
|
||||
defaultPreferences.edit()
|
||||
.putLong(getString(R.string.update_expiry_key), 0).apply();
|
||||
startNewVersionCheckService();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCreatePreferences(final Bundle savedInstanceState, final String rootKey) {
|
||||
addPreferencesFromResource(R.xml.update_settings);
|
||||
|
||||
final String updateToggleKey = getString(R.string.update_app_key);
|
||||
findPreference(updateToggleKey).setOnPreferenceChangeListener(updatePreferenceChange);
|
||||
findPreference(getString(R.string.update_app_key))
|
||||
.setOnPreferenceChangeListener(updatePreferenceChange);
|
||||
findPreference(getString(R.string.manual_update_key))
|
||||
.setOnPreferenceClickListener(manualUpdateClick);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -299,8 +299,8 @@ public class ChooseTabsFragment extends Fragment {
|
|||
return false;
|
||||
}
|
||||
|
||||
final int sourceIndex = source.getAdapterPosition();
|
||||
final int targetIndex = target.getAdapterPosition();
|
||||
final int sourceIndex = source.getBindingAdapterPosition();
|
||||
final int targetIndex = target.getBindingAdapterPosition();
|
||||
selectedTabsAdapter.swapItems(sourceIndex, targetIndex);
|
||||
return true;
|
||||
}
|
||||
|
|
@ -318,7 +318,7 @@ public class ChooseTabsFragment extends Fragment {
|
|||
@Override
|
||||
public void onSwiped(@NonNull final RecyclerView.ViewHolder viewHolder,
|
||||
final int swipeDir) {
|
||||
final int position = viewHolder.getAdapterPosition();
|
||||
final int position = viewHolder.getBindingAdapterPosition();
|
||||
tabList.remove(position);
|
||||
selectedTabsAdapter.notifyItemRemoved(position);
|
||||
|
||||
|
|
|
|||
|
|
@ -77,7 +77,8 @@ public final class NavigationHelper {
|
|||
@NonNull
|
||||
public static <T> Intent getPlayerIntent(@NonNull final Context context,
|
||||
@NonNull final Class<T> targetClazz,
|
||||
@Nullable final PlayQueue playQueue) {
|
||||
@Nullable final PlayQueue playQueue,
|
||||
final boolean resumePlayback) {
|
||||
final Intent intent = new Intent(context, targetClazz);
|
||||
|
||||
if (playQueue != null) {
|
||||
|
|
@ -87,6 +88,7 @@ public final class NavigationHelper {
|
|||
}
|
||||
}
|
||||
intent.putExtra(Player.PLAYER_TYPE, MainPlayer.PlayerType.VIDEO.ordinal());
|
||||
intent.putExtra(Player.RESUME_PLAYBACK, resumePlayback);
|
||||
|
||||
return intent;
|
||||
}
|
||||
|
|
@ -95,8 +97,9 @@ public final class NavigationHelper {
|
|||
public static <T> Intent getPlayerIntent(@NonNull final Context context,
|
||||
@NonNull final Class<T> targetClazz,
|
||||
@Nullable final PlayQueue playQueue,
|
||||
final boolean resumePlayback,
|
||||
final boolean playWhenReady) {
|
||||
return getPlayerIntent(context, targetClazz, playQueue)
|
||||
return getPlayerIntent(context, targetClazz, playQueue, resumePlayback)
|
||||
.putExtra(Player.PLAY_WHEN_READY, playWhenReady);
|
||||
}
|
||||
|
||||
|
|
@ -104,7 +107,14 @@ public final class NavigationHelper {
|
|||
public static <T> Intent getPlayerEnqueueIntent(@NonNull final Context context,
|
||||
@NonNull final Class<T> targetClazz,
|
||||
@Nullable final PlayQueue playQueue) {
|
||||
return getPlayerIntent(context, targetClazz, playQueue)
|
||||
// when enqueueing `resumePlayback` is always `false` since:
|
||||
// - if there is a video already playing, the value of `resumePlayback` just doesn't make
|
||||
// any difference.
|
||||
// - if there is nothing already playing, it is useful for the enqueue action to have a
|
||||
// slightly different behaviour than the normal play action: the latter resumes playback,
|
||||
// the former doesn't. (note that enqueue can be triggered when nothing is playing only
|
||||
// by long pressing the video detail fragment, playlist or channel controls
|
||||
return getPlayerIntent(context, targetClazz, playQueue, false)
|
||||
.putExtra(Player.ENQUEUE, true);
|
||||
}
|
||||
|
||||
|
|
@ -112,7 +122,8 @@ public final class NavigationHelper {
|
|||
public static <T> Intent getPlayerEnqueueNextIntent(@NonNull final Context context,
|
||||
@NonNull final Class<T> targetClazz,
|
||||
@Nullable final PlayQueue playQueue) {
|
||||
return getPlayerIntent(context, targetClazz, playQueue)
|
||||
// see comment in `getPlayerEnqueueIntent` as to why `resumePlayback` is false
|
||||
return getPlayerIntent(context, targetClazz, playQueue, false)
|
||||
.putExtra(Player.ENQUEUE_NEXT, true);
|
||||
}
|
||||
|
||||
|
|
@ -495,6 +506,27 @@ public final class NavigationHelper {
|
|||
context.startActivity(intent);
|
||||
}
|
||||
|
||||
/**
|
||||
* Opens {@link ChannelFragment}.
|
||||
* Use this instead of {@link #openChannelFragment(FragmentManager, int, String, String)}
|
||||
* when no fragments are used / no FragmentManager is available.
|
||||
* @param context
|
||||
* @param serviceId
|
||||
* @param url
|
||||
* @param title
|
||||
*/
|
||||
public static void openChannelFragmentUsingIntent(final Context context,
|
||||
final int serviceId,
|
||||
final String url,
|
||||
@NonNull final String title) {
|
||||
final Intent intent = getOpenIntent(context, url, serviceId,
|
||||
StreamingService.LinkType.CHANNEL);
|
||||
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
|
||||
intent.putExtra(Constants.KEY_TITLE, title);
|
||||
|
||||
context.startActivity(intent);
|
||||
}
|
||||
|
||||
public static void openMainActivity(final Context context) {
|
||||
final Intent mIntent = new Intent(context, MainActivity.class);
|
||||
mIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
|
||||
|
|
|
|||
|
|
@ -0,0 +1,61 @@
|
|||
package org.schabi.newpipe.util;
|
||||
|
||||
import android.content.Context;
|
||||
import android.text.Selection;
|
||||
import android.text.Spannable;
|
||||
import android.widget.TextView;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import org.schabi.newpipe.util.external_communication.ShareUtils;
|
||||
import org.schabi.newpipe.views.NewPipeEditText;
|
||||
import org.schabi.newpipe.views.NewPipeTextView;
|
||||
|
||||
public final class NewPipeTextViewHelper {
|
||||
private NewPipeTextViewHelper() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Share the selected text of {@link NewPipeTextView NewPipeTextViews} and
|
||||
* {@link NewPipeEditText NewPipeEditTexts} with
|
||||
* {@link ShareUtils#shareText(Context, String, String)}.
|
||||
*
|
||||
* <p>
|
||||
* This allows EMUI users to get the Android share sheet instead of the EMUI share sheet when
|
||||
* using the {@code Share} command of the popup menu which appears when selecting text.
|
||||
* </p>
|
||||
*
|
||||
* @param textView the {@link TextView} on which sharing the selected text. It should be a
|
||||
* {@link NewPipeTextView} or a {@link NewPipeEditText} (even if
|
||||
* {@link TextView standard TextViews} are supported).
|
||||
*/
|
||||
public static void shareSelectedTextWithShareUtils(@NonNull final TextView textView) {
|
||||
final CharSequence textViewText = textView.getText();
|
||||
shareSelectedTextIfNotNullAndNotEmpty(textView, getSelectedText(textView, textViewText));
|
||||
if (textViewText instanceof Spannable) {
|
||||
Selection.setSelection((Spannable) textViewText, textView.getSelectionEnd());
|
||||
}
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private static CharSequence getSelectedText(@NonNull final TextView textView,
|
||||
@Nullable final CharSequence text) {
|
||||
if (!textView.hasSelection() || text == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
final int start = textView.getSelectionStart();
|
||||
final int end = textView.getSelectionEnd();
|
||||
return String.valueOf(start > end ? text.subSequence(end, start)
|
||||
: text.subSequence(start, end));
|
||||
}
|
||||
|
||||
private static void shareSelectedTextIfNotNullAndNotEmpty(
|
||||
@NonNull final TextView textView,
|
||||
@Nullable final CharSequence selectedText) {
|
||||
if (selectedText != null && selectedText.length() != 0) {
|
||||
ShareUtils.shareText(textView.getContext(), "", selectedText.toString());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,45 @@
|
|||
package org.schabi.newpipe.views;
|
||||
|
||||
import android.content.Context;
|
||||
import android.util.AttributeSet;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.appcompat.widget.AppCompatEditText;
|
||||
|
||||
import org.schabi.newpipe.util.NewPipeTextViewHelper;
|
||||
import org.schabi.newpipe.util.external_communication.ShareUtils;
|
||||
|
||||
/**
|
||||
* An {@link AppCompatEditText} which uses {@link ShareUtils#shareText(Context, String, String)}
|
||||
* when sharing selected text by using the {@code Share} command of the floating actions.
|
||||
* <p>
|
||||
* This allows NewPipe to show Android share sheet instead of EMUI share sheet when sharing text
|
||||
* from {@link AppCompatEditText} on EMUI devices.
|
||||
* </p>
|
||||
*/
|
||||
public class NewPipeEditText extends AppCompatEditText {
|
||||
|
||||
public NewPipeEditText(@NonNull final Context context) {
|
||||
super(context);
|
||||
}
|
||||
|
||||
public NewPipeEditText(@NonNull final Context context, @Nullable final AttributeSet attrs) {
|
||||
super(context, attrs);
|
||||
}
|
||||
|
||||
public NewPipeEditText(@NonNull final Context context,
|
||||
@Nullable final AttributeSet attrs,
|
||||
final int defStyleAttr) {
|
||||
super(context, attrs, defStyleAttr);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onTextContextMenuItem(final int id) {
|
||||
if (id == android.R.id.shareText) {
|
||||
NewPipeTextViewHelper.shareSelectedTextWithShareUtils(this);
|
||||
return true;
|
||||
}
|
||||
return super.onTextContextMenuItem(id);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,45 @@
|
|||
package org.schabi.newpipe.views;
|
||||
|
||||
import android.content.Context;
|
||||
import android.util.AttributeSet;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.appcompat.widget.AppCompatTextView;
|
||||
|
||||
import org.schabi.newpipe.util.NewPipeTextViewHelper;
|
||||
import org.schabi.newpipe.util.external_communication.ShareUtils;
|
||||
|
||||
/**
|
||||
* An {@link AppCompatTextView} which uses {@link ShareUtils#shareText(Context, String, String)}
|
||||
* when sharing selected text by using the {@code Share} command of the floating actions.
|
||||
* <p>
|
||||
* This allows NewPipe to show Android share sheet instead of EMUI share sheet when sharing text
|
||||
* from {@link AppCompatTextView} on EMUI devices.
|
||||
* </p>
|
||||
*/
|
||||
public class NewPipeTextView extends AppCompatTextView {
|
||||
|
||||
public NewPipeTextView(@NonNull final Context context) {
|
||||
super(context);
|
||||
}
|
||||
|
||||
public NewPipeTextView(@NonNull final Context context, @Nullable final AttributeSet attrs) {
|
||||
super(context, attrs);
|
||||
}
|
||||
|
||||
public NewPipeTextView(@NonNull final Context context,
|
||||
@Nullable final AttributeSet attrs,
|
||||
final int defStyleAttr) {
|
||||
super(context, attrs, defStyleAttr);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onTextContextMenuItem(final int id) {
|
||||
if (id == android.R.id.shareText) {
|
||||
NewPipeTextViewHelper.shareSelectedTextWithShareUtils(this);
|
||||
return true;
|
||||
}
|
||||
return super.onTextContextMenuItem(id);
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue