1 /*
2  * Copyright (C) 2016 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 package com.example.android.supportv7.util;
17 
18 import android.os.AsyncTask;
19 import android.os.Bundle;
20 import android.view.View;
21 import android.view.ViewGroup;
22 import android.widget.Button;
23 import android.widget.LinearLayout;
24 import android.widget.Toast;
25 
26 import androidx.annotation.Nullable;
27 import androidx.appcompat.app.AppCompatActivity;
28 import androidx.core.util.Pair;
29 import androidx.recyclerview.widget.DiffUtil;
30 import androidx.recyclerview.widget.LinearLayoutManager;
31 import androidx.recyclerview.widget.RecyclerView;
32 
33 import com.example.android.supportv7.Cheeses;
34 import com.example.android.supportv7.widget.adapter.SimpleStringAdapter;
35 
36 import java.util.ArrayList;
37 import java.util.Collections;
38 import java.util.List;
39 import java.util.Random;
40 import java.util.concurrent.atomic.AtomicBoolean;
41 
42 /**
43  * A sample activity that demonstrates usage if {@link android.support.v7.util.DiffUtil} with
44  * a RecyclerView.
45  */
46 public class DiffUtilActivity extends AppCompatActivity {
47     private Random mRandom = new Random(System.nanoTime());
48 
49     @Override
onCreate(@ullable Bundle savedInstanceState)50     protected void onCreate(@Nullable Bundle savedInstanceState) {
51         super.onCreate(savedInstanceState);
52         LinearLayout ll = new LinearLayout(this);
53         RecyclerView rv = new RecyclerView(this);
54         Button shuffle = new Button(this);
55         shuffle.setText("Shuffle");
56         ll.addView(shuffle);
57         ll.addView(rv);
58         rv.setLayoutParams(new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
59                 ViewGroup.LayoutParams.MATCH_PARENT));
60         rv.setLayoutManager(new LinearLayoutManager(this));
61         List<String> cheeseList = createRandomCheeseList(Collections.<String>emptyList(), 50);
62         final SimpleStringAdapter adapter =
63                 new SimpleStringAdapter(this, cheeseList.toArray(new String[cheeseList.size()]));
64         rv.setAdapter(adapter);
65         final AtomicBoolean refreshingList = new AtomicBoolean(false);
66         shuffle.setOnClickListener(new View.OnClickListener() {
67             @Override
68             public void onClick(View view) {
69                 if (refreshingList.getAndSet(true)) {
70                     // already refreshing, do not allow modifications
71                     return;
72                 }
73                 //noinspection unchecked
74                 new AsyncTask<List<String>, Void, Pair<List<String>, DiffUtil.DiffResult>>() {
75 
76                     @Override
77                     protected Pair<List<String>, DiffUtil.DiffResult> doInBackground(
78                             List<String>... lists) {
79                         List<String> oldList = lists[0];
80                         List<String> newList = createRandomCheeseList(oldList, 5);
81                         DiffUtil.DiffResult diffResult = DiffUtil.calculateDiff(
82                                 new MyCallback(oldList, newList));
83                         //noinspection unchecked
84                         return new Pair(newList, diffResult);
85                     }
86 
87                     @Override
88                     protected void onPostExecute(
89                             Pair<List<String>, DiffUtil.DiffResult> resultPair) {
90                         refreshingList.set(false);
91                         adapter.setValues(resultPair.first);
92                         resultPair.second.dispatchUpdatesTo(adapter);
93                         Toast.makeText(DiffUtilActivity.this, "new list size "
94                                 + resultPair.first.size(), Toast.LENGTH_SHORT).show();
95                     }
96                 }.execute(adapter.getValues());
97 
98             }
99         });
100         setContentView(ll);
101     }
102 
103     private static class MyCallback extends DiffUtil.Callback {
104         private final List<String> mOld;
105         private final List<String> mNew;
106 
MyCallback(List<String> old, List<String> aNew)107         public MyCallback(List<String> old, List<String> aNew) {
108             mOld = old;
109             mNew = aNew;
110         }
111 
112         @Override
getOldListSize()113         public int getOldListSize() {
114             return mOld.size();
115         }
116 
117         @Override
getNewListSize()118         public int getNewListSize() {
119             return mNew.size();
120         }
121 
122         @Override
areItemsTheSame(int oldItemPosition, int newItemPosition)123         public boolean areItemsTheSame(int oldItemPosition, int newItemPosition) {
124             // for strings, content equality is the same as identitiy equality since we don't have
125             // duplicates in this sample.
126             return mOld.get(oldItemPosition).equals(mNew.get(newItemPosition));
127         }
128 
129         @Override
areContentsTheSame(int oldItemPosition, int newItemPosition)130         public boolean areContentsTheSame(int oldItemPosition, int newItemPosition) {
131             return mOld.get(oldItemPosition).equals(mNew.get(newItemPosition));
132         }
133     }
134 
createRandomCheeseList(List<String> seed, int iterations)135     private List<String> createRandomCheeseList(List<String> seed, int iterations) {
136         List<String> output = new ArrayList<>();
137         output.addAll(seed);
138         for (int i = 0; i < iterations; i++) {
139             switch (mRandom.nextInt(3)) {
140                 case 0: //add
141                     output.add(mRandom.nextInt(1 + output.size()), getRandomCheese(output));
142                     break;
143                 case 1: // remove
144                     if (output.size() > 0) {
145                         output.remove(mRandom.nextInt(output.size()));
146                     }
147                     break;
148                 case 2: // move
149                     if (output.size() > 0) {
150                         int from = mRandom.nextInt(output.size());
151                         int to = mRandom.nextInt(output.size());
152                         output.add(to, output.remove(from));
153                     }
154                     break;
155             }
156         }
157         return output;
158     }
159 
getRandomCheese(List<String> excludes)160     private String getRandomCheese(List<String> excludes) {
161         String chosen = Cheeses.sCheeseStrings[mRandom.nextInt(Cheeses.sCheeseStrings.length)];
162         while (excludes.contains(chosen)) {
163             chosen = Cheeses.sCheeseStrings[mRandom.nextInt(Cheeses.sCheeseStrings.length)];
164         }
165         return chosen;
166     }
167 }
168