Merge remote-tracking branch 'newpipe/dev' into rebase
This commit is contained in:
commit
2cac8a739d
265 changed files with 12286 additions and 8976 deletions
|
|
@ -1,11 +1,11 @@
|
|||
package org.schabi.newpipe.fragments;
|
||||
|
||||
/**
|
||||
* Indicates that the current fragment can handle back presses
|
||||
* Indicates that the current fragment can handle back presses.
|
||||
*/
|
||||
public interface BackPressable {
|
||||
/**
|
||||
* A back press was delegated to this fragment
|
||||
* A back press was delegated to this fragment.
|
||||
*
|
||||
* @return if the back press was handled
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -1,9 +1,8 @@
|
|||
package org.schabi.newpipe.fragments;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.os.Bundle;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.annotation.StringRes;
|
||||
import android.util.Log;
|
||||
import android.view.View;
|
||||
import android.widget.Button;
|
||||
|
|
@ -11,6 +10,9 @@ import android.widget.ProgressBar;
|
|||
import android.widget.TextView;
|
||||
import android.widget.Toast;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.annotation.StringRes;
|
||||
|
||||
import com.jakewharton.rxbinding2.view.RxView;
|
||||
|
||||
import org.schabi.newpipe.BaseFragment;
|
||||
|
|
@ -36,22 +38,21 @@ import io.reactivex.android.schedulers.AndroidSchedulers;
|
|||
import static org.schabi.newpipe.util.AnimationUtils.animateView;
|
||||
|
||||
public abstract class BaseStateFragment<I> extends BaseFragment implements ViewContract<I> {
|
||||
|
||||
@State
|
||||
protected AtomicBoolean wasLoading = new AtomicBoolean();
|
||||
protected AtomicBoolean isLoading = new AtomicBoolean();
|
||||
|
||||
@Nullable
|
||||
protected View emptyStateView;
|
||||
private View emptyStateView;
|
||||
@Nullable
|
||||
protected ProgressBar loadingProgressBar;
|
||||
private ProgressBar loadingProgressBar;
|
||||
|
||||
protected View errorPanelRoot;
|
||||
protected Button errorButtonRetry;
|
||||
protected TextView errorTextView;
|
||||
private Button errorButtonRetry;
|
||||
private TextView errorTextView;
|
||||
|
||||
@Override
|
||||
public void onViewCreated(View rootView, Bundle savedInstanceState) {
|
||||
public void onViewCreated(final View rootView, final Bundle savedInstanceState) {
|
||||
super.onViewCreated(rootView, savedInstanceState);
|
||||
doInitialLoadLogic();
|
||||
}
|
||||
|
|
@ -62,14 +63,12 @@ public abstract class BaseStateFragment<I> extends BaseFragment implements ViewC
|
|||
wasLoading.set(isLoading.get());
|
||||
}
|
||||
|
||||
|
||||
/*//////////////////////////////////////////////////////////////////////////
|
||||
// Init
|
||||
//////////////////////////////////////////////////////////////////////////*/
|
||||
|
||||
|
||||
@Override
|
||||
protected void initViews(View rootView, Bundle savedInstanceState) {
|
||||
protected void initViews(final View rootView, final Bundle savedInstanceState) {
|
||||
super.initViews(rootView, savedInstanceState);
|
||||
|
||||
emptyStateView = rootView.findViewById(R.id.empty_state_view);
|
||||
|
|
@ -105,8 +104,10 @@ public abstract class BaseStateFragment<I> extends BaseFragment implements ViewC
|
|||
startLoading(true);
|
||||
}
|
||||
|
||||
protected void startLoading(boolean forceLoad) {
|
||||
if (DEBUG) Log.d(TAG, "startLoading() called with: forceLoad = [" + forceLoad + "]");
|
||||
protected void startLoading(final boolean forceLoad) {
|
||||
if (DEBUG) {
|
||||
Log.d(TAG, "startLoading() called with: forceLoad = [" + forceLoad + "]");
|
||||
}
|
||||
showLoading();
|
||||
isLoading.set(true);
|
||||
}
|
||||
|
|
@ -117,42 +118,62 @@ public abstract class BaseStateFragment<I> extends BaseFragment implements ViewC
|
|||
|
||||
@Override
|
||||
public void showLoading() {
|
||||
if (emptyStateView != null) animateView(emptyStateView, false, 150);
|
||||
if (loadingProgressBar != null) animateView(loadingProgressBar, true, 400);
|
||||
if (emptyStateView != null) {
|
||||
animateView(emptyStateView, false, 150);
|
||||
}
|
||||
if (loadingProgressBar != null) {
|
||||
animateView(loadingProgressBar, true, 400);
|
||||
}
|
||||
animateView(errorPanelRoot, false, 150);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void hideLoading() {
|
||||
if (emptyStateView != null) animateView(emptyStateView, false, 150);
|
||||
if (loadingProgressBar != null) animateView(loadingProgressBar, false, 0);
|
||||
if (emptyStateView != null) {
|
||||
animateView(emptyStateView, false, 150);
|
||||
}
|
||||
if (loadingProgressBar != null) {
|
||||
animateView(loadingProgressBar, false, 0);
|
||||
}
|
||||
animateView(errorPanelRoot, false, 150);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void showEmptyState() {
|
||||
isLoading.set(false);
|
||||
if (emptyStateView != null) animateView(emptyStateView, true, 200);
|
||||
if (loadingProgressBar != null) animateView(loadingProgressBar, false, 0);
|
||||
if (emptyStateView != null) {
|
||||
animateView(emptyStateView, true, 200);
|
||||
}
|
||||
if (loadingProgressBar != null) {
|
||||
animateView(loadingProgressBar, false, 0);
|
||||
}
|
||||
animateView(errorPanelRoot, false, 150);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void showError(String message, boolean showRetryButton) {
|
||||
if (DEBUG) Log.d(TAG, "showError() called with: message = [" + message + "], showRetryButton = [" + showRetryButton + "]");
|
||||
public void showError(final String message, final boolean showRetryButton) {
|
||||
if (DEBUG) {
|
||||
Log.d(TAG, "showError() called with: "
|
||||
+ "message = [" + message + "], showRetryButton = [" + showRetryButton + "]");
|
||||
}
|
||||
isLoading.set(false);
|
||||
InfoCache.getInstance().clearCache();
|
||||
hideLoading();
|
||||
|
||||
errorTextView.setText(message);
|
||||
if (showRetryButton) animateView(errorButtonRetry, true, 600);
|
||||
else animateView(errorButtonRetry, false, 0);
|
||||
if (showRetryButton) {
|
||||
animateView(errorButtonRetry, true, 600);
|
||||
} else {
|
||||
animateView(errorButtonRetry, false, 0);
|
||||
}
|
||||
animateView(errorPanelRoot, true, 300);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleResult(I result) {
|
||||
if (DEBUG) Log.d(TAG, "handleResult() called with: result = [" + result + "]");
|
||||
public void handleResult(final I result) {
|
||||
if (DEBUG) {
|
||||
Log.d(TAG, "handleResult() called with: result = [" + result + "]");
|
||||
}
|
||||
hideLoading();
|
||||
}
|
||||
|
||||
|
|
@ -161,21 +182,28 @@ public abstract class BaseStateFragment<I> extends BaseFragment implements ViewC
|
|||
//////////////////////////////////////////////////////////////////////////*/
|
||||
|
||||
/**
|
||||
* Default implementation handles some general exceptions
|
||||
* Default implementation handles some general exceptions.
|
||||
*
|
||||
* @return if the exception was handled
|
||||
* @param exception The exception that should be handled
|
||||
* @return If the exception was handled
|
||||
*/
|
||||
protected boolean onError(Throwable exception) {
|
||||
if (DEBUG) Log.d(TAG, "onError() called with: exception = [" + exception + "]");
|
||||
protected boolean onError(final Throwable exception) {
|
||||
if (DEBUG) {
|
||||
Log.d(TAG, "onError() called with: exception = [" + exception + "]");
|
||||
}
|
||||
isLoading.set(false);
|
||||
|
||||
if (isDetached() || isRemoving()) {
|
||||
if (DEBUG) Log.w(TAG, "onError() is detached or removing = [" + exception + "]");
|
||||
if (DEBUG) {
|
||||
Log.w(TAG, "onError() is detached or removing = [" + exception + "]");
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
if (ExtractorHelper.isInterruptedCaused(exception)) {
|
||||
if (DEBUG) Log.w(TAG, "onError() isInterruptedCaused! = [" + exception + "]");
|
||||
if (DEBUG) {
|
||||
Log.w(TAG, "onError() isInterruptedCaused! = [" + exception + "]");
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
@ -193,8 +221,10 @@ public abstract class BaseStateFragment<I> extends BaseFragment implements ViewC
|
|||
return false;
|
||||
}
|
||||
|
||||
public void onReCaptchaException(ReCaptchaException exception) {
|
||||
if (DEBUG) Log.d(TAG, "onReCaptchaException() called");
|
||||
public void onReCaptchaException(final ReCaptchaException exception) {
|
||||
if (DEBUG) {
|
||||
Log.d(TAG, "onReCaptchaException() called");
|
||||
}
|
||||
Toast.makeText(activity, R.string.recaptcha_request_toast, Toast.LENGTH_LONG).show();
|
||||
// Starting ReCaptcha Challenge Activity
|
||||
Intent intent = new Intent(activity, ReCaptchaActivity.class);
|
||||
|
|
@ -204,33 +234,58 @@ public abstract class BaseStateFragment<I> extends BaseFragment implements ViewC
|
|||
showError(getString(R.string.recaptcha_request_toast), false);
|
||||
}
|
||||
|
||||
public void onUnrecoverableError(Throwable exception, UserAction userAction, String serviceName, String request, @StringRes int errorId) {
|
||||
onUnrecoverableError(Collections.singletonList(exception), userAction, serviceName, request, errorId);
|
||||
public void onUnrecoverableError(final Throwable exception, final UserAction userAction,
|
||||
final String serviceName, final String request,
|
||||
@StringRes final int errorId) {
|
||||
onUnrecoverableError(Collections.singletonList(exception), userAction, serviceName,
|
||||
request, errorId);
|
||||
}
|
||||
|
||||
public void onUnrecoverableError(List<Throwable> exception, UserAction userAction, String serviceName, String request, @StringRes int errorId) {
|
||||
if (DEBUG) Log.d(TAG, "onUnrecoverableError() called with: exception = [" + exception + "]");
|
||||
public void onUnrecoverableError(final List<Throwable> exception, final UserAction userAction,
|
||||
final String serviceName, final String request,
|
||||
@StringRes final int errorId) {
|
||||
if (DEBUG) {
|
||||
Log.d(TAG, "onUnrecoverableError() called with: exception = [" + exception + "]");
|
||||
}
|
||||
|
||||
if (serviceName == null) serviceName = "none";
|
||||
if (request == null) request = "none";
|
||||
|
||||
ErrorActivity.reportError(getContext(), exception, MainActivity.class, null, ErrorActivity.ErrorInfo.make(userAction, serviceName, request, errorId));
|
||||
ErrorActivity.reportError(getContext(), exception, MainActivity.class, null,
|
||||
ErrorActivity.ErrorInfo.make(userAction, serviceName == null ? "none" : serviceName,
|
||||
request == null ? "none" : request, errorId));
|
||||
}
|
||||
|
||||
public void showSnackBarError(Throwable exception, UserAction userAction, String serviceName, String request, @StringRes int errorId) {
|
||||
showSnackBarError(Collections.singletonList(exception), userAction, serviceName, request, errorId);
|
||||
public void showSnackBarError(final Throwable exception, final UserAction userAction,
|
||||
final String serviceName, final String request,
|
||||
@StringRes final int errorId) {
|
||||
showSnackBarError(Collections.singletonList(exception), userAction, serviceName, request,
|
||||
errorId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Show a SnackBar and only call ErrorActivity#reportError IF we a find a valid view (otherwise the error screen appears)
|
||||
* Show a SnackBar and only call
|
||||
* {@link ErrorActivity#reportError(Context, List, Class, View, ErrorActivity.ErrorInfo)}
|
||||
* IF we a find a valid view (otherwise the error screen appears).
|
||||
*
|
||||
* @param exception List of the exceptions to show
|
||||
* @param userAction The user action that caused the exception
|
||||
* @param serviceName The service where the exception happened
|
||||
* @param request The page that was requested
|
||||
* @param errorId The ID of the error
|
||||
*/
|
||||
public void showSnackBarError(List<Throwable> exception, UserAction userAction, String serviceName, String request, @StringRes int errorId) {
|
||||
public void showSnackBarError(final List<Throwable> exception, final UserAction userAction,
|
||||
final String serviceName, final String request,
|
||||
@StringRes final int errorId) {
|
||||
if (DEBUG) {
|
||||
Log.d(TAG, "showSnackBarError() called with: exception = [" + exception + "], userAction = [" + userAction + "], request = [" + request + "], errorId = [" + errorId + "]");
|
||||
Log.d(TAG, "showSnackBarError() called with: "
|
||||
+ "exception = [" + exception + "], userAction = [" + userAction + "], "
|
||||
+ "request = [" + request + "], errorId = [" + errorId + "]");
|
||||
}
|
||||
View rootView = activity != null ? activity.findViewById(android.R.id.content) : null;
|
||||
if (rootView == null && getView() != null) rootView = getView();
|
||||
if (rootView == null) return;
|
||||
if (rootView == null && getView() != null) {
|
||||
rootView = getView();
|
||||
}
|
||||
if (rootView == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
ErrorActivity.reportError(getContext(), exception, MainActivity.class, rootView,
|
||||
ErrorActivity.ErrorInfo.make(userAction, serviceName, request, errorId));
|
||||
|
|
|
|||
|
|
@ -1,24 +1,26 @@
|
|||
package org.schabi.newpipe.fragments;
|
||||
|
||||
import android.os.Bundle;
|
||||
import androidx.annotation.Nullable;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import org.schabi.newpipe.BaseFragment;
|
||||
import org.schabi.newpipe.R;
|
||||
|
||||
public class BlankFragment extends BaseFragment {
|
||||
@Nullable
|
||||
@Override
|
||||
public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, Bundle savedInstanceState) {
|
||||
public View onCreateView(final LayoutInflater inflater, @Nullable final ViewGroup container,
|
||||
final Bundle savedInstanceState) {
|
||||
setTitle("NewPipe");
|
||||
return inflater.inflate(R.layout.fragment_blank, container, false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setUserVisibleHint(boolean isVisibleToUser) {
|
||||
public void setUserVisibleHint(final boolean isVisibleToUser) {
|
||||
super.setUserVisibleHint(isVisibleToUser);
|
||||
setTitle("NewPipe");
|
||||
// leave this inline. Will make it harder for copy cats.
|
||||
|
|
|
|||
|
|
@ -1,17 +1,19 @@
|
|||
package org.schabi.newpipe.fragments;
|
||||
|
||||
import android.os.Bundle;
|
||||
import androidx.annotation.Nullable;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import org.schabi.newpipe.BaseFragment;
|
||||
import org.schabi.newpipe.R;
|
||||
|
||||
public class EmptyFragment extends BaseFragment {
|
||||
@Override
|
||||
public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, Bundle savedInstanceState) {
|
||||
public View onCreateView(final LayoutInflater inflater, @Nullable final ViewGroup container,
|
||||
final Bundle savedInstanceState) {
|
||||
return inflater.inflate(R.layout.fragment_empty, container, false);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -50,14 +50,15 @@ public class MainFragment extends BaseFragment implements TabLayout.OnTabSelecte
|
|||
//////////////////////////////////////////////////////////////////////////*/
|
||||
|
||||
@Override
|
||||
public void onCreate(Bundle savedInstanceState) {
|
||||
public void onCreate(final Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
setHasOptionsMenu(true);
|
||||
|
||||
tabsManager = TabsManager.getManager(activity);
|
||||
tabsManager.setSavedTabsListener(() -> {
|
||||
if (DEBUG) {
|
||||
Log.d(TAG, "TabsManager.SavedTabsChangeListener: onTabsChanged called, isResumed = " + isResumed());
|
||||
Log.d(TAG, "TabsManager.SavedTabsChangeListener: "
|
||||
+ "onTabsChanged called, isResumed = " + isResumed());
|
||||
}
|
||||
if (isResumed()) {
|
||||
setupTabs();
|
||||
|
|
@ -68,12 +69,14 @@ public class MainFragment extends BaseFragment implements TabLayout.OnTabSelecte
|
|||
}
|
||||
|
||||
@Override
|
||||
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
|
||||
public View onCreateView(@NonNull final LayoutInflater inflater,
|
||||
@Nullable final ViewGroup container,
|
||||
@Nullable final Bundle savedInstanceState) {
|
||||
return inflater.inflate(R.layout.fragment_main, container, false);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void initViews(View rootView, Bundle savedInstanceState) {
|
||||
protected void initViews(final View rootView, final Bundle savedInstanceState) {
|
||||
super.initViews(rootView, savedInstanceState);
|
||||
|
||||
tabLayout = rootView.findViewById(R.id.main_tab_layout);
|
||||
|
|
@ -89,14 +92,18 @@ public class MainFragment extends BaseFragment implements TabLayout.OnTabSelecte
|
|||
public void onResume() {
|
||||
super.onResume();
|
||||
|
||||
if (hasTabsChanged) setupTabs();
|
||||
if (hasTabsChanged) {
|
||||
setupTabs();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDestroy() {
|
||||
super.onDestroy();
|
||||
tabsManager.unsetSavedTabsListener();
|
||||
if (viewPager != null) viewPager.setAdapter(null);
|
||||
if (viewPager != null) {
|
||||
viewPager.setAdapter(null);
|
||||
}
|
||||
}
|
||||
|
||||
/*//////////////////////////////////////////////////////////////////////////
|
||||
|
|
@ -104,9 +111,12 @@ public class MainFragment extends BaseFragment implements TabLayout.OnTabSelecte
|
|||
//////////////////////////////////////////////////////////////////////////*/
|
||||
|
||||
@Override
|
||||
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
|
||||
public void onCreateOptionsMenu(final Menu menu, final MenuInflater inflater) {
|
||||
super.onCreateOptionsMenu(menu, inflater);
|
||||
if (DEBUG) Log.d(TAG, "onCreateOptionsMenu() called with: menu = [" + menu + "], inflater = [" + inflater + "]");
|
||||
if (DEBUG) {
|
||||
Log.d(TAG, "onCreateOptionsMenu() called with: "
|
||||
+ "menu = [" + menu + "], inflater = [" + inflater + "]");
|
||||
}
|
||||
inflater.inflate(R.menu.main_fragment_menu, menu);
|
||||
|
||||
ActionBar supportActionBar = activity.getSupportActionBar();
|
||||
|
|
@ -116,7 +126,7 @@ public class MainFragment extends BaseFragment implements TabLayout.OnTabSelecte
|
|||
}
|
||||
|
||||
@Override
|
||||
public boolean onOptionsItemSelected(MenuItem item) {
|
||||
public boolean onOptionsItemSelected(final MenuItem item) {
|
||||
switch (item.getItemId()) {
|
||||
case R.id.action_search:
|
||||
try {
|
||||
|
|
@ -141,7 +151,8 @@ public class MainFragment extends BaseFragment implements TabLayout.OnTabSelecte
|
|||
tabsList.addAll(tabsManager.getTabs());
|
||||
|
||||
if (pagerAdapter == null || !pagerAdapter.sameTabs(tabsList)) {
|
||||
pagerAdapter = new SelectedTabsPagerAdapter(requireContext(), getChildFragmentManager(), tabsList);
|
||||
pagerAdapter = new SelectedTabsPagerAdapter(requireContext(),
|
||||
getChildFragmentManager(), tabsList);
|
||||
}
|
||||
|
||||
viewPager.setAdapter(null);
|
||||
|
|
@ -165,31 +176,37 @@ public class MainFragment extends BaseFragment implements TabLayout.OnTabSelecte
|
|||
}
|
||||
}
|
||||
|
||||
private void updateTitleForTab(int tabPosition) {
|
||||
private void updateTitleForTab(final int tabPosition) {
|
||||
setTitle(tabsList.get(tabPosition).getTabName(requireContext()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTabSelected(TabLayout.Tab selectedTab) {
|
||||
if (DEBUG) Log.d(TAG, "onTabSelected() called with: selectedTab = [" + selectedTab + "]");
|
||||
public void onTabSelected(final TabLayout.Tab selectedTab) {
|
||||
if (DEBUG) {
|
||||
Log.d(TAG, "onTabSelected() called with: selectedTab = [" + selectedTab + "]");
|
||||
}
|
||||
updateTitleForTab(selectedTab.getPosition());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTabUnselected(TabLayout.Tab tab) {
|
||||
}
|
||||
public void onTabUnselected(final TabLayout.Tab tab) { }
|
||||
|
||||
@Override
|
||||
public void onTabReselected(TabLayout.Tab tab) {
|
||||
if (DEBUG) Log.d(TAG, "onTabReselected() called with: tab = [" + tab + "]");
|
||||
public void onTabReselected(final TabLayout.Tab tab) {
|
||||
if (DEBUG) {
|
||||
Log.d(TAG, "onTabReselected() called with: tab = [" + tab + "]");
|
||||
}
|
||||
updateTitleForTab(tab.getPosition());
|
||||
}
|
||||
|
||||
private static class SelectedTabsPagerAdapter extends FragmentStatePagerAdapterMenuWorkaround {
|
||||
private static final class SelectedTabsPagerAdapter
|
||||
extends FragmentStatePagerAdapterMenuWorkaround {
|
||||
private final Context context;
|
||||
private final List<Tab> internalTabsList;
|
||||
|
||||
private SelectedTabsPagerAdapter(Context context, FragmentManager fragmentManager, List<Tab> tabsList) {
|
||||
private SelectedTabsPagerAdapter(final Context context,
|
||||
final FragmentManager fragmentManager,
|
||||
final List<Tab> tabsList) {
|
||||
super(fragmentManager, BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT);
|
||||
this.context = context;
|
||||
this.internalTabsList = new ArrayList<>(tabsList);
|
||||
|
|
@ -197,7 +214,7 @@ public class MainFragment extends BaseFragment implements TabLayout.OnTabSelecte
|
|||
|
||||
@NonNull
|
||||
@Override
|
||||
public Fragment getItem(int position) {
|
||||
public Fragment getItem(final int position) {
|
||||
final Tab tab = internalTabsList.get(position);
|
||||
|
||||
Throwable throwable = null;
|
||||
|
|
@ -209,8 +226,8 @@ public class MainFragment extends BaseFragment implements TabLayout.OnTabSelecte
|
|||
}
|
||||
|
||||
if (throwable != null) {
|
||||
ErrorActivity.reportError(context, throwable, null, null,
|
||||
ErrorActivity.ErrorInfo.make(UserAction.UI_ERROR, "none", "", R.string.app_ui_crash));
|
||||
ErrorActivity.reportError(context, throwable, null, null, ErrorActivity.ErrorInfo
|
||||
.make(UserAction.UI_ERROR, "none", "", R.string.app_ui_crash));
|
||||
return new BlankFragment();
|
||||
}
|
||||
|
||||
|
|
@ -222,7 +239,7 @@ public class MainFragment extends BaseFragment implements TabLayout.OnTabSelecte
|
|||
}
|
||||
|
||||
@Override
|
||||
public int getItemPosition(Object object) {
|
||||
public int getItemPosition(final Object object) {
|
||||
// Causes adapter to reload all Fragments when
|
||||
// notifyDataSetChanged is called
|
||||
return POSITION_NONE;
|
||||
|
|
@ -233,7 +250,7 @@ public class MainFragment extends BaseFragment implements TabLayout.OnTabSelecte
|
|||
return internalTabsList.size();
|
||||
}
|
||||
|
||||
public boolean sameTabs(List<Tab> tabsToCompare) {
|
||||
public boolean sameTabs(final List<Tab> tabsToCompare) {
|
||||
return internalTabsList.equals(tabsToCompare);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -9,12 +9,13 @@ import androidx.recyclerview.widget.StaggeredGridLayoutManager;
|
|||
* if the view is scrolled below the last item.
|
||||
*/
|
||||
public abstract class OnScrollBelowItemsListener extends RecyclerView.OnScrollListener {
|
||||
|
||||
@Override
|
||||
public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
|
||||
public void onScrolled(final RecyclerView recyclerView, final int dx, final int dy) {
|
||||
super.onScrolled(recyclerView, dx, dy);
|
||||
if (dy > 0) {
|
||||
int pastVisibleItems = 0, visibleItemCount, totalItemCount;
|
||||
int pastVisibleItems = 0;
|
||||
int visibleItemCount;
|
||||
int totalItemCount;
|
||||
RecyclerView.LayoutManager layoutManager = recyclerView.getLayoutManager();
|
||||
|
||||
visibleItemCount = layoutManager.getChildCount();
|
||||
|
|
@ -22,10 +23,14 @@ public abstract class OnScrollBelowItemsListener extends RecyclerView.OnScrollLi
|
|||
|
||||
// Already covers the GridLayoutManager case
|
||||
if (layoutManager instanceof LinearLayoutManager) {
|
||||
pastVisibleItems = ((LinearLayoutManager) layoutManager).findFirstVisibleItemPosition();
|
||||
pastVisibleItems = ((LinearLayoutManager) layoutManager)
|
||||
.findFirstVisibleItemPosition();
|
||||
} else if (layoutManager instanceof StaggeredGridLayoutManager) {
|
||||
int[] positions = ((StaggeredGridLayoutManager) layoutManager).findFirstVisibleItemPositions(null);
|
||||
if (positions != null && positions.length > 0) pastVisibleItems = positions[0];
|
||||
int[] positions = ((StaggeredGridLayoutManager) layoutManager)
|
||||
.findFirstVisibleItemPositions(null);
|
||||
if (positions != null && positions.length > 0) {
|
||||
pastVisibleItems = positions[0];
|
||||
}
|
||||
}
|
||||
|
||||
if ((visibleItemCount + pastVisibleItems) >= totalItemCount) {
|
||||
|
|
|
|||
|
|
@ -2,8 +2,11 @@ package org.schabi.newpipe.fragments;
|
|||
|
||||
public interface ViewContract<I> {
|
||||
void showLoading();
|
||||
|
||||
void hideLoading();
|
||||
|
||||
void showEmptyState();
|
||||
|
||||
void showError(String message, boolean showRetryButton);
|
||||
|
||||
void handleResult(I result);
|
||||
|
|
|
|||
|
|
@ -4,19 +4,15 @@ import java.io.Serializable;
|
|||
|
||||
class StackItem implements Serializable {
|
||||
private final int serviceId;
|
||||
private String title;
|
||||
private final String url;
|
||||
private String title;
|
||||
|
||||
StackItem(int serviceId, String url, String title) {
|
||||
StackItem(final int serviceId, final String url, final String title) {
|
||||
this.serviceId = serviceId;
|
||||
this.url = url;
|
||||
this.title = title;
|
||||
}
|
||||
|
||||
public void setTitle(String title) {
|
||||
this.title = title;
|
||||
}
|
||||
|
||||
public int getServiceId() {
|
||||
return serviceId;
|
||||
}
|
||||
|
|
@ -25,6 +21,10 @@ class StackItem implements Serializable {
|
|||
return title;
|
||||
}
|
||||
|
||||
public void setTitle(final String title) {
|
||||
this.title = title;
|
||||
}
|
||||
|
||||
public String getUrl() {
|
||||
return url;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,27 +1,27 @@
|
|||
package org.schabi.newpipe.fragments.detail;
|
||||
|
||||
import android.view.ViewGroup;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.fragment.app.Fragment;
|
||||
import androidx.fragment.app.FragmentManager;
|
||||
import androidx.fragment.app.FragmentPagerAdapter;
|
||||
import android.view.ViewGroup;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class TabAdaptor extends FragmentPagerAdapter {
|
||||
|
||||
private final List<Fragment> mFragmentList = new ArrayList<>();
|
||||
private final List<String> mFragmentTitleList = new ArrayList<>();
|
||||
private final FragmentManager fragmentManager;
|
||||
|
||||
public TabAdaptor(FragmentManager fm) {
|
||||
public TabAdaptor(final FragmentManager fm) {
|
||||
super(fm);
|
||||
this.fragmentManager = fm;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Fragment getItem(int position) {
|
||||
public Fragment getItem(final int position) {
|
||||
return mFragmentList.get(position);
|
||||
}
|
||||
|
||||
|
|
@ -30,7 +30,7 @@ public class TabAdaptor extends FragmentPagerAdapter {
|
|||
return mFragmentList.size();
|
||||
}
|
||||
|
||||
public void addFragment(Fragment fragment, String title) {
|
||||
public void addFragment(final Fragment fragment, final String title) {
|
||||
mFragmentList.add(fragment);
|
||||
mFragmentTitleList.add(title);
|
||||
}
|
||||
|
|
@ -40,46 +40,49 @@ public class TabAdaptor extends FragmentPagerAdapter {
|
|||
mFragmentTitleList.clear();
|
||||
}
|
||||
|
||||
public void removeItem(int position){
|
||||
public void removeItem(final int position) {
|
||||
mFragmentList.remove(position == 0 ? 0 : position - 1);
|
||||
mFragmentTitleList.remove(position == 0 ? 0 : position - 1);
|
||||
}
|
||||
|
||||
public void updateItem(int position, Fragment fragment){
|
||||
public void updateItem(final int position, final Fragment fragment) {
|
||||
mFragmentList.set(position, fragment);
|
||||
}
|
||||
|
||||
public void updateItem(String title, Fragment fragment){
|
||||
public void updateItem(final String title, final Fragment fragment) {
|
||||
int index = mFragmentTitleList.indexOf(title);
|
||||
if(index != -1){
|
||||
if (index != -1) {
|
||||
updateItem(index, fragment);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getItemPosition(Object object) {
|
||||
if (mFragmentList.contains(object)) return mFragmentList.indexOf(object);
|
||||
else return POSITION_NONE;
|
||||
public int getItemPosition(final Object object) {
|
||||
if (mFragmentList.contains(object)) {
|
||||
return mFragmentList.indexOf(object);
|
||||
} else {
|
||||
return POSITION_NONE;
|
||||
}
|
||||
}
|
||||
|
||||
public int getItemPositionByTitle(String title) {
|
||||
public int getItemPositionByTitle(final String title) {
|
||||
return mFragmentTitleList.indexOf(title);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public String getItemTitle(int position) {
|
||||
public String getItemTitle(final int position) {
|
||||
if (position < 0 || position >= mFragmentTitleList.size()) {
|
||||
return null;
|
||||
}
|
||||
return mFragmentTitleList.get(position);
|
||||
}
|
||||
|
||||
public void notifyDataSetUpdate(){
|
||||
public void notifyDataSetUpdate() {
|
||||
notifyDataSetChanged();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void destroyItem(ViewGroup container, int position, Object object) {
|
||||
public void destroyItem(final ViewGroup container, final int position, final Object object) {
|
||||
fragmentManager.beginTransaction().remove((Fragment) object).commitNowAllowingStateLoss();
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -8,16 +8,6 @@ import android.net.Uri;
|
|||
import android.os.Build;
|
||||
import android.os.Bundle;
|
||||
import android.preference.PreferenceManager;
|
||||
import androidx.annotation.DrawableRes;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import com.google.android.material.appbar.AppBarLayout;
|
||||
import com.google.android.material.tabs.TabLayout;
|
||||
import androidx.fragment.app.Fragment;
|
||||
import androidx.core.content.ContextCompat;
|
||||
import androidx.viewpager.widget.ViewPager;
|
||||
import androidx.appcompat.app.ActionBar;
|
||||
import androidx.appcompat.app.AppCompatActivity;
|
||||
import android.text.Html;
|
||||
import android.text.Spanned;
|
||||
import android.text.TextUtils;
|
||||
|
|
@ -41,6 +31,17 @@ import android.widget.Spinner;
|
|||
import android.widget.TextView;
|
||||
import android.widget.Toast;
|
||||
|
||||
import androidx.annotation.DrawableRes;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.appcompat.app.ActionBar;
|
||||
import androidx.appcompat.app.AppCompatActivity;
|
||||
import androidx.core.content.ContextCompat;
|
||||
import androidx.fragment.app.Fragment;
|
||||
import androidx.viewpager.widget.ViewPager;
|
||||
|
||||
import com.google.android.material.appbar.AppBarLayout;
|
||||
import com.google.android.material.tabs.TabLayout;
|
||||
import com.nostra13.universalimageloader.core.assist.FailReason;
|
||||
import com.nostra13.universalimageloader.core.listener.ImageLoadingListener;
|
||||
import com.nostra13.universalimageloader.core.listener.SimpleImageLoadingListener;
|
||||
|
|
@ -52,7 +53,6 @@ import org.schabi.newpipe.extractor.InfoItem;
|
|||
import org.schabi.newpipe.extractor.NewPipe;
|
||||
import org.schabi.newpipe.extractor.ServiceList;
|
||||
import org.schabi.newpipe.extractor.exceptions.ExtractionException;
|
||||
import org.schabi.newpipe.extractor.exceptions.ParsingException;
|
||||
import org.schabi.newpipe.extractor.services.youtube.extractors.YoutubeStreamExtractor;
|
||||
import org.schabi.newpipe.extractor.stream.AudioStream;
|
||||
import org.schabi.newpipe.extractor.stream.Description;
|
||||
|
|
@ -106,12 +106,9 @@ import io.reactivex.schedulers.Schedulers;
|
|||
import static org.schabi.newpipe.extractor.StreamingService.ServiceInfo.MediaCapability.COMMENTS;
|
||||
import static org.schabi.newpipe.util.AnimationUtils.animateView;
|
||||
|
||||
public class VideoDetailFragment
|
||||
extends BaseStateFragment<StreamInfo>
|
||||
implements BackPressable,
|
||||
SharedPreferences.OnSharedPreferenceChangeListener,
|
||||
View.OnClickListener,
|
||||
View.OnLongClickListener {
|
||||
public class VideoDetailFragment extends BaseStateFragment<StreamInfo>
|
||||
implements BackPressable, SharedPreferences.OnSharedPreferenceChangeListener,
|
||||
View.OnClickListener, View.OnLongClickListener {
|
||||
public static final String AUTO_PLAY = "auto_play";
|
||||
|
||||
private int updateFlags = 0;
|
||||
|
|
@ -184,32 +181,41 @@ public class VideoDetailFragment
|
|||
private ImageView thumbsDownImageView;
|
||||
private TextView thumbsDisabledTextView;
|
||||
|
||||
private static final String COMMENTS_TAB_TAG = "COMMENTS";
|
||||
private static final String RELATED_TAB_TAG = "NEXT VIDEO";
|
||||
private static final String EMPTY_TAB_TAG = "EMPTY TAB";
|
||||
|
||||
private AppBarLayout appBarLayout;
|
||||
private ViewPager viewPager;
|
||||
private ViewPager viewPager;
|
||||
private TabAdaptor pageAdapter;
|
||||
private TabLayout tabLayout;
|
||||
private FrameLayout relatedStreamsLayout;
|
||||
|
||||
|
||||
/*////////////////////////////////////////////////////////////////////////*/
|
||||
|
||||
public static VideoDetailFragment getInstance(int serviceId, String videoUrl, String name) {
|
||||
private static final String COMMENTS_TAB_TAG = "COMMENTS";
|
||||
private static final String RELATED_TAB_TAG = "NEXT VIDEO";
|
||||
private static final String EMPTY_TAB_TAG = "EMPTY TAB";
|
||||
|
||||
private static final String INFO_KEY = "info_key";
|
||||
private static final String STACK_KEY = "stack_key";
|
||||
|
||||
/**
|
||||
* Stack that contains the "navigation history".<br>
|
||||
* The peek is the current video.
|
||||
*/
|
||||
private final LinkedList<StackItem> stack = new LinkedList<>();
|
||||
|
||||
public static VideoDetailFragment getInstance(final int serviceId, final String videoUrl,
|
||||
final String name) {
|
||||
VideoDetailFragment instance = new VideoDetailFragment();
|
||||
instance.setInitialData(serviceId, videoUrl, name);
|
||||
return instance;
|
||||
}
|
||||
|
||||
|
||||
/*//////////////////////////////////////////////////////////////////////////
|
||||
// Fragment's Lifecycle
|
||||
//////////////////////////////////////////////////////////////////////////*/
|
||||
|
||||
@Override
|
||||
public void
|
||||
onCreate(Bundle savedInstanceState) {
|
||||
public void onCreate(final Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
setHasOptionsMenu(true);
|
||||
|
||||
|
|
@ -227,17 +233,21 @@ public class VideoDetailFragment
|
|||
}
|
||||
|
||||
@Override
|
||||
public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
|
||||
public View onCreateView(@NonNull final LayoutInflater inflater, final ViewGroup container,
|
||||
final Bundle savedInstanceState) {
|
||||
return inflater.inflate(R.layout.fragment_video_detail, container, false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPause() {
|
||||
super.onPause();
|
||||
if (currentWorker != null) currentWorker.dispose();
|
||||
if (currentWorker != null) {
|
||||
currentWorker.dispose();
|
||||
}
|
||||
PreferenceManager.getDefaultSharedPreferences(getContext())
|
||||
.edit()
|
||||
.putString(getString(R.string.stream_info_selected_tab_key), pageAdapter.getItemTitle(viewPager.getCurrentItem()))
|
||||
.putString(getString(R.string.stream_info_selected_tab_key),
|
||||
pageAdapter.getItemTitle(viewPager.getCurrentItem()))
|
||||
.apply();
|
||||
}
|
||||
|
||||
|
|
@ -247,9 +257,15 @@ public class VideoDetailFragment
|
|||
|
||||
if (updateFlags != 0) {
|
||||
if (!isLoading.get() && currentInfo != null) {
|
||||
if ((updateFlags & RELATED_STREAMS_UPDATE_FLAG) != 0) startLoading(false);
|
||||
if ((updateFlags & RESOLUTIONS_MENU_UPDATE_FLAG) != 0) setupActionBar(currentInfo);
|
||||
if ((updateFlags & COMMENTS_UPDATE_FLAG) != 0) startLoading(false);
|
||||
if ((updateFlags & RELATED_STREAMS_UPDATE_FLAG) != 0) {
|
||||
startLoading(false);
|
||||
}
|
||||
if ((updateFlags & RESOLUTIONS_MENU_UPDATE_FLAG) != 0) {
|
||||
setupActionBar(currentInfo);
|
||||
}
|
||||
if ((updateFlags & COMMENTS_UPDATE_FLAG) != 0) {
|
||||
startLoading(false);
|
||||
}
|
||||
}
|
||||
|
||||
if ((updateFlags & TOOLBAR_ITEMS_UPDATE_FLAG) != 0
|
||||
|
|
@ -274,9 +290,15 @@ public class VideoDetailFragment
|
|||
PreferenceManager.getDefaultSharedPreferences(activity)
|
||||
.unregisterOnSharedPreferenceChangeListener(this);
|
||||
|
||||
if (positionSubscriber != null) positionSubscriber.dispose();
|
||||
if (currentWorker != null) currentWorker.dispose();
|
||||
if (disposables != null) disposables.clear();
|
||||
if (positionSubscriber != null) {
|
||||
positionSubscriber.dispose();
|
||||
}
|
||||
if (currentWorker != null) {
|
||||
currentWorker.dispose();
|
||||
}
|
||||
if (disposables != null) {
|
||||
disposables.clear();
|
||||
}
|
||||
positionSubscriber = null;
|
||||
currentWorker = null;
|
||||
disposables = null;
|
||||
|
|
@ -284,20 +306,25 @@ public class VideoDetailFragment
|
|||
|
||||
@Override
|
||||
public void onDestroyView() {
|
||||
if (DEBUG) Log.d(TAG, "onDestroyView() called");
|
||||
if (DEBUG) {
|
||||
Log.d(TAG, "onDestroyView() called");
|
||||
}
|
||||
spinnerToolbar.setOnItemSelectedListener(null);
|
||||
spinnerToolbar.setAdapter(null);
|
||||
super.onDestroyView();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onActivityResult(int requestCode, int resultCode, Intent data) {
|
||||
public void onActivityResult(final int requestCode, final int resultCode, final Intent data) {
|
||||
super.onActivityResult(requestCode, resultCode, data);
|
||||
switch (requestCode) {
|
||||
case ReCaptchaActivity.RECAPTCHA_REQUEST:
|
||||
if (resultCode == Activity.RESULT_OK) {
|
||||
NavigationHelper.openVideoDetailFragment(getFragmentManager(), serviceId, url, name);
|
||||
} else Log.e(TAG, "ReCaptcha failed");
|
||||
NavigationHelper
|
||||
.openVideoDetailFragment(getFragmentManager(), serviceId, url, name);
|
||||
} else {
|
||||
Log.e(TAG, "ReCaptcha failed");
|
||||
}
|
||||
break;
|
||||
default:
|
||||
Log.e(TAG, "Request code from activity not supported [" + requestCode + "]");
|
||||
|
|
@ -306,7 +333,8 @@ public class VideoDetailFragment
|
|||
}
|
||||
|
||||
@Override
|
||||
public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) {
|
||||
public void onSharedPreferenceChanged(final SharedPreferences sharedPreferences,
|
||||
final String key) {
|
||||
if (key.equals(getString(R.string.show_next_video_key))) {
|
||||
showRelatedStreams = sharedPreferences.getBoolean(key, true);
|
||||
updateFlags |= RELATED_STREAMS_UPDATE_FLAG;
|
||||
|
|
@ -327,11 +355,8 @@ public class VideoDetailFragment
|
|||
// State Saving
|
||||
//////////////////////////////////////////////////////////////////////////*/
|
||||
|
||||
private static final String INFO_KEY = "info_key";
|
||||
private static final String STACK_KEY = "stack_key";
|
||||
|
||||
@Override
|
||||
public void onSaveInstanceState(Bundle outState) {
|
||||
public void onSaveInstanceState(final Bundle outState) {
|
||||
super.onSaveInstanceState(outState);
|
||||
|
||||
// Check if the next video label and video is visible,
|
||||
|
|
@ -346,7 +371,7 @@ public class VideoDetailFragment
|
|||
}
|
||||
|
||||
@Override
|
||||
protected void onRestoreInstanceState(@NonNull Bundle savedState) {
|
||||
protected void onRestoreInstanceState(@NonNull final Bundle savedState) {
|
||||
super.onRestoreInstanceState(savedState);
|
||||
|
||||
Serializable serializable = savedState.getSerializable(INFO_KEY);
|
||||
|
|
@ -361,7 +386,6 @@ public class VideoDetailFragment
|
|||
//noinspection unchecked
|
||||
stack.addAll((Collection<? extends StackItem>) serializable);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/*//////////////////////////////////////////////////////////////////////////
|
||||
|
|
@ -369,8 +393,10 @@ public class VideoDetailFragment
|
|||
//////////////////////////////////////////////////////////////////////////*/
|
||||
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
if (isLoading.get() || currentInfo == null) return;
|
||||
public void onClick(final View v) {
|
||||
if (isLoading.get() || currentInfo == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
switch (v.getId()) {
|
||||
case R.id.detail_controls_background:
|
||||
|
|
@ -396,14 +422,14 @@ public class VideoDetailFragment
|
|||
Log.w(TAG, "Can't open channel because we got no channel URL");
|
||||
} else {
|
||||
try {
|
||||
NavigationHelper.openChannelFragment(
|
||||
getFragmentManager(),
|
||||
currentInfo.getServiceId(),
|
||||
currentInfo.getUploaderUrl(),
|
||||
currentInfo.getUploaderName());
|
||||
NavigationHelper.openChannelFragment(
|
||||
getFragmentManager(),
|
||||
currentInfo.getServiceId(),
|
||||
currentInfo.getUploaderUrl(),
|
||||
currentInfo.getUploaderName());
|
||||
} catch (Exception e) {
|
||||
ErrorActivity.reportUiError((AppCompatActivity) getActivity(), e);
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
case R.id.detail_thumbnail_root_layout:
|
||||
|
|
@ -421,8 +447,10 @@ public class VideoDetailFragment
|
|||
}
|
||||
|
||||
@Override
|
||||
public boolean onLongClick(View v) {
|
||||
if (isLoading.get() || currentInfo == null) return false;
|
||||
public boolean onLongClick(final View v) {
|
||||
if (isLoading.get() || currentInfo == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
switch (v.getId()) {
|
||||
case R.id.detail_controls_background:
|
||||
|
|
@ -459,7 +487,7 @@ public class VideoDetailFragment
|
|||
//////////////////////////////////////////////////////////////////////////*/
|
||||
|
||||
@Override
|
||||
protected void initViews(View rootView, Bundle savedInstanceState) {
|
||||
protected void initViews(final View rootView, final Bundle savedInstanceState) {
|
||||
super.initViews(rootView, savedInstanceState);
|
||||
spinnerToolbar = activity.findViewById(R.id.toolbar).findViewById(R.id.toolbar_spinner);
|
||||
|
||||
|
|
@ -547,41 +575,41 @@ public class VideoDetailFragment
|
|||
};
|
||||
}
|
||||
|
||||
private void initThumbnailViews(@NonNull StreamInfo info) {
|
||||
private void initThumbnailViews(@NonNull final StreamInfo info) {
|
||||
thumbnailImageView.setImageResource(R.drawable.dummy_thumbnail_dark);
|
||||
if (!TextUtils.isEmpty(info.getThumbnailUrl())) {
|
||||
final String infoServiceName = NewPipe.getNameOfService(info.getServiceId());
|
||||
final ImageLoadingListener onFailListener = new SimpleImageLoadingListener() {
|
||||
@Override
|
||||
public void onLoadingFailed(String imageUri, View view, FailReason failReason) {
|
||||
public void onLoadingFailed(final String imageUri, final View view,
|
||||
final FailReason failReason) {
|
||||
showSnackBarError(failReason.getCause(), UserAction.LOAD_IMAGE,
|
||||
infoServiceName, imageUri, R.string.could_not_load_thumbnails);
|
||||
}
|
||||
};
|
||||
|
||||
imageLoader.displayImage(info.getThumbnailUrl(), thumbnailImageView,
|
||||
IMAGE_LOADER.displayImage(info.getThumbnailUrl(), thumbnailImageView,
|
||||
ImageDisplayConstants.DISPLAY_THUMBNAIL_OPTIONS, onFailListener);
|
||||
}
|
||||
|
||||
if (!TextUtils.isEmpty(info.getUploaderAvatarUrl())) {
|
||||
imageLoader.displayImage(info.getUploaderAvatarUrl(), uploaderThumb,
|
||||
IMAGE_LOADER.displayImage(info.getUploaderAvatarUrl(), uploaderThumb,
|
||||
ImageDisplayConstants.DISPLAY_AVATAR_OPTIONS);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*//////////////////////////////////////////////////////////////////////////
|
||||
// Menu
|
||||
//////////////////////////////////////////////////////////////////////////*/
|
||||
|
||||
@Override
|
||||
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
|
||||
this.menu = menu;
|
||||
public void onCreateOptionsMenu(final Menu m, final MenuInflater inflater) {
|
||||
this.menu = m;
|
||||
|
||||
// CAUTION set item properties programmatically otherwise it would not be accepted by
|
||||
// appcompat itemsinflater.inflate(R.menu.videoitem_detail, menu);
|
||||
|
||||
inflater.inflate(R.menu.video_detail_menu, menu);
|
||||
inflater.inflate(R.menu.video_detail_menu, m);
|
||||
|
||||
updateMenuItemVisibility();
|
||||
|
||||
|
|
@ -593,7 +621,6 @@ public class VideoDetailFragment
|
|||
}
|
||||
|
||||
private void updateMenuItemVisibility() {
|
||||
|
||||
// show kodi if set in settings
|
||||
menu.findItem(R.id.action_play_with_kodi).setVisible(
|
||||
PreferenceManager.getDefaultSharedPreferences(activity).getBoolean(
|
||||
|
|
@ -601,7 +628,7 @@ public class VideoDetailFragment
|
|||
}
|
||||
|
||||
@Override
|
||||
public boolean onOptionsItemSelected(MenuItem item) {
|
||||
public boolean onOptionsItemSelected(final MenuItem item) {
|
||||
int id = item.getItemId();
|
||||
if (id == R.id.action_settings) {
|
||||
NavigationHelper.openSettings(requireContext());
|
||||
|
|
@ -614,24 +641,25 @@ public class VideoDetailFragment
|
|||
}
|
||||
|
||||
switch (id) {
|
||||
case R.id.menu_item_share: {
|
||||
case R.id.menu_item_share:
|
||||
if (currentInfo != null) {
|
||||
ShareUtils.shareUrl(requireContext(), currentInfo.getName(), currentInfo.getOriginalUrl());
|
||||
ShareUtils.shareUrl(requireContext(), currentInfo.getName(),
|
||||
currentInfo.getOriginalUrl());
|
||||
}
|
||||
return true;
|
||||
}
|
||||
case R.id.menu_item_openInBrowser: {
|
||||
case R.id.menu_item_openInBrowser:
|
||||
if (currentInfo != null) {
|
||||
ShareUtils.openUrlInBrowser(requireContext(), currentInfo.getOriginalUrl());
|
||||
}
|
||||
return true;
|
||||
}
|
||||
case R.id.action_play_with_kodi:
|
||||
try {
|
||||
NavigationHelper.playWithKore(activity, Uri.parse(
|
||||
url.replace("https", "http")));
|
||||
} catch (Exception e) {
|
||||
if (DEBUG) Log.i(TAG, "Failed to start kore", e);
|
||||
if (DEBUG) {
|
||||
Log.i(TAG, "Failed to start kore", e);
|
||||
}
|
||||
KoreUtil.showInstallKoreDialog(activity);
|
||||
}
|
||||
return true;
|
||||
|
|
@ -640,37 +668,39 @@ public class VideoDetailFragment
|
|||
}
|
||||
}
|
||||
|
||||
private void setupActionBarOnError(final String url) {
|
||||
if (DEBUG) Log.d(TAG, "setupActionBarHandlerOnError() called with: url = [" + url + "]");
|
||||
private void setupActionBarOnError(final String u) {
|
||||
if (DEBUG) {
|
||||
Log.d(TAG, "setupActionBarHandlerOnError() called with: url = [" + u + "]");
|
||||
}
|
||||
Log.e("-----", "missing code");
|
||||
}
|
||||
|
||||
private void setupActionBar(final StreamInfo info) {
|
||||
if (DEBUG) Log.d(TAG, "setupActionBarHandler() called with: info = [" + info + "]");
|
||||
if (DEBUG) {
|
||||
Log.d(TAG, "setupActionBarHandler() called with: info = [" + info + "]");
|
||||
}
|
||||
boolean isExternalPlayerEnabled = PreferenceManager.getDefaultSharedPreferences(activity)
|
||||
.getBoolean(activity.getString(R.string.use_external_video_player_key), false);
|
||||
|
||||
sortedVideoStreams = ListHelper.getSortedStreamVideosList(
|
||||
activity,
|
||||
info.getVideoStreams(),
|
||||
info.getVideoOnlyStreams(),
|
||||
false);
|
||||
selectedVideoStreamIndex = ListHelper.getDefaultResolutionIndex(activity, sortedVideoStreams);
|
||||
sortedVideoStreams = ListHelper.getSortedStreamVideosList(activity, info.getVideoStreams(),
|
||||
info.getVideoOnlyStreams(), false);
|
||||
selectedVideoStreamIndex = ListHelper
|
||||
.getDefaultResolutionIndex(activity, sortedVideoStreams);
|
||||
|
||||
final StreamItemAdapter<VideoStream, Stream> streamsAdapter =
|
||||
new StreamItemAdapter<>(activity,
|
||||
new StreamSizeWrapper<>(sortedVideoStreams, activity), isExternalPlayerEnabled);
|
||||
final StreamItemAdapter<VideoStream, Stream> streamsAdapter = new StreamItemAdapter<>(
|
||||
activity, new StreamSizeWrapper<>(sortedVideoStreams, activity),
|
||||
isExternalPlayerEnabled);
|
||||
spinnerToolbar.setAdapter(streamsAdapter);
|
||||
spinnerToolbar.setSelection(selectedVideoStreamIndex);
|
||||
spinnerToolbar.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
|
||||
@Override
|
||||
public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
|
||||
public void onItemSelected(final AdapterView<?> parent, final View view,
|
||||
final int position, final long id) {
|
||||
selectedVideoStreamIndex = position;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onNothingSelected(AdapterView<?> parent) {
|
||||
}
|
||||
public void onNothingSelected(final AdapterView<?> parent) { }
|
||||
});
|
||||
}
|
||||
|
||||
|
|
@ -678,37 +708,31 @@ public class VideoDetailFragment
|
|||
// OwnStack
|
||||
//////////////////////////////////////////////////////////////////////////*/
|
||||
|
||||
/**
|
||||
* Stack that contains the "navigation history".<br>
|
||||
* The peek is the current video.
|
||||
*/
|
||||
protected final LinkedList<StackItem> stack = new LinkedList<>();
|
||||
|
||||
public void pushToStack(int serviceId, String videoUrl, String name) {
|
||||
private void pushToStack(final int sid, final String videoUrl, final String title) {
|
||||
if (DEBUG) {
|
||||
Log.d(TAG, "pushToStack() called with: serviceId = ["
|
||||
+ serviceId + "], videoUrl = [" + videoUrl + "], name = [" + name + "]");
|
||||
+ sid + "], videoUrl = [" + videoUrl + "], title = [" + title + "]");
|
||||
}
|
||||
|
||||
if (stack.size() > 0
|
||||
&& stack.peek().getServiceId() == serviceId
|
||||
&& stack.peek().getServiceId() == sid
|
||||
&& stack.peek().getUrl().equals(videoUrl)) {
|
||||
Log.d(TAG, "pushToStack() called with: serviceId == peek.serviceId = ["
|
||||
+ serviceId + "], videoUrl == peek.getUrl = [" + videoUrl + "]");
|
||||
+ sid + "], videoUrl == peek.getUrl = [" + videoUrl + "]");
|
||||
return;
|
||||
} else {
|
||||
Log.d(TAG, "pushToStack() wasn't equal");
|
||||
}
|
||||
|
||||
stack.push(new StackItem(serviceId, videoUrl, name));
|
||||
stack.push(new StackItem(sid, videoUrl, title));
|
||||
}
|
||||
|
||||
public void setTitleToUrl(int serviceId, String videoUrl, String name) {
|
||||
if (name != null && !name.isEmpty()) {
|
||||
private void setTitleToUrl(final int sid, final String videoUrl, final String title) {
|
||||
if (title != null && !title.isEmpty()) {
|
||||
for (StackItem stackItem : stack) {
|
||||
if (stack.peek().getServiceId() == serviceId
|
||||
if (stack.peek().getServiceId() == sid
|
||||
&& stackItem.getUrl().equals(videoUrl)) {
|
||||
stackItem.setTitle(name);
|
||||
stackItem.setTitle(title);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -716,20 +740,21 @@ public class VideoDetailFragment
|
|||
|
||||
@Override
|
||||
public boolean onBackPressed() {
|
||||
if (DEBUG) Log.d(TAG, "onBackPressed() called");
|
||||
if (DEBUG) {
|
||||
Log.d(TAG, "onBackPressed() called");
|
||||
}
|
||||
// That means that we are on the start of the stack,
|
||||
// return false to let the MainActivity handle the onBack
|
||||
if (stack.size() <= 1) return false;
|
||||
if (stack.size() <= 1) {
|
||||
return false;
|
||||
}
|
||||
// Remove top
|
||||
stack.pop();
|
||||
// Get stack item from the new top
|
||||
StackItem peek = stack.peek();
|
||||
|
||||
selectAndLoadVideo(peek.getServiceId(),
|
||||
peek.getUrl(),
|
||||
!TextUtils.isEmpty(peek.getTitle())
|
||||
? peek.getTitle()
|
||||
: "");
|
||||
selectAndLoadVideo(peek.getServiceId(), peek.getUrl(),
|
||||
!TextUtils.isEmpty(peek.getTitle()) ? peek.getTitle() : "");
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
@ -739,43 +764,52 @@ public class VideoDetailFragment
|
|||
|
||||
@Override
|
||||
protected void doInitialLoadLogic() {
|
||||
if (currentInfo == null) prepareAndLoadInfo();
|
||||
else prepareAndHandleInfo(currentInfo, false);
|
||||
if (currentInfo == null) {
|
||||
prepareAndLoadInfo();
|
||||
} else {
|
||||
prepareAndHandleInfo(currentInfo, false);
|
||||
}
|
||||
}
|
||||
|
||||
public void selectAndLoadVideo(int serviceId, String videoUrl, String name) {
|
||||
setInitialData(serviceId, videoUrl, name);
|
||||
public void selectAndLoadVideo(final int sid, final String videoUrl, final String title) {
|
||||
setInitialData(sid, videoUrl, title);
|
||||
prepareAndLoadInfo();
|
||||
}
|
||||
|
||||
public void prepareAndHandleInfo(final StreamInfo info, boolean scrollToTop) {
|
||||
if (DEBUG) Log.d(TAG, "prepareAndHandleInfo() called with: info = ["
|
||||
+ info + "], scrollToTop = [" + scrollToTop + "]");
|
||||
private void prepareAndHandleInfo(final StreamInfo info, final boolean scrollToTop) {
|
||||
if (DEBUG) {
|
||||
Log.d(TAG, "prepareAndHandleInfo() called with: "
|
||||
+ "info = [" + info + "], scrollToTop = [" + scrollToTop + "]");
|
||||
}
|
||||
|
||||
setInitialData(info.getServiceId(), info.getUrl(), info.getName());
|
||||
pushToStack(serviceId, url, name);
|
||||
showLoading();
|
||||
initTabs();
|
||||
|
||||
if (scrollToTop) appBarLayout.setExpanded(true, true);
|
||||
if (scrollToTop) {
|
||||
appBarLayout.setExpanded(true, true);
|
||||
}
|
||||
handleResult(info);
|
||||
showContent();
|
||||
|
||||
}
|
||||
|
||||
protected void prepareAndLoadInfo() {
|
||||
private void prepareAndLoadInfo() {
|
||||
appBarLayout.setExpanded(true, true);
|
||||
pushToStack(serviceId, url, name);
|
||||
startLoading(false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void startLoading(boolean forceLoad) {
|
||||
public void startLoading(final boolean forceLoad) {
|
||||
super.startLoading(forceLoad);
|
||||
|
||||
initTabs();
|
||||
currentInfo = null;
|
||||
if (currentWorker != null) currentWorker.dispose();
|
||||
if (currentWorker != null) {
|
||||
currentWorker.dispose();
|
||||
}
|
||||
|
||||
currentWorker = ExtractorHelper.getStreamInfo(serviceId, url, forceLoad)
|
||||
.subscribeOn(Schedulers.io())
|
||||
|
|
@ -798,26 +832,29 @@ public class VideoDetailFragment
|
|||
}
|
||||
pageAdapter.clearAllItems();
|
||||
|
||||
if(shouldShowComments()){
|
||||
pageAdapter.addFragment(CommentsFragment.getInstance(serviceId, url, name), COMMENTS_TAB_TAG);
|
||||
if (shouldShowComments()) {
|
||||
pageAdapter.addFragment(CommentsFragment.getInstance(serviceId, url, name),
|
||||
COMMENTS_TAB_TAG);
|
||||
}
|
||||
|
||||
if(showRelatedStreams && null == relatedStreamsLayout){
|
||||
if (showRelatedStreams && null == relatedStreamsLayout) {
|
||||
//temp empty fragment. will be updated in handleResult
|
||||
pageAdapter.addFragment(new Fragment(), RELATED_TAB_TAG);
|
||||
}
|
||||
|
||||
if(pageAdapter.getCount() == 0){
|
||||
if (pageAdapter.getCount() == 0) {
|
||||
pageAdapter.addFragment(new EmptyFragment(), EMPTY_TAB_TAG);
|
||||
}
|
||||
|
||||
pageAdapter.notifyDataSetUpdate();
|
||||
|
||||
if(pageAdapter.getCount() < 2){
|
||||
if (pageAdapter.getCount() < 2) {
|
||||
tabLayout.setVisibility(View.GONE);
|
||||
}else{
|
||||
} else {
|
||||
int position = pageAdapter.getItemPositionByTitle(selectedTabTag);
|
||||
if(position != -1) viewPager.setCurrentItem(position);
|
||||
if (position != -1) {
|
||||
viewPager.setCurrentItem(position);
|
||||
}
|
||||
tabLayout.setVisibility(View.VISIBLE);
|
||||
}
|
||||
}
|
||||
|
|
@ -862,9 +899,8 @@ public class VideoDetailFragment
|
|||
NavigationHelper.enqueueOnPopupPlayer(activity, itemQueue, false);
|
||||
} else {
|
||||
Toast.makeText(activity, R.string.popup_playing_toast, Toast.LENGTH_SHORT).show();
|
||||
final Intent intent = NavigationHelper.getPlayerIntent(
|
||||
activity, PopupVideoPlayer.class, itemQueue, getSelectedVideoStream().resolution, true
|
||||
);
|
||||
final Intent intent = NavigationHelper.getPlayerIntent(activity,
|
||||
PopupVideoPlayer.class, itemQueue, getSelectedVideoStream().resolution, true);
|
||||
activity.startService(intent);
|
||||
}
|
||||
}
|
||||
|
|
@ -903,7 +939,7 @@ public class VideoDetailFragment
|
|||
// Utils
|
||||
//////////////////////////////////////////////////////////////////////////*/
|
||||
|
||||
public void setAutoplay(boolean autoplay) {
|
||||
public void setAutoplay(final boolean autoplay) {
|
||||
this.autoPlayEnabled = autoplay;
|
||||
}
|
||||
|
||||
|
|
@ -916,7 +952,7 @@ public class VideoDetailFragment
|
|||
final HistoryRecordManager recordManager = new HistoryRecordManager(requireContext());
|
||||
disposables.add(recordManager.onViewed(info).onErrorComplete()
|
||||
.subscribe(
|
||||
ignored -> {/* successful */},
|
||||
ignored -> { /* successful */ },
|
||||
error -> Log.e(TAG, "Register view failure: ", error)
|
||||
));
|
||||
}
|
||||
|
|
@ -926,8 +962,9 @@ public class VideoDetailFragment
|
|||
return sortedVideoStreams != null ? sortedVideoStreams.get(selectedVideoStreamIndex) : null;
|
||||
}
|
||||
|
||||
private void prepareDescription(Description description) {
|
||||
if (TextUtils.isEmpty(description.getContent()) || description == Description.emptyDescription) {
|
||||
private void prepareDescription(final Description description) {
|
||||
if (TextUtils.isEmpty(description.getContent())
|
||||
|| description == Description.emptyDescription) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -978,14 +1015,16 @@ public class VideoDetailFragment
|
|||
contentRootLayoutHiding.setVisibility(View.VISIBLE);
|
||||
}
|
||||
|
||||
protected void setInitialData(int serviceId, String url, String name) {
|
||||
this.serviceId = serviceId;
|
||||
this.url = url;
|
||||
this.name = !TextUtils.isEmpty(name) ? name : "";
|
||||
protected void setInitialData(final int sid, final String u, final String title) {
|
||||
this.serviceId = sid;
|
||||
this.url = u;
|
||||
this.name = !TextUtils.isEmpty(title) ? title : "";
|
||||
}
|
||||
|
||||
private void setErrorImage(final int imageResource) {
|
||||
if (thumbnailImageView == null || activity == null) return;
|
||||
if (thumbnailImageView == null || activity == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
thumbnailImageView.setImageDrawable(ContextCompat.getDrawable(activity, imageResource));
|
||||
animateView(thumbnailImageView, false, 0, 0,
|
||||
|
|
@ -993,11 +1032,12 @@ public class VideoDetailFragment
|
|||
}
|
||||
|
||||
@Override
|
||||
public void showError(String message, boolean showRetryButton) {
|
||||
public void showError(final String message, final boolean showRetryButton) {
|
||||
showError(message, showRetryButton, R.drawable.not_available_monkey);
|
||||
}
|
||||
|
||||
protected void showError(String message, boolean showRetryButton, @DrawableRes int imageError) {
|
||||
protected void showError(final String message, final boolean showRetryButton,
|
||||
@DrawableRes final int imageError) {
|
||||
super.showError(message, showRetryButton);
|
||||
setErrorImage(imageError);
|
||||
}
|
||||
|
|
@ -1012,7 +1052,7 @@ public class VideoDetailFragment
|
|||
super.showLoading();
|
||||
|
||||
//if data is already cached, transition from VISIBLE -> INVISIBLE -> VISIBLE is not required
|
||||
if(!ExtractorHelper.isCached(serviceId, url, InfoItem.InfoType.STREAM)){
|
||||
if (!ExtractorHelper.isCached(serviceId, url, InfoItem.InfoType.STREAM)) {
|
||||
contentRootLayoutHiding.setVisibility(View.INVISIBLE);
|
||||
}
|
||||
|
||||
|
|
@ -1031,33 +1071,35 @@ public class VideoDetailFragment
|
|||
videoTitleToggleArrow.setVisibility(View.GONE);
|
||||
videoTitleRoot.setClickable(false);
|
||||
|
||||
if(relatedStreamsLayout != null){
|
||||
if(showRelatedStreams){
|
||||
if (relatedStreamsLayout != null) {
|
||||
if (showRelatedStreams) {
|
||||
relatedStreamsLayout.setVisibility(View.INVISIBLE);
|
||||
}else{
|
||||
} else {
|
||||
relatedStreamsLayout.setVisibility(View.GONE);
|
||||
}
|
||||
}
|
||||
|
||||
imageLoader.cancelDisplayTask(thumbnailImageView);
|
||||
imageLoader.cancelDisplayTask(uploaderThumb);
|
||||
IMAGE_LOADER.cancelDisplayTask(thumbnailImageView);
|
||||
IMAGE_LOADER.cancelDisplayTask(uploaderThumb);
|
||||
thumbnailImageView.setImageBitmap(null);
|
||||
uploaderThumb.setImageBitmap(null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleResult(@NonNull StreamInfo info) {
|
||||
public void handleResult(@NonNull final StreamInfo info) {
|
||||
super.handleResult(info);
|
||||
|
||||
setInitialData(info.getServiceId(), info.getOriginalUrl(), info.getName());
|
||||
|
||||
if(showRelatedStreams){
|
||||
if(null == relatedStreamsLayout){ //phone
|
||||
pageAdapter.updateItem(RELATED_TAB_TAG, RelatedVideosFragment.getInstance(currentInfo));
|
||||
if (showRelatedStreams) {
|
||||
if (null == relatedStreamsLayout) { //phone
|
||||
pageAdapter.updateItem(RELATED_TAB_TAG,
|
||||
RelatedVideosFragment.getInstance(currentInfo));
|
||||
pageAdapter.notifyDataSetUpdate();
|
||||
}else{ //tablet
|
||||
} else { //tablet
|
||||
getChildFragmentManager().beginTransaction()
|
||||
.replace(R.id.relatedStreamsLayout, RelatedVideosFragment.getInstance(currentInfo))
|
||||
.replace(R.id.relatedStreamsLayout,
|
||||
RelatedVideosFragment.getInstance(currentInfo))
|
||||
.commitNow();
|
||||
relatedStreamsLayout.setVisibility(View.VISIBLE);
|
||||
}
|
||||
|
|
@ -1081,9 +1123,11 @@ public class VideoDetailFragment
|
|||
if (info.getStreamType().equals(StreamType.AUDIO_LIVE_STREAM)) {
|
||||
videoCountView.setText(Localization.listeningCount(activity, info.getViewCount()));
|
||||
} else if (info.getStreamType().equals(StreamType.LIVE_STREAM)) {
|
||||
videoCountView.setText(Localization.localizeWatchingCount(activity, info.getViewCount()));
|
||||
videoCountView.setText(Localization
|
||||
.localizeWatchingCount(activity, info.getViewCount()));
|
||||
} else {
|
||||
videoCountView.setText(Localization.localizeViewCount(activity, info.getViewCount()));
|
||||
videoCountView.setText(Localization
|
||||
.localizeViewCount(activity, info.getViewCount()));
|
||||
}
|
||||
videoCountView.setVisibility(View.VISIBLE);
|
||||
} else {
|
||||
|
|
@ -1099,7 +1143,8 @@ public class VideoDetailFragment
|
|||
thumbsDisabledTextView.setVisibility(View.VISIBLE);
|
||||
} else {
|
||||
if (info.getDislikeCount() >= 0) {
|
||||
thumbsDownTextView.setText(Localization.shortCount(activity, info.getDislikeCount()));
|
||||
thumbsDownTextView.setText(Localization
|
||||
.shortCount(activity, info.getDislikeCount()));
|
||||
thumbsDownTextView.setVisibility(View.VISIBLE);
|
||||
thumbsDownImageView.setVisibility(View.VISIBLE);
|
||||
} else {
|
||||
|
|
@ -1139,7 +1184,8 @@ public class VideoDetailFragment
|
|||
videoDescriptionRootLayout.setVisibility(View.GONE);
|
||||
|
||||
if (info.getUploadDate() != null) {
|
||||
videoUploadDateView.setText(Localization.localizeUploadDate(activity, info.getUploadDate().date().getTime()));
|
||||
videoUploadDateView.setText(Localization
|
||||
.localizeUploadDate(activity, info.getUploadDate().date().getTime()));
|
||||
videoUploadDateView.setVisibility(View.VISIBLE);
|
||||
} else {
|
||||
videoUploadDateView.setText(null);
|
||||
|
|
@ -1171,9 +1217,12 @@ public class VideoDetailFragment
|
|||
spinnerToolbar.setVisibility(View.GONE);
|
||||
break;
|
||||
default:
|
||||
if(info.getAudioStreams().isEmpty()) detailControlsBackground.setVisibility(View.GONE);
|
||||
if (!info.getVideoStreams().isEmpty()
|
||||
|| !info.getVideoOnlyStreams().isEmpty()) break;
|
||||
if (info.getAudioStreams().isEmpty()) {
|
||||
detailControlsBackground.setVisibility(View.GONE);
|
||||
}
|
||||
if (!info.getVideoStreams().isEmpty() || !info.getVideoOnlyStreams().isEmpty()) {
|
||||
break;
|
||||
}
|
||||
|
||||
detailControlsPopup.setVisibility(View.GONE);
|
||||
spinnerToolbar.setVisibility(View.GONE);
|
||||
|
|
@ -1190,28 +1239,28 @@ public class VideoDetailFragment
|
|||
|
||||
|
||||
public void openDownloadDialog() {
|
||||
try {
|
||||
DownloadDialog downloadDialog = DownloadDialog.newInstance(currentInfo);
|
||||
downloadDialog.setVideoStreams(sortedVideoStreams);
|
||||
downloadDialog.setAudioStreams(currentInfo.getAudioStreams());
|
||||
downloadDialog.setSelectedVideoStream(selectedVideoStreamIndex);
|
||||
downloadDialog.setSubtitleStreams(currentInfo.getSubtitles());
|
||||
try {
|
||||
DownloadDialog downloadDialog = DownloadDialog.newInstance(currentInfo);
|
||||
downloadDialog.setVideoStreams(sortedVideoStreams);
|
||||
downloadDialog.setAudioStreams(currentInfo.getAudioStreams());
|
||||
downloadDialog.setSelectedVideoStream(selectedVideoStreamIndex);
|
||||
downloadDialog.setSubtitleStreams(currentInfo.getSubtitles());
|
||||
|
||||
downloadDialog.show(getActivity().getSupportFragmentManager(), "downloadDialog");
|
||||
} catch (Exception e) {
|
||||
ErrorActivity.ErrorInfo info = ErrorActivity.ErrorInfo.make(UserAction.UI_ERROR,
|
||||
ServiceList.all()
|
||||
.get(currentInfo
|
||||
.getServiceId())
|
||||
.getServiceInfo()
|
||||
.getName(), "",
|
||||
R.string.could_not_setup_download_menu);
|
||||
downloadDialog.show(getActivity().getSupportFragmentManager(), "downloadDialog");
|
||||
} catch (Exception e) {
|
||||
ErrorActivity.ErrorInfo info = ErrorActivity.ErrorInfo.make(UserAction.UI_ERROR,
|
||||
ServiceList.all()
|
||||
.get(currentInfo
|
||||
.getServiceId())
|
||||
.getServiceInfo()
|
||||
.getName(), "",
|
||||
R.string.could_not_setup_download_menu);
|
||||
|
||||
ErrorActivity.reportError(getActivity(),
|
||||
e,
|
||||
getActivity().getClass(),
|
||||
getActivity().findViewById(android.R.id.content), info);
|
||||
}
|
||||
ErrorActivity.reportError(getActivity(),
|
||||
e,
|
||||
getActivity().getClass(),
|
||||
getActivity().findViewById(android.R.id.content), info);
|
||||
}
|
||||
}
|
||||
|
||||
/*//////////////////////////////////////////////////////////////////////////
|
||||
|
|
@ -1219,12 +1268,16 @@ public class VideoDetailFragment
|
|||
//////////////////////////////////////////////////////////////////////////*/
|
||||
|
||||
@Override
|
||||
protected boolean onError(Throwable exception) {
|
||||
if (super.onError(exception)) return true;
|
||||
protected boolean onError(final Throwable exception) {
|
||||
if (super.onError(exception)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
int errorId = exception instanceof YoutubeStreamExtractor.DecryptException ? R.string.youtube_signature_decryption_error
|
||||
: exception instanceof ExtractionException ? R.string.parsing_error
|
||||
: R.string.general_error;
|
||||
int errorId = exception instanceof YoutubeStreamExtractor.DecryptException
|
||||
? R.string.youtube_signature_decryption_error
|
||||
: exception instanceof ExtractionException
|
||||
? R.string.parsing_error
|
||||
: R.string.general_error;
|
||||
|
||||
onUnrecoverableError(exception, UserAction.REQUESTED_STREAM,
|
||||
NewPipe.getNameOfService(serviceId), url, errorId);
|
||||
|
|
@ -1237,9 +1290,9 @@ public class VideoDetailFragment
|
|||
positionSubscriber.dispose();
|
||||
}
|
||||
final SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(activity);
|
||||
final boolean playbackResumeEnabled =
|
||||
prefs.getBoolean(activity.getString(R.string.enable_watch_history_key), true)
|
||||
&& prefs.getBoolean(activity.getString(R.string.enable_playback_resume_key), true);
|
||||
final boolean playbackResumeEnabled = prefs
|
||||
.getBoolean(activity.getString(R.string.enable_watch_history_key), true)
|
||||
&& prefs.getBoolean(activity.getString(R.string.enable_playback_resume_key), true);
|
||||
|
||||
if (!playbackResumeEnabled || info.getDuration() <= 0) {
|
||||
positionView.setVisibility(View.INVISIBLE);
|
||||
|
|
@ -1247,8 +1300,8 @@ public class VideoDetailFragment
|
|||
|
||||
// TODO: Remove this check when separation of concerns is done.
|
||||
// (live streams weren't getting updated because they are mixed)
|
||||
if (!info.getStreamType().equals(StreamType.LIVE_STREAM) &&
|
||||
!info.getStreamType().equals(StreamType.AUDIO_LIVE_STREAM)) {
|
||||
if (!info.getStreamType().equals(StreamType.LIVE_STREAM)
|
||||
&& !info.getStreamType().equals(StreamType.AUDIO_LIVE_STREAM)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
|
@ -1261,14 +1314,17 @@ public class VideoDetailFragment
|
|||
.onErrorComplete()
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribe(state -> {
|
||||
final int seconds = (int) TimeUnit.MILLISECONDS.toSeconds(state.getProgressTime());
|
||||
final int seconds
|
||||
= (int) TimeUnit.MILLISECONDS.toSeconds(state.getProgressTime());
|
||||
positionView.setMax((int) info.getDuration());
|
||||
positionView.setProgressAnimated(seconds);
|
||||
detailPositionView.setText(Localization.getDurationString(seconds));
|
||||
animateView(positionView, true, 500);
|
||||
animateView(detailPositionView, true, 500);
|
||||
}, e -> {
|
||||
if (DEBUG) e.printStackTrace();
|
||||
if (DEBUG) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}, () -> {
|
||||
animateView(positionView, false, 500);
|
||||
animateView(detailPositionView, false, 500);
|
||||
|
|
|
|||
|
|
@ -7,16 +7,17 @@ import android.content.res.Configuration;
|
|||
import android.content.res.Resources;
|
||||
import android.os.Bundle;
|
||||
import android.preference.PreferenceManager;
|
||||
import android.util.Log;
|
||||
import android.view.Menu;
|
||||
import android.view.MenuInflater;
|
||||
import android.view.View;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.appcompat.app.ActionBar;
|
||||
import androidx.appcompat.app.AppCompatActivity;
|
||||
import androidx.recyclerview.widget.GridLayoutManager;
|
||||
import androidx.recyclerview.widget.LinearLayoutManager;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
import android.util.Log;
|
||||
import android.view.Menu;
|
||||
import android.view.MenuInflater;
|
||||
import android.view.View;
|
||||
|
||||
import org.schabi.newpipe.R;
|
||||
import org.schabi.newpipe.extractor.InfoItem;
|
||||
|
|
@ -42,7 +43,14 @@ import java.util.Queue;
|
|||
|
||||
import static org.schabi.newpipe.util.AnimationUtils.animateView;
|
||||
|
||||
public abstract class BaseListFragment<I, N> extends BaseStateFragment<I> implements ListViewContract<I, N>, StateSaver.WriteRead, SharedPreferences.OnSharedPreferenceChangeListener {
|
||||
public abstract class BaseListFragment<I, N> extends BaseStateFragment<I>
|
||||
implements ListViewContract<I, N>, StateSaver.WriteRead,
|
||||
SharedPreferences.OnSharedPreferenceChangeListener {
|
||||
private static final int LIST_MODE_UPDATE_FLAG = 0x32;
|
||||
protected StateSaver.SavedState savedState;
|
||||
|
||||
private boolean useDefaultStateSaving = true;
|
||||
private int updateFlags = 0;
|
||||
|
||||
/*//////////////////////////////////////////////////////////////////////////
|
||||
// Views
|
||||
|
|
@ -50,17 +58,14 @@ public abstract class BaseListFragment<I, N> extends BaseStateFragment<I> implem
|
|||
|
||||
protected InfoListAdapter infoListAdapter;
|
||||
protected RecyclerView itemsList;
|
||||
private int updateFlags = 0;
|
||||
private int focusedPosition = -1;
|
||||
|
||||
private static final int LIST_MODE_UPDATE_FLAG = 0x32;
|
||||
|
||||
/*//////////////////////////////////////////////////////////////////////////
|
||||
// LifeCycle
|
||||
//////////////////////////////////////////////////////////////////////////*/
|
||||
|
||||
@Override
|
||||
public void onAttach(Context context) {
|
||||
public void onAttach(final Context context) {
|
||||
super.onAttach(context);
|
||||
|
||||
if (infoListAdapter == null) {
|
||||
|
|
@ -74,7 +79,7 @@ public abstract class BaseListFragment<I, N> extends BaseStateFragment<I> implem
|
|||
}
|
||||
|
||||
@Override
|
||||
public void onCreate(Bundle savedInstanceState) {
|
||||
public void onCreate(final Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
setHasOptionsMenu(true);
|
||||
PreferenceManager.getDefaultSharedPreferences(activity)
|
||||
|
|
@ -84,7 +89,9 @@ public abstract class BaseListFragment<I, N> extends BaseStateFragment<I> implem
|
|||
@Override
|
||||
public void onDestroy() {
|
||||
super.onDestroy();
|
||||
if (useDefaultStateSaving) StateSaver.onDestroy(savedState);
|
||||
if (useDefaultStateSaving) {
|
||||
StateSaver.onDestroy(savedState);
|
||||
}
|
||||
PreferenceManager.getDefaultSharedPreferences(activity)
|
||||
.unregisterOnSharedPreferenceChangeListener(this);
|
||||
}
|
||||
|
|
@ -96,8 +103,9 @@ public abstract class BaseListFragment<I, N> extends BaseStateFragment<I> implem
|
|||
if (updateFlags != 0) {
|
||||
if ((updateFlags & LIST_MODE_UPDATE_FLAG) != 0) {
|
||||
final boolean useGrid = isGridLayout();
|
||||
itemsList.setLayoutManager(useGrid ? getGridLayoutManager() : getListLayoutManager());
|
||||
infoListAdapter.setGridItemVariants(useGrid);
|
||||
itemsList.setLayoutManager(useGrid
|
||||
? getGridLayoutManager() : getListLayoutManager());
|
||||
infoListAdapter.setUseGridVariant(useGrid);
|
||||
infoListAdapter.notifyDataSetChanged();
|
||||
}
|
||||
updateFlags = 0;
|
||||
|
|
@ -108,16 +116,14 @@ public abstract class BaseListFragment<I, N> extends BaseStateFragment<I> implem
|
|||
// State Saving
|
||||
//////////////////////////////////////////////////////////////////////////*/
|
||||
|
||||
protected StateSaver.SavedState savedState;
|
||||
protected boolean useDefaultStateSaving = true;
|
||||
|
||||
/**
|
||||
* If the default implementation of {@link StateSaver.WriteRead} should be used.
|
||||
*
|
||||
* @see StateSaver
|
||||
* @param useDefaultStateSaving Whether the default implementation should be used
|
||||
*/
|
||||
public void useDefaultStateSaving(boolean useDefault) {
|
||||
this.useDefaultStateSaving = useDefault;
|
||||
public void setUseDefaultStateSaving(final boolean useDefaultStateSaving) {
|
||||
this.useDefaultStateSaving = useDefaultStateSaving;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
@ -139,7 +145,7 @@ public abstract class BaseListFragment<I, N> extends BaseStateFragment<I> implem
|
|||
}
|
||||
|
||||
@Override
|
||||
public void writeTo(Queue<Object> objectsToSave) {
|
||||
public void writeTo(final Queue<Object> objectsToSave) {
|
||||
if (!useDefaultStateSaving) {
|
||||
return;
|
||||
}
|
||||
|
|
@ -150,7 +156,7 @@ public abstract class BaseListFragment<I, N> extends BaseStateFragment<I> implem
|
|||
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
public void readFrom(@NonNull Queue<Object> savedObjects) throws Exception {
|
||||
public void readFrom(@NonNull final Queue<Object> savedObjects) throws Exception {
|
||||
if (!useDefaultStateSaving) {
|
||||
return;
|
||||
}
|
||||
|
|
@ -174,15 +180,20 @@ public abstract class BaseListFragment<I, N> extends BaseStateFragment<I> implem
|
|||
}
|
||||
|
||||
@Override
|
||||
public void onSaveInstanceState(Bundle bundle) {
|
||||
public void onSaveInstanceState(final Bundle bundle) {
|
||||
super.onSaveInstanceState(bundle);
|
||||
if (useDefaultStateSaving) savedState = StateSaver.tryToSave(activity.isChangingConfigurations(), savedState, bundle, this);
|
||||
if (useDefaultStateSaving) {
|
||||
savedState = StateSaver
|
||||
.tryToSave(activity.isChangingConfigurations(), savedState, bundle, this);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onRestoreInstanceState(@NonNull Bundle bundle) {
|
||||
protected void onRestoreInstanceState(@NonNull final Bundle bundle) {
|
||||
super.onRestoreInstanceState(bundle);
|
||||
if (useDefaultStateSaving) savedState = StateSaver.tryToRestore(bundle, this);
|
||||
if (useDefaultStateSaving) {
|
||||
savedState = StateSaver.tryToRestore(bundle, this);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
@ -217,29 +228,32 @@ public abstract class BaseListFragment<I, N> extends BaseStateFragment<I> implem
|
|||
final Resources resources = activity.getResources();
|
||||
int width = resources.getDimensionPixelSize(R.dimen.video_item_grid_thumbnail_image_width);
|
||||
width += (24 * resources.getDisplayMetrics().density);
|
||||
final int spanCount = (int) Math.floor(resources.getDisplayMetrics().widthPixels / (double)width);
|
||||
final int spanCount = (int) Math.floor(resources.getDisplayMetrics().widthPixels
|
||||
/ (double) width);
|
||||
final GridLayoutManager lm = new GridLayoutManager(activity, spanCount);
|
||||
lm.setSpanSizeLookup(infoListAdapter.getSpanSizeLookup(spanCount));
|
||||
return lm;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void initViews(View rootView, Bundle savedInstanceState) {
|
||||
protected void initViews(final View rootView, final Bundle savedInstanceState) {
|
||||
super.initViews(rootView, savedInstanceState);
|
||||
|
||||
final boolean useGrid = isGridLayout();
|
||||
itemsList = rootView.findViewById(R.id.items_list);
|
||||
itemsList.setLayoutManager(useGrid ? getGridLayoutManager() : getListLayoutManager());
|
||||
|
||||
infoListAdapter.setGridItemVariants(useGrid);
|
||||
infoListAdapter.setUseGridVariant(useGrid);
|
||||
infoListAdapter.setFooter(getListFooter());
|
||||
infoListAdapter.setHeader(getListHeader());
|
||||
|
||||
itemsList.setAdapter(infoListAdapter);
|
||||
}
|
||||
|
||||
protected void onItemSelected(InfoItem selectedItem) {
|
||||
if (DEBUG) Log.d(TAG, "onItemSelected() called with: selectedItem = [" + selectedItem + "]");
|
||||
protected void onItemSelected(final InfoItem selectedItem) {
|
||||
if (DEBUG) {
|
||||
Log.d(TAG, "onItemSelected() called with: selectedItem = [" + selectedItem + "]");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
@ -247,19 +261,19 @@ public abstract class BaseListFragment<I, N> extends BaseStateFragment<I> implem
|
|||
super.initListeners();
|
||||
infoListAdapter.setOnStreamSelectedListener(new OnClickGesture<StreamInfoItem>() {
|
||||
@Override
|
||||
public void selected(StreamInfoItem selectedItem) {
|
||||
public void selected(final StreamInfoItem selectedItem) {
|
||||
onStreamSelected(selectedItem);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void held(StreamInfoItem selectedItem) {
|
||||
public void held(final StreamInfoItem selectedItem) {
|
||||
showStreamDialog(selectedItem);
|
||||
}
|
||||
});
|
||||
|
||||
infoListAdapter.setOnChannelSelectedListener(new OnClickGesture<ChannelInfoItem>() {
|
||||
@Override
|
||||
public void selected(ChannelInfoItem selectedItem) {
|
||||
public void selected(final ChannelInfoItem selectedItem) {
|
||||
try {
|
||||
onItemSelected(selectedItem);
|
||||
NavigationHelper.openChannelFragment(getFM(),
|
||||
|
|
@ -274,7 +288,7 @@ public abstract class BaseListFragment<I, N> extends BaseStateFragment<I> implem
|
|||
|
||||
infoListAdapter.setOnPlaylistSelectedListener(new OnClickGesture<PlaylistInfoItem>() {
|
||||
@Override
|
||||
public void selected(PlaylistInfoItem selectedItem) {
|
||||
public void selected(final PlaylistInfoItem selectedItem) {
|
||||
try {
|
||||
onItemSelected(selectedItem);
|
||||
NavigationHelper.openPlaylistFragment(getFM(),
|
||||
|
|
@ -289,7 +303,7 @@ public abstract class BaseListFragment<I, N> extends BaseStateFragment<I> implem
|
|||
|
||||
infoListAdapter.setOnCommentsSelectedListener(new OnClickGesture<CommentsInfoItem>() {
|
||||
@Override
|
||||
public void selected(CommentsInfoItem selectedItem) {
|
||||
public void selected(final CommentsInfoItem selectedItem) {
|
||||
onItemSelected(selectedItem);
|
||||
}
|
||||
});
|
||||
|
|
@ -297,13 +311,13 @@ public abstract class BaseListFragment<I, N> extends BaseStateFragment<I> implem
|
|||
itemsList.clearOnScrollListeners();
|
||||
itemsList.addOnScrollListener(new OnScrollBelowItemsListener() {
|
||||
@Override
|
||||
public void onScrolledDown(RecyclerView recyclerView) {
|
||||
public void onScrolledDown(final RecyclerView recyclerView) {
|
||||
onScrollToBottom();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void onStreamSelected(StreamInfoItem selectedItem) {
|
||||
private void onStreamSelected(final StreamInfoItem selectedItem) {
|
||||
onItemSelected(selectedItem);
|
||||
NavigationHelper.openVideoDetailFragment(getFM(),
|
||||
selectedItem.getServiceId(), selectedItem.getUrl(), selectedItem.getName());
|
||||
|
|
@ -316,12 +330,12 @@ public abstract class BaseListFragment<I, N> extends BaseStateFragment<I> implem
|
|||
}
|
||||
|
||||
|
||||
|
||||
|
||||
protected void showStreamDialog(final StreamInfoItem item) {
|
||||
final Context context = getContext();
|
||||
final Activity activity = getActivity();
|
||||
if (context == null || context.getResources() == null || activity == null) return;
|
||||
if (context == null || context.getResources() == null || activity == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (item.getStreamType() == StreamType.AUDIO_STREAM) {
|
||||
StreamDialogEntry.setEnabledEntries(
|
||||
|
|
@ -339,8 +353,8 @@ public abstract class BaseListFragment<I, N> extends BaseStateFragment<I> implem
|
|||
StreamDialogEntry.share);
|
||||
}
|
||||
|
||||
new InfoItemDialog(activity, item, StreamDialogEntry.getCommands(context), (dialog, which) ->
|
||||
StreamDialogEntry.clickOn(which, this, item)).show();
|
||||
new InfoItemDialog(activity, item, StreamDialogEntry.getCommands(context),
|
||||
(dialog, which) -> StreamDialogEntry.clickOn(which, this, item)).show();
|
||||
}
|
||||
|
||||
/*//////////////////////////////////////////////////////////////////////////
|
||||
|
|
@ -348,8 +362,11 @@ public abstract class BaseListFragment<I, N> extends BaseStateFragment<I> implem
|
|||
//////////////////////////////////////////////////////////////////////////*/
|
||||
|
||||
@Override
|
||||
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
|
||||
if (DEBUG) Log.d(TAG, "onCreateOptionsMenu() called with: menu = [" + menu + "], inflater = [" + inflater + "]");
|
||||
public void onCreateOptionsMenu(final Menu menu, final MenuInflater inflater) {
|
||||
if (DEBUG) {
|
||||
Log.d(TAG, "onCreateOptionsMenu() called with: "
|
||||
+ "menu = [" + menu + "], inflater = [" + inflater + "]");
|
||||
}
|
||||
super.onCreateOptionsMenu(menu, inflater);
|
||||
ActionBar supportActionBar = activity.getSupportActionBar();
|
||||
if (supportActionBar != null) {
|
||||
|
|
@ -387,7 +404,7 @@ public abstract class BaseListFragment<I, N> extends BaseStateFragment<I> implem
|
|||
}
|
||||
|
||||
@Override
|
||||
public void showError(String message, boolean showRetryButton) {
|
||||
public void showError(final String message, final boolean showRetryButton) {
|
||||
super.showError(message, showRetryButton);
|
||||
showListFooter(false);
|
||||
animateView(itemsList, false, 200);
|
||||
|
|
@ -409,25 +426,28 @@ public abstract class BaseListFragment<I, N> extends BaseStateFragment<I> implem
|
|||
}
|
||||
|
||||
@Override
|
||||
public void handleNextItems(N result) {
|
||||
public void handleNextItems(final N result) {
|
||||
isLoading.set(false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) {
|
||||
public void onSharedPreferenceChanged(final SharedPreferences sharedPreferences,
|
||||
final String key) {
|
||||
if (key.equals(getString(R.string.list_view_mode_key))) {
|
||||
updateFlags |= LIST_MODE_UPDATE_FLAG;
|
||||
}
|
||||
}
|
||||
|
||||
protected boolean isGridLayout() {
|
||||
final String list_mode = PreferenceManager.getDefaultSharedPreferences(activity).getString(getString(R.string.list_view_mode_key), getString(R.string.list_view_mode_value));
|
||||
if ("auto".equals(list_mode)) {
|
||||
final String listMode = PreferenceManager.getDefaultSharedPreferences(activity)
|
||||
.getString(getString(R.string.list_view_mode_key),
|
||||
getString(R.string.list_view_mode_value));
|
||||
if ("auto".equals(listMode)) {
|
||||
final Configuration configuration = getResources().getConfiguration();
|
||||
return configuration.orientation == Configuration.ORIENTATION_LANDSCAPE
|
||||
&& configuration.isLayoutSizeAtLeast(Configuration.SCREENLAYOUT_SIZE_LARGE);
|
||||
} else {
|
||||
return "grid".equals(list_mode);
|
||||
return "grid".equals(listMode);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -24,7 +24,6 @@ import io.reactivex.schedulers.Schedulers;
|
|||
|
||||
public abstract class BaseListInfoFragment<I extends ListInfo>
|
||||
extends BaseListFragment<I, ListExtractor.InfoItemsPage> {
|
||||
|
||||
@State
|
||||
protected int serviceId = Constants.NO_SERVICE_ID;
|
||||
@State
|
||||
|
|
@ -37,7 +36,7 @@ public abstract class BaseListInfoFragment<I extends ListInfo>
|
|||
protected Disposable currentWorker;
|
||||
|
||||
@Override
|
||||
protected void initViews(View rootView, Bundle savedInstanceState) {
|
||||
protected void initViews(final View rootView, final Bundle savedInstanceState) {
|
||||
super.initViews(rootView, savedInstanceState);
|
||||
setTitle(name);
|
||||
showListFooter(hasMoreItems());
|
||||
|
|
@ -46,7 +45,9 @@ public abstract class BaseListInfoFragment<I extends ListInfo>
|
|||
@Override
|
||||
public void onPause() {
|
||||
super.onPause();
|
||||
if (currentWorker != null) currentWorker.dispose();
|
||||
if (currentWorker != null) {
|
||||
currentWorker.dispose();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
@ -76,7 +77,7 @@ public abstract class BaseListInfoFragment<I extends ListInfo>
|
|||
//////////////////////////////////////////////////////////////////////////*/
|
||||
|
||||
@Override
|
||||
public void writeTo(Queue<Object> objectsToSave) {
|
||||
public void writeTo(final Queue<Object> objectsToSave) {
|
||||
super.writeTo(objectsToSave);
|
||||
objectsToSave.add(currentInfo);
|
||||
objectsToSave.add(currentNextPageUrl);
|
||||
|
|
@ -84,7 +85,7 @@ public abstract class BaseListInfoFragment<I extends ListInfo>
|
|||
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
public void readFrom(@NonNull Queue<Object> savedObjects) throws Exception {
|
||||
public void readFrom(@NonNull final Queue<Object> savedObjects) throws Exception {
|
||||
super.readFrom(savedObjects);
|
||||
currentInfo = (I) savedObjects.poll();
|
||||
currentNextPageUrl = (String) savedObjects.poll();
|
||||
|
|
@ -95,10 +96,14 @@ public abstract class BaseListInfoFragment<I extends ListInfo>
|
|||
//////////////////////////////////////////////////////////////////////////*/
|
||||
|
||||
protected void doInitialLoadLogic() {
|
||||
if (DEBUG) Log.d(TAG, "doInitialLoadLogic() called");
|
||||
if (DEBUG) {
|
||||
Log.d(TAG, "doInitialLoadLogic() called");
|
||||
}
|
||||
if (currentInfo == null) {
|
||||
startLoading(false);
|
||||
} else handleResult(currentInfo);
|
||||
} else {
|
||||
handleResult(currentInfo);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -106,18 +111,21 @@ public abstract class BaseListInfoFragment<I extends ListInfo>
|
|||
* You can use the default implementations from {@link org.schabi.newpipe.util.ExtractorHelper}.
|
||||
*
|
||||
* @param forceLoad allow or disallow the result to come from the cache
|
||||
* @return Rx {@link Single} containing the {@link ListInfo}
|
||||
*/
|
||||
protected abstract Single<I> loadResult(boolean forceLoad);
|
||||
|
||||
@Override
|
||||
public void startLoading(boolean forceLoad) {
|
||||
public void startLoading(final boolean forceLoad) {
|
||||
super.startLoading(forceLoad);
|
||||
|
||||
showListFooter(false);
|
||||
infoListAdapter.clearStreamItemList();
|
||||
|
||||
currentInfo = null;
|
||||
if (currentWorker != null) currentWorker.dispose();
|
||||
if (currentWorker != null) {
|
||||
currentWorker.dispose();
|
||||
}
|
||||
currentWorker = loadResult(forceLoad)
|
||||
.subscribeOn(Schedulers.io())
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
|
|
@ -130,15 +138,20 @@ public abstract class BaseListInfoFragment<I extends ListInfo>
|
|||
}
|
||||
|
||||
/**
|
||||
* Implement the logic to load more items<br/>
|
||||
* You can use the default implementations from {@link org.schabi.newpipe.util.ExtractorHelper}
|
||||
* Implement the logic to load more items.
|
||||
* <p>You can use the default implementations
|
||||
* from {@link org.schabi.newpipe.util.ExtractorHelper}.</p>
|
||||
*
|
||||
* @return Rx {@link Single} containing the {@link ListExtractor.InfoItemsPage}
|
||||
*/
|
||||
protected abstract Single<ListExtractor.InfoItemsPage> loadMoreItemsLogic();
|
||||
|
||||
protected void loadMoreItems() {
|
||||
isLoading.set(true);
|
||||
|
||||
if (currentWorker != null) currentWorker.dispose();
|
||||
if (currentWorker != null) {
|
||||
currentWorker.dispose();
|
||||
}
|
||||
|
||||
forbidDownwardFocusScroll();
|
||||
|
||||
|
|
@ -146,7 +159,8 @@ public abstract class BaseListInfoFragment<I extends ListInfo>
|
|||
.subscribeOn(Schedulers.io())
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.doFinally(this::allowDownwardFocusScroll)
|
||||
.subscribe((@io.reactivex.annotations.NonNull ListExtractor.InfoItemsPage InfoItemsPage) -> {
|
||||
.subscribe((@io.reactivex.annotations.NonNull
|
||||
ListExtractor.InfoItemsPage InfoItemsPage) -> {
|
||||
isLoading.set(false);
|
||||
handleNextItems(InfoItemsPage);
|
||||
}, (@io.reactivex.annotations.NonNull Throwable throwable) -> {
|
||||
|
|
@ -168,7 +182,7 @@ public abstract class BaseListInfoFragment<I extends ListInfo>
|
|||
}
|
||||
|
||||
@Override
|
||||
public void handleNextItems(ListExtractor.InfoItemsPage result) {
|
||||
public void handleNextItems(final ListExtractor.InfoItemsPage result) {
|
||||
super.handleNextItems(result);
|
||||
currentNextPageUrl = result.getNextPageUrl();
|
||||
infoListAdapter.addInfoItemList(result.getItems());
|
||||
|
|
@ -186,7 +200,7 @@ public abstract class BaseListInfoFragment<I extends ListInfo>
|
|||
//////////////////////////////////////////////////////////////////////////*/
|
||||
|
||||
@Override
|
||||
public void handleResult(@NonNull I result) {
|
||||
public void handleResult(@NonNull final I result) {
|
||||
super.handleResult(result);
|
||||
|
||||
name = result.getName();
|
||||
|
|
@ -207,9 +221,9 @@ public abstract class BaseListInfoFragment<I extends ListInfo>
|
|||
// Utils
|
||||
//////////////////////////////////////////////////////////////////////////*/
|
||||
|
||||
protected void setInitialData(int serviceId, String url, String name) {
|
||||
this.serviceId = serviceId;
|
||||
this.url = url;
|
||||
this.name = !TextUtils.isEmpty(name) ? name : "";
|
||||
protected void setInitialData(final int sid, final String u, final String title) {
|
||||
this.serviceId = sid;
|
||||
this.url = u;
|
||||
this.name = !TextUtils.isEmpty(title) ? title : "";
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,10 +4,6 @@ import android.content.Context;
|
|||
import android.content.Intent;
|
||||
import android.net.Uri;
|
||||
import android.os.Bundle;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.core.content.ContextCompat;
|
||||
import androidx.appcompat.app.ActionBar;
|
||||
import android.text.TextUtils;
|
||||
import android.util.Log;
|
||||
import android.view.LayoutInflater;
|
||||
|
|
@ -21,6 +17,11 @@ import android.widget.ImageView;
|
|||
import android.widget.LinearLayout;
|
||||
import android.widget.TextView;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.appcompat.app.ActionBar;
|
||||
import androidx.core.content.ContextCompat;
|
||||
|
||||
import com.jakewharton.rxbinding2.view.RxView;
|
||||
|
||||
import org.schabi.newpipe.R;
|
||||
|
|
@ -29,7 +30,6 @@ import org.schabi.newpipe.extractor.InfoItem;
|
|||
import org.schabi.newpipe.extractor.ListExtractor;
|
||||
import org.schabi.newpipe.extractor.NewPipe;
|
||||
import org.schabi.newpipe.extractor.channel.ChannelInfo;
|
||||
import org.schabi.newpipe.extractor.exceptions.ContentNotAvailableException;
|
||||
import org.schabi.newpipe.extractor.exceptions.ExtractionException;
|
||||
import org.schabi.newpipe.extractor.stream.StreamInfoItem;
|
||||
import org.schabi.newpipe.fragments.list.BaseListInfoFragment;
|
||||
|
|
@ -63,15 +63,15 @@ import static org.schabi.newpipe.util.AnimationUtils.animateTextColor;
|
|||
import static org.schabi.newpipe.util.AnimationUtils.animateView;
|
||||
|
||||
public class ChannelFragment extends BaseListInfoFragment<ChannelInfo> {
|
||||
|
||||
private static final int BUTTON_DEBOUNCE_INTERVAL = 100;
|
||||
private final CompositeDisposable disposables = new CompositeDisposable();
|
||||
private Disposable subscribeButtonMonitor;
|
||||
private SubscriptionManager subscriptionManager;
|
||||
|
||||
/*//////////////////////////////////////////////////////////////////////////
|
||||
// Views
|
||||
//////////////////////////////////////////////////////////////////////////*/
|
||||
|
||||
private SubscriptionManager subscriptionManager;
|
||||
private View headerRootLayout;
|
||||
private ImageView headerChannelBanner;
|
||||
private ImageView headerAvatarView;
|
||||
|
|
@ -79,25 +79,20 @@ public class ChannelFragment extends BaseListInfoFragment<ChannelInfo> {
|
|||
private TextView headerSubscribersTextView;
|
||||
private Button headerSubscribeButton;
|
||||
private View playlistCtrl;
|
||||
|
||||
private LinearLayout headerPlayAllButton;
|
||||
private LinearLayout headerPopupButton;
|
||||
private LinearLayout headerBackgroundButton;
|
||||
|
||||
private MenuItem menuRssButton;
|
||||
|
||||
public static ChannelFragment getInstance(int serviceId, String url, String name) {
|
||||
public static ChannelFragment getInstance(final int serviceId, final String url,
|
||||
final String name) {
|
||||
ChannelFragment instance = new ChannelFragment();
|
||||
instance.setInitialData(serviceId, url, name);
|
||||
return instance;
|
||||
}
|
||||
|
||||
/*//////////////////////////////////////////////////////////////////////////
|
||||
// LifeCycle
|
||||
//////////////////////////////////////////////////////////////////////////*/
|
||||
|
||||
@Override
|
||||
public void setUserVisibleHint(boolean isVisibleToUser) {
|
||||
public void setUserVisibleHint(final boolean isVisibleToUser) {
|
||||
super.setUserVisibleHint(isVisibleToUser);
|
||||
if (activity != null
|
||||
&& useAsFrontPage
|
||||
|
|
@ -106,22 +101,32 @@ public class ChannelFragment extends BaseListInfoFragment<ChannelInfo> {
|
|||
}
|
||||
}
|
||||
|
||||
/*//////////////////////////////////////////////////////////////////////////
|
||||
// LifeCycle
|
||||
//////////////////////////////////////////////////////////////////////////*/
|
||||
|
||||
@Override
|
||||
public void onAttach(Context context) {
|
||||
public void onAttach(final Context context) {
|
||||
super.onAttach(context);
|
||||
subscriptionManager = new SubscriptionManager(activity);
|
||||
}
|
||||
|
||||
@Override
|
||||
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
|
||||
public View onCreateView(@NonNull final LayoutInflater inflater,
|
||||
@Nullable final ViewGroup container,
|
||||
@Nullable final Bundle savedInstanceState) {
|
||||
return inflater.inflate(R.layout.fragment_channel, container, false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDestroy() {
|
||||
super.onDestroy();
|
||||
if (disposables != null) disposables.clear();
|
||||
if (subscribeButtonMonitor != null) subscribeButtonMonitor.dispose();
|
||||
if (disposables != null) {
|
||||
disposables.clear();
|
||||
}
|
||||
if (subscribeButtonMonitor != null) {
|
||||
subscribeButtonMonitor.dispose();
|
||||
}
|
||||
}
|
||||
|
||||
/*//////////////////////////////////////////////////////////////////////////
|
||||
|
|
@ -129,7 +134,8 @@ public class ChannelFragment extends BaseListInfoFragment<ChannelInfo> {
|
|||
//////////////////////////////////////////////////////////////////////////*/
|
||||
|
||||
protected View getListHeader() {
|
||||
headerRootLayout = activity.getLayoutInflater().inflate(R.layout.channel_header, itemsList, false);
|
||||
headerRootLayout = activity.getLayoutInflater()
|
||||
.inflate(R.layout.channel_header, itemsList, false);
|
||||
headerChannelBanner = headerRootLayout.findViewById(R.id.channel_banner_image);
|
||||
headerAvatarView = headerRootLayout.findViewById(R.id.channel_avatar_view);
|
||||
headerTitleView = headerRootLayout.findViewById(R.id.channel_title_view);
|
||||
|
|
@ -150,7 +156,7 @@ public class ChannelFragment extends BaseListInfoFragment<ChannelInfo> {
|
|||
//////////////////////////////////////////////////////////////////////////*/
|
||||
|
||||
@Override
|
||||
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
|
||||
public void onCreateOptionsMenu(final Menu menu, final MenuInflater inflater) {
|
||||
super.onCreateOptionsMenu(menu, inflater);
|
||||
ActionBar supportActionBar = activity.getSupportActionBar();
|
||||
if (useAsFrontPage && supportActionBar != null) {
|
||||
|
|
@ -158,8 +164,10 @@ public class ChannelFragment extends BaseListInfoFragment<ChannelInfo> {
|
|||
} else {
|
||||
inflater.inflate(R.menu.menu_channel, menu);
|
||||
|
||||
if (DEBUG) Log.d(TAG, "onCreateOptionsMenu() called with: menu = [" + menu +
|
||||
"], inflater = [" + inflater + "]");
|
||||
if (DEBUG) {
|
||||
Log.d(TAG, "onCreateOptionsMenu() called with: "
|
||||
+ "menu = [" + menu + "], inflater = [" + inflater + "]");
|
||||
}
|
||||
menuRssButton = menu.findItem(R.id.menu_item_rss);
|
||||
}
|
||||
}
|
||||
|
|
@ -173,7 +181,7 @@ public class ChannelFragment extends BaseListInfoFragment<ChannelInfo> {
|
|||
}
|
||||
|
||||
@Override
|
||||
public boolean onOptionsItemSelected(MenuItem item) {
|
||||
public boolean onOptionsItemSelected(final MenuItem item) {
|
||||
switch (item.getItemId()) {
|
||||
case R.id.action_settings:
|
||||
NavigationHelper.openSettings(requireContext());
|
||||
|
|
@ -201,18 +209,16 @@ public class ChannelFragment extends BaseListInfoFragment<ChannelInfo> {
|
|||
// Channel Subscription
|
||||
//////////////////////////////////////////////////////////////////////////*/
|
||||
|
||||
private static final int BUTTON_DEBOUNCE_INTERVAL = 100;
|
||||
|
||||
private void monitorSubscription(final ChannelInfo info) {
|
||||
final Consumer<Throwable> onError = (Throwable throwable) -> {
|
||||
animateView(headerSubscribeButton, false, 100);
|
||||
showSnackBarError(throwable, UserAction.SUBSCRIPTION,
|
||||
NewPipe.getNameOfService(currentInfo.getServiceId()),
|
||||
"Get subscription status",
|
||||
0);
|
||||
animateView(headerSubscribeButton, false, 100);
|
||||
showSnackBarError(throwable, UserAction.SUBSCRIPTION,
|
||||
NewPipe.getNameOfService(currentInfo.getServiceId()),
|
||||
"Get subscription status", 0);
|
||||
};
|
||||
|
||||
final Observable<List<SubscriptionEntity>> observable = subscriptionManager.subscriptionTable()
|
||||
final Observable<List<SubscriptionEntity>> observable = subscriptionManager
|
||||
.subscriptionTable()
|
||||
.getSubscriptionFlowable(info.getServiceId(), info.getUrl())
|
||||
.toObservable();
|
||||
|
||||
|
|
@ -221,17 +227,19 @@ public class ChannelFragment extends BaseListInfoFragment<ChannelInfo> {
|
|||
.subscribe(getSubscribeUpdateMonitor(info), onError));
|
||||
|
||||
disposables.add(observable
|
||||
// Some updates are very rapid (when calling the updateSubscription(info), for example)
|
||||
// so only update the UI for the latest emission ("sync" the subscribe button's state)
|
||||
// Some updates are very rapid
|
||||
// (for example when calling the updateSubscription(info))
|
||||
// so only update the UI for the latest emission
|
||||
// ("sync" the subscribe button's state)
|
||||
.debounce(100, TimeUnit.MILLISECONDS)
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribe((List<SubscriptionEntity> subscriptionEntities) ->
|
||||
updateSubscribeButton(!subscriptionEntities.isEmpty())
|
||||
, onError));
|
||||
updateSubscribeButton(!subscriptionEntities.isEmpty()), onError));
|
||||
|
||||
}
|
||||
|
||||
private Function<Object, Object> mapOnSubscribe(final SubscriptionEntity subscription, ChannelInfo info) {
|
||||
private Function<Object, Object> mapOnSubscribe(final SubscriptionEntity subscription,
|
||||
final ChannelInfo info) {
|
||||
return (@NonNull Object o) -> {
|
||||
subscriptionManager.insertSubscription(subscription, info);
|
||||
return o;
|
||||
|
|
@ -246,9 +254,13 @@ public class ChannelFragment extends BaseListInfoFragment<ChannelInfo> {
|
|||
}
|
||||
|
||||
private void updateSubscription(final ChannelInfo info) {
|
||||
if (DEBUG) Log.d(TAG, "updateSubscription() called with: info = [" + info + "]");
|
||||
if (DEBUG) {
|
||||
Log.d(TAG, "updateSubscription() called with: info = [" + info + "]");
|
||||
}
|
||||
final Action onComplete = () -> {
|
||||
if (DEBUG) Log.d(TAG, "Updated subscription: " + info.getUrl());
|
||||
if (DEBUG) {
|
||||
Log.d(TAG, "Updated subscription: " + info.getUrl());
|
||||
}
|
||||
};
|
||||
|
||||
final Consumer<Throwable> onError = (@NonNull Throwable throwable) ->
|
||||
|
|
@ -264,9 +276,12 @@ public class ChannelFragment extends BaseListInfoFragment<ChannelInfo> {
|
|||
.subscribe(onComplete, onError));
|
||||
}
|
||||
|
||||
private Disposable monitorSubscribeButton(final Button subscribeButton, final Function<Object, Object> action) {
|
||||
private Disposable monitorSubscribeButton(final Button subscribeButton,
|
||||
final Function<Object, Object> action) {
|
||||
final Consumer<Object> onNext = (@NonNull Object o) -> {
|
||||
if (DEBUG) Log.d(TAG, "Changed subscription status to this channel!");
|
||||
if (DEBUG) {
|
||||
Log.d(TAG, "Changed subscription status to this channel!");
|
||||
}
|
||||
};
|
||||
|
||||
final Consumer<Throwable> onError = (@NonNull Throwable throwable) ->
|
||||
|
|
@ -287,12 +302,18 @@ public class ChannelFragment extends BaseListInfoFragment<ChannelInfo> {
|
|||
|
||||
private Consumer<List<SubscriptionEntity>> getSubscribeUpdateMonitor(final ChannelInfo info) {
|
||||
return (List<SubscriptionEntity> subscriptionEntities) -> {
|
||||
if (DEBUG)
|
||||
Log.d(TAG, "subscriptionManager.subscriptionTable.doOnNext() called with: subscriptionEntities = [" + subscriptionEntities + "]");
|
||||
if (subscribeButtonMonitor != null) subscribeButtonMonitor.dispose();
|
||||
if (DEBUG) {
|
||||
Log.d(TAG, "subscriptionManager.subscriptionTable.doOnNext() called with: "
|
||||
+ "subscriptionEntities = [" + subscriptionEntities + "]");
|
||||
}
|
||||
if (subscribeButtonMonitor != null) {
|
||||
subscribeButtonMonitor.dispose();
|
||||
}
|
||||
|
||||
if (subscriptionEntities.isEmpty()) {
|
||||
if (DEBUG) Log.d(TAG, "No subscription to this channel!");
|
||||
if (DEBUG) {
|
||||
Log.d(TAG, "No subscription to this channel!");
|
||||
}
|
||||
SubscriptionEntity channel = new SubscriptionEntity();
|
||||
channel.setServiceId(info.getServiceId());
|
||||
channel.setUrl(info.getUrl());
|
||||
|
|
@ -300,34 +321,45 @@ public class ChannelFragment extends BaseListInfoFragment<ChannelInfo> {
|
|||
info.getAvatarUrl(),
|
||||
info.getDescription(),
|
||||
info.getSubscriberCount());
|
||||
subscribeButtonMonitor = monitorSubscribeButton(headerSubscribeButton, mapOnSubscribe(channel, info));
|
||||
subscribeButtonMonitor = monitorSubscribeButton(headerSubscribeButton,
|
||||
mapOnSubscribe(channel, info));
|
||||
} else {
|
||||
if (DEBUG) Log.d(TAG, "Found subscription to this channel!");
|
||||
if (DEBUG) {
|
||||
Log.d(TAG, "Found subscription to this channel!");
|
||||
}
|
||||
final SubscriptionEntity subscription = subscriptionEntities.get(0);
|
||||
subscribeButtonMonitor = monitorSubscribeButton(headerSubscribeButton, mapOnUnsubscribe(subscription));
|
||||
subscribeButtonMonitor = monitorSubscribeButton(headerSubscribeButton,
|
||||
mapOnUnsubscribe(subscription));
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private void updateSubscribeButton(boolean isSubscribed) {
|
||||
if (DEBUG) Log.d(TAG, "updateSubscribeButton() called with: isSubscribed = [" + isSubscribed + "]");
|
||||
private void updateSubscribeButton(final boolean isSubscribed) {
|
||||
if (DEBUG) {
|
||||
Log.d(TAG, "updateSubscribeButton() called with: "
|
||||
+ "isSubscribed = [" + isSubscribed + "]");
|
||||
}
|
||||
|
||||
boolean isButtonVisible = headerSubscribeButton.getVisibility() == View.VISIBLE;
|
||||
int backgroundDuration = isButtonVisible ? 300 : 0;
|
||||
int textDuration = isButtonVisible ? 200 : 0;
|
||||
|
||||
int subscribeBackground = ContextCompat.getColor(activity, R.color.subscribe_background_color);
|
||||
int subscribeBackground = ContextCompat
|
||||
.getColor(activity, R.color.subscribe_background_color);
|
||||
int subscribeText = ContextCompat.getColor(activity, R.color.subscribe_text_color);
|
||||
int subscribedBackground = ContextCompat.getColor(activity, R.color.subscribed_background_color);
|
||||
int subscribedBackground = ContextCompat
|
||||
.getColor(activity, R.color.subscribed_background_color);
|
||||
int subscribedText = ContextCompat.getColor(activity, R.color.subscribed_text_color);
|
||||
|
||||
if (!isSubscribed) {
|
||||
headerSubscribeButton.setText(R.string.subscribe_button_title);
|
||||
animateBackgroundColor(headerSubscribeButton, backgroundDuration, subscribedBackground, subscribeBackground);
|
||||
animateBackgroundColor(headerSubscribeButton, backgroundDuration, subscribedBackground,
|
||||
subscribeBackground);
|
||||
animateTextColor(headerSubscribeButton, textDuration, subscribedText, subscribeText);
|
||||
} else {
|
||||
headerSubscribeButton.setText(R.string.subscribed_button_title);
|
||||
animateBackgroundColor(headerSubscribeButton, backgroundDuration, subscribeBackground, subscribedBackground);
|
||||
animateBackgroundColor(headerSubscribeButton, backgroundDuration, subscribeBackground,
|
||||
subscribedBackground);
|
||||
animateTextColor(headerSubscribeButton, textDuration, subscribeText, subscribedText);
|
||||
}
|
||||
|
||||
|
|
@ -344,7 +376,7 @@ public class ChannelFragment extends BaseListInfoFragment<ChannelInfo> {
|
|||
}
|
||||
|
||||
@Override
|
||||
protected Single<ChannelInfo> loadResult(boolean forceLoad) {
|
||||
protected Single<ChannelInfo> loadResult(final boolean forceLoad) {
|
||||
return ExtractorHelper.getChannelInfo(serviceId, url, forceLoad);
|
||||
}
|
||||
|
||||
|
|
@ -356,47 +388,55 @@ public class ChannelFragment extends BaseListInfoFragment<ChannelInfo> {
|
|||
public void showLoading() {
|
||||
super.showLoading();
|
||||
|
||||
imageLoader.cancelDisplayTask(headerChannelBanner);
|
||||
imageLoader.cancelDisplayTask(headerAvatarView);
|
||||
IMAGE_LOADER.cancelDisplayTask(headerChannelBanner);
|
||||
IMAGE_LOADER.cancelDisplayTask(headerAvatarView);
|
||||
animateView(headerSubscribeButton, false, 100);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleResult(@NonNull ChannelInfo result) {
|
||||
public void handleResult(@NonNull final ChannelInfo result) {
|
||||
super.handleResult(result);
|
||||
|
||||
headerRootLayout.setVisibility(View.VISIBLE);
|
||||
imageLoader.displayImage(result.getBannerUrl(), headerChannelBanner,
|
||||
IMAGE_LOADER.displayImage(result.getBannerUrl(), headerChannelBanner,
|
||||
ImageDisplayConstants.DISPLAY_BANNER_OPTIONS);
|
||||
imageLoader.displayImage(result.getAvatarUrl(), headerAvatarView,
|
||||
IMAGE_LOADER.displayImage(result.getAvatarUrl(), headerAvatarView,
|
||||
ImageDisplayConstants.DISPLAY_AVATAR_OPTIONS);
|
||||
|
||||
headerSubscribersTextView.setVisibility(View.VISIBLE);
|
||||
if (result.getSubscriberCount() >= 0) {
|
||||
headerSubscribersTextView.setText(Localization.shortSubscriberCount(activity, result.getSubscriberCount()));
|
||||
headerSubscribersTextView.setText(Localization
|
||||
.shortSubscriberCount(activity, result.getSubscriberCount()));
|
||||
} else {
|
||||
headerSubscribersTextView.setText(R.string.subscribers_count_not_available);
|
||||
}
|
||||
|
||||
if (menuRssButton != null) menuRssButton.setVisible(!TextUtils.isEmpty(result.getFeedUrl()));
|
||||
if (menuRssButton != null) {
|
||||
menuRssButton.setVisible(!TextUtils.isEmpty(result.getFeedUrl()));
|
||||
}
|
||||
|
||||
playlistCtrl.setVisibility(View.VISIBLE);
|
||||
|
||||
if (!result.getErrors().isEmpty()) {
|
||||
showSnackBarError(result.getErrors(), UserAction.REQUESTED_CHANNEL, NewPipe.getNameOfService(result.getServiceId()), result.getUrl(), 0);
|
||||
showSnackBarError(result.getErrors(), UserAction.REQUESTED_CHANNEL,
|
||||
NewPipe.getNameOfService(result.getServiceId()), result.getUrl(), 0);
|
||||
}
|
||||
|
||||
if (disposables != null) disposables.clear();
|
||||
if (subscribeButtonMonitor != null) subscribeButtonMonitor.dispose();
|
||||
if (disposables != null) {
|
||||
disposables.clear();
|
||||
}
|
||||
if (subscribeButtonMonitor != null) {
|
||||
subscribeButtonMonitor.dispose();
|
||||
}
|
||||
updateSubscription(result);
|
||||
monitorSubscription(result);
|
||||
|
||||
headerPlayAllButton.setOnClickListener(
|
||||
view -> NavigationHelper.playOnMainPlayer(activity, getPlayQueue(), false));
|
||||
headerPopupButton.setOnClickListener(
|
||||
view -> NavigationHelper.playOnPopupPlayer(activity, getPlayQueue(), false));
|
||||
headerBackgroundButton.setOnClickListener(
|
||||
view -> NavigationHelper.playOnBackgroundPlayer(activity, getPlayQueue(), false));
|
||||
headerPlayAllButton.setOnClickListener(view -> NavigationHelper
|
||||
.playOnMainPlayer(activity, getPlayQueue(), false));
|
||||
headerPopupButton.setOnClickListener(view -> NavigationHelper
|
||||
.playOnPopupPlayer(activity, getPlayQueue(), false));
|
||||
headerBackgroundButton.setOnClickListener(view -> NavigationHelper
|
||||
.playOnBackgroundPlayer(activity, getPlayQueue(), false));
|
||||
}
|
||||
|
||||
private PlayQueue getPlayQueue() {
|
||||
|
|
@ -410,17 +450,12 @@ public class ChannelFragment extends BaseListInfoFragment<ChannelInfo> {
|
|||
streamItems.add((StreamInfoItem) i);
|
||||
}
|
||||
}
|
||||
return new ChannelPlayQueue(
|
||||
currentInfo.getServiceId(),
|
||||
currentInfo.getUrl(),
|
||||
currentInfo.getNextPageUrl(),
|
||||
streamItems,
|
||||
index
|
||||
);
|
||||
return new ChannelPlayQueue(currentInfo.getServiceId(), currentInfo.getUrl(),
|
||||
currentInfo.getNextPageUrl(), streamItems, index);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleNextItems(ListExtractor.InfoItemsPage result) {
|
||||
public void handleNextItems(final ListExtractor.InfoItemsPage result) {
|
||||
super.handleNextItems(result);
|
||||
|
||||
if (!result.getErrors().isEmpty()) {
|
||||
|
|
@ -437,8 +472,10 @@ public class ChannelFragment extends BaseListInfoFragment<ChannelInfo> {
|
|||
//////////////////////////////////////////////////////////////////////////*/
|
||||
|
||||
@Override
|
||||
protected boolean onError(Throwable exception) {
|
||||
if (super.onError(exception)) return true;
|
||||
protected boolean onError(final Throwable exception) {
|
||||
if (super.onError(exception)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
int errorId = exception instanceof ExtractionException
|
||||
? R.string.parsing_error : R.string.general_error;
|
||||
|
|
@ -454,8 +491,10 @@ public class ChannelFragment extends BaseListInfoFragment<ChannelInfo> {
|
|||
//////////////////////////////////////////////////////////////////////////*/
|
||||
|
||||
@Override
|
||||
public void setTitle(String title) {
|
||||
public void setTitle(final String title) {
|
||||
super.setTitle(title);
|
||||
if (!useAsFrontPage) headerTitleView.setText(title);
|
||||
if (!useAsFrontPage) {
|
||||
headerTitleView.setText(title);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,14 +2,15 @@ package org.schabi.newpipe.fragments.list.comments;
|
|||
|
||||
import android.content.Context;
|
||||
import android.os.Bundle;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.Menu;
|
||||
import android.view.MenuInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import org.schabi.newpipe.R;
|
||||
import org.schabi.newpipe.extractor.ListExtractor;
|
||||
import org.schabi.newpipe.extractor.NewPipe;
|
||||
|
|
@ -23,17 +24,12 @@ import io.reactivex.Single;
|
|||
import io.reactivex.disposables.CompositeDisposable;
|
||||
|
||||
public class CommentsFragment extends BaseListInfoFragment<CommentsInfo> {
|
||||
|
||||
private CompositeDisposable disposables = new CompositeDisposable();
|
||||
/*//////////////////////////////////////////////////////////////////////////
|
||||
// Views
|
||||
//////////////////////////////////////////////////////////////////////////*/
|
||||
|
||||
|
||||
|
||||
private boolean mIsVisibleToUser = false;
|
||||
|
||||
public static CommentsFragment getInstance(int serviceId, String url, String name) {
|
||||
public static CommentsFragment getInstance(final int serviceId, final String url,
|
||||
final String name) {
|
||||
CommentsFragment instance = new CommentsFragment();
|
||||
instance.setInitialData(serviceId, url, name);
|
||||
return instance;
|
||||
|
|
@ -44,28 +40,31 @@ public class CommentsFragment extends BaseListInfoFragment<CommentsInfo> {
|
|||
//////////////////////////////////////////////////////////////////////////*/
|
||||
|
||||
@Override
|
||||
public void setUserVisibleHint(boolean isVisibleToUser) {
|
||||
public void setUserVisibleHint(final boolean isVisibleToUser) {
|
||||
super.setUserVisibleHint(isVisibleToUser);
|
||||
mIsVisibleToUser = isVisibleToUser;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAttach(Context context) {
|
||||
public void onAttach(final Context context) {
|
||||
super.onAttach(context);
|
||||
}
|
||||
|
||||
@Override
|
||||
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
|
||||
public View onCreateView(@NonNull final LayoutInflater inflater,
|
||||
@Nullable final ViewGroup container,
|
||||
@Nullable final Bundle savedInstanceState) {
|
||||
return inflater.inflate(R.layout.fragment_comments, container, false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDestroy() {
|
||||
super.onDestroy();
|
||||
if (disposables != null) disposables.clear();
|
||||
if (disposables != null) {
|
||||
disposables.clear();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*//////////////////////////////////////////////////////////////////////////
|
||||
// Load and handle
|
||||
//////////////////////////////////////////////////////////////////////////*/
|
||||
|
|
@ -76,7 +75,7 @@ public class CommentsFragment extends BaseListInfoFragment<CommentsInfo> {
|
|||
}
|
||||
|
||||
@Override
|
||||
protected Single<CommentsInfo> loadResult(boolean forceLoad) {
|
||||
protected Single<CommentsInfo> loadResult(final boolean forceLoad) {
|
||||
return ExtractorHelper.getCommentsInfo(serviceId, url, forceLoad);
|
||||
}
|
||||
|
||||
|
|
@ -90,27 +89,28 @@ public class CommentsFragment extends BaseListInfoFragment<CommentsInfo> {
|
|||
}
|
||||
|
||||
@Override
|
||||
public void handleResult(@NonNull CommentsInfo result) {
|
||||
public void handleResult(@NonNull final CommentsInfo result) {
|
||||
super.handleResult(result);
|
||||
|
||||
AnimationUtils.slideUp(getView(),120, 150, 0.06f);
|
||||
AnimationUtils.slideUp(getView(), 120, 150, 0.06f);
|
||||
|
||||
if (!result.getErrors().isEmpty()) {
|
||||
showSnackBarError(result.getErrors(), UserAction.REQUESTED_COMMENTS, NewPipe.getNameOfService(result.getServiceId()), result.getUrl(), 0);
|
||||
showSnackBarError(result.getErrors(), UserAction.REQUESTED_COMMENTS,
|
||||
NewPipe.getNameOfService(result.getServiceId()), result.getUrl(), 0);
|
||||
}
|
||||
|
||||
if (disposables != null) disposables.clear();
|
||||
if (disposables != null) {
|
||||
disposables.clear();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleNextItems(ListExtractor.InfoItemsPage result) {
|
||||
public void handleNextItems(final ListExtractor.InfoItemsPage result) {
|
||||
super.handleNextItems(result);
|
||||
|
||||
if (!result.getErrors().isEmpty()) {
|
||||
showSnackBarError(result.getErrors(),
|
||||
UserAction.REQUESTED_COMMENTS,
|
||||
NewPipe.getNameOfService(serviceId),
|
||||
"Get next page of: " + url,
|
||||
showSnackBarError(result.getErrors(), UserAction.REQUESTED_COMMENTS,
|
||||
NewPipe.getNameOfService(serviceId), "Get next page of: " + url,
|
||||
R.string.general_error);
|
||||
}
|
||||
}
|
||||
|
|
@ -120,11 +120,14 @@ public class CommentsFragment extends BaseListInfoFragment<CommentsInfo> {
|
|||
//////////////////////////////////////////////////////////////////////////*/
|
||||
|
||||
@Override
|
||||
protected boolean onError(Throwable exception) {
|
||||
if (super.onError(exception)) return true;
|
||||
protected boolean onError(final Throwable exception) {
|
||||
if (super.onError(exception)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
hideLoading();
|
||||
showSnackBarError(exception, UserAction.REQUESTED_COMMENTS, NewPipe.getNameOfService(serviceId), url, R.string.error_unable_to_load_comments);
|
||||
showSnackBarError(exception, UserAction.REQUESTED_COMMENTS,
|
||||
NewPipe.getNameOfService(serviceId), url, R.string.error_unable_to_load_comments);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
@ -133,14 +136,10 @@ public class CommentsFragment extends BaseListInfoFragment<CommentsInfo> {
|
|||
//////////////////////////////////////////////////////////////////////////*/
|
||||
|
||||
@Override
|
||||
public void setTitle(String title) {
|
||||
return;
|
||||
}
|
||||
public void setTitle(final String title) { }
|
||||
|
||||
@Override
|
||||
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
|
||||
return;
|
||||
}
|
||||
public void onCreateOptionsMenu(final Menu menu, final MenuInflater inflater) { }
|
||||
|
||||
@Override
|
||||
protected boolean isGridLayout() {
|
||||
|
|
|
|||
|
|
@ -10,9 +10,8 @@ import org.schabi.newpipe.util.KioskTranslator;
|
|||
import org.schabi.newpipe.util.ServiceHelper;
|
||||
|
||||
public class DefaultKioskFragment extends KioskFragment {
|
||||
|
||||
@Override
|
||||
public void onCreate(Bundle savedInstanceState) {
|
||||
public void onCreate(final Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
|
||||
if (serviceId < 0) {
|
||||
|
|
@ -25,7 +24,9 @@ public class DefaultKioskFragment extends KioskFragment {
|
|||
super.onResume();
|
||||
|
||||
if (serviceId != ServiceHelper.getSelectedServiceId(requireContext())) {
|
||||
if (currentWorker != null) currentWorker.dispose();
|
||||
if (currentWorker != null) {
|
||||
currentWorker.dispose();
|
||||
}
|
||||
updateSelectedDefaultKiosk();
|
||||
reloadContent();
|
||||
}
|
||||
|
|
@ -45,7 +46,8 @@ public class DefaultKioskFragment extends KioskFragment {
|
|||
currentInfo = null;
|
||||
currentNextPageUrl = null;
|
||||
} catch (ExtractionException e) {
|
||||
onUnrecoverableError(e, UserAction.REQUESTED_KIOSK, "none", "Loading default kiosk from selected service", 0);
|
||||
onUnrecoverableError(e, UserAction.REQUESTED_KIOSK, "none",
|
||||
"Loading default kiosk from selected service", 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,17 +1,16 @@
|
|||
package org.schabi.newpipe.fragments.list.kiosk;
|
||||
|
||||
import android.os.Bundle;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.appcompat.app.ActionBar;
|
||||
|
||||
import android.preference.PreferenceManager;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.Menu;
|
||||
import android.view.MenuInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.appcompat.app.ActionBar;
|
||||
|
||||
import org.schabi.newpipe.R;
|
||||
import org.schabi.newpipe.extractor.ListExtractor;
|
||||
import org.schabi.newpipe.extractor.NewPipe;
|
||||
|
|
@ -33,45 +32,45 @@ import static org.schabi.newpipe.util.AnimationUtils.animateView;
|
|||
|
||||
/**
|
||||
* Created by Christian Schabesberger on 23.09.17.
|
||||
*
|
||||
* <p>
|
||||
* Copyright (C) Christian Schabesberger 2017 <chris.schabesberger@mailbox.org>
|
||||
* KioskFragment.java is part of NewPipe.
|
||||
*
|
||||
* </p>
|
||||
* <p>
|
||||
* NewPipe is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* </p>
|
||||
* <p>
|
||||
* NewPipe is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* </p>
|
||||
* <p>
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with NewPipe. If not, see <http://www.gnu.org/licenses/>.
|
||||
* along with NewPipe. If not, see <http://www.gnu.org/licenses/>.
|
||||
* </p>
|
||||
*/
|
||||
|
||||
public class KioskFragment extends BaseListInfoFragment<KioskInfo> {
|
||||
|
||||
@State
|
||||
protected String kioskId = "";
|
||||
protected String kioskTranslatedName;
|
||||
String kioskId = "";
|
||||
String kioskTranslatedName;
|
||||
@State
|
||||
protected ContentCountry contentCountry;
|
||||
|
||||
ContentCountry contentCountry;
|
||||
|
||||
/*//////////////////////////////////////////////////////////////////////////
|
||||
// Views
|
||||
//////////////////////////////////////////////////////////////////////////*/
|
||||
|
||||
public static KioskFragment getInstance(int serviceId)
|
||||
throws ExtractionException {
|
||||
public static KioskFragment getInstance(final int serviceId) throws ExtractionException {
|
||||
return getInstance(serviceId, NewPipe.getService(serviceId)
|
||||
.getKioskList()
|
||||
.getDefaultKioskId());
|
||||
.getKioskList().getDefaultKioskId());
|
||||
}
|
||||
|
||||
public static KioskFragment getInstance(int serviceId, String kioskId)
|
||||
public static KioskFragment getInstance(final int serviceId, final String kioskId)
|
||||
throws ExtractionException {
|
||||
KioskFragment instance = new KioskFragment();
|
||||
StreamingService service = NewPipe.getService(serviceId);
|
||||
|
|
@ -88,7 +87,7 @@ public class KioskFragment extends BaseListInfoFragment<KioskInfo> {
|
|||
//////////////////////////////////////////////////////////////////////////*/
|
||||
|
||||
@Override
|
||||
public void onCreate(Bundle savedInstanceState) {
|
||||
public void onCreate(final Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
|
||||
kioskTranslatedName = KioskTranslator.getTranslatedKioskName(kioskId, activity);
|
||||
|
|
@ -97,9 +96,9 @@ public class KioskFragment extends BaseListInfoFragment<KioskInfo> {
|
|||
}
|
||||
|
||||
@Override
|
||||
public void setUserVisibleHint(boolean isVisibleToUser) {
|
||||
public void setUserVisibleHint(final boolean isVisibleToUser) {
|
||||
super.setUserVisibleHint(isVisibleToUser);
|
||||
if(useAsFrontPage && isVisibleToUser && activity != null) {
|
||||
if (useAsFrontPage && isVisibleToUser && activity != null) {
|
||||
try {
|
||||
setTitle(kioskTranslatedName);
|
||||
} catch (Exception e) {
|
||||
|
|
@ -111,7 +110,9 @@ public class KioskFragment extends BaseListInfoFragment<KioskInfo> {
|
|||
}
|
||||
|
||||
@Override
|
||||
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
|
||||
public View onCreateView(@NonNull final LayoutInflater inflater,
|
||||
@Nullable final ViewGroup container,
|
||||
@Nullable final Bundle savedInstanceState) {
|
||||
return inflater.inflate(R.layout.fragment_kiosk, container, false);
|
||||
}
|
||||
|
||||
|
|
@ -129,7 +130,7 @@ public class KioskFragment extends BaseListInfoFragment<KioskInfo> {
|
|||
//////////////////////////////////////////////////////////////////////////*/
|
||||
|
||||
@Override
|
||||
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
|
||||
public void onCreateOptionsMenu(final Menu menu, final MenuInflater inflater) {
|
||||
super.onCreateOptionsMenu(menu, inflater);
|
||||
ActionBar supportActionBar = activity.getSupportActionBar();
|
||||
if (supportActionBar != null && useAsFrontPage) {
|
||||
|
|
@ -142,18 +143,14 @@ public class KioskFragment extends BaseListInfoFragment<KioskInfo> {
|
|||
//////////////////////////////////////////////////////////////////////////*/
|
||||
|
||||
@Override
|
||||
public Single<KioskInfo> loadResult(boolean forceReload) {
|
||||
public Single<KioskInfo> loadResult(final boolean forceReload) {
|
||||
contentCountry = Localization.getPreferredContentCountry(requireContext());
|
||||
return ExtractorHelper.getKioskInfo(serviceId,
|
||||
url,
|
||||
forceReload);
|
||||
return ExtractorHelper.getKioskInfo(serviceId, url, forceReload);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Single<ListExtractor.InfoItemsPage> loadMoreItemsLogic() {
|
||||
return ExtractorHelper.getMoreKioskItems(serviceId,
|
||||
url,
|
||||
currentNextPageUrl);
|
||||
return ExtractorHelper.getMoreKioskItems(serviceId, url, currentNextPageUrl);
|
||||
}
|
||||
|
||||
/*//////////////////////////////////////////////////////////////////////////
|
||||
|
|
@ -181,13 +178,13 @@ public class KioskFragment extends BaseListInfoFragment<KioskInfo> {
|
|||
}
|
||||
|
||||
@Override
|
||||
public void handleNextItems(ListExtractor.InfoItemsPage result) {
|
||||
public void handleNextItems(final ListExtractor.InfoItemsPage result) {
|
||||
super.handleNextItems(result);
|
||||
|
||||
if (!result.getErrors().isEmpty()) {
|
||||
showSnackBarError(result.getErrors(),
|
||||
UserAction.REQUESTED_PLAYLIST, NewPipe.getNameOfService(serviceId)
|
||||
, "Get next page of: " + url, 0);
|
||||
UserAction.REQUESTED_PLAYLIST, NewPipe.getNameOfService(serviceId),
|
||||
"Get next page of: " + url, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,9 +3,6 @@ package org.schabi.newpipe.fragments.list.playlist;
|
|||
import android.app.Activity;
|
||||
import android.content.Context;
|
||||
import android.os.Bundle;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.appcompat.app.AppCompatActivity;
|
||||
import android.text.TextUtils;
|
||||
import android.util.Log;
|
||||
import android.view.LayoutInflater;
|
||||
|
|
@ -17,6 +14,10 @@ import android.view.ViewGroup;
|
|||
import android.widget.ImageView;
|
||||
import android.widget.TextView;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.appcompat.app.AppCompatActivity;
|
||||
|
||||
import org.reactivestreams.Subscriber;
|
||||
import org.reactivestreams.Subscription;
|
||||
import org.schabi.newpipe.NewPipeDatabase;
|
||||
|
|
@ -57,7 +58,6 @@ import io.reactivex.disposables.Disposables;
|
|||
import static org.schabi.newpipe.util.AnimationUtils.animateView;
|
||||
|
||||
public class PlaylistFragment extends BaseListInfoFragment<PlaylistInfo> {
|
||||
|
||||
private CompositeDisposable disposables;
|
||||
private Subscription bookmarkReactor;
|
||||
private AtomicBoolean isBookmarkButtonReady;
|
||||
|
|
@ -82,7 +82,8 @@ public class PlaylistFragment extends BaseListInfoFragment<PlaylistInfo> {
|
|||
|
||||
private MenuItem playlistBookmarkButton;
|
||||
|
||||
public static PlaylistFragment getInstance(int serviceId, String url, String name) {
|
||||
public static PlaylistFragment getInstance(final int serviceId, final String url,
|
||||
final String name) {
|
||||
PlaylistFragment instance = new PlaylistFragment();
|
||||
instance.setInitialData(serviceId, url, name);
|
||||
return instance;
|
||||
|
|
@ -93,17 +94,18 @@ public class PlaylistFragment extends BaseListInfoFragment<PlaylistInfo> {
|
|||
//////////////////////////////////////////////////////////////////////////*/
|
||||
|
||||
@Override
|
||||
public void onCreate(Bundle savedInstanceState) {
|
||||
public void onCreate(final Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
disposables = new CompositeDisposable();
|
||||
isBookmarkButtonReady = new AtomicBoolean(false);
|
||||
remotePlaylistManager = new RemotePlaylistManager(NewPipeDatabase.getInstance(
|
||||
requireContext()));
|
||||
remotePlaylistManager = new RemotePlaylistManager(NewPipeDatabase
|
||||
.getInstance(requireContext()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container,
|
||||
@Nullable Bundle savedInstanceState) {
|
||||
public View onCreateView(@NonNull final LayoutInflater inflater,
|
||||
@Nullable final ViewGroup container,
|
||||
@Nullable final Bundle savedInstanceState) {
|
||||
return inflater.inflate(R.layout.fragment_playlist, container, false);
|
||||
}
|
||||
|
||||
|
|
@ -112,7 +114,8 @@ public class PlaylistFragment extends BaseListInfoFragment<PlaylistInfo> {
|
|||
//////////////////////////////////////////////////////////////////////////*/
|
||||
|
||||
protected View getListHeader() {
|
||||
headerRootLayout = activity.getLayoutInflater().inflate(R.layout.playlist_header, itemsList, false);
|
||||
headerRootLayout = activity.getLayoutInflater()
|
||||
.inflate(R.layout.playlist_header, itemsList, false);
|
||||
headerTitleView = headerRootLayout.findViewById(R.id.playlist_title_view);
|
||||
headerUploaderLayout = headerRootLayout.findViewById(R.id.uploader_layout);
|
||||
headerUploaderName = headerRootLayout.findViewById(R.id.uploader_name);
|
||||
|
|
@ -129,21 +132,23 @@ public class PlaylistFragment extends BaseListInfoFragment<PlaylistInfo> {
|
|||
}
|
||||
|
||||
@Override
|
||||
protected void initViews(View rootView, Bundle savedInstanceState) {
|
||||
protected void initViews(final View rootView, final Bundle savedInstanceState) {
|
||||
super.initViews(rootView, savedInstanceState);
|
||||
|
||||
infoListAdapter.useMiniItemVariants(true);
|
||||
infoListAdapter.setUseMiniVariant(true);
|
||||
}
|
||||
|
||||
private PlayQueue getPlayQueueStartingAt(StreamInfoItem infoItem) {
|
||||
private PlayQueue getPlayQueueStartingAt(final StreamInfoItem infoItem) {
|
||||
return getPlayQueue(Math.max(infoListAdapter.getItemsList().indexOf(infoItem), 0));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void showStreamDialog(StreamInfoItem item) {
|
||||
protected void showStreamDialog(final StreamInfoItem item) {
|
||||
final Context context = getContext();
|
||||
final Activity activity = getActivity();
|
||||
if (context == null || context.getResources() == null || activity == null) return;
|
||||
if (context == null || context.getResources() == null || activity == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (item.getStreamType() == StreamType.AUDIO_STREAM) {
|
||||
StreamDialogEntry.setEnabledEntries(
|
||||
|
|
@ -160,21 +165,25 @@ public class PlaylistFragment extends BaseListInfoFragment<PlaylistInfo> {
|
|||
StreamDialogEntry.append_playlist,
|
||||
StreamDialogEntry.share);
|
||||
|
||||
StreamDialogEntry.start_here_on_popup.setCustomAction(
|
||||
(fragment, infoItem) -> NavigationHelper.playOnPopupPlayer(context, getPlayQueueStartingAt(infoItem), true));
|
||||
StreamDialogEntry.start_here_on_popup.setCustomAction((fragment, infoItem) ->
|
||||
NavigationHelper.playOnPopupPlayer(context,
|
||||
getPlayQueueStartingAt(infoItem), true));
|
||||
}
|
||||
|
||||
StreamDialogEntry.start_here_on_background.setCustomAction(
|
||||
(fragment, infoItem) -> NavigationHelper.playOnBackgroundPlayer(context, getPlayQueueStartingAt(infoItem), true));
|
||||
StreamDialogEntry.start_here_on_background.setCustomAction((fragment, infoItem) ->
|
||||
NavigationHelper.playOnBackgroundPlayer(context,
|
||||
getPlayQueueStartingAt(infoItem), true));
|
||||
|
||||
new InfoItemDialog(activity, item, StreamDialogEntry.getCommands(context), (dialog, which) ->
|
||||
StreamDialogEntry.clickOn(which, this, item)).show();
|
||||
new InfoItemDialog(activity, item, StreamDialogEntry.getCommands(context),
|
||||
(dialog, which) -> StreamDialogEntry.clickOn(which, this, item)).show();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
|
||||
if (DEBUG) Log.d(TAG, "onCreateOptionsMenu() called with: menu = [" + menu +
|
||||
"], inflater = [" + inflater + "]");
|
||||
public void onCreateOptionsMenu(final Menu menu, final MenuInflater inflater) {
|
||||
if (DEBUG) {
|
||||
Log.d(TAG, "onCreateOptionsMenu() called with: "
|
||||
+ "menu = [" + menu + "], inflater = [" + inflater + "]");
|
||||
}
|
||||
super.onCreateOptionsMenu(menu, inflater);
|
||||
inflater.inflate(R.menu.menu_playlist, menu);
|
||||
|
||||
|
|
@ -185,10 +194,16 @@ public class PlaylistFragment extends BaseListInfoFragment<PlaylistInfo> {
|
|||
@Override
|
||||
public void onDestroyView() {
|
||||
super.onDestroyView();
|
||||
if (isBookmarkButtonReady != null) isBookmarkButtonReady.set(false);
|
||||
if (isBookmarkButtonReady != null) {
|
||||
isBookmarkButtonReady.set(false);
|
||||
}
|
||||
|
||||
if (disposables != null) disposables.clear();
|
||||
if (bookmarkReactor != null) bookmarkReactor.cancel();
|
||||
if (disposables != null) {
|
||||
disposables.clear();
|
||||
}
|
||||
if (bookmarkReactor != null) {
|
||||
bookmarkReactor.cancel();
|
||||
}
|
||||
|
||||
bookmarkReactor = null;
|
||||
}
|
||||
|
|
@ -197,7 +212,9 @@ public class PlaylistFragment extends BaseListInfoFragment<PlaylistInfo> {
|
|||
public void onDestroy() {
|
||||
super.onDestroy();
|
||||
|
||||
if (disposables != null) disposables.dispose();
|
||||
if (disposables != null) {
|
||||
disposables.dispose();
|
||||
}
|
||||
|
||||
disposables = null;
|
||||
remotePlaylistManager = null;
|
||||
|
|
@ -215,12 +232,12 @@ public class PlaylistFragment extends BaseListInfoFragment<PlaylistInfo> {
|
|||
}
|
||||
|
||||
@Override
|
||||
protected Single<PlaylistInfo> loadResult(boolean forceLoad) {
|
||||
protected Single<PlaylistInfo> loadResult(final boolean forceLoad) {
|
||||
return ExtractorHelper.getPlaylistInfo(serviceId, url, forceLoad);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onOptionsItemSelected(MenuItem item) {
|
||||
public boolean onOptionsItemSelected(final MenuItem item) {
|
||||
switch (item.getItemId()) {
|
||||
case R.id.action_settings:
|
||||
NavigationHelper.openSettings(requireContext());
|
||||
|
|
@ -251,7 +268,7 @@ public class PlaylistFragment extends BaseListInfoFragment<PlaylistInfo> {
|
|||
animateView(headerRootLayout, false, 200);
|
||||
animateView(itemsList, false, 100);
|
||||
|
||||
imageLoader.cancelDisplayTask(headerUploaderAvatar);
|
||||
IMAGE_LOADER.cancelDisplayTask(headerUploaderAvatar);
|
||||
animateView(headerUploaderLayout, false, 200);
|
||||
}
|
||||
|
||||
|
|
@ -262,7 +279,8 @@ public class PlaylistFragment extends BaseListInfoFragment<PlaylistInfo> {
|
|||
animateView(headerRootLayout, true, 100);
|
||||
animateView(headerUploaderLayout, true, 300);
|
||||
headerUploaderLayout.setOnClickListener(null);
|
||||
if (!TextUtils.isEmpty(result.getUploaderName())) { // If we have an uploader : Put them into the ui
|
||||
// If we have an uploader put them into the UI
|
||||
if (!TextUtils.isEmpty(result.getUploaderName())) {
|
||||
headerUploaderName.setText(result.getUploaderName());
|
||||
if (!TextUtils.isEmpty(result.getUploaderUrl())) {
|
||||
headerUploaderLayout.setOnClickListener(v -> {
|
||||
|
|
@ -276,19 +294,20 @@ public class PlaylistFragment extends BaseListInfoFragment<PlaylistInfo> {
|
|||
}
|
||||
});
|
||||
}
|
||||
} else { // Else : say we have no uploader
|
||||
} else { // Otherwise say we have no uploader
|
||||
headerUploaderName.setText(R.string.playlist_no_uploader);
|
||||
}
|
||||
|
||||
playlistCtrl.setVisibility(View.VISIBLE);
|
||||
|
||||
imageLoader.displayImage(result.getUploaderAvatarUrl(), headerUploaderAvatar,
|
||||
IMAGE_LOADER.displayImage(result.getUploaderAvatarUrl(), headerUploaderAvatar,
|
||||
ImageDisplayConstants.DISPLAY_AVATAR_OPTIONS);
|
||||
headerStreamCount.setText(getResources().getQuantityString(R.plurals.videos,
|
||||
(int) result.getStreamCount(), (int) result.getStreamCount()));
|
||||
|
||||
if (!result.getErrors().isEmpty()) {
|
||||
showSnackBarError(result.getErrors(), UserAction.REQUESTED_PLAYLIST, NewPipe.getNameOfService(result.getServiceId()), result.getUrl(), 0);
|
||||
showSnackBarError(result.getErrors(), UserAction.REQUESTED_PLAYLIST,
|
||||
NewPipe.getNameOfService(result.getServiceId()), result.getUrl(), 0);
|
||||
}
|
||||
|
||||
remotePlaylistManager.getPlaylist(result)
|
||||
|
|
@ -321,8 +340,8 @@ public class PlaylistFragment extends BaseListInfoFragment<PlaylistInfo> {
|
|||
|
||||
private PlayQueue getPlayQueue(final int index) {
|
||||
final List<StreamInfoItem> infoItems = new ArrayList<>();
|
||||
for(InfoItem i : infoListAdapter.getItemsList()) {
|
||||
if(i instanceof StreamInfoItem) {
|
||||
for (InfoItem i : infoListAdapter.getItemsList()) {
|
||||
if (i instanceof StreamInfoItem) {
|
||||
infoItems.add((StreamInfoItem) i);
|
||||
}
|
||||
}
|
||||
|
|
@ -336,12 +355,12 @@ public class PlaylistFragment extends BaseListInfoFragment<PlaylistInfo> {
|
|||
}
|
||||
|
||||
@Override
|
||||
public void handleNextItems(ListExtractor.InfoItemsPage result) {
|
||||
public void handleNextItems(final ListExtractor.InfoItemsPage result) {
|
||||
super.handleNextItems(result);
|
||||
|
||||
if (!result.getErrors().isEmpty()) {
|
||||
showSnackBarError(result.getErrors(), UserAction.REQUESTED_PLAYLIST, NewPipe.getNameOfService(serviceId)
|
||||
, "Get next page of: " + url, 0);
|
||||
showSnackBarError(result.getErrors(), UserAction.REQUESTED_PLAYLIST,
|
||||
NewPipe.getNameOfService(serviceId), "Get next page of: " + url, 0);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -350,15 +369,15 @@ public class PlaylistFragment extends BaseListInfoFragment<PlaylistInfo> {
|
|||
//////////////////////////////////////////////////////////////////////////*/
|
||||
|
||||
@Override
|
||||
protected boolean onError(Throwable exception) {
|
||||
if (super.onError(exception)) return true;
|
||||
protected boolean onError(final Throwable exception) {
|
||||
if (super.onError(exception)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
int errorId = exception instanceof ExtractionException ? R.string.parsing_error : R.string.general_error;
|
||||
onUnrecoverableError(exception,
|
||||
UserAction.REQUESTED_PLAYLIST,
|
||||
NewPipe.getNameOfService(serviceId),
|
||||
url,
|
||||
errorId);
|
||||
int errorId = exception instanceof ExtractionException
|
||||
? R.string.parsing_error : R.string.general_error;
|
||||
onUnrecoverableError(exception, UserAction.REQUESTED_PLAYLIST,
|
||||
NewPipe.getNameOfService(serviceId), url, errorId);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
@ -366,13 +385,18 @@ public class PlaylistFragment extends BaseListInfoFragment<PlaylistInfo> {
|
|||
// Utils
|
||||
//////////////////////////////////////////////////////////////////////////*/
|
||||
|
||||
private Flowable<Integer> getUpdateProcessor(@NonNull List<PlaylistRemoteEntity> playlists,
|
||||
@NonNull PlaylistInfo result) {
|
||||
private Flowable<Integer> getUpdateProcessor(
|
||||
@NonNull final List<PlaylistRemoteEntity> playlists,
|
||||
@NonNull final PlaylistInfo result) {
|
||||
final Flowable<Integer> noItemToUpdate = Flowable.just(/*noItemToUpdate=*/-1);
|
||||
if (playlists.isEmpty()) return noItemToUpdate;
|
||||
if (playlists.isEmpty()) {
|
||||
return noItemToUpdate;
|
||||
}
|
||||
|
||||
final PlaylistRemoteEntity playlistEntity = playlists.get(0);
|
||||
if (playlistEntity.isIdenticalTo(result)) return noItemToUpdate;
|
||||
final PlaylistRemoteEntity playlistRemoteEntity = playlists.get(0);
|
||||
if (playlistRemoteEntity.isIdenticalTo(result)) {
|
||||
return noItemToUpdate;
|
||||
}
|
||||
|
||||
return remotePlaylistManager.onUpdate(playlists.get(0).getUid(), result).toFlowable();
|
||||
}
|
||||
|
|
@ -380,56 +404,59 @@ public class PlaylistFragment extends BaseListInfoFragment<PlaylistInfo> {
|
|||
private Subscriber<List<PlaylistRemoteEntity>> getPlaylistBookmarkSubscriber() {
|
||||
return new Subscriber<List<PlaylistRemoteEntity>>() {
|
||||
@Override
|
||||
public void onSubscribe(Subscription s) {
|
||||
if (bookmarkReactor != null) bookmarkReactor.cancel();
|
||||
public void onSubscribe(final Subscription s) {
|
||||
if (bookmarkReactor != null) {
|
||||
bookmarkReactor.cancel();
|
||||
}
|
||||
bookmarkReactor = s;
|
||||
bookmarkReactor.request(1);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onNext(List<PlaylistRemoteEntity> playlist) {
|
||||
public void onNext(final List<PlaylistRemoteEntity> playlist) {
|
||||
playlistEntity = playlist.isEmpty() ? null : playlist.get(0);
|
||||
|
||||
updateBookmarkButtons();
|
||||
isBookmarkButtonReady.set(true);
|
||||
|
||||
if (bookmarkReactor != null) bookmarkReactor.request(1);
|
||||
if (bookmarkReactor != null) {
|
||||
bookmarkReactor.request(1);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onError(Throwable t) {
|
||||
public void onError(final Throwable t) {
|
||||
PlaylistFragment.this.onError(t);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onComplete() {
|
||||
|
||||
}
|
||||
public void onComplete() { }
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setTitle(String title) {
|
||||
public void setTitle(final String title) {
|
||||
super.setTitle(title);
|
||||
headerTitleView.setText(title);
|
||||
}
|
||||
|
||||
private void onBookmarkClicked() {
|
||||
if (isBookmarkButtonReady == null || !isBookmarkButtonReady.get() ||
|
||||
remotePlaylistManager == null)
|
||||
if (isBookmarkButtonReady == null || !isBookmarkButtonReady.get()
|
||||
|| remotePlaylistManager == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
final Disposable action;
|
||||
|
||||
if (currentInfo != null && playlistEntity == null) {
|
||||
action = remotePlaylistManager.onBookmark(currentInfo)
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribe(ignored -> {/* Do nothing */}, this::onError);
|
||||
.subscribe(ignored -> { /* Do nothing */ }, this::onError);
|
||||
} else if (playlistEntity != null) {
|
||||
action = remotePlaylistManager.deletePlaylist(playlistEntity.getUid())
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.doFinally(() -> playlistEntity = null)
|
||||
.subscribe(ignored -> {/* Do nothing */}, this::onError);
|
||||
.subscribe(ignored -> { /* Do nothing */ }, this::onError);
|
||||
} else {
|
||||
action = Disposables.empty();
|
||||
}
|
||||
|
|
@ -438,13 +465,15 @@ public class PlaylistFragment extends BaseListInfoFragment<PlaylistInfo> {
|
|||
}
|
||||
|
||||
private void updateBookmarkButtons() {
|
||||
if (playlistBookmarkButton == null || activity == null) return;
|
||||
if (playlistBookmarkButton == null || activity == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
final int iconAttr = playlistEntity == null ?
|
||||
R.attr.ic_playlist_add : R.attr.ic_playlist_check;
|
||||
final int iconAttr = playlistEntity == null
|
||||
? R.attr.ic_playlist_add : R.attr.ic_playlist_check;
|
||||
|
||||
final int titleRes = playlistEntity == null ?
|
||||
R.string.bookmark_playlist : R.string.unbookmark_playlist;
|
||||
final int titleRes = playlistEntity == null
|
||||
? R.string.bookmark_playlist : R.string.unbookmark_playlist;
|
||||
|
||||
playlistBookmarkButton.setIcon(ThemeHelper.resolveResourceIdFromAttr(activity, iconAttr));
|
||||
playlistBookmarkButton.setTitle(titleRes);
|
||||
|
|
|
|||
|
|
@ -6,13 +6,6 @@ import android.content.Intent;
|
|||
import android.content.SharedPreferences;
|
||||
import android.os.Bundle;
|
||||
import android.preference.PreferenceManager;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.appcompat.app.ActionBar;
|
||||
import androidx.appcompat.app.AlertDialog;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
import androidx.appcompat.widget.TooltipCompat;
|
||||
import androidx.recyclerview.widget.ItemTouchHelper;
|
||||
import android.text.Editable;
|
||||
import android.text.TextUtils;
|
||||
import android.text.TextWatcher;
|
||||
|
|
@ -30,6 +23,14 @@ import android.view.inputmethod.InputMethodManager;
|
|||
import android.widget.EditText;
|
||||
import android.widget.TextView;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.appcompat.app.ActionBar;
|
||||
import androidx.appcompat.app.AlertDialog;
|
||||
import androidx.appcompat.widget.TooltipCompat;
|
||||
import androidx.recyclerview.widget.ItemTouchHelper;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
|
||||
import org.schabi.newpipe.R;
|
||||
import org.schabi.newpipe.ReCaptchaActivity;
|
||||
import org.schabi.newpipe.database.history.model.SearchHistoryEntry;
|
||||
|
|
@ -77,10 +78,8 @@ import static androidx.recyclerview.widget.ItemTouchHelper.Callback.makeMovement
|
|||
import static java.util.Arrays.asList;
|
||||
import static org.schabi.newpipe.util.AnimationUtils.animateView;
|
||||
|
||||
public class SearchFragment
|
||||
extends BaseListFragment<SearchInfo, ListExtractor.InfoItemsPage>
|
||||
public class SearchFragment extends BaseListFragment<SearchInfo, ListExtractor.InfoItemsPage>
|
||||
implements BackPressable {
|
||||
|
||||
/*//////////////////////////////////////////////////////////////////////////
|
||||
// Search
|
||||
//////////////////////////////////////////////////////////////////////////*/
|
||||
|
|
@ -92,35 +91,38 @@ public class SearchFragment
|
|||
private static final int THRESHOLD_NETWORK_SUGGESTION = 1;
|
||||
|
||||
/**
|
||||
* How much time have to pass without emitting a item (i.e. the user stop typing) to fetch/show the suggestions, in milliseconds.
|
||||
* How much time have to pass without emitting a item (i.e. the user stop typing)
|
||||
* to fetch/show the suggestions, in milliseconds.
|
||||
*/
|
||||
private static final int SUGGESTIONS_DEBOUNCE = 120; //ms
|
||||
private final PublishSubject<String> suggestionPublisher = PublishSubject.create();
|
||||
|
||||
@State
|
||||
protected int filterItemCheckedId = -1;
|
||||
int filterItemCheckedId = -1;
|
||||
|
||||
@State
|
||||
protected int serviceId = Constants.NO_SERVICE_ID;
|
||||
|
||||
// this three represet the current search query
|
||||
|
||||
// these three represents the current search query
|
||||
@State
|
||||
protected String searchString;
|
||||
String searchString;
|
||||
|
||||
/**
|
||||
* No content filter should add like contentfilter = all
|
||||
* No content filter should add like contentFilter = all
|
||||
* be aware of this when implementing an extractor.
|
||||
*/
|
||||
@State
|
||||
protected String[] contentFilter = new String[0];
|
||||
String[] contentFilter = new String[0];
|
||||
|
||||
@State
|
||||
protected String sortFilter;
|
||||
|
||||
// these represtent the last search
|
||||
String sortFilter;
|
||||
|
||||
// these represents the last search
|
||||
@State
|
||||
protected String lastSearchedString;
|
||||
|
||||
String lastSearchedString;
|
||||
|
||||
@State
|
||||
protected boolean wasSearchFocused = false;
|
||||
boolean wasSearchFocused = false;
|
||||
|
||||
private Map<Integer, String> menuItemToFilterName;
|
||||
private StreamingService service;
|
||||
|
|
@ -129,7 +131,6 @@ public class SearchFragment
|
|||
private String contentCountry;
|
||||
private boolean isSuggestionsEnabled = true;
|
||||
|
||||
private final PublishSubject<String> suggestionPublisher = PublishSubject.create();
|
||||
private Disposable searchDisposable;
|
||||
private Disposable suggestionDisposable;
|
||||
private final CompositeDisposable disposables = new CompositeDisposable();
|
||||
|
|
@ -150,7 +151,9 @@ public class SearchFragment
|
|||
|
||||
/*////////////////////////////////////////////////////////////////////////*/
|
||||
|
||||
public static SearchFragment getInstance(int serviceId, String searchString) {
|
||||
private TextWatcher textWatcher;
|
||||
|
||||
public static SearchFragment getInstance(final int serviceId, final String searchString) {
|
||||
SearchFragment searchFragment = new SearchFragment();
|
||||
searchFragment.setQuery(serviceId, searchString, new String[0], "");
|
||||
|
||||
|
|
@ -173,33 +176,37 @@ public class SearchFragment
|
|||
//////////////////////////////////////////////////////////////////////////*/
|
||||
|
||||
@Override
|
||||
public void onAttach(Context context) {
|
||||
public void onAttach(final Context context) {
|
||||
super.onAttach(context);
|
||||
|
||||
suggestionListAdapter = new SuggestionListAdapter(activity);
|
||||
SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(activity);
|
||||
boolean isSearchHistoryEnabled = preferences.getBoolean(getString(R.string.enable_search_history_key), true);
|
||||
boolean isSearchHistoryEnabled = preferences
|
||||
.getBoolean(getString(R.string.enable_search_history_key), true);
|
||||
suggestionListAdapter.setShowSuggestionHistory(isSearchHistoryEnabled);
|
||||
|
||||
historyRecordManager = new HistoryRecordManager(context);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCreate(Bundle savedInstanceState) {
|
||||
public void onCreate(final Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
|
||||
SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(activity);
|
||||
isSuggestionsEnabled = preferences.getBoolean(getString(R.string.show_search_suggestions_key), true);
|
||||
contentCountry = preferences.getString(getString(R.string.content_country_key), getString(R.string.default_localization_key));
|
||||
isSuggestionsEnabled = preferences
|
||||
.getBoolean(getString(R.string.show_search_suggestions_key), true);
|
||||
contentCountry = preferences.getString(getString(R.string.content_country_key),
|
||||
getString(R.string.default_localization_key));
|
||||
}
|
||||
|
||||
@Override
|
||||
public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
|
||||
public View onCreateView(final LayoutInflater inflater, @Nullable final ViewGroup container,
|
||||
@Nullable final Bundle savedInstanceState) {
|
||||
return inflater.inflate(R.layout.fragment_search, container, false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onViewCreated(View rootView, Bundle savedInstanceState) {
|
||||
public void onViewCreated(final View rootView, final Bundle savedInstanceState) {
|
||||
super.onViewCreated(rootView, savedInstanceState);
|
||||
showSearchOnStart();
|
||||
initSearchListeners();
|
||||
|
|
@ -211,15 +218,23 @@ public class SearchFragment
|
|||
|
||||
wasSearchFocused = searchEditText.hasFocus();
|
||||
|
||||
if (searchDisposable != null) searchDisposable.dispose();
|
||||
if (suggestionDisposable != null) suggestionDisposable.dispose();
|
||||
if (disposables != null) disposables.clear();
|
||||
if (searchDisposable != null) {
|
||||
searchDisposable.dispose();
|
||||
}
|
||||
if (suggestionDisposable != null) {
|
||||
suggestionDisposable.dispose();
|
||||
}
|
||||
if (disposables != null) {
|
||||
disposables.clear();
|
||||
}
|
||||
hideKeyboardSearch();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onResume() {
|
||||
if (DEBUG) Log.d(TAG, "onResume() called");
|
||||
if (DEBUG) {
|
||||
Log.d(TAG, "onResume() called");
|
||||
}
|
||||
super.onResume();
|
||||
|
||||
try {
|
||||
|
|
@ -245,7 +260,9 @@ public class SearchFragment
|
|||
}
|
||||
}
|
||||
|
||||
if (suggestionDisposable == null || suggestionDisposable.isDisposed()) initSuggestionObserver();
|
||||
if (suggestionDisposable == null || suggestionDisposable.isDisposed()) {
|
||||
initSuggestionObserver();
|
||||
}
|
||||
|
||||
if (TextUtils.isEmpty(searchString) || wasSearchFocused) {
|
||||
showKeyboardSearch();
|
||||
|
|
@ -259,7 +276,9 @@ public class SearchFragment
|
|||
|
||||
@Override
|
||||
public void onDestroyView() {
|
||||
if (DEBUG) Log.d(TAG, "onDestroyView() called");
|
||||
if (DEBUG) {
|
||||
Log.d(TAG, "onDestroyView() called");
|
||||
}
|
||||
unsetSearchListeners();
|
||||
super.onDestroyView();
|
||||
}
|
||||
|
|
@ -267,19 +286,27 @@ public class SearchFragment
|
|||
@Override
|
||||
public void onDestroy() {
|
||||
super.onDestroy();
|
||||
if (searchDisposable != null) searchDisposable.dispose();
|
||||
if (suggestionDisposable != null) suggestionDisposable.dispose();
|
||||
if (disposables != null) disposables.clear();
|
||||
if (searchDisposable != null) {
|
||||
searchDisposable.dispose();
|
||||
}
|
||||
if (suggestionDisposable != null) {
|
||||
suggestionDisposable.dispose();
|
||||
}
|
||||
if (disposables != null) {
|
||||
disposables.clear();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onActivityResult(int requestCode, int resultCode, Intent data) {
|
||||
public void onActivityResult(final int requestCode, final int resultCode, final Intent data) {
|
||||
switch (requestCode) {
|
||||
case ReCaptchaActivity.RECAPTCHA_REQUEST:
|
||||
if (resultCode == Activity.RESULT_OK
|
||||
&& !TextUtils.isEmpty(searchString)) {
|
||||
search(searchString, contentFilter, sortFilter);
|
||||
} else Log.e(TAG, "ReCaptcha failed");
|
||||
} else {
|
||||
Log.e(TAG, "ReCaptcha failed");
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
|
|
@ -293,25 +320,27 @@ public class SearchFragment
|
|||
//////////////////////////////////////////////////////////////////////////*/
|
||||
|
||||
@Override
|
||||
protected void initViews(View rootView, Bundle savedInstanceState) {
|
||||
protected void initViews(final View rootView, final Bundle savedInstanceState) {
|
||||
super.initViews(rootView, savedInstanceState);
|
||||
suggestionsPanel = rootView.findViewById(R.id.suggestions_panel);
|
||||
suggestionsRecyclerView = rootView.findViewById(R.id.suggestions_list);
|
||||
suggestionsRecyclerView.setAdapter(suggestionListAdapter);
|
||||
new ItemTouchHelper(new ItemTouchHelper.Callback() {
|
||||
@Override
|
||||
public int getMovementFlags(@NonNull RecyclerView recyclerView, @NonNull RecyclerView.ViewHolder viewHolder) {
|
||||
public int getMovementFlags(@NonNull final RecyclerView recyclerView,
|
||||
@NonNull final RecyclerView.ViewHolder viewHolder) {
|
||||
return getSuggestionMovementFlags(recyclerView, viewHolder);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onMove(@NonNull RecyclerView recyclerView, @NonNull RecyclerView.ViewHolder viewHolder,
|
||||
@NonNull RecyclerView.ViewHolder viewHolder1) {
|
||||
public boolean onMove(@NonNull final RecyclerView recyclerView,
|
||||
@NonNull final RecyclerView.ViewHolder viewHolder,
|
||||
@NonNull final RecyclerView.ViewHolder viewHolder1) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSwiped(@NonNull RecyclerView.ViewHolder viewHolder, int i) {
|
||||
public void onSwiped(@NonNull final RecyclerView.ViewHolder viewHolder, final int i) {
|
||||
onSuggestionItemSwiped(viewHolder, i);
|
||||
}
|
||||
}).attachToRecyclerView(suggestionsRecyclerView);
|
||||
|
|
@ -326,21 +355,21 @@ public class SearchFragment
|
|||
//////////////////////////////////////////////////////////////////////////*/
|
||||
|
||||
@Override
|
||||
public void writeTo(Queue<Object> objectsToSave) {
|
||||
public void writeTo(final Queue<Object> objectsToSave) {
|
||||
super.writeTo(objectsToSave);
|
||||
objectsToSave.add(currentPageUrl);
|
||||
objectsToSave.add(nextPageUrl);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void readFrom(@NonNull Queue<Object> savedObjects) throws Exception {
|
||||
public void readFrom(@NonNull final Queue<Object> savedObjects) throws Exception {
|
||||
super.readFrom(savedObjects);
|
||||
currentPageUrl = (String) savedObjects.poll();
|
||||
nextPageUrl = (String) savedObjects.poll();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSaveInstanceState(Bundle bundle) {
|
||||
public void onSaveInstanceState(final Bundle bundle) {
|
||||
searchString = searchEditText != null
|
||||
? searchEditText.getText().toString()
|
||||
: searchString;
|
||||
|
|
@ -372,7 +401,7 @@ public class SearchFragment
|
|||
//////////////////////////////////////////////////////////////////////////*/
|
||||
|
||||
@Override
|
||||
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
|
||||
public void onCreateOptionsMenu(final Menu menu, final MenuInflater inflater) {
|
||||
super.onCreateOptionsMenu(menu, inflater);
|
||||
|
||||
ActionBar supportActionBar = activity.getSupportActionBar();
|
||||
|
|
@ -386,13 +415,13 @@ public class SearchFragment
|
|||
int itemId = 0;
|
||||
boolean isFirstItem = true;
|
||||
final Context c = getContext();
|
||||
for(String filter : service.getSearchQHFactory().getAvailableContentFilter()) {
|
||||
for (String filter : service.getSearchQHFactory().getAvailableContentFilter()) {
|
||||
menuItemToFilterName.put(itemId, filter);
|
||||
MenuItem item = menu.add(1,
|
||||
itemId++,
|
||||
0,
|
||||
ServiceHelper.getTranslatedFilterString(filter, c));
|
||||
if(isFirstItem) {
|
||||
if (isFirstItem) {
|
||||
item.setChecked(true);
|
||||
isFirstItem = false;
|
||||
}
|
||||
|
|
@ -403,19 +432,20 @@ public class SearchFragment
|
|||
}
|
||||
|
||||
@Override
|
||||
public boolean onOptionsItemSelected(MenuItem item) {
|
||||
|
||||
List<String> contentFilter = new ArrayList<>(1);
|
||||
contentFilter.add(menuItemToFilterName.get(item.getItemId()));
|
||||
changeContentFilter(item, contentFilter);
|
||||
public boolean onOptionsItemSelected(final MenuItem item) {
|
||||
List<String> cf = new ArrayList<>(1);
|
||||
cf.add(menuItemToFilterName.get(item.getItemId()));
|
||||
changeContentFilter(item, cf);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private void restoreFilterChecked(Menu menu, int itemId) {
|
||||
private void restoreFilterChecked(final Menu menu, final int itemId) {
|
||||
if (itemId != -1) {
|
||||
MenuItem item = menu.findItem(itemId);
|
||||
if (item == null) return;
|
||||
if (item == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
item.setChecked(true);
|
||||
}
|
||||
|
|
@ -425,13 +455,13 @@ public class SearchFragment
|
|||
// Search
|
||||
//////////////////////////////////////////////////////////////////////////*/
|
||||
|
||||
private TextWatcher textWatcher;
|
||||
|
||||
private void showSearchOnStart() {
|
||||
if (DEBUG) Log.d(TAG, "showSearchOnStart() called, searchQuery → "
|
||||
+ searchString
|
||||
+ ", lastSearchedQuery → "
|
||||
+ lastSearchedString);
|
||||
if (DEBUG) {
|
||||
Log.d(TAG, "showSearchOnStart() called, searchQuery → "
|
||||
+ searchString
|
||||
+ ", lastSearchedQuery → "
|
||||
+ lastSearchedString);
|
||||
}
|
||||
searchEditText.setText(searchString);
|
||||
|
||||
if (TextUtils.isEmpty(searchString) || TextUtils.isEmpty(searchEditText.getText())) {
|
||||
|
|
@ -451,9 +481,13 @@ public class SearchFragment
|
|||
}
|
||||
|
||||
private void initSearchListeners() {
|
||||
if (DEBUG) Log.d(TAG, "initSearchListeners() called");
|
||||
if (DEBUG) {
|
||||
Log.d(TAG, "initSearchListeners() called");
|
||||
}
|
||||
searchClear.setOnClickListener(v -> {
|
||||
if (DEBUG) Log.d(TAG, "onClick() called with: v = [" + v + "]");
|
||||
if (DEBUG) {
|
||||
Log.d(TAG, "onClick() called with: v = [" + v + "]");
|
||||
}
|
||||
if (TextUtils.isEmpty(searchEditText.getText())) {
|
||||
NavigationHelper.gotoMainFragment(getFragmentManager());
|
||||
return;
|
||||
|
|
@ -467,7 +501,9 @@ public class SearchFragment
|
|||
TooltipCompat.setTooltipText(searchClear, getString(R.string.clear));
|
||||
|
||||
searchEditText.setOnClickListener(v -> {
|
||||
if (DEBUG) Log.d(TAG, "onClick() called with: v = [" + v + "]");
|
||||
if (DEBUG) {
|
||||
Log.d(TAG, "onClick() called with: v = [" + v + "]");
|
||||
}
|
||||
if (isSuggestionsEnabled && errorPanelRoot.getVisibility() != View.VISIBLE) {
|
||||
showSuggestionsPanel();
|
||||
}
|
||||
|
|
@ -477,43 +513,51 @@ public class SearchFragment
|
|||
});
|
||||
|
||||
searchEditText.setOnFocusChangeListener((View v, boolean hasFocus) -> {
|
||||
if (DEBUG) Log.d(TAG, "onFocusChange() called with: v = [" + v + "], hasFocus = [" + hasFocus + "]");
|
||||
if (isSuggestionsEnabled && hasFocus && errorPanelRoot.getVisibility() != View.VISIBLE) {
|
||||
if (DEBUG) {
|
||||
Log.d(TAG, "onFocusChange() called with: "
|
||||
+ "v = [" + v + "], hasFocus = [" + hasFocus + "]");
|
||||
}
|
||||
if (isSuggestionsEnabled && hasFocus
|
||||
&& errorPanelRoot.getVisibility() != View.VISIBLE) {
|
||||
showSuggestionsPanel();
|
||||
}
|
||||
});
|
||||
|
||||
suggestionListAdapter.setListener(new SuggestionListAdapter.OnSuggestionItemSelected() {
|
||||
@Override
|
||||
public void onSuggestionItemSelected(SuggestionItem item) {
|
||||
public void onSuggestionItemSelected(final SuggestionItem item) {
|
||||
search(item.query, new String[0], "");
|
||||
searchEditText.setText(item.query);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSuggestionItemInserted(SuggestionItem item) {
|
||||
public void onSuggestionItemInserted(final SuggestionItem item) {
|
||||
searchEditText.setText(item.query);
|
||||
searchEditText.setSelection(searchEditText.getText().length());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSuggestionItemLongClick(SuggestionItem item) {
|
||||
if (item.fromHistory) showDeleteSuggestionDialog(item);
|
||||
public void onSuggestionItemLongClick(final SuggestionItem item) {
|
||||
if (item.fromHistory) {
|
||||
showDeleteSuggestionDialog(item);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
if (textWatcher != null) searchEditText.removeTextChangedListener(textWatcher);
|
||||
if (textWatcher != null) {
|
||||
searchEditText.removeTextChangedListener(textWatcher);
|
||||
}
|
||||
textWatcher = new TextWatcher() {
|
||||
@Override
|
||||
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
|
||||
}
|
||||
public void beforeTextChanged(final CharSequence s, final int start,
|
||||
final int count, final int after) { }
|
||||
|
||||
@Override
|
||||
public void onTextChanged(CharSequence s, int start, int before, int count) {
|
||||
}
|
||||
public void onTextChanged(final CharSequence s, final int start,
|
||||
final int before, final int count) { }
|
||||
|
||||
@Override
|
||||
public void afterTextChanged(Editable s) {
|
||||
public void afterTextChanged(final Editable s) {
|
||||
String newText = searchEditText.getText().toString();
|
||||
suggestionPublisher.onNext(newText);
|
||||
}
|
||||
|
|
@ -522,48 +566,62 @@ public class SearchFragment
|
|||
searchEditText.setOnEditorActionListener(
|
||||
(TextView v, int actionId, KeyEvent event) -> {
|
||||
if (DEBUG) {
|
||||
Log.d(TAG, "onEditorAction() called with: v = [" + v + "], actionId = [" + actionId + "], event = [" + event + "]");
|
||||
Log.d(TAG, "onEditorAction() called with: v = [" + v + "], "
|
||||
+ "actionId = [" + actionId + "], event = [" + event + "]");
|
||||
}
|
||||
if(actionId == EditorInfo.IME_ACTION_PREVIOUS){
|
||||
if (actionId == EditorInfo.IME_ACTION_PREVIOUS) {
|
||||
hideKeyboardSearch();
|
||||
} else if (event != null
|
||||
&& (event.getKeyCode() == KeyEvent.KEYCODE_ENTER
|
||||
|| event.getAction() == EditorInfo.IME_ACTION_SEARCH)) {
|
||||
|| event.getAction() == EditorInfo.IME_ACTION_SEARCH)) {
|
||||
search(searchEditText.getText().toString(), new String[0], "");
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
});
|
||||
|
||||
if (suggestionDisposable == null || suggestionDisposable.isDisposed())
|
||||
if (suggestionDisposable == null || suggestionDisposable.isDisposed()) {
|
||||
initSuggestionObserver();
|
||||
}
|
||||
}
|
||||
|
||||
private void unsetSearchListeners() {
|
||||
if (DEBUG) Log.d(TAG, "unsetSearchListeners() called");
|
||||
if (DEBUG) {
|
||||
Log.d(TAG, "unsetSearchListeners() called");
|
||||
}
|
||||
searchClear.setOnClickListener(null);
|
||||
searchClear.setOnLongClickListener(null);
|
||||
searchEditText.setOnClickListener(null);
|
||||
searchEditText.setOnFocusChangeListener(null);
|
||||
searchEditText.setOnEditorActionListener(null);
|
||||
|
||||
if (textWatcher != null) searchEditText.removeTextChangedListener(textWatcher);
|
||||
if (textWatcher != null) {
|
||||
searchEditText.removeTextChangedListener(textWatcher);
|
||||
}
|
||||
textWatcher = null;
|
||||
}
|
||||
|
||||
private void showSuggestionsPanel() {
|
||||
if (DEBUG) Log.d(TAG, "showSuggestionsPanel() called");
|
||||
if (DEBUG) {
|
||||
Log.d(TAG, "showSuggestionsPanel() called");
|
||||
}
|
||||
animateView(suggestionsPanel, AnimationUtils.Type.LIGHT_SLIDE_AND_ALPHA, true, 200);
|
||||
}
|
||||
|
||||
private void hideSuggestionsPanel() {
|
||||
if (DEBUG) Log.d(TAG, "hideSuggestionsPanel() called");
|
||||
if (DEBUG) {
|
||||
Log.d(TAG, "hideSuggestionsPanel() called");
|
||||
}
|
||||
animateView(suggestionsPanel, AnimationUtils.Type.LIGHT_SLIDE_AND_ALPHA, false, 200);
|
||||
}
|
||||
|
||||
private void showKeyboardSearch() {
|
||||
if (DEBUG) Log.d(TAG, "showKeyboardSearch() called");
|
||||
if (searchEditText == null) return;
|
||||
if (DEBUG) {
|
||||
Log.d(TAG, "showKeyboardSearch() called");
|
||||
}
|
||||
if (searchEditText == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (searchEditText.requestFocus()) {
|
||||
InputMethodManager imm = (InputMethodManager) activity.getSystemService(
|
||||
|
|
@ -573,19 +631,26 @@ public class SearchFragment
|
|||
}
|
||||
|
||||
private void hideKeyboardSearch() {
|
||||
if (DEBUG) Log.d(TAG, "hideKeyboardSearch() called");
|
||||
if (searchEditText == null) return;
|
||||
if (DEBUG) {
|
||||
Log.d(TAG, "hideKeyboardSearch() called");
|
||||
}
|
||||
if (searchEditText == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
InputMethodManager imm = (InputMethodManager) activity.getSystemService(
|
||||
Context.INPUT_METHOD_SERVICE);
|
||||
imm.hideSoftInputFromWindow(searchEditText.getWindowToken(), InputMethodManager.RESULT_UNCHANGED_SHOWN);
|
||||
InputMethodManager imm = (InputMethodManager) activity
|
||||
.getSystemService(Context.INPUT_METHOD_SERVICE);
|
||||
imm.hideSoftInputFromWindow(searchEditText.getWindowToken(),
|
||||
InputMethodManager.RESULT_UNCHANGED_SHOWN);
|
||||
|
||||
searchEditText.clearFocus();
|
||||
}
|
||||
|
||||
private void showDeleteSuggestionDialog(final SuggestionItem item) {
|
||||
if (activity == null || historyRecordManager == null || suggestionPublisher == null ||
|
||||
searchEditText == null || disposables == null) return;
|
||||
if (activity == null || historyRecordManager == null || suggestionPublisher == null
|
||||
|| searchEditText == null || disposables == null) {
|
||||
return;
|
||||
}
|
||||
final String query = item.query;
|
||||
new AlertDialog.Builder(activity)
|
||||
.setTitle(query)
|
||||
|
|
@ -624,15 +689,19 @@ public class SearchFragment
|
|||
}
|
||||
|
||||
private void initSuggestionObserver() {
|
||||
if (DEBUG) Log.d(TAG, "initSuggestionObserver() called");
|
||||
if (suggestionDisposable != null) suggestionDisposable.dispose();
|
||||
if (DEBUG) {
|
||||
Log.d(TAG, "initSuggestionObserver() called");
|
||||
}
|
||||
if (suggestionDisposable != null) {
|
||||
suggestionDisposable.dispose();
|
||||
}
|
||||
|
||||
final Observable<String> observable = suggestionPublisher
|
||||
.debounce(SUGGESTIONS_DEBOUNCE, TimeUnit.MILLISECONDS)
|
||||
.startWith(searchString != null
|
||||
? searchString
|
||||
: "")
|
||||
.filter(searchString -> isSuggestionsEnabled);
|
||||
.filter(ss -> isSuggestionsEnabled);
|
||||
|
||||
suggestionDisposable = observable
|
||||
.switchMap(query -> {
|
||||
|
|
@ -641,13 +710,15 @@ public class SearchFragment
|
|||
final Observable<List<SuggestionItem>> local = flowable.toObservable()
|
||||
.map(searchHistoryEntries -> {
|
||||
List<SuggestionItem> result = new ArrayList<>();
|
||||
for (SearchHistoryEntry entry : searchHistoryEntries)
|
||||
for (SearchHistoryEntry entry : searchHistoryEntries) {
|
||||
result.add(new SuggestionItem(true, entry.getSearch()));
|
||||
}
|
||||
return result;
|
||||
});
|
||||
|
||||
if (query.length() < THRESHOLD_NETWORK_SUGGESTION) {
|
||||
// Only pass through if the query length is equal or greater than THRESHOLD_NETWORK_SUGGESTION
|
||||
// Only pass through if the query length
|
||||
// is equal or greater than THRESHOLD_NETWORK_SUGGESTION
|
||||
return local.materialize();
|
||||
}
|
||||
|
||||
|
|
@ -664,7 +735,9 @@ public class SearchFragment
|
|||
|
||||
return Observable.zip(local, network, (localResult, networkResult) -> {
|
||||
List<SuggestionItem> result = new ArrayList<>();
|
||||
if (localResult.size() > 0) result.addAll(localResult);
|
||||
if (localResult.size() > 0) {
|
||||
result.addAll(localResult);
|
||||
}
|
||||
|
||||
// Remove duplicates
|
||||
final Iterator<SuggestionItem> iterator = networkResult.iterator();
|
||||
|
|
@ -678,7 +751,9 @@ public class SearchFragment
|
|||
}
|
||||
}
|
||||
|
||||
if (networkResult.size() > 0) result.addAll(networkResult);
|
||||
if (networkResult.size() > 0) {
|
||||
result.addAll(networkResult);
|
||||
}
|
||||
return result;
|
||||
}).materialize();
|
||||
})
|
||||
|
|
@ -703,17 +778,21 @@ public class SearchFragment
|
|||
// no-op
|
||||
}
|
||||
|
||||
private void search(final String searchString, String[] contentFilter, String sortFilter) {
|
||||
if (DEBUG) Log.d(TAG, "search() called with: query = [" + searchString + "]");
|
||||
if (searchString.isEmpty()) return;
|
||||
private void search(final String ss, final String[] cf, final String sf) {
|
||||
if (DEBUG) {
|
||||
Log.d(TAG, "search() called with: query = [" + ss + "]");
|
||||
}
|
||||
if (ss.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
final StreamingService service = NewPipe.getServiceByUrl(searchString);
|
||||
if (service != null) {
|
||||
final StreamingService streamingService = NewPipe.getServiceByUrl(ss);
|
||||
if (streamingService != null) {
|
||||
showLoading();
|
||||
disposables.add(Observable
|
||||
.fromCallable(() ->
|
||||
NavigationHelper.getIntentByLink(activity, service, searchString))
|
||||
NavigationHelper.getIntentByLink(activity, streamingService, ss))
|
||||
.subscribeOn(Schedulers.io())
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribe(intent -> {
|
||||
|
|
@ -728,31 +807,36 @@ public class SearchFragment
|
|||
}
|
||||
|
||||
lastSearchedString = this.searchString;
|
||||
this.searchString = searchString;
|
||||
this.searchString = ss;
|
||||
infoListAdapter.clearStreamItemList();
|
||||
hideSuggestionsPanel();
|
||||
hideKeyboardSearch();
|
||||
|
||||
historyRecordManager.onSearched(serviceId, searchString)
|
||||
historyRecordManager.onSearched(serviceId, ss)
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribe(
|
||||
ignored -> {},
|
||||
ignored -> {
|
||||
},
|
||||
error -> showSnackBarError(error, UserAction.SEARCHED,
|
||||
NewPipe.getNameOfService(serviceId), searchString, 0)
|
||||
NewPipe.getNameOfService(serviceId), ss, 0)
|
||||
);
|
||||
suggestionPublisher.onNext(searchString);
|
||||
suggestionPublisher.onNext(ss);
|
||||
startLoading(false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void startLoading(boolean forceLoad) {
|
||||
public void startLoading(final boolean forceLoad) {
|
||||
super.startLoading(forceLoad);
|
||||
if (disposables != null) disposables.clear();
|
||||
if (searchDisposable != null) searchDisposable.dispose();
|
||||
if (disposables != null) {
|
||||
disposables.clear();
|
||||
}
|
||||
if (searchDisposable != null) {
|
||||
searchDisposable.dispose();
|
||||
}
|
||||
searchDisposable = ExtractorHelper.searchFor(serviceId,
|
||||
searchString,
|
||||
Arrays.asList(contentFilter),
|
||||
sortFilter)
|
||||
searchString,
|
||||
Arrays.asList(contentFilter),
|
||||
sortFilter)
|
||||
.subscribeOn(Schedulers.io())
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.doOnEvent((searchResult, throwable) -> isLoading.set(false))
|
||||
|
|
@ -762,16 +846,20 @@ public class SearchFragment
|
|||
|
||||
@Override
|
||||
protected void loadMoreItems() {
|
||||
if(nextPageUrl == null || nextPageUrl.isEmpty()) return;
|
||||
if (nextPageUrl == null || nextPageUrl.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
isLoading.set(true);
|
||||
showListFooter(true);
|
||||
if (searchDisposable != null) searchDisposable.dispose();
|
||||
if (searchDisposable != null) {
|
||||
searchDisposable.dispose();
|
||||
}
|
||||
searchDisposable = ExtractorHelper.getMoreSearchItems(
|
||||
serviceId,
|
||||
searchString,
|
||||
asList(contentFilter),
|
||||
sortFilter,
|
||||
nextPageUrl)
|
||||
serviceId,
|
||||
searchString,
|
||||
asList(contentFilter),
|
||||
sortFilter,
|
||||
nextPageUrl)
|
||||
.subscribeOn(Schedulers.io())
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.doOnEvent((nextItemsResult, throwable) -> isLoading.set(false))
|
||||
|
|
@ -785,7 +873,7 @@ public class SearchFragment
|
|||
}
|
||||
|
||||
@Override
|
||||
protected void onItemSelected(InfoItem selectedItem) {
|
||||
protected void onItemSelected(final InfoItem selectedItem) {
|
||||
super.onItemSelected(selectedItem);
|
||||
hideKeyboardSearch();
|
||||
}
|
||||
|
|
@ -794,22 +882,22 @@ public class SearchFragment
|
|||
// Utils
|
||||
//////////////////////////////////////////////////////////////////////////*/
|
||||
|
||||
private void changeContentFilter(MenuItem item, List<String> contentFilter) {
|
||||
private void changeContentFilter(final MenuItem item, final List<String> cf) {
|
||||
this.filterItemCheckedId = item.getItemId();
|
||||
item.setChecked(true);
|
||||
|
||||
this.contentFilter = new String[] {contentFilter.get(0)};
|
||||
this.contentFilter = new String[]{cf.get(0)};
|
||||
|
||||
if (!TextUtils.isEmpty(searchString)) {
|
||||
search(searchString, this.contentFilter, sortFilter);
|
||||
}
|
||||
}
|
||||
|
||||
private void setQuery(int serviceId, String searchString, String[] contentfilter, String sortFilter) {
|
||||
this.serviceId = serviceId;
|
||||
private void setQuery(final int sid, final String ss, final String[] cf, final String sf) {
|
||||
this.serviceId = sid;
|
||||
this.searchString = searchString;
|
||||
this.contentFilter = contentfilter;
|
||||
this.sortFilter = sortFilter;
|
||||
this.contentFilter = cf;
|
||||
this.sortFilter = sf;
|
||||
}
|
||||
|
||||
/*//////////////////////////////////////////////////////////////////////////
|
||||
|
|
@ -817,7 +905,9 @@ public class SearchFragment
|
|||
//////////////////////////////////////////////////////////////////////////*/
|
||||
|
||||
public void handleSuggestions(@NonNull final List<SuggestionItem> suggestions) {
|
||||
if (DEBUG) Log.d(TAG, "handleSuggestions() called with: suggestions = [" + suggestions + "]");
|
||||
if (DEBUG) {
|
||||
Log.d(TAG, "handleSuggestions() called with: suggestions = [" + suggestions + "]");
|
||||
}
|
||||
suggestionsRecyclerView.smoothScrollToPosition(0);
|
||||
suggestionsRecyclerView.post(() -> suggestionListAdapter.setItems(suggestions));
|
||||
|
||||
|
|
@ -826,9 +916,13 @@ public class SearchFragment
|
|||
}
|
||||
}
|
||||
|
||||
public void onSuggestionError(Throwable exception) {
|
||||
if (DEBUG) Log.d(TAG, "onSuggestionError() called with: exception = [" + exception + "]");
|
||||
if (super.onError(exception)) return;
|
||||
public void onSuggestionError(final Throwable exception) {
|
||||
if (DEBUG) {
|
||||
Log.d(TAG, "onSuggestionError() called with: exception = [" + exception + "]");
|
||||
}
|
||||
if (super.onError(exception)) {
|
||||
return;
|
||||
}
|
||||
|
||||
int errorId = exception instanceof ParsingException
|
||||
? R.string.parsing_error
|
||||
|
|
@ -848,7 +942,7 @@ public class SearchFragment
|
|||
}
|
||||
|
||||
@Override
|
||||
public void showError(String message, boolean showRetryButton) {
|
||||
public void showError(final String message, final boolean showRetryButton) {
|
||||
super.showError(message, showRetryButton);
|
||||
hideSuggestionsPanel();
|
||||
hideKeyboardSearch();
|
||||
|
|
@ -859,11 +953,11 @@ public class SearchFragment
|
|||
//////////////////////////////////////////////////////////////////////////*/
|
||||
|
||||
@Override
|
||||
public void handleResult(@NonNull SearchInfo result) {
|
||||
public void handleResult(@NonNull final SearchInfo result) {
|
||||
final List<Throwable> exceptions = result.getErrors();
|
||||
if (!exceptions.isEmpty()
|
||||
&& !(exceptions.size() == 1
|
||||
&& exceptions.get(0) instanceof SearchExtractor.NothingFoundException)){
|
||||
&& !(exceptions.size() == 1
|
||||
&& exceptions.get(0) instanceof SearchExtractor.NothingFoundException)) {
|
||||
showSnackBarError(result.getErrors(), UserAction.SEARCHED,
|
||||
NewPipe.getNameOfService(serviceId), searchString, 0);
|
||||
}
|
||||
|
|
@ -886,7 +980,7 @@ public class SearchFragment
|
|||
}
|
||||
|
||||
@Override
|
||||
public void handleNextItems(ListExtractor.InfoItemsPage result) {
|
||||
public void handleNextItems(final ListExtractor.InfoItemsPage result) {
|
||||
showListFooter(false);
|
||||
currentPageUrl = result.getNextPageUrl();
|
||||
infoListAdapter.addInfoItemList(result.getItems());
|
||||
|
|
@ -894,15 +988,17 @@ public class SearchFragment
|
|||
|
||||
if (!result.getErrors().isEmpty()) {
|
||||
showSnackBarError(result.getErrors(), UserAction.SEARCHED,
|
||||
NewPipe.getNameOfService(serviceId)
|
||||
, "\"" + searchString + "\" → page: " + nextPageUrl, 0);
|
||||
NewPipe.getNameOfService(serviceId),
|
||||
"\"" + searchString + "\" → page: " + nextPageUrl, 0);
|
||||
}
|
||||
super.handleNextItems(result);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean onError(Throwable exception) {
|
||||
if (super.onError(exception)) return true;
|
||||
protected boolean onError(final Throwable exception) {
|
||||
if (super.onError(exception)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (exception instanceof SearchExtractor.NothingFoundException) {
|
||||
infoListAdapter.clearStreamItemList();
|
||||
|
|
@ -922,13 +1018,16 @@ public class SearchFragment
|
|||
// Suggestion item touch helper
|
||||
//////////////////////////////////////////////////////////////////////////*/
|
||||
|
||||
public int getSuggestionMovementFlags(@NonNull RecyclerView recyclerView, @NonNull RecyclerView.ViewHolder viewHolder) {
|
||||
public int getSuggestionMovementFlags(@NonNull final RecyclerView recyclerView,
|
||||
@NonNull final RecyclerView.ViewHolder viewHolder) {
|
||||
final int position = viewHolder.getAdapterPosition();
|
||||
final SuggestionItem item = suggestionListAdapter.getItem(position);
|
||||
return item.fromHistory ? makeMovementFlags(0, ItemTouchHelper.LEFT | ItemTouchHelper.RIGHT) : 0;
|
||||
return item.fromHistory ? makeMovementFlags(0,
|
||||
ItemTouchHelper.LEFT | ItemTouchHelper.RIGHT) : 0;
|
||||
}
|
||||
|
||||
public void onSuggestionItemSwiped(@NonNull RecyclerView.ViewHolder viewHolder, int i) {
|
||||
public void onSuggestionItemSwiped(@NonNull final RecyclerView.ViewHolder viewHolder,
|
||||
final int i) {
|
||||
final int position = viewHolder.getAdapterPosition();
|
||||
final String query = suggestionListAdapter.getItem(position).query;
|
||||
final Disposable onDelete = historyRecordManager.deleteSearchHistory(query)
|
||||
|
|
|
|||
|
|
@ -1,10 +1,10 @@
|
|||
package org.schabi.newpipe.fragments.list.search;
|
||||
|
||||
public class SuggestionItem {
|
||||
public final boolean fromHistory;
|
||||
final boolean fromHistory;
|
||||
public final String query;
|
||||
|
||||
public SuggestionItem(boolean fromHistory, String query) {
|
||||
public SuggestionItem(final boolean fromHistory, final String query) {
|
||||
this.fromHistory = fromHistory;
|
||||
this.query = query;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,36 +2,32 @@ package org.schabi.newpipe.fragments.list.search;
|
|||
|
||||
import android.content.Context;
|
||||
import android.content.res.TypedArray;
|
||||
import androidx.annotation.AttrRes;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.TextView;
|
||||
|
||||
import androidx.annotation.AttrRes;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
|
||||
import org.schabi.newpipe.R;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class SuggestionListAdapter extends RecyclerView.Adapter<SuggestionListAdapter.SuggestionItemHolder> {
|
||||
public class SuggestionListAdapter
|
||||
extends RecyclerView.Adapter<SuggestionListAdapter.SuggestionItemHolder> {
|
||||
private final ArrayList<SuggestionItem> items = new ArrayList<>();
|
||||
private final Context context;
|
||||
private OnSuggestionItemSelected listener;
|
||||
private boolean showSuggestionHistory = true;
|
||||
|
||||
public interface OnSuggestionItemSelected {
|
||||
void onSuggestionItemSelected(SuggestionItem item);
|
||||
void onSuggestionItemInserted(SuggestionItem item);
|
||||
void onSuggestionItemLongClick(SuggestionItem item);
|
||||
}
|
||||
|
||||
public SuggestionListAdapter(Context context) {
|
||||
public SuggestionListAdapter(final Context context) {
|
||||
this.context = context;
|
||||
}
|
||||
|
||||
public void setItems(List<SuggestionItem> items) {
|
||||
public void setItems(final List<SuggestionItem> items) {
|
||||
this.items.clear();
|
||||
if (showSuggestionHistory) {
|
||||
this.items.addAll(items);
|
||||
|
|
@ -46,36 +42,43 @@ public class SuggestionListAdapter extends RecyclerView.Adapter<SuggestionListAd
|
|||
notifyDataSetChanged();
|
||||
}
|
||||
|
||||
public void setListener(OnSuggestionItemSelected listener) {
|
||||
public void setListener(final OnSuggestionItemSelected listener) {
|
||||
this.listener = listener;
|
||||
}
|
||||
|
||||
public void setShowSuggestionHistory(boolean v) {
|
||||
public void setShowSuggestionHistory(final boolean v) {
|
||||
showSuggestionHistory = v;
|
||||
}
|
||||
|
||||
@Override
|
||||
public SuggestionItemHolder onCreateViewHolder(ViewGroup parent, int viewType) {
|
||||
return new SuggestionItemHolder(LayoutInflater.from(context).inflate(R.layout.item_search_suggestion, parent, false));
|
||||
public SuggestionItemHolder onCreateViewHolder(final ViewGroup parent, final int viewType) {
|
||||
return new SuggestionItemHolder(LayoutInflater.from(context)
|
||||
.inflate(R.layout.item_search_suggestion, parent, false));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBindViewHolder(SuggestionItemHolder holder, int position) {
|
||||
public void onBindViewHolder(final SuggestionItemHolder holder, final int position) {
|
||||
final SuggestionItem currentItem = getItem(position);
|
||||
holder.updateFrom(currentItem);
|
||||
holder.queryView.setOnClickListener(v -> {
|
||||
if (listener != null) listener.onSuggestionItemSelected(currentItem);
|
||||
if (listener != null) {
|
||||
listener.onSuggestionItemSelected(currentItem);
|
||||
}
|
||||
});
|
||||
holder.queryView.setOnLongClickListener(v -> {
|
||||
if (listener != null) listener.onSuggestionItemLongClick(currentItem);
|
||||
return true;
|
||||
if (listener != null) {
|
||||
listener.onSuggestionItemLongClick(currentItem);
|
||||
}
|
||||
return true;
|
||||
});
|
||||
holder.insertView.setOnClickListener(v -> {
|
||||
if (listener != null) listener.onSuggestionItemInserted(currentItem);
|
||||
if (listener != null) {
|
||||
listener.onSuggestionItemInserted(currentItem);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
SuggestionItem getItem(int position) {
|
||||
SuggestionItem getItem(final int position) {
|
||||
return items.get(position);
|
||||
}
|
||||
|
||||
|
|
@ -88,7 +91,15 @@ public class SuggestionListAdapter extends RecyclerView.Adapter<SuggestionListAd
|
|||
return getItemCount() == 0;
|
||||
}
|
||||
|
||||
public static class SuggestionItemHolder extends RecyclerView.ViewHolder {
|
||||
public interface OnSuggestionItemSelected {
|
||||
void onSuggestionItemSelected(SuggestionItem item);
|
||||
|
||||
void onSuggestionItemInserted(SuggestionItem item);
|
||||
|
||||
void onSuggestionItemLongClick(SuggestionItem item);
|
||||
}
|
||||
|
||||
public static final class SuggestionItemHolder extends RecyclerView.ViewHolder {
|
||||
private final TextView itemSuggestionQuery;
|
||||
private final ImageView suggestionIcon;
|
||||
private final View queryView;
|
||||
|
|
@ -98,7 +109,7 @@ public class SuggestionListAdapter extends RecyclerView.Adapter<SuggestionListAd
|
|||
private final int historyResId;
|
||||
private final int searchResId;
|
||||
|
||||
private SuggestionItemHolder(View rootView) {
|
||||
private SuggestionItemHolder(final View rootView) {
|
||||
super(rootView);
|
||||
suggestionIcon = rootView.findViewById(R.id.item_suggestion_icon);
|
||||
itemSuggestionQuery = rootView.findViewById(R.id.item_suggestion_query);
|
||||
|
|
@ -110,16 +121,17 @@ public class SuggestionListAdapter extends RecyclerView.Adapter<SuggestionListAd
|
|||
searchResId = resolveResourceIdFromAttr(rootView.getContext(), R.attr.search);
|
||||
}
|
||||
|
||||
private void updateFrom(SuggestionItem item) {
|
||||
suggestionIcon.setImageResource(item.fromHistory ? historyResId : searchResId);
|
||||
itemSuggestionQuery.setText(item.query);
|
||||
}
|
||||
|
||||
private static int resolveResourceIdFromAttr(Context context, @AttrRes int attr) {
|
||||
private static int resolveResourceIdFromAttr(final Context context,
|
||||
@AttrRes final int attr) {
|
||||
TypedArray a = context.getTheme().obtainStyledAttributes(new int[]{attr});
|
||||
int attributeResourceId = a.getResourceId(0, 0);
|
||||
a.recycle();
|
||||
return attributeResourceId;
|
||||
}
|
||||
|
||||
private void updateFrom(final SuggestionItem item) {
|
||||
suggestionIcon.setImageResource(item.fromHistory ? historyResId : searchResId);
|
||||
itemSuggestionQuery.setText(item.query);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,8 +4,6 @@ import android.content.Context;
|
|||
import android.content.SharedPreferences;
|
||||
import android.os.Bundle;
|
||||
import android.preference.PreferenceManager;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.Menu;
|
||||
import android.view.MenuInflater;
|
||||
|
|
@ -14,6 +12,9 @@ import android.view.ViewGroup;
|
|||
import android.widget.CompoundButton;
|
||||
import android.widget.Switch;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import org.schabi.newpipe.R;
|
||||
import org.schabi.newpipe.extractor.ListExtractor;
|
||||
import org.schabi.newpipe.extractor.NewPipe;
|
||||
|
|
@ -28,53 +29,61 @@ import java.io.Serializable;
|
|||
import io.reactivex.Single;
|
||||
import io.reactivex.disposables.CompositeDisposable;
|
||||
|
||||
public class RelatedVideosFragment extends BaseListInfoFragment<RelatedStreamInfo> implements SharedPreferences.OnSharedPreferenceChangeListener{
|
||||
|
||||
public class RelatedVideosFragment extends BaseListInfoFragment<RelatedStreamInfo>
|
||||
implements SharedPreferences.OnSharedPreferenceChangeListener {
|
||||
private static final String INFO_KEY = "related_info_key";
|
||||
private CompositeDisposable disposables = new CompositeDisposable();
|
||||
private RelatedStreamInfo relatedStreamInfo;
|
||||
|
||||
/*//////////////////////////////////////////////////////////////////////////
|
||||
// Views
|
||||
//////////////////////////////////////////////////////////////////////////*/
|
||||
|
||||
private View headerRootLayout;
|
||||
private Switch aSwitch;
|
||||
|
||||
private boolean mIsVisibleToUser = false;
|
||||
|
||||
public static RelatedVideosFragment getInstance(StreamInfo info) {
|
||||
public static RelatedVideosFragment getInstance(final StreamInfo info) {
|
||||
RelatedVideosFragment instance = new RelatedVideosFragment();
|
||||
instance.setInitialData(info);
|
||||
return instance;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setUserVisibleHint(final boolean isVisibleToUser) {
|
||||
super.setUserVisibleHint(isVisibleToUser);
|
||||
mIsVisibleToUser = isVisibleToUser;
|
||||
}
|
||||
|
||||
/*//////////////////////////////////////////////////////////////////////////
|
||||
// LifeCycle
|
||||
//////////////////////////////////////////////////////////////////////////*/
|
||||
|
||||
@Override
|
||||
public void setUserVisibleHint(boolean isVisibleToUser) {
|
||||
super.setUserVisibleHint(isVisibleToUser);
|
||||
mIsVisibleToUser = isVisibleToUser;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAttach(Context context) {
|
||||
public void onAttach(final Context context) {
|
||||
super.onAttach(context);
|
||||
}
|
||||
|
||||
@Override
|
||||
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
|
||||
public View onCreateView(@NonNull final LayoutInflater inflater,
|
||||
@Nullable final ViewGroup container,
|
||||
@Nullable final Bundle savedInstanceState) {
|
||||
return inflater.inflate(R.layout.fragment_related_streams, container, false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDestroy() {
|
||||
super.onDestroy();
|
||||
if (disposables != null) disposables.clear();
|
||||
if (disposables != null) {
|
||||
disposables.clear();
|
||||
}
|
||||
}
|
||||
|
||||
protected View getListHeader(){
|
||||
if(relatedStreamInfo != null && relatedStreamInfo.getNextStream() != null){
|
||||
headerRootLayout = activity.getLayoutInflater().inflate(R.layout.related_streams_header, itemsList, false);
|
||||
protected View getListHeader() {
|
||||
if (relatedStreamInfo != null && relatedStreamInfo.getNextStream() != null) {
|
||||
headerRootLayout = activity.getLayoutInflater()
|
||||
.inflate(R.layout.related_streams_header, itemsList, false);
|
||||
aSwitch = headerRootLayout.findViewById(R.id.autoplay_switch);
|
||||
|
||||
SharedPreferences pref = PreferenceManager.getDefaultSharedPreferences(getContext());
|
||||
|
|
@ -82,14 +91,14 @@ public class RelatedVideosFragment extends BaseListInfoFragment<RelatedStreamInf
|
|||
aSwitch.setChecked(autoplay);
|
||||
aSwitch.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
|
||||
@Override
|
||||
public void onCheckedChanged(CompoundButton compoundButton, boolean b) {
|
||||
SharedPreferences.Editor prefEdit = PreferenceManager.getDefaultSharedPreferences(getContext()).edit();
|
||||
prefEdit.putBoolean(getString(R.string.auto_queue_key), b);
|
||||
prefEdit.apply();
|
||||
public void onCheckedChanged(final CompoundButton compoundButton,
|
||||
final boolean b) {
|
||||
PreferenceManager.getDefaultSharedPreferences(getContext()).edit()
|
||||
.putBoolean(getString(R.string.auto_queue_key), b).apply();
|
||||
}
|
||||
});
|
||||
return headerRootLayout;
|
||||
}else{
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
|
@ -99,38 +108,44 @@ public class RelatedVideosFragment extends BaseListInfoFragment<RelatedStreamInf
|
|||
return Single.fromCallable(() -> ListExtractor.InfoItemsPage.emptyPage());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Single<RelatedStreamInfo> loadResult(boolean forceLoad) {
|
||||
return Single.fromCallable(() -> relatedStreamInfo);
|
||||
}
|
||||
|
||||
/*//////////////////////////////////////////////////////////////////////////
|
||||
// Contract
|
||||
//////////////////////////////////////////////////////////////////////////*/
|
||||
|
||||
@Override
|
||||
protected Single<RelatedStreamInfo> loadResult(final boolean forceLoad) {
|
||||
return Single.fromCallable(() -> relatedStreamInfo);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void showLoading() {
|
||||
super.showLoading();
|
||||
if(null != headerRootLayout) headerRootLayout.setVisibility(View.INVISIBLE);
|
||||
if (headerRootLayout != null) {
|
||||
headerRootLayout.setVisibility(View.INVISIBLE);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleResult(@NonNull RelatedStreamInfo result) {
|
||||
|
||||
public void handleResult(@NonNull final RelatedStreamInfo result) {
|
||||
super.handleResult(result);
|
||||
|
||||
if(null != headerRootLayout) headerRootLayout.setVisibility(View.VISIBLE);
|
||||
AnimationUtils.slideUp(getView(),120, 96, 0.06f);
|
||||
if (headerRootLayout != null) {
|
||||
headerRootLayout.setVisibility(View.VISIBLE);
|
||||
}
|
||||
AnimationUtils.slideUp(getView(), 120, 96, 0.06f);
|
||||
|
||||
if (!result.getErrors().isEmpty()) {
|
||||
showSnackBarError(result.getErrors(), UserAction.REQUESTED_STREAM, NewPipe.getNameOfService(result.getServiceId()), result.getUrl(), 0);
|
||||
showSnackBarError(result.getErrors(), UserAction.REQUESTED_STREAM,
|
||||
NewPipe.getNameOfService(result.getServiceId()), result.getUrl(), 0);
|
||||
}
|
||||
|
||||
if (disposables != null) disposables.clear();
|
||||
if (disposables != null) {
|
||||
disposables.clear();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleNextItems(ListExtractor.InfoItemsPage result) {
|
||||
public void handleNextItems(final ListExtractor.InfoItemsPage result) {
|
||||
super.handleNextItems(result);
|
||||
|
||||
if (!result.getErrors().isEmpty()) {
|
||||
|
|
@ -147,11 +162,14 @@ public class RelatedVideosFragment extends BaseListInfoFragment<RelatedStreamInf
|
|||
//////////////////////////////////////////////////////////////////////////*/
|
||||
|
||||
@Override
|
||||
protected boolean onError(Throwable exception) {
|
||||
if (super.onError(exception)) return true;
|
||||
protected boolean onError(final Throwable exception) {
|
||||
if (super.onError(exception)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
hideLoading();
|
||||
showSnackBarError(exception, UserAction.REQUESTED_STREAM, NewPipe.getNameOfService(serviceId), url, R.string.general_error);
|
||||
showSnackBarError(exception, UserAction.REQUESTED_STREAM,
|
||||
NewPipe.getNameOfService(serviceId), url, R.string.general_error);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
@ -160,45 +178,47 @@ public class RelatedVideosFragment extends BaseListInfoFragment<RelatedStreamInf
|
|||
//////////////////////////////////////////////////////////////////////////*/
|
||||
|
||||
@Override
|
||||
public void setTitle(String title) {
|
||||
public void setTitle(final String title) {
|
||||
return;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
|
||||
public void onCreateOptionsMenu(final Menu menu, final MenuInflater inflater) {
|
||||
return;
|
||||
}
|
||||
|
||||
private void setInitialData(StreamInfo info) {
|
||||
private void setInitialData(final StreamInfo info) {
|
||||
super.setInitialData(info.getServiceId(), info.getUrl(), info.getName());
|
||||
if(this.relatedStreamInfo == null) this.relatedStreamInfo = RelatedStreamInfo.getInfo(info);
|
||||
if (this.relatedStreamInfo == null) {
|
||||
this.relatedStreamInfo = RelatedStreamInfo.getInfo(info);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private static final String INFO_KEY = "related_info_key";
|
||||
|
||||
@Override
|
||||
public void onSaveInstanceState(Bundle outState) {
|
||||
public void onSaveInstanceState(final Bundle outState) {
|
||||
super.onSaveInstanceState(outState);
|
||||
outState.putSerializable(INFO_KEY, relatedStreamInfo);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onRestoreInstanceState(@NonNull Bundle savedState) {
|
||||
protected void onRestoreInstanceState(@NonNull final Bundle savedState) {
|
||||
super.onRestoreInstanceState(savedState);
|
||||
if (savedState != null) {
|
||||
Serializable serializable = savedState.getSerializable(INFO_KEY);
|
||||
if(serializable instanceof RelatedStreamInfo){
|
||||
if (serializable instanceof RelatedStreamInfo) {
|
||||
this.relatedStreamInfo = (RelatedStreamInfo) serializable;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String s) {
|
||||
public void onSharedPreferenceChanged(final SharedPreferences sharedPreferences,
|
||||
final String s) {
|
||||
SharedPreferences pref = PreferenceManager.getDefaultSharedPreferences(getContext());
|
||||
Boolean autoplay = pref.getBoolean(getString(R.string.auto_queue_key), false);
|
||||
if(null != aSwitch) aSwitch.setChecked(autoplay);
|
||||
boolean autoplay = pref.getBoolean(getString(R.string.auto_queue_key), false);
|
||||
if (null != aSwitch) {
|
||||
aSwitch.setChecked(autoplay);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue