package org.schabi.newpipe.fragments.subscription; import android.content.Context; import android.os.Bundle; import android.os.Parcelable; import android.support.annotation.NonNull; import android.support.annotation.Nullable; import android.support.v7.widget.LinearLayoutManager; import android.support.v7.widget.RecyclerView; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import org.schabi.newpipe.R; import org.schabi.newpipe.database.subscription.SubscriptionEntity; import org.schabi.newpipe.extractor.InfoItem; import org.schabi.newpipe.extractor.channel.ChannelInfoItem; import org.schabi.newpipe.fragments.BaseStateFragment; import org.schabi.newpipe.info_list.InfoItemBuilder; import org.schabi.newpipe.info_list.InfoListAdapter; import org.schabi.newpipe.report.UserAction; import org.schabi.newpipe.util.NavigationHelper; import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; import java.util.List; import icepick.State; import io.reactivex.Observer; import io.reactivex.android.schedulers.AndroidSchedulers; import io.reactivex.disposables.CompositeDisposable; import io.reactivex.disposables.Disposable; import io.reactivex.schedulers.Schedulers; import static org.schabi.newpipe.util.AnimationUtils.animateView; public class SubscriptionFragment extends BaseStateFragment> { private View headerRootLayout; private InfoListAdapter infoListAdapter; private RecyclerView itemsList; @State protected Parcelable itemsListState; /* Used for independent events */ private CompositeDisposable disposables = new CompositeDisposable(); private SubscriptionService subscriptionService; /////////////////////////////////////////////////////////////////////////// // Fragment LifeCycle /////////////////////////////////////////////////////////////////////////// @Override public void onAttach(Context context) { super.onAttach(context); infoListAdapter = new InfoListAdapter(activity); subscriptionService = SubscriptionService.getInstance(); } @Nullable @Override public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, Bundle savedInstanceState) { return inflater.inflate(R.layout.fragment_subscription, container, false); } @Override public void onPause() { super.onPause(); itemsListState = itemsList.getLayoutManager().onSaveInstanceState(); } @Override public void onDestroyView() { if (disposables != null) disposables.clear(); super.onDestroyView(); } @Override public void onDestroy() { if (disposables != null) disposables.dispose(); disposables = null; subscriptionService = null; super.onDestroy(); } /////////////////////////////////////////////////////////////////////////// // Fragment Views /////////////////////////////////////////////////////////////////////////// @Override protected void initViews(View rootView, Bundle savedInstanceState) { super.initViews(rootView, savedInstanceState); infoListAdapter = new InfoListAdapter(getActivity()); itemsList = rootView.findViewById(R.id.items_list); itemsList.setLayoutManager(new LinearLayoutManager(activity)); infoListAdapter.setHeader(headerRootLayout = activity.getLayoutInflater().inflate(R.layout.subscription_header, itemsList, false)); infoListAdapter.useMiniItemVariants(true); itemsList.setAdapter(infoListAdapter); } @Override protected void initListeners() { super.initListeners(); infoListAdapter.setOnChannelSelectedListener(new InfoItemBuilder.OnInfoItemSelectedListener() { @Override public void selected(ChannelInfoItem selectedItem) { // Requires the parent fragment to find holder for fragment replacement NavigationHelper.openChannelFragment(getParentFragment().getFragmentManager(), selectedItem.service_id, selectedItem.url, selectedItem.name); } }); headerRootLayout.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { NavigationHelper.openWhatsNewFragment(getParentFragment().getFragmentManager()); } }); } private void resetFragment() { if (disposables != null) disposables.clear(); if (infoListAdapter != null) infoListAdapter.clearStreamItemList(); } /////////////////////////////////////////////////////////////////////////// // Subscriptions Loader /////////////////////////////////////////////////////////////////////////// @Override public void startLoading(boolean forceLoad) { super.startLoading(forceLoad); resetFragment(); subscriptionService.getSubscription().toObservable() .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) .subscribe(getSubscriptionObserver()); } private Observer> getSubscriptionObserver() { return new Observer>() { @Override public void onSubscribe(Disposable d) { showLoading(); disposables.add(d); } @Override public void onNext(List subscriptions) { handleResult(subscriptions); } @Override public void onError(Throwable exception) { SubscriptionFragment.this.onError(exception); } @Override public void onComplete() { } }; } @Override public void handleResult(@NonNull List result) { super.handleResult(result); infoListAdapter.clearStreamItemList(); if (result.isEmpty()) { showEmptyState(); } else { infoListAdapter.addInfoItemList(getSubscriptionItems(result)); if (itemsListState != null) { itemsList.getLayoutManager().onRestoreInstanceState(itemsListState); itemsListState = null; } hideLoading(); } } private List getSubscriptionItems(List subscriptions) { List items = new ArrayList<>(); for (final SubscriptionEntity subscription : subscriptions) items.add(subscription.toChannelInfoItem()); Collections.sort(items, new Comparator() { @Override public int compare(InfoItem o1, InfoItem o2) { return o1.name.compareToIgnoreCase(o2.name); } }); return items; } /*////////////////////////////////////////////////////////////////////////// // Contract //////////////////////////////////////////////////////////////////////////*/ @Override public void showLoading() { super.showLoading(); animateView(itemsList, false, 100); } @Override public void hideLoading() { super.hideLoading(); animateView(itemsList, true, 200); } @Override public void showEmptyState() { super.showEmptyState(); animateView(itemsList, false, 200); } /////////////////////////////////////////////////////////////////////////// // Fragment Error Handling /////////////////////////////////////////////////////////////////////////// @Override protected boolean onError(Throwable exception) { resetFragment(); if (super.onError(exception)) return true; onUnrecoverableError(exception, UserAction.SOMETHING_ELSE, "none", "Subscriptions", R.string.general_error); return true; } }