Improve code style to be more consistent
This commit is contained in:
parent
819e52cab3
commit
fda5405e48
244 changed files with 10116 additions and 7222 deletions
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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()));
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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(),
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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) { }
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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) {
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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()));
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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) {
|
||||
|
|
|
|||
|
|
@ -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());
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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())
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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() {
|
||||
|
|
|
|||
|
|
@ -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() {
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -14,4 +14,4 @@ public interface ImportExportEventListener {
|
|||
* @param itemName the name of the subscription item
|
||||
*/
|
||||
void onItemCompleted(String itemName);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue