diff --git a/app/src/main/java/org/schabi/newpipe/settings/PeertubeInstanceListFragment.java b/app/src/main/java/org/schabi/newpipe/settings/PeertubeInstanceListFragment.java index 81fddbcfbc8..c97768e8ae8 100644 --- a/app/src/main/java/org/schabi/newpipe/settings/PeertubeInstanceListFragment.java +++ b/app/src/main/java/org/schabi/newpipe/settings/PeertubeInstanceListFragment.java @@ -18,6 +18,7 @@ import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.appcompat.app.AlertDialog; +import androidx.appcompat.widget.SwitchCompat; import androidx.fragment.app.Fragment; import androidx.preference.PreferenceManager; import androidx.recyclerview.widget.DiffUtil; @@ -134,6 +135,17 @@ public void onCreateOptionsMenu(@NonNull final Menu menu, @NonNull final MenuInflater inflater) { super.onCreateOptionsMenu(menu, inflater); inflater.inflate(R.menu.menu_chooser_fragment, menu); + final SwitchCompat editSwitch = + (SwitchCompat) menu.findItem(R.id.menu_item_edit_mode).getActionView(); + if (editSwitch != null) { + editSwitch.setChecked( + instanceListAdapter != null && instanceListAdapter.editModeEnabled); + editSwitch.setOnCheckedChangeListener((buttonView, isChecked) -> { + if (instanceListAdapter != null) { + instanceListAdapter.setEditMode(isChecked); + } + }); + } } @Override @@ -199,6 +211,24 @@ private void showAddItemDialog(final Context c) { .show(); } + private void showDeleteInstanceDialog(final PeertubeInstance instance, final int position) { + new AlertDialog.Builder(requireContext()) + .setTitle(instance.getName()) + .setCancelable(true) + .setNegativeButton(R.string.cancel, null) + .setPositiveButton(R.string.delete, (dialog, which) -> { + final var list = new ArrayList<>(instanceListAdapter.getCurrentList()); + list.remove(position); + + if (list.isEmpty()) { + list.add(selectedInstance); + } + + instanceListAdapter.submitList(list); + }) + .show(); + } + private void addInstance(final String url) { final String cleanUrl = cleanUrl(url); if (cleanUrl == null) { @@ -324,6 +354,12 @@ private class InstanceListAdapter private final LayoutInflater inflater; private final ItemTouchHelper itemTouchHelper; private RadioButton lastChecked; + boolean editModeEnabled = false; + + void setEditMode(final boolean enabled) { + editModeEnabled = enabled; + notifyDataSetChanged(); + } InstanceListAdapter(final Context context, final ItemTouchHelper itemTouchHelper) { super(new PeertubeInstanceCallback()); @@ -357,6 +393,9 @@ class TabViewHolder extends RecyclerView.ViewHolder { TabViewHolder(final ItemInstanceBinding binding) { super(binding.getRoot()); this.itemBinding = binding; + + itemView.setOnLongClickListener(null); + itemView.setOnClickListener(null); } @SuppressLint("ClickableViewAccessibility") @@ -392,6 +431,34 @@ void bind(final int position) { } }); itemBinding.instanceIcon.setImageResource(R.drawable.ic_placeholder_peertube); + + itemBinding.handle.setVisibility(editModeEnabled ? View.GONE : View.VISIBLE); + itemBinding.itemActions.setVisibility(editModeEnabled ? View.VISIBLE : View.GONE); + if (editModeEnabled) { + final boolean isActiveInstance = + instance.getUrl().equals(selectedInstance.getUrl()); + itemBinding.btnMoveUp.setEnabled(position > 0); + itemBinding.btnMoveDown.setEnabled(position < getItemCount() - 1); + itemBinding.btnDelete.setEnabled(!isActiveInstance); + itemBinding.btnMoveUp.setOnClickListener(v -> { + final int pos = getBindingAdapterPosition(); + if (pos > 0) { + swapItems(pos, pos - 1); + } + }); + itemBinding.btnMoveDown.setOnClickListener(v -> { + final int pos = getBindingAdapterPosition(); + if (pos < getItemCount() - 1) { + swapItems(pos, pos + 1); + } + }); + itemBinding.btnDelete.setOnClickListener(v -> { + final int pos = getBindingAdapterPosition(); + if (pos != RecyclerView.NO_POSITION) { + showDeleteInstanceDialog(getItem(pos), pos); + } + }); + } } } } diff --git a/app/src/main/java/org/schabi/newpipe/settings/tabs/ChooseTabsFragment.java b/app/src/main/java/org/schabi/newpipe/settings/tabs/ChooseTabsFragment.java index 738a9c9263e..061e86a3ca9 100644 --- a/app/src/main/java/org/schabi/newpipe/settings/tabs/ChooseTabsFragment.java +++ b/app/src/main/java/org/schabi/newpipe/settings/tabs/ChooseTabsFragment.java @@ -13,6 +13,7 @@ import android.view.MotionEvent; import android.view.View; import android.view.ViewGroup; +import android.widget.ImageButton; import android.widget.ImageView; import android.widget.TextView; @@ -20,6 +21,7 @@ import androidx.annotation.Nullable; import androidx.appcompat.app.AlertDialog; import androidx.appcompat.widget.AppCompatImageView; +import androidx.appcompat.widget.SwitchCompat; import androidx.fragment.app.Fragment; import androidx.recyclerview.widget.ItemTouchHelper; import androidx.recyclerview.widget.LinearLayoutManager; @@ -111,6 +113,17 @@ public void onCreateOptionsMenu(@NonNull final Menu menu, restoreDefaults(); return true; }); + final SwitchCompat editSwitch = + (SwitchCompat) menu.findItem(R.id.menu_item_edit_mode).getActionView(); + if (editSwitch != null) { + editSwitch.setChecked( + selectedTabsAdapter != null && selectedTabsAdapter.editModeEnabled); + editSwitch.setOnCheckedChangeListener((buttonView, isChecked) -> { + if (selectedTabsAdapter != null) { + selectedTabsAdapter.setEditMode(isChecked); + } + }); + } } /*////////////////////////////////////////////////////////////////////////// @@ -139,6 +152,23 @@ private void restoreDefaults() { .show(); } + private void showDeleteTabDialog(final int position, final String tabName) { + new AlertDialog.Builder(requireContext()) + .setTitle(tabName) + .setCancelable(true) + .setNegativeButton(R.string.cancel, null) + .setPositiveButton(R.string.delete, (dialog, which) -> { + tabList.remove(position); + selectedTabsAdapter.notifyItemRemoved(position); + + if (tabList.isEmpty()) { + tabList.add(Tab.Type.BLANK.getTab()); + selectedTabsAdapter.notifyItemInserted(0); + } + }) + .show(); + } + private void initButton(final View rootView) { final FloatingActionButton fab = rootView.findViewById(R.id.addTabsButton); fab.setOnClickListener(v -> { @@ -333,6 +363,12 @@ private class SelectedTabsAdapter extends RecyclerView.Adapter { private final LayoutInflater inflater; private final ItemTouchHelper itemTouchHelper; + boolean editModeEnabled = false; + + void setEditMode(final boolean enabled) { + editModeEnabled = enabled; + notifyDataSetChanged(); + } SelectedTabsAdapter(final Context context, final ItemTouchHelper itemTouchHelper) { this.itemTouchHelper = itemTouchHelper; @@ -368,6 +404,10 @@ class TabViewHolder extends RecyclerView.ViewHolder { private final AppCompatImageView tabIconView; private final TextView tabNameView; private final ImageView handle; + private final View itemActions; + private final ImageButton btnMoveUp; + private final ImageButton btnMoveDown; + private final ImageButton btnDelete; TabViewHolder(final View itemView) { super(itemView); @@ -375,6 +415,10 @@ class TabViewHolder extends RecyclerView.ViewHolder { tabNameView = itemView.findViewById(R.id.tabName); tabIconView = itemView.findViewById(R.id.tabIcon); handle = itemView.findViewById(R.id.handle); + itemActions = itemView.findViewById(R.id.itemActions); + btnMoveUp = itemView.findViewById(R.id.btnMoveUp); + btnMoveDown = itemView.findViewById(R.id.btnMoveDown); + btnDelete = itemView.findViewById(R.id.btnDelete); } @SuppressLint("ClickableViewAccessibility") @@ -390,6 +434,33 @@ void bind(final int position, final TabViewHolder holder) { tabNameView.setText(getTabName(type, tab)); tabIconView.setImageResource(tab.getTabIconRes(requireContext())); + + handle.setVisibility(editModeEnabled ? View.GONE : View.VISIBLE); + itemActions.setVisibility(editModeEnabled ? View.VISIBLE : View.GONE); + if (editModeEnabled) { + btnMoveUp.setEnabled(position > 0); + btnMoveDown.setEnabled(position < getItemCount() - 1); + btnMoveUp.setOnClickListener(v -> { + final int pos = getBindingAdapterPosition(); + if (pos > 0) { + Collections.swap(tabList, pos, pos - 1); + notifyDataSetChanged(); + } + }); + btnMoveDown.setOnClickListener(v -> { + final int pos = getBindingAdapterPosition(); + if (pos < getItemCount() - 1) { + Collections.swap(tabList, pos, pos + 1); + notifyDataSetChanged(); + } + }); + btnDelete.setOnClickListener(v -> { + final int pos = getBindingAdapterPosition(); + if (pos != RecyclerView.NO_POSITION) { + showDeleteTabDialog(pos, tabNameView.getText().toString()); + } + }); + } } private String getTabName(@NonNull final Tab.Type type, @NonNull final Tab tab) { diff --git a/app/src/main/res/layout/item_instance.xml b/app/src/main/res/layout/item_instance.xml index 12ecb2ea712..efb68250d9d 100644 --- a/app/src/main/res/layout/item_instance.xml +++ b/app/src/main/res/layout/item_instance.xml @@ -63,19 +63,62 @@ android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_centerVertical="true" - android:layout_toLeftOf="@id/handle" /> + android:layout_toLeftOf="@id/rightContainer" /> - + android:orientation="horizontal" + tools:ignore="RtlHardcoded"> + + + + + + + + + + + + diff --git a/app/src/main/res/layout/list_choose_tabs.xml b/app/src/main/res/layout/list_choose_tabs.xml index ed7ad94c434..fea7f267785 100644 --- a/app/src/main/res/layout/list_choose_tabs.xml +++ b/app/src/main/res/layout/list_choose_tabs.xml @@ -37,7 +37,7 @@ android:layout_marginLeft="16dp" android:layout_marginTop="6dp" android:layout_marginBottom="6dp" - android:layout_toLeftOf="@+id/handle" + android:layout_toLeftOf="@+id/rightContainer" android:layout_toRightOf="@+id/tabIcon" android:ellipsize="end" android:maxLines="2" @@ -45,17 +45,60 @@ tools:ignore="RtlHardcoded" tools:text="Lorem ipsum dolor sit amet" /> - + android:orientation="horizontal" + tools:ignore="RtlHardcoded"> + + + + + + + + + + + + diff --git a/app/src/main/res/menu/menu_chooser_fragment.xml b/app/src/main/res/menu/menu_chooser_fragment.xml index 8052f60ef99..1be34d6e9cc 100644 --- a/app/src/main/res/menu/menu_chooser_fragment.xml +++ b/app/src/main/res/menu/menu_chooser_fragment.xml @@ -5,4 +5,9 @@ android:title="@string/restore_defaults" android:icon="@drawable/ic_settings_backup_restore" app:showAsAction="always" /> + diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 836e11984d4..62518640bec 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -149,6 +149,9 @@ Could not validate instance Only HTTPS URLs are supported Instance already exists + Move up + Move down + Arrange Player Behavior Video and audio @@ -404,7 +407,7 @@ Content of main page What tabs are shown on the main page - Swipe items to remove them + Swipe items to remove them, long press for menu Blank page Kiosk page Default Kiosk