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 */
16package com.example.android.supportv7.util;
17
18import android.os.AsyncTask;
19import android.os.Bundle;
20import android.view.View;
21import android.view.ViewGroup;
22import android.widget.Button;
23import android.widget.LinearLayout;
24import android.widget.Toast;
25
26import androidx.annotation.Nullable;
27import androidx.appcompat.app.AppCompatActivity;
28import androidx.core.util.Pair;
29import androidx.recyclerview.widget.DiffUtil;
30import androidx.recyclerview.widget.LinearLayoutManager;
31import androidx.recyclerview.widget.RecyclerView;
32
33import com.example.android.supportv7.Cheeses;
34import com.example.android.supportv7.widget.adapter.SimpleStringAdapter;
35
36import java.util.ArrayList;
37import java.util.Collections;
38import java.util.List;
39import java.util.Random;
40import 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 */
46public class DiffUtilActivity extends AppCompatActivity {
47    private Random mRandom = new Random(System.nanoTime());
48
49    @Override
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
107        public MyCallback(List<String> old, List<String> aNew) {
108            mOld = old;
109            mNew = aNew;
110        }
111
112        @Override
113        public int getOldListSize() {
114            return mOld.size();
115        }
116
117        @Override
118        public int getNewListSize() {
119            return mNew.size();
120        }
121
122        @Override
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
130        public boolean areContentsTheSame(int oldItemPosition, int newItemPosition) {
131            return mOld.get(oldItemPosition).equals(mNew.get(newItemPosition));
132        }
133    }
134
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
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