Merge pull request #8719 from Isira-Seneviratne/Use_ListAdapter

Use ListAdapter for search predictions.
This commit is contained in:
Taco 2022-08-19 15:50:41 -04:00 committed by GitHub
commit 2089f3e54c
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
6 changed files with 164 additions and 213 deletions

View file

@ -200,7 +200,7 @@ public class SearchFragment extends BaseListFragment<SearchInfo, ListExtractor.I
showLocalSuggestions = NewPipeSettings.showLocalSearchSuggestions(activity, prefs); showLocalSuggestions = NewPipeSettings.showLocalSearchSuggestions(activity, prefs);
showRemoteSuggestions = NewPipeSettings.showRemoteSearchSuggestions(activity, prefs); showRemoteSuggestions = NewPipeSettings.showRemoteSearchSuggestions(activity, prefs);
suggestionListAdapter = new SuggestionListAdapter(activity); suggestionListAdapter = new SuggestionListAdapter();
historyRecordManager = new HistoryRecordManager(context); historyRecordManager = new HistoryRecordManager(context);
} }
@ -530,7 +530,7 @@ public class SearchFragment extends BaseListFragment<SearchInfo, ListExtractor.I
searchBinding.correctSuggestion.setVisibility(View.GONE); searchBinding.correctSuggestion.setVisibility(View.GONE);
searchEditText.setText(""); searchEditText.setText("");
suggestionListAdapter.setItems(new ArrayList<>()); suggestionListAdapter.submitList(null);
showKeyboardSearch(); showKeyboardSearch();
}); });
@ -945,7 +945,7 @@ public class SearchFragment extends BaseListFragment<SearchInfo, ListExtractor.I
Log.d(TAG, "handleSuggestions() called with: suggestions = [" + suggestions + "]"); Log.d(TAG, "handleSuggestions() called with: suggestions = [" + suggestions + "]");
} }
searchBinding.suggestionsList.smoothScrollToPosition(0); searchBinding.suggestionsList.smoothScrollToPosition(0);
searchBinding.suggestionsList.post(() -> suggestionListAdapter.setItems(suggestions)); suggestionListAdapter.submitList(suggestions);
if (suggestionsPanelVisible && isErrorPanelVisible()) { if (suggestionsPanelVisible && isErrorPanelVisible()) {
hideLoading(); hideLoading();
@ -1066,14 +1066,14 @@ public class SearchFragment extends BaseListFragment<SearchInfo, ListExtractor.I
return 0; return 0;
} }
final SuggestionItem item = suggestionListAdapter.getItem(position); final SuggestionItem item = suggestionListAdapter.getCurrentList().get(position);
return item.fromHistory ? makeMovementFlags(0, return item.fromHistory ? makeMovementFlags(0,
ItemTouchHelper.LEFT | ItemTouchHelper.RIGHT) : 0; ItemTouchHelper.LEFT | ItemTouchHelper.RIGHT) : 0;
} }
public void onSuggestionItemSwiped(@NonNull final RecyclerView.ViewHolder viewHolder) { public void onSuggestionItemSwiped(@NonNull final RecyclerView.ViewHolder viewHolder) {
final int position = viewHolder.getBindingAdapterPosition(); final int position = viewHolder.getBindingAdapterPosition();
final String query = suggestionListAdapter.getItem(position).query; final String query = suggestionListAdapter.getCurrentList().get(position).query;
final Disposable onDelete = historyRecordManager.deleteSearchHistory(query) final Disposable onDelete = historyRecordManager.deleteSearchHistory(query)
.observeOn(AndroidSchedulers.mainThread()) .observeOn(AndroidSchedulers.mainThread())
.subscribe( .subscribe(

View file

@ -1,34 +1,22 @@
package org.schabi.newpipe.fragments.list.search; package org.schabi.newpipe.fragments.list.search;
import android.content.Context;
import android.view.LayoutInflater; import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup; import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.TextView;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import androidx.recyclerview.widget.DiffUtil;
import androidx.recyclerview.widget.ListAdapter;
import androidx.recyclerview.widget.RecyclerView; import androidx.recyclerview.widget.RecyclerView;
import org.schabi.newpipe.R; import org.schabi.newpipe.R;
import org.schabi.newpipe.databinding.ItemSearchSuggestionBinding;
import java.util.ArrayList;
import java.util.List;
public class SuggestionListAdapter public class SuggestionListAdapter
extends RecyclerView.Adapter<SuggestionListAdapter.SuggestionItemHolder> { extends ListAdapter<SuggestionItem, SuggestionListAdapter.SuggestionItemHolder> {
private final ArrayList<SuggestionItem> items = new ArrayList<>();
private final Context context;
private OnSuggestionItemSelected listener; private OnSuggestionItemSelected listener;
public SuggestionListAdapter(final Context context) { public SuggestionListAdapter() {
this.context = context; super(new SuggestionItemCallback());
}
public void setItems(final List<SuggestionItem> items) {
this.items.clear();
this.items.addAll(items);
notifyDataSetChanged();
} }
public void setListener(final OnSuggestionItemSelected listener) { public void setListener(final OnSuggestionItemSelected listener) {
@ -39,45 +27,32 @@ public class SuggestionListAdapter
@Override @Override
public SuggestionItemHolder onCreateViewHolder(@NonNull final ViewGroup parent, public SuggestionItemHolder onCreateViewHolder(@NonNull final ViewGroup parent,
final int viewType) { final int viewType) {
return new SuggestionItemHolder(LayoutInflater.from(context) return new SuggestionItemHolder(ItemSearchSuggestionBinding
.inflate(R.layout.item_search_suggestion, parent, false)); .inflate(LayoutInflater.from(parent.getContext()), parent, false));
} }
@Override @Override
public void onBindViewHolder(final SuggestionItemHolder holder, final int position) { public void onBindViewHolder(final SuggestionItemHolder holder, final int position) {
final SuggestionItem currentItem = getItem(position); final SuggestionItem currentItem = getItem(position);
holder.updateFrom(currentItem); holder.updateFrom(currentItem);
holder.queryView.setOnClickListener(v -> { holder.itemBinding.suggestionSearch.setOnClickListener(v -> {
if (listener != null) { if (listener != null) {
listener.onSuggestionItemSelected(currentItem); listener.onSuggestionItemSelected(currentItem);
} }
}); });
holder.queryView.setOnLongClickListener(v -> { holder.itemBinding.suggestionSearch.setOnLongClickListener(v -> {
if (listener != null) { if (listener != null) {
listener.onSuggestionItemLongClick(currentItem); listener.onSuggestionItemLongClick(currentItem);
} }
return true; return true;
}); });
holder.insertView.setOnClickListener(v -> { holder.itemBinding.suggestionInsert.setOnClickListener(v -> {
if (listener != null) { if (listener != null) {
listener.onSuggestionItemInserted(currentItem); listener.onSuggestionItemInserted(currentItem);
} }
}); });
} }
SuggestionItem getItem(final int position) {
return items.get(position);
}
@Override
public int getItemCount() {
return items.size();
}
public boolean isEmpty() {
return getItemCount() == 0;
}
public interface OnSuggestionItemSelected { public interface OnSuggestionItemSelected {
void onSuggestionItemSelected(SuggestionItem item); void onSuggestionItemSelected(SuggestionItem item);
@ -87,30 +62,31 @@ public class SuggestionListAdapter
} }
public static final class SuggestionItemHolder extends RecyclerView.ViewHolder { public static final class SuggestionItemHolder extends RecyclerView.ViewHolder {
private final TextView itemSuggestionQuery; private final ItemSearchSuggestionBinding itemBinding;
private final ImageView suggestionIcon;
private final View queryView;
private final View insertView;
// Cache some ids, as they can potentially be constantly updated/recycled private SuggestionItemHolder(final ItemSearchSuggestionBinding binding) {
private final int historyResId; super(binding.getRoot());
private final int searchResId; this.itemBinding = binding;
private SuggestionItemHolder(final View rootView) {
super(rootView);
suggestionIcon = rootView.findViewById(R.id.item_suggestion_icon);
itemSuggestionQuery = rootView.findViewById(R.id.item_suggestion_query);
queryView = rootView.findViewById(R.id.suggestion_search);
insertView = rootView.findViewById(R.id.suggestion_insert);
historyResId = R.drawable.ic_history;
searchResId = R.drawable.ic_search;
} }
private void updateFrom(final SuggestionItem item) { private void updateFrom(final SuggestionItem item) {
suggestionIcon.setImageResource(item.fromHistory ? historyResId : searchResId); itemBinding.itemSuggestionIcon.setImageResource(item.fromHistory ? R.drawable.ic_history
itemSuggestionQuery.setText(item.query); : R.drawable.ic_search);
itemBinding.itemSuggestionQuery.setText(item.query);
}
}
private static class SuggestionItemCallback extends DiffUtil.ItemCallback<SuggestionItem> {
@Override
public boolean areItemsTheSame(@NonNull final SuggestionItem oldItem,
@NonNull final SuggestionItem newItem) {
return oldItem.query.equals(newItem.query);
}
@Override
public boolean areContentsTheSame(@NonNull final SuggestionItem oldItem,
@NonNull final SuggestionItem newItem) {
return oldItem.equals(newItem);
} }
} }
} }

View file

@ -12,28 +12,27 @@ import android.view.MenuItem;
import android.view.MotionEvent; import android.view.MotionEvent;
import android.view.View; import android.view.View;
import android.view.ViewGroup; import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.ProgressBar;
import android.widget.RadioButton; import android.widget.RadioButton;
import android.widget.TextView;
import android.widget.Toast; import android.widget.Toast;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
import androidx.appcompat.app.AlertDialog; import androidx.appcompat.app.AlertDialog;
import androidx.appcompat.widget.AppCompatImageView;
import androidx.fragment.app.Fragment; import androidx.fragment.app.Fragment;
import androidx.preference.PreferenceManager; import androidx.preference.PreferenceManager;
import androidx.recyclerview.widget.DiffUtil;
import androidx.recyclerview.widget.ItemTouchHelper; import androidx.recyclerview.widget.ItemTouchHelper;
import androidx.recyclerview.widget.LinearLayoutManager; import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.ListAdapter;
import androidx.recyclerview.widget.RecyclerView; import androidx.recyclerview.widget.RecyclerView;
import com.google.android.material.floatingactionbutton.FloatingActionButton;
import com.grack.nanojson.JsonStringWriter; import com.grack.nanojson.JsonStringWriter;
import com.grack.nanojson.JsonWriter; import com.grack.nanojson.JsonWriter;
import org.schabi.newpipe.R; import org.schabi.newpipe.R;
import org.schabi.newpipe.databinding.DialogEditTextBinding; import org.schabi.newpipe.databinding.DialogEditTextBinding;
import org.schabi.newpipe.databinding.FragmentInstanceListBinding;
import org.schabi.newpipe.databinding.ItemInstanceBinding;
import org.schabi.newpipe.extractor.services.peertube.PeertubeInstance; import org.schabi.newpipe.extractor.services.peertube.PeertubeInstance;
import org.schabi.newpipe.util.Constants; import org.schabi.newpipe.util.Constants;
import org.schabi.newpipe.util.PeertubeHelper; import org.schabi.newpipe.util.PeertubeHelper;
@ -41,7 +40,6 @@ import org.schabi.newpipe.util.ThemeHelper;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collections; import java.util.Collections;
import java.util.List;
import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers; import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers;
import io.reactivex.rxjava3.core.Single; import io.reactivex.rxjava3.core.Single;
@ -50,12 +48,11 @@ import io.reactivex.rxjava3.disposables.Disposable;
import io.reactivex.rxjava3.schedulers.Schedulers; import io.reactivex.rxjava3.schedulers.Schedulers;
public class PeertubeInstanceListFragment extends Fragment { public class PeertubeInstanceListFragment extends Fragment {
private final List<PeertubeInstance> instanceList = new ArrayList<>();
private PeertubeInstance selectedInstance; private PeertubeInstance selectedInstance;
private String savedInstanceListKey; private String savedInstanceListKey;
private InstanceListAdapter instanceListAdapter; private InstanceListAdapter instanceListAdapter;
private ProgressBar progressBar; private FragmentInstanceListBinding binding;
private SharedPreferences sharedPreferences; private SharedPreferences sharedPreferences;
private CompositeDisposable disposables = new CompositeDisposable(); private CompositeDisposable disposables = new CompositeDisposable();
@ -71,7 +68,6 @@ public class PeertubeInstanceListFragment extends Fragment {
sharedPreferences = PreferenceManager.getDefaultSharedPreferences(requireContext()); sharedPreferences = PreferenceManager.getDefaultSharedPreferences(requireContext());
savedInstanceListKey = getString(R.string.peertube_instance_list_key); savedInstanceListKey = getString(R.string.peertube_instance_list_key);
selectedInstance = PeertubeHelper.getCurrentInstance(); selectedInstance = PeertubeHelper.getCurrentInstance();
updateInstanceList();
setHasOptionsMenu(true); setHasOptionsMenu(true);
} }
@ -79,7 +75,8 @@ public class PeertubeInstanceListFragment extends Fragment {
@Override @Override
public View onCreateView(@NonNull final LayoutInflater inflater, final ViewGroup container, public View onCreateView(@NonNull final LayoutInflater inflater, final ViewGroup container,
final Bundle savedInstanceState) { final Bundle savedInstanceState) {
return inflater.inflate(R.layout.fragment_instance_list, container, false); binding = FragmentInstanceListBinding.inflate(inflater, container, false);
return binding.getRoot();
} }
@Override @Override
@ -87,26 +84,17 @@ public class PeertubeInstanceListFragment extends Fragment {
@Nullable final Bundle savedInstanceState) { @Nullable final Bundle savedInstanceState) {
super.onViewCreated(rootView, savedInstanceState); super.onViewCreated(rootView, savedInstanceState);
initViews(rootView); binding.instanceHelpTV.setText(getString(R.string.peertube_instance_url_help,
}
private void initViews(@NonNull final View rootView) {
final TextView instanceHelpTV = rootView.findViewById(R.id.instanceHelpTV);
instanceHelpTV.setText(getString(R.string.peertube_instance_url_help,
getString(R.string.peertube_instance_list_url))); getString(R.string.peertube_instance_list_url)));
binding.addInstanceButton.setOnClickListener(v -> showAddItemDialog(requireContext()));
initButton(rootView); binding.instances.setLayoutManager(new LinearLayoutManager(requireContext()));
final RecyclerView listInstances = rootView.findViewById(R.id.instances);
listInstances.setLayoutManager(new LinearLayoutManager(requireContext()));
final ItemTouchHelper itemTouchHelper = new ItemTouchHelper(getItemTouchCallback()); final ItemTouchHelper itemTouchHelper = new ItemTouchHelper(getItemTouchCallback());
itemTouchHelper.attachToRecyclerView(listInstances); itemTouchHelper.attachToRecyclerView(binding.instances);
instanceListAdapter = new InstanceListAdapter(requireContext(), itemTouchHelper); instanceListAdapter = new InstanceListAdapter(requireContext(), itemTouchHelper);
listInstances.setAdapter(instanceListAdapter); binding.instances.setAdapter(instanceListAdapter);
instanceListAdapter.submitList(PeertubeHelper.getInstanceList(requireContext()));
progressBar = rootView.findViewById(R.id.loading_progress_bar);
} }
@Override @Override
@ -131,6 +119,12 @@ public class PeertubeInstanceListFragment extends Fragment {
disposables = null; disposables = null;
} }
@Override
public void onDestroyView() {
binding = null;
super.onDestroyView();
}
/*////////////////////////////////////////////////////////////////////////// /*//////////////////////////////////////////////////////////////////////////
// Menu // Menu
//////////////////////////////////////////////////////////////////////////*/ //////////////////////////////////////////////////////////////////////////*/
@ -156,11 +150,6 @@ public class PeertubeInstanceListFragment extends Fragment {
// Utils // Utils
//////////////////////////////////////////////////////////////////////////*/ //////////////////////////////////////////////////////////////////////////*/
private void updateInstanceList() {
instanceList.clear();
instanceList.addAll(PeertubeHelper.getInstanceList(requireContext()));
}
private void selectInstance(final PeertubeInstance instance) { private void selectInstance(final PeertubeInstance instance) {
selectedInstance = PeertubeHelper.selectInstance(instance, requireContext()); selectedInstance = PeertubeHelper.selectInstance(instance, requireContext());
sharedPreferences.edit().putBoolean(Constants.KEY_MAIN_PAGE_CHANGE, true).apply(); sharedPreferences.edit().putBoolean(Constants.KEY_MAIN_PAGE_CHANGE, true).apply();
@ -168,7 +157,7 @@ public class PeertubeInstanceListFragment extends Fragment {
private void saveChanges() { private void saveChanges() {
final JsonStringWriter jsonWriter = JsonWriter.string().object().array("instances"); final JsonStringWriter jsonWriter = JsonWriter.string().object().array("instances");
for (final PeertubeInstance instance : instanceList) { for (final PeertubeInstance instance : instanceListAdapter.getCurrentList()) {
jsonWriter.object(); jsonWriter.object();
jsonWriter.value("name", instance.getName()); jsonWriter.value("name", instance.getName());
jsonWriter.value("url", instance.getUrl()); jsonWriter.value("url", instance.getUrl());
@ -179,28 +168,21 @@ public class PeertubeInstanceListFragment extends Fragment {
} }
private void restoreDefaults() { private void restoreDefaults() {
new AlertDialog.Builder(requireContext()) final Context context = requireContext();
new AlertDialog.Builder(context)
.setTitle(R.string.restore_defaults) .setTitle(R.string.restore_defaults)
.setMessage(R.string.restore_defaults_confirmation) .setMessage(R.string.restore_defaults_confirmation)
.setNegativeButton(R.string.cancel, null) .setNegativeButton(R.string.cancel, null)
.setPositiveButton(R.string.ok, (dialog, which) -> { .setPositiveButton(R.string.ok, (dialog, which) -> {
sharedPreferences.edit().remove(savedInstanceListKey).apply(); sharedPreferences.edit().remove(savedInstanceListKey).apply();
selectInstance(PeertubeInstance.DEFAULT_INSTANCE); selectInstance(PeertubeInstance.DEFAULT_INSTANCE);
updateInstanceList(); instanceListAdapter.submitList(PeertubeHelper.getInstanceList(context));
instanceListAdapter.notifyDataSetChanged();
}) })
.show(); .show();
} }
private void initButton(final View rootView) {
final FloatingActionButton fab = rootView.findViewById(R.id.addInstanceButton);
fab.setOnClickListener(v ->
showAddItemDialog(requireContext()));
}
private void showAddItemDialog(final Context c) { private void showAddItemDialog(final Context c) {
final DialogEditTextBinding dialogBinding = final var dialogBinding = DialogEditTextBinding.inflate(getLayoutInflater());
DialogEditTextBinding.inflate(getLayoutInflater());
dialogBinding.dialogEditText.setInputType( dialogBinding.dialogEditText.setInputType(
InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_VARIATION_URI); InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_VARIATION_URI);
dialogBinding.dialogEditText.setHint(R.string.peertube_instance_add_help); dialogBinding.dialogEditText.setHint(R.string.peertube_instance_add_help);
@ -222,17 +204,17 @@ public class PeertubeInstanceListFragment extends Fragment {
if (cleanUrl == null) { if (cleanUrl == null) {
return; return;
} }
progressBar.setVisibility(View.VISIBLE); binding.loadingProgressBar.setVisibility(View.VISIBLE);
final Disposable disposable = Single.fromCallable(() -> { final Disposable disposable = Single.fromCallable(() -> {
final PeertubeInstance instance = new PeertubeInstance(cleanUrl); final PeertubeInstance instance = new PeertubeInstance(cleanUrl);
instance.fetchInstanceMetaData(); instance.fetchInstanceMetaData();
return instance; return instance;
}).subscribeOn(Schedulers.io()).observeOn(AndroidSchedulers.mainThread()) }).subscribeOn(Schedulers.io()).observeOn(AndroidSchedulers.mainThread())
.subscribe((instance) -> { .subscribe((instance) -> {
progressBar.setVisibility(View.GONE); binding.loadingProgressBar.setVisibility(View.GONE);
add(instance); add(instance);
}, e -> { }, e -> {
progressBar.setVisibility(View.GONE); binding.loadingProgressBar.setVisibility(View.GONE);
Toast.makeText(getActivity(), R.string.peertube_instance_add_fail, Toast.makeText(getActivity(), R.string.peertube_instance_add_fail,
Toast.LENGTH_SHORT).show(); Toast.LENGTH_SHORT).show();
}); });
@ -255,7 +237,7 @@ public class PeertubeInstanceListFragment extends Fragment {
return null; return null;
} }
// only allow if not already exists // only allow if not already exists
for (final PeertubeInstance instance : instanceList) { for (final PeertubeInstance instance : instanceListAdapter.getCurrentList()) {
if (instance.getUrl().equals(cleanUrl)) { if (instance.getUrl().equals(cleanUrl)) {
Toast.makeText(getActivity(), R.string.peertube_instance_add_exists, Toast.makeText(getActivity(), R.string.peertube_instance_add_exists,
Toast.LENGTH_SHORT).show(); Toast.LENGTH_SHORT).show();
@ -266,8 +248,9 @@ public class PeertubeInstanceListFragment extends Fragment {
} }
private void add(final PeertubeInstance instance) { private void add(final PeertubeInstance instance) {
instanceList.add(instance); final var list = new ArrayList<>(instanceListAdapter.getCurrentList());
instanceListAdapter.notifyDataSetChanged(); list.add(instance);
instanceListAdapter.submitList(list);
} }
private ItemTouchHelper.SimpleCallback getItemTouchCallback() { private ItemTouchHelper.SimpleCallback getItemTouchCallback() {
@ -281,8 +264,7 @@ public class PeertubeInstanceListFragment extends Fragment {
final long msSinceStartScroll) { final long msSinceStartScroll) {
final int standardSpeed = super.interpolateOutOfBoundsScroll(recyclerView, viewSize, final int standardSpeed = super.interpolateOutOfBoundsScroll(recyclerView, viewSize,
viewSizeOutOfBounds, totalSize, msSinceStartScroll); viewSizeOutOfBounds, totalSize, msSinceStartScroll);
final int minimumAbsVelocity = Math.max(12, final int minimumAbsVelocity = Math.max(12, Math.abs(standardSpeed));
Math.abs(standardSpeed));
return minimumAbsVelocity * (int) Math.signum(viewSizeOutOfBounds); return minimumAbsVelocity * (int) Math.signum(viewSizeOutOfBounds);
} }
@ -316,17 +298,19 @@ public class PeertubeInstanceListFragment extends Fragment {
final int swipeDir) { final int swipeDir) {
final int position = viewHolder.getBindingAdapterPosition(); final int position = viewHolder.getBindingAdapterPosition();
// do not allow swiping the selected instance // do not allow swiping the selected instance
if (instanceList.get(position).getUrl().equals(selectedInstance.getUrl())) { if (instanceListAdapter.getCurrentList().get(position).getUrl()
.equals(selectedInstance.getUrl())) {
instanceListAdapter.notifyItemChanged(position); instanceListAdapter.notifyItemChanged(position);
return; return;
} }
instanceList.remove(position); final var list = new ArrayList<>(instanceListAdapter.getCurrentList());
instanceListAdapter.notifyItemRemoved(position); list.remove(position);
if (instanceList.isEmpty()) { if (list.isEmpty()) {
instanceList.add(selectedInstance); list.add(selectedInstance);
instanceListAdapter.notifyItemInserted(0);
} }
instanceListAdapter.submitList(list);
} }
}; };
} }
@ -336,96 +320,94 @@ public class PeertubeInstanceListFragment extends Fragment {
//////////////////////////////////////////////////////////////////////////*/ //////////////////////////////////////////////////////////////////////////*/
private class InstanceListAdapter private class InstanceListAdapter
extends RecyclerView.Adapter<InstanceListAdapter.TabViewHolder> { extends ListAdapter<PeertubeInstance, InstanceListAdapter.TabViewHolder> {
private final LayoutInflater inflater; private final LayoutInflater inflater;
private final ItemTouchHelper itemTouchHelper; private final ItemTouchHelper itemTouchHelper;
private RadioButton lastChecked; private RadioButton lastChecked;
InstanceListAdapter(final Context context, final ItemTouchHelper itemTouchHelper) { InstanceListAdapter(final Context context, final ItemTouchHelper itemTouchHelper) {
super(new PeertubeInstanceCallback());
this.itemTouchHelper = itemTouchHelper; this.itemTouchHelper = itemTouchHelper;
this.inflater = LayoutInflater.from(context); this.inflater = LayoutInflater.from(context);
} }
public void swapItems(final int fromPosition, final int toPosition) { public void swapItems(final int fromPosition, final int toPosition) {
Collections.swap(instanceList, fromPosition, toPosition); final var list = new ArrayList<>(getCurrentList());
notifyItemMoved(fromPosition, toPosition); Collections.swap(list, fromPosition, toPosition);
submitList(list);
} }
@NonNull @NonNull
@Override @Override
public InstanceListAdapter.TabViewHolder onCreateViewHolder(@NonNull final ViewGroup parent, public InstanceListAdapter.TabViewHolder onCreateViewHolder(@NonNull final ViewGroup parent,
final int viewType) { final int viewType) {
final View view = inflater.inflate(R.layout.item_instance, parent, false); return new InstanceListAdapter.TabViewHolder(ItemInstanceBinding.inflate(inflater,
return new InstanceListAdapter.TabViewHolder(view); parent, false));
} }
@Override @Override
public void onBindViewHolder(@NonNull final InstanceListAdapter.TabViewHolder holder, public void onBindViewHolder(@NonNull final InstanceListAdapter.TabViewHolder holder,
final int position) { final int position) {
holder.bind(position, holder); holder.bind(position);
}
@Override
public int getItemCount() {
return instanceList.size();
} }
class TabViewHolder extends RecyclerView.ViewHolder { class TabViewHolder extends RecyclerView.ViewHolder {
private final AppCompatImageView instanceIconView; private final ItemInstanceBinding itemBinding;
private final TextView instanceNameView;
private final TextView instanceUrlView;
private final RadioButton instanceRB;
private final ImageView handle;
TabViewHolder(final View itemView) { TabViewHolder(final ItemInstanceBinding binding) {
super(itemView); super(binding.getRoot());
this.itemBinding = binding;
instanceIconView = itemView.findViewById(R.id.instanceIcon);
instanceNameView = itemView.findViewById(R.id.instanceName);
instanceUrlView = itemView.findViewById(R.id.instanceUrl);
instanceRB = itemView.findViewById(R.id.selectInstanceRB);
handle = itemView.findViewById(R.id.handle);
} }
@SuppressLint("ClickableViewAccessibility") @SuppressLint("ClickableViewAccessibility")
void bind(final int position, final TabViewHolder holder) { void bind(final int position) {
handle.setOnTouchListener(getOnTouchListener(holder)); itemBinding.handle.setOnTouchListener((view, motionEvent) -> {
final PeertubeInstance instance = instanceList.get(position);
instanceNameView.setText(instance.getName());
instanceUrlView.setText(instance.getUrl());
instanceRB.setOnCheckedChangeListener(null);
if (selectedInstance.getUrl().equals(instance.getUrl())) {
if (lastChecked != null && lastChecked != instanceRB) {
lastChecked.setChecked(false);
}
instanceRB.setChecked(true);
lastChecked = instanceRB;
}
instanceRB.setOnCheckedChangeListener((buttonView, isChecked) -> {
if (isChecked) {
selectInstance(instance);
if (lastChecked != null && lastChecked != instanceRB) {
lastChecked.setChecked(false);
}
lastChecked = instanceRB;
}
});
instanceIconView.setImageResource(R.drawable.ic_placeholder_peertube);
}
@SuppressLint("ClickableViewAccessibility")
private View.OnTouchListener getOnTouchListener(final RecyclerView.ViewHolder item) {
return (view, motionEvent) -> {
if (motionEvent.getActionMasked() == MotionEvent.ACTION_DOWN) { if (motionEvent.getActionMasked() == MotionEvent.ACTION_DOWN) {
if (itemTouchHelper != null && getItemCount() > 1) { if (itemTouchHelper != null && getItemCount() > 1) {
itemTouchHelper.startDrag(item); itemTouchHelper.startDrag(this);
return true; return true;
} }
} }
return false; return false;
}; });
final PeertubeInstance instance = getItem(position);
itemBinding.instanceName.setText(instance.getName());
itemBinding.instanceUrl.setText(instance.getUrl());
itemBinding.selectInstanceRB.setOnCheckedChangeListener(null);
if (selectedInstance.getUrl().equals(instance.getUrl())) {
if (lastChecked != null && lastChecked != itemBinding.selectInstanceRB) {
lastChecked.setChecked(false);
}
itemBinding.selectInstanceRB.setChecked(true);
lastChecked = itemBinding.selectInstanceRB;
}
itemBinding.selectInstanceRB.setOnCheckedChangeListener((buttonView, isChecked) -> {
if (isChecked) {
selectInstance(instance);
if (lastChecked != null && lastChecked != itemBinding.selectInstanceRB) {
lastChecked.setChecked(false);
}
lastChecked = itemBinding.selectInstanceRB;
}
});
itemBinding.instanceIcon.setImageResource(R.drawable.ic_placeholder_peertube);
} }
} }
} }
private static class PeertubeInstanceCallback extends DiffUtil.ItemCallback<PeertubeInstance> {
@Override
public boolean areItemsTheSame(@NonNull final PeertubeInstance oldItem,
@NonNull final PeertubeInstance newItem) {
return oldItem.getUrl().equals(newItem.getUrl());
}
@Override
public boolean areContentsTheSame(@NonNull final PeertubeInstance oldItem,
@NonNull final PeertubeInstance newItem) {
return oldItem.getName().equals(newItem.getName())
&& oldItem.getUrl().equals(newItem.getUrl());
}
}
} }

View file

@ -1,54 +1,48 @@
package org.schabi.newpipe.settings.preferencesearch; package org.schabi.newpipe.settings.preferencesearch;
import android.text.TextUtils;
import android.view.LayoutInflater; import android.view.LayoutInflater;
import android.view.View; import android.view.View;
import android.view.ViewGroup; import android.view.ViewGroup;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import androidx.recyclerview.widget.DiffUtil;
import androidx.recyclerview.widget.ListAdapter;
import androidx.recyclerview.widget.RecyclerView; import androidx.recyclerview.widget.RecyclerView;
import org.schabi.newpipe.databinding.SettingsPreferencesearchListItemResultBinding; import org.schabi.newpipe.databinding.SettingsPreferencesearchListItemResultBinding;
import java.util.ArrayList;
import java.util.List;
import java.util.function.Consumer; import java.util.function.Consumer;
class PreferenceSearchAdapter class PreferenceSearchAdapter
extends RecyclerView.Adapter<PreferenceSearchAdapter.PreferenceViewHolder> { extends ListAdapter<PreferenceSearchItem, PreferenceSearchAdapter.PreferenceViewHolder> {
private List<PreferenceSearchItem> dataset = new ArrayList<>();
private Consumer<PreferenceSearchItem> onItemClickListener; private Consumer<PreferenceSearchItem> onItemClickListener;
PreferenceSearchAdapter() {
super(new PreferenceCallback());
}
@NonNull @NonNull
@Override @Override
public PreferenceViewHolder onCreateViewHolder( public PreferenceViewHolder onCreateViewHolder(@NonNull final ViewGroup parent,
@NonNull final ViewGroup parent, final int viewType) {
final int viewType return new PreferenceViewHolder(SettingsPreferencesearchListItemResultBinding.inflate(
) { LayoutInflater.from(parent.getContext()), parent, false));
return new PreferenceViewHolder(
SettingsPreferencesearchListItemResultBinding.inflate(
LayoutInflater.from(parent.getContext()),
parent,
false));
} }
@Override @Override
public void onBindViewHolder( public void onBindViewHolder(@NonNull final PreferenceViewHolder holder, final int position) {
@NonNull final PreferenceViewHolder holder, final PreferenceSearchItem item = getItem(position);
final int position
) {
final PreferenceSearchItem item = dataset.get(position);
holder.binding.title.setText(item.getTitle()); holder.binding.title.setText(item.getTitle());
if (TextUtils.isEmpty(item.getSummary())) { if (item.getSummary().isEmpty()) {
holder.binding.summary.setVisibility(View.GONE); holder.binding.summary.setVisibility(View.GONE);
} else { } else {
holder.binding.summary.setVisibility(View.VISIBLE); holder.binding.summary.setVisibility(View.VISIBLE);
holder.binding.summary.setText(item.getSummary()); holder.binding.summary.setText(item.getSummary());
} }
if (TextUtils.isEmpty(item.getBreadcrumbs())) { if (item.getBreadcrumbs().isEmpty()) {
holder.binding.breadcrumbs.setVisibility(View.GONE); holder.binding.breadcrumbs.setVisibility(View.GONE);
} else { } else {
holder.binding.breadcrumbs.setVisibility(View.VISIBLE); holder.binding.breadcrumbs.setVisibility(View.VISIBLE);
@ -62,16 +56,6 @@ class PreferenceSearchAdapter
}); });
} }
void setContent(final List<PreferenceSearchItem> items) {
dataset = new ArrayList<>(items);
this.notifyDataSetChanged();
}
@Override
public int getItemCount() {
return dataset.size();
}
void setOnItemClickListener(final Consumer<PreferenceSearchItem> onItemClickListener) { void setOnItemClickListener(final Consumer<PreferenceSearchItem> onItemClickListener) {
this.onItemClickListener = onItemClickListener; this.onItemClickListener = onItemClickListener;
} }
@ -84,4 +68,19 @@ class PreferenceSearchAdapter
this.binding = binding; this.binding = binding;
} }
} }
private static class PreferenceCallback extends DiffUtil.ItemCallback<PreferenceSearchItem> {
@Override
public boolean areItemsTheSame(@NonNull final PreferenceSearchItem oldItem,
@NonNull final PreferenceSearchItem newItem) {
return oldItem.getKey().equals(newItem.getKey());
}
@Override
public boolean areContentsTheSame(@NonNull final PreferenceSearchItem oldItem,
@NonNull final PreferenceSearchItem newItem) {
return oldItem.getAllRelevantSearchFields().equals(newItem
.getAllRelevantSearchFields());
}
}
} }

View file

@ -1,7 +1,6 @@
package org.schabi.newpipe.settings.preferencesearch; package org.schabi.newpipe.settings.preferencesearch;
import android.os.Bundle; import android.os.Bundle;
import android.text.TextUtils;
import android.view.LayoutInflater; import android.view.LayoutInflater;
import android.view.View; import android.view.View;
import android.view.ViewGroup; import android.view.ViewGroup;
@ -13,7 +12,6 @@ import androidx.recyclerview.widget.LinearLayoutManager;
import org.schabi.newpipe.databinding.SettingsPreferencesearchFragmentBinding; import org.schabi.newpipe.databinding.SettingsPreferencesearchFragmentBinding;
import java.util.ArrayList;
import java.util.List; import java.util.List;
/** /**
@ -54,13 +52,8 @@ public class PreferenceSearchFragment extends Fragment {
return; return;
} }
final List<PreferenceSearchItem> results = final List<PreferenceSearchItem> results = searcher.searchFor(keyword);
!TextUtils.isEmpty(keyword) adapter.submitList(results);
? searcher.searchFor(keyword)
: new ArrayList<>();
adapter.setContent(new ArrayList<>(results));
setEmptyViewShown(results.isEmpty()); setEmptyViewShown(results.isEmpty());
} }

View file

@ -3,6 +3,7 @@ package org.schabi.newpipe.settings.preferencesearch;
import android.text.TextUtils; import android.text.TextUtils;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collections;
import java.util.List; import java.util.List;
import java.util.stream.Collectors; import java.util.stream.Collectors;
@ -21,7 +22,7 @@ public class PreferenceSearcher {
List<PreferenceSearchItem> searchFor(final String keyword) { List<PreferenceSearchItem> searchFor(final String keyword) {
if (TextUtils.isEmpty(keyword)) { if (TextUtils.isEmpty(keyword)) {
return new ArrayList<>(); return Collections.emptyList();
} }
return configuration.getSearcher() return configuration.getSearcher()