Improve code style to be more consistent

This commit is contained in:
wb9688 2020-03-31 19:20:15 +02:00
parent 819e52cab3
commit fda5405e48
244 changed files with 10116 additions and 7222 deletions

View file

@ -5,16 +5,17 @@ import android.content.res.Configuration;
import android.content.res.Resources;
import android.os.Bundle;
import android.preference.PreferenceManager;
import androidx.fragment.app.Fragment;
import androidx.appcompat.app.ActionBar;
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 androidx.appcompat.app.ActionBar;
import androidx.fragment.app.Fragment;
import androidx.recyclerview.widget.GridLayoutManager;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import org.schabi.newpipe.R;
import org.schabi.newpipe.fragments.BaseStateFragment;
import org.schabi.newpipe.fragments.list.ListViewContract;
@ -25,10 +26,14 @@ import static org.schabi.newpipe.util.AnimationUtils.animateView;
* This fragment is design to be used with persistent data such as
* {@link org.schabi.newpipe.database.LocalItem}, and does not cache the data contained
* in the list adapter to avoid extra writes when the it exits or re-enters its lifecycle.
*
* <p>
* This fragment destroys its adapter and views when {@link Fragment#onDestroyView()} is
* called and is memory efficient when in backstack.
* */
* </p>
*
* @param <I> List of {@link org.schabi.newpipe.database.LocalItem}s
* @param <N> {@link Void}
*/
public abstract class BaseLocalListFragment<I, N> extends BaseStateFragment<I>
implements ListViewContract<I, N>, SharedPreferences.OnSharedPreferenceChangeListener {
@ -36,21 +41,19 @@ public abstract class BaseLocalListFragment<I, N> extends BaseStateFragment<I>
// Views
//////////////////////////////////////////////////////////////////////////*/
protected View headerRootView;
protected View footerRootView;
private static final int LIST_MODE_UPDATE_FLAG = 0x32;
private View headerRootView;
private View footerRootView;
protected LocalItemListAdapter itemListAdapter;
protected RecyclerView itemsList;
private int updateFlags = 0;
private static final int LIST_MODE_UPDATE_FLAG = 0x32;
/*//////////////////////////////////////////////////////////////////////////
// Lifecycle - Creation
//////////////////////////////////////////////////////////////////////////*/
@Override
public void onCreate(Bundle savedInstanceState) {
public void onCreate(final Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setHasOptionsMenu(true);
PreferenceManager.getDefaultSharedPreferences(activity)
@ -70,8 +73,9 @@ public abstract class BaseLocalListFragment<I, N> extends BaseStateFragment<I>
if (updateFlags != 0) {
if ((updateFlags & LIST_MODE_UPDATE_FLAG) != 0) {
final boolean useGrid = isGridLayout();
itemsList.setLayoutManager(useGrid ? getGridLayoutManager() : getListLayoutManager());
itemListAdapter.setGridItemVariants(useGrid);
itemsList.setLayoutManager(
useGrid ? getGridLayoutManager() : getListLayoutManager());
itemListAdapter.setUseGridVariant(useGrid);
itemListAdapter.notifyDataSetChanged();
}
updateFlags = 0;
@ -94,7 +98,8 @@ public abstract class BaseLocalListFragment<I, N> extends BaseStateFragment<I>
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(itemListAdapter.getSpanSizeLookup(spanCount));
return lm;
@ -105,7 +110,7 @@ public abstract class BaseLocalListFragment<I, N> extends BaseStateFragment<I>
}
@Override
protected void initViews(View rootView, Bundle savedInstanceState) {
protected void initViews(final View rootView, final Bundle savedInstanceState) {
super.initViews(rootView, savedInstanceState);
itemListAdapter = new LocalItemListAdapter(activity);
@ -114,9 +119,11 @@ public abstract class BaseLocalListFragment<I, N> extends BaseStateFragment<I>
itemsList = rootView.findViewById(R.id.items_list);
itemsList.setLayoutManager(useGrid ? getGridLayoutManager() : getListLayoutManager());
itemListAdapter.setGridItemVariants(useGrid);
itemListAdapter.setHeader(headerRootView = getListHeader());
itemListAdapter.setFooter(footerRootView = getListFooter());
itemListAdapter.setUseGridVariant(useGrid);
headerRootView = getListHeader();
itemListAdapter.setHeader(headerRootView);
footerRootView = getListFooter();
itemListAdapter.setFooter(footerRootView);
itemsList.setAdapter(itemListAdapter);
}
@ -131,13 +138,17 @@ public abstract class BaseLocalListFragment<I, N> extends BaseStateFragment<I>
//////////////////////////////////////////////////////////////////////////*/
@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 + "]");
}
final ActionBar supportActionBar = activity.getSupportActionBar();
if (supportActionBar == null) return;
if (supportActionBar == null) {
return;
}
supportActionBar.setDisplayShowTitleEnabled(true);
}
@ -158,7 +169,7 @@ public abstract class BaseLocalListFragment<I, N> extends BaseStateFragment<I>
//////////////////////////////////////////////////////////////////////////*/
@Override
public void startLoading(boolean forceLoad) {
public void startLoading(final boolean forceLoad) {
super.startLoading(forceLoad);
resetFragment();
}
@ -166,24 +177,36 @@ public abstract class BaseLocalListFragment<I, N> extends BaseStateFragment<I>
@Override
public void showLoading() {
super.showLoading();
if (itemsList != null) animateView(itemsList, false, 200);
if (headerRootView != null) animateView(headerRootView, false, 200);
if (itemsList != null) {
animateView(itemsList, false, 200);
}
if (headerRootView != null) {
animateView(headerRootView, false, 200);
}
}
@Override
public void hideLoading() {
super.hideLoading();
if (itemsList != null) animateView(itemsList, true, 200);
if (headerRootView != null) animateView(headerRootView, true, 200);
if (itemsList != null) {
animateView(itemsList, true, 200);
}
if (headerRootView != null) {
animateView(headerRootView, true, 200);
}
}
@Override
public void showError(String message, boolean showRetryButton) {
public void showError(final String message, final boolean showRetryButton) {
super.showError(message, showRetryButton);
showListFooter(false);
if (itemsList != null) animateView(itemsList, false, 200);
if (headerRootView != null) animateView(headerRootView, false, 200);
if (itemsList != null) {
animateView(itemsList, false, 200);
}
if (headerRootView != null) {
animateView(headerRootView, false, 200);
}
}
@Override
@ -194,14 +217,18 @@ public abstract class BaseLocalListFragment<I, N> extends BaseStateFragment<I>
@Override
public void showListFooter(final boolean show) {
if (itemsList == null) return;
if (itemsList == null) {
return;
}
itemsList.post(() -> {
if (itemListAdapter != null) itemListAdapter.showFooter(show);
if (itemListAdapter != null) {
itemListAdapter.showFooter(show);
}
});
}
@Override
public void handleNextItems(N result) {
public void handleNextItems(final N result) {
isLoading.set(false);
}
@ -210,30 +237,35 @@ public abstract class BaseLocalListFragment<I, N> extends BaseStateFragment<I>
//////////////////////////////////////////////////////////////////////////*/
protected void resetFragment() {
if (itemListAdapter != null) itemListAdapter.clearStreamItemList();
if (itemListAdapter != null) {
itemListAdapter.clearStreamItemList();
}
}
@Override
protected boolean onError(Throwable exception) {
protected boolean onError(final Throwable exception) {
resetFragment();
return super.onError(exception);
}
@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);
}
}
}

View file

@ -1,13 +1,14 @@
package org.schabi.newpipe.local;
import androidx.recyclerview.widget.RecyclerView;
import android.view.View;
import androidx.recyclerview.widget.RecyclerView;
public class HeaderFooterHolder extends RecyclerView.ViewHolder {
public View view;
public HeaderFooterHolder(View v) {
public HeaderFooterHolder(final View v) {
super(v);
view = v;
}
}
}

View file

@ -30,14 +30,12 @@ import org.schabi.newpipe.util.OnClickGesture;
*/
public class LocalItemBuilder {
private static final String TAG = LocalItemBuilder.class.toString();
private final Context context;
private final ImageLoader imageLoader = ImageLoader.getInstance();
private OnClickGesture<LocalItem> onSelectedListener;
public LocalItemBuilder(Context context) {
public LocalItemBuilder(final Context context) {
this.context = context;
}
@ -54,7 +52,7 @@ public class LocalItemBuilder {
return onSelectedListener;
}
public void setOnItemSelectedListener(OnClickGesture<LocalItem> listener) {
public void setOnItemSelectedListener(final OnClickGesture<LocalItem> listener) {
this.onSelectedListener = listener;
}
}

View file

@ -1,13 +1,14 @@
package org.schabi.newpipe.local;
import android.content.Context;
import android.util.Log;
import android.view.View;
import android.view.ViewGroup;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.recyclerview.widget.GridLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import android.util.Log;
import android.view.View;
import android.view.ViewGroup;
import org.schabi.newpipe.database.LocalItem;
import org.schabi.newpipe.database.stream.model.StreamStateEntity;
@ -50,7 +51,6 @@ import java.util.List;
*/
public class LocalItemListAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
private static final String TAG = LocalItemListAdapter.class.getSimpleName();
private static final boolean DEBUG = false;
@ -63,8 +63,8 @@ public class LocalItemListAdapter extends RecyclerView.Adapter<RecyclerView.View
private static final int STREAM_PLAYLIST_GRID_HOLDER_TYPE = 0x1004;
private static final int LOCAL_PLAYLIST_HOLDER_TYPE = 0x2000;
private static final int REMOTE_PLAYLIST_HOLDER_TYPE = 0x2001;
private static final int LOCAL_PLAYLIST_GRID_HOLDER_TYPE = 0x2002;
private static final int REMOTE_PLAYLIST_GRID_HOLDER_TYPE = 0x2004;
private static final int LOCAL_PLAYLIST_GRID_HOLDER_TYPE = 0x2002;
private static final int REMOTE_PLAYLIST_GRID_HOLDER_TYPE = 0x2004;
private final LocalItemBuilder localItemBuilder;
private final ArrayList<LocalItem> localItems;
@ -76,7 +76,7 @@ public class LocalItemListAdapter extends RecyclerView.Adapter<RecyclerView.View
private View header = null;
private View footer = null;
public LocalItemListAdapter(Context context) {
public LocalItemListAdapter(final Context context) {
recordManager = new HistoryRecordManager(context);
localItemBuilder = new LocalItemBuilder(context);
localItems = new ArrayList<>();
@ -84,7 +84,7 @@ public class LocalItemListAdapter extends RecyclerView.Adapter<RecyclerView.View
Localization.getPreferredLocale(context));
}
public void setSelectedListener(OnClickGesture<LocalItem> listener) {
public void setSelectedListener(final OnClickGesture<LocalItem> listener) {
localItemBuilder.setOnItemSelectedListener(listener);
}
@ -92,28 +92,34 @@ public class LocalItemListAdapter extends RecyclerView.Adapter<RecyclerView.View
localItemBuilder.setOnItemSelectedListener(null);
}
public void addItems(@Nullable List<? extends LocalItem> data) {
public void addItems(@Nullable final List<? extends LocalItem> data) {
if (data == null) {
return;
}
if (DEBUG) Log.d(TAG, "addItems() before > localItems.size() = " +
localItems.size() + ", data.size() = " + data.size());
if (DEBUG) {
Log.d(TAG, "addItems() before > localItems.size() = "
+ localItems.size() + ", data.size() = " + data.size());
}
int offsetStart = sizeConsideringHeader();
localItems.addAll(data);
if (DEBUG) Log.d(TAG, "addItems() after > offsetStart = " + offsetStart +
", localItems.size() = " + localItems.size() +
", header = " + header + ", footer = " + footer +
", showFooter = " + showFooter);
if (DEBUG) {
Log.d(TAG, "addItems() after > offsetStart = " + offsetStart + ", "
+ "localItems.size() = " + localItems.size() + ", "
+ "header = " + header + ", footer = " + footer + ", "
+ "showFooter = " + showFooter);
}
notifyItemRangeInserted(offsetStart, data.size());
if (footer != null && showFooter) {
int footerNow = sizeConsideringHeader();
notifyItemMoved(offsetStart, footerNow);
if (DEBUG) Log.d(TAG, "addItems() footer from " + offsetStart +
" to " + footerNow);
if (DEBUG) {
Log.d(TAG, "addItems() footer from " + offsetStart
+ " to " + footerNow);
}
}
}
@ -123,12 +129,16 @@ public class LocalItemListAdapter extends RecyclerView.Adapter<RecyclerView.View
notifyItemRemoved(index + (header != null ? 1 : 0));
}
public boolean swapItems(int fromAdapterPosition, int toAdapterPosition) {
public boolean swapItems(final int fromAdapterPosition, final int toAdapterPosition) {
final int actualFrom = adapterOffsetWithoutHeader(fromAdapterPosition);
final int actualTo = adapterOffsetWithoutHeader(toAdapterPosition);
if (actualFrom < 0 || actualTo < 0) return false;
if (actualFrom >= localItems.size() || actualTo >= localItems.size()) return false;
if (actualFrom < 0 || actualTo < 0) {
return false;
}
if (actualFrom >= localItems.size() || actualTo >= localItems.size()) {
return false;
}
localItems.add(actualTo, localItems.remove(actualFrom));
notifyItemMoved(fromAdapterPosition, toAdapterPosition);
@ -143,27 +153,36 @@ public class LocalItemListAdapter extends RecyclerView.Adapter<RecyclerView.View
notifyDataSetChanged();
}
public void setGridItemVariants(boolean useGridVariant) {
public void setUseGridVariant(final boolean useGridVariant) {
this.useGridVariant = useGridVariant;
}
public void setHeader(View header) {
public void setHeader(final View header) {
boolean changed = header != this.header;
this.header = header;
if (changed) notifyDataSetChanged();
if (changed) {
notifyDataSetChanged();
}
}
public void setFooter(View view) {
public void setFooter(final View view) {
this.footer = view;
}
public void showFooter(boolean show) {
if (DEBUG) Log.d(TAG, "showFooter() called with: show = [" + show + "]");
if (show == showFooter) return;
public void showFooter(final boolean show) {
if (DEBUG) {
Log.d(TAG, "showFooter() called with: show = [" + show + "]");
}
if (show == showFooter) {
return;
}
showFooter = show;
if (show) notifyItemInserted(sizeConsideringHeader());
else notifyItemRemoved(sizeConsideringHeader());
if (show) {
notifyItemInserted(sizeConsideringHeader());
} else {
notifyItemRemoved(sizeConsideringHeader());
}
}
private int adapterOffsetWithoutHeader(final int offset) {
@ -181,21 +200,27 @@ public class LocalItemListAdapter extends RecyclerView.Adapter<RecyclerView.View
@Override
public int getItemCount() {
int count = localItems.size();
if (header != null) count++;
if (footer != null && showFooter) count++;
if (header != null) {
count++;
}
if (footer != null && showFooter) {
count++;
}
if (DEBUG) {
Log.d(TAG, "getItemCount() called, count = " + count +
", localItems.size() = " + localItems.size() +
", header = " + header + ", footer = " + footer +
", showFooter = " + showFooter);
Log.d(TAG, "getItemCount() called, count = " + count + ", "
+ "localItems.size() = " + localItems.size() + ", "
+ "header = " + header + ", footer = " + footer + ", "
+ "showFooter = " + showFooter);
}
return count;
}
@Override
public int getItemViewType(int position) {
if (DEBUG) Log.d(TAG, "getItemViewType() called with: position = [" + position + "]");
if (DEBUG) {
Log.d(TAG, "getItemViewType() called with: position = [" + position + "]");
}
if (header != null && position == 0) {
return HEADER_TYPE;
@ -208,23 +233,34 @@ public class LocalItemListAdapter extends RecyclerView.Adapter<RecyclerView.View
final LocalItem item = localItems.get(position);
switch (item.getLocalItemType()) {
case PLAYLIST_LOCAL_ITEM: return useGridVariant ? LOCAL_PLAYLIST_GRID_HOLDER_TYPE : LOCAL_PLAYLIST_HOLDER_TYPE;
case PLAYLIST_REMOTE_ITEM: return useGridVariant ? REMOTE_PLAYLIST_GRID_HOLDER_TYPE : REMOTE_PLAYLIST_HOLDER_TYPE;
case PLAYLIST_LOCAL_ITEM:
return useGridVariant
? LOCAL_PLAYLIST_GRID_HOLDER_TYPE : LOCAL_PLAYLIST_HOLDER_TYPE;
case PLAYLIST_REMOTE_ITEM:
return useGridVariant
? REMOTE_PLAYLIST_GRID_HOLDER_TYPE : REMOTE_PLAYLIST_HOLDER_TYPE;
case PLAYLIST_STREAM_ITEM: return useGridVariant ? STREAM_PLAYLIST_GRID_HOLDER_TYPE : STREAM_PLAYLIST_HOLDER_TYPE;
case STATISTIC_STREAM_ITEM: return useGridVariant ? STREAM_STATISTICS_GRID_HOLDER_TYPE : STREAM_STATISTICS_HOLDER_TYPE;
case PLAYLIST_STREAM_ITEM:
return useGridVariant
? STREAM_PLAYLIST_GRID_HOLDER_TYPE : STREAM_PLAYLIST_HOLDER_TYPE;
case STATISTIC_STREAM_ITEM:
return useGridVariant
? STREAM_STATISTICS_GRID_HOLDER_TYPE : STREAM_STATISTICS_HOLDER_TYPE;
default:
Log.e(TAG, "No holder type has been considered for item: [" +
item.getLocalItemType() + "]");
Log.e(TAG, "No holder type has been considered for item: ["
+ item.getLocalItemType() + "]");
return -1;
}
}
@NonNull
@Override
public RecyclerView.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int type) {
if (DEBUG) Log.d(TAG, "onCreateViewHolder() called with: parent = [" +
parent + "], type = [" + type + "]");
public RecyclerView.ViewHolder onCreateViewHolder(@NonNull final ViewGroup parent,
final int type) {
if (DEBUG) {
Log.d(TAG, "onCreateViewHolder() called with: "
+ "parent = [" + parent + "], type = [" + type + "]");
}
switch (type) {
case HEADER_TYPE:
return new HeaderFooterHolder(header);
@ -253,15 +289,21 @@ public class LocalItemListAdapter extends RecyclerView.Adapter<RecyclerView.View
}
@Override
public void onBindViewHolder(@NonNull RecyclerView.ViewHolder holder, int position) {
if (DEBUG) Log.d(TAG, "onBindViewHolder() called with: holder = [" +
holder.getClass().getSimpleName() + "], position = [" + position + "]");
public void onBindViewHolder(@NonNull final RecyclerView.ViewHolder holder, int position) {
if (DEBUG) {
Log.d(TAG, "onBindViewHolder() called with: "
+ "holder = [" + holder.getClass().getSimpleName() + "], "
+ "position = [" + position + "]");
}
if (holder instanceof LocalItemHolder) {
// If header isn't null, offset the items by -1
if (header != null) position--;
if (header != null) {
position--;
}
((LocalItemHolder) holder).updateFromItem(localItems.get(position), recordManager, dateFormat);
((LocalItemHolder) holder)
.updateFromItem(localItems.get(position), recordManager, dateFormat);
} else if (holder instanceof HeaderFooterHolder && position == 0 && header != null) {
((HeaderFooterHolder) holder).view = header;
} else if (holder instanceof HeaderFooterHolder && position == sizeConsideringHeader()
@ -271,13 +313,16 @@ public class LocalItemListAdapter extends RecyclerView.Adapter<RecyclerView.View
}
@Override
public void onBindViewHolder(@NonNull RecyclerView.ViewHolder holder, int position, @NonNull List<Object> payloads) {
public void onBindViewHolder(@NonNull final RecyclerView.ViewHolder holder, final int position,
@NonNull final List<Object> payloads) {
if (!payloads.isEmpty() && holder instanceof LocalItemHolder) {
for (Object payload : payloads) {
if (payload instanceof StreamStateEntity) {
((LocalItemHolder) holder).updateState(localItems.get(header == null ? position : position - 1), recordManager);
((LocalItemHolder) holder).updateState(localItems
.get(header == null ? position : position - 1), recordManager);
} else if (payload instanceof Boolean) {
((LocalItemHolder) holder).updateState(localItems.get(header == null ? position : position - 1), recordManager);
((LocalItemHolder) holder).updateState(localItems
.get(header == null ? position : position - 1), recordManager);
}
}
} else {
@ -288,7 +333,7 @@ public class LocalItemListAdapter extends RecyclerView.Adapter<RecyclerView.View
public GridLayoutManager.SpanSizeLookup getSpanSizeLookup(final int spanCount) {
return new GridLayoutManager.SpanSizeLookup() {
@Override
public int getSpanSize(int position) {
public int getSpanSize(final int position) {
final int type = getItemViewType(position);
return type == HEADER_TYPE || type == FOOTER_TYPE ? spanCount : 1;
}

View file

@ -5,15 +5,15 @@ import android.app.AlertDialog.Builder;
import android.os.Bundle;
import android.os.Parcelable;
import android.util.Log;
import android.widget.EditText;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.fragment.app.FragmentManager;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.EditText;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.fragment.app.FragmentManager;
import io.reactivex.disposables.Disposable;
import org.reactivestreams.Subscriber;
import org.reactivestreams.Subscription;
import org.schabi.newpipe.NewPipeDatabase;
@ -39,10 +39,9 @@ import io.reactivex.Flowable;
import io.reactivex.Single;
import io.reactivex.android.schedulers.AndroidSchedulers;
import io.reactivex.disposables.CompositeDisposable;
import io.reactivex.disposables.Disposable;
public final class BookmarkFragment
extends BaseLocalListFragment<List<PlaylistLocalItem>, Void> {
public final class BookmarkFragment extends BaseLocalListFragment<List<PlaylistLocalItem>, Void> {
@State
protected Parcelable itemsListState;
@ -55,10 +54,26 @@ public final class BookmarkFragment
// Fragment LifeCycle - Creation
///////////////////////////////////////////////////////////////////////////
private static List<PlaylistLocalItem> merge(
final List<PlaylistMetadataEntry> localPlaylists,
final List<PlaylistRemoteEntity> remotePlaylists) {
List<PlaylistLocalItem> items = new ArrayList<>(
localPlaylists.size() + remotePlaylists.size());
items.addAll(localPlaylists);
items.addAll(remotePlaylists);
Collections.sort(items, (left, right) ->
left.getOrderingName().compareToIgnoreCase(right.getOrderingName()));
return items;
}
@Override
public void onCreate(Bundle savedInstanceState) {
public void onCreate(final Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if (activity == null) return;
if (activity == null) {
return;
}
final AppDatabase database = NewPipeDatabase.getInstance(activity);
localPlaylistManager = new LocalPlaylistManager(database);
remotePlaylistManager = new RemotePlaylistManager(database);
@ -67,41 +82,44 @@ public final class BookmarkFragment
@Nullable
@Override
public View onCreateView(@NonNull LayoutInflater inflater,
@Nullable ViewGroup container,
Bundle savedInstanceState) {
public View onCreateView(@NonNull final LayoutInflater inflater,
@Nullable final ViewGroup container,
final Bundle savedInstanceState) {
if(!useAsFrontPage) {
if (!useAsFrontPage) {
setTitle(activity.getString(R.string.tab_bookmarks));
}
return inflater.inflate(R.layout.fragment_bookmarks, container, false);
}
@Override
public void setUserVisibleHint(boolean isVisibleToUser) {
super.setUserVisibleHint(isVisibleToUser);
if (activity != null && isVisibleToUser) {
setTitle(activity.getString(R.string.tab_bookmarks));
}
}
///////////////////////////////////////////////////////////////////////////
// Fragment LifeCycle - Views
///////////////////////////////////////////////////////////////////////////
@Override
protected void initViews(View rootView, Bundle savedInstanceState) {
public void setUserVisibleHint(final boolean isVisibleToUser) {
super.setUserVisibleHint(isVisibleToUser);
if (activity != null && isVisibleToUser) {
setTitle(activity.getString(R.string.tab_bookmarks));
}
}
@Override
protected void initViews(final View rootView, final Bundle savedInstanceState) {
super.initViews(rootView, savedInstanceState);
}
///////////////////////////////////////////////////////////////////////////
// Fragment LifeCycle - Loading
///////////////////////////////////////////////////////////////////////////
@Override
protected void initListeners() {
super.initListeners();
itemListAdapter.setSelectedListener(new OnClickGesture<LocalItem>() {
@Override
public void selected(LocalItem selectedItem) {
public void selected(final LocalItem selectedItem) {
final FragmentManager fragmentManager = getFM();
if (selectedItem instanceof PlaylistMetadataEntry) {
@ -120,7 +138,7 @@ public final class BookmarkFragment
}
@Override
public void held(LocalItem selectedItem) {
public void held(final LocalItem selectedItem) {
if (selectedItem instanceof PlaylistMetadataEntry) {
showLocalDialog((PlaylistMetadataEntry) selectedItem);
} else if (selectedItem instanceof PlaylistRemoteEntity) {
@ -131,26 +149,20 @@ public final class BookmarkFragment
}
///////////////////////////////////////////////////////////////////////////
// Fragment LifeCycle - Loading
// Fragment LifeCycle - Destruction
///////////////////////////////////////////////////////////////////////////
@Override
public void startLoading(boolean forceLoad) {
public void startLoading(final boolean forceLoad) {
super.startLoading(forceLoad);
Flowable.combineLatest(
localPlaylistManager.getPlaylists(),
remotePlaylistManager.getPlaylists(),
BookmarkFragment::merge
).onBackpressureLatest()
.observeOn(AndroidSchedulers.mainThread())
.subscribe(getPlaylistsSubscriber());
Flowable.combineLatest(localPlaylistManager.getPlaylists(),
remotePlaylistManager.getPlaylists(), BookmarkFragment::merge)
.onBackpressureLatest()
.observeOn(AndroidSchedulers.mainThread())
.subscribe(getPlaylistsSubscriber());
}
///////////////////////////////////////////////////////////////////////////
// Fragment LifeCycle - Destruction
///////////////////////////////////////////////////////////////////////////
@Override
public void onPause() {
super.onPause();
@ -161,16 +173,26 @@ public final class BookmarkFragment
public void onDestroyView() {
super.onDestroyView();
if (disposables != null) disposables.clear();
if (databaseSubscription != null) databaseSubscription.cancel();
if (disposables != null) {
disposables.clear();
}
if (databaseSubscription != null) {
databaseSubscription.cancel();
}
databaseSubscription = null;
}
///////////////////////////////////////////////////////////////////////////
// Subscriptions Loader
///////////////////////////////////////////////////////////////////////////
@Override
public void onDestroy() {
super.onDestroy();
if (disposables != null) disposables.dispose();
if (disposables != null) {
disposables.dispose();
}
disposables = null;
localPlaylistManager = null;
@ -178,39 +200,41 @@ public final class BookmarkFragment
itemsListState = null;
}
///////////////////////////////////////////////////////////////////////////
// Subscriptions Loader
///////////////////////////////////////////////////////////////////////////
private Subscriber<List<PlaylistLocalItem>> getPlaylistsSubscriber() {
return new Subscriber<List<PlaylistLocalItem>>() {
@Override
public void onSubscribe(Subscription s) {
public void onSubscribe(final Subscription s) {
showLoading();
if (databaseSubscription != null) databaseSubscription.cancel();
if (databaseSubscription != null) {
databaseSubscription.cancel();
}
databaseSubscription = s;
databaseSubscription.request(1);
}
@Override
public void onNext(List<PlaylistLocalItem> subscriptions) {
public void onNext(final List<PlaylistLocalItem> subscriptions) {
handleResult(subscriptions);
if (databaseSubscription != null) databaseSubscription.request(1);
if (databaseSubscription != null) {
databaseSubscription.request(1);
}
}
@Override
public void onError(Throwable exception) {
public void onError(final Throwable exception) {
BookmarkFragment.this.onError(exception);
}
@Override
public void onComplete() {
}
public void onComplete() { }
};
}
///////////////////////////////////////////////////////////////////////////
// Fragment Error Handling
///////////////////////////////////////////////////////////////////////////
@Override
public void handleResult(@NonNull List<PlaylistLocalItem> result) {
public void handleResult(@NonNull final List<PlaylistLocalItem> result) {
super.handleResult(result);
itemListAdapter.clearStreamItemList();
@ -227,55 +251,58 @@ public final class BookmarkFragment
}
hideLoading();
}
///////////////////////////////////////////////////////////////////////////
// Fragment Error Handling
///////////////////////////////////////////////////////////////////////////
@Override
protected boolean onError(Throwable exception) {
if (super.onError(exception)) return true;
protected boolean onError(final Throwable exception) {
if (super.onError(exception)) {
return true;
}
onUnrecoverableError(exception, UserAction.SOMETHING_ELSE,
"none", "Bookmark", R.string.general_error);
return true;
}
@Override
protected void resetFragment() {
super.resetFragment();
if (disposables != null) disposables.clear();
}
///////////////////////////////////////////////////////////////////////////
// Utils
///////////////////////////////////////////////////////////////////////////
@Override
protected void resetFragment() {
super.resetFragment();
if (disposables != null) {
disposables.clear();
}
}
private void showRemoteDeleteDialog(final PlaylistRemoteEntity item) {
showDeleteDialog(item.getName(), remotePlaylistManager.deletePlaylist(item.getUid()));
}
private void showLocalDialog(PlaylistMetadataEntry selectedItem) {
private void showLocalDialog(final PlaylistMetadataEntry selectedItem) {
View dialogView = View.inflate(getContext(), R.layout.dialog_bookmark, null);
EditText editText = dialogView.findViewById(R.id.playlist_name_edit_text);
editText.setText(selectedItem.name);
Builder builder = new AlertDialog.Builder(activity);
builder.setView(dialogView)
.setPositiveButton(R.string.rename_playlist, (dialog, which) -> {
changeLocalPlaylistName(selectedItem.uid, editText.getText().toString());
})
.setNegativeButton(R.string.cancel, null)
.setNeutralButton(R.string.delete, (dialog, which) -> {
showDeleteDialog(selectedItem.name,
localPlaylistManager.deletePlaylist(selectedItem.uid));
dialog.dismiss();
})
.create()
.show();
.setPositiveButton(R.string.rename_playlist, (dialog, which) -> {
changeLocalPlaylistName(selectedItem.uid, editText.getText().toString());
})
.setNegativeButton(R.string.cancel, null)
.setNeutralButton(R.string.delete, (dialog, which) -> {
showDeleteDialog(selectedItem.name,
localPlaylistManager.deletePlaylist(selectedItem.uid));
dialog.dismiss();
})
.create()
.show();
}
private void showDeleteDialog(final String name, final Single<Integer> deleteReactor) {
if (activity == null || disposables == null) return;
if (activity == null || disposables == null) {
return;
}
new AlertDialog.Builder(activity)
.setTitle(name)
@ -284,40 +311,27 @@ public final class BookmarkFragment
.setPositiveButton(R.string.delete, (dialog, i) ->
disposables.add(deleteReactor
.observeOn(AndroidSchedulers.mainThread())
.subscribe(ignored -> {/*Do nothing on success*/}, this::onError))
.subscribe(ignored -> { /*Do nothing on success*/ }, this::onError))
)
.setNegativeButton(R.string.cancel, null)
.show();
}
private void changeLocalPlaylistName(long id, String name) {
private void changeLocalPlaylistName(final long id, final String name) {
if (localPlaylistManager == null) {
return;
}
if (DEBUG) {
Log.d(TAG, "Updating playlist id=[" + id +
"] with new name=[" + name + "] items");
Log.d(TAG, "Updating playlist id=[" + id + "] "
+ "with new name=[" + name + "] items");
}
localPlaylistManager.renamePlaylist(id, name);
final Disposable disposable = localPlaylistManager.renamePlaylist(id, name)
.observeOn(AndroidSchedulers.mainThread())
.subscribe(longs -> {/*Do nothing on success*/}, this::onError);
.observeOn(AndroidSchedulers.mainThread())
.subscribe(longs -> { /*Do nothing on success*/ }, this::onError);
disposables.add(disposable);
}
private static List<PlaylistLocalItem> merge(final List<PlaylistMetadataEntry> localPlaylists,
final List<PlaylistRemoteEntity> remotePlaylists) {
List<PlaylistLocalItem> items = new ArrayList<>(
localPlaylists.size() + remotePlaylists.size());
items.addAll(localPlaylists);
items.addAll(remotePlaylists);
Collections.sort(items, (left, right) ->
left.getOrderingName().compareToIgnoreCase(right.getOrderingName()));
return items;
}
}

View file

@ -69,13 +69,13 @@ public final class PlaylistAppendDialog extends PlaylistDialog {
//////////////////////////////////////////////////////////////////////////*/
@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.dialog_playlists, container);
}
@Override
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
public void onViewCreated(@NonNull final View view, @Nullable final Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
final LocalPlaylistManager playlistManager =
@ -84,9 +84,10 @@ public final class PlaylistAppendDialog extends PlaylistDialog {
playlistAdapter = new LocalItemListAdapter(getActivity());
playlistAdapter.setSelectedListener(new OnClickGesture<LocalItem>() {
@Override
public void selected(LocalItem selectedItem) {
if (!(selectedItem instanceof PlaylistMetadataEntry) || getStreams() == null)
public void selected(final LocalItem selectedItem) {
if (!(selectedItem instanceof PlaylistMetadataEntry) || getStreams() == null) {
return;
}
onPlaylistSelected(playlistManager, (PlaylistMetadataEntry) selectedItem,
getStreams());
}
@ -126,7 +127,9 @@ public final class PlaylistAppendDialog extends PlaylistDialog {
//////////////////////////////////////////////////////////////////////////*/
public void openCreatePlaylistDialog() {
if (getStreams() == null || getFragmentManager() == null) return;
if (getStreams() == null || getFragmentManager() == null) {
return;
}
PlaylistCreationDialog.newInstance(getStreams()).show(getFragmentManager(), TAG);
getDialog().dismiss();
@ -145,16 +148,19 @@ public final class PlaylistAppendDialog extends PlaylistDialog {
}
}
private void onPlaylistSelected(@NonNull LocalPlaylistManager manager,
@NonNull PlaylistMetadataEntry playlist,
@NonNull List<StreamEntity> streams) {
if (getStreams() == null) return;
private void onPlaylistSelected(@NonNull final LocalPlaylistManager manager,
@NonNull final PlaylistMetadataEntry playlist,
@NonNull final List<StreamEntity> streams) {
if (getStreams() == null) {
return;
}
final Toast successToast = Toast.makeText(getContext(),
R.string.playlist_add_stream_success, Toast.LENGTH_SHORT);
if (playlist.thumbnailUrl.equals("drawable://" + R.drawable.dummy_thumbnail_playlist)) {
playlistDisposables.add(manager.changePlaylistThumbnail(playlist.uid, streams.get(0).getThumbnailUrl())
playlistDisposables.add(manager
.changePlaylistThumbnail(playlist.uid, streams.get(0).getThumbnailUrl())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(ignored -> successToast.show()));
}

View file

@ -3,12 +3,13 @@ package org.schabi.newpipe.local.dialog;
import android.app.AlertDialog;
import android.app.Dialog;
import android.os.Bundle;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import android.view.View;
import android.widget.EditText;
import android.widget.Toast;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import org.schabi.newpipe.NewPipeDatabase;
import org.schabi.newpipe.R;
import org.schabi.newpipe.database.stream.model.StreamEntity;
@ -19,8 +20,6 @@ import java.util.List;
import io.reactivex.android.schedulers.AndroidSchedulers;
public final class PlaylistCreationDialog extends PlaylistDialog {
private static final String TAG = PlaylistCreationDialog.class.getCanonicalName();
public static PlaylistCreationDialog newInstance(final List<StreamEntity> streams) {
PlaylistCreationDialog dialog = new PlaylistCreationDialog();
dialog.setInfo(streams);
@ -33,8 +32,10 @@ public final class PlaylistCreationDialog extends PlaylistDialog {
@NonNull
@Override
public Dialog onCreateDialog(@Nullable Bundle savedInstanceState) {
if (getStreams() == null) return super.onCreateDialog(savedInstanceState);
public Dialog onCreateDialog(@Nullable final Bundle savedInstanceState) {
if (getStreams() == null) {
return super.onCreateDialog(savedInstanceState);
}
View dialogView = View.inflate(getContext(), R.layout.dialog_playlist_name, null);
EditText nameInput = dialogView.findViewById(R.id.playlist_name);

View file

@ -2,10 +2,11 @@ package org.schabi.newpipe.local.dialog;
import android.app.Dialog;
import android.os.Bundle;
import android.view.Window;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.fragment.app.DialogFragment;
import android.view.Window;
import org.schabi.newpipe.database.stream.model.StreamEntity;
import org.schabi.newpipe.util.StateSaver;
@ -14,7 +15,6 @@ import java.util.List;
import java.util.Queue;
public abstract class PlaylistDialog extends DialogFragment implements StateSaver.WriteRead {
private List<StreamEntity> streamEntities;
private StateSaver.SavedState savedState;
@ -32,7 +32,7 @@ public abstract class PlaylistDialog extends DialogFragment implements StateSave
//////////////////////////////////////////////////////////////////////////*/
@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
public void onCreate(@Nullable final Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
savedState = StateSaver.tryToRestore(savedInstanceState, this);
}
@ -45,7 +45,7 @@ public abstract class PlaylistDialog extends DialogFragment implements StateSave
@NonNull
@Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
public Dialog onCreateDialog(final Bundle savedInstanceState) {
final Dialog dialog = super.onCreateDialog(savedInstanceState);
//remove title
final Window window = dialog.getWindow();
@ -66,18 +66,18 @@ public abstract class PlaylistDialog extends DialogFragment implements StateSave
}
@Override
public void writeTo(Queue<Object> objectsToSave) {
public void writeTo(final Queue<Object> objectsToSave) {
objectsToSave.add(streamEntities);
}
@Override
@SuppressWarnings("unchecked")
public void readFrom(@NonNull Queue<Object> savedObjects) {
public void readFrom(@NonNull final Queue<Object> savedObjects) {
streamEntities = (List<StreamEntity>) savedObjects.poll();
}
@Override
public void onSaveInstanceState(Bundle outState) {
public void onSaveInstanceState(final Bundle outState) {
super.onSaveInstanceState(outState);
if (getActivity() != null) {
savedState = StateSaver.tryToSave(getActivity().isChangingConfigurations(),

View file

@ -41,7 +41,9 @@ import java.util.*
class FeedFragment : BaseListFragment<FeedState, Unit>() {
private lateinit var viewModel: FeedViewModel
@State @JvmField var listState: Parcelable? = null
@State
@JvmField
var listState: Parcelable? = null
private var groupId = FeedGroupEntity.GROUP_ALL_ID
private var groupName = ""
@ -49,13 +51,14 @@ class FeedFragment : BaseListFragment<FeedState, Unit>() {
init {
setHasOptionsMenu(true)
useDefaultStateSaving(false)
setUseDefaultStateSaving(false)
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
groupId = arguments?.getLong(KEY_GROUP_ID, FeedGroupEntity.GROUP_ALL_ID) ?: FeedGroupEntity.GROUP_ALL_ID
groupId = arguments?.getLong(KEY_GROUP_ID, FeedGroupEntity.GROUP_ALL_ID)
?: FeedGroupEntity.GROUP_ALL_ID
groupName = arguments?.getString(KEY_GROUP_NAME) ?: ""
}
@ -107,7 +110,7 @@ class FeedFragment : BaseListFragment<FeedState, Unit>() {
inflater.inflate(R.menu.menu_feed_fragment, menu)
if (useAsFrontPage) {
menu.findItem(R.id.menu_item_feed_help).setShowAsAction(MenuItem.SHOW_AS_ACTION_NEVER);
menu.findItem(R.id.menu_item_feed_help).setShowAsAction(MenuItem.SHOW_AS_ACTION_NEVER)
}
}
@ -324,4 +327,4 @@ class FeedFragment : BaseListFragment<FeedState, Unit>() {
return feedFragment
}
}
}
}

View file

@ -1,6 +1,7 @@
package org.schabi.newpipe.local.history;
import android.content.Context;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.recyclerview.widget.RecyclerView;
@ -14,19 +15,19 @@ import java.util.Date;
/**
* Adapter for history entries
* @param <E> the type of the entries
* This is an adapter for history entries.
*
* @param <E> the type of the entries
* @param <VH> the type of the view holder
*/
public abstract class HistoryEntryAdapter<E, VH extends RecyclerView.ViewHolder> extends RecyclerView.Adapter<VH> {
public abstract class HistoryEntryAdapter<E, VH extends RecyclerView.ViewHolder>
extends RecyclerView.Adapter<VH> {
private final ArrayList<E> mEntries;
private final DateFormat mDateFormat;
private final Context mContext;
private OnHistoryItemClickListener<E> onHistoryItemClickListener = null;
public HistoryEntryAdapter(Context context) {
public HistoryEntryAdapter(final Context context) {
super();
mContext = context;
mEntries = new ArrayList<>();
@ -34,7 +35,7 @@ public abstract class HistoryEntryAdapter<E, VH extends RecyclerView.ViewHolder>
Localization.getPreferredLocale(context));
}
public void setEntries(@NonNull Collection<E> historyEntries) {
public void setEntries(@NonNull final Collection<E> historyEntries) {
mEntries.clear();
mEntries.addAll(historyEntries);
notifyDataSetChanged();
@ -49,7 +50,7 @@ public abstract class HistoryEntryAdapter<E, VH extends RecyclerView.ViewHolder>
notifyDataSetChanged();
}
protected String getFormattedDate(Date date) {
protected String getFormattedDate(final Date date) {
return mDateFormat.format(date);
}
@ -63,10 +64,10 @@ public abstract class HistoryEntryAdapter<E, VH extends RecyclerView.ViewHolder>
}
@Override
public void onBindViewHolder(VH holder, int position) {
public void onBindViewHolder(final VH holder, final int position) {
final E entry = mEntries.get(position);
holder.itemView.setOnClickListener(v -> {
if(onHistoryItemClickListener != null) {
if (onHistoryItemClickListener != null) {
onHistoryItemClickListener.onHistoryItemClick(entry);
}
});
@ -83,14 +84,15 @@ public abstract class HistoryEntryAdapter<E, VH extends RecyclerView.ViewHolder>
}
@Override
public void onViewRecycled(VH holder) {
public void onViewRecycled(final VH holder) {
super.onViewRecycled(holder);
holder.itemView.setOnClickListener(null);
}
abstract void onBindViewHolder(VH holder, E entry, int position);
public void setOnHistoryItemClickListener(@Nullable OnHistoryItemClickListener<E> onHistoryItemClickListener) {
public void setOnHistoryItemClickListener(
@Nullable final OnHistoryItemClickListener<E> onHistoryItemClickListener) {
this.onHistoryItemClickListener = onHistoryItemClickListener;
}
@ -100,6 +102,7 @@ public abstract class HistoryEntryAdapter<E, VH extends RecyclerView.ViewHolder>
public interface OnHistoryItemClickListener<E> {
void onHistoryItemClick(E item);
void onHistoryItemLongClick(E item);
}
}

View file

@ -1,34 +0,0 @@
package org.schabi.newpipe.local.history;
import androidx.annotation.Nullable;
import org.schabi.newpipe.extractor.stream.AudioStream;
import org.schabi.newpipe.extractor.stream.StreamInfo;
import org.schabi.newpipe.extractor.stream.VideoStream;
public interface HistoryListener {
/**
* Called when a video is played
*
* @param streamInfo the stream info
* @param videoStream the video stream that is played. Can be null if it's not sure what
* quality was viewed (e.g. with Kodi).
*/
void onVideoPlayed(StreamInfo streamInfo, @Nullable VideoStream videoStream);
/**
* Called when the audio is played in the background
*
* @param streamInfo the stream info
* @param audioStream the audio stream that is played
*/
void onAudioPlayed(StreamInfo streamInfo, AudioStream audioStream);
/**
* Called when the user searched for something
*
* @param serviceId which service the search was done
* @param query what the user searched for
*/
void onSearch(int serviceId, String query);
}

View file

@ -21,6 +21,7 @@ package org.schabi.newpipe.local.history;
import android.content.Context;
import android.content.SharedPreferences;
import android.preference.PreferenceManager;
import androidx.annotation.NonNull;
import org.schabi.newpipe.NewPipeDatabase;
@ -55,7 +56,6 @@ import io.reactivex.Single;
import io.reactivex.schedulers.Schedulers;
public class HistoryRecordManager {
private final AppDatabase database;
private final StreamDAO streamTable;
private final StreamHistoryDAO streamHistoryTable;
@ -81,7 +81,9 @@ public class HistoryRecordManager {
///////////////////////////////////////////////////////
public Maybe<Long> onViewed(final StreamInfo info) {
if (!isStreamHistoryEnabled()) return Maybe.empty();
if (!isStreamHistoryEnabled()) {
return Maybe.empty();
}
final Date currentTime = new Date();
return Maybe.fromCallable(() -> database.runInTransaction(() -> {
@ -149,7 +151,9 @@ public class HistoryRecordManager {
///////////////////////////////////////////////////////
public Maybe<Long> onSearched(final int serviceId, final String search) {
if (!isSearchHistoryEnabled()) return Maybe.empty();
if (!isSearchHistoryEnabled()) {
return Maybe.empty();
}
final Date currentTime = new Date();
final SearchHistoryEntry newEntry = new SearchHistoryEntry(currentTime, serviceId, search);
@ -231,11 +235,13 @@ public class HistoryRecordManager {
public Single<StreamStateEntity[]> loadStreamState(final InfoItem info) {
return Single.fromCallable(() -> {
final List<StreamEntity> entities = streamTable.getStream(info.getServiceId(), info.getUrl()).blockingFirst();
final List<StreamEntity> entities = streamTable
.getStream(info.getServiceId(), info.getUrl()).blockingFirst();
if (entities.isEmpty()) {
return new StreamStateEntity[]{null};
}
final List<StreamStateEntity> states = streamStateTable.getState(entities.get(0).getUid()).blockingFirst();
final List<StreamStateEntity> states = streamStateTable
.getState(entities.get(0).getUid()).blockingFirst();
if (states.isEmpty()) {
return new StreamStateEntity[]{null};
}
@ -247,12 +253,14 @@ public class HistoryRecordManager {
return Single.fromCallable(() -> {
final List<StreamStateEntity> result = new ArrayList<>(infos.size());
for (InfoItem info : infos) {
final List<StreamEntity> entities = streamTable.getStream(info.getServiceId(), info.getUrl()).blockingFirst();
final List<StreamEntity> entities = streamTable
.getStream(info.getServiceId(), info.getUrl()).blockingFirst();
if (entities.isEmpty()) {
result.add(null);
continue;
}
final List<StreamStateEntity> states = streamStateTable.getState(entities.get(0).getUid()).blockingFirst();
final List<StreamStateEntity> states = streamStateTable
.getState(entities.get(0).getUid()).blockingFirst();
if (states.isEmpty()) {
result.add(null);
continue;
@ -263,7 +271,8 @@ public class HistoryRecordManager {
}).subscribeOn(Schedulers.io());
}
public Single<List<StreamStateEntity>> loadLocalStreamStateBatch(final List<? extends LocalItem> items) {
public Single<List<StreamStateEntity>> loadLocalStreamStateBatch(
final List<? extends LocalItem> items) {
return Single.fromCallable(() -> {
final List<StreamStateEntity> result = new ArrayList<>(items.size());
for (LocalItem item : items) {
@ -278,7 +287,8 @@ public class HistoryRecordManager {
result.add(null);
continue;
}
final List<StreamStateEntity> states = streamStateTable.getState(streamId).blockingFirst();
final List<StreamStateEntity> states = streamStateTable.getState(streamId)
.blockingFirst();
if (states.isEmpty()) {
result.add(null);
continue;

View file

@ -4,10 +4,6 @@ import android.app.Activity;
import android.content.Context;
import android.os.Bundle;
import android.os.Parcelable;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import com.google.android.material.snackbar.Snackbar;
import androidx.appcompat.app.AlertDialog;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuInflater;
@ -18,6 +14,12 @@ import android.widget.ImageView;
import android.widget.TextView;
import android.widget.Toast;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.appcompat.app.AlertDialog;
import com.google.android.material.snackbar.Snackbar;
import org.reactivestreams.Subscriber;
import org.reactivestreams.Subscription;
import org.schabi.newpipe.R;
@ -48,7 +50,10 @@ import io.reactivex.disposables.Disposable;
public class StatisticsPlaylistFragment
extends BaseLocalListFragment<List<StreamStatisticsEntry>, Void> {
private final CompositeDisposable disposables = new CompositeDisposable();
@State
Parcelable itemsListState;
private StatisticSortMode sortMode = StatisticSortMode.LAST_PLAYED;
private View headerPlayAllButton;
private View headerPopupButton;
private View headerBackgroundButton;
@ -56,55 +61,44 @@ public class StatisticsPlaylistFragment
private View sortButton;
private ImageView sortButtonIcon;
private TextView sortButtonText;
@State
protected Parcelable itemsListState;
/* Used for independent events */
private Subscription databaseSubscription;
private HistoryRecordManager recordManager;
private final CompositeDisposable disposables = new CompositeDisposable();
private enum StatisticSortMode {
LAST_PLAYED,
MOST_PLAYED,
}
StatisticSortMode sortMode = StatisticSortMode.LAST_PLAYED;
protected List<StreamStatisticsEntry> processResult(final List<StreamStatisticsEntry> results) {
private List<StreamStatisticsEntry> processResult(final List<StreamStatisticsEntry> results) {
switch (sortMode) {
case LAST_PLAYED:
Collections.sort(results, (left, right) ->
right.getLatestAccessDate().compareTo(left.getLatestAccessDate()));
right.getLatestAccessDate().compareTo(left.getLatestAccessDate()));
return results;
case MOST_PLAYED:
Collections.sort(results, (left, right) ->
Long.compare(right.getWatchCount(), left.getWatchCount()));
return results;
default: return null;
default:
return null;
}
}
@Override
public void onCreate(final Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
recordManager = new HistoryRecordManager(getContext());
}
///////////////////////////////////////////////////////////////////////////
// Fragment LifeCycle - Creation
///////////////////////////////////////////////////////////////////////////
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
recordManager = new HistoryRecordManager(getContext());
}
@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);
}
@Override
public void setUserVisibleHint(boolean isVisibleToUser) {
public void setUserVisibleHint(final boolean isVisibleToUser) {
super.setUserVisibleHint(isVisibleToUser);
if (activity != null && isVisibleToUser) {
setTitle(activity.getString(R.string.title_activity_history));
@ -112,27 +106,27 @@ public class StatisticsPlaylistFragment
}
@Override
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
public void onCreateOptionsMenu(final Menu menu, final MenuInflater inflater) {
super.onCreateOptionsMenu(menu, inflater);
inflater.inflate(R.menu.menu_history, menu);
}
@Override
protected void initViews(final View rootView, final Bundle savedInstanceState) {
super.initViews(rootView, savedInstanceState);
if (!useAsFrontPage) {
setTitle(getString(R.string.title_last_played));
}
}
///////////////////////////////////////////////////////////////////////////
// Fragment LifeCycle - Views
///////////////////////////////////////////////////////////////////////////
@Override
protected void initViews(View rootView, Bundle savedInstanceState) {
super.initViews(rootView, savedInstanceState);
if(!useAsFrontPage) {
setTitle(getString(R.string.title_last_played));
}
}
@Override
protected View getListHeader() {
final View headerRootLayout = activity.getLayoutInflater().inflate(R.layout.statistic_playlist_control,
itemsList, false);
final View headerRootLayout = activity.getLayoutInflater()
.inflate(R.layout.statistic_playlist_control, itemsList, false);
playlistCtrl = headerRootLayout.findViewById(R.id.playlist_control);
headerPlayAllButton = headerRootLayout.findViewById(R.id.playlist_ctrl_play_all_button);
headerPopupButton = headerRootLayout.findViewById(R.id.playlist_ctrl_play_popup_button);
@ -149,7 +143,7 @@ public class StatisticsPlaylistFragment
itemListAdapter.setSelectedListener(new OnClickGesture<LocalItem>() {
@Override
public void selected(LocalItem selectedItem) {
public void selected(final LocalItem selectedItem) {
if (selectedItem instanceof StreamStatisticsEntry) {
final StreamStatisticsEntry item = (StreamStatisticsEntry) selectedItem;
NavigationHelper.openVideoDetailFragment(getFM(),
@ -160,7 +154,7 @@ public class StatisticsPlaylistFragment
}
@Override
public void held(LocalItem selectedItem) {
public void held(final LocalItem selectedItem) {
if (selectedItem instanceof StreamStatisticsEntry) {
showStreamDialog((StreamStatisticsEntry) selectedItem);
}
@ -169,7 +163,7 @@ public class StatisticsPlaylistFragment
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
public boolean onOptionsItemSelected(final MenuItem item) {
switch (item.getItemId()) {
case R.id.action_history_clear:
new AlertDialog.Builder(activity)
@ -194,7 +188,8 @@ public class StatisticsPlaylistFragment
final Disposable onClearOrphans = recordManager.removeOrphanedRecords()
.observeOn(AndroidSchedulers.mainThread())
.subscribe(
howManyDeleted -> {},
howManyDeleted -> {
},
throwable -> ErrorActivity.reportError(getContext(),
throwable,
SettingsActivity.class, null,
@ -215,12 +210,8 @@ public class StatisticsPlaylistFragment
return true;
}
///////////////////////////////////////////////////////////////////////////
// Fragment LifeCycle - Loading
///////////////////////////////////////////////////////////////////////////
@Override
public void startLoading(boolean forceLoad) {
public void startLoading(final boolean forceLoad) {
super.startLoading(forceLoad);
recordManager.getStreamStatistics()
.observeOn(AndroidSchedulers.mainThread())
@ -228,7 +219,7 @@ public class StatisticsPlaylistFragment
}
///////////////////////////////////////////////////////////////////////////
// Fragment LifeCycle - Destruction
// Fragment LifeCycle - Loading
///////////////////////////////////////////////////////////////////////////
@Override
@ -237,16 +228,30 @@ public class StatisticsPlaylistFragment
itemsListState = itemsList.getLayoutManager().onSaveInstanceState();
}
///////////////////////////////////////////////////////////////////////////
// Fragment LifeCycle - Destruction
///////////////////////////////////////////////////////////////////////////
@Override
public void onDestroyView() {
super.onDestroyView();
if (itemListAdapter != null) itemListAdapter.unsetSelectedListener();
if (headerBackgroundButton != null) headerBackgroundButton.setOnClickListener(null);
if (headerPlayAllButton != null) headerPlayAllButton.setOnClickListener(null);
if (headerPopupButton != null) headerPopupButton.setOnClickListener(null);
if (itemListAdapter != null) {
itemListAdapter.unsetSelectedListener();
}
if (headerBackgroundButton != null) {
headerBackgroundButton.setOnClickListener(null);
}
if (headerPlayAllButton != null) {
headerPlayAllButton.setOnClickListener(null);
}
if (headerPopupButton != null) {
headerPopupButton.setOnClickListener(null);
}
if (databaseSubscription != null) databaseSubscription.cancel();
if (databaseSubscription != null) {
databaseSubscription.cancel();
}
databaseSubscription = null;
}
@ -257,29 +262,29 @@ public class StatisticsPlaylistFragment
itemsListState = null;
}
///////////////////////////////////////////////////////////////////////////
// Statistics Loader
///////////////////////////////////////////////////////////////////////////
private Subscriber<List<StreamStatisticsEntry>> getHistoryObserver() {
return new Subscriber<List<StreamStatisticsEntry>>() {
@Override
public void onSubscribe(Subscription s) {
public void onSubscribe(final Subscription s) {
showLoading();
if (databaseSubscription != null) databaseSubscription.cancel();
if (databaseSubscription != null) {
databaseSubscription.cancel();
}
databaseSubscription = s;
databaseSubscription.request(1);
}
@Override
public void onNext(List<StreamStatisticsEntry> streams) {
public void onNext(final List<StreamStatisticsEntry> streams) {
handleResult(streams);
if (databaseSubscription != null) databaseSubscription.request(1);
if (databaseSubscription != null) {
databaseSubscription.request(1);
}
}
@Override
public void onError(Throwable exception) {
public void onError(final Throwable exception) {
StatisticsPlaylistFragment.this.onError(exception);
}
@ -289,10 +294,16 @@ public class StatisticsPlaylistFragment
};
}
///////////////////////////////////////////////////////////////////////////
// Statistics Loader
///////////////////////////////////////////////////////////////////////////
@Override
public void handleResult(@NonNull List<StreamStatisticsEntry> result) {
public void handleResult(@NonNull final List<StreamStatisticsEntry> result) {
super.handleResult(result);
if (itemListAdapter == null) return;
if (itemListAdapter == null) {
return;
}
playlistCtrl.setVisibility(View.VISIBLE);
@ -319,52 +330,60 @@ public class StatisticsPlaylistFragment
hideLoading();
}
@Override
protected void resetFragment() {
super.resetFragment();
if (databaseSubscription != null) {
databaseSubscription.cancel();
}
}
///////////////////////////////////////////////////////////////////////////
// Fragment Error Handling
///////////////////////////////////////////////////////////////////////////
@Override
protected void resetFragment() {
super.resetFragment();
if (databaseSubscription != null) databaseSubscription.cancel();
}
@Override
protected boolean onError(Throwable exception) {
if (super.onError(exception)) return true;
protected boolean onError(final Throwable exception) {
if (super.onError(exception)) {
return true;
}
onUnrecoverableError(exception, UserAction.SOMETHING_ELSE,
"none", "History Statistics", R.string.general_error);
return true;
}
/*//////////////////////////////////////////////////////////////////////////
// Utils
//////////////////////////////////////////////////////////////////////////*/
private void toggleSortMode() {
if(sortMode == StatisticSortMode.LAST_PLAYED) {
if (sortMode == StatisticSortMode.LAST_PLAYED) {
sortMode = StatisticSortMode.MOST_PLAYED;
setTitle(getString(R.string.title_most_played));
sortButtonIcon.setImageResource(ThemeHelper.getIconByAttr(R.attr.history, getContext()));
sortButtonIcon
.setImageResource(ThemeHelper.getIconByAttr(R.attr.history, getContext()));
sortButtonText.setText(R.string.title_last_played);
} else {
sortMode = StatisticSortMode.LAST_PLAYED;
setTitle(getString(R.string.title_last_played));
sortButtonIcon.setImageResource(ThemeHelper.getIconByAttr(R.attr.filter, getContext()));
sortButtonIcon
.setImageResource(ThemeHelper.getIconByAttr(R.attr.filter, getContext()));
sortButtonText.setText(R.string.title_most_played);
}
startLoading(true);
}
private PlayQueue getPlayQueueStartingAt(StreamStatisticsEntry infoItem) {
/*//////////////////////////////////////////////////////////////////////////
// Utils
//////////////////////////////////////////////////////////////////////////*/
private PlayQueue getPlayQueueStartingAt(final StreamStatisticsEntry infoItem) {
return getPlayQueue(Math.max(itemListAdapter.getItemsList().indexOf(infoItem), 0));
}
private void showStreamDialog(final StreamStatisticsEntry 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;
}
final StreamInfoItem infoItem = item.toStreamInfoItem();
if (infoItem.getStreamType() == StreamType.AUDIO_STREAM) {
@ -384,29 +403,31 @@ public class StatisticsPlaylistFragment
StreamDialogEntry.append_playlist,
StreamDialogEntry.share);
StreamDialogEntry.start_here_on_popup.setCustomAction(
(fragment, infoItemDuplicate) -> NavigationHelper.playOnPopupPlayer(context, getPlayQueueStartingAt(item), true));
StreamDialogEntry.start_here_on_popup.setCustomAction((fragment, infoItemDuplicate) ->
NavigationHelper
.playOnPopupPlayer(context, getPlayQueueStartingAt(item), true));
}
StreamDialogEntry.start_here_on_background.setCustomAction(
(fragment, infoItemDuplicate) -> NavigationHelper.playOnBackgroundPlayer(context, getPlayQueueStartingAt(item), true));
StreamDialogEntry.start_here_on_background.setCustomAction((fragment, infoItemDuplicate) ->
NavigationHelper
.playOnBackgroundPlayer(context, getPlayQueueStartingAt(item), true));
StreamDialogEntry.delete.setCustomAction((fragment, infoItemDuplicate) ->
deleteEntry(Math.max(itemListAdapter.getItemsList().indexOf(item), 0)));
deleteEntry(Math.max(itemListAdapter.getItemsList().indexOf(item), 0)));
new InfoItemDialog(activity, infoItem, StreamDialogEntry.getCommands(context), (dialog, which) ->
StreamDialogEntry.clickOn(which, this, infoItem)).show();
new InfoItemDialog(activity, infoItem, StreamDialogEntry.getCommands(context),
(dialog, which) -> StreamDialogEntry.clickOn(which, this, infoItem)).show();
}
private void deleteEntry(final int index) {
final LocalItem infoItem = itemListAdapter.getItemsList()
.get(index);
if(infoItem instanceof StreamStatisticsEntry) {
if (infoItem instanceof StreamStatisticsEntry) {
final StreamStatisticsEntry entry = (StreamStatisticsEntry) infoItem;
final Disposable onDelete = recordManager.deleteStreamHistory(entry.getStreamId())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(
howManyDeleted -> {
if(getView() != null) {
if (getView() != null) {
Snackbar.make(getView(), R.string.one_item_deleted,
Snackbar.LENGTH_SHORT).show();
} else {
@ -441,5 +462,10 @@ public class StatisticsPlaylistFragment
}
return new SinglePlayQueue(streamInfoItems, index);
}
private enum StatisticSortMode {
LAST_PLAYED,
MOST_PLAYED,
}
}

View file

@ -1,9 +1,10 @@
package org.schabi.newpipe.local.holder;
import androidx.recyclerview.widget.RecyclerView;
import android.view.LayoutInflater;
import android.view.ViewGroup;
import androidx.recyclerview.widget.RecyclerView;
import org.schabi.newpipe.database.LocalItem;
import org.schabi.newpipe.local.LocalItemBuilder;
import org.schabi.newpipe.local.history.HistoryRecordManager;
@ -33,14 +34,15 @@ import java.text.DateFormat;
public abstract class LocalItemHolder extends RecyclerView.ViewHolder {
protected final LocalItemBuilder itemBuilder;
public LocalItemHolder(LocalItemBuilder itemBuilder, int layoutId, ViewGroup parent) {
super(LayoutInflater.from(itemBuilder.getContext())
.inflate(layoutId, parent, false));
public LocalItemHolder(final LocalItemBuilder itemBuilder, final int layoutId,
final ViewGroup parent) {
super(LayoutInflater.from(itemBuilder.getContext()).inflate(layoutId, parent, false));
this.itemBuilder = itemBuilder;
}
public abstract void updateFromItem(final LocalItem item, HistoryRecordManager historyRecordManager, final DateFormat dateFormat);
public abstract void updateFromItem(LocalItem item, HistoryRecordManager historyRecordManager,
DateFormat dateFormat);
public void updateState(final LocalItem localItem, HistoryRecordManager historyRecordManager) {
}
public void updateState(final LocalItem localItem,
final HistoryRecordManager historyRecordManager) { }
}

View file

@ -6,8 +6,8 @@ import org.schabi.newpipe.R;
import org.schabi.newpipe.local.LocalItemBuilder;
public class LocalPlaylistGridItemHolder extends LocalPlaylistItemHolder {
public LocalPlaylistGridItemHolder(LocalItemBuilder infoItemBuilder, ViewGroup parent) {
super(infoItemBuilder, R.layout.list_playlist_grid_item, parent);
}
public LocalPlaylistGridItemHolder(final LocalItemBuilder infoItemBuilder,
final ViewGroup parent) {
super(infoItemBuilder, R.layout.list_playlist_grid_item, parent);
}
}

View file

@ -12,18 +12,22 @@ import org.schabi.newpipe.util.ImageDisplayConstants;
import java.text.DateFormat;
public class LocalPlaylistItemHolder extends PlaylistItemHolder {
public LocalPlaylistItemHolder(LocalItemBuilder infoItemBuilder, ViewGroup parent) {
public LocalPlaylistItemHolder(final LocalItemBuilder infoItemBuilder, final ViewGroup parent) {
super(infoItemBuilder, parent);
}
LocalPlaylistItemHolder(LocalItemBuilder infoItemBuilder, int layoutId, ViewGroup parent) {
LocalPlaylistItemHolder(final LocalItemBuilder infoItemBuilder, final int layoutId,
final ViewGroup parent) {
super(infoItemBuilder, layoutId, parent);
}
@Override
public void updateFromItem(final LocalItem localItem, HistoryRecordManager historyRecordManager, final DateFormat dateFormat) {
if (!(localItem instanceof PlaylistMetadataEntry)) return;
public void updateFromItem(final LocalItem localItem,
final HistoryRecordManager historyRecordManager,
final DateFormat dateFormat) {
if (!(localItem instanceof PlaylistMetadataEntry)) {
return;
}
final PlaylistMetadataEntry item = (PlaylistMetadataEntry) localItem;
itemTitleView.setText(item.name);

View file

@ -6,8 +6,8 @@ import org.schabi.newpipe.R;
import org.schabi.newpipe.local.LocalItemBuilder;
public class LocalPlaylistStreamGridItemHolder extends LocalPlaylistStreamItemHolder {
public LocalPlaylistStreamGridItemHolder(LocalItemBuilder infoItemBuilder, ViewGroup parent) {
super(infoItemBuilder, R.layout.list_stream_playlist_grid_item, parent); //TODO
}
public LocalPlaylistStreamGridItemHolder(final LocalItemBuilder infoItemBuilder,
final ViewGroup parent) {
super(infoItemBuilder, R.layout.list_stream_playlist_grid_item, parent); // TODO
}
}

View file

@ -1,12 +1,13 @@
package org.schabi.newpipe.local.holder;
import androidx.core.content.ContextCompat;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.TextView;
import androidx.core.content.ContextCompat;
import org.schabi.newpipe.R;
import org.schabi.newpipe.database.LocalItem;
import org.schabi.newpipe.database.playlist.PlaylistStreamEntry;
@ -24,15 +25,15 @@ import java.util.ArrayList;
import java.util.concurrent.TimeUnit;
public class LocalPlaylistStreamItemHolder extends LocalItemHolder {
public final ImageView itemThumbnailView;
public final TextView itemVideoTitleView;
public final TextView itemAdditionalDetailsView;
private final TextView itemAdditionalDetailsView;
public final TextView itemDurationView;
public final View itemHandleView;
public final AnimatedProgressBar itemProgressView;
private final View itemHandleView;
private final AnimatedProgressBar itemProgressView;
LocalPlaylistStreamItemHolder(LocalItemBuilder infoItemBuilder, int layoutId, ViewGroup parent) {
LocalPlaylistStreamItemHolder(final LocalItemBuilder infoItemBuilder, final int layoutId,
final ViewGroup parent) {
super(infoItemBuilder, layoutId, parent);
itemThumbnailView = itemView.findViewById(R.id.itemThumbnailView);
@ -43,30 +44,41 @@ public class LocalPlaylistStreamItemHolder extends LocalItemHolder {
itemProgressView = itemView.findViewById(R.id.itemProgressView);
}
public LocalPlaylistStreamItemHolder(LocalItemBuilder infoItemBuilder, ViewGroup parent) {
public LocalPlaylistStreamItemHolder(final LocalItemBuilder infoItemBuilder,
final ViewGroup parent) {
this(infoItemBuilder, R.layout.list_stream_playlist_item, parent);
}
@Override
public void updateFromItem(final LocalItem localItem, HistoryRecordManager historyRecordManager, final DateFormat dateFormat) {
if (!(localItem instanceof PlaylistStreamEntry)) return;
public void updateFromItem(final LocalItem localItem,
final HistoryRecordManager historyRecordManager,
final DateFormat dateFormat) {
if (!(localItem instanceof PlaylistStreamEntry)) {
return;
}
final PlaylistStreamEntry item = (PlaylistStreamEntry) localItem;
itemVideoTitleView.setText(item.getStreamEntity().getTitle());
itemAdditionalDetailsView.setText(Localization.concatenateStrings(item.getStreamEntity().getUploader(),
NewPipe.getNameOfService(item.getStreamEntity().getServiceId())));
itemAdditionalDetailsView.setText(Localization
.concatenateStrings(item.getStreamEntity().getUploader(),
NewPipe.getNameOfService(item.getStreamEntity().getServiceId())));
if (item.getStreamEntity().getDuration() > 0) {
itemDurationView.setText(Localization.getDurationString(item.getStreamEntity().getDuration()));
itemDurationView.setText(Localization
.getDurationString(item.getStreamEntity().getDuration()));
itemDurationView.setBackgroundColor(ContextCompat.getColor(itemBuilder.getContext(),
R.color.duration_background_color));
itemDurationView.setVisibility(View.VISIBLE);
StreamStateEntity state = historyRecordManager.loadLocalStreamStateBatch(new ArrayList<LocalItem>() {{ add(localItem); }}).blockingGet().get(0);
StreamStateEntity state = historyRecordManager
.loadLocalStreamStateBatch(new ArrayList<LocalItem>() {{
add(localItem);
}}).blockingGet().get(0);
if (state != null) {
itemProgressView.setVisibility(View.VISIBLE);
itemProgressView.setMax((int) item.getStreamEntity().getDuration());
itemProgressView.setProgress((int) TimeUnit.MILLISECONDS.toSeconds(state.getProgressTime()));
itemProgressView.setProgress((int) TimeUnit.MILLISECONDS
.toSeconds(state.getProgressTime()));
} else {
itemProgressView.setVisibility(View.GONE);
}
@ -97,17 +109,25 @@ public class LocalPlaylistStreamItemHolder extends LocalItemHolder {
}
@Override
public void updateState(LocalItem localItem, HistoryRecordManager historyRecordManager) {
if (!(localItem instanceof PlaylistStreamEntry)) return;
public void updateState(final LocalItem localItem,
final HistoryRecordManager historyRecordManager) {
if (!(localItem instanceof PlaylistStreamEntry)) {
return;
}
final PlaylistStreamEntry item = (PlaylistStreamEntry) localItem;
StreamStateEntity state = historyRecordManager.loadLocalStreamStateBatch(new ArrayList<LocalItem>() {{ add(localItem); }}).blockingGet().get(0);
StreamStateEntity state = historyRecordManager
.loadLocalStreamStateBatch(new ArrayList<LocalItem>() {{
add(localItem);
}}).blockingGet().get(0);
if (state != null && item.getStreamEntity().getDuration() > 0) {
itemProgressView.setMax((int) item.getStreamEntity().getDuration());
if (itemProgressView.getVisibility() == View.VISIBLE) {
itemProgressView.setProgressAnimated((int) TimeUnit.MILLISECONDS.toSeconds(state.getProgressTime()));
itemProgressView.setProgressAnimated((int) TimeUnit.MILLISECONDS
.toSeconds(state.getProgressTime()));
} else {
itemProgressView.setProgress((int) TimeUnit.MILLISECONDS.toSeconds(state.getProgressTime()));
itemProgressView.setProgress((int) TimeUnit.MILLISECONDS
.toSeconds(state.getProgressTime()));
AnimationUtils.animateView(itemProgressView, true, 500);
}
} else if (itemProgressView.getVisibility() == View.VISIBLE) {
@ -118,8 +138,8 @@ public class LocalPlaylistStreamItemHolder extends LocalItemHolder {
private View.OnTouchListener getOnTouchListener(final PlaylistStreamEntry item) {
return (view, motionEvent) -> {
view.performClick();
if (itemBuilder != null && itemBuilder.getOnItemSelectedListener() != null &&
motionEvent.getActionMasked() == MotionEvent.ACTION_DOWN) {
if (itemBuilder != null && itemBuilder.getOnItemSelectedListener() != null
&& motionEvent.getActionMasked() == MotionEvent.ACTION_DOWN) {
itemBuilder.getOnItemSelectedListener().drag(item,
LocalPlaylistStreamItemHolder.this);
}

View file

@ -6,8 +6,8 @@ import org.schabi.newpipe.R;
import org.schabi.newpipe.local.LocalItemBuilder;
public class LocalStatisticStreamGridItemHolder extends LocalStatisticStreamItemHolder {
public LocalStatisticStreamGridItemHolder(LocalItemBuilder infoItemBuilder, ViewGroup parent) {
super(infoItemBuilder, R.layout.list_stream_grid_item, parent);
}
public LocalStatisticStreamGridItemHolder(final LocalItemBuilder infoItemBuilder,
final ViewGroup parent) {
super(infoItemBuilder, R.layout.list_stream_grid_item, parent);
}
}

View file

@ -1,12 +1,13 @@
package org.schabi.newpipe.local.holder;
import androidx.annotation.Nullable;
import androidx.core.content.ContextCompat;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.TextView;
import androidx.annotation.Nullable;
import androidx.core.content.ContextCompat;
import org.schabi.newpipe.R;
import org.schabi.newpipe.database.LocalItem;
import org.schabi.newpipe.database.stream.StreamStatisticsEntry;
@ -44,20 +45,21 @@ import java.util.concurrent.TimeUnit;
*/
public class LocalStatisticStreamItemHolder extends LocalItemHolder {
public final ImageView itemThumbnailView;
public final TextView itemVideoTitleView;
public final TextView itemUploaderView;
public final TextView itemDurationView;
@Nullable
public final TextView itemAdditionalDetails;
public final AnimatedProgressBar itemProgressView;
private final AnimatedProgressBar itemProgressView;
public LocalStatisticStreamItemHolder(LocalItemBuilder itemBuilder, ViewGroup parent) {
public LocalStatisticStreamItemHolder(final LocalItemBuilder itemBuilder,
final ViewGroup parent) {
this(itemBuilder, R.layout.list_stream_item, parent);
}
LocalStatisticStreamItemHolder(LocalItemBuilder infoItemBuilder, int layoutId, ViewGroup parent) {
LocalStatisticStreamItemHolder(final LocalItemBuilder infoItemBuilder, final int layoutId,
final ViewGroup parent) {
super(infoItemBuilder, layoutId, parent);
itemThumbnailView = itemView.findViewById(R.id.itemThumbnailView);
@ -70,32 +72,41 @@ public class LocalStatisticStreamItemHolder extends LocalItemHolder {
private String getStreamInfoDetailLine(final StreamStatisticsEntry entry,
final DateFormat dateFormat) {
final String watchCount = Localization.shortViewCount(itemBuilder.getContext(),
entry.getWatchCount());
final String watchCount = Localization
.shortViewCount(itemBuilder.getContext(), entry.getWatchCount());
final String uploadDate = dateFormat.format(entry.getLatestAccessDate());
final String serviceName = NewPipe.getNameOfService(entry.getStreamEntity().getServiceId());
return Localization.concatenateStrings(watchCount, uploadDate, serviceName);
}
@Override
public void updateFromItem(final LocalItem localItem, HistoryRecordManager historyRecordManager, final DateFormat dateFormat) {
if (!(localItem instanceof StreamStatisticsEntry)) return;
public void updateFromItem(final LocalItem localItem,
final HistoryRecordManager historyRecordManager,
final DateFormat dateFormat) {
if (!(localItem instanceof StreamStatisticsEntry)) {
return;
}
final StreamStatisticsEntry item = (StreamStatisticsEntry) localItem;
itemVideoTitleView.setText(item.getStreamEntity().getTitle());
itemUploaderView.setText(item.getStreamEntity().getUploader());
if (item.getStreamEntity().getDuration() > 0) {
itemDurationView.setText(Localization.getDurationString(item.getStreamEntity().getDuration()));
itemDurationView.
setText(Localization.getDurationString(item.getStreamEntity().getDuration()));
itemDurationView.setBackgroundColor(ContextCompat.getColor(itemBuilder.getContext(),
R.color.duration_background_color));
itemDurationView.setVisibility(View.VISIBLE);
StreamStateEntity state = historyRecordManager.loadLocalStreamStateBatch(new ArrayList<LocalItem>() {{ add(localItem); }}).blockingGet().get(0);
StreamStateEntity state = historyRecordManager
.loadLocalStreamStateBatch(new ArrayList<LocalItem>() {{
add(localItem);
}}).blockingGet().get(0);
if (state != null) {
itemProgressView.setVisibility(View.VISIBLE);
itemProgressView.setMax((int) item.getStreamEntity().getDuration());
itemProgressView.setProgress((int) TimeUnit.MILLISECONDS.toSeconds(state.getProgressTime()));
itemProgressView.setProgress((int) TimeUnit.MILLISECONDS
.toSeconds(state.getProgressTime()));
} else {
itemProgressView.setVisibility(View.GONE);
}
@ -128,17 +139,25 @@ public class LocalStatisticStreamItemHolder extends LocalItemHolder {
}
@Override
public void updateState(LocalItem localItem, HistoryRecordManager historyRecordManager) {
if (!(localItem instanceof StreamStatisticsEntry)) return;
public void updateState(final LocalItem localItem,
final HistoryRecordManager historyRecordManager) {
if (!(localItem instanceof StreamStatisticsEntry)) {
return;
}
final StreamStatisticsEntry item = (StreamStatisticsEntry) localItem;
StreamStateEntity state = historyRecordManager.loadLocalStreamStateBatch(new ArrayList<LocalItem>() {{ add(localItem); }}).blockingGet().get(0);
StreamStateEntity state = historyRecordManager
.loadLocalStreamStateBatch(new ArrayList<LocalItem>() {{
add(localItem);
}}).blockingGet().get(0);
if (state != null && item.getStreamEntity().getDuration() > 0) {
itemProgressView.setMax((int) item.getStreamEntity().getDuration());
if (itemProgressView.getVisibility() == View.VISIBLE) {
itemProgressView.setProgressAnimated((int) TimeUnit.MILLISECONDS.toSeconds(state.getProgressTime()));
itemProgressView.setProgressAnimated((int) TimeUnit.MILLISECONDS
.toSeconds(state.getProgressTime()));
} else {
itemProgressView.setProgress((int) TimeUnit.MILLISECONDS.toSeconds(state.getProgressTime()));
itemProgressView.setProgress((int) TimeUnit.MILLISECONDS
.toSeconds(state.getProgressTime()));
AnimationUtils.animateView(itemProgressView, true, 500);
}
} else if (itemProgressView.getVisibility() == View.VISIBLE) {

View file

@ -13,12 +13,12 @@ import java.text.DateFormat;
public abstract class PlaylistItemHolder extends LocalItemHolder {
public final ImageView itemThumbnailView;
public final TextView itemStreamCountView;
final TextView itemStreamCountView;
public final TextView itemTitleView;
public final TextView itemUploaderView;
public PlaylistItemHolder(LocalItemBuilder infoItemBuilder,
int layoutId, ViewGroup parent) {
public PlaylistItemHolder(final LocalItemBuilder infoItemBuilder, final int layoutId,
final ViewGroup parent) {
super(infoItemBuilder, layoutId, parent);
itemThumbnailView = itemView.findViewById(R.id.itemThumbnailView);
@ -27,12 +27,14 @@ public abstract class PlaylistItemHolder extends LocalItemHolder {
itemUploaderView = itemView.findViewById(R.id.itemUploaderView);
}
public PlaylistItemHolder(LocalItemBuilder infoItemBuilder, ViewGroup parent) {
public PlaylistItemHolder(final LocalItemBuilder infoItemBuilder, final ViewGroup parent) {
this(infoItemBuilder, R.layout.list_playlist_mini_item, parent);
}
@Override
public void updateFromItem(final LocalItem localItem, HistoryRecordManager historyRecordManager, final DateFormat dateFormat) {
public void updateFromItem(final LocalItem localItem,
final HistoryRecordManager historyRecordManager,
final DateFormat dateFormat) {
itemView.setOnClickListener(view -> {
if (itemBuilder.getOnItemSelectedListener() != null) {
itemBuilder.getOnItemSelectedListener().selected(localItem);

View file

@ -6,8 +6,8 @@ import org.schabi.newpipe.R;
import org.schabi.newpipe.local.LocalItemBuilder;
public class RemotePlaylistGridItemHolder extends RemotePlaylistItemHolder {
public RemotePlaylistGridItemHolder(LocalItemBuilder infoItemBuilder, ViewGroup parent) {
super(infoItemBuilder, R.layout.list_playlist_grid_item, parent);
}
public RemotePlaylistGridItemHolder(final LocalItemBuilder infoItemBuilder,
final ViewGroup parent) {
super(infoItemBuilder, R.layout.list_playlist_grid_item, parent);
}
}

View file

@ -1,5 +1,6 @@
package org.schabi.newpipe.local.holder;
import android.text.TextUtils;
import android.view.ViewGroup;
import org.schabi.newpipe.database.LocalItem;
@ -10,22 +11,26 @@ import org.schabi.newpipe.local.history.HistoryRecordManager;
import org.schabi.newpipe.util.ImageDisplayConstants;
import org.schabi.newpipe.util.Localization;
import android.text.TextUtils;
import java.text.DateFormat;
public class RemotePlaylistItemHolder extends PlaylistItemHolder {
public RemotePlaylistItemHolder(LocalItemBuilder infoItemBuilder, ViewGroup parent) {
public RemotePlaylistItemHolder(final LocalItemBuilder infoItemBuilder,
final ViewGroup parent) {
super(infoItemBuilder, parent);
}
RemotePlaylistItemHolder(LocalItemBuilder infoItemBuilder, int layoutId, ViewGroup parent) {
RemotePlaylistItemHolder(final LocalItemBuilder infoItemBuilder, final int layoutId,
final ViewGroup parent) {
super(infoItemBuilder, layoutId, parent);
}
@Override
public void updateFromItem(final LocalItem localItem, HistoryRecordManager historyRecordManager, final DateFormat dateFormat) {
if (!(localItem instanceof PlaylistRemoteEntity)) return;
public void updateFromItem(final LocalItem localItem,
final HistoryRecordManager historyRecordManager,
final DateFormat dateFormat) {
if (!(localItem instanceof PlaylistRemoteEntity)) {
return;
}
final PlaylistRemoteEntity item = (PlaylistRemoteEntity) localItem;
itemTitleView.setText(item.getName());
@ -33,7 +38,7 @@ public class RemotePlaylistItemHolder extends PlaylistItemHolder {
// Here is where the uploader name is set in the bookmarked playlists library
if (!TextUtils.isEmpty(item.getUploader())) {
itemUploaderView.setText(Localization.concatenateStrings(item.getUploader(),
NewPipe.getNameOfService(item.getServiceId())));
NewPipe.getNameOfService(item.getServiceId())));
} else {
itemUploaderView.setText(NewPipe.getNameOfService(item.getServiceId()));
}

View file

@ -53,27 +53,22 @@ import io.reactivex.subjects.PublishSubject;
import static org.schabi.newpipe.util.AnimationUtils.animateView;
public class LocalPlaylistFragment extends BaseLocalListFragment<List<PlaylistStreamEntry>, Void> {
// Save the list 10 seconds after the last change occurred
private static final long SAVE_DEBOUNCE_MILLIS = 10000;
private static final int MINIMUM_INITIAL_DRAG_VELOCITY = 12;
private View headerRootLayout;
private TextView headerTitleView;
private TextView headerStreamCount;
private View playlistControl;
private View headerPlayAllButton;
private View headerPopupButton;
private View headerBackgroundButton;
@State
protected Long playlistId;
@State
protected String name;
@State
protected Parcelable itemsListState;
private View headerRootLayout;
private TextView headerTitleView;
private TextView headerStreamCount;
private View playlistControl;
private View headerPlayAllButton;
private View headerPopupButton;
private View headerBackgroundButton;
private ItemTouchHelper itemTouchHelper;
private LocalPlaylistManager playlistManager;
@ -87,7 +82,7 @@ public class LocalPlaylistFragment extends BaseLocalListFragment<List<PlaylistSt
/* Has the playlist been modified (e.g. items reordered or deleted) */
private AtomicBoolean isModified;
public static LocalPlaylistFragment getInstance(long playlistId, String name) {
public static LocalPlaylistFragment getInstance(final long playlistId, final String name) {
LocalPlaylistFragment instance = new LocalPlaylistFragment();
instance.setInitialData(playlistId, name);
return instance;
@ -98,7 +93,7 @@ public class LocalPlaylistFragment extends BaseLocalListFragment<List<PlaylistSt
///////////////////////////////////////////////////////////////////////////
@Override
public void onCreate(Bundle savedInstanceState) {
public void onCreate(final Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
playlistManager = new LocalPlaylistManager(NewPipeDatabase.getInstance(getContext()));
debouncedSaveSignal = PublishSubject.create();
@ -110,9 +105,9 @@ public class LocalPlaylistFragment extends BaseLocalListFragment<List<PlaylistSt
}
@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);
}
@ -130,15 +125,15 @@ public class LocalPlaylistFragment extends BaseLocalListFragment<List<PlaylistSt
}
@Override
protected void initViews(View rootView, Bundle savedInstanceState) {
protected void initViews(final View rootView, final Bundle savedInstanceState) {
super.initViews(rootView, savedInstanceState);
setTitle(name);
}
@Override
protected View getListHeader() {
headerRootLayout = activity.getLayoutInflater().inflate(R.layout.local_playlist_header,
itemsList, false);
headerRootLayout = activity.getLayoutInflater()
.inflate(R.layout.local_playlist_header, itemsList, false);
headerTitleView = headerRootLayout.findViewById(R.id.playlist_title_view);
headerTitleView.setSelected(true);
@ -164,24 +159,28 @@ public class LocalPlaylistFragment extends BaseLocalListFragment<List<PlaylistSt
itemListAdapter.setSelectedListener(new OnClickGesture<LocalItem>() {
@Override
public void selected(LocalItem selectedItem) {
public void selected(final LocalItem selectedItem) {
if (selectedItem instanceof PlaylistStreamEntry) {
final PlaylistStreamEntry item = (PlaylistStreamEntry) selectedItem;
NavigationHelper.openVideoDetailFragment(getFragmentManager(),
item.getStreamEntity().getServiceId(), item.getStreamEntity().getUrl(), item.getStreamEntity().getTitle());
item.getStreamEntity().getServiceId(), item.getStreamEntity().getUrl(),
item.getStreamEntity().getTitle());
}
}
@Override
public void held(LocalItem selectedItem) {
public void held(final LocalItem selectedItem) {
if (selectedItem instanceof PlaylistStreamEntry) {
showStreamItemDialog((PlaylistStreamEntry) selectedItem);
}
}
@Override
public void drag(LocalItem selectedItem, RecyclerView.ViewHolder viewHolder) {
if (itemTouchHelper != null) itemTouchHelper.startDrag(viewHolder);
public void drag(final LocalItem selectedItem,
final RecyclerView.ViewHolder viewHolder) {
if (itemTouchHelper != null) {
itemTouchHelper.startDrag(viewHolder);
}
}
});
}
@ -193,22 +192,32 @@ public class LocalPlaylistFragment extends BaseLocalListFragment<List<PlaylistSt
@Override
public void showLoading() {
super.showLoading();
if (headerRootLayout != null) animateView(headerRootLayout, false, 200);
if (playlistControl != null) animateView(playlistControl, false, 200);
if (headerRootLayout != null) {
animateView(headerRootLayout, false, 200);
}
if (playlistControl != null) {
animateView(playlistControl, false, 200);
}
}
@Override
public void hideLoading() {
super.hideLoading();
if (headerRootLayout != null) animateView(headerRootLayout, true, 200);
if (playlistControl != null) animateView(playlistControl, true, 200);
if (headerRootLayout != null) {
animateView(headerRootLayout, true, 200);
}
if (playlistControl != null) {
animateView(playlistControl, true, 200);
}
}
@Override
public void startLoading(boolean forceLoad) {
public void startLoading(final boolean forceLoad) {
super.startLoading(forceLoad);
if (disposables != null) disposables.clear();
if (disposables != null) {
disposables.clear();
}
disposables.add(getDebouncedSaver());
isLoadingComplete.set(false);
@ -237,13 +246,25 @@ public class LocalPlaylistFragment extends BaseLocalListFragment<List<PlaylistSt
public void onDestroyView() {
super.onDestroyView();
if (itemListAdapter != null) itemListAdapter.unsetSelectedListener();
if (headerBackgroundButton != null) headerBackgroundButton.setOnClickListener(null);
if (headerPlayAllButton != null) headerPlayAllButton.setOnClickListener(null);
if (headerPopupButton != null) headerPopupButton.setOnClickListener(null);
if (itemListAdapter != null) {
itemListAdapter.unsetSelectedListener();
}
if (headerBackgroundButton != null) {
headerBackgroundButton.setOnClickListener(null);
}
if (headerPlayAllButton != null) {
headerPlayAllButton.setOnClickListener(null);
}
if (headerPopupButton != null) {
headerPopupButton.setOnClickListener(null);
}
if (databaseSubscription != null) databaseSubscription.cancel();
if (disposables != null) disposables.clear();
if (databaseSubscription != null) {
databaseSubscription.cancel();
}
if (disposables != null) {
disposables.clear();
}
databaseSubscription = null;
itemTouchHelper = null;
@ -252,8 +273,12 @@ public class LocalPlaylistFragment extends BaseLocalListFragment<List<PlaylistSt
@Override
public void onDestroy() {
super.onDestroy();
if (debouncedSaveSignal != null) debouncedSaveSignal.onComplete();
if (disposables != null) disposables.dispose();
if (debouncedSaveSignal != null) {
debouncedSaveSignal.onComplete();
}
if (disposables != null) {
disposables.dispose();
}
debouncedSaveSignal = null;
playlistManager = null;
@ -270,40 +295,46 @@ public class LocalPlaylistFragment extends BaseLocalListFragment<List<PlaylistSt
private Subscriber<List<PlaylistStreamEntry>> getPlaylistObserver() {
return new Subscriber<List<PlaylistStreamEntry>>() {
@Override
public void onSubscribe(Subscription s) {
public void onSubscribe(final Subscription s) {
showLoading();
isLoadingComplete.set(false);
if (databaseSubscription != null) databaseSubscription.cancel();
if (databaseSubscription != null) {
databaseSubscription.cancel();
}
databaseSubscription = s;
databaseSubscription.request(1);
}
@Override
public void onNext(List<PlaylistStreamEntry> streams) {
public void onNext(final List<PlaylistStreamEntry> streams) {
// Skip handling the result after it has been modified
if (isModified == null || !isModified.get()) {
handleResult(streams);
isLoadingComplete.set(true);
}
if (databaseSubscription != null) databaseSubscription.request(1);
if (databaseSubscription != null) {
databaseSubscription.request(1);
}
}
@Override
public void onError(Throwable exception) {
public void onError(final Throwable exception) {
LocalPlaylistFragment.this.onError(exception);
}
@Override
public void onComplete() {}
public void onComplete() { }
};
}
@Override
public void handleResult(@NonNull List<PlaylistStreamEntry> result) {
public void handleResult(@NonNull final List<PlaylistStreamEntry> result) {
super.handleResult(result);
if (itemListAdapter == null) return;
if (itemListAdapter == null) {
return;
}
itemListAdapter.clearStreamItemList();
@ -346,12 +377,16 @@ public class LocalPlaylistFragment extends BaseLocalListFragment<List<PlaylistSt
@Override
protected void resetFragment() {
super.resetFragment();
if (databaseSubscription != null) databaseSubscription.cancel();
if (databaseSubscription != null) {
databaseSubscription.cancel();
}
}
@Override
protected boolean onError(Throwable exception) {
if (super.onError(exception)) return true;
protected boolean onError(final Throwable exception) {
if (super.onError(exception)) {
return true;
}
onUnrecoverableError(exception, UserAction.SOMETHING_ELSE,
"none", "Local Playlist", R.string.general_error);
@ -363,7 +398,9 @@ public class LocalPlaylistFragment extends BaseLocalListFragment<List<PlaylistSt
//////////////////////////////////////////////////////////////////////////*/
private void createRenameDialog() {
if (playlistId == null || name == null || getContext() == null) return;
if (playlistId == null || name == null || getContext() == null) {
return;
}
final View dialogView = View.inflate(getContext(), R.layout.dialog_playlist_name, null);
EditText nameEdit = dialogView.findViewById(R.id.playlist_name);
@ -382,33 +419,37 @@ public class LocalPlaylistFragment extends BaseLocalListFragment<List<PlaylistSt
dialogBuilder.show();
}
private void changePlaylistName(final String name) {
if (playlistManager == null) return;
this.name = name;
setTitle(name);
if (DEBUG) {
Log.d(TAG, "Updating playlist id=[" + playlistId +
"] with new name=[" + name + "] items");
private void changePlaylistName(final String title) {
if (playlistManager == null) {
return;
}
final Disposable disposable = playlistManager.renamePlaylist(playlistId, name)
this.name = title;
setTitle(title);
if (DEBUG) {
Log.d(TAG, "Updating playlist id=[" + playlistId + "] "
+ "with new title=[" + title + "] items");
}
final Disposable disposable = playlistManager.renamePlaylist(playlistId, title)
.observeOn(AndroidSchedulers.mainThread())
.subscribe(longs -> {/*Do nothing on success*/}, this::onError);
.subscribe(longs -> { /*Do nothing on success*/ }, this::onError);
disposables.add(disposable);
}
private void changeThumbnailUrl(final String thumbnailUrl) {
if (playlistManager == null) return;
if (playlistManager == null) {
return;
}
final Toast successToast = Toast.makeText(getActivity(),
R.string.playlist_thumbnail_change_success,
Toast.LENGTH_SHORT);
if (DEBUG) {
Log.d(TAG, "Updating playlist id=[" + playlistId +
"] with new thumbnail url=[" + thumbnailUrl + "]");
Log.d(TAG, "Updating playlist id=[" + playlistId + "] "
+ "with new thumbnail url=[" + thumbnailUrl + "]");
}
final Disposable disposable = playlistManager
@ -422,7 +463,8 @@ public class LocalPlaylistFragment extends BaseLocalListFragment<List<PlaylistSt
String newThumbnailUrl;
if (!itemListAdapter.getItemsList().isEmpty()) {
newThumbnailUrl = ((PlaylistStreamEntry) itemListAdapter.getItemsList().get(0)).getStreamEntity().getThumbnailUrl();
newThumbnailUrl = ((PlaylistStreamEntry) itemListAdapter.getItemsList().get(0))
.getStreamEntity().getThumbnailUrl();
} else {
newThumbnailUrl = "drawable://" + R.drawable.dummy_thumbnail_playlist;
}
@ -431,25 +473,33 @@ public class LocalPlaylistFragment extends BaseLocalListFragment<List<PlaylistSt
}
private void deleteItem(final PlaylistStreamEntry item) {
if (itemListAdapter == null) return;
if (itemListAdapter == null) {
return;
}
itemListAdapter.removeItem(item);
if (playlistManager.getPlaylistThumbnail(playlistId).equals(item.getStreamEntity().getThumbnailUrl()))
if (playlistManager.getPlaylistThumbnail(playlistId)
.equals(item.getStreamEntity().getThumbnailUrl())) {
updateThumbnailUrl();
}
setVideoCount(itemListAdapter.getItemsList().size());
saveChanges();
}
private void saveChanges() {
if (isModified == null || debouncedSaveSignal == null) return;
if (isModified == null || debouncedSaveSignal == null) {
return;
}
isModified.set(true);
debouncedSaveSignal.onNext(System.currentTimeMillis());
}
private Disposable getDebouncedSaver() {
if (debouncedSaveSignal == null) return Disposables.empty();
if (debouncedSaveSignal == null) {
return Disposables.empty();
}
return debouncedSaveSignal
.debounce(SAVE_DEBOUNCE_MILLIS, TimeUnit.MILLISECONDS)
@ -458,13 +508,15 @@ public class LocalPlaylistFragment extends BaseLocalListFragment<List<PlaylistSt
}
private void saveImmediate() {
if (playlistManager == null || itemListAdapter == null) return;
if (playlistManager == null || itemListAdapter == null) {
return;
}
// List must be loaded and modified in order to save
if (isLoadingComplete == null || isModified == null ||
!isLoadingComplete.get() || !isModified.get()) {
Log.w(TAG, "Attempting to save playlist when local playlist " +
"is not loaded or not modified: playlist id=[" + playlistId + "]");
if (isLoadingComplete == null || isModified == null
|| !isLoadingComplete.get() || !isModified.get()) {
Log.w(TAG, "Attempting to save playlist when local playlist "
+ "is not loaded or not modified: playlist id=[" + playlistId + "]");
return;
}
@ -477,14 +529,18 @@ public class LocalPlaylistFragment extends BaseLocalListFragment<List<PlaylistSt
}
if (DEBUG) {
Log.d(TAG, "Updating playlist id=[" + playlistId +
"] with [" + streamIds.size() + "] items");
Log.d(TAG, "Updating playlist id=[" + playlistId + "] "
+ "with [" + streamIds.size() + "] items");
}
final Disposable disposable = playlistManager.updateJoin(playlistId, streamIds)
.observeOn(AndroidSchedulers.mainThread())
.subscribe(
() -> { if (isModified != null) isModified.set(false); },
() -> {
if (isModified != null) {
isModified.set(false);
}
},
this::onError
);
disposables.add(disposable);
@ -499,28 +555,33 @@ public class LocalPlaylistFragment extends BaseLocalListFragment<List<PlaylistSt
return new ItemTouchHelper.SimpleCallback(directions,
ItemTouchHelper.ACTION_STATE_IDLE) {
@Override
public int interpolateOutOfBoundsScroll(RecyclerView recyclerView, int viewSize,
int viewSizeOutOfBounds, int totalSize,
long msSinceStartScroll) {
final int standardSpeed = super.interpolateOutOfBoundsScroll(recyclerView, viewSize,
viewSizeOutOfBounds, totalSize, msSinceStartScroll);
public int interpolateOutOfBoundsScroll(final RecyclerView recyclerView,
final int viewSize,
final int viewSizeOutOfBounds,
final int totalSize,
final long msSinceStartScroll) {
final int standardSpeed = super.interpolateOutOfBoundsScroll(recyclerView,
viewSize, viewSizeOutOfBounds, totalSize, msSinceStartScroll);
final int minimumAbsVelocity = Math.max(MINIMUM_INITIAL_DRAG_VELOCITY,
Math.abs(standardSpeed));
return minimumAbsVelocity * (int) Math.signum(viewSizeOutOfBounds);
}
@Override
public boolean onMove(RecyclerView recyclerView, RecyclerView.ViewHolder source,
RecyclerView.ViewHolder target) {
if (source.getItemViewType() != target.getItemViewType() ||
itemListAdapter == null) {
public boolean onMove(final RecyclerView recyclerView,
final RecyclerView.ViewHolder source,
final RecyclerView.ViewHolder target) {
if (source.getItemViewType() != target.getItemViewType()
|| itemListAdapter == null) {
return false;
}
final int sourceIndex = source.getAdapterPosition();
final int targetIndex = target.getAdapterPosition();
final boolean isSwapped = itemListAdapter.swapItems(sourceIndex, targetIndex);
if (isSwapped) saveChanges();
if (isSwapped) {
saveChanges();
}
return isSwapped;
}
@ -535,7 +596,7 @@ public class LocalPlaylistFragment extends BaseLocalListFragment<List<PlaylistSt
}
@Override
public void onSwiped(RecyclerView.ViewHolder viewHolder, int swipeDir) {}
public void onSwiped(final RecyclerView.ViewHolder viewHolder, final int swipeDir) { }
};
}
@ -543,14 +604,16 @@ public class LocalPlaylistFragment extends BaseLocalListFragment<List<PlaylistSt
// Utils
//////////////////////////////////////////////////////////////////////////*/
private PlayQueue getPlayQueueStartingAt(PlaylistStreamEntry infoItem) {
private PlayQueue getPlayQueueStartingAt(final PlaylistStreamEntry infoItem) {
return getPlayQueue(Math.max(itemListAdapter.getItemsList().indexOf(infoItem), 0));
}
protected void showStreamItemDialog(final PlaylistStreamEntry 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;
}
final StreamInfoItem infoItem = item.toStreamInfoItem();
if (infoItem.getStreamType() == StreamType.AUDIO_STREAM) {
@ -573,23 +636,26 @@ public class LocalPlaylistFragment extends BaseLocalListFragment<List<PlaylistSt
StreamDialogEntry.share);
StreamDialogEntry.start_here_on_popup.setCustomAction(
(fragment, infoItemDuplicate) -> NavigationHelper.playOnPopupPlayer(context, getPlayQueueStartingAt(item), true));
(fragment, infoItemDuplicate) -> NavigationHelper.
playOnPopupPlayer(context, getPlayQueueStartingAt(item), true));
}
StreamDialogEntry.start_here_on_background.setCustomAction(
(fragment, infoItemDuplicate) -> NavigationHelper.playOnBackgroundPlayer(context, getPlayQueueStartingAt(item), true));
StreamDialogEntry.start_here_on_background.setCustomAction((fragment, infoItemDuplicate) ->
NavigationHelper.playOnBackgroundPlayer(context,
getPlayQueueStartingAt(item), true));
StreamDialogEntry.set_as_playlist_thumbnail.setCustomAction(
(fragment, infoItemDuplicate) -> changeThumbnailUrl(item.getStreamEntity().getThumbnailUrl()));
StreamDialogEntry.delete.setCustomAction(
(fragment, infoItemDuplicate) -> deleteItem(item));
(fragment, infoItemDuplicate) ->
changeThumbnailUrl(item.getStreamEntity().getThumbnailUrl()));
StreamDialogEntry.delete.setCustomAction((fragment, infoItemDuplicate) ->
deleteItem(item));
new InfoItemDialog(activity, infoItem, StreamDialogEntry.getCommands(context), (dialog, which) ->
StreamDialogEntry.clickOn(which, this, infoItem)).show();
new InfoItemDialog(activity, infoItem, StreamDialogEntry.getCommands(context),
(dialog, which) -> StreamDialogEntry.clickOn(which, this, infoItem)).show();
}
private void setInitialData(long playlistId, String name) {
this.playlistId = playlistId;
this.name = !TextUtils.isEmpty(name) ? name : "";
private void setInitialData(final long pid, final String title) {
this.playlistId = pid;
this.name = !TextUtils.isEmpty(title) ? title : "";
}
private void setVideoCount(final long count) {

View file

@ -22,7 +22,6 @@ import io.reactivex.Single;
import io.reactivex.schedulers.Schedulers;
public class LocalPlaylistManager {
private final AppDatabase database;
private final StreamDAO streamTable;
private final PlaylistDAO playlistTable;
@ -37,7 +36,9 @@ public class LocalPlaylistManager {
public Maybe<List<Long>> createPlaylist(final String name, final List<StreamEntity> streams) {
// Disallow creation of empty playlists
if (streams.isEmpty()) return Maybe.empty();
if (streams.isEmpty()) {
return Maybe.empty();
}
final StreamEntity defaultStream = streams.get(0);
final PlaylistEntity newPlaylist =
new PlaylistEntity(name, defaultStream.getThumbnailUrl());
@ -115,8 +116,12 @@ public class LocalPlaylistManager {
.filter(playlistEntities -> !playlistEntities.isEmpty())
.map(playlistEntities -> {
PlaylistEntity playlist = playlistEntities.get(0);
if (name != null) playlist.setName(name);
if (thumbnailUrl != null) playlist.setThumbnailUrl(thumbnailUrl);
if (name != null) {
playlist.setName(name);
}
if (thumbnailUrl != null) {
playlist.setThumbnailUrl(thumbnailUrl);
}
return playlistTable.update(playlist);
}).subscribeOn(Schedulers.io());
}

View file

@ -4,6 +4,7 @@ import android.app.AlertDialog;
import android.app.Dialog;
import android.content.Intent;
import android.os.Bundle;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.fragment.app.DialogFragment;
@ -21,21 +22,24 @@ public class ImportConfirmationDialog extends DialogFragment {
@State
protected Intent resultServiceIntent;
public void setResultServiceIntent(Intent resultServiceIntent) {
this.resultServiceIntent = resultServiceIntent;
}
public static void show(@NonNull Fragment fragment, @NonNull Intent resultServiceIntent) {
if (fragment.getFragmentManager() == null) return;
public static void show(@NonNull final Fragment fragment,
@NonNull final Intent resultServiceIntent) {
if (fragment.getFragmentManager() == null) {
return;
}
final ImportConfirmationDialog confirmationDialog = new ImportConfirmationDialog();
confirmationDialog.setResultServiceIntent(resultServiceIntent);
confirmationDialog.show(fragment.getFragmentManager(), null);
}
public void setResultServiceIntent(final Intent resultServiceIntent) {
this.resultServiceIntent = resultServiceIntent;
}
@NonNull
@Override
public Dialog onCreateDialog(@Nullable Bundle savedInstanceState) {
public Dialog onCreateDialog(@Nullable final Bundle savedInstanceState) {
assureCorrectAppLanguage(getContext());
return new AlertDialog.Builder(getContext(), ThemeHelper.getDialogTheme(getContext()))
.setMessage(R.string.import_network_expensive_warning)
@ -51,16 +55,18 @@ public class ImportConfirmationDialog extends DialogFragment {
}
@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
public void onCreate(@Nullable final Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if (resultServiceIntent == null) throw new IllegalStateException("Result intent is null");
if (resultServiceIntent == null) {
throw new IllegalStateException("Result intent is null");
}
Icepick.restoreInstanceState(this, savedInstanceState);
}
@Override
public void onSaveInstanceState(Bundle outState) {
public void onSaveInstanceState(final Bundle outState) {
super.onSaveInstanceState(outState);
Icepick.saveInstanceState(this, outState);
}

View file

@ -59,9 +59,15 @@ class SubscriptionFragment : BaseStateFragment<SubscriptionState>() {
private lateinit var feedGroupsSortMenuItem: HeaderWithMenuItem
private val subscriptionsSection = Section()
@State @JvmField var itemsListState: Parcelable? = null
@State @JvmField var feedGroupsListState: Parcelable? = null
@State @JvmField var importExportItemExpandedState: Boolean? = null
@State
@JvmField
var itemsListState: Parcelable? = null
@State
@JvmField
var feedGroupsListState: Parcelable? = null
@State
@JvmField
var importExportItemExpandedState: Boolean? = null
init {
setHasOptionsMenu(true)

View file

@ -3,11 +3,6 @@ package org.schabi.newpipe.local.subscription;
import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.StringRes;
import androidx.core.text.util.LinkifyCompat;
import androidx.appcompat.app.ActionBar;
import android.text.TextUtils;
import android.text.util.Linkify;
import android.view.LayoutInflater;
@ -17,6 +12,12 @@ import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.StringRes;
import androidx.appcompat.app.ActionBar;
import androidx.core.text.util.LinkifyCompat;
import com.nononsenseapps.filepicker.Utils;
import org.schabi.newpipe.BaseFragment;
@ -24,9 +25,9 @@ import org.schabi.newpipe.R;
import org.schabi.newpipe.extractor.NewPipe;
import org.schabi.newpipe.extractor.exceptions.ExtractionException;
import org.schabi.newpipe.extractor.subscription.SubscriptionExtractor;
import org.schabi.newpipe.local.subscription.services.SubscriptionsImportService;
import org.schabi.newpipe.report.ErrorActivity;
import org.schabi.newpipe.report.UserAction;
import org.schabi.newpipe.local.subscription.services.SubscriptionsImportService;
import org.schabi.newpipe.util.Constants;
import org.schabi.newpipe.util.FilePickerActivityHelper;
import org.schabi.newpipe.util.ServiceHelper;
@ -52,45 +53,44 @@ public class SubscriptionsImportFragment extends BaseFragment {
private String relatedUrl;
@StringRes
private int instructionsString;
private TextView infoTextView;
private EditText inputText;
public static SubscriptionsImportFragment getInstance(int serviceId) {
/*//////////////////////////////////////////////////////////////////////////
// Views
//////////////////////////////////////////////////////////////////////////*/
private Button inputButton;
public static SubscriptionsImportFragment getInstance(final int serviceId) {
SubscriptionsImportFragment instance = new SubscriptionsImportFragment();
instance.setInitialData(serviceId);
return instance;
}
public void setInitialData(int serviceId) {
public void setInitialData(final int serviceId) {
this.currentServiceId = serviceId;
}
/*//////////////////////////////////////////////////////////////////////////
// Views
//////////////////////////////////////////////////////////////////////////*/
private TextView infoTextView;
private EditText inputText;
private Button inputButton;
///////////////////////////////////////////////////////////////////////////
// Fragment LifeCycle
///////////////////////////////////////////////////////////////////////////
@Override
public void onCreate(Bundle savedInstanceState) {
public void onCreate(final Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setupServiceVariables();
if (supportedSources.isEmpty() && currentServiceId != Constants.NO_SERVICE_ID) {
ErrorActivity.reportError(activity, Collections.emptyList(), null, null, ErrorActivity.ErrorInfo.make(UserAction.SOMETHING_ELSE,
NewPipe.getNameOfService(currentServiceId), "Service don't support importing", R.string.general_error));
ErrorActivity.reportError(activity, Collections.emptyList(), null, null,
ErrorActivity.ErrorInfo.make(UserAction.SOMETHING_ELSE,
NewPipe.getNameOfService(currentServiceId),
"Service don't support importing", R.string.general_error));
activity.finish();
}
}
@Override
public void setUserVisibleHint(boolean isVisibleToUser) {
public void setUserVisibleHint(final boolean isVisibleToUser) {
super.setUserVisibleHint(isVisibleToUser);
if (isVisibleToUser) {
setTitle(getString(R.string.import_title));
@ -99,7 +99,9 @@ public class SubscriptionsImportFragment extends BaseFragment {
@Nullable
@Override
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, Bundle savedInstanceState) {
public View onCreateView(@NonNull final LayoutInflater inflater,
@Nullable final ViewGroup container,
final Bundle savedInstanceState) {
return inflater.inflate(R.layout.fragment_import, container, false);
}
@ -108,7 +110,7 @@ public class SubscriptionsImportFragment extends BaseFragment {
/////////////////////////////////////////////////////////////////////////*/
@Override
protected void initViews(View rootView, Bundle savedInstanceState) {
protected void initViews(final View rootView, final Bundle savedInstanceState) {
super.initViews(rootView, savedInstanceState);
inputButton = rootView.findViewById(R.id.input_button);
@ -116,7 +118,8 @@ public class SubscriptionsImportFragment extends BaseFragment {
infoTextView = rootView.findViewById(R.id.info_text_view);
// TODO: Support services that can import from more than one source (show the option to the user)
// TODO: Support services that can import from more than one source
// (show the option to the user)
if (supportedSources.contains(CHANNEL_URL)) {
inputButton.setText(R.string.import_title);
inputText.setVisibility(View.VISIBLE);
@ -151,13 +154,15 @@ public class SubscriptionsImportFragment extends BaseFragment {
private void onImportClicked() {
if (inputText.getVisibility() == View.VISIBLE) {
final String value = inputText.getText().toString();
if (!value.isEmpty()) onImportUrl(value);
if (!value.isEmpty()) {
onImportUrl(value);
}
} else {
onImportFile();
}
}
public void onImportUrl(String value) {
public void onImportUrl(final String value) {
ImportConfirmationDialog.show(this, new Intent(activity, SubscriptionsImportService.class)
.putExtra(KEY_MODE, CHANNEL_URL_MODE)
.putExtra(KEY_VALUE, value)
@ -165,20 +170,24 @@ public class SubscriptionsImportFragment extends BaseFragment {
}
public void onImportFile() {
startActivityForResult(FilePickerActivityHelper.chooseSingleFile(activity), REQUEST_IMPORT_FILE_CODE);
startActivityForResult(FilePickerActivityHelper.chooseSingleFile(activity),
REQUEST_IMPORT_FILE_CODE);
}
@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);
if (data == null) return;
if (data == null) {
return;
}
if (resultCode == Activity.RESULT_OK && requestCode == REQUEST_IMPORT_FILE_CODE && data.getData() != null) {
if (resultCode == Activity.RESULT_OK && requestCode == REQUEST_IMPORT_FILE_CODE
&& data.getData() != null) {
final String path = Utils.getFileForUri(data.getData()).getAbsolutePath();
ImportConfirmationDialog.show(this, new Intent(activity, SubscriptionsImportService.class)
.putExtra(KEY_MODE, INPUT_STREAM_MODE)
.putExtra(KEY_VALUE, path)
.putExtra(Constants.KEY_SERVICE_ID, currentServiceId));
ImportConfirmationDialog.show(this,
new Intent(activity, SubscriptionsImportService.class)
.putExtra(KEY_MODE, INPUT_STREAM_MODE).putExtra(KEY_VALUE, path)
.putExtra(Constants.KEY_SERVICE_ID, currentServiceId));
}
}
@ -189,7 +198,8 @@ public class SubscriptionsImportFragment extends BaseFragment {
private void setupServiceVariables() {
if (currentServiceId != Constants.NO_SERVICE_ID) {
try {
final SubscriptionExtractor extractor = NewPipe.getService(currentServiceId).getSubscriptionExtractor();
final SubscriptionExtractor extractor = NewPipe.getService(currentServiceId)
.getSubscriptionExtractor();
supportedSources = extractor.getSupportedSources();
relatedUrl = extractor.getRelatedUrl();
instructionsString = ServiceHelper.getImportInstructions(currentServiceId);
@ -203,7 +213,7 @@ public class SubscriptionsImportFragment extends BaseFragment {
instructionsString = 0;
}
private void setInfoText(String infoString) {
private void setInfoText(final String infoString) {
infoTextView.setText(infoString);
LinkifyCompat.addLinks(infoTextView, Linkify.WEB_URLS);
}

View file

@ -49,12 +49,22 @@ class FeedGroupDialog : DialogFragment() {
object DeleteScreen : ScreenState()
}
@State @JvmField var selectedIcon: FeedGroupIcon? = null
@State @JvmField var selectedSubscriptions: HashSet<Long> = HashSet()
@State @JvmField var currentScreen: ScreenState = InitialScreen
@State
@JvmField
var selectedIcon: FeedGroupIcon? = null
@State
@JvmField
var selectedSubscriptions: HashSet<Long> = HashSet()
@State
@JvmField
var currentScreen: ScreenState = InitialScreen
@State @JvmField var subscriptionsListState: Parcelable? = null
@State @JvmField var iconsListState: Parcelable? = null
@State
@JvmField
var subscriptionsListState: Parcelable? = null
@State
@JvmField
var iconsListState: Parcelable? = null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)

View file

@ -19,7 +19,8 @@ import icepick.State
import kotlinx.android.synthetic.main.dialog_feed_group_reorder.*
import org.schabi.newpipe.R
import org.schabi.newpipe.database.feed.model.FeedGroupEntity
import org.schabi.newpipe.local.subscription.dialog.FeedGroupReorderDialogViewModel.DialogEvent.*
import org.schabi.newpipe.local.subscription.dialog.FeedGroupReorderDialogViewModel.DialogEvent.ProcessingEvent
import org.schabi.newpipe.local.subscription.dialog.FeedGroupReorderDialogViewModel.DialogEvent.SuccessEvent
import org.schabi.newpipe.local.subscription.item.FeedGroupReorderItem
import org.schabi.newpipe.util.ThemeHelper
import java.util.*
@ -28,7 +29,9 @@ import kotlin.collections.ArrayList
class FeedGroupReorderDialog : DialogFragment() {
private lateinit var viewModel: FeedGroupReorderDialogViewModel
@State @JvmField var groupOrderedIdList = ArrayList<Long>()
@State
@JvmField
var groupOrderedIdList = ArrayList<Long>()
private val groupAdapter = GroupAdapter<GroupieViewHolder>()
private val itemTouchHelper = ItemTouchHelper(getItemTouchCallback())

View file

@ -2,8 +2,8 @@ package org.schabi.newpipe.local.subscription.item
import android.content.Context
import com.nostra13.universalimageloader.core.ImageLoader
import com.xwray.groupie.kotlinandroidextensions.Item
import com.xwray.groupie.kotlinandroidextensions.GroupieViewHolder
import com.xwray.groupie.kotlinandroidextensions.Item
import kotlinx.android.synthetic.main.list_channel_item.*
import org.schabi.newpipe.R
import org.schabi.newpipe.extractor.channel.ChannelInfoItem

View file

@ -1,7 +1,7 @@
package org.schabi.newpipe.local.subscription.item
import com.xwray.groupie.kotlinandroidextensions.Item
import com.xwray.groupie.kotlinandroidextensions.GroupieViewHolder
import com.xwray.groupie.kotlinandroidextensions.Item
import org.schabi.newpipe.R
class EmptyPlaceholderItem : Item() {

View file

@ -1,7 +1,7 @@
package org.schabi.newpipe.local.subscription.item
import com.xwray.groupie.kotlinandroidextensions.Item
import com.xwray.groupie.kotlinandroidextensions.GroupieViewHolder
import com.xwray.groupie.kotlinandroidextensions.Item
import org.schabi.newpipe.R
class FeedGroupAddItem : Item() {

View file

@ -1,7 +1,7 @@
package org.schabi.newpipe.local.subscription.item
import com.xwray.groupie.kotlinandroidextensions.Item
import com.xwray.groupie.kotlinandroidextensions.GroupieViewHolder
import com.xwray.groupie.kotlinandroidextensions.Item
import kotlinx.android.synthetic.main.feed_group_card_item.*
import org.schabi.newpipe.R
import org.schabi.newpipe.database.feed.model.FeedGroupEntity

View file

@ -6,8 +6,8 @@ import android.view.View
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import com.xwray.groupie.GroupAdapter
import com.xwray.groupie.kotlinandroidextensions.Item
import com.xwray.groupie.kotlinandroidextensions.GroupieViewHolder
import com.xwray.groupie.kotlinandroidextensions.Item
import kotlinx.android.synthetic.main.feed_item_carousel.*
import org.schabi.newpipe.R
import org.schabi.newpipe.local.subscription.decoration.FeedGroupCarouselDecoration

View file

@ -1,8 +1,8 @@
package org.schabi.newpipe.local.subscription.item
import android.view.View.OnClickListener
import com.xwray.groupie.kotlinandroidextensions.Item
import com.xwray.groupie.kotlinandroidextensions.GroupieViewHolder
import com.xwray.groupie.kotlinandroidextensions.Item
import kotlinx.android.synthetic.main.header_item.*
import org.schabi.newpipe.R

View file

@ -2,14 +2,15 @@ package org.schabi.newpipe.local.subscription.item
import android.content.Context
import androidx.annotation.DrawableRes
import com.xwray.groupie.kotlinandroidextensions.Item
import com.xwray.groupie.kotlinandroidextensions.GroupieViewHolder
import com.xwray.groupie.kotlinandroidextensions.Item
import kotlinx.android.synthetic.main.picker_icon_item.*
import org.schabi.newpipe.R
import org.schabi.newpipe.local.subscription.FeedGroupIcon
class PickerIconItem(context: Context, val icon: FeedGroupIcon) : Item() {
@DrawableRes val iconRes: Int = icon.getDrawableRes(context)
@DrawableRes
val iconRes: Int = icon.getDrawableRes(context)
override fun getLayout(): Int = R.layout.picker_icon_item

View file

@ -3,8 +3,8 @@ package org.schabi.newpipe.local.subscription.item
import android.view.View
import com.nostra13.universalimageloader.core.DisplayImageOptions
import com.nostra13.universalimageloader.core.ImageLoader
import com.xwray.groupie.kotlinandroidextensions.Item
import com.xwray.groupie.kotlinandroidextensions.GroupieViewHolder
import com.xwray.groupie.kotlinandroidextensions.Item
import kotlinx.android.synthetic.main.picker_subscription_item.*
import org.schabi.newpipe.R
import org.schabi.newpipe.database.subscription.SubscriptionEntity

View file

@ -23,13 +23,14 @@ import android.app.Service;
import android.content.Intent;
import android.os.Build;
import android.os.IBinder;
import android.text.TextUtils;
import android.widget.Toast;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.StringRes;
import androidx.core.app.NotificationCompat;
import androidx.core.app.NotificationManagerCompat;
import android.text.TextUtils;
import android.widget.Toast;
import org.reactivestreams.Publisher;
import org.schabi.newpipe.R;
@ -52,17 +53,36 @@ import io.reactivex.processors.PublishProcessor;
public abstract class BaseImportExportService extends Service {
protected final String TAG = this.getClass().getSimpleName();
protected NotificationManagerCompat notificationManager;
protected NotificationCompat.Builder notificationBuilder;
protected SubscriptionManager subscriptionManager;
private static final int NOTIFICATION_SAMPLING_PERIOD = 2500;
protected final CompositeDisposable disposables = new CompositeDisposable();
protected final PublishProcessor<String> notificationUpdater = PublishProcessor.create();
protected final AtomicInteger currentProgress = new AtomicInteger(-1);
protected final AtomicInteger maxProgress = new AtomicInteger(-1);
protected final ImportExportEventListener eventListener = new ImportExportEventListener() {
@Override
public void onSizeReceived(final int size) {
maxProgress.set(size);
currentProgress.set(0);
}
@Override
public void onItemCompleted(final String itemName) {
currentProgress.incrementAndGet();
notificationUpdater.onNext(itemName);
}
};
protected NotificationManagerCompat notificationManager;
protected NotificationCompat.Builder notificationBuilder;
protected SubscriptionManager subscriptionManager;
/*//////////////////////////////////////////////////////////////////////////
// Notification Impl
//////////////////////////////////////////////////////////////////////////*/
protected Toast toast;
@Nullable
@Override
public IBinder onBind(Intent intent) {
public IBinder onBind(final Intent intent) {
return null;
}
@ -83,29 +103,8 @@ public abstract class BaseImportExportService extends Service {
disposables.clear();
}
/*//////////////////////////////////////////////////////////////////////////
// Notification Impl
//////////////////////////////////////////////////////////////////////////*/
private static final int NOTIFICATION_SAMPLING_PERIOD = 2500;
protected final AtomicInteger currentProgress = new AtomicInteger(-1);
protected final AtomicInteger maxProgress = new AtomicInteger(-1);
protected final ImportExportEventListener eventListener = new ImportExportEventListener() {
@Override
public void onSizeReceived(int size) {
maxProgress.set(size);
currentProgress.set(0);
}
@Override
public void onItemCompleted(String itemName) {
currentProgress.incrementAndGet();
notificationUpdater.onNext(itemName);
}
};
protected abstract int getNotificationId();
@StringRes
public abstract int getTitle();
@ -114,8 +113,9 @@ public abstract class BaseImportExportService extends Service {
notificationBuilder = createNotification();
startForeground(getNotificationId(), notificationBuilder.build());
final Function<Flowable<String>, Publisher<String>> throttleAfterFirstEmission = flow -> flow.limit(1)
.concatWith(flow.skip(1).throttleLast(NOTIFICATION_SAMPLING_PERIOD, TimeUnit.MILLISECONDS));
final Function<Flowable<String>, Publisher<String>> throttleAfterFirstEmission = flow ->
flow.limit(1).concatWith(flow.skip(1)
.throttleLast(NOTIFICATION_SAMPLING_PERIOD, TimeUnit.MILLISECONDS));
disposables.add(notificationUpdater
.filter(s -> !s.isEmpty())
@ -124,17 +124,20 @@ public abstract class BaseImportExportService extends Service {
.subscribe(this::updateNotification));
}
protected void updateNotification(String text) {
notificationBuilder.setProgress(maxProgress.get(), currentProgress.get(), maxProgress.get() == -1);
protected void updateNotification(final String text) {
notificationBuilder
.setProgress(maxProgress.get(), currentProgress.get(), maxProgress.get() == -1);
final String progressText = currentProgress + "/" + maxProgress;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
if (!TextUtils.isEmpty(text)) text = text + " (" + progressText + ")";
if (!TextUtils.isEmpty(text)) {
notificationBuilder.setContentText(text + " (" + progressText + ")");
}
} else {
notificationBuilder.setContentInfo(progressText);
notificationBuilder.setContentText(text);
}
if (!TextUtils.isEmpty(text)) notificationBuilder.setContentText(text);
notificationManager.notify(getNotificationId(), notificationBuilder.build());
}
@ -142,16 +145,16 @@ public abstract class BaseImportExportService extends Service {
postErrorResult(null, null);
}
protected void stopAndReportError(@Nullable Throwable error, String request) {
protected void stopAndReportError(@Nullable final Throwable error, final String request) {
stopService();
final ErrorActivity.ErrorInfo errorInfo = ErrorActivity.ErrorInfo.make(UserAction.SUBSCRIPTION, "unknown",
request, R.string.general_error);
ErrorActivity.reportError(this, error != null ? Collections.singletonList(error) : Collections.emptyList(),
null, null, errorInfo);
final ErrorActivity.ErrorInfo errorInfo = ErrorActivity.ErrorInfo
.make(UserAction.SUBSCRIPTION, "unknown", request, R.string.general_error);
ErrorActivity.reportError(this, error != null ? Collections.singletonList(error)
: Collections.emptyList(), null, null, errorInfo);
}
protected void postErrorResult(String title, String text) {
protected void postErrorResult(final String title, final String text) {
disposeAll();
stopForeground(true);
stopSelf();
@ -160,16 +163,21 @@ public abstract class BaseImportExportService extends Service {
return;
}
text = text == null ? "" : text;
notificationBuilder = new NotificationCompat.Builder(this, getString(R.string.notification_channel_id))
final String textOrEmpty = text == null ? "" : text;
notificationBuilder = new NotificationCompat
.Builder(this, getString(R.string.notification_channel_id))
.setSmallIcon(R.drawable.ic_newpipe_triangle_white)
.setVisibility(NotificationCompat.VISIBILITY_PUBLIC)
.setContentTitle(title)
.setStyle(new NotificationCompat.BigTextStyle().bigText(text))
.setContentText(text);
.setStyle(new NotificationCompat.BigTextStyle().bigText(textOrEmpty))
.setContentText(textOrEmpty);
notificationManager.notify(getNotificationId(), notificationBuilder.build());
}
/*//////////////////////////////////////////////////////////////////////////
// Toast
//////////////////////////////////////////////////////////////////////////*/
protected NotificationCompat.Builder createNotification() {
return new NotificationCompat.Builder(this, getString(R.string.notification_channel_id))
.setOngoing(true)
@ -179,18 +187,14 @@ public abstract class BaseImportExportService extends Service {
.setContentTitle(getString(getTitle()));
}
/*//////////////////////////////////////////////////////////////////////////
// Toast
//////////////////////////////////////////////////////////////////////////*/
protected Toast toast;
protected void showToast(@StringRes int message) {
protected void showToast(@StringRes final int message) {
showToast(getString(message));
}
protected void showToast(String message) {
if (toast != null) toast.cancel();
protected void showToast(final String message) {
if (toast != null) {
toast.cancel();
}
toast = Toast.makeText(this, message, Toast.LENGTH_SHORT);
toast.show();
@ -200,7 +204,7 @@ public abstract class BaseImportExportService extends Service {
// Error handling
//////////////////////////////////////////////////////////////////////////*/
protected void handleError(@StringRes int errorTitle, @NonNull Throwable error) {
protected void handleError(@StringRes final int errorTitle, @NonNull final Throwable error) {
String message = getErrorMessage(error);
if (TextUtils.isEmpty(message)) {
@ -212,7 +216,7 @@ public abstract class BaseImportExportService extends Service {
postErrorResult(getString(errorTitle), message);
}
protected String getErrorMessage(Throwable error) {
protected String getErrorMessage(final Throwable error) {
String message = null;
if (error instanceof SubscriptionExtractor.InvalidSourceException) {
message = getString(R.string.invalid_source);

View file

@ -14,4 +14,4 @@ public interface ImportExportEventListener {
* @param itemName the name of the subscription item
*/
void onItemCompleted(String itemName);
}
}

View file

@ -41,8 +41,7 @@ import java.util.List;
* A JSON implementation capable of importing and exporting subscriptions, it has the advantage
* of being able to transfer subscriptions to any device.
*/
public class ImportExportJsonHelper {
public final class ImportExportJsonHelper {
/*//////////////////////////////////////////////////////////////////////////
// Json implementation
//////////////////////////////////////////////////////////////////////////*/
@ -56,21 +55,30 @@ public class ImportExportJsonHelper {
private static final String JSON_URL_KEY = "url";
private static final String JSON_NAME_KEY = "name";
private ImportExportJsonHelper() { }
/**
* Read a JSON source through the input stream and return the parsed subscription items.
* Read a JSON source through the input stream.
*
* @param in the input stream (e.g. a file)
* @param eventListener listener for the events generated
* @return the parsed subscription items
*/
public static List<SubscriptionItem> readFrom(InputStream in, @Nullable ImportExportEventListener eventListener) throws InvalidSourceException {
if (in == null) throw new InvalidSourceException("input is null");
public static List<SubscriptionItem> readFrom(
final InputStream in, @Nullable final ImportExportEventListener eventListener)
throws InvalidSourceException {
if (in == null) {
throw new InvalidSourceException("input is null");
}
final List<SubscriptionItem> channels = new ArrayList<>();
try {
JsonObject parentObject = JsonParser.object().from(in);
JsonArray channelsArray = parentObject.getArray(JSON_SUBSCRIPTIONS_ARRAY_KEY);
if (eventListener != null) eventListener.onSizeReceived(channelsArray.size());
if (eventListener != null) {
eventListener.onSizeReceived(channelsArray.size());
}
if (channelsArray == null) {
throw new InvalidSourceException("Channels array is null");
@ -85,7 +93,9 @@ public class ImportExportJsonHelper {
if (url != null && name != null && !url.isEmpty() && !name.isEmpty()) {
channels.add(new SubscriptionItem(serviceId, url, name));
if (eventListener != null) eventListener.onItemCompleted(name);
if (eventListener != null) {
eventListener.onItemCompleted(name);
}
}
}
}
@ -103,7 +113,8 @@ public class ImportExportJsonHelper {
* @param out the output stream (e.g. a file)
* @param eventListener listener for the events generated
*/
public static void writeTo(List<SubscriptionItem> items, OutputStream out, @Nullable ImportExportEventListener eventListener) {
public static void writeTo(final List<SubscriptionItem> items, final OutputStream out,
@Nullable final ImportExportEventListener eventListener) {
JsonAppendableWriter writer = JsonWriter.on(out);
writeTo(items, writer, eventListener);
writer.done();
@ -111,9 +122,15 @@ public class ImportExportJsonHelper {
/**
* @see #writeTo(List, OutputStream, ImportExportEventListener)
* @param items the list of subscriptions items
* @param writer the output {@link JsonSink}
* @param eventListener listener for the events generated
*/
public static void writeTo(List<SubscriptionItem> items, JsonSink writer, @Nullable ImportExportEventListener eventListener) {
if (eventListener != null) eventListener.onSizeReceived(items.size());
public static void writeTo(final List<SubscriptionItem> items, final JsonSink writer,
@Nullable final ImportExportEventListener eventListener) {
if (eventListener != null) {
eventListener.onSizeReceived(items.size());
}
writer.object();
@ -128,11 +145,12 @@ public class ImportExportJsonHelper {
writer.value(JSON_NAME_KEY, item.getName());
writer.end();
if (eventListener != null) eventListener.onItemCompleted(item.getName());
if (eventListener != null) {
eventListener.onItemCompleted(item.getName());
}
}
writer.end();
writer.end();
}
}

View file

@ -20,10 +20,11 @@
package org.schabi.newpipe.local.subscription.services;
import android.content.Intent;
import androidx.localbroadcastmanager.content.LocalBroadcastManager;
import android.text.TextUtils;
import android.util.Log;
import androidx.localbroadcastmanager.content.LocalBroadcastManager;
import org.reactivestreams.Subscriber;
import org.reactivestreams.Subscription;
import org.schabi.newpipe.R;
@ -46,26 +47,33 @@ public class SubscriptionsExportService extends BaseImportExportService {
public static final String KEY_FILE_PATH = "key_file_path";
/**
* A {@link LocalBroadcastManager local broadcast} will be made with this action when the export is successfully completed.
* A {@link LocalBroadcastManager local broadcast} will be made with this action
* when the export is successfully completed.
*/
public static final String EXPORT_COMPLETE_ACTION = "org.schabi.newpipe.local.subscription.services.SubscriptionsExportService.EXPORT_COMPLETE";
public static final String EXPORT_COMPLETE_ACTION = "org.schabi.newpipe.local.subscription"
+ ".services.SubscriptionsExportService.EXPORT_COMPLETE";
private Subscription subscription;
private File outFile;
private FileOutputStream outputStream;
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
if (intent == null || subscription != null) return START_NOT_STICKY;
public int onStartCommand(final Intent intent, final int flags, final int startId) {
if (intent == null || subscription != null) {
return START_NOT_STICKY;
}
final String path = intent.getStringExtra(KEY_FILE_PATH);
if (TextUtils.isEmpty(path)) {
stopAndReportError(new IllegalStateException("Exporting to a file, but the path is empty or null"), "Exporting subscriptions");
stopAndReportError(new IllegalStateException(
"Exporting to a file, but the path is empty or null"),
"Exporting subscriptions");
return START_NOT_STICKY;
}
try {
outputStream = new FileOutputStream(outFile = new File(path));
outFile = new File(path);
outputStream = new FileOutputStream(outFile);
} catch (FileNotFoundException e) {
handleError(e);
return START_NOT_STICKY;
@ -89,19 +97,21 @@ public class SubscriptionsExportService extends BaseImportExportService {
@Override
protected void disposeAll() {
super.disposeAll();
if (subscription != null) subscription.cancel();
if (subscription != null) {
subscription.cancel();
}
}
private void startExport() {
showToast(R.string.export_ongoing);
subscriptionManager.subscriptionTable()
.getAll()
.take(1)
subscriptionManager.subscriptionTable().getAll().take(1)
.map(subscriptionEntities -> {
final List<SubscriptionItem> result = new ArrayList<>(subscriptionEntities.size());
final List<SubscriptionItem> result
= new ArrayList<>(subscriptionEntities.size());
for (SubscriptionEntity entity : subscriptionEntities) {
result.add(new SubscriptionItem(entity.getServiceId(), entity.getUrl(), entity.getName()));
result.add(new SubscriptionItem(entity.getServiceId(), entity.getUrl(),
entity.getName()));
}
return result;
})
@ -114,25 +124,28 @@ public class SubscriptionsExportService extends BaseImportExportService {
private Subscriber<File> getSubscriber() {
return new Subscriber<File>() {
@Override
public void onSubscribe(Subscription s) {
public void onSubscribe(final Subscription s) {
subscription = s;
s.request(1);
}
@Override
public void onNext(File file) {
if (DEBUG) Log.d(TAG, "startExport() success: file = " + file);
public void onNext(final File file) {
if (DEBUG) {
Log.d(TAG, "startExport() success: file = " + file);
}
}
@Override
public void onError(Throwable error) {
public void onError(final Throwable error) {
Log.e(TAG, "onError() called with: error = [" + error + "]", error);
handleError(error);
}
@Override
public void onComplete() {
LocalBroadcastManager.getInstance(SubscriptionsExportService.this).sendBroadcast(new Intent(EXPORT_COMPLETE_ACTION));
LocalBroadcastManager.getInstance(SubscriptionsExportService.this)
.sendBroadcast(new Intent(EXPORT_COMPLETE_ACTION));
showToast(R.string.export_complete_toast);
stopService();
}
@ -146,7 +159,7 @@ public class SubscriptionsExportService extends BaseImportExportService {
};
}
protected void handleError(Throwable error) {
protected void handleError(final Throwable error) {
super.handleError(R.string.subscriptions_export_unsuccessful, error);
}
}

View file

@ -20,11 +20,12 @@
package org.schabi.newpipe.local.subscription.services;
import android.content.Intent;
import android.text.TextUtils;
import android.util.Log;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.localbroadcastmanager.content.LocalBroadcastManager;
import android.text.TextUtils;
import android.util.Log;
import org.reactivestreams.Subscriber;
import org.reactivestreams.Subscription;
@ -61,22 +62,33 @@ public class SubscriptionsImportService extends BaseImportExportService {
public static final String KEY_VALUE = "key_value";
/**
* A {@link LocalBroadcastManager local broadcast} will be made with this action when the import is successfully completed.
* A {@link LocalBroadcastManager local broadcast} will be made with this action
* when the import is successfully completed.
*/
public static final String IMPORT_COMPLETE_ACTION = "org.schabi.newpipe.local.subscription.services.SubscriptionsImportService.IMPORT_COMPLETE";
public static final String IMPORT_COMPLETE_ACTION = "org.schabi.newpipe.local.subscription"
+ ".services.SubscriptionsImportService.IMPORT_COMPLETE";
/**
* How many extractions running in parallel.
*/
public static final int PARALLEL_EXTRACTIONS = 8;
/**
* Number of items to buffer to mass-insert in the subscriptions table,
* this leads to a better performance as we can then use db transactions.
*/
public static final int BUFFER_COUNT_BEFORE_INSERT = 50;
private Subscription subscription;
private int currentMode;
private int currentServiceId;
@Nullable
private String channelUrl;
@Nullable
private InputStream inputStream;
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
if (intent == null || subscription != null) return START_NOT_STICKY;
public int onStartCommand(final Intent intent, final int flags, final int startId) {
if (intent == null || subscription != null) {
return START_NOT_STICKY;
}
currentMode = intent.getIntExtra(KEY_MODE, -1);
currentServiceId = intent.getIntExtra(Constants.KEY_SERVICE_ID, Constants.NO_SERVICE_ID);
@ -86,7 +98,9 @@ public class SubscriptionsImportService extends BaseImportExportService {
} else {
final String filePath = intent.getStringExtra(KEY_VALUE);
if (TextUtils.isEmpty(filePath)) {
stopAndReportError(new IllegalStateException("Importing from input stream, but file path is empty or null"), "Importing subscriptions");
stopAndReportError(new IllegalStateException(
"Importing from input stream, but file path is empty or null"),
"Importing subscriptions");
return START_NOT_STICKY;
}
@ -99,8 +113,12 @@ public class SubscriptionsImportService extends BaseImportExportService {
}
if (currentMode == -1 || currentMode == CHANNEL_URL_MODE && channelUrl == null) {
final String errorDescription = "Some important field is null or in illegal state: currentMode=[" + currentMode + "], channelUrl=[" + channelUrl + "], inputStream=[" + inputStream + "]";
stopAndReportError(new IllegalStateException(errorDescription), "Importing subscriptions");
final String errorDescription = "Some important field is null or in illegal state: "
+ "currentMode=[" + currentMode + "], "
+ "channelUrl=[" + channelUrl + "], "
+ "inputStream=[" + inputStream + "]";
stopAndReportError(new IllegalStateException(errorDescription),
"Importing subscriptions");
return START_NOT_STICKY;
}
@ -113,6 +131,10 @@ public class SubscriptionsImportService extends BaseImportExportService {
return 4568;
}
/*//////////////////////////////////////////////////////////////////////////
// Imports
//////////////////////////////////////////////////////////////////////////*/
@Override
public int getTitle() {
return R.string.import_ongoing;
@ -121,24 +143,11 @@ public class SubscriptionsImportService extends BaseImportExportService {
@Override
protected void disposeAll() {
super.disposeAll();
if (subscription != null) subscription.cancel();
if (subscription != null) {
subscription.cancel();
}
}
/*//////////////////////////////////////////////////////////////////////////
// Imports
//////////////////////////////////////////////////////////////////////////*/
/**
* How many extractions running in parallel.
*/
public static final int PARALLEL_EXTRACTIONS = 8;
/**
* Number of items to buffer to mass-insert in the subscriptions table, this leads to
* a better performance as we can then use db transactions.
*/
public static final int BUFFER_COUNT_BEFORE_INSERT = 50;
private void startImport() {
showToast(R.string.import_ongoing);
@ -156,12 +165,14 @@ public class SubscriptionsImportService extends BaseImportExportService {
}
if (flowable == null) {
final String message = "Flowable given by \"importFrom\" is null (current mode: " + currentMode + ")";
final String message = "Flowable given by \"importFrom\" is null "
+ "(current mode: " + currentMode + ")";
stopAndReportError(new IllegalStateException(message), "Importing subscriptions");
return;
}
flowable.doOnNext(subscriptionItems -> eventListener.onSizeReceived(subscriptionItems.size()))
flowable.doOnNext(subscriptionItems ->
eventListener.onSizeReceived(subscriptionItems.size()))
.flatMap(Flowable::fromIterable)
.parallel(PARALLEL_EXTRACTIONS)
@ -169,7 +180,8 @@ public class SubscriptionsImportService extends BaseImportExportService {
.map((Function<SubscriptionItem, Notification<ChannelInfo>>) subscriptionItem -> {
try {
return Notification.createOnNext(ExtractorHelper
.getChannelInfo(subscriptionItem.getServiceId(), subscriptionItem.getUrl(), true)
.getChannelInfo(subscriptionItem.getServiceId(),
subscriptionItem.getUrl(), true)
.blockingGet());
} catch (Throwable e) {
return Notification.createOnError(e);
@ -190,27 +202,30 @@ public class SubscriptionsImportService extends BaseImportExportService {
private Subscriber<List<SubscriptionEntity>> getSubscriber() {
return new Subscriber<List<SubscriptionEntity>>() {
@Override
public void onSubscribe(Subscription s) {
public void onSubscribe(final Subscription s) {
subscription = s;
s.request(Long.MAX_VALUE);
}
@Override
public void onNext(List<SubscriptionEntity> successfulInserted) {
if (DEBUG) Log.d(TAG, "startImport() " + successfulInserted.size() + " items successfully inserted into the database");
public void onNext(final List<SubscriptionEntity> successfulInserted) {
if (DEBUG) {
Log.d(TAG, "startImport() " + successfulInserted.size()
+ " items successfully inserted into the database");
}
}
@Override
public void onError(Throwable error) {
public void onError(final Throwable error) {
Log.e(TAG, "Got an error!", error);
handleError(error);
}
@Override
public void onComplete() {
LocalBroadcastManager.getInstance(SubscriptionsImportService.this).sendBroadcast(new Intent(IMPORT_COMPLETE_ACTION));
LocalBroadcastManager.getInstance(SubscriptionsImportService.this)
.sendBroadcast(new Intent(IMPORT_COMPLETE_ACTION));
showToast(R.string.import_complete_toast);
stopService();
}
@ -240,7 +255,9 @@ public class SubscriptionsImportService extends BaseImportExportService {
return notificationList -> {
final List<ChannelInfo> infoList = new ArrayList<>(notificationList.size());
for (Notification<ChannelInfo> n : notificationList) {
if (n.isOnNext()) infoList.add(n.getValue());
if (n.isOnNext()) {
infoList.add(n.getValue());
}
}
return subscriptionManager.upsertAll(infoList);
@ -263,7 +280,7 @@ public class SubscriptionsImportService extends BaseImportExportService {
return Flowable.fromCallable(() -> ImportExportJsonHelper.readFrom(inputStream, null));
}
protected void handleError(@NonNull Throwable error) {
protected void handleError(@NonNull final Throwable error) {
super.handleError(R.string.subscriptions_import_unsuccessful, error);
}
}
}