content: /*
   * Copyright 2018 The Android Open Source Project
   *
   * Licensed under the Apache License, Version 2.0 (the "License");
   * you may not use this file except in compliance with the License.
   * You may obtain a copy of the License at
   *
   *      http://www.apache.org/licenses/LICENSE-2.0
   *
   * Unless required by applicable law or agreed to in writing, software
   * distributed under the License is distributed on an "AS IS" BASIS,
   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   * See the License for the specific language governing permissions and
   * limitations under the License.
   */
  package com.github.anrimian.musicplayer.ui.utils.views.recycler_view.diff_utils.adapter;
  
  import androidx.annotation.NonNull;
  import androidx.annotation.Nullable;
  import androidx.recyclerview.widget.AdapterListUpdateCallback;
  import androidx.recyclerview.widget.AsyncDifferConfig;
  import androidx.recyclerview.widget.AsyncListDiffer;
  import androidx.recyclerview.widget.DiffUtil;
  import androidx.recyclerview.widget.RecyclerView;
  
  import java.util.List;
  
  /**
   * {@link RecyclerView.Adapter RecyclerView.Adapter} base class for presenting List data in a
   * {@link RecyclerView}, including computing diffs between Lists on a background thread.
   * <p>
   * This class is a convenience wrapper around {@link AsyncListDiffer} that implements Adapter common
   * default behavior for item access and counting.
   * <p>
   * While using a LiveData&lt;List> is an easy way to provide data to the adapter, it isn't required
   * - you can use {@link #submitList(List)} when new lists are available.
   * <p>
   * A complete usage pattern with Room would look like this:
   * <pre>
   * {@literal @}Dao
   * interface UserDao {
   *     {@literal @}Query("SELECT * FROM user ORDER BY lastName ASC")
   *     public abstract LiveData&lt;List&lt;User>> usersByLastName();
   * }
   *
   * class MyViewModel extends ViewModel {
   *     public final LiveData&lt;List&lt;User>> usersList;
   *     public MyViewModel(UserDao userDao) {
   *         usersList = userDao.usersByLastName();
   *     }
   * }
   *
   * class MyActivity extends AppCompatActivity {
   *     {@literal @}Override
   *     public void onCreate(Bundle savedState) {
   *         super.onCreate(savedState);
   *         MyViewModel viewModel = new ViewModelProvider(this).get(MyViewModel.class);
   *         RecyclerView recyclerView = findViewById(R.id.user_list);
   *         UserAdapter&lt;User> adapter = new UserAdapter();
   *         viewModel.usersList.observe(this, list -> adapter.submitList(list));
   *         recyclerView.setAdapter(adapter);
   *     }
   * }
   *
   * class UserAdapter extends ListAdapter&lt;User, UserViewHolder> {
   *     public UserAdapter() {
   *         super(User.DIFF_CALLBACK);
   *     }
   *     {@literal @}Override
   *     public void onBindViewHolder(UserViewHolder holder, int position) {
   *         holder.bindTo(getItem(position));
   *     }
   *     public static final DiffUtil.ItemCallback&lt;User> DIFF_CALLBACK =
   *             new DiffUtil.ItemCallback&lt;User>() {
   *         {@literal @}Override
   *         public boolean areItemsTheSame(
   *                 {@literal @}NonNull User oldUser, {@literal @}NonNull User newUser) {
   *             // User properties may have changed if reloaded from the DB, but ID is fixed
   *             return oldUser.getId() == newUser.getId();
   *         }
   *         {@literal @}Override
   *         public boolean areContentsTheSame(
   *                 {@literal @}NonNull User oldUser, {@literal @}NonNull User newUser) {
   *             // NOTE: if you use equals, your object must properly override Object#equals()
   *             // Incorrectly returning false here will result in too many animations.
   *             return oldUser.equals(newUser);
   *         }
   *     }
   * }</pre>
   *
   * Advanced users that wish for more control over adapter behavior, or to provide a specific base
   * class should refer to {@link AsyncListDiffer}, which provides custom mapping from diff events
   * to adapter positions.
   *
   * @param <T> Type of the Lists this Adapter will receive.
   * @param <VH> A class that extends ViewHolder that will be used by the adapter.
   */
  public abstract class DiffListAdapter<T, VH extends RecyclerView.ViewHolder>
          extends RecyclerView.Adapter<VH> {
      final CustomAsyncListDiffer<T> mDiffer;
      private final AsyncListDiffer.ListListener<T> mListener = DiffListAdapter.this::onCurrentListChanged;
  
      @SuppressWarnings("unused")
      protected DiffListAdapter(RecyclerView recyclerView, @NonNull DiffUtil.ItemCallback<T> diffCallback) {
          this(recyclerView, diffCallback, true);
      }
  
      protected DiffListAdapter(RecyclerView recyclerView,
                                @NonNull DiffUtil.ItemCallback<T> diffCallback,
                                boolean detectMoves) {
          mDiffer = new CustomAsyncListDiffer<>(recyclerView,
                  new AdapterListUpdateCallback(this),
                  new AsyncDifferConfig.Builder<>(diffCallback).build(),
                  detectMoves);
          mDiffer.addListListener(mListener);
      }
  
      @SuppressWarnings("unused")
      protected DiffListAdapter(RecyclerView recyclerView, @NonNull AsyncDifferConfig<T> config) {
          mDiffer = new CustomAsyncListDiffer<>(recyclerView, new AdapterListUpdateCallback(this), config);
          mDiffer.addListListener(mListener);
      }
  
      /**
       * Submits a new list to be diffed, and displayed.
       * <p>
       * If a list is already being displayed, a diff will be computed on a background thread, which
       * will dispatch Adapter.notifyItem events on the main thread.
       *
       * @param list The new list to be displayed.
       */
      public void submitList(@Nullable List<T> list) {
          mDiffer.submitList(list);
      }
  
      /**
       * Set the new list to be displayed.
       * <p>
       * If a List is already being displayed, a diff will be computed on a background thread, which
       * will dispatch Adapter.notifyItem events on the main thread.
       * <p>
       * The commit callback can be used to know when the List is committed, but note that it
       * may not be executed. If List B is submitted immediately after List A, and is
       * committed directly, the callback associated with List A will not be run.
       *
       * @param list The new list to be displayed.
       * @param commitCallback Optional runnable that is executed when the List is committed, if
       *                       it is committed.
       */
      public void submitList(@Nullable List<T> list, @Nullable final Runnable commitCallback) {
          mDiffer.submitList(list, commitCallback);
      }
  
      protected T getItem(int position) {
          return mDiffer.getCurrentList().get(position);
      }
  
      @Override
      public int getItemCount() {
          return mDiffer.getCurrentList().size();
      }
  
      /**
       * Get the current List - any diffing to present this list has already been computed and
       * dispatched via the ListUpdateCallback.
       * <p>
       * If a <code>null</code> List, or no List has been submitted, an empty list will be returned.
       * <p>
       * The returned list may not be mutated - mutations to content must be done through
       * {@link #submitList(List)}.
       *
       * @return The list currently being displayed.
       *
       * @see #onCurrentListChanged(List, List)
       */
      @NonNull
      public List<T> getCurrentList() {
          return mDiffer.getCurrentList();
      }
  
      /**
       * Called when the current List is updated.
       * <p>
       * If a <code>null</code> List is passed to {@link #submitList(List)}, or no List has been
       * submitted, the current List is represented as an empty List.
       *
       * @param previousList List that was displayed previously.
       * @param currentList new List being displayed, will be empty if {@code null} was passed to
       *          {@link #submitList(List)}.
       *
       * @see #getCurrentList()
       */
      public void onCurrentListChanged(@NonNull List<T> previousList, @NonNull List<T> currentList) {
      }
  }
  
  