mirror of
https://github.com/TeamNewPipe/NewPipe
synced 2025-10-30 14:52:59 +00:00
Merge branch 'dev' into separate-gesture-options
This commit is contained in:
@@ -1,7 +1,5 @@
|
||||
package org.schabi.newpipe.fragments;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.SharedPreferences;
|
||||
import android.os.Bundle;
|
||||
import android.support.annotation.NonNull;
|
||||
import android.support.annotation.Nullable;
|
||||
@@ -12,7 +10,6 @@ import android.support.v4.app.FragmentPagerAdapter;
|
||||
import android.support.v4.view.ViewPager;
|
||||
import android.support.v7.app.ActionBar;
|
||||
import android.support.v7.app.AppCompatActivity;
|
||||
import android.support.v7.preference.PreferenceManager;
|
||||
import android.util.Log;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.Menu;
|
||||
@@ -23,42 +20,26 @@ import android.view.ViewGroup;
|
||||
|
||||
import org.schabi.newpipe.BaseFragment;
|
||||
import org.schabi.newpipe.R;
|
||||
import org.schabi.newpipe.fragments.list.channel.ChannelFragment;
|
||||
import org.schabi.newpipe.fragments.list.kiosk.KioskFragment;
|
||||
import org.schabi.newpipe.local.bookmark.BookmarkFragment;
|
||||
import org.schabi.newpipe.local.feed.FeedFragment;
|
||||
import org.schabi.newpipe.local.history.StatisticsPlaylistFragment;
|
||||
import org.schabi.newpipe.local.subscription.SubscriptionFragment;
|
||||
import org.schabi.newpipe.extractor.exceptions.ExtractionException;
|
||||
import org.schabi.newpipe.report.ErrorActivity;
|
||||
import org.schabi.newpipe.report.UserAction;
|
||||
import org.schabi.newpipe.util.KioskTranslator;
|
||||
import org.schabi.newpipe.settings.tabs.Tab;
|
||||
import org.schabi.newpipe.settings.tabs.TabsManager;
|
||||
import org.schabi.newpipe.util.NavigationHelper;
|
||||
import org.schabi.newpipe.util.ServiceHelper;
|
||||
import org.schabi.newpipe.util.ThemeHelper;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
public class MainFragment extends BaseFragment implements TabLayout.OnTabSelectedListener {
|
||||
|
||||
public int currentServiceId = -1;
|
||||
private ViewPager viewPager;
|
||||
private List<String> tabs = new ArrayList<>();
|
||||
static PagerAdapter adapter;
|
||||
TabLayout tabLayout;
|
||||
private SharedPreferences prefs;
|
||||
private Bundle savedInstanceStateBundle;
|
||||
private SelectedTabsPagerAdapter pagerAdapter;
|
||||
private TabLayout tabLayout;
|
||||
|
||||
private static final String TAB_NUMBER_BLANK = "0";
|
||||
private static final String TAB_NUMBER_KIOSK = "1";
|
||||
private static final String TAB_NUMBER_SUBSCIRPTIONS = "2";
|
||||
private static final String TAB_NUMBER_FEED = "3";
|
||||
private static final String TAB_NUMBER_BOOKMARKS = "4";
|
||||
private static final String TAB_NUMBER_HISTORY = "5";
|
||||
private static final String TAB_NUMBER_CHANNEL = "6";
|
||||
private List<Tab> tabsList = new ArrayList<>();
|
||||
private TabsManager tabsManager;
|
||||
|
||||
SharedPreferences.OnSharedPreferenceChangeListener listener;
|
||||
private boolean hasTabsChanged = false;
|
||||
|
||||
/*//////////////////////////////////////////////////////////////////////////
|
||||
// Fragment's LifeCycle
|
||||
@@ -66,23 +47,24 @@ public class MainFragment extends BaseFragment implements TabLayout.OnTabSelecte
|
||||
|
||||
@Override
|
||||
public void onCreate(Bundle savedInstanceState) {
|
||||
savedInstanceStateBundle = savedInstanceState;
|
||||
super.onCreate(savedInstanceState);
|
||||
setHasOptionsMenu(true);
|
||||
listener = (prefs, key) -> {
|
||||
if(key.equals("saveUsedTabs")) {
|
||||
mainPageChanged();
|
||||
|
||||
tabsManager = TabsManager.getManager(activity);
|
||||
tabsManager.setSavedTabsListener(() -> {
|
||||
if (DEBUG) {
|
||||
Log.d(TAG, "TabsManager.SavedTabsChangeListener: onTabsChanged called, isResumed = " + isResumed());
|
||||
}
|
||||
};
|
||||
if (isResumed()) {
|
||||
updateTabs();
|
||||
} else {
|
||||
hasTabsChanged = true;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
|
||||
currentServiceId = ServiceHelper.getSelectedServiceId(activity);
|
||||
|
||||
prefs = PreferenceManager.getDefaultSharedPreferences(getContext());
|
||||
prefs.registerOnSharedPreferenceChangeListener(listener);
|
||||
|
||||
return inflater.inflate(R.layout.fragment_main, container, false);
|
||||
}
|
||||
|
||||
@@ -94,110 +76,28 @@ public class MainFragment extends BaseFragment implements TabLayout.OnTabSelecte
|
||||
viewPager = rootView.findViewById(R.id.pager);
|
||||
|
||||
/* Nested fragment, use child fragment here to maintain backstack in view pager. */
|
||||
adapter = new PagerAdapter(getChildFragmentManager());
|
||||
viewPager.setAdapter(adapter);
|
||||
pagerAdapter = new SelectedTabsPagerAdapter(getChildFragmentManager());
|
||||
viewPager.setAdapter(pagerAdapter);
|
||||
|
||||
tabLayout.setupWithViewPager(viewPager);
|
||||
|
||||
mainPageChanged();
|
||||
tabLayout.addOnTabSelectedListener(this);
|
||||
updateTabs();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onResume() {
|
||||
super.onResume();
|
||||
|
||||
public void mainPageChanged() {
|
||||
getTabOrder();
|
||||
adapter.notifyDataSetChanged();
|
||||
viewPager.setOffscreenPageLimit(adapter.getCount());
|
||||
setIcons();
|
||||
setFirstTitle();
|
||||
}
|
||||
|
||||
private void setFirstTitle() {
|
||||
if((tabs.size() > 0)
|
||||
&& activity != null) {
|
||||
String tabInformation = tabs.get(0);
|
||||
if (tabInformation.startsWith(TAB_NUMBER_KIOSK + "\t")) {
|
||||
String kiosk[] = tabInformation.split("\t");
|
||||
if (kiosk.length == 3) {
|
||||
setTitle(kiosk[1]);
|
||||
}
|
||||
} else if (tabInformation.startsWith(TAB_NUMBER_CHANNEL + "\t")) {
|
||||
|
||||
String channelInfo[] = tabInformation.split("\t");
|
||||
if(channelInfo.length==4) {
|
||||
setTitle(channelInfo[2]);
|
||||
}
|
||||
} else {
|
||||
switch (tabInformation) {
|
||||
case TAB_NUMBER_BLANK:
|
||||
setTitle(getString(R.string.app_name));
|
||||
break;
|
||||
case TAB_NUMBER_SUBSCIRPTIONS:
|
||||
setTitle(getString(R.string.tab_subscriptions));
|
||||
break;
|
||||
case TAB_NUMBER_FEED:
|
||||
setTitle(getString(R.string.fragment_whats_new));
|
||||
break;
|
||||
case TAB_NUMBER_BOOKMARKS:
|
||||
setTitle(getString(R.string.tab_bookmarks));
|
||||
break;
|
||||
case TAB_NUMBER_HISTORY:
|
||||
setTitle(getString(R.string.title_activity_history));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (hasTabsChanged) {
|
||||
hasTabsChanged = false;
|
||||
updateTabs();
|
||||
}
|
||||
}
|
||||
|
||||
private void setIcons() {
|
||||
for (int i = 0; i < tabs.size(); i++) {
|
||||
String tabInformation = tabs.get(i);
|
||||
|
||||
TabLayout.Tab tabToSet = tabLayout.getTabAt(i);
|
||||
Context c = getContext();
|
||||
|
||||
if (tabToSet != null && c != null) {
|
||||
|
||||
if (tabInformation.startsWith(TAB_NUMBER_KIOSK + "\t")) {
|
||||
String kiosk[] = tabInformation.split("\t");
|
||||
if (kiosk.length == 3) {
|
||||
tabToSet.setIcon(KioskTranslator.getKioskIcons(kiosk[1], getContext()));
|
||||
}
|
||||
} else if (tabInformation.startsWith(TAB_NUMBER_CHANNEL + "\t")) {
|
||||
tabToSet.setIcon(ThemeHelper.resolveResourceIdFromAttr(getContext(), R.attr.ic_channel));
|
||||
} else {
|
||||
switch (tabInformation) {
|
||||
case TAB_NUMBER_BLANK:
|
||||
tabToSet.setIcon(ThemeHelper.resolveResourceIdFromAttr(getContext(), R.attr.ic_hot));
|
||||
break;
|
||||
case TAB_NUMBER_SUBSCIRPTIONS:
|
||||
tabToSet.setIcon(ThemeHelper.resolveResourceIdFromAttr(getContext(), R.attr.ic_channel));
|
||||
break;
|
||||
case TAB_NUMBER_FEED:
|
||||
tabToSet.setIcon(ThemeHelper.resolveResourceIdFromAttr(getContext(), R.attr.rss));
|
||||
break;
|
||||
case TAB_NUMBER_BOOKMARKS:
|
||||
tabToSet.setIcon(ThemeHelper.resolveResourceIdFromAttr(getContext(), R.attr.ic_bookmark));
|
||||
break;
|
||||
case TAB_NUMBER_HISTORY:
|
||||
tabToSet.setIcon(ThemeHelper.resolveResourceIdFromAttr(getContext(), R.attr.history));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private void getTabOrder() {
|
||||
tabs.clear();
|
||||
|
||||
String save = prefs.getString("saveUsedTabs", "1\tTrending\t0\n2\n4\n");
|
||||
String tabsArray[] = save.trim().split("\n");
|
||||
|
||||
Collections.addAll(tabs, tabsArray);
|
||||
@Override
|
||||
public void onDestroy() {
|
||||
super.onDestroy();
|
||||
tabsManager.unsetSavedTabsListener();
|
||||
}
|
||||
|
||||
/*//////////////////////////////////////////////////////////////////////////
|
||||
@@ -237,9 +137,33 @@ public class MainFragment extends BaseFragment implements TabLayout.OnTabSelecte
|
||||
// Tabs
|
||||
//////////////////////////////////////////////////////////////////////////*/
|
||||
|
||||
public void updateTabs() {
|
||||
tabsList.clear();
|
||||
tabsList.addAll(tabsManager.getTabs());
|
||||
pagerAdapter.notifyDataSetChanged();
|
||||
|
||||
viewPager.setOffscreenPageLimit(pagerAdapter.getCount());
|
||||
updateTabsIcon();
|
||||
updateCurrentTitle();
|
||||
}
|
||||
|
||||
private void updateTabsIcon() {
|
||||
for (int i = 0; i < tabsList.size(); i++) {
|
||||
final TabLayout.Tab tabToSet = tabLayout.getTabAt(i);
|
||||
if (tabToSet != null) {
|
||||
tabToSet.setIcon(tabsList.get(i).getTabIconRes(activity));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void updateCurrentTitle() {
|
||||
setTitle(tabsList.get(viewPager.getCurrentItem()).getTabName(requireContext()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTabSelected(TabLayout.Tab tab) {
|
||||
viewPager.setCurrentItem(tab.getPosition());
|
||||
public void onTabSelected(TabLayout.Tab selectedTab) {
|
||||
if (DEBUG) Log.d(TAG, "onTabSelected() called with: selectedTab = [" + selectedTab + "]");
|
||||
updateCurrentTitle();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -248,68 +172,40 @@ public class MainFragment extends BaseFragment implements TabLayout.OnTabSelecte
|
||||
|
||||
@Override
|
||||
public void onTabReselected(TabLayout.Tab tab) {
|
||||
if (DEBUG) Log.d(TAG, "onTabReselected() called with: tab = [" + tab + "]");
|
||||
updateCurrentTitle();
|
||||
}
|
||||
|
||||
private class PagerAdapter extends FragmentPagerAdapter {
|
||||
PagerAdapter(FragmentManager fm) {
|
||||
super(fm);
|
||||
private class SelectedTabsPagerAdapter extends FragmentPagerAdapter {
|
||||
private SelectedTabsPagerAdapter(FragmentManager fragmentManager) {
|
||||
super(fragmentManager);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Fragment getItem(int position) {
|
||||
String tabInformation = tabs.get(position);
|
||||
final Tab tab = tabsList.get(position);
|
||||
|
||||
if(tabInformation.startsWith(TAB_NUMBER_KIOSK + "\t")) {
|
||||
String kiosk[] = tabInformation.split("\t");
|
||||
if(kiosk.length==3) {
|
||||
KioskFragment fragment = null;
|
||||
try {
|
||||
fragment = KioskFragment.getInstance(Integer.parseInt(kiosk[2]), kiosk[1]);
|
||||
fragment.useAsFrontPage(true);
|
||||
return fragment;
|
||||
} catch (Exception e) {
|
||||
ErrorActivity.reportError(activity, e,
|
||||
activity.getClass(),
|
||||
null,
|
||||
ErrorActivity.ErrorInfo.make(UserAction.UI_ERROR,
|
||||
"none", "", R.string.app_ui_crash));
|
||||
}
|
||||
}
|
||||
} else if(tabInformation.startsWith(TAB_NUMBER_CHANNEL + "\t")) {
|
||||
String channelInfo[] = tabInformation.split("\t");
|
||||
if(channelInfo.length==4) {
|
||||
ChannelFragment fragment = ChannelFragment.getInstance(Integer.parseInt(channelInfo[3]), channelInfo[1], channelInfo[2]);
|
||||
fragment.useAsFrontPage(true);
|
||||
return fragment;
|
||||
} else {
|
||||
return new BlankFragment();
|
||||
}
|
||||
} else {
|
||||
switch (tabInformation) {
|
||||
case TAB_NUMBER_BLANK:
|
||||
return new BlankFragment();
|
||||
case TAB_NUMBER_SUBSCIRPTIONS:
|
||||
SubscriptionFragment sFragment = new SubscriptionFragment();
|
||||
sFragment.useAsFrontPage(true);
|
||||
return sFragment;
|
||||
case TAB_NUMBER_FEED:
|
||||
FeedFragment fFragment = new FeedFragment();
|
||||
fFragment.useAsFrontPage(true);
|
||||
return fFragment;
|
||||
case TAB_NUMBER_BOOKMARKS:
|
||||
BookmarkFragment bFragment = new BookmarkFragment();
|
||||
bFragment.useAsFrontPage(true);
|
||||
return bFragment;
|
||||
case TAB_NUMBER_HISTORY:
|
||||
StatisticsPlaylistFragment cFragment = new StatisticsPlaylistFragment();
|
||||
cFragment.useAsFrontPage(true);
|
||||
return cFragment;
|
||||
}
|
||||
}
|
||||
|
||||
return new BlankFragment();
|
||||
Throwable throwable = null;
|
||||
Fragment fragment = null;
|
||||
try {
|
||||
fragment = tab.getFragment();
|
||||
} catch (ExtractionException e) {
|
||||
throwable = e;
|
||||
}
|
||||
|
||||
if (throwable != null) {
|
||||
ErrorActivity.reportError(activity, throwable, activity.getClass(), null,
|
||||
ErrorActivity.ErrorInfo.make(UserAction.UI_ERROR, "none", "", R.string.app_ui_crash));
|
||||
return new BlankFragment();
|
||||
}
|
||||
|
||||
if (fragment instanceof BaseFragment) {
|
||||
((BaseFragment) fragment).useAsFrontPage(true);
|
||||
}
|
||||
|
||||
return fragment;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getItemPosition(Object object) {
|
||||
// Causes adapter to reload all Fragments when
|
||||
@@ -319,14 +215,14 @@ public class MainFragment extends BaseFragment implements TabLayout.OnTabSelecte
|
||||
|
||||
@Override
|
||||
public int getCount() {
|
||||
return tabs.size();
|
||||
return tabsList.size();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void destroyItem(ViewGroup container, int position, Object object) {
|
||||
getFragmentManager()
|
||||
getChildFragmentManager()
|
||||
.beginTransaction()
|
||||
.remove((Fragment)object)
|
||||
.remove((Fragment) object)
|
||||
.commitNowAllowingStateLoss();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -33,15 +33,14 @@ import org.schabi.newpipe.extractor.NewPipe;
|
||||
import org.schabi.newpipe.extractor.channel.ChannelInfo;
|
||||
import org.schabi.newpipe.extractor.exceptions.ExtractionException;
|
||||
import org.schabi.newpipe.extractor.stream.StreamInfoItem;
|
||||
import org.schabi.newpipe.extractor.linkhandler.ListLinkHandler;
|
||||
import org.schabi.newpipe.fragments.list.BaseListInfoFragment;
|
||||
import org.schabi.newpipe.info_list.InfoItemDialog;
|
||||
import org.schabi.newpipe.local.dialog.PlaylistAppendDialog;
|
||||
import org.schabi.newpipe.local.subscription.SubscriptionService;
|
||||
import org.schabi.newpipe.player.playqueue.ChannelPlayQueue;
|
||||
import org.schabi.newpipe.player.playqueue.PlayQueue;
|
||||
import org.schabi.newpipe.player.playqueue.SinglePlayQueue;
|
||||
import org.schabi.newpipe.report.UserAction;
|
||||
import org.schabi.newpipe.local.subscription.SubscriptionService;
|
||||
import org.schabi.newpipe.util.AnimationUtils;
|
||||
import org.schabi.newpipe.util.ExtractorHelper;
|
||||
import org.schabi.newpipe.util.ImageDisplayConstants;
|
||||
@@ -422,10 +421,12 @@ public class ChannelFragment extends BaseListInfoFragment<ChannelInfo> {
|
||||
imageLoader.displayImage(result.getAvatarUrl(), headerAvatarView,
|
||||
ImageDisplayConstants.DISPLAY_AVATAR_OPTIONS);
|
||||
|
||||
if (result.getSubscriberCount() != -1) {
|
||||
headerSubscribersTextView.setVisibility(View.VISIBLE);
|
||||
if (result.getSubscriberCount() >= 0) {
|
||||
headerSubscribersTextView.setText(Localization.localizeSubscribersCount(activity, result.getSubscriberCount()));
|
||||
headerSubscribersTextView.setVisibility(View.VISIBLE);
|
||||
} else headerSubscribersTextView.setVisibility(View.GONE);
|
||||
} else {
|
||||
headerSubscribersTextView.setText(R.string.subscribers_count_not_available);
|
||||
}
|
||||
|
||||
if (menuRssButton != null) menuRssButton.setVisible(!TextUtils.isEmpty(result.getFeedUrl()));
|
||||
|
||||
|
||||
@@ -70,7 +70,6 @@ import org.schabi.newpipe.player.playqueue.PlayQueue;
|
||||
import org.schabi.newpipe.player.playqueue.PlayQueueAdapter;
|
||||
import org.schabi.newpipe.player.playqueue.PlayQueueItem;
|
||||
import org.schabi.newpipe.player.resolver.MediaSourceTag;
|
||||
import org.schabi.newpipe.report.ErrorActivity;
|
||||
import org.schabi.newpipe.util.ImageDisplayConstants;
|
||||
import org.schabi.newpipe.util.SerializedCache;
|
||||
|
||||
@@ -88,7 +87,6 @@ import static com.google.android.exoplayer2.Player.DISCONTINUITY_REASON_INTERNAL
|
||||
import static com.google.android.exoplayer2.Player.DISCONTINUITY_REASON_PERIOD_TRANSITION;
|
||||
import static com.google.android.exoplayer2.Player.DISCONTINUITY_REASON_SEEK;
|
||||
import static com.google.android.exoplayer2.Player.DISCONTINUITY_REASON_SEEK_ADJUSTMENT;
|
||||
import static org.schabi.newpipe.report.UserAction.PLAY_STREAM;
|
||||
|
||||
/**
|
||||
* Base for the players, joining the common properties
|
||||
@@ -175,7 +173,6 @@ public abstract class BasePlayer implements
|
||||
};
|
||||
this.intentFilter = new IntentFilter();
|
||||
setupBroadcastReceiver(intentFilter);
|
||||
context.registerReceiver(broadcastReceiver, intentFilter);
|
||||
|
||||
this.recordManager = new HistoryRecordManager(context);
|
||||
|
||||
@@ -212,6 +209,8 @@ public abstract class BasePlayer implements
|
||||
audioReactor = new AudioReactor(context, simpleExoPlayer);
|
||||
mediaSessionManager = new MediaSessionManager(context, simpleExoPlayer,
|
||||
new BasePlayerMediaSession(this));
|
||||
|
||||
registerBroadcastReceiver();
|
||||
}
|
||||
|
||||
public void initListeners() {}
|
||||
@@ -362,11 +361,17 @@ public abstract class BasePlayer implements
|
||||
}
|
||||
}
|
||||
|
||||
public void unregisterBroadcastReceiver() {
|
||||
protected void registerBroadcastReceiver() {
|
||||
// Try to unregister current first
|
||||
unregisterBroadcastReceiver();
|
||||
context.registerReceiver(broadcastReceiver, intentFilter);
|
||||
}
|
||||
|
||||
protected void unregisterBroadcastReceiver() {
|
||||
try {
|
||||
context.unregisterReceiver(broadcastReceiver);
|
||||
} catch (final IllegalArgumentException unregisteredException) {
|
||||
Log.e(TAG, "Broadcast receiver already unregistered.", unregisteredException);
|
||||
Log.w(TAG, "Broadcast receiver already unregistered (" + unregisteredException.getMessage() + ")");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -460,7 +460,7 @@ public final class MainVideoPlayer extends AppCompatActivity
|
||||
public void initListeners() {
|
||||
super.initListeners();
|
||||
|
||||
MySimpleOnGestureListener listener = new MySimpleOnGestureListener();
|
||||
PlayerGestureListener listener = new PlayerGestureListener();
|
||||
gestureDetector = new GestureDetector(context, listener);
|
||||
gestureDetector.setIsLongpressEnabled(false);
|
||||
getRootView().setOnTouchListener(listener);
|
||||
@@ -489,6 +489,8 @@ public final class MainVideoPlayer extends AppCompatActivity
|
||||
|
||||
volumeProgressBar.setMax(maxGestureLength);
|
||||
brightnessProgressBar.setMax(maxGestureLength);
|
||||
|
||||
setInitialGestureValues();
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -799,6 +801,13 @@ public final class MainVideoPlayer extends AppCompatActivity
|
||||
// Utils
|
||||
//////////////////////////////////////////////////////////////////////////*/
|
||||
|
||||
private void setInitialGestureValues() {
|
||||
if (getAudioReactor() != null) {
|
||||
final float currentVolumeNormalized = (float) getAudioReactor().getVolume() / getAudioReactor().getMaxVolume();
|
||||
volumeProgressBar.setProgress((int) (volumeProgressBar.getMax() * currentVolumeNormalized));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void showControlsThenHide() {
|
||||
if (queueVisible) return;
|
||||
@@ -939,7 +948,7 @@ public final class MainVideoPlayer extends AppCompatActivity
|
||||
}
|
||||
}
|
||||
|
||||
private class MySimpleOnGestureListener extends GestureDetector.SimpleOnGestureListener implements View.OnTouchListener {
|
||||
private class PlayerGestureListener extends GestureDetector.SimpleOnGestureListener implements View.OnTouchListener {
|
||||
private boolean isMoving;
|
||||
|
||||
@Override
|
||||
@@ -978,33 +987,34 @@ public final class MainVideoPlayer extends AppCompatActivity
|
||||
return super.onDown(e);
|
||||
}
|
||||
|
||||
private static final int MOVEMENT_THRESHOLD = 40;
|
||||
|
||||
private final boolean isVolumeGestureEnabled = PlayerHelper.isVolumeGestureEnabled(getApplicationContext());
|
||||
private final boolean isBrightnessGestureEnabled = PlayerHelper.isBrightnessGestureEnabled(getApplicationContext());
|
||||
|
||||
private final boolean isPlayerGestureEnabled = PlayerHelper.isPlayerGestureEnabled(getApplicationContext());
|
||||
private final int maxVolume = playerImpl.getAudioReactor().getMaxVolume();
|
||||
|
||||
private final int MOVEMENT_THRESHOLD = 40;
|
||||
|
||||
@Override
|
||||
public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {
|
||||
public boolean onScroll(MotionEvent initialEvent, MotionEvent movingEvent, float distanceX, float distanceY) {
|
||||
if (!isVolumeGestureEnabled && !isBrightnessGestureEnabled) return false;
|
||||
|
||||
//noinspection PointlessBooleanExpression
|
||||
if (DEBUG && false) Log.d(TAG, "MainVideoPlayer.onScroll = " +
|
||||
", e1.getRaw = [" + e1.getRawX() + ", " + e1.getRawY() + "]" +
|
||||
", e2.getRaw = [" + e2.getRawX() + ", " + e2.getRawY() + "]" +
|
||||
", e1.getRaw = [" + initialEvent.getRawX() + ", " + initialEvent.getRawY() + "]" +
|
||||
", e2.getRaw = [" + movingEvent.getRawX() + ", " + movingEvent.getRawY() + "]" +
|
||||
", distanceXy = [" + distanceX + ", " + distanceY + "]");
|
||||
|
||||
if (!isMoving && (
|
||||
Math.abs(e2.getY() - e1.getY()) <= MOVEMENT_THRESHOLD
|
||||
|| Math.abs(distanceX) > Math.abs(distanceY)
|
||||
) || playerImpl.getCurrentState() == BasePlayer.STATE_COMPLETED)
|
||||
final boolean insideThreshold = Math.abs(movingEvent.getY() - initialEvent.getY()) <= MOVEMENT_THRESHOLD;
|
||||
if (!isMoving && (insideThreshold || Math.abs(distanceX) > Math.abs(distanceY))
|
||||
|| playerImpl.getCurrentState() == BasePlayer.STATE_COMPLETED) {
|
||||
return false;
|
||||
}
|
||||
|
||||
isMoving = true;
|
||||
|
||||
boolean acceptAnyArea = isVolumeGestureEnabled != isBrightnessGestureEnabled;
|
||||
boolean acceptVolumeArea = acceptAnyArea || e1.getX() > playerImpl.getRootView().getWidth() / 2;
|
||||
boolean acceptVolumeArea = acceptAnyArea || initialEvent.getX() > playerImpl.getRootView().getWidth() / 2;
|
||||
boolean acceptBrightnessArea = acceptAnyArea || !acceptVolumeArea;
|
||||
|
||||
if (isVolumeGestureEnabled && acceptVolumeArea) {
|
||||
|
||||
@@ -1,41 +0,0 @@
|
||||
package org.schabi.newpipe.settings;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.app.AlertDialog;
|
||||
import android.content.Context;
|
||||
import android.content.DialogInterface;
|
||||
import android.support.annotation.NonNull;
|
||||
import android.support.annotation.Nullable;
|
||||
import android.view.View;
|
||||
import android.widget.TextView;
|
||||
|
||||
import org.schabi.newpipe.R;
|
||||
import org.schabi.newpipe.extractor.stream.StreamInfoItem;
|
||||
|
||||
public class AddTabsDialog {
|
||||
private final AlertDialog dialog;
|
||||
|
||||
public AddTabsDialog(@NonNull final Context context,
|
||||
@NonNull final String title,
|
||||
@NonNull final String[] commands,
|
||||
@NonNull final DialogInterface.OnClickListener actions) {
|
||||
|
||||
final View bannerView = View.inflate(context, R.layout.dialog_title, null);
|
||||
bannerView.setSelected(true);
|
||||
|
||||
TextView titleView = bannerView.findViewById(R.id.itemTitleView);
|
||||
titleView.setText(title);
|
||||
|
||||
TextView detailsView = bannerView.findViewById(R.id.itemAdditionalDetails);
|
||||
detailsView.setVisibility(View.GONE);
|
||||
|
||||
dialog = new AlertDialog.Builder(context)
|
||||
.setCustomTitle(bannerView)
|
||||
.setItems(commands, actions)
|
||||
.create();
|
||||
}
|
||||
|
||||
public void show() {
|
||||
dialog.show();
|
||||
}
|
||||
}
|
||||
@@ -1,291 +0,0 @@
|
||||
package org.schabi.newpipe.settings;
|
||||
|
||||
import android.app.Dialog;
|
||||
import android.content.SharedPreferences;
|
||||
import android.content.res.ColorStateList;
|
||||
import android.os.Bundle;
|
||||
import android.support.annotation.NonNull;
|
||||
import android.support.annotation.Nullable;
|
||||
import android.support.design.widget.FloatingActionButton;
|
||||
import android.support.v4.app.Fragment;
|
||||
import android.support.v7.app.AppCompatActivity;
|
||||
import android.support.v7.widget.CardView;
|
||||
import android.support.v7.widget.LinearLayoutManager;
|
||||
import android.support.v7.widget.RecyclerView;
|
||||
import android.support.v7.widget.helper.ItemTouchHelper;
|
||||
import android.util.TypedValue;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.MotionEvent;
|
||||
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.util.ThemeHelper;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
public class ChoseTabsFragment extends Fragment {
|
||||
|
||||
public ChoseTabsFragment.SelectedTabsAdapter selectedTabsAdapter;
|
||||
|
||||
RecyclerView selectedTabsView;
|
||||
|
||||
List<String> selectedTabs = new ArrayList<>();
|
||||
private String saveString;
|
||||
public String[] availableTabs = new String[7];
|
||||
|
||||
@Override
|
||||
public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container,
|
||||
Bundle savedInstanceState) {
|
||||
((AppCompatActivity)getContext()).getSupportActionBar().setTitle(R.string.main_page_content);
|
||||
return inflater.inflate(R.layout.fragment_chose_tabs, container, false);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void onViewCreated(@NonNull View rootView, @Nullable Bundle savedInstanceState) {
|
||||
super.onViewCreated(rootView, savedInstanceState);
|
||||
|
||||
tabNames();
|
||||
initUsedTabs();
|
||||
initButton(rootView);
|
||||
|
||||
selectedTabsView = rootView.findViewById(R.id.usedTabs);
|
||||
selectedTabsView.setLayoutManager(new LinearLayoutManager(getContext()));
|
||||
selectedTabsAdapter = new ChoseTabsFragment.SelectedTabsAdapter();
|
||||
|
||||
|
||||
ItemTouchHelper itemTouchHelper = new ItemTouchHelper(getItemTouchCallback());
|
||||
itemTouchHelper.attachToRecyclerView(selectedTabsView);
|
||||
selectedTabsAdapter.setOnItemSelectedListener(itemTouchHelper);
|
||||
|
||||
selectedTabsView.setAdapter(selectedTabsAdapter);
|
||||
}
|
||||
|
||||
private void saveChanges() {
|
||||
StringBuilder save = new StringBuilder();
|
||||
if(selectedTabs.size()==0) {
|
||||
save = new StringBuilder("0");
|
||||
} else {
|
||||
for(String s: selectedTabs) {
|
||||
save.append(s);
|
||||
save.append("\n");
|
||||
}
|
||||
}
|
||||
saveString = save.toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPause() {
|
||||
saveChanges();
|
||||
SharedPreferences sharedPreferences = android.preference.PreferenceManager.getDefaultSharedPreferences(getContext());
|
||||
SharedPreferences.Editor editor = sharedPreferences.edit();
|
||||
editor.putString("saveUsedTabs", saveString);
|
||||
editor.commit();
|
||||
super.onPause();
|
||||
}
|
||||
|
||||
private void initUsedTabs() {
|
||||
String save = android.preference.PreferenceManager.getDefaultSharedPreferences(getContext()).getString("saveUsedTabs", "1\tTrending\t0\n2\n4\n");
|
||||
String tabs[] = save.trim().split("\n");
|
||||
selectedTabs.addAll(Arrays.asList(tabs));
|
||||
}
|
||||
|
||||
private void tabNames() {
|
||||
availableTabs[0] = getString(R.string.blank_page_summary);
|
||||
availableTabs[1] = getString(R.string.kiosk_page_summary);
|
||||
availableTabs[2] = getString(R.string.subscription_page_summary);
|
||||
availableTabs[3] = getString(R.string.feed_page_summary);
|
||||
availableTabs[4] = getString(R.string.tab_bookmarks);
|
||||
availableTabs[5] = getString(R.string.title_activity_history);
|
||||
availableTabs[6] = getString(R.string.channel_page_summary);
|
||||
}
|
||||
|
||||
private void initButton(View rootView) {
|
||||
FloatingActionButton fab = rootView.findViewById(R.id.floatingActionButton);
|
||||
fab.setImageResource(ThemeHelper.getIconByAttr(R.attr.ic_add, getContext()));
|
||||
fab.setOnClickListener(v -> {
|
||||
Dialog.OnClickListener onClickListener = (dialog, which) -> addTab(which);
|
||||
|
||||
new AddTabsDialog(getContext(),
|
||||
getString(R.string.tab_chose),
|
||||
availableTabs,
|
||||
onClickListener)
|
||||
.show();
|
||||
});
|
||||
|
||||
TypedValue typedValue = new TypedValue();
|
||||
getActivity().getTheme().resolveAttribute(R.attr.colorPrimary, typedValue, true);
|
||||
int color = typedValue.data;
|
||||
fab.setBackgroundTintList(ColorStateList.valueOf(color));
|
||||
}
|
||||
|
||||
|
||||
private void addTab(int position) {
|
||||
if(position==6) {
|
||||
SelectChannelFragment selectChannelFragment = new SelectChannelFragment();
|
||||
selectChannelFragment.setOnSelectedLisener((String url, String name, int service) -> {
|
||||
selectedTabs.add(position+"\t"+url+"\t"+name+"\t"+service);
|
||||
selectedTabsAdapter.notifyDataSetChanged();
|
||||
saveChanges();
|
||||
});
|
||||
selectChannelFragment.show(getFragmentManager(), "select_channel");
|
||||
} else if(position==1) {
|
||||
SelectKioskFragment selectKioskFragment = new SelectKioskFragment();
|
||||
selectKioskFragment.setOnSelectedLisener((String kioskId, int service_id) -> {
|
||||
selectedTabs.add(position+"\t"+kioskId+"\t"+service_id);
|
||||
selectedTabsAdapter.notifyDataSetChanged();
|
||||
saveChanges();
|
||||
});
|
||||
selectKioskFragment.show(getFragmentManager(), "select_kiosk");
|
||||
} else {
|
||||
selectedTabs.add(String.valueOf(position));
|
||||
selectedTabsAdapter.notifyDataSetChanged();
|
||||
saveChanges();
|
||||
}
|
||||
}
|
||||
|
||||
public class SelectedTabsAdapter extends RecyclerView.Adapter<ChoseTabsFragment.SelectedTabsAdapter.TabViewHolder>{
|
||||
private ItemTouchHelper itemTouchHelper;
|
||||
|
||||
public void setOnItemSelectedListener(ItemTouchHelper mItemTouchHelper) {
|
||||
itemTouchHelper = mItemTouchHelper;
|
||||
}
|
||||
|
||||
public void swapItems(int fromPosition, int toPosition) {
|
||||
String temp = selectedTabs.get(fromPosition);
|
||||
selectedTabs.set(fromPosition, selectedTabs.get(toPosition));
|
||||
selectedTabs.set(toPosition, temp);
|
||||
notifyItemMoved(fromPosition, toPosition);
|
||||
saveChanges();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ChoseTabsFragment.SelectedTabsAdapter.TabViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
|
||||
|
||||
LayoutInflater inflater = LayoutInflater.from(getContext());
|
||||
View view = inflater.inflate(R.layout.viewholder_chose_tabs, parent, false);
|
||||
return new ChoseTabsFragment.SelectedTabsAdapter.TabViewHolder(view);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBindViewHolder(@NonNull ChoseTabsFragment.SelectedTabsAdapter.TabViewHolder holder, int position) {
|
||||
holder.bind(position, holder);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getItemCount() {
|
||||
return selectedTabs.size();
|
||||
}
|
||||
|
||||
class TabViewHolder extends RecyclerView.ViewHolder {
|
||||
|
||||
TextView text;
|
||||
View view;
|
||||
CardView cardView;
|
||||
ImageView handle;
|
||||
|
||||
public TabViewHolder(View itemView) {
|
||||
super(itemView);
|
||||
|
||||
text = itemView.findViewById(R.id.tabName);
|
||||
cardView = itemView.findViewById(R.id.layoutCard);
|
||||
handle = itemView.findViewById(R.id.handle);
|
||||
view = itemView;
|
||||
}
|
||||
|
||||
void bind(int position, TabViewHolder holder) {
|
||||
handle.setImageResource(ThemeHelper.getIconByAttr(R.attr.drag_handle, getContext()));
|
||||
handle.setOnTouchListener(getOnTouchListener(holder));
|
||||
|
||||
view.setOnLongClickListener(getOnLongClickListener(holder));
|
||||
|
||||
if(selectedTabs.get(position).startsWith("6\t")) {
|
||||
String channelInfo[] = selectedTabs.get(position).split("\t");
|
||||
String channelName = "";
|
||||
if (channelInfo.length == 4) channelName = channelInfo[2];
|
||||
String textToSet = availableTabs[6] + ": " + channelName;
|
||||
text.setText(textToSet);
|
||||
} else if(selectedTabs.get(position).startsWith("1\t")) {
|
||||
String kioskInfo[] = selectedTabs.get(position).split("\t");
|
||||
String kioskName = "";
|
||||
if (kioskInfo.length == 3) kioskName = kioskInfo[1];
|
||||
String textToSet = availableTabs[1] + ": " + kioskName;
|
||||
text.setText(textToSet);
|
||||
} else {
|
||||
text.setText(availableTabs[Integer.parseInt(selectedTabs.get(position))]);
|
||||
}
|
||||
}
|
||||
|
||||
private View.OnTouchListener getOnTouchListener(final RecyclerView.ViewHolder item) {
|
||||
return (view, motionEvent) -> {
|
||||
view.performClick();
|
||||
if (motionEvent.getActionMasked() == MotionEvent.ACTION_DOWN) {
|
||||
if(itemTouchHelper != null) itemTouchHelper.startDrag(item);
|
||||
}
|
||||
return false;
|
||||
};
|
||||
}
|
||||
|
||||
private View.OnLongClickListener getOnLongClickListener(TabViewHolder holder) {
|
||||
return (view) -> {
|
||||
if(itemTouchHelper != null) itemTouchHelper.startSwipe(holder);
|
||||
return false;
|
||||
};
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
private ItemTouchHelper.SimpleCallback getItemTouchCallback() {
|
||||
return new ItemTouchHelper.SimpleCallback(ItemTouchHelper.UP | ItemTouchHelper.DOWN,
|
||||
ItemTouchHelper.START | ItemTouchHelper.END) {
|
||||
@Override
|
||||
public int interpolateOutOfBoundsScroll(RecyclerView recyclerView, int viewSize,
|
||||
int viewSizeOutOfBounds, int totalSize,
|
||||
long msSinceStartScroll) {
|
||||
final int standardSpeed = super.interpolateOutOfBoundsScroll(recyclerView, viewSize,
|
||||
viewSizeOutOfBounds, totalSize, msSinceStartScroll);
|
||||
final int minimumAbsVelocity = Math.max(12,
|
||||
Math.abs(standardSpeed));
|
||||
return minimumAbsVelocity * (int) Math.signum(viewSizeOutOfBounds);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onMove(RecyclerView recyclerView, RecyclerView.ViewHolder source,
|
||||
RecyclerView.ViewHolder target) {
|
||||
if (source.getItemViewType() != target.getItemViewType() ||
|
||||
selectedTabsAdapter == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
final int sourceIndex = source.getAdapterPosition();
|
||||
final int targetIndex = target.getAdapterPosition();
|
||||
selectedTabsAdapter.swapItems(sourceIndex, targetIndex);
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isLongPressDragEnabled() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isItemViewSwipeEnabled() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSwiped(RecyclerView.ViewHolder viewHolder, int swipeDir) {
|
||||
int position = viewHolder.getAdapterPosition();
|
||||
selectedTabs.remove(position);
|
||||
selectedTabsAdapter.notifyItemRemoved(position);
|
||||
saveChanges();
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -66,7 +66,7 @@ public class SelectChannelFragment extends DialogFragment {
|
||||
//////////////////////////////////////////////////////////////////////////*/
|
||||
|
||||
public interface OnSelectedLisener {
|
||||
void onChannelSelected(String url, String name, int service);
|
||||
void onChannelSelected(int serviceId, String url, String name);
|
||||
}
|
||||
OnSelectedLisener onSelectedLisener = null;
|
||||
public void setOnSelectedLisener(OnSelectedLisener listener) {
|
||||
@@ -126,7 +126,7 @@ public class SelectChannelFragment extends DialogFragment {
|
||||
private void clickedItem(int position) {
|
||||
if(onSelectedLisener != null) {
|
||||
SubscriptionEntity entry = subscriptions.get(position);
|
||||
onSelectedLisener.onChannelSelected(entry.getUrl(), entry.getName(), entry.getServiceId());
|
||||
onSelectedLisener.onChannelSelected(entry.getServiceId(), entry.getUrl(), entry.getName());
|
||||
}
|
||||
dismiss();
|
||||
}
|
||||
|
||||
@@ -56,7 +56,7 @@ public class SelectKioskFragment extends DialogFragment {
|
||||
//////////////////////////////////////////////////////////////////////////*/
|
||||
|
||||
public interface OnSelectedLisener {
|
||||
void onKioskSelected(String kioskId, int service_id);
|
||||
void onKioskSelected(int serviceId, String kioskId, String kioskName);
|
||||
}
|
||||
|
||||
OnSelectedLisener onSelectedLisener = null;
|
||||
@@ -101,7 +101,7 @@ public class SelectKioskFragment extends DialogFragment {
|
||||
|
||||
private void clickedItem(SelectKioskAdapter.Entry entry) {
|
||||
if(onSelectedLisener != null) {
|
||||
onSelectedLisener.onKioskSelected(entry.kioskId, entry.serviceId);
|
||||
onSelectedLisener.onKioskSelected(entry.serviceId, entry.kioskId, entry.kioskName);
|
||||
}
|
||||
dismiss();
|
||||
}
|
||||
|
||||
@@ -77,7 +77,8 @@ public class SettingsActivity extends AppCompatActivity implements BasePreferenc
|
||||
finish();
|
||||
} else getSupportFragmentManager().popBackStack();
|
||||
}
|
||||
return true;
|
||||
|
||||
return super.onOptionsItemSelected(item);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -0,0 +1,94 @@
|
||||
package org.schabi.newpipe.settings.tabs;
|
||||
|
||||
import android.app.AlertDialog;
|
||||
import android.content.Context;
|
||||
import android.content.DialogInterface;
|
||||
import android.support.annotation.DrawableRes;
|
||||
import android.support.annotation.NonNull;
|
||||
import android.support.v7.widget.AppCompatImageView;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.BaseAdapter;
|
||||
import android.widget.TextView;
|
||||
|
||||
import org.schabi.newpipe.R;
|
||||
import org.schabi.newpipe.util.ThemeHelper;
|
||||
|
||||
public class AddTabDialog {
|
||||
private final AlertDialog dialog;
|
||||
|
||||
AddTabDialog(@NonNull final Context context,
|
||||
@NonNull final ChooseTabListItem[] items,
|
||||
@NonNull final DialogInterface.OnClickListener actions) {
|
||||
|
||||
dialog = new AlertDialog.Builder(context)
|
||||
.setTitle(context.getString(R.string.tab_choose))
|
||||
.setAdapter(new DialogListAdapter(context, items), actions)
|
||||
.create();
|
||||
}
|
||||
|
||||
public void show() {
|
||||
dialog.show();
|
||||
}
|
||||
|
||||
public static final class ChooseTabListItem {
|
||||
final int tabId;
|
||||
final String itemName;
|
||||
@DrawableRes final int itemIcon;
|
||||
|
||||
ChooseTabListItem(Context context, Tab tab) {
|
||||
this(tab.getTabId(), tab.getTabName(context), tab.getTabIconRes(context));
|
||||
}
|
||||
|
||||
ChooseTabListItem(int tabId, String itemName, @DrawableRes int itemIcon) {
|
||||
this.tabId = tabId;
|
||||
this.itemName = itemName;
|
||||
this.itemIcon = itemIcon;
|
||||
}
|
||||
}
|
||||
|
||||
private static class DialogListAdapter extends BaseAdapter {
|
||||
private final LayoutInflater inflater;
|
||||
private final ChooseTabListItem[] items;
|
||||
|
||||
@DrawableRes private final int fallbackIcon;
|
||||
|
||||
private DialogListAdapter(Context context, ChooseTabListItem[] items) {
|
||||
this.inflater = LayoutInflater.from(context);
|
||||
this.items = items;
|
||||
this.fallbackIcon = ThemeHelper.resolveResourceIdFromAttr(context, R.attr.ic_hot);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getCount() {
|
||||
return items.length;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ChooseTabListItem getItem(int position) {
|
||||
return items[position];
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getItemId(int position) {
|
||||
return getItem(position).tabId;
|
||||
}
|
||||
|
||||
@Override
|
||||
public View getView(int position, View convertView, ViewGroup parent) {
|
||||
if (convertView == null) {
|
||||
convertView = inflater.inflate(R.layout.list_choose_tabs_dialog, parent, false);
|
||||
}
|
||||
|
||||
final ChooseTabListItem item = getItem(position);
|
||||
final AppCompatImageView tabIconView = convertView.findViewById(R.id.tabIcon);
|
||||
final TextView tabNameView = convertView.findViewById(R.id.tabName);
|
||||
|
||||
tabIconView.setImageResource(item.itemIcon > 0 ? item.itemIcon : fallbackIcon);
|
||||
tabNameView.setText(item.itemName);
|
||||
|
||||
return convertView;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,386 @@
|
||||
package org.schabi.newpipe.settings.tabs;
|
||||
|
||||
import android.annotation.SuppressLint;
|
||||
import android.app.Dialog;
|
||||
import android.content.Context;
|
||||
import android.os.Bundle;
|
||||
import android.support.annotation.NonNull;
|
||||
import android.support.annotation.Nullable;
|
||||
import android.support.design.widget.FloatingActionButton;
|
||||
import android.support.v4.app.Fragment;
|
||||
import android.support.v7.app.ActionBar;
|
||||
import android.support.v7.app.AlertDialog;
|
||||
import android.support.v7.app.AppCompatActivity;
|
||||
import android.support.v7.content.res.AppCompatResources;
|
||||
import android.support.v7.widget.AppCompatImageView;
|
||||
import android.support.v7.widget.LinearLayoutManager;
|
||||
import android.support.v7.widget.RecyclerView;
|
||||
import android.support.v7.widget.helper.ItemTouchHelper;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.Menu;
|
||||
import android.view.MenuInflater;
|
||||
import android.view.MenuItem;
|
||||
import android.view.MotionEvent;
|
||||
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.NewPipe;
|
||||
import org.schabi.newpipe.report.ErrorActivity;
|
||||
import org.schabi.newpipe.report.UserAction;
|
||||
import org.schabi.newpipe.settings.SelectChannelFragment;
|
||||
import org.schabi.newpipe.settings.SelectKioskFragment;
|
||||
import org.schabi.newpipe.settings.tabs.AddTabDialog.ChooseTabListItem;
|
||||
import org.schabi.newpipe.util.ThemeHelper;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import static org.schabi.newpipe.settings.tabs.Tab.typeFrom;
|
||||
|
||||
public class ChooseTabsFragment extends Fragment {
|
||||
|
||||
private TabsManager tabsManager;
|
||||
private List<Tab> tabList = new ArrayList<>();
|
||||
public ChooseTabsFragment.SelectedTabsAdapter selectedTabsAdapter;
|
||||
|
||||
/*//////////////////////////////////////////////////////////////////////////
|
||||
// Lifecycle
|
||||
//////////////////////////////////////////////////////////////////////////*/
|
||||
|
||||
@Override
|
||||
public void onCreate(@Nullable Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
|
||||
tabsManager = TabsManager.getManager(requireContext());
|
||||
updateTabList();
|
||||
|
||||
setHasOptionsMenu(true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
|
||||
return inflater.inflate(R.layout.fragment_choose_tabs, container, false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onViewCreated(@NonNull View rootView, @Nullable Bundle savedInstanceState) {
|
||||
super.onViewCreated(rootView, savedInstanceState);
|
||||
|
||||
initButton(rootView);
|
||||
|
||||
RecyclerView listSelectedTabs = rootView.findViewById(R.id.selectedTabs);
|
||||
listSelectedTabs.setLayoutManager(new LinearLayoutManager(requireContext()));
|
||||
|
||||
ItemTouchHelper itemTouchHelper = new ItemTouchHelper(getItemTouchCallback());
|
||||
itemTouchHelper.attachToRecyclerView(listSelectedTabs);
|
||||
|
||||
selectedTabsAdapter = new SelectedTabsAdapter(requireContext(), itemTouchHelper);
|
||||
listSelectedTabs.setAdapter(selectedTabsAdapter);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onResume() {
|
||||
super.onResume();
|
||||
updateTitle();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPause() {
|
||||
super.onPause();
|
||||
saveChanges();
|
||||
}
|
||||
|
||||
/*//////////////////////////////////////////////////////////////////////////
|
||||
// Menu
|
||||
//////////////////////////////////////////////////////////////////////////*/
|
||||
|
||||
private final int MENU_ITEM_RESTORE_ID = 123456;
|
||||
|
||||
@Override
|
||||
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
|
||||
super.onCreateOptionsMenu(menu, inflater);
|
||||
|
||||
final MenuItem restoreItem = menu.add(Menu.NONE, MENU_ITEM_RESTORE_ID, Menu.NONE, R.string.restore_defaults);
|
||||
restoreItem.setShowAsAction(MenuItem.SHOW_AS_ACTION_ALWAYS);
|
||||
|
||||
final int restoreIcon = ThemeHelper.resolveResourceIdFromAttr(requireContext(), R.attr.ic_restore_defaults);
|
||||
restoreItem.setIcon(AppCompatResources.getDrawable(requireContext(), restoreIcon));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onOptionsItemSelected(MenuItem item) {
|
||||
if (item.getItemId() == MENU_ITEM_RESTORE_ID) {
|
||||
restoreDefaults();
|
||||
return true;
|
||||
}
|
||||
|
||||
return super.onOptionsItemSelected(item);
|
||||
}
|
||||
|
||||
/*//////////////////////////////////////////////////////////////////////////
|
||||
// Utils
|
||||
//////////////////////////////////////////////////////////////////////////*/
|
||||
|
||||
private void updateTabList() {
|
||||
tabList.clear();
|
||||
tabList.addAll(tabsManager.getTabs());
|
||||
}
|
||||
|
||||
private void updateTitle() {
|
||||
if (getActivity() instanceof AppCompatActivity) {
|
||||
ActionBar actionBar = ((AppCompatActivity) getActivity()).getSupportActionBar();
|
||||
if (actionBar != null) actionBar.setTitle(R.string.main_page_content);
|
||||
}
|
||||
}
|
||||
|
||||
private void saveChanges() {
|
||||
tabsManager.saveTabs(tabList);
|
||||
}
|
||||
|
||||
private void restoreDefaults() {
|
||||
new AlertDialog.Builder(requireContext(), ThemeHelper.getDialogTheme(requireContext()))
|
||||
.setTitle(R.string.restore_defaults)
|
||||
.setMessage(R.string.restore_defaults_confirmation)
|
||||
.setNegativeButton(R.string.cancel, null)
|
||||
.setPositiveButton(R.string.yes, (dialog, which) -> {
|
||||
tabsManager.resetTabs();
|
||||
updateTabList();
|
||||
selectedTabsAdapter.notifyDataSetChanged();
|
||||
})
|
||||
.show();
|
||||
}
|
||||
|
||||
private void initButton(View rootView) {
|
||||
final FloatingActionButton fab = rootView.findViewById(R.id.addTabsButton);
|
||||
fab.setOnClickListener(v -> {
|
||||
final ChooseTabListItem[] availableTabs = getAvailableTabs(requireContext());
|
||||
|
||||
if (availableTabs.length == 0) {
|
||||
//Toast.makeText(requireContext(), "No available tabs", Toast.LENGTH_SHORT).show();
|
||||
return;
|
||||
}
|
||||
|
||||
Dialog.OnClickListener actionListener = (dialog, which) -> {
|
||||
final ChooseTabListItem selected = availableTabs[which];
|
||||
addTab(selected.tabId);
|
||||
};
|
||||
|
||||
new AddTabDialog(requireContext(), availableTabs, actionListener)
|
||||
.show();
|
||||
});
|
||||
}
|
||||
|
||||
private void addTab(final Tab tab) {
|
||||
tabList.add(tab);
|
||||
selectedTabsAdapter.notifyDataSetChanged();
|
||||
}
|
||||
|
||||
private void addTab(int tabId) {
|
||||
final Tab.Type type = typeFrom(tabId);
|
||||
|
||||
if (type == null) {
|
||||
ErrorActivity.reportError(requireContext(), new IllegalStateException("Tab id not found: " + tabId), null, null,
|
||||
ErrorActivity.ErrorInfo.make(UserAction.SOMETHING_ELSE, "none", "Choosing tabs on settings", 0));
|
||||
return;
|
||||
}
|
||||
|
||||
switch (type) {
|
||||
case KIOSK: {
|
||||
SelectKioskFragment selectFragment = new SelectKioskFragment();
|
||||
selectFragment.setOnSelectedLisener((serviceId, kioskId, kioskName) ->
|
||||
addTab(new Tab.KioskTab(serviceId, kioskId)));
|
||||
selectFragment.show(requireFragmentManager(), "select_kiosk");
|
||||
return;
|
||||
}
|
||||
case CHANNEL: {
|
||||
SelectChannelFragment selectFragment = new SelectChannelFragment();
|
||||
selectFragment.setOnSelectedLisener((serviceId, url, name) ->
|
||||
addTab(new Tab.ChannelTab(serviceId, url, name)));
|
||||
selectFragment.show(requireFragmentManager(), "select_channel");
|
||||
return;
|
||||
}
|
||||
default:
|
||||
addTab(type.getTab());
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
public ChooseTabListItem[] getAvailableTabs(Context context) {
|
||||
final ArrayList<ChooseTabListItem> returnList = new ArrayList<>();
|
||||
|
||||
for (Tab.Type type : Tab.Type.values()) {
|
||||
final Tab tab = type.getTab();
|
||||
switch (type) {
|
||||
case BLANK:
|
||||
if (!tabList.contains(tab)) {
|
||||
returnList.add(new ChooseTabListItem(tab.getTabId(), getString(R.string.blank_page_summary),
|
||||
tab.getTabIconRes(context)));
|
||||
}
|
||||
break;
|
||||
case KIOSK:
|
||||
returnList.add(new ChooseTabListItem(tab.getTabId(), getString(R.string.kiosk_page_summary),
|
||||
ThemeHelper.resolveResourceIdFromAttr(context, R.attr.ic_hot)));
|
||||
break;
|
||||
case CHANNEL:
|
||||
returnList.add(new ChooseTabListItem(tab.getTabId(), getString(R.string.channel_page_summary),
|
||||
tab.getTabIconRes(context)));
|
||||
break;
|
||||
default:
|
||||
if (!tabList.contains(tab)) {
|
||||
returnList.add(new ChooseTabListItem(context, tab));
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return returnList.toArray(new ChooseTabListItem[0]);
|
||||
}
|
||||
|
||||
/*//////////////////////////////////////////////////////////////////////////
|
||||
// List Handling
|
||||
//////////////////////////////////////////////////////////////////////////*/
|
||||
|
||||
private class SelectedTabsAdapter extends RecyclerView.Adapter<ChooseTabsFragment.SelectedTabsAdapter.TabViewHolder> {
|
||||
private ItemTouchHelper itemTouchHelper;
|
||||
private final LayoutInflater inflater;
|
||||
|
||||
SelectedTabsAdapter(Context context, ItemTouchHelper itemTouchHelper) {
|
||||
this.itemTouchHelper = itemTouchHelper;
|
||||
this.inflater = LayoutInflater.from(context);
|
||||
}
|
||||
|
||||
public void swapItems(int fromPosition, int toPosition) {
|
||||
Collections.swap(tabList, fromPosition, toPosition);
|
||||
notifyItemMoved(fromPosition, toPosition);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public ChooseTabsFragment.SelectedTabsAdapter.TabViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
|
||||
View view = inflater.inflate(R.layout.list_choose_tabs, parent, false);
|
||||
return new ChooseTabsFragment.SelectedTabsAdapter.TabViewHolder(view);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBindViewHolder(@NonNull ChooseTabsFragment.SelectedTabsAdapter.TabViewHolder holder, int position) {
|
||||
holder.bind(position, holder);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getItemCount() {
|
||||
return tabList.size();
|
||||
}
|
||||
|
||||
class TabViewHolder extends RecyclerView.ViewHolder {
|
||||
private AppCompatImageView tabIconView;
|
||||
private TextView tabNameView;
|
||||
private ImageView handle;
|
||||
|
||||
TabViewHolder(View itemView) {
|
||||
super(itemView);
|
||||
|
||||
tabNameView = itemView.findViewById(R.id.tabName);
|
||||
tabIconView = itemView.findViewById(R.id.tabIcon);
|
||||
handle = itemView.findViewById(R.id.handle);
|
||||
}
|
||||
|
||||
@SuppressLint("ClickableViewAccessibility")
|
||||
void bind(int position, TabViewHolder holder) {
|
||||
handle.setOnTouchListener(getOnTouchListener(holder));
|
||||
|
||||
final Tab tab = tabList.get(position);
|
||||
final Tab.Type type = Tab.typeFrom(tab.getTabId());
|
||||
|
||||
if (type == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
String tabName = tab.getTabName(requireContext());
|
||||
switch (type) {
|
||||
case BLANK:
|
||||
tabName = requireContext().getString(R.string.blank_page_summary);
|
||||
break;
|
||||
case KIOSK:
|
||||
tabName = NewPipe.getNameOfService(((Tab.KioskTab) tab).getKioskServiceId()) + "/" + tabName;
|
||||
break;
|
||||
case CHANNEL:
|
||||
tabName = NewPipe.getNameOfService(((Tab.ChannelTab) tab).getChannelServiceId()) + "/" + tabName;
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
tabNameView.setText(tabName);
|
||||
tabIconView.setImageResource(tab.getTabIconRes(requireContext()));
|
||||
}
|
||||
|
||||
@SuppressLint("ClickableViewAccessibility")
|
||||
private View.OnTouchListener getOnTouchListener(final RecyclerView.ViewHolder item) {
|
||||
return (view, motionEvent) -> {
|
||||
if (motionEvent.getActionMasked() == MotionEvent.ACTION_DOWN) {
|
||||
if (itemTouchHelper != null && getItemCount() > 1) {
|
||||
itemTouchHelper.startDrag(item);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private ItemTouchHelper.SimpleCallback getItemTouchCallback() {
|
||||
return new ItemTouchHelper.SimpleCallback(ItemTouchHelper.UP | ItemTouchHelper.DOWN,
|
||||
ItemTouchHelper.START | ItemTouchHelper.END) {
|
||||
@Override
|
||||
public int interpolateOutOfBoundsScroll(RecyclerView recyclerView, int viewSize,
|
||||
int viewSizeOutOfBounds, int totalSize,
|
||||
long msSinceStartScroll) {
|
||||
final int standardSpeed = super.interpolateOutOfBoundsScroll(recyclerView, viewSize,
|
||||
viewSizeOutOfBounds, totalSize, msSinceStartScroll);
|
||||
final int minimumAbsVelocity = Math.max(12,
|
||||
Math.abs(standardSpeed));
|
||||
return minimumAbsVelocity * (int) Math.signum(viewSizeOutOfBounds);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onMove(RecyclerView recyclerView, RecyclerView.ViewHolder source,
|
||||
RecyclerView.ViewHolder target) {
|
||||
if (source.getItemViewType() != target.getItemViewType() ||
|
||||
selectedTabsAdapter == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
final int sourceIndex = source.getAdapterPosition();
|
||||
final int targetIndex = target.getAdapterPosition();
|
||||
selectedTabsAdapter.swapItems(sourceIndex, targetIndex);
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isLongPressDragEnabled() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isItemViewSwipeEnabled() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSwiped(RecyclerView.ViewHolder viewHolder, int swipeDir) {
|
||||
int position = viewHolder.getAdapterPosition();
|
||||
tabList.remove(position);
|
||||
selectedTabsAdapter.notifyItemRemoved(position);
|
||||
|
||||
if (tabList.isEmpty()) {
|
||||
tabList.add(Tab.Type.BLANK.getTab());
|
||||
selectedTabsAdapter.notifyItemInserted(0);
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
416
app/src/main/java/org/schabi/newpipe/settings/tabs/Tab.java
Normal file
416
app/src/main/java/org/schabi/newpipe/settings/tabs/Tab.java
Normal file
@@ -0,0 +1,416 @@
|
||||
package org.schabi.newpipe.settings.tabs;
|
||||
|
||||
import android.content.Context;
|
||||
import android.support.annotation.DrawableRes;
|
||||
import android.support.annotation.NonNull;
|
||||
import android.support.annotation.Nullable;
|
||||
import android.support.v4.app.Fragment;
|
||||
|
||||
import com.grack.nanojson.JsonObject;
|
||||
import com.grack.nanojson.JsonSink;
|
||||
|
||||
import org.schabi.newpipe.R;
|
||||
import org.schabi.newpipe.extractor.exceptions.ExtractionException;
|
||||
import org.schabi.newpipe.fragments.BlankFragment;
|
||||
import org.schabi.newpipe.fragments.list.channel.ChannelFragment;
|
||||
import org.schabi.newpipe.fragments.list.kiosk.KioskFragment;
|
||||
import org.schabi.newpipe.local.bookmark.BookmarkFragment;
|
||||
import org.schabi.newpipe.local.feed.FeedFragment;
|
||||
import org.schabi.newpipe.local.history.StatisticsPlaylistFragment;
|
||||
import org.schabi.newpipe.local.subscription.SubscriptionFragment;
|
||||
import org.schabi.newpipe.util.KioskTranslator;
|
||||
import org.schabi.newpipe.util.ThemeHelper;
|
||||
|
||||
public abstract class Tab {
|
||||
Tab() {
|
||||
}
|
||||
|
||||
Tab(@NonNull JsonObject jsonObject) {
|
||||
readDataFromJson(jsonObject);
|
||||
}
|
||||
|
||||
public abstract int getTabId();
|
||||
public abstract String getTabName(Context context);
|
||||
@DrawableRes public abstract int getTabIconRes(Context context);
|
||||
|
||||
/**
|
||||
* Return a instance of the fragment that this tab represent.
|
||||
*/
|
||||
public abstract Fragment getFragment() throws ExtractionException;
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
return obj instanceof Tab && obj.getClass().equals(this.getClass())
|
||||
&& ((Tab) obj).getTabId() == this.getTabId();
|
||||
}
|
||||
|
||||
/*//////////////////////////////////////////////////////////////////////////
|
||||
// JSON Handling
|
||||
//////////////////////////////////////////////////////////////////////////*/
|
||||
|
||||
private static final String JSON_TAB_ID_KEY = "tab_id";
|
||||
|
||||
public void writeJsonOn(JsonSink jsonSink) {
|
||||
jsonSink.object();
|
||||
|
||||
jsonSink.value(JSON_TAB_ID_KEY, getTabId());
|
||||
writeDataToJson(jsonSink);
|
||||
|
||||
jsonSink.end();
|
||||
}
|
||||
|
||||
protected void writeDataToJson(JsonSink writerSink) {
|
||||
// No-op
|
||||
}
|
||||
|
||||
protected void readDataFromJson(JsonObject jsonObject) {
|
||||
// No-op
|
||||
}
|
||||
|
||||
/*//////////////////////////////////////////////////////////////////////////
|
||||
// Tab Handling
|
||||
//////////////////////////////////////////////////////////////////////////*/
|
||||
|
||||
@Nullable
|
||||
public static Tab from(@NonNull JsonObject jsonObject) {
|
||||
final int tabId = jsonObject.getInt(Tab.JSON_TAB_ID_KEY, -1);
|
||||
|
||||
if (tabId == -1) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return from(tabId, jsonObject);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public static Tab from(final int tabId) {
|
||||
return from(tabId, null);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public static Type typeFrom(int tabId) {
|
||||
for (Type available : Type.values()) {
|
||||
if (available.getTabId() == tabId) {
|
||||
return available;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private static Tab from(final int tabId, @Nullable JsonObject jsonObject) {
|
||||
final Type type = typeFrom(tabId);
|
||||
|
||||
if (type == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (jsonObject != null) {
|
||||
switch (type) {
|
||||
case KIOSK:
|
||||
return new KioskTab(jsonObject);
|
||||
case CHANNEL:
|
||||
return new ChannelTab(jsonObject);
|
||||
}
|
||||
}
|
||||
|
||||
return type.getTab();
|
||||
}
|
||||
|
||||
/*//////////////////////////////////////////////////////////////////////////
|
||||
// Implementations
|
||||
//////////////////////////////////////////////////////////////////////////*/
|
||||
|
||||
public enum Type {
|
||||
BLANK(new BlankTab()),
|
||||
SUBSCRIPTIONS(new SubscriptionsTab()),
|
||||
FEED(new FeedTab()),
|
||||
BOOKMARKS(new BookmarksTab()),
|
||||
HISTORY(new HistoryTab()),
|
||||
KIOSK(new KioskTab()),
|
||||
CHANNEL(new ChannelTab());
|
||||
|
||||
private Tab tab;
|
||||
|
||||
Type(Tab tab) {
|
||||
this.tab = tab;
|
||||
}
|
||||
|
||||
public int getTabId() {
|
||||
return tab.getTabId();
|
||||
}
|
||||
|
||||
public Tab getTab() {
|
||||
return tab;
|
||||
}
|
||||
}
|
||||
|
||||
public static class BlankTab extends Tab {
|
||||
public static final int ID = 0;
|
||||
|
||||
@Override
|
||||
public int getTabId() {
|
||||
return ID;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getTabName(Context context) {
|
||||
return "NewPipe"; //context.getString(R.string.blank_page_summary);
|
||||
}
|
||||
|
||||
@DrawableRes
|
||||
@Override
|
||||
public int getTabIconRes(Context context) {
|
||||
return ThemeHelper.resolveResourceIdFromAttr(context, R.attr.ic_blank_page);
|
||||
}
|
||||
|
||||
@Override
|
||||
public BlankFragment getFragment() {
|
||||
return new BlankFragment();
|
||||
}
|
||||
}
|
||||
|
||||
public static class SubscriptionsTab extends Tab {
|
||||
public static final int ID = 1;
|
||||
|
||||
@Override
|
||||
public int getTabId() {
|
||||
return ID;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getTabName(Context context) {
|
||||
return context.getString(R.string.tab_subscriptions);
|
||||
}
|
||||
|
||||
@DrawableRes
|
||||
@Override
|
||||
public int getTabIconRes(Context context) {
|
||||
return ThemeHelper.resolveResourceIdFromAttr(context, R.attr.ic_channel);
|
||||
}
|
||||
|
||||
@Override
|
||||
public SubscriptionFragment getFragment() {
|
||||
return new SubscriptionFragment();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public static class FeedTab extends Tab {
|
||||
public static final int ID = 2;
|
||||
|
||||
@Override
|
||||
public int getTabId() {
|
||||
return ID;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getTabName(Context context) {
|
||||
return context.getString(R.string.fragment_whats_new);
|
||||
}
|
||||
|
||||
@DrawableRes
|
||||
@Override
|
||||
public int getTabIconRes(Context context) {
|
||||
return ThemeHelper.resolveResourceIdFromAttr(context, R.attr.rss);
|
||||
}
|
||||
|
||||
@Override
|
||||
public FeedFragment getFragment() {
|
||||
return new FeedFragment();
|
||||
}
|
||||
}
|
||||
|
||||
public static class BookmarksTab extends Tab {
|
||||
public static final int ID = 3;
|
||||
|
||||
@Override
|
||||
public int getTabId() {
|
||||
return ID;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getTabName(Context context) {
|
||||
return context.getString(R.string.tab_bookmarks);
|
||||
}
|
||||
|
||||
@DrawableRes
|
||||
@Override
|
||||
public int getTabIconRes(Context context) {
|
||||
return ThemeHelper.resolveResourceIdFromAttr(context, R.attr.ic_bookmark);
|
||||
}
|
||||
|
||||
@Override
|
||||
public BookmarkFragment getFragment() {
|
||||
return new BookmarkFragment();
|
||||
}
|
||||
}
|
||||
|
||||
public static class HistoryTab extends Tab {
|
||||
public static final int ID = 4;
|
||||
|
||||
@Override
|
||||
public int getTabId() {
|
||||
return ID;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getTabName(Context context) {
|
||||
return context.getString(R.string.title_activity_history);
|
||||
}
|
||||
|
||||
@DrawableRes
|
||||
@Override
|
||||
public int getTabIconRes(Context context) {
|
||||
return ThemeHelper.resolveResourceIdFromAttr(context, R.attr.history);
|
||||
}
|
||||
|
||||
@Override
|
||||
public StatisticsPlaylistFragment getFragment() {
|
||||
return new StatisticsPlaylistFragment();
|
||||
}
|
||||
}
|
||||
|
||||
public static class KioskTab extends Tab {
|
||||
public static final int ID = 5;
|
||||
|
||||
private int kioskServiceId;
|
||||
private String kioskId;
|
||||
|
||||
private static final String JSON_KIOSK_SERVICE_ID_KEY = "service_id";
|
||||
private static final String JSON_KIOSK_ID_KEY = "kiosk_id";
|
||||
|
||||
private KioskTab() {
|
||||
this(-1, "<no-id>");
|
||||
}
|
||||
|
||||
public KioskTab(int kioskServiceId, String kioskId) {
|
||||
this.kioskServiceId = kioskServiceId;
|
||||
this.kioskId = kioskId;
|
||||
}
|
||||
|
||||
public KioskTab(JsonObject jsonObject) {
|
||||
super(jsonObject);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getTabId() {
|
||||
return ID;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getTabName(Context context) {
|
||||
return KioskTranslator.getTranslatedKioskName(kioskId, context);
|
||||
}
|
||||
|
||||
@DrawableRes
|
||||
@Override
|
||||
public int getTabIconRes(Context context) {
|
||||
final int kioskIcon = KioskTranslator.getKioskIcons(kioskId, context);
|
||||
|
||||
if (kioskIcon <= 0) {
|
||||
throw new IllegalStateException("Kiosk ID is not valid: \"" + kioskId + "\"");
|
||||
}
|
||||
|
||||
return kioskIcon;
|
||||
}
|
||||
|
||||
@Override
|
||||
public KioskFragment getFragment() throws ExtractionException {
|
||||
return KioskFragment.getInstance(kioskServiceId, kioskId);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void writeDataToJson(JsonSink writerSink) {
|
||||
writerSink.value(JSON_KIOSK_SERVICE_ID_KEY, kioskServiceId)
|
||||
.value(JSON_KIOSK_ID_KEY, kioskId);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void readDataFromJson(JsonObject jsonObject) {
|
||||
kioskServiceId = jsonObject.getInt(JSON_KIOSK_SERVICE_ID_KEY, -1);
|
||||
kioskId = jsonObject.getString(JSON_KIOSK_ID_KEY, "<no-id>");
|
||||
}
|
||||
|
||||
public int getKioskServiceId() {
|
||||
return kioskServiceId;
|
||||
}
|
||||
|
||||
public String getKioskId() {
|
||||
return kioskId;
|
||||
}
|
||||
}
|
||||
|
||||
public static class ChannelTab extends Tab {
|
||||
public static final int ID = 6;
|
||||
|
||||
private int channelServiceId;
|
||||
private String channelUrl;
|
||||
private String channelName;
|
||||
|
||||
private static final String JSON_CHANNEL_SERVICE_ID_KEY = "channel_service_id";
|
||||
private static final String JSON_CHANNEL_URL_KEY = "channel_url";
|
||||
private static final String JSON_CHANNEL_NAME_KEY = "channel_name";
|
||||
|
||||
private ChannelTab() {
|
||||
this(-1, "<no-url>", "<no-name>");
|
||||
}
|
||||
|
||||
public ChannelTab(int channelServiceId, String channelUrl, String channelName) {
|
||||
this.channelServiceId = channelServiceId;
|
||||
this.channelUrl = channelUrl;
|
||||
this.channelName = channelName;
|
||||
}
|
||||
|
||||
public ChannelTab(JsonObject jsonObject) {
|
||||
super(jsonObject);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getTabId() {
|
||||
return ID;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getTabName(Context context) {
|
||||
return channelName;
|
||||
}
|
||||
|
||||
@DrawableRes
|
||||
@Override
|
||||
public int getTabIconRes(Context context) {
|
||||
return ThemeHelper.resolveResourceIdFromAttr(context, R.attr.ic_channel);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ChannelFragment getFragment() {
|
||||
return ChannelFragment.getInstance(channelServiceId, channelUrl, channelName);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void writeDataToJson(JsonSink writerSink) {
|
||||
writerSink.value(JSON_CHANNEL_SERVICE_ID_KEY, channelServiceId)
|
||||
.value(JSON_CHANNEL_URL_KEY, channelUrl)
|
||||
.value(JSON_CHANNEL_NAME_KEY, channelName);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void readDataFromJson(JsonObject jsonObject) {
|
||||
channelServiceId = jsonObject.getInt(JSON_CHANNEL_SERVICE_ID_KEY, -1);
|
||||
channelUrl = jsonObject.getString(JSON_CHANNEL_URL_KEY, "<no-url>");
|
||||
channelName = jsonObject.getString(JSON_CHANNEL_NAME_KEY, "<no-name>");
|
||||
}
|
||||
|
||||
public int getChannelServiceId() {
|
||||
return channelServiceId;
|
||||
}
|
||||
|
||||
public String getChannelUrl() {
|
||||
return channelUrl;
|
||||
}
|
||||
|
||||
public String getChannelName() {
|
||||
return channelName;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,114 @@
|
||||
package org.schabi.newpipe.settings.tabs;
|
||||
|
||||
import android.support.annotation.Nullable;
|
||||
|
||||
import com.grack.nanojson.JsonArray;
|
||||
import com.grack.nanojson.JsonObject;
|
||||
import com.grack.nanojson.JsonParser;
|
||||
import com.grack.nanojson.JsonParserException;
|
||||
import com.grack.nanojson.JsonStringWriter;
|
||||
import com.grack.nanojson.JsonWriter;
|
||||
|
||||
import org.schabi.newpipe.settings.tabs.Tab.Type;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import static org.schabi.newpipe.extractor.ServiceList.YouTube;
|
||||
|
||||
/**
|
||||
* Class to get a JSON representation of a list of tabs, and the other way around.
|
||||
*/
|
||||
public class TabsJsonHelper {
|
||||
private static final String JSON_TABS_ARRAY_KEY = "tabs";
|
||||
|
||||
protected static final List<Tab> FALLBACK_INITIAL_TABS_LIST = Collections.unmodifiableList(Arrays.asList(
|
||||
new Tab.KioskTab(YouTube.getServiceId(), "Trending"),
|
||||
Type.SUBSCRIPTIONS.getTab(),
|
||||
Type.BOOKMARKS.getTab()
|
||||
));
|
||||
|
||||
public static class InvalidJsonException extends Exception {
|
||||
private InvalidJsonException() {
|
||||
super();
|
||||
}
|
||||
|
||||
private InvalidJsonException(String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
private InvalidJsonException(Throwable cause) {
|
||||
super(cause);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Try to reads the passed JSON and returns the list of tabs if no error were encountered.
|
||||
* <p>
|
||||
* If the JSON is null or empty, or the list of tabs that it represents is empty, the
|
||||
* {@link #FALLBACK_INITIAL_TABS_LIST fallback list} will be returned.
|
||||
* <p>
|
||||
* Tabs with invalid ids (i.e. not in the {@link Tab.Type} enum) will be ignored.
|
||||
*
|
||||
* @param tabsJson a JSON string got from {@link #getJsonToSave(List)}.
|
||||
* @return a list of {@link Tab tabs}.
|
||||
* @throws InvalidJsonException if the JSON string is not valid
|
||||
*/
|
||||
public static List<Tab> getTabsFromJson(@Nullable String tabsJson) throws InvalidJsonException {
|
||||
if (tabsJson == null || tabsJson.isEmpty()) {
|
||||
return FALLBACK_INITIAL_TABS_LIST;
|
||||
}
|
||||
|
||||
final List<Tab> returnTabs = new ArrayList<>();
|
||||
|
||||
final JsonObject outerJsonObject;
|
||||
try {
|
||||
outerJsonObject = JsonParser.object().from(tabsJson);
|
||||
final JsonArray tabsArray = outerJsonObject.getArray(JSON_TABS_ARRAY_KEY);
|
||||
|
||||
if (tabsArray == null) {
|
||||
throw new InvalidJsonException("JSON doesn't contain \"" + JSON_TABS_ARRAY_KEY + "\" array");
|
||||
}
|
||||
|
||||
for (Object o : tabsArray) {
|
||||
if (!(o instanceof JsonObject)) continue;
|
||||
|
||||
final Tab tab = Tab.from((JsonObject) o);
|
||||
|
||||
if (tab != null) {
|
||||
returnTabs.add(tab);
|
||||
}
|
||||
}
|
||||
} catch (JsonParserException e) {
|
||||
throw new InvalidJsonException(e);
|
||||
}
|
||||
|
||||
if (returnTabs.isEmpty()) {
|
||||
return FALLBACK_INITIAL_TABS_LIST;
|
||||
}
|
||||
|
||||
return returnTabs;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a JSON representation from a list of tabs.
|
||||
*
|
||||
* @param tabList a list of {@link Tab tabs}.
|
||||
* @return a JSON string representing the list of tabs
|
||||
*/
|
||||
public static String getJsonToSave(@Nullable List<Tab> tabList) {
|
||||
final JsonStringWriter jsonWriter = JsonWriter.string();
|
||||
jsonWriter.object();
|
||||
|
||||
jsonWriter.array(JSON_TABS_ARRAY_KEY);
|
||||
if (tabList != null) for (Tab tab : tabList) {
|
||||
tab.writeJsonOn(jsonWriter);
|
||||
}
|
||||
jsonWriter.end();
|
||||
|
||||
jsonWriter.end();
|
||||
return jsonWriter.done();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,93 @@
|
||||
package org.schabi.newpipe.settings.tabs;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.SharedPreferences;
|
||||
import android.preference.PreferenceManager;
|
||||
import android.widget.Toast;
|
||||
|
||||
import org.schabi.newpipe.R;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class TabsManager {
|
||||
private final SharedPreferences sharedPreferences;
|
||||
private final String savedTabsKey;
|
||||
private final Context context;
|
||||
|
||||
public static TabsManager getManager(Context context) {
|
||||
return new TabsManager(context);
|
||||
}
|
||||
|
||||
private TabsManager(Context context) {
|
||||
this.context = context;
|
||||
this.sharedPreferences = PreferenceManager.getDefaultSharedPreferences(context);
|
||||
this.savedTabsKey = context.getString(R.string.saved_tabs_key);
|
||||
}
|
||||
|
||||
public List<Tab> getTabs() {
|
||||
final String savedJson = sharedPreferences.getString(savedTabsKey, null);
|
||||
try {
|
||||
return TabsJsonHelper.getTabsFromJson(savedJson);
|
||||
} catch (TabsJsonHelper.InvalidJsonException e) {
|
||||
Toast.makeText(context, R.string.saved_tabs_invalid_json, Toast.LENGTH_SHORT).show();
|
||||
return getDefaultTabs();
|
||||
}
|
||||
}
|
||||
|
||||
public void saveTabs(List<Tab> tabList) {
|
||||
final String jsonToSave = TabsJsonHelper.getJsonToSave(tabList);
|
||||
sharedPreferences.edit().putString(savedTabsKey, jsonToSave).apply();
|
||||
}
|
||||
|
||||
public void resetTabs() {
|
||||
sharedPreferences.edit().remove(savedTabsKey).apply();
|
||||
}
|
||||
|
||||
public List<Tab> getDefaultTabs() {
|
||||
return TabsJsonHelper.FALLBACK_INITIAL_TABS_LIST;
|
||||
}
|
||||
|
||||
/*//////////////////////////////////////////////////////////////////////////
|
||||
// Listener
|
||||
//////////////////////////////////////////////////////////////////////////*/
|
||||
|
||||
public interface SavedTabsChangeListener {
|
||||
void onTabsChanged();
|
||||
}
|
||||
|
||||
private SavedTabsChangeListener savedTabsChangeListener;
|
||||
private SharedPreferences.OnSharedPreferenceChangeListener preferenceChangeListener;
|
||||
|
||||
public void setSavedTabsListener(SavedTabsChangeListener listener) {
|
||||
if (preferenceChangeListener != null) {
|
||||
sharedPreferences.unregisterOnSharedPreferenceChangeListener(preferenceChangeListener);
|
||||
}
|
||||
savedTabsChangeListener = listener;
|
||||
preferenceChangeListener = getPreferenceChangeListener();
|
||||
sharedPreferences.registerOnSharedPreferenceChangeListener(preferenceChangeListener);
|
||||
}
|
||||
|
||||
public void unsetSavedTabsListener() {
|
||||
if (preferenceChangeListener != null) {
|
||||
sharedPreferences.unregisterOnSharedPreferenceChangeListener(preferenceChangeListener);
|
||||
}
|
||||
preferenceChangeListener = null;
|
||||
savedTabsChangeListener = null;
|
||||
}
|
||||
|
||||
private SharedPreferences.OnSharedPreferenceChangeListener getPreferenceChangeListener() {
|
||||
return (sharedPreferences, key) -> {
|
||||
if (key.equals(savedTabsKey)) {
|
||||
if (savedTabsChangeListener != null) savedTabsChangeListener.onTabsChanged();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user