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 
17 package androidx.recyclerview.widget;
18 
19 import android.annotation.SuppressLint;
20 import android.util.Log;
21 import android.view.View;
22 
23 import org.jspecify.annotations.NonNull;
24 import org.jspecify.annotations.Nullable;
25 
26 /**
27  * A wrapper class for ItemAnimator that records View bounds and decides whether it should run
28  * move, change, add or remove animations. This class also replicates the original ItemAnimator
29  * API.
30  * <p>
31  * It uses {@link RecyclerView.ItemAnimator.ItemHolderInfo} to track the bounds information of
32  * the Views. If you would like to extend this class, you can override {@link #obtainHolderInfo()}
33  * method to provide your own info class that extends
34  * {@link RecyclerView.ItemAnimator.ItemHolderInfo}.
35  */
36 public abstract class SimpleItemAnimator extends RecyclerView.ItemAnimator {
37 
38     private static final boolean DEBUG = false;
39 
40     private static final String TAG = "SimpleItemAnimator";
41 
42     boolean mSupportsChangeAnimations = true;
43 
44     /**
45      * Returns whether this ItemAnimator supports animations of change events.
46      *
47      * @return true if change animations are supported, false otherwise
48      */
49     @SuppressWarnings("unused")
getSupportsChangeAnimations()50     public boolean getSupportsChangeAnimations() {
51         return mSupportsChangeAnimations;
52     }
53 
54     /**
55      * Sets whether this ItemAnimator supports animations of item change events.
56      * If you set this property to false, actions on the data set which change the
57      * contents of items will not be animated. What those animations do is left
58      * up to the discretion of the ItemAnimator subclass, in its
59      * {@link #animateChange(RecyclerView.ViewHolder, RecyclerView.ViewHolder, int, int, int, int)} implementation.
60      * The value of this property is true by default.
61      *
62      * @param supportsChangeAnimations true if change animations are supported by
63      *                                 this ItemAnimator, false otherwise. If the property is false,
64      *                                 the ItemAnimator
65      *                                 will not receive a call to
66      *                                 {@link #animateChange(RecyclerView.ViewHolder, RecyclerView.ViewHolder, int, int, int,
67      *                                 int)} when changes occur.
68      * @see RecyclerView.Adapter#notifyItemChanged(int)
69      * @see RecyclerView.Adapter#notifyItemRangeChanged(int, int)
70      */
setSupportsChangeAnimations(boolean supportsChangeAnimations)71     public void setSupportsChangeAnimations(boolean supportsChangeAnimations) {
72         mSupportsChangeAnimations = supportsChangeAnimations;
73     }
74 
75     /**
76      * {@inheritDoc}
77      *
78      * @return True if change animations are not supported or the ViewHolder is invalid,
79      * false otherwise.
80      *
81      * @see #setSupportsChangeAnimations(boolean)
82      */
83     @Override
canReuseUpdatedViewHolder(RecyclerView.@onNull ViewHolder viewHolder)84     public boolean canReuseUpdatedViewHolder(RecyclerView.@NonNull ViewHolder viewHolder) {
85         return !mSupportsChangeAnimations || viewHolder.isInvalid();
86     }
87 
88     @Override
animateDisappearance(RecyclerView.@onNull ViewHolder viewHolder, @NonNull ItemHolderInfo preLayoutInfo, @Nullable ItemHolderInfo postLayoutInfo)89     public boolean animateDisappearance(RecyclerView.@NonNull ViewHolder viewHolder,
90             @NonNull ItemHolderInfo preLayoutInfo, @Nullable ItemHolderInfo postLayoutInfo) {
91         int oldLeft = preLayoutInfo.left;
92         int oldTop = preLayoutInfo.top;
93         View disappearingItemView = viewHolder.itemView;
94         int newLeft = postLayoutInfo == null ? disappearingItemView.getLeft() : postLayoutInfo.left;
95         int newTop = postLayoutInfo == null ? disappearingItemView.getTop() : postLayoutInfo.top;
96         if (!viewHolder.isRemoved() && (oldLeft != newLeft || oldTop != newTop)) {
97             disappearingItemView.layout(newLeft, newTop,
98                     newLeft + disappearingItemView.getWidth(),
99                     newTop + disappearingItemView.getHeight());
100             if (DEBUG) {
101                 Log.d(TAG, "DISAPPEARING: " + viewHolder + " with view " + disappearingItemView);
102             }
103             return animateMove(viewHolder, oldLeft, oldTop, newLeft, newTop);
104         } else {
105             if (DEBUG) {
106                 Log.d(TAG, "REMOVED: " + viewHolder + " with view " + disappearingItemView);
107             }
108             return animateRemove(viewHolder);
109         }
110     }
111 
112     @Override
animateAppearance(RecyclerView.@onNull ViewHolder viewHolder, @Nullable ItemHolderInfo preLayoutInfo, @NonNull ItemHolderInfo postLayoutInfo)113     public boolean animateAppearance(RecyclerView.@NonNull ViewHolder viewHolder,
114             @Nullable ItemHolderInfo preLayoutInfo, @NonNull ItemHolderInfo postLayoutInfo) {
115         if (preLayoutInfo != null && (preLayoutInfo.left != postLayoutInfo.left
116                 || preLayoutInfo.top != postLayoutInfo.top)) {
117             // slide items in if before/after locations differ
118             if (DEBUG) {
119                 Log.d(TAG, "APPEARING: " + viewHolder + " with view " + viewHolder);
120             }
121             return animateMove(viewHolder, preLayoutInfo.left, preLayoutInfo.top,
122                     postLayoutInfo.left, postLayoutInfo.top);
123         } else {
124             if (DEBUG) {
125                 Log.d(TAG, "ADDED: " + viewHolder + " with view " + viewHolder);
126             }
127             return animateAdd(viewHolder);
128         }
129     }
130 
131     @Override
animatePersistence(RecyclerView.@onNull ViewHolder viewHolder, @NonNull ItemHolderInfo preLayoutInfo, @NonNull ItemHolderInfo postLayoutInfo)132     public boolean animatePersistence(RecyclerView.@NonNull ViewHolder viewHolder,
133             @NonNull ItemHolderInfo preLayoutInfo, @NonNull ItemHolderInfo postLayoutInfo) {
134         if (preLayoutInfo.left != postLayoutInfo.left || preLayoutInfo.top != postLayoutInfo.top) {
135             if (DEBUG) {
136                 Log.d(TAG, "PERSISTENT: " + viewHolder
137                         + " with view " + viewHolder.itemView);
138             }
139             return animateMove(viewHolder,
140                     preLayoutInfo.left, preLayoutInfo.top, postLayoutInfo.left, postLayoutInfo.top);
141         }
142         dispatchMoveFinished(viewHolder);
143         return false;
144     }
145 
146     @Override
animateChange(RecyclerView.@onNull ViewHolder oldHolder, RecyclerView.@NonNull ViewHolder newHolder, @NonNull ItemHolderInfo preLayoutInfo, @NonNull ItemHolderInfo postLayoutInfo)147     public boolean animateChange(RecyclerView.@NonNull ViewHolder oldHolder,
148             RecyclerView.@NonNull ViewHolder newHolder, @NonNull ItemHolderInfo preLayoutInfo,
149             @NonNull ItemHolderInfo postLayoutInfo) {
150         if (DEBUG) {
151             Log.d(TAG, "CHANGED: " + oldHolder + " with view " + oldHolder.itemView);
152         }
153         final int fromLeft = preLayoutInfo.left;
154         final int fromTop = preLayoutInfo.top;
155         final int toLeft, toTop;
156         if (newHolder.shouldIgnore()) {
157             toLeft = preLayoutInfo.left;
158             toTop = preLayoutInfo.top;
159         } else {
160             toLeft = postLayoutInfo.left;
161             toTop = postLayoutInfo.top;
162         }
163         return animateChange(oldHolder, newHolder, fromLeft, fromTop, toLeft, toTop);
164     }
165 
166     /**
167      * Called when an item is removed from the RecyclerView. Implementors can choose
168      * whether and how to animate that change, but must always call
169      * {@link #dispatchRemoveFinished(RecyclerView.ViewHolder)} when done, either
170      * immediately (if no animation will occur) or after the animation actually finishes.
171      * The return value indicates whether an animation has been set up and whether the
172      * ItemAnimator's {@link #runPendingAnimations()} method should be called at the
173      * next opportunity. This mechanism allows ItemAnimator to set up individual animations
174      * as separate calls to {@link #animateAdd(RecyclerView.ViewHolder) animateAdd()},
175      * {@link #animateMove(RecyclerView.ViewHolder, int, int, int, int) animateMove()},
176      * {@link #animateRemove(RecyclerView.ViewHolder) animateRemove()}, and
177      * {@link #animateChange(RecyclerView.ViewHolder, RecyclerView.ViewHolder, int, int, int, int)} come in one by one,
178      * then start the animations together in the later call to {@link #runPendingAnimations()}.
179      *
180      * <p>This method may also be called for disappearing items which continue to exist in the
181      * RecyclerView, but for which the system does not have enough information to animate
182      * them out of view. In that case, the default animation for removing items is run
183      * on those items as well.</p>
184      *
185      * @param holder The item that is being removed.
186      * @return true if a later call to {@link #runPendingAnimations()} is requested,
187      * false otherwise.
188      */
189     @SuppressLint("UnknownNullness") // b/240775049: Cannot annotate properly
animateRemove(RecyclerView.ViewHolder holder)190     public abstract boolean animateRemove(RecyclerView.ViewHolder holder);
191 
192     /**
193      * Called when an item is added to the RecyclerView. Implementors can choose
194      * whether and how to animate that change, but must always call
195      * {@link #dispatchAddFinished(RecyclerView.ViewHolder)} when done, either
196      * immediately (if no animation will occur) or after the animation actually finishes.
197      * The return value indicates whether an animation has been set up and whether the
198      * ItemAnimator's {@link #runPendingAnimations()} method should be called at the
199      * next opportunity. This mechanism allows ItemAnimator to set up individual animations
200      * as separate calls to {@link #animateAdd(RecyclerView.ViewHolder) animateAdd()},
201      * {@link #animateMove(RecyclerView.ViewHolder, int, int, int, int) animateMove()},
202      * {@link #animateRemove(RecyclerView.ViewHolder) animateRemove()}, and
203      * {@link #animateChange(RecyclerView.ViewHolder, RecyclerView.ViewHolder, int, int, int, int)} come in one by one,
204      * then start the animations together in the later call to {@link #runPendingAnimations()}.
205      *
206      * <p>This method may also be called for appearing items which were already in the
207      * RecyclerView, but for which the system does not have enough information to animate
208      * them into view. In that case, the default animation for adding items is run
209      * on those items as well.</p>
210      *
211      * @param holder The item that is being added.
212      * @return true if a later call to {@link #runPendingAnimations()} is requested,
213      * false otherwise.
214      */
215     @SuppressLint("UnknownNullness") // b/240775049: Cannot annotate properly
animateAdd(RecyclerView.ViewHolder holder)216     public abstract boolean animateAdd(RecyclerView.ViewHolder holder);
217 
218     /**
219      * Called when an item is moved in the RecyclerView. Implementors can choose
220      * whether and how to animate that change, but must always call
221      * {@link #dispatchMoveFinished(RecyclerView.ViewHolder)} when done, either
222      * immediately (if no animation will occur) or after the animation actually finishes.
223      * The return value indicates whether an animation has been set up and whether the
224      * ItemAnimator's {@link #runPendingAnimations()} method should be called at the
225      * next opportunity. This mechanism allows ItemAnimator to set up individual animations
226      * as separate calls to {@link #animateAdd(RecyclerView.ViewHolder) animateAdd()},
227      * {@link #animateMove(RecyclerView.ViewHolder, int, int, int, int) animateMove()},
228      * {@link #animateRemove(RecyclerView.ViewHolder) animateRemove()}, and
229      * {@link #animateChange(RecyclerView.ViewHolder, RecyclerView.ViewHolder, int, int, int, int)} come in one by one,
230      * then start the animations together in the later call to {@link #runPendingAnimations()}.
231      *
232      * @param holder The item that is being moved.
233      * @param fromX x coordinate from which to start animation.
234      * @param fromY y coordinate from which to start animation.
235      * @param toX x coordinate at which to end animation.
236      * @param toY y coordinate at which to end animation.
237      * @return true if a later call to {@link #runPendingAnimations()} is requested,
238      * false otherwise.
239      */
240     @SuppressLint("UnknownNullness") // b/240775049: Cannot annotate properly
animateMove(RecyclerView.ViewHolder holder, int fromX, int fromY, int toX, int toY)241     public abstract boolean animateMove(RecyclerView.ViewHolder holder, int fromX, int fromY,
242             int toX, int toY);
243 
244     /**
245      * Called when an item is changed in the RecyclerView, as indicated by a call to
246      * {@link RecyclerView.Adapter#notifyItemChanged(int)} or
247      * {@link RecyclerView.Adapter#notifyItemRangeChanged(int, int)}.
248      * <p>
249      * Implementers can choose whether and how to animate changes, but must always call
250      * {@link #dispatchChangeFinished(RecyclerView.ViewHolder, boolean)} for each non-null
251      * distinct ViewHolder,
252      * either immediately (if no animation will occur) or after the animation actually finishes.
253      * If the {@code oldHolder} is the same ViewHolder as the {@code newHolder}, you must call
254      * {@link #dispatchChangeFinished(RecyclerView.ViewHolder, boolean)} once and only once. In
255      * that case, the
256      * second parameter of {@code dispatchChangeFinished} is ignored.
257      * <p>
258      * The return value indicates whether an animation has been set up and whether the
259      * ItemAnimator's {@link #runPendingAnimations()} method should be called at the
260      * next opportunity. This mechanism allows ItemAnimator to set up individual animations
261      * as separate calls to {@link #animateAdd(RecyclerView.ViewHolder) animateAdd()},
262      * {@link #animateMove(RecyclerView.ViewHolder, int, int, int, int) animateMove()},
263      * {@link #animateRemove(RecyclerView.ViewHolder) animateRemove()}, and
264      * {@link #animateChange(RecyclerView.ViewHolder, RecyclerView.ViewHolder, int, int, int, int)} come in one by one,
265      * then start the animations together in the later call to {@link #runPendingAnimations()}.
266      *
267      * @param oldHolder The original item that changed.
268      * @param newHolder The new item that was created with the changed content. Might be null
269      * @param fromLeft  Left of the old view holder
270      * @param fromTop   Top of the old view holder
271      * @param toLeft    Left of the new view holder
272      * @param toTop     Top of the new view holder
273      * @return true if a later call to {@link #runPendingAnimations()} is requested,
274      * false otherwise.
275      */
276     @SuppressLint("UnknownNullness") // b/240775049: Cannot annotate properly
animateChange(RecyclerView.ViewHolder oldHolder, RecyclerView.ViewHolder newHolder, int fromLeft, int fromTop, int toLeft, int toTop)277     public abstract boolean animateChange(RecyclerView.ViewHolder oldHolder,
278             RecyclerView.ViewHolder newHolder, int fromLeft, int fromTop, int toLeft, int toTop);
279 
280     /**
281      * Method to be called by subclasses when a remove animation is done.
282      *
283      * @param item The item which has been removed
284      * @see RecyclerView.ItemAnimator#animateDisappearance(RecyclerView.ViewHolder, ItemHolderInfo,
285      * ItemHolderInfo)
286      */
287     @SuppressLint("UnknownNullness") // b/240775049: Cannot annotate properly
dispatchRemoveFinished(RecyclerView.ViewHolder item)288     public final void dispatchRemoveFinished(RecyclerView.ViewHolder item) {
289         onRemoveFinished(item);
290         dispatchAnimationFinished(item);
291     }
292 
293     /**
294      * Method to be called by subclasses when a move animation is done.
295      *
296      * @param item The item which has been moved
297      * @see RecyclerView.ItemAnimator#animateDisappearance(RecyclerView.ViewHolder, ItemHolderInfo,
298      * ItemHolderInfo)
299      * @see RecyclerView.ItemAnimator#animatePersistence(RecyclerView.ViewHolder, ItemHolderInfo, ItemHolderInfo)
300      * @see RecyclerView.ItemAnimator#animateAppearance(RecyclerView.ViewHolder, ItemHolderInfo, ItemHolderInfo)
301      */
302     @SuppressLint("UnknownNullness") // b/240775049: Cannot annotate properly
dispatchMoveFinished(RecyclerView.ViewHolder item)303     public final void dispatchMoveFinished(RecyclerView.ViewHolder item) {
304         onMoveFinished(item);
305         dispatchAnimationFinished(item);
306     }
307 
308     /**
309      * Method to be called by subclasses when an add animation is done.
310      *
311      * @param item The item which has been added
312      */
313     @SuppressLint("UnknownNullness") // b/240775049: Cannot annotate properly
dispatchAddFinished(RecyclerView.ViewHolder item)314     public final void dispatchAddFinished(RecyclerView.ViewHolder item) {
315         onAddFinished(item);
316         dispatchAnimationFinished(item);
317     }
318 
319     /**
320      * Method to be called by subclasses when a change animation is done.
321      *
322      * @param item    The item which has been changed (this method must be called for
323      *                each non-null ViewHolder passed into
324      *                {@link #animateChange(RecyclerView.ViewHolder, RecyclerView.ViewHolder, int, int, int, int)}).
325      * @param oldItem true if this is the old item that was changed, false if
326      *                it is the new item that replaced the old item.
327      * @see #animateChange(RecyclerView.ViewHolder, RecyclerView.ViewHolder, int, int, int, int)
328      */
329     @SuppressLint("UnknownNullness") // b/240775049: Cannot annotate properly
dispatchChangeFinished(RecyclerView.ViewHolder item, boolean oldItem)330     public final void dispatchChangeFinished(RecyclerView.ViewHolder item, boolean oldItem) {
331         onChangeFinished(item, oldItem);
332         dispatchAnimationFinished(item);
333     }
334 
335     /**
336      * Method to be called by subclasses when a remove animation is being started.
337      *
338      * @param item The item being removed
339      */
340     @SuppressLint("UnknownNullness") // b/240775049: Cannot annotate properly
dispatchRemoveStarting(RecyclerView.ViewHolder item)341     public final void dispatchRemoveStarting(RecyclerView.ViewHolder item) {
342         onRemoveStarting(item);
343     }
344 
345     /**
346      * Method to be called by subclasses when a move animation is being started.
347      *
348      * @param item The item being moved
349      */
350     @SuppressLint("UnknownNullness") // b/240775049: Cannot annotate properly
dispatchMoveStarting(RecyclerView.ViewHolder item)351     public final void dispatchMoveStarting(RecyclerView.ViewHolder item) {
352         onMoveStarting(item);
353     }
354 
355     /**
356      * Method to be called by subclasses when an add animation is being started.
357      *
358      * @param item The item being added
359      */
360     @SuppressLint("UnknownNullness") // b/240775049: Cannot annotate properly
dispatchAddStarting(RecyclerView.ViewHolder item)361     public final void dispatchAddStarting(RecyclerView.ViewHolder item) {
362         onAddStarting(item);
363     }
364 
365     /**
366      * Method to be called by subclasses when a change animation is being started.
367      *
368      * @param item    The item which has been changed (this method must be called for
369      *                each non-null ViewHolder passed into
370      *                {@link #animateChange(RecyclerView.ViewHolder, RecyclerView.ViewHolder, int, int, int, int)}).
371      * @param oldItem true if this is the old item that was changed, false if
372      *                it is the new item that replaced the old item.
373      */
374     @SuppressLint("UnknownNullness") // b/240775049: Cannot annotate properly
dispatchChangeStarting(RecyclerView.ViewHolder item, boolean oldItem)375     public final void dispatchChangeStarting(RecyclerView.ViewHolder item, boolean oldItem) {
376         onChangeStarting(item, oldItem);
377     }
378 
379     /**
380      * Called when a remove animation is being started on the given ViewHolder.
381      * The default implementation does nothing. Subclasses may wish to override
382      * this method to handle any ViewHolder-specific operations linked to animation
383      * lifecycles.
384      *
385      * @param item The ViewHolder being animated.
386      */
387     @SuppressLint("UnknownNullness") // b/240775049: Cannot annotate properly
388     @SuppressWarnings("UnusedParameters")
onRemoveStarting(RecyclerView.ViewHolder item)389     public void onRemoveStarting(RecyclerView.ViewHolder item) {
390     }
391 
392     /**
393      * Called when a remove animation has ended on the given ViewHolder.
394      * The default implementation does nothing. Subclasses may wish to override
395      * this method to handle any ViewHolder-specific operations linked to animation
396      * lifecycles.
397      *
398      * @param item The ViewHolder being animated.
399      */
400     @SuppressLint("UnknownNullness") // b/240775049: Cannot annotate properly
onRemoveFinished(RecyclerView.ViewHolder item)401     public void onRemoveFinished(RecyclerView.ViewHolder item) {
402     }
403 
404     /**
405      * Called when an add animation is being started on the given ViewHolder.
406      * The default implementation does nothing. Subclasses may wish to override
407      * this method to handle any ViewHolder-specific operations linked to animation
408      * lifecycles.
409      *
410      * @param item The ViewHolder being animated.
411      */
412     @SuppressWarnings("UnusedParameters")
413     @SuppressLint("UnknownNullness") // b/240775049: Cannot annotate properly
onAddStarting(RecyclerView.ViewHolder item)414     public void onAddStarting(RecyclerView.ViewHolder item) {
415     }
416 
417     /**
418      * Called when an add animation has ended on the given ViewHolder.
419      * The default implementation does nothing. Subclasses may wish to override
420      * this method to handle any ViewHolder-specific operations linked to animation
421      * lifecycles.
422      *
423      * @param item The ViewHolder being animated.
424      */
425     @SuppressLint("UnknownNullness") // b/240775049: Cannot annotate properly
onAddFinished(RecyclerView.ViewHolder item)426     public void onAddFinished(RecyclerView.ViewHolder item) {
427     }
428 
429     /**
430      * Called when a move animation is being started on the given ViewHolder.
431      * The default implementation does nothing. Subclasses may wish to override
432      * this method to handle any ViewHolder-specific operations linked to animation
433      * lifecycles.
434      *
435      * @param item The ViewHolder being animated.
436      */
437     @SuppressWarnings("UnusedParameters")
438     @SuppressLint("UnknownNullness") // b/240775049: Cannot annotate properly
onMoveStarting(RecyclerView.ViewHolder item)439     public void onMoveStarting(RecyclerView.ViewHolder item) {
440     }
441 
442     /**
443      * Called when a move animation has ended on the given ViewHolder.
444      * The default implementation does nothing. Subclasses may wish to override
445      * this method to handle any ViewHolder-specific operations linked to animation
446      * lifecycles.
447      *
448      * @param item The ViewHolder being animated.
449      */
450     @SuppressLint("UnknownNullness") // b/240775049: Cannot annotate properly
onMoveFinished(RecyclerView.ViewHolder item)451     public void onMoveFinished(RecyclerView.ViewHolder item) {
452     }
453 
454     /**
455      * Called when a change animation is being started on the given ViewHolder.
456      * The default implementation does nothing. Subclasses may wish to override
457      * this method to handle any ViewHolder-specific operations linked to animation
458      * lifecycles.
459      *
460      * @param item    The ViewHolder being animated.
461      * @param oldItem true if this is the old item that was changed, false if
462      *                it is the new item that replaced the old item.
463      */
464     @SuppressLint("UnknownNullness") // b/240775049: Cannot annotate properly
465     @SuppressWarnings("UnusedParameters")
onChangeStarting(RecyclerView.ViewHolder item, boolean oldItem)466     public void onChangeStarting(RecyclerView.ViewHolder item, boolean oldItem) {
467     }
468 
469     /**
470      * Called when a change animation has ended on the given ViewHolder.
471      * The default implementation does nothing. Subclasses may wish to override
472      * this method to handle any ViewHolder-specific operations linked to animation
473      * lifecycles.
474      *
475      * @param item    The ViewHolder being animated.
476      * @param oldItem true if this is the old item that was changed, false if
477      *                it is the new item that replaced the old item.
478      */
479     @SuppressLint("UnknownNullness") // b/240775049: Cannot annotate properly
onChangeFinished(RecyclerView.ViewHolder item, boolean oldItem)480     public void onChangeFinished(RecyclerView.ViewHolder item, boolean oldItem) {
481     }
482 }
483 
484