-Modified BaseLocalItemFragment to no longer cache items when going into background.
-Refactored and restructured all LocalItem related fragments and dialogs. -Added error logging to unmonitored single-use observables. -Modified playlist metadata query to return by alphabetical order. -Removed sending toast when playlist is renamed or deleted as it is obvious. -Removed unused code in main fragment.
This commit is contained in:
parent
75a58d6381
commit
225b43ca3c
|
@ -63,6 +63,7 @@ public abstract class PlaylistStreamDAO implements BasicDAO<PlaylistStreamEntity
|
|||
" FROM " + PLAYLIST_TABLE +
|
||||
" LEFT JOIN " + PLAYLIST_STREAM_JOIN_TABLE +
|
||||
" ON " + PLAYLIST_ID + " = " + JOIN_PLAYLIST_ID +
|
||||
" GROUP BY " + JOIN_PLAYLIST_ID)
|
||||
" GROUP BY " + JOIN_PLAYLIST_ID +
|
||||
" ORDER BY " + PLAYLIST_NAME + " COLLATE NOCASE ASC")
|
||||
public abstract Flowable<List<PlaylistMetadataEntry>> getPlaylistMetadata();
|
||||
}
|
||||
|
|
|
@ -229,10 +229,6 @@ public class MainFragment extends BaseFragment implements TabLayout.OnTabSelecte
|
|||
ChannelFragment fragment = ChannelFragment.getInstance(serviceId, url, name);
|
||||
fragment.useAsFrontPage(true);
|
||||
return fragment;
|
||||
} else if (setMainPage.equals(getString(R.string.bookmark_page_key))) {
|
||||
final BookmarkFragment fragment = new BookmarkFragment();
|
||||
fragment.useAsFrontPage(true);
|
||||
return fragment;
|
||||
} else {
|
||||
return new BlankFragment();
|
||||
}
|
||||
|
|
|
@ -1,8 +1,7 @@
|
|||
package org.schabi.newpipe.fragments.local;
|
||||
|
||||
import android.content.Context;
|
||||
import android.os.Bundle;
|
||||
import android.support.annotation.NonNull;
|
||||
import android.support.v4.app.Fragment;
|
||||
import android.support.v7.app.ActionBar;
|
||||
import android.support.v7.widget.LinearLayoutManager;
|
||||
import android.support.v7.widget.RecyclerView;
|
||||
|
@ -12,86 +11,44 @@ import android.view.MenuInflater;
|
|||
import android.view.View;
|
||||
|
||||
import org.schabi.newpipe.R;
|
||||
import org.schabi.newpipe.database.LocalItem;
|
||||
import org.schabi.newpipe.fragments.BaseStateFragment;
|
||||
import org.schabi.newpipe.fragments.list.ListViewContract;
|
||||
import org.schabi.newpipe.util.StateSaver;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Queue;
|
||||
|
||||
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.
|
||||
*
|
||||
* This fragment destroys its adapter and views when {@link Fragment#onDestroyView()} is
|
||||
* called and is memory efficient when in backstack.
|
||||
* */
|
||||
public abstract class BaseLocalListFragment<I, N> extends BaseStateFragment<I>
|
||||
implements ListViewContract<I, N>, StateSaver.WriteRead {
|
||||
implements ListViewContract<I, N> {
|
||||
|
||||
/*//////////////////////////////////////////////////////////////////////////
|
||||
// Views
|
||||
//////////////////////////////////////////////////////////////////////////*/
|
||||
|
||||
protected View headerRootView;
|
||||
protected View footerRootView;
|
||||
|
||||
protected LocalItemListAdapter itemListAdapter;
|
||||
protected RecyclerView itemsList;
|
||||
|
||||
/*//////////////////////////////////////////////////////////////////////////
|
||||
// LifeCycle
|
||||
// Lifecycle - Creation
|
||||
//////////////////////////////////////////////////////////////////////////*/
|
||||
|
||||
@Override
|
||||
public void onAttach(Context context) {
|
||||
super.onAttach(context);
|
||||
itemListAdapter = new LocalItemListAdapter(activity);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
setHasOptionsMenu(true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDestroy() {
|
||||
super.onDestroy();
|
||||
StateSaver.onDestroy(savedState);
|
||||
}
|
||||
|
||||
/*//////////////////////////////////////////////////////////////////////////
|
||||
// State Saving
|
||||
//////////////////////////////////////////////////////////////////////////*/
|
||||
|
||||
protected StateSaver.SavedState savedState;
|
||||
|
||||
@Override
|
||||
public String generateSuffix() {
|
||||
// Naive solution, but it's good for now (the items don't change)
|
||||
return "." + itemListAdapter.getItemsList().size() + ".list";
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeTo(Queue<Object> objectsToSave) {
|
||||
objectsToSave.add(itemListAdapter.getItemsList());
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
public void readFrom(@NonNull Queue<Object> savedObjects) throws Exception {
|
||||
itemListAdapter.getItemsList().clear();
|
||||
itemListAdapter.getItemsList().addAll((List<LocalItem>) savedObjects.poll());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSaveInstanceState(Bundle bundle) {
|
||||
super.onSaveInstanceState(bundle);
|
||||
savedState = StateSaver.tryToSave(activity.isChangingConfigurations(), savedState, bundle, this);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onRestoreInstanceState(@NonNull Bundle bundle) {
|
||||
super.onRestoreInstanceState(bundle);
|
||||
savedState = StateSaver.tryToRestore(bundle, this);
|
||||
}
|
||||
|
||||
/*//////////////////////////////////////////////////////////////////////////
|
||||
// Init
|
||||
// Lifecycle - View
|
||||
//////////////////////////////////////////////////////////////////////////*/
|
||||
|
||||
protected View getListHeader() {
|
||||
|
@ -113,8 +70,9 @@ public abstract class BaseLocalListFragment<I, N> extends BaseStateFragment<I>
|
|||
itemsList = rootView.findViewById(R.id.items_list);
|
||||
itemsList.setLayoutManager(getListLayoutManager());
|
||||
|
||||
itemListAdapter.setFooter(getListFooter());
|
||||
itemListAdapter.setHeader(getListHeader());
|
||||
itemListAdapter = new LocalItemListAdapter(activity);
|
||||
itemListAdapter.setHeader(headerRootView = getListHeader());
|
||||
itemListAdapter.setFooter(footerRootView = getListFooter());
|
||||
|
||||
itemsList.setAdapter(itemListAdapter);
|
||||
}
|
||||
|
@ -125,12 +83,13 @@ public abstract class BaseLocalListFragment<I, N> extends BaseStateFragment<I>
|
|||
}
|
||||
|
||||
/*//////////////////////////////////////////////////////////////////////////
|
||||
// Menu
|
||||
// Lifecycle - Menu
|
||||
//////////////////////////////////////////////////////////////////////////*/
|
||||
|
||||
@Override
|
||||
public void onCreateOptionsMenu(Menu menu, MenuInflater 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 + "]");
|
||||
super.onCreateOptionsMenu(menu, inflater);
|
||||
ActionBar supportActionBar = activity.getSupportActionBar();
|
||||
if (supportActionBar != null) {
|
||||
|
@ -143,27 +102,48 @@ public abstract class BaseLocalListFragment<I, N> extends BaseStateFragment<I>
|
|||
}
|
||||
}
|
||||
|
||||
/*//////////////////////////////////////////////////////////////////////////
|
||||
// Lifecycle - Destruction
|
||||
//////////////////////////////////////////////////////////////////////////*/
|
||||
|
||||
@Override
|
||||
public void onDestroyView() {
|
||||
super.onDestroyView();
|
||||
itemsList = null;
|
||||
itemListAdapter = null;
|
||||
}
|
||||
|
||||
/*//////////////////////////////////////////////////////////////////////////
|
||||
// Contract
|
||||
//////////////////////////////////////////////////////////////////////////*/
|
||||
|
||||
@Override
|
||||
public void startLoading(boolean forceLoad) {
|
||||
super.startLoading(forceLoad);
|
||||
resetFragment();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void showLoading() {
|
||||
super.showLoading();
|
||||
// animateView(itemsList, false, 400);
|
||||
animateView(itemsList, false, 200);
|
||||
if (headerRootView != null) animateView(headerRootView, false, 200);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void hideLoading() {
|
||||
super.hideLoading();
|
||||
animateView(itemsList, true, 300);
|
||||
animateView(itemsList, true, 200);
|
||||
if (headerRootView != null) animateView(headerRootView, true, 200);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void showError(String message, boolean showRetryButton) {
|
||||
super.showError(message, showRetryButton);
|
||||
showListFooter(false);
|
||||
|
||||
animateView(itemsList, false, 200);
|
||||
if (headerRootView != null) animateView(headerRootView, false, 200);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -181,4 +161,18 @@ public abstract class BaseLocalListFragment<I, N> extends BaseStateFragment<I>
|
|||
public void handleNextItems(N result) {
|
||||
isLoading.set(false);
|
||||
}
|
||||
|
||||
/*//////////////////////////////////////////////////////////////////////////
|
||||
// Error handling
|
||||
//////////////////////////////////////////////////////////////////////////*/
|
||||
|
||||
protected void resetFragment() {
|
||||
if (itemListAdapter != null) itemListAdapter.clearStreamItemList();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean onError(Throwable exception) {
|
||||
resetFragment();
|
||||
return super.onError(exception);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -11,6 +11,7 @@ import android.support.v7.app.AlertDialog;
|
|||
import android.support.v7.widget.RecyclerView;
|
||||
import android.support.v7.widget.helper.ItemTouchHelper;
|
||||
import android.text.TextUtils;
|
||||
import android.util.Log;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
|
@ -38,7 +39,6 @@ import java.util.concurrent.TimeUnit;
|
|||
|
||||
import icepick.State;
|
||||
import io.reactivex.android.schedulers.AndroidSchedulers;
|
||||
import io.reactivex.disposables.CompositeDisposable;
|
||||
import io.reactivex.disposables.Disposable;
|
||||
import io.reactivex.subjects.PublishSubject;
|
||||
|
||||
|
@ -51,8 +51,8 @@ public class LocalPlaylistFragment extends BaseLocalListFragment<List<PlaylistSt
|
|||
private View headerRootLayout;
|
||||
private TextView headerTitleView;
|
||||
private TextView headerStreamCount;
|
||||
private View playlistControl;
|
||||
|
||||
private View playlistControl;
|
||||
private View headerPlayAllButton;
|
||||
private View headerPopupButton;
|
||||
private View headerBackgroundButton;
|
||||
|
@ -66,10 +66,8 @@ public class LocalPlaylistFragment extends BaseLocalListFragment<List<PlaylistSt
|
|||
|
||||
private ItemTouchHelper itemTouchHelper;
|
||||
|
||||
/* Used for independent events */
|
||||
private CompositeDisposable disposables = new CompositeDisposable();
|
||||
private Subscription databaseSubscription;
|
||||
private LocalPlaylistManager playlistManager;
|
||||
private Subscription databaseSubscription;
|
||||
|
||||
private PublishSubject<Long> debouncedSaveSignal;
|
||||
private Disposable debouncedSaver;
|
||||
|
@ -81,13 +79,14 @@ public class LocalPlaylistFragment extends BaseLocalListFragment<List<PlaylistSt
|
|||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
// Fragment LifeCycle
|
||||
// Fragment LifeCycle - Creation
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
|
||||
@Override
|
||||
public void onAttach(Context context) {
|
||||
super.onAttach(context);
|
||||
playlistManager = new LocalPlaylistManager(NewPipeDatabase.getInstance(context));
|
||||
public void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
playlistManager = new LocalPlaylistManager(NewPipeDatabase.getInstance(getContext()));
|
||||
debouncedSaveSignal = PublishSubject.create();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -97,53 +96,23 @@ public class LocalPlaylistFragment extends BaseLocalListFragment<List<PlaylistSt
|
|||
return inflater.inflate(R.layout.fragment_playlist, container, false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onResume() {
|
||||
super.onResume();
|
||||
debouncedSaveSignal = PublishSubject.create();
|
||||
debouncedSaver = getDebouncedSaver();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPause() {
|
||||
super.onPause();
|
||||
|
||||
if (debouncedSaveSignal != null) debouncedSaveSignal.onComplete();
|
||||
if (debouncedSaver != null) debouncedSaver.dispose();
|
||||
|
||||
debouncedSaveSignal = null;
|
||||
debouncedSaver = null;
|
||||
|
||||
itemsListState = itemsList.getLayoutManager().onSaveInstanceState();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDestroyView() {
|
||||
if (disposables != null) disposables.clear();
|
||||
|
||||
super.onDestroyView();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDestroy() {
|
||||
if (disposables != null) disposables.dispose();
|
||||
if (databaseSubscription != null) databaseSubscription.cancel();
|
||||
|
||||
disposables = null;
|
||||
databaseSubscription = null;
|
||||
playlistManager = null;
|
||||
|
||||
super.onDestroy();
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
// Fragment Views
|
||||
// Fragment Lifecycle - Views
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
|
||||
@Override
|
||||
public void setTitle(final String title) {
|
||||
super.setTitle(title);
|
||||
|
||||
if (headerTitleView != null) {
|
||||
headerTitleView.setText(title);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void initViews(View rootView, Bundle savedInstanceState) {
|
||||
super.initViews(rootView, savedInstanceState);
|
||||
setFragmentTitle(name);
|
||||
setTitle(name);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -155,6 +124,7 @@ public class LocalPlaylistFragment extends BaseLocalListFragment<List<PlaylistSt
|
|||
headerTitleView.setSelected(true);
|
||||
|
||||
headerStreamCount = headerRootLayout.findViewById(R.id.playlist_stream_count);
|
||||
|
||||
playlistControl = 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);
|
||||
|
@ -167,6 +137,8 @@ public class LocalPlaylistFragment extends BaseLocalListFragment<List<PlaylistSt
|
|||
protected void initListeners() {
|
||||
super.initListeners();
|
||||
|
||||
headerTitleView.setOnClickListener(view -> createRenameDialog());
|
||||
|
||||
itemTouchHelper = new ItemTouchHelper(getItemTouchCallback());
|
||||
itemTouchHelper.attachToRecyclerView(itemsList);
|
||||
|
||||
|
@ -192,9 +164,236 @@ public class LocalPlaylistFragment extends BaseLocalListFragment<List<PlaylistSt
|
|||
if (itemTouchHelper != null) itemTouchHelper.startDrag(viewHolder);
|
||||
}
|
||||
});
|
||||
headerTitleView.setOnClickListener(view -> createRenameDialog());
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
// Fragment Lifecycle - Loading
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
|
||||
@Override
|
||||
public void showLoading() {
|
||||
super.showLoading();
|
||||
animateView(headerRootLayout, false, 200);
|
||||
animateView(playlistControl, false, 200);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void hideLoading() {
|
||||
super.hideLoading();
|
||||
animateView(headerRootLayout, true, 200);
|
||||
animateView(playlistControl, true, 200);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void startLoading(boolean forceLoad) {
|
||||
super.startLoading(forceLoad);
|
||||
|
||||
if (debouncedSaver != null) debouncedSaver.dispose();
|
||||
debouncedSaver = getDebouncedSaver();
|
||||
|
||||
playlistManager.getPlaylistStreams(playlistId)
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribe(getPlaylistObserver());
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
// Fragment Lifecycle - Destruction
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
|
||||
@Override
|
||||
public void onPause() {
|
||||
super.onPause();
|
||||
itemsListState = itemsList.getLayoutManager().onSaveInstanceState();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDestroyView() {
|
||||
super.onDestroyView();
|
||||
if (databaseSubscription != null) databaseSubscription.cancel();
|
||||
if (debouncedSaver != null) debouncedSaver.dispose();
|
||||
|
||||
databaseSubscription = null;
|
||||
debouncedSaver = null;
|
||||
itemTouchHelper = null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDestroy() {
|
||||
super.onDestroy();
|
||||
if (debouncedSaveSignal != null) debouncedSaveSignal.onComplete();
|
||||
|
||||
debouncedSaveSignal = null;
|
||||
playlistManager = null;
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
// Playlist Stream Loader
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
|
||||
private Subscriber<List<PlaylistStreamEntry>> getPlaylistObserver() {
|
||||
return new Subscriber<List<PlaylistStreamEntry>>() {
|
||||
@Override
|
||||
public void onSubscribe(Subscription s) {
|
||||
showLoading();
|
||||
|
||||
if (databaseSubscription != null) databaseSubscription.cancel();
|
||||
databaseSubscription = s;
|
||||
databaseSubscription.request(1);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onNext(List<PlaylistStreamEntry> streams) {
|
||||
// Do not allow saving while the result is being updated
|
||||
if (debouncedSaver != null) debouncedSaver.dispose();
|
||||
handleResult(streams);
|
||||
debouncedSaver = getDebouncedSaver();
|
||||
|
||||
if (databaseSubscription != null) databaseSubscription.request(1);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onError(Throwable exception) {
|
||||
LocalPlaylistFragment.this.onError(exception);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onComplete() {}
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleResult(@NonNull List<PlaylistStreamEntry> result) {
|
||||
super.handleResult(result);
|
||||
itemListAdapter.clearStreamItemList();
|
||||
|
||||
if (result.isEmpty()) {
|
||||
showEmptyState();
|
||||
return;
|
||||
}
|
||||
|
||||
itemListAdapter.addItems(result);
|
||||
if (itemsListState != null) {
|
||||
itemsList.getLayoutManager().onRestoreInstanceState(itemsListState);
|
||||
itemsListState = null;
|
||||
}
|
||||
setVideoCount(itemListAdapter.getItemsList().size());
|
||||
|
||||
headerPlayAllButton.setOnClickListener(view ->
|
||||
NavigationHelper.playOnMainPlayer(activity, getPlayQueue()));
|
||||
headerPopupButton.setOnClickListener(view ->
|
||||
NavigationHelper.playOnPopupPlayer(activity, getPlayQueue()));
|
||||
headerBackgroundButton.setOnClickListener(view ->
|
||||
NavigationHelper.playOnBackgroundPlayer(activity, getPlayQueue()));
|
||||
|
||||
hideLoading();
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
// 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;
|
||||
|
||||
onUnrecoverableError(exception, UserAction.SOMETHING_ELSE,
|
||||
"none", "Local Playlist", R.string.general_error);
|
||||
return true;
|
||||
}
|
||||
|
||||
/*//////////////////////////////////////////////////////////////////////////
|
||||
// Playlist Metadata/Streams Manipulation
|
||||
//////////////////////////////////////////////////////////////////////////*/
|
||||
|
||||
private void createRenameDialog() {
|
||||
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);
|
||||
nameEdit.setText(name);
|
||||
nameEdit.setSelection(nameEdit.getText().length());
|
||||
|
||||
final AlertDialog.Builder dialogBuilder = new AlertDialog.Builder(getContext())
|
||||
.setTitle(R.string.rename_playlist)
|
||||
.setView(dialogView)
|
||||
.setCancelable(true)
|
||||
.setNegativeButton(R.string.cancel, null)
|
||||
.setPositiveButton(R.string.create, (dialogInterface, i) ->
|
||||
changePlaylistName(nameEdit.getText().toString())
|
||||
);
|
||||
|
||||
dialogBuilder.show();
|
||||
}
|
||||
|
||||
private void changePlaylistName(final String name) {
|
||||
this.name = name;
|
||||
setTitle(name);
|
||||
|
||||
Log.e(TAG, "Updating playlist id=[" + playlistId +
|
||||
"] with new name=[" + name + "] items");
|
||||
|
||||
playlistManager.renamePlaylist(playlistId, name)
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribe(longs -> {/*Do nothing on success*/}, this::onError);
|
||||
}
|
||||
|
||||
private void changeThumbnailUrl(final String thumbnailUrl) {
|
||||
final Toast successToast = Toast.makeText(getActivity(),
|
||||
R.string.playlist_thumbnail_change_success,
|
||||
Toast.LENGTH_SHORT);
|
||||
|
||||
Log.e(TAG, "Updating playlist id=[" + playlistId +
|
||||
"] with new thumbnail url=[" + thumbnailUrl + "]");
|
||||
|
||||
playlistManager.changePlaylistThumbnail(playlistId, thumbnailUrl)
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribe(ignore -> successToast.show(), this::onError);
|
||||
}
|
||||
|
||||
private void deleteItem(final PlaylistStreamEntry item) {
|
||||
itemListAdapter.removeItem(item);
|
||||
setVideoCount(itemListAdapter.getItemsList().size());
|
||||
saveDebounced();
|
||||
}
|
||||
|
||||
private void saveDebounced() {
|
||||
debouncedSaveSignal.onNext(System.currentTimeMillis());
|
||||
}
|
||||
|
||||
private Disposable getDebouncedSaver() {
|
||||
return debouncedSaveSignal
|
||||
.debounce(SAVE_DEBOUNCE_MILLIS, TimeUnit.MILLISECONDS)
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribe(ignored -> saveJoin());
|
||||
}
|
||||
|
||||
private void saveJoin() {
|
||||
final List<LocalItem> items = itemListAdapter.getItemsList();
|
||||
List<Long> streamIds = new ArrayList<>(items.size());
|
||||
for (final LocalItem item : items) {
|
||||
if (item instanceof PlaylistStreamEntry) {
|
||||
streamIds.add(((PlaylistStreamEntry) item).streamId);
|
||||
}
|
||||
}
|
||||
|
||||
Log.e(TAG, "Updating playlist id=[" + playlistId +
|
||||
"] with [" + streamIds.size() + "] items");
|
||||
|
||||
playlistManager.updateJoin(playlistId, streamIds)
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribe(() -> {/*Do nothing on success*/}, this::onError);
|
||||
}
|
||||
|
||||
/*//////////////////////////////////////////////////////////////////////////
|
||||
// Utils
|
||||
//////////////////////////////////////////////////////////////////////////*/
|
||||
|
||||
protected void showStreamDialog(final PlaylistStreamEntry item) {
|
||||
final Context context = getContext();
|
||||
final Activity activity = getActivity();
|
||||
|
@ -236,9 +435,7 @@ public class LocalPlaylistFragment extends BaseLocalListFragment<List<PlaylistSt
|
|||
changeThumbnailUrl(item.thumbnailUrl);
|
||||
break;
|
||||
case 6:
|
||||
itemListAdapter.removeItem(item);
|
||||
setVideoCount(itemListAdapter.getItemsList().size());
|
||||
saveDebounced();
|
||||
deleteItem(item);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
|
@ -281,124 +478,11 @@ public class LocalPlaylistFragment extends BaseLocalListFragment<List<PlaylistSt
|
|||
};
|
||||
}
|
||||
|
||||
private void resetFragment() {
|
||||
if (disposables != null) disposables.clear();
|
||||
if (databaseSubscription != null) databaseSubscription.cancel();
|
||||
if (itemListAdapter != null) itemListAdapter.clearStreamItemList();
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
// Loader
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
|
||||
@Override
|
||||
public void showLoading() {
|
||||
super.showLoading();
|
||||
animateView(headerRootLayout, false, 200);
|
||||
animateView(itemsList, false, 100);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void startLoading(boolean forceLoad) {
|
||||
super.startLoading(forceLoad);
|
||||
resetFragment();
|
||||
|
||||
playlistManager.getPlaylistStreams(playlistId)
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribe(getPlaylistObserver());
|
||||
}
|
||||
|
||||
private Subscriber<List<PlaylistStreamEntry>> getPlaylistObserver() {
|
||||
return new Subscriber<List<PlaylistStreamEntry>>() {
|
||||
@Override
|
||||
public void onSubscribe(Subscription s) {
|
||||
showLoading();
|
||||
|
||||
if (databaseSubscription != null) databaseSubscription.cancel();
|
||||
databaseSubscription = s;
|
||||
databaseSubscription.request(1);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onNext(List<PlaylistStreamEntry> streams) {
|
||||
handleResult(streams);
|
||||
if (databaseSubscription != null) databaseSubscription.request(1);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onError(Throwable exception) {
|
||||
LocalPlaylistFragment.this.onError(exception);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onComplete() {
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleResult(@NonNull List<PlaylistStreamEntry> result) {
|
||||
super.handleResult(result);
|
||||
itemListAdapter.clearStreamItemList();
|
||||
|
||||
if (result.isEmpty()) {
|
||||
showEmptyState();
|
||||
return;
|
||||
}
|
||||
|
||||
animateView(headerRootLayout, true, 100);
|
||||
animateView(itemsList, true, 300);
|
||||
|
||||
itemListAdapter.addItems(result);
|
||||
if (itemsListState != null) {
|
||||
itemsList.getLayoutManager().onRestoreInstanceState(itemsListState);
|
||||
itemsListState = null;
|
||||
}
|
||||
setVideoCount(itemListAdapter.getItemsList().size());
|
||||
|
||||
playlistControl.setVisibility(View.VISIBLE);
|
||||
|
||||
headerPlayAllButton.setOnClickListener(view ->
|
||||
NavigationHelper.playOnMainPlayer(activity, getPlayQueue()));
|
||||
headerPopupButton.setOnClickListener(view ->
|
||||
NavigationHelper.playOnPopupPlayer(activity, getPlayQueue()));
|
||||
headerBackgroundButton.setOnClickListener(view ->
|
||||
NavigationHelper.playOnBackgroundPlayer(activity, getPlayQueue()));
|
||||
hideLoading();
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
// Fragment Error Handling
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
|
||||
@Override
|
||||
protected boolean onError(Throwable exception) {
|
||||
resetFragment();
|
||||
if (super.onError(exception)) return true;
|
||||
|
||||
onUnrecoverableError(exception, UserAction.SOMETHING_ELSE,
|
||||
"none", "Local Playlist", R.string.general_error);
|
||||
return true;
|
||||
}
|
||||
|
||||
/*//////////////////////////////////////////////////////////////////////////
|
||||
// Utils
|
||||
//////////////////////////////////////////////////////////////////////////*/
|
||||
|
||||
private void setInitialData(long playlistId, String name) {
|
||||
this.playlistId = playlistId;
|
||||
this.name = !TextUtils.isEmpty(name) ? name : "";
|
||||
}
|
||||
|
||||
private void setFragmentTitle(final String title) {
|
||||
if (activity != null && activity.getSupportActionBar() != null) {
|
||||
activity.getSupportActionBar().setTitle(title);
|
||||
}
|
||||
if (headerTitleView != null) {
|
||||
headerTitleView.setText(title);
|
||||
}
|
||||
}
|
||||
|
||||
private void setVideoCount(final long count) {
|
||||
if (activity != null && headerStreamCount != null) {
|
||||
headerStreamCount.setText(Localization.localizeStreamCount(activity, count));
|
||||
|
@ -419,71 +503,5 @@ public class LocalPlaylistFragment extends BaseLocalListFragment<List<PlaylistSt
|
|||
}
|
||||
return new SinglePlayQueue(streamInfoItems, index);
|
||||
}
|
||||
|
||||
private void createRenameDialog() {
|
||||
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);
|
||||
nameEdit.setText(name);
|
||||
nameEdit.setSelection(nameEdit.getText().length());
|
||||
|
||||
final AlertDialog.Builder dialogBuilder = new AlertDialog.Builder(getContext())
|
||||
.setTitle(R.string.rename_playlist)
|
||||
.setView(dialogView)
|
||||
.setCancelable(true)
|
||||
.setNegativeButton(R.string.cancel, null)
|
||||
.setPositiveButton(R.string.create, (dialogInterface, i) -> {
|
||||
name = nameEdit.getText().toString();
|
||||
setFragmentTitle(name);
|
||||
|
||||
final LocalPlaylistManager playlistManager =
|
||||
new LocalPlaylistManager(NewPipeDatabase.getInstance(getContext()));
|
||||
final Toast successToast = Toast.makeText(getActivity(),
|
||||
R.string.playlist_rename_success,
|
||||
Toast.LENGTH_SHORT);
|
||||
|
||||
playlistManager.renamePlaylist(playlistId, name)
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribe(longs -> successToast.show());
|
||||
});
|
||||
|
||||
dialogBuilder.show();
|
||||
}
|
||||
|
||||
private void changeThumbnailUrl(final String thumbnailUrl) {
|
||||
final Toast successToast = Toast.makeText(getActivity(),
|
||||
R.string.playlist_thumbnail_change_success,
|
||||
Toast.LENGTH_SHORT);
|
||||
|
||||
playlistManager.changePlaylistThumbnail(playlistId, thumbnailUrl)
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribe(ignore -> successToast.show());
|
||||
}
|
||||
|
||||
private void saveDebounced() {
|
||||
debouncedSaveSignal.onNext(System.currentTimeMillis());
|
||||
}
|
||||
|
||||
private Disposable getDebouncedSaver() {
|
||||
return debouncedSaveSignal
|
||||
.debounce(SAVE_DEBOUNCE_MILLIS, TimeUnit.MILLISECONDS)
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribe(ignored -> saveJoin());
|
||||
}
|
||||
|
||||
private void saveJoin() {
|
||||
final List<LocalItem> items = itemListAdapter.getItemsList();
|
||||
List<Long> streamIds = new ArrayList<>(items.size());
|
||||
for (final LocalItem item : items) {
|
||||
if (item instanceof PlaylistStreamEntry) {
|
||||
streamIds.add(((PlaylistStreamEntry) item).streamId);
|
||||
}
|
||||
}
|
||||
|
||||
playlistManager.updateJoin(playlistId, streamIds)
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribe();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
package org.schabi.newpipe.fragments.local;
|
||||
|
||||
import android.annotation.SuppressLint;
|
||||
import android.content.Context;
|
||||
import android.os.Bundle;
|
||||
import android.support.annotation.NonNull;
|
||||
import android.support.annotation.Nullable;
|
||||
|
@ -25,6 +24,8 @@ import java.util.ArrayList;
|
|||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
|
||||
import io.reactivex.android.schedulers.AndroidSchedulers;
|
||||
import io.reactivex.disposables.Disposable;
|
||||
|
||||
|
@ -63,26 +64,7 @@ public final class PlaylistAppendDialog extends PlaylistDialog {
|
|||
}
|
||||
|
||||
/*//////////////////////////////////////////////////////////////////////////
|
||||
// LifeCycle
|
||||
//////////////////////////////////////////////////////////////////////////*/
|
||||
|
||||
@Override
|
||||
public void onAttach(Context context) {
|
||||
super.onAttach(context);
|
||||
playlistAdapter = new LocalItemListAdapter(getActivity());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDestroy() {
|
||||
super.onDestroy();
|
||||
if (playlistReactor != null) playlistReactor.dispose();
|
||||
playlistReactor = null;
|
||||
playlistRecyclerView = null;
|
||||
playlistAdapter = null;
|
||||
}
|
||||
|
||||
/*//////////////////////////////////////////////////////////////////////////
|
||||
// Views
|
||||
// LifeCycle - Creation
|
||||
//////////////////////////////////////////////////////////////////////////*/
|
||||
|
||||
@Override
|
||||
|
@ -95,52 +77,44 @@ public final class PlaylistAppendDialog extends PlaylistDialog {
|
|||
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
|
||||
super.onViewCreated(view, savedInstanceState);
|
||||
|
||||
final View newPlaylistButton = view.findViewById(R.id.newPlaylist);
|
||||
playlistRecyclerView = view.findViewById(R.id.playlist_list);
|
||||
playlistRecyclerView.setLayoutManager(new LinearLayoutManager(getContext()));
|
||||
playlistRecyclerView.setAdapter(playlistAdapter);
|
||||
|
||||
final LocalPlaylistManager playlistManager =
|
||||
new LocalPlaylistManager(NewPipeDatabase.getInstance(getContext()));
|
||||
|
||||
newPlaylistButton.setOnClickListener(ignored -> openCreatePlaylistDialog());
|
||||
|
||||
playlistAdapter = new LocalItemListAdapter(getActivity());
|
||||
playlistAdapter.setSelectedListener(new OnLocalItemGesture<LocalItem>() {
|
||||
@Override
|
||||
public void selected(LocalItem selectedItem) {
|
||||
if (!(selectedItem instanceof PlaylistMetadataEntry) || getStreams() == null)
|
||||
return;
|
||||
|
||||
final long playlistId = ((PlaylistMetadataEntry) selectedItem).uid;
|
||||
@SuppressLint("ShowToast")
|
||||
final Toast successToast = Toast.makeText(getContext(), R.string.playlist_add_stream_success,
|
||||
Toast.LENGTH_SHORT);
|
||||
|
||||
playlistManager.appendToPlaylist(playlistId, getStreams())
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.doOnDispose(successToast::show)
|
||||
.subscribe(ignored -> {});
|
||||
|
||||
getDialog().dismiss();
|
||||
onPlaylistSelected(playlistManager, (PlaylistMetadataEntry) selectedItem,
|
||||
getStreams());
|
||||
}
|
||||
});
|
||||
|
||||
playlistRecyclerView = view.findViewById(R.id.playlist_list);
|
||||
playlistRecyclerView.setLayoutManager(new LinearLayoutManager(getContext()));
|
||||
playlistRecyclerView.setAdapter(playlistAdapter);
|
||||
|
||||
final View newPlaylistButton = view.findViewById(R.id.newPlaylist);
|
||||
newPlaylistButton.setOnClickListener(ignored -> openCreatePlaylistDialog());
|
||||
|
||||
playlistReactor = playlistManager.getPlaylists()
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribe(metadataEntries -> {
|
||||
if (metadataEntries.isEmpty()) {
|
||||
openCreatePlaylistDialog();
|
||||
return;
|
||||
.subscribe(this::onPlaylistsReceived);
|
||||
}
|
||||
|
||||
if (playlistAdapter != null) {
|
||||
playlistAdapter.clearStreamItemList();
|
||||
playlistAdapter.addItems(metadataEntries);
|
||||
}
|
||||
if (playlistRecyclerView != null) {
|
||||
playlistRecyclerView.setVisibility(View.VISIBLE);
|
||||
}
|
||||
});
|
||||
/*//////////////////////////////////////////////////////////////////////////
|
||||
// LifeCycle - Destruction
|
||||
//////////////////////////////////////////////////////////////////////////*/
|
||||
|
||||
@Override
|
||||
public void onDestroyView() {
|
||||
super.onDestroyView();
|
||||
if (playlistReactor != null) playlistReactor.dispose();
|
||||
|
||||
playlistReactor = null;
|
||||
playlistRecyclerView = null;
|
||||
playlistAdapter = null;
|
||||
}
|
||||
|
||||
/*//////////////////////////////////////////////////////////////////////////
|
||||
|
@ -153,4 +127,33 @@ public final class PlaylistAppendDialog extends PlaylistDialog {
|
|||
PlaylistCreationDialog.newInstance(getStreams()).show(getFragmentManager(), TAG);
|
||||
getDialog().dismiss();
|
||||
}
|
||||
|
||||
private void onPlaylistsReceived(@NonNull final List<PlaylistMetadataEntry> playlists) {
|
||||
if (playlists.isEmpty()) {
|
||||
openCreatePlaylistDialog();
|
||||
return;
|
||||
}
|
||||
|
||||
if (playlistAdapter != null && playlistRecyclerView != null) {
|
||||
playlistAdapter.clearStreamItemList();
|
||||
playlistAdapter.addItems(playlists);
|
||||
playlistRecyclerView.setVisibility(View.VISIBLE);
|
||||
}
|
||||
}
|
||||
|
||||
private void onPlaylistSelected(@NonNull LocalPlaylistManager manager,
|
||||
@NonNull PlaylistMetadataEntry playlist,
|
||||
@Nonnull List<StreamEntity> streams) {
|
||||
if (getStreams() == null) return;
|
||||
|
||||
@SuppressLint("ShowToast")
|
||||
final Toast successToast = Toast.makeText(getContext(),
|
||||
R.string.playlist_add_stream_success, Toast.LENGTH_SHORT);
|
||||
|
||||
manager.appendToPlaylist(playlist.uid, streams)
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribe(ignored -> successToast.show());
|
||||
|
||||
getDialog().dismiss();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,17 +1,14 @@
|
|||
package org.schabi.newpipe.fragments.local.bookmark;
|
||||
|
||||
import android.app.AlertDialog;
|
||||
import android.content.Context;
|
||||
import android.os.Bundle;
|
||||
import android.os.Parcelable;
|
||||
import android.support.annotation.NonNull;
|
||||
import android.support.annotation.Nullable;
|
||||
import android.support.v7.widget.LinearLayoutManager;
|
||||
import android.support.v7.widget.RecyclerView;
|
||||
import android.util.Log;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.Toast;
|
||||
|
||||
import org.reactivestreams.Subscriber;
|
||||
import org.reactivestreams.Subscription;
|
||||
|
@ -19,29 +16,25 @@ import org.schabi.newpipe.NewPipeDatabase;
|
|||
import org.schabi.newpipe.R;
|
||||
import org.schabi.newpipe.database.LocalItem;
|
||||
import org.schabi.newpipe.database.playlist.PlaylistMetadataEntry;
|
||||
import org.schabi.newpipe.fragments.BaseStateFragment;
|
||||
import org.schabi.newpipe.fragments.local.LocalItemListAdapter;
|
||||
import org.schabi.newpipe.fragments.local.BaseLocalListFragment;
|
||||
import org.schabi.newpipe.fragments.local.LocalPlaylistManager;
|
||||
import org.schabi.newpipe.fragments.local.OnLocalItemGesture;
|
||||
import org.schabi.newpipe.report.UserAction;
|
||||
import org.schabi.newpipe.util.NavigationHelper;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import icepick.State;
|
||||
import io.reactivex.android.schedulers.AndroidSchedulers;
|
||||
import io.reactivex.disposables.CompositeDisposable;
|
||||
import io.reactivex.disposables.Disposable;
|
||||
|
||||
import static org.schabi.newpipe.util.AnimationUtils.animateView;
|
||||
public final class BookmarkFragment
|
||||
extends BaseLocalListFragment<List<PlaylistMetadataEntry>, Void> {
|
||||
|
||||
public class BookmarkFragment extends BaseStateFragment<List<PlaylistMetadataEntry>> {
|
||||
private View watchHistoryButton;
|
||||
private View mostWatchedButton;
|
||||
|
||||
private LocalItemListAdapter itemListAdapter;
|
||||
private RecyclerView itemsList;
|
||||
|
||||
@State
|
||||
protected Parcelable itemsListState;
|
||||
|
||||
|
@ -50,23 +43,14 @@ public class BookmarkFragment extends BaseStateFragment<List<PlaylistMetadataEnt
|
|||
private LocalPlaylistManager localPlaylistManager;
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
// Fragment LifeCycle
|
||||
// Fragment LifeCycle - Creation
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
|
||||
@Override
|
||||
public void setUserVisibleHint(boolean isVisibleToUser) {
|
||||
super.setUserVisibleHint(isVisibleToUser);
|
||||
if(isVisibleToUser && activity != null && activity.getSupportActionBar() != null) {
|
||||
activity.getSupportActionBar().setTitle(R.string.tab_bookmarks);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void onAttach(Context context) {
|
||||
super.onAttach(context);
|
||||
itemListAdapter = new LocalItemListAdapter(activity);
|
||||
localPlaylistManager = new LocalPlaylistManager(NewPipeDatabase.getInstance(context));
|
||||
public void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
localPlaylistManager = new LocalPlaylistManager(NewPipeDatabase.getInstance(getContext()));
|
||||
disposables = new CompositeDisposable();
|
||||
}
|
||||
|
||||
@Nullable
|
||||
|
@ -74,62 +58,37 @@ public class BookmarkFragment extends BaseStateFragment<List<PlaylistMetadataEnt
|
|||
public View onCreateView(@NonNull LayoutInflater inflater,
|
||||
@Nullable ViewGroup container,
|
||||
Bundle savedInstanceState) {
|
||||
if (activity.getSupportActionBar() != null) {
|
||||
if (activity != null && activity.getSupportActionBar() != null) {
|
||||
activity.getSupportActionBar().setDisplayShowTitleEnabled(true);
|
||||
activity.setTitle(R.string.tab_subscriptions);
|
||||
}
|
||||
|
||||
activity.setTitle(R.string.tab_bookmarks);
|
||||
if(useAsFrontPage) {
|
||||
activity.getSupportActionBar().setDisplayHomeAsUpEnabled(false);
|
||||
}
|
||||
return inflater.inflate(R.layout.fragment_bookmarks, container, false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPause() {
|
||||
super.onPause();
|
||||
itemsListState = itemsList.getLayoutManager().onSaveInstanceState();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDestroyView() {
|
||||
if (disposables != null) disposables.clear();
|
||||
if (databaseSubscription != null) databaseSubscription.cancel();
|
||||
|
||||
super.onDestroyView();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDestroy() {
|
||||
if (disposables != null) disposables.dispose();
|
||||
if (databaseSubscription != null) databaseSubscription.cancel();
|
||||
|
||||
disposables = null;
|
||||
databaseSubscription = null;
|
||||
localPlaylistManager = null;
|
||||
|
||||
super.onDestroy();
|
||||
public void setUserVisibleHint(boolean isVisibleToUser) {
|
||||
super.setUserVisibleHint(isVisibleToUser);
|
||||
if (isVisibleToUser) setTitle(getString(R.string.tab_bookmarks));
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
// Fragment Views
|
||||
// Fragment LifeCycle - Views
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
|
||||
@Override
|
||||
protected void initViews(View rootView, Bundle savedInstanceState) {
|
||||
super.initViews(rootView, savedInstanceState);
|
||||
}
|
||||
|
||||
itemsList = rootView.findViewById(R.id.items_list);
|
||||
itemsList.setLayoutManager(new LinearLayoutManager(activity));
|
||||
|
||||
@Override
|
||||
protected View getListHeader() {
|
||||
final View headerRootLayout = activity.getLayoutInflater()
|
||||
.inflate(R.layout.bookmark_header, itemsList, false);
|
||||
watchHistoryButton = headerRootLayout.findViewById(R.id.watchHistory);
|
||||
mostWatchedButton = headerRootLayout.findViewById(R.id.mostWatched);
|
||||
|
||||
itemListAdapter.setHeader(headerRootLayout);
|
||||
|
||||
itemsList.setAdapter(itemListAdapter);
|
||||
return headerRootLayout;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -168,41 +127,51 @@ public class BookmarkFragment extends BaseStateFragment<List<PlaylistMetadataEnt
|
|||
});
|
||||
}
|
||||
|
||||
private void showDeleteDialog(final PlaylistMetadataEntry item) {
|
||||
new AlertDialog.Builder(activity)
|
||||
.setTitle(item.name)
|
||||
.setMessage(R.string.delete_playlist_prompt)
|
||||
.setCancelable(true)
|
||||
.setPositiveButton(R.string.delete, (dialog, i) -> {
|
||||
final Toast deleteSuccessful = Toast.makeText(getContext(),
|
||||
R.string.playlist_delete_success, Toast.LENGTH_SHORT);
|
||||
disposables.add(localPlaylistManager.deletePlaylist(item.uid)
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribe(ignored -> deleteSuccessful.show()));
|
||||
})
|
||||
.setNegativeButton(R.string.cancel, null)
|
||||
.show();
|
||||
}
|
||||
|
||||
private void resetFragment() {
|
||||
if (disposables != null) disposables.clear();
|
||||
if (itemListAdapter != null) itemListAdapter.clearStreamItemList();
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
// Subscriptions Loader
|
||||
// Fragment LifeCycle - Loading
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
|
||||
@Override
|
||||
public void startLoading(boolean forceLoad) {
|
||||
super.startLoading(forceLoad);
|
||||
resetFragment();
|
||||
|
||||
localPlaylistManager.getPlaylists()
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribe(getSubscriptionSubscriber());
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
// Fragment LifeCycle - Destruction
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
|
||||
@Override
|
||||
public void onPause() {
|
||||
super.onPause();
|
||||
itemsListState = itemsList.getLayoutManager().onSaveInstanceState();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDestroyView() {
|
||||
super.onDestroyView();
|
||||
if (disposables != null) disposables.clear();
|
||||
if (databaseSubscription != null) databaseSubscription.cancel();
|
||||
|
||||
databaseSubscription = null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDestroy() {
|
||||
super.onDestroy();
|
||||
if (disposables != null) disposables.dispose();
|
||||
|
||||
disposables = null;
|
||||
localPlaylistManager = null;
|
||||
itemsListState = null;
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
// Subscriptions Loader
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
|
||||
private Subscriber<List<PlaylistMetadataEntry>> getSubscriptionSubscriber() {
|
||||
return new Subscriber<List<PlaylistMetadataEntry>>() {
|
||||
@Override
|
||||
|
@ -238,55 +207,58 @@ public class BookmarkFragment extends BaseStateFragment<List<PlaylistMetadataEnt
|
|||
|
||||
if (result.isEmpty()) {
|
||||
showEmptyState();
|
||||
} else {
|
||||
itemListAdapter.addItems(infoItemsOf(result));
|
||||
return;
|
||||
}
|
||||
|
||||
itemListAdapter.addItems(result);
|
||||
if (itemsListState != null) {
|
||||
itemsList.getLayoutManager().onRestoreInstanceState(itemsListState);
|
||||
itemsListState = null;
|
||||
}
|
||||
hideLoading();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private List<PlaylistMetadataEntry> infoItemsOf(List<PlaylistMetadataEntry> playlists) {
|
||||
Collections.sort(playlists, (o1, o2) -> o1.name.compareToIgnoreCase(o2.name));
|
||||
return playlists;
|
||||
}
|
||||
|
||||
/*//////////////////////////////////////////////////////////////////////////
|
||||
// Contract
|
||||
//////////////////////////////////////////////////////////////////////////*/
|
||||
|
||||
@Override
|
||||
public void showLoading() {
|
||||
super.showLoading();
|
||||
animateView(itemsList, false, 100);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void hideLoading() {
|
||||
super.hideLoading();
|
||||
animateView(itemsList, true, 200);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void showEmptyState() {
|
||||
super.showEmptyState();
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
// Fragment Error Handling
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
|
||||
@Override
|
||||
protected boolean onError(Throwable exception) {
|
||||
resetFragment();
|
||||
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
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
|
||||
private void showDeleteDialog(final PlaylistMetadataEntry item) {
|
||||
new AlertDialog.Builder(activity)
|
||||
.setTitle(item.name)
|
||||
.setMessage(R.string.delete_playlist_prompt)
|
||||
.setCancelable(true)
|
||||
.setPositiveButton(R.string.delete, (dialog, i) ->
|
||||
disposables.add(deletePlaylist(item.uid))
|
||||
)
|
||||
.setNegativeButton(R.string.cancel, null)
|
||||
.show();
|
||||
}
|
||||
|
||||
private Disposable deletePlaylist(final long playlistId) {
|
||||
return localPlaylistManager.deletePlaylist(playlistId)
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribe(ignored -> {/*Do nothing on success*/},
|
||||
throwable -> Log.e(TAG, "Playlist deletion failed, id=["
|
||||
+ playlistId + "]")
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -6,7 +6,7 @@ import org.schabi.newpipe.database.stream.StreamStatisticsEntry;
|
|||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
public class MostPlayedFragment extends StatisticsPlaylistFragment {
|
||||
public final class MostPlayedFragment extends StatisticsPlaylistFragment {
|
||||
@Override
|
||||
protected String getName() {
|
||||
return getString(R.string.title_most_played);
|
||||
|
|
|
@ -32,13 +32,9 @@ import java.util.List;
|
|||
import icepick.State;
|
||||
import io.reactivex.android.schedulers.AndroidSchedulers;
|
||||
|
||||
import static org.schabi.newpipe.util.AnimationUtils.animateView;
|
||||
|
||||
public abstract class StatisticsPlaylistFragment
|
||||
extends BaseLocalListFragment<List<StreamStatisticsEntry>, Void> {
|
||||
|
||||
private View headerRootLayout;
|
||||
private View playlistControl;
|
||||
private View headerPlayAllButton;
|
||||
private View headerPopupButton;
|
||||
private View headerBackgroundButton;
|
||||
|
@ -59,13 +55,13 @@ public abstract class StatisticsPlaylistFragment
|
|||
protected abstract List<StreamStatisticsEntry> processResult(final List<StreamStatisticsEntry> results);
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
// Fragment LifeCycle
|
||||
// Fragment LifeCycle - Creation
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
|
||||
@Override
|
||||
public void onAttach(Context context) {
|
||||
super.onAttach(context);
|
||||
recordManager = new HistoryRecordManager(context);
|
||||
public void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
recordManager = new HistoryRecordManager(getContext());
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -75,46 +71,23 @@ public abstract class StatisticsPlaylistFragment
|
|||
return inflater.inflate(R.layout.fragment_playlist, container, false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPause() {
|
||||
super.onPause();
|
||||
itemsListState = itemsList.getLayoutManager().onSaveInstanceState();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDestroyView() {
|
||||
if (databaseSubscription != null) databaseSubscription.cancel();
|
||||
super.onDestroyView();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDestroy() {
|
||||
if (databaseSubscription != null) databaseSubscription.cancel();
|
||||
databaseSubscription = null;
|
||||
recordManager = null;
|
||||
|
||||
super.onDestroy();
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
// Fragment Views
|
||||
// Fragment LifeCycle - Views
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
|
||||
@Override
|
||||
protected void initViews(View rootView, Bundle savedInstanceState) {
|
||||
super.initViews(rootView, savedInstanceState);
|
||||
setFragmentTitle(getName());
|
||||
setTitle(getName());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected View getListHeader() {
|
||||
headerRootLayout = activity.getLayoutInflater().inflate(R.layout.playlist_control,
|
||||
final View headerRootLayout = activity.getLayoutInflater().inflate(R.layout.playlist_control,
|
||||
itemsList, false);
|
||||
playlistControl = 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);
|
||||
headerBackgroundButton = headerRootLayout.findViewById(R.id.playlist_ctrl_play_bg_button);
|
||||
|
||||
return headerRootLayout;
|
||||
}
|
||||
|
||||
|
@ -139,9 +112,124 @@ public abstract class StatisticsPlaylistFragment
|
|||
}
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
// Fragment LifeCycle - Loading
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
|
||||
@Override
|
||||
public void startLoading(boolean forceLoad) {
|
||||
super.startLoading(forceLoad);
|
||||
recordManager.getStreamStatistics()
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribe(getHistoryObserver());
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
// Fragment LifeCycle - Destruction
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
|
||||
@Override
|
||||
public void onPause() {
|
||||
super.onPause();
|
||||
itemsListState = itemsList.getLayoutManager().onSaveInstanceState();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDestroyView() {
|
||||
super.onDestroyView();
|
||||
if (databaseSubscription != null) databaseSubscription.cancel();
|
||||
databaseSubscription = null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDestroy() {
|
||||
super.onDestroy();
|
||||
recordManager = null;
|
||||
itemsListState = null;
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
// Statistics Loader
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
|
||||
private Subscriber<List<StreamStatisticsEntry>> getHistoryObserver() {
|
||||
return new Subscriber<List<StreamStatisticsEntry>>() {
|
||||
@Override
|
||||
public void onSubscribe(Subscription s) {
|
||||
showLoading();
|
||||
|
||||
if (databaseSubscription != null) databaseSubscription.cancel();
|
||||
databaseSubscription = s;
|
||||
databaseSubscription.request(1);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onNext(List<StreamStatisticsEntry> streams) {
|
||||
handleResult(streams);
|
||||
if (databaseSubscription != null) databaseSubscription.request(1);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onError(Throwable exception) {
|
||||
StatisticsPlaylistFragment.this.onError(exception);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onComplete() {
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleResult(@NonNull List<StreamStatisticsEntry> result) {
|
||||
super.handleResult(result);
|
||||
itemListAdapter.clearStreamItemList();
|
||||
|
||||
if (result.isEmpty()) {
|
||||
showEmptyState();
|
||||
return;
|
||||
}
|
||||
|
||||
itemListAdapter.addItems(processResult(result));
|
||||
if (itemsListState != null) {
|
||||
itemsList.getLayoutManager().onRestoreInstanceState(itemsListState);
|
||||
itemsListState = null;
|
||||
}
|
||||
|
||||
headerPlayAllButton.setOnClickListener(view ->
|
||||
NavigationHelper.playOnMainPlayer(activity, getPlayQueue()));
|
||||
headerPopupButton.setOnClickListener(view ->
|
||||
NavigationHelper.playOnPopupPlayer(activity, getPlayQueue()));
|
||||
headerBackgroundButton.setOnClickListener(view ->
|
||||
NavigationHelper.playOnBackgroundPlayer(activity, getPlayQueue()));
|
||||
|
||||
hideLoading();
|
||||
}
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
// 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;
|
||||
|
||||
onUnrecoverableError(exception, UserAction.SOMETHING_ELSE,
|
||||
"none", "History Statistics", R.string.general_error);
|
||||
return true;
|
||||
}
|
||||
|
||||
/*//////////////////////////////////////////////////////////////////////////
|
||||
// Utils
|
||||
//////////////////////////////////////////////////////////////////////////*/
|
||||
|
||||
private void showStreamDialog(final StreamStatisticsEntry item) {
|
||||
final Context context = getContext();
|
||||
final Activity activity = getActivity();
|
||||
|
@ -182,113 +270,6 @@ public abstract class StatisticsPlaylistFragment
|
|||
new InfoItemDialog(getActivity(), infoItem, commands, actions).show();
|
||||
}
|
||||
|
||||
private void resetFragment() {
|
||||
if (databaseSubscription != null) databaseSubscription.cancel();
|
||||
if (itemListAdapter != null) itemListAdapter.clearStreamItemList();
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
// Loader
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
|
||||
@Override
|
||||
public void showLoading() {
|
||||
super.showLoading();
|
||||
animateView(headerRootLayout, false, 200);
|
||||
animateView(itemsList, false, 100);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void startLoading(boolean forceLoad) {
|
||||
super.startLoading(forceLoad);
|
||||
resetFragment();
|
||||
|
||||
recordManager.getStreamStatistics()
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribe(getHistoryObserver());
|
||||
}
|
||||
|
||||
private Subscriber<List<StreamStatisticsEntry>> getHistoryObserver() {
|
||||
return new Subscriber<List<StreamStatisticsEntry>>() {
|
||||
@Override
|
||||
public void onSubscribe(Subscription s) {
|
||||
showLoading();
|
||||
|
||||
if (databaseSubscription != null) databaseSubscription.cancel();
|
||||
databaseSubscription = s;
|
||||
databaseSubscription.request(1);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onNext(List<StreamStatisticsEntry> streams) {
|
||||
handleResult(streams);
|
||||
if (databaseSubscription != null) databaseSubscription.request(1);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onError(Throwable exception) {
|
||||
StatisticsPlaylistFragment.this.onError(exception);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onComplete() {
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleResult(@NonNull List<StreamStatisticsEntry> result) {
|
||||
super.handleResult(result);
|
||||
itemListAdapter.clearStreamItemList();
|
||||
|
||||
if (result.isEmpty()) {
|
||||
showEmptyState();
|
||||
return;
|
||||
}
|
||||
|
||||
animateView(headerRootLayout, true, 100);
|
||||
animateView(itemsList, true, 300);
|
||||
|
||||
itemListAdapter.addItems(processResult(result));
|
||||
if (itemsListState != null) {
|
||||
itemsList.getLayoutManager().onRestoreInstanceState(itemsListState);
|
||||
itemsListState = null;
|
||||
}
|
||||
|
||||
playlistControl.setVisibility(View.VISIBLE);
|
||||
headerPlayAllButton.setOnClickListener(view ->
|
||||
NavigationHelper.playOnMainPlayer(activity, getPlayQueue()));
|
||||
headerPopupButton.setOnClickListener(view ->
|
||||
NavigationHelper.playOnPopupPlayer(activity, getPlayQueue()));
|
||||
headerBackgroundButton.setOnClickListener(view ->
|
||||
NavigationHelper.playOnBackgroundPlayer(activity, getPlayQueue()));
|
||||
hideLoading();
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
// Fragment Error Handling
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
|
||||
@Override
|
||||
protected boolean onError(Throwable exception) {
|
||||
resetFragment();
|
||||
if (super.onError(exception)) return true;
|
||||
|
||||
onUnrecoverableError(exception, UserAction.SOMETHING_ELSE,
|
||||
"none", "History Statistics", R.string.general_error);
|
||||
return true;
|
||||
}
|
||||
|
||||
/*//////////////////////////////////////////////////////////////////////////
|
||||
// Utils
|
||||
//////////////////////////////////////////////////////////////////////////*/
|
||||
|
||||
protected void setFragmentTitle(final String title) {
|
||||
if (activity.getSupportActionBar() != null) {
|
||||
activity.getSupportActionBar().setTitle(title);
|
||||
}
|
||||
}
|
||||
|
||||
private PlayQueue getPlayQueue() {
|
||||
return getPlayQueue(0);
|
||||
}
|
||||
|
|
|
@ -6,7 +6,7 @@ import org.schabi.newpipe.database.stream.StreamStatisticsEntry;
|
|||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
public class WatchHistoryFragment extends StatisticsPlaylistFragment {
|
||||
public final class WatchHistoryFragment extends StatisticsPlaylistFragment {
|
||||
@Override
|
||||
protected String getName() {
|
||||
return getString(R.string.title_watch_history);
|
||||
|
|
|
@ -14,6 +14,7 @@ import android.support.design.widget.Snackbar;
|
|||
import android.support.v7.app.AlertDialog;
|
||||
import android.support.v7.widget.LinearLayoutManager;
|
||||
import android.support.v7.widget.RecyclerView;
|
||||
import android.util.Log;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
|
@ -173,10 +174,19 @@ public abstract class HistoryFragment<E> extends BaseFragment
|
|||
|
||||
final Disposable deletion = delete(itemsToDelete)
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribe();
|
||||
.subscribe(
|
||||
ignored -> Log.d(TAG, "Clear history deleted [" +
|
||||
itemsToDelete.size() + "] items."),
|
||||
error -> Log.e(TAG, "Clear history delete step failed", error)
|
||||
);
|
||||
|
||||
final Disposable cleanUp = historyRecordManager.removeOrphanedRecords()
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribe();
|
||||
.subscribe(
|
||||
ignored -> Log.d(TAG, "Clear history deleted orphaned stream records"),
|
||||
error -> Log.e(TAG, "Clear history remove orphaned records failed", error)
|
||||
);
|
||||
|
||||
disposables.addAll(deletion, cleanUp);
|
||||
|
||||
makeSnackbar(R.string.history_cleared);
|
||||
|
|
|
@ -7,6 +7,7 @@ import android.support.annotation.Nullable;
|
|||
import android.support.annotation.StringRes;
|
||||
import android.support.v7.app.AlertDialog;
|
||||
import android.support.v7.widget.RecyclerView;
|
||||
import android.util.Log;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
|
@ -83,17 +84,25 @@ public class SearchHistoryFragment extends HistoryFragment<SearchHistoryEntry> {
|
|||
.setCancelable(true)
|
||||
.setNeutralButton(R.string.cancel, null)
|
||||
.setPositiveButton(R.string.delete_one, (dialog, i) -> {
|
||||
final Single<Integer> onDelete = historyRecordManager
|
||||
final Disposable onDelete = historyRecordManager
|
||||
.deleteSearches(Collections.singleton(item))
|
||||
.observeOn(AndroidSchedulers.mainThread());
|
||||
disposables.add(onDelete.subscribe());
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribe(
|
||||
ignored -> {/*successful*/},
|
||||
error -> Log.e(TAG, "Search history Delete One failed:", error)
|
||||
);
|
||||
disposables.add(onDelete);
|
||||
makeSnackbar(R.string.item_deleted);
|
||||
})
|
||||
.setNegativeButton(R.string.delete_all, (dialog, i) -> {
|
||||
final Single<Integer> onDeleteAll = historyRecordManager
|
||||
final Disposable onDeleteAll = historyRecordManager
|
||||
.deleteSearchHistory(item.getSearch())
|
||||
.observeOn(AndroidSchedulers.mainThread());
|
||||
disposables.add(onDeleteAll.subscribe());
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribe(
|
||||
ignored -> {/*successful*/},
|
||||
error -> Log.e(TAG, "Search history Delete All failed:", error)
|
||||
);
|
||||
disposables.add(onDeleteAll);
|
||||
makeSnackbar(R.string.item_deleted);
|
||||
})
|
||||
.show();
|
||||
|
@ -112,8 +121,7 @@ public class SearchHistoryFragment extends HistoryFragment<SearchHistoryEntry> {
|
|||
|
||||
protected class SearchHistoryAdapter extends HistoryEntryAdapter<SearchHistoryEntry, ViewHolder> {
|
||||
|
||||
|
||||
public SearchHistoryAdapter(Context context) {
|
||||
SearchHistoryAdapter(Context context) {
|
||||
super(context);
|
||||
}
|
||||
|
||||
|
|
|
@ -8,6 +8,7 @@ import android.support.annotation.Nullable;
|
|||
import android.support.annotation.StringRes;
|
||||
import android.support.v7.app.AlertDialog;
|
||||
import android.support.v7.widget.RecyclerView;
|
||||
import android.util.Log;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
|
@ -29,6 +30,7 @@ import java.util.List;
|
|||
import io.reactivex.Flowable;
|
||||
import io.reactivex.Single;
|
||||
import io.reactivex.android.schedulers.AndroidSchedulers;
|
||||
import io.reactivex.disposables.Disposable;
|
||||
|
||||
|
||||
public class WatchedHistoryFragment extends HistoryFragment<StreamHistoryEntry> {
|
||||
|
@ -85,17 +87,25 @@ public class WatchedHistoryFragment extends HistoryFragment<StreamHistoryEntry>
|
|||
.setCancelable(true)
|
||||
.setNeutralButton(R.string.cancel, null)
|
||||
.setPositiveButton(R.string.delete_one, (dialog, i) -> {
|
||||
final Single<Integer> onDelete = historyRecordManager
|
||||
final Disposable onDelete = historyRecordManager
|
||||
.deleteStreamHistory(Collections.singleton(item))
|
||||
.observeOn(AndroidSchedulers.mainThread());
|
||||
disposables.add(onDelete.subscribe());
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribe(
|
||||
ignored -> {/*successful*/},
|
||||
error -> Log.e(TAG, "Watch history Delete One failed:", error)
|
||||
);
|
||||
disposables.add(onDelete);
|
||||
makeSnackbar(R.string.item_deleted);
|
||||
})
|
||||
.setNegativeButton(R.string.delete_all, (dialog, i) -> {
|
||||
final Single<Integer> onDeleteAll = historyRecordManager
|
||||
final Disposable onDeleteAll = historyRecordManager
|
||||
.deleteStreamHistory(item.streamId)
|
||||
.observeOn(AndroidSchedulers.mainThread());
|
||||
disposables.add(onDeleteAll.subscribe());
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribe(
|
||||
ignored -> {/*successful*/},
|
||||
error -> Log.e(TAG, "Watch history Delete All failed:", error)
|
||||
);
|
||||
disposables.add(onDeleteAll);
|
||||
makeSnackbar(R.string.item_deleted);
|
||||
})
|
||||
.show();
|
||||
|
|
|
@ -676,7 +676,11 @@ public abstract class BasePlayer implements Player.EventListener, PlaybackListen
|
|||
}
|
||||
|
||||
// TODO: update exoplayer to 2.6.x in order to register view count on repeated streams
|
||||
databaseUpdateReactor.add(recordManager.onViewed(currentInfo).subscribe());
|
||||
databaseUpdateReactor.add(recordManager.onViewed(currentInfo).onErrorComplete()
|
||||
.subscribe(
|
||||
ignored -> {/* successful */},
|
||||
error -> Log.e(TAG, "Player onViewed() failure: ", error)
|
||||
));
|
||||
initThumbnail(info == null ? item.getThumbnailUrl() : info.thumbnail_url);
|
||||
}
|
||||
|
||||
|
@ -844,7 +848,10 @@ public abstract class BasePlayer implements Player.EventListener, PlaybackListen
|
|||
final Disposable stateSaver = recordManager.saveStreamState(info, progress)
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.onErrorComplete()
|
||||
.subscribe();
|
||||
.subscribe(
|
||||
ignored -> {/* successful */},
|
||||
error -> Log.e(TAG, "savePlaybackState() failure: ", error)
|
||||
);
|
||||
databaseUpdateReactor.add(stateSaver);
|
||||
}
|
||||
|
||||
|
|
|
@ -11,9 +11,12 @@
|
|||
android:id="@+id/playlist_title_view"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginLeft="8dp"
|
||||
android:layout_marginRight="8dp"
|
||||
android:layout_marginTop="6dp"
|
||||
android:paddingTop="6dp"
|
||||
android:paddingBottom="6dp"
|
||||
android:paddingLeft="12dp"
|
||||
android:paddingRight="12dp"
|
||||
android:layout_alignParentLeft="true"
|
||||
android:layout_alignParentStart="true"
|
||||
android:layout_toLeftOf="@id/playlist_stream_count"
|
||||
android:layout_toStartOf="@id/playlist_stream_count"
|
||||
android:background="?attr/selectableItemBackground"
|
||||
|
@ -34,7 +37,7 @@
|
|||
android:layout_height="wrap_content"
|
||||
android:layout_alignBottom="@id/playlist_title_view"
|
||||
android:layout_alignParentRight="true"
|
||||
android:layout_marginRight="6dp"
|
||||
android:padding="6dp"
|
||||
android:ellipsize="end"
|
||||
android:gravity="right|center_vertical"
|
||||
android:maxLines="1"
|
||||
|
|
|
@ -119,14 +119,12 @@
|
|||
<string name="subscription_page_key" translatable="false">subscription_page_key</string>
|
||||
<string name="kiosk_page_key" translatable="false">kiosk_page</string>
|
||||
<string name="channel_page_key" translatable="false">channel_page</string>
|
||||
<string name="bookmark_page_key" translatable="false">bookmark_page</string>
|
||||
<string-array name="main_page_content_pages" translatable="false">
|
||||
<item>@string/blank_page_key</item>
|
||||
<item>@string/kiosk_page_key</item>
|
||||
<item>@string/feed_page_key</item>
|
||||
<item>@string/subscription_page_key</item>
|
||||
<item>@string/channel_page_key</item>
|
||||
<item>@string/bookmark_page_key</item>
|
||||
</string-array>
|
||||
<string name="main_page_selected_service" translatable="false">main_page_selected_service</string>
|
||||
<string name="main_page_selected_channel_name" translatable="false">main_page_selected_channel_name</string>
|
||||
|
|
|
@ -390,6 +390,5 @@
|
|||
<string name="playlist_creation_success">Playlist successfully created</string>
|
||||
<string name="playlist_add_stream_success">Added to playlist</string>
|
||||
<string name="playlist_thumbnail_change_success">Playlist thumbnail changed</string>
|
||||
<string name="playlist_rename_success">Playlist renamed</string>
|
||||
<string name="playlist_delete_success">Playlist deleted</string>
|
||||
<string name="playlist_delete_failure">Failed to delete playlist</string>
|
||||
</resources>
|
||||
|
|
Loading…
Reference in New Issue