From bfb56b4144f1fec9680dbafd5fe79420d731e0a7 Mon Sep 17 00:00:00 2001 From: GGAutomaton <32899400+GGAutomaton@users.noreply.github.com> Date: Thu, 14 Apr 2022 16:59:52 +0800 Subject: [PATCH] UI design and behavior --- .../playlist/model/PlaylistEntity.java | 1 - .../local/bookmark/BookmarkFragment.java | 207 +++++++++++++----- .../local/holder/LocalPlaylistItemHolder.java | 21 +- .../holder/RemotePlaylistItemHolder.java | 22 +- .../layout/list_playlist_bookmark_item.xml | 84 +++++++ 5 files changed, 282 insertions(+), 53 deletions(-) create mode 100644 app/src/main/res/layout/list_playlist_bookmark_item.xml diff --git a/app/src/main/java/org/schabi/newpipe/database/playlist/model/PlaylistEntity.java b/app/src/main/java/org/schabi/newpipe/database/playlist/model/PlaylistEntity.java index 82f697ab3..cdbbdebc0 100644 --- a/app/src/main/java/org/schabi/newpipe/database/playlist/model/PlaylistEntity.java +++ b/app/src/main/java/org/schabi/newpipe/database/playlist/model/PlaylistEntity.java @@ -2,7 +2,6 @@ package org.schabi.newpipe.database.playlist.model; import androidx.room.ColumnInfo; import androidx.room.Entity; -import androidx.room.Ignore; import androidx.room.Index; import androidx.room.PrimaryKey; diff --git a/app/src/main/java/org/schabi/newpipe/local/bookmark/BookmarkFragment.java b/app/src/main/java/org/schabi/newpipe/local/bookmark/BookmarkFragment.java index 6d2fbfbfc..63c63c1e0 100644 --- a/app/src/main/java/org/schabi/newpipe/local/bookmark/BookmarkFragment.java +++ b/app/src/main/java/org/schabi/newpipe/local/bookmark/BookmarkFragment.java @@ -1,5 +1,7 @@ package org.schabi.newpipe.local.bookmark; +import static org.schabi.newpipe.util.ThemeHelper.shouldUseGridLayout; + import android.os.Bundle; import android.os.Parcelable; import android.text.InputType; @@ -12,6 +14,8 @@ import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.appcompat.app.AlertDialog; import androidx.fragment.app.FragmentManager; +import androidx.recyclerview.widget.ItemTouchHelper; +import androidx.recyclerview.widget.RecyclerView; import org.reactivestreams.Subscriber; import org.reactivestreams.Subscription; @@ -41,6 +45,7 @@ import io.reactivex.rxjava3.disposables.CompositeDisposable; import io.reactivex.rxjava3.disposables.Disposable; public final class BookmarkFragment extends BaseLocalListFragment, Void> { + private static final int MINIMUM_INITIAL_DRAG_VELOCITY = 12; @State protected Parcelable itemsListState; @@ -48,6 +53,7 @@ public final class BookmarkFragment extends BaseLocalListFragment() { @Override public void selected(final LocalItem selectedItem) { @@ -126,6 +135,14 @@ public final class BookmarkFragment extends BaseLocalListFragment - changeLocalPlaylistName( - selectedItem.uid, - dialogBinding.dialogEditText.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 deleteReactor) { - if (activity == null || disposables == null) { - return; - } - - new AlertDialog.Builder(activity) - .setTitle(name) - .setMessage(R.string.delete_playlist_prompt) - .setCancelable(true) - .setPositiveButton(R.string.delete, (dialog, i) -> - disposables.add(deleteReactor - .observeOn(AndroidSchedulers.mainThread()) - .subscribe(ignored -> { /*Do nothing on success*/ }, throwable -> - showError(new ErrorInfo(throwable, - UserAction.REQUESTED_BOOKMARK, - "Deleting playlist"))))) - .setNegativeButton(R.string.cancel, null) - .show(); - } + /*////////////////////////////////////////////////////////////////////////// + // Playlist Metadata Manipulation + //////////////////////////////////////////////////////////////////////////*/ private void changeLocalPlaylistName(final long id, final String name) { if (localPlaylistManager == null) { @@ -379,5 +350,141 @@ public final class BookmarkFragment extends BaseLocalListFragment items = itemListAdapter.getItemsList(); + for (int i = 0; i < items.size(); i++) { + final LocalItem item = items.get(i); + if (item instanceof PlaylistMetadataEntry) { + changeLocalPlaylistDisplayIndex(((PlaylistMetadataEntry) item).uid, i); + } else if (item instanceof PlaylistRemoteEntity) { + changeLocalPlaylistDisplayIndex(((PlaylistRemoteEntity) item).getUid(), i); + } + } + } + + private ItemTouchHelper.SimpleCallback getItemTouchCallback() { + int directions = ItemTouchHelper.UP | ItemTouchHelper.DOWN; + if (shouldUseGridLayout(requireContext())) { + directions |= ItemTouchHelper.LEFT | ItemTouchHelper.RIGHT; + } + return new ItemTouchHelper.SimpleCallback(directions, + ItemTouchHelper.ACTION_STATE_IDLE) { + @Override + public int interpolateOutOfBoundsScroll(@NonNull 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(@NonNull final RecyclerView recyclerView, + @NonNull final RecyclerView.ViewHolder source, + @NonNull final RecyclerView.ViewHolder target) { + if (source.getItemViewType() != target.getItemViewType() + || itemListAdapter == null) { + return false; + } + + // todo: is it correct + final int sourceIndex = source.getBindingAdapterPosition(); + final int targetIndex = target.getBindingAdapterPosition(); + final boolean isSwapped = itemListAdapter.swapItems(sourceIndex, targetIndex); + if (isSwapped) { + // todo + //saveChanges(); + saveImmediate(); + } + return isSwapped; + } + + @Override + public boolean isLongPressDragEnabled() { + return false; + } + + @Override + public boolean isItemViewSwipeEnabled() { + return false; + } + + @Override + public void onSwiped(@NonNull final RecyclerView.ViewHolder viewHolder, + final int swipeDir) { + } + }; + } + + /////////////////////////////////////////////////////////////////////////// + // Utils + /////////////////////////////////////////////////////////////////////////// + + private void showRemoteDeleteDialog(final PlaylistRemoteEntity item) { + showDeleteDialog(item.getName(), remotePlaylistManager.deletePlaylist(item.getUid())); + } + + private void showLocalDialog(final PlaylistMetadataEntry selectedItem) { + final DialogEditTextBinding dialogBinding + = DialogEditTextBinding.inflate(getLayoutInflater()); + dialogBinding.dialogEditText.setHint(R.string.name); + dialogBinding.dialogEditText.setInputType(InputType.TYPE_CLASS_TEXT); + dialogBinding.dialogEditText.setText(selectedItem.name); + + final AlertDialog.Builder builder = new AlertDialog.Builder(activity); + builder.setView(dialogBinding.getRoot()) + .setPositiveButton(R.string.rename_playlist, (dialog, which) -> + changeLocalPlaylistName( + selectedItem.uid, + dialogBinding.dialogEditText.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 deleteReactor) { + if (activity == null || disposables == null) { + return; + } + + new AlertDialog.Builder(activity) + .setTitle(name) + .setMessage(R.string.delete_playlist_prompt) + .setCancelable(true) + .setPositiveButton(R.string.delete, (dialog, i) -> + disposables.add(deleteReactor + .observeOn(AndroidSchedulers.mainThread()) + .subscribe(ignored -> { /*Do nothing on success*/ }, throwable -> + showError(new ErrorInfo(throwable, + UserAction.REQUESTED_BOOKMARK, + "Deleting playlist"))))) + .setNegativeButton(R.string.cancel, null) + .show(); + } } diff --git a/app/src/main/java/org/schabi/newpipe/local/holder/LocalPlaylistItemHolder.java b/app/src/main/java/org/schabi/newpipe/local/holder/LocalPlaylistItemHolder.java index f8c5176ec..57a944709 100644 --- a/app/src/main/java/org/schabi/newpipe/local/holder/LocalPlaylistItemHolder.java +++ b/app/src/main/java/org/schabi/newpipe/local/holder/LocalPlaylistItemHolder.java @@ -1,8 +1,10 @@ package org.schabi.newpipe.local.holder; +import android.view.MotionEvent; import android.view.View; import android.view.ViewGroup; +import org.schabi.newpipe.R; import org.schabi.newpipe.database.LocalItem; import org.schabi.newpipe.database.playlist.PlaylistMetadataEntry; import org.schabi.newpipe.local.LocalItemBuilder; @@ -13,13 +15,16 @@ import org.schabi.newpipe.util.Localization; import java.time.format.DateTimeFormatter; public class LocalPlaylistItemHolder extends PlaylistItemHolder { + private final View itemHandleView; + public LocalPlaylistItemHolder(final LocalItemBuilder infoItemBuilder, final ViewGroup parent) { - super(infoItemBuilder, parent); + this(infoItemBuilder, R.layout.list_playlist_bookmark_item, parent); } LocalPlaylistItemHolder(final LocalItemBuilder infoItemBuilder, final int layoutId, final ViewGroup parent) { super(infoItemBuilder, layoutId, parent); + itemHandleView = itemView.findViewById(R.id.itemHandle); } @Override @@ -38,6 +43,20 @@ public class LocalPlaylistItemHolder extends PlaylistItemHolder { PicassoHelper.loadPlaylistThumbnail(item.thumbnailUrl).into(itemThumbnailView); + itemHandleView.setOnTouchListener(getOnTouchListener(item)); + super.updateFromItem(localItem, historyRecordManager, dateTimeFormatter); } + + private View.OnTouchListener getOnTouchListener(final PlaylistMetadataEntry item) { + return (view, motionEvent) -> { + view.performClick(); + if (itemBuilder != null && itemBuilder.getOnItemSelectedListener() != null + && motionEvent.getActionMasked() == MotionEvent.ACTION_DOWN) { + itemBuilder.getOnItemSelectedListener().drag(item, + LocalPlaylistItemHolder.this); + } + return false; + }; + } } diff --git a/app/src/main/java/org/schabi/newpipe/local/holder/RemotePlaylistItemHolder.java b/app/src/main/java/org/schabi/newpipe/local/holder/RemotePlaylistItemHolder.java index 440353ac7..9ecfa6979 100644 --- a/app/src/main/java/org/schabi/newpipe/local/holder/RemotePlaylistItemHolder.java +++ b/app/src/main/java/org/schabi/newpipe/local/holder/RemotePlaylistItemHolder.java @@ -1,8 +1,11 @@ package org.schabi.newpipe.local.holder; import android.text.TextUtils; +import android.view.MotionEvent; +import android.view.View; import android.view.ViewGroup; +import org.schabi.newpipe.R; import org.schabi.newpipe.database.LocalItem; import org.schabi.newpipe.database.playlist.model.PlaylistRemoteEntity; import org.schabi.newpipe.extractor.NewPipe; @@ -14,14 +17,17 @@ import org.schabi.newpipe.util.Localization; import java.time.format.DateTimeFormatter; public class RemotePlaylistItemHolder extends PlaylistItemHolder { + private final View itemHandleView; + public RemotePlaylistItemHolder(final LocalItemBuilder infoItemBuilder, final ViewGroup parent) { - super(infoItemBuilder, parent); + this(infoItemBuilder, R.layout.list_playlist_bookmark_item, parent); } RemotePlaylistItemHolder(final LocalItemBuilder infoItemBuilder, final int layoutId, final ViewGroup parent) { super(infoItemBuilder, layoutId, parent); + itemHandleView = itemView.findViewById(R.id.itemHandle); } @Override @@ -46,6 +52,20 @@ public class RemotePlaylistItemHolder extends PlaylistItemHolder { PicassoHelper.loadPlaylistThumbnail(item.getThumbnailUrl()).into(itemThumbnailView); + itemHandleView.setOnTouchListener(getOnTouchListener(item)); + super.updateFromItem(localItem, historyRecordManager, dateTimeFormatter); } + + private View.OnTouchListener getOnTouchListener(final PlaylistRemoteEntity item) { + return (view, motionEvent) -> { + view.performClick(); + if (itemBuilder != null && itemBuilder.getOnItemSelectedListener() != null + && motionEvent.getActionMasked() == MotionEvent.ACTION_DOWN) { + itemBuilder.getOnItemSelectedListener().drag(item, + RemotePlaylistItemHolder.this); + } + return false; + }; + } } diff --git a/app/src/main/res/layout/list_playlist_bookmark_item.xml b/app/src/main/res/layout/list_playlist_bookmark_item.xml new file mode 100644 index 000000000..642ea2949 --- /dev/null +++ b/app/src/main/res/layout/list_playlist_bookmark_item.xml @@ -0,0 +1,84 @@ + + + + + + + + + + + + + +