1/*
2 * Copyright 2018 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package androidx.recyclerview.widget;
18
19import androidx.annotation.NonNull;
20import androidx.annotation.Nullable;
21
22import java.util.List;
23
24/**
25 * {@link RecyclerView.Adapter RecyclerView.Adapter} base class for presenting List data in a
26 * {@link RecyclerView}, including computing diffs between Lists on a background thread.
27 * <p>
28 * This class is a convenience wrapper around {@link AsyncListDiffer} that implements Adapter common
29 * default behavior for item access and counting.
30 * <p>
31 * While using a LiveData&lt;List> is an easy way to provide data to the adapter, it isn't required
32 * - you can use {@link #submitList(List)} when new lists are available.
33 * <p>
34 * A complete usage pattern with Room would look like this:
35 * <pre>
36 * {@literal @}Dao
37 * interface UserDao {
38 *     {@literal @}Query("SELECT * FROM user ORDER BY lastName ASC")
39 *     public abstract LiveData&lt;List&lt;User>> usersByLastName();
40 * }
41 *
42 * class MyViewModel extends ViewModel {
43 *     public final LiveData&lt;List&lt;User>> usersList;
44 *     public MyViewModel(UserDao userDao) {
45 *         usersList = userDao.usersByLastName();
46 *     }
47 * }
48 *
49 * class MyActivity extends AppCompatActivity {
50 *     {@literal @}Override
51 *     public void onCreate(Bundle savedState) {
52 *         super.onCreate(savedState);
53 *         MyViewModel viewModel = ViewModelProviders.of(this).get(MyViewModel.class);
54 *         RecyclerView recyclerView = findViewById(R.id.user_list);
55 *         UserAdapter&lt;User> adapter = new UserAdapter();
56 *         viewModel.usersList.observe(this, list -> adapter.submitList(list));
57 *         recyclerView.setAdapter(adapter);
58 *     }
59 * }
60 *
61 * class UserAdapter extends ListAdapter&lt;User, UserViewHolder> {
62 *     public UserAdapter() {
63 *         super(User.DIFF_CALLBACK);
64 *     }
65 *     {@literal @}Override
66 *     public void onBindViewHolder(UserViewHolder holder, int position) {
67 *         holder.bindTo(getItem(position));
68 *     }
69 *     public static final DiffUtil.ItemCallback&lt;User> DIFF_CALLBACK =
70 *             new DiffUtil.ItemCallback&lt;User>() {
71 *         {@literal @}Override
72 *         public boolean areItemsTheSame(
73 *                 {@literal @}NonNull User oldUser, {@literal @}NonNull User newUser) {
74 *             // User properties may have changed if reloaded from the DB, but ID is fixed
75 *             return oldUser.getId() == newUser.getId();
76 *         }
77 *         {@literal @}Override
78 *         public boolean areContentsTheSame(
79 *                 {@literal @}NonNull User oldUser, {@literal @}NonNull User newUser) {
80 *             // NOTE: if you use equals, your object must properly override Object#equals()
81 *             // Incorrectly returning false here will result in too many animations.
82 *             return oldUser.equals(newUser);
83 *         }
84 *     }
85 * }</pre>
86 *
87 * Advanced users that wish for more control over adapter behavior, or to provide a specific base
88 * class should refer to {@link AsyncListDiffer}, which provides custom mapping from diff events
89 * to adapter positions.
90 *
91 * @param <T> Type of the Lists this Adapter will receive.
92 * @param  A class that extends ViewHolder that will be used by the adapter.
93 */
94public abstract class ListAdapter<T, VH extends RecyclerView.ViewHolder>
95        extends RecyclerView.Adapter<VH> {
96    private final AsyncListDiffer<T> mHelper;
97
98    @SuppressWarnings("unused")
99    protected ListAdapter(@NonNull DiffUtil.ItemCallback<T> diffCallback) {
100        mHelper = new AsyncListDiffer<>(new AdapterListUpdateCallback(this),
101                new AsyncDifferConfig.Builder<>(diffCallback).build());
102    }
103
104    @SuppressWarnings("unused")
105    protected ListAdapter(@NonNull AsyncDifferConfig<T> config) {
106        mHelper = new AsyncListDiffer<>(new AdapterListUpdateCallback(this), config);
107    }
108
109    /**
110     * Submits a new list to be diffed, and displayed.
111     * <p>
112     * If a list is already being displayed, a diff will be computed on a background thread, which
113     * will dispatch Adapter.notifyItem events on the main thread.
114     *
115     * @param list The new list to be displayed.
116     */
117    @SuppressWarnings("WeakerAccess")
118    public void submitList(@Nullable List<T> list) {
119        mHelper.submitList(list);
120    }
121
122    @SuppressWarnings("unused")
123    protected T getItem(int position) {
124        return mHelper.getCurrentList().get(position);
125    }
126
127    @Override
128    public int getItemCount() {
129        return mHelper.getCurrentList().size();
130    }
131}
132