1616import androidx .appcompat .widget .LinearLayoutCompat ;
1717import androidx .appcompat .widget .PopupMenu ;
1818import androidx .core .content .ContextCompat ;
19+ import androidx .recyclerview .widget .DiffUtil ;
20+ import androidx .recyclerview .widget .RecyclerView ;
1921
2022import com .google .android .material .button .MaterialButton ;
2123import com .google .android .material .card .MaterialCardView ;
4446import io .github .muntashirakon .util .AdapterUtils ;
4547import io .github .muntashirakon .widget .MultiSelectionView ;
4648
47- class FmAdapter extends MultiSelectionView .Adapter <FmAdapter .ViewHolder > {
49+ class FmAdapter extends MultiSelectionView .Adapter <FmItem , FmAdapter .ViewHolder > {
4850 private static final List <String > DEX_EXTENSIONS = Arrays .asList ("dex" , "jar" );
4951
50- private final List <FmItem > mAdapterList = Collections .synchronizedList (new ArrayList <>());
5152 private final FmViewModel mViewModel ;
5253 private final FmActivity mFmActivity ;
5354
55+ private static final DiffUtil .ItemCallback <FmItem > DIFF_CALLBACK = new DiffUtil .ItemCallback <FmItem >() {
56+ @ Override
57+ public boolean areItemsTheSame (@ NonNull FmItem oldItem , @ NonNull FmItem newItem ) {
58+ return Objects .equals (oldItem .path , newItem .path );
59+ }
60+
61+ @ Override
62+ public boolean areContentsTheSame (@ NonNull FmItem oldItem , @ NonNull FmItem newItem ) {
63+ return oldItem .isDirectory == newItem .isDirectory
64+ && oldItem .getSize () == newItem .getSize ()
65+ && oldItem .getLastModified () == newItem .getLastModified ()
66+ && Objects .equals (oldItem .getName (), newItem .getName ());
67+ }
68+ };
69+
5470 public FmAdapter (FmViewModel viewModel , FmActivity activity ) {
71+ super (DIFF_CALLBACK );
5572 mViewModel = viewModel ;
5673 mFmActivity = activity ;
5774 }
5875
5976 public void setFmList (List <FmItem > list ) {
60- AdapterUtils . notifyDataSetChanged ( this , mAdapterList , list );
77+ submitList ( list != null ? new ArrayList <>( list ) : null );
6178 notifySelectionChange ();
6279 }
6380
@@ -73,24 +90,28 @@ public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
7390
7491 @ Override
7592 public void onBindViewHolder (@ NonNull ViewHolder holder , int position ) {
76- FmItem item = mAdapterList . get (position );
93+ FmItem item = getItem (position );
7794 holder .itemView .setTag (item .path );
7895 holder .title .setText (item .getName ());
7996 // Load attributes
8097 cacheAndLoadAttributes (holder , item );
8198 if (item .isDirectory ) {
8299 holder .itemView .setOnClickListener (v -> {
100+ int currentPos = holder .getBindingAdapterPosition ();
101+ if (currentPos == RecyclerView .NO_POSITION ) return ;
83102 if (isInSelectionMode ()) {
84- toggleSelection (position );
103+ toggleSelection (currentPos );
85104 AccessibilityUtils .requestAccessibilityFocus (holder .itemView );
86105 return ;
87106 }
88107 mViewModel .loadFiles (item .path .getUri ());
89108 });
90109 } else {
91110 holder .itemView .setOnClickListener (v -> {
111+ int currentPos = holder .getBindingAdapterPosition ();
112+ if (currentPos == RecyclerView .NO_POSITION ) return ;
92113 if (isInSelectionMode ()) {
93- toggleSelection (position );
114+ toggleSelection (currentPos );
94115 AccessibilityUtils .requestAccessibilityFocus (holder .itemView );
95116 return ;
96117 }
@@ -105,33 +126,37 @@ public void onBindViewHolder(@NonNull ViewHolder holder, int position) {
105126 holder .itemView .setCardBackgroundColor (ContextCompat .getColor (holder .itemView .getContext (), android .R .color .transparent ));
106127 // Set selections
107128 holder .icon .setOnClickListener (v -> {
108- toggleSelection (position );
109- AccessibilityUtils .requestAccessibilityFocus (holder .itemView );
129+ int currentPos = holder .getBindingAdapterPosition ();
130+ if (currentPos != RecyclerView .NO_POSITION ) {
131+ toggleSelection (currentPos );
132+ AccessibilityUtils .requestAccessibilityFocus (holder .itemView );
133+ }
110134 });
111135 // Set actions
112- PopupMenu popupMenu = getPopupMenu (holder .action , item , position );
136+ PopupMenu popupMenu = getPopupMenu (holder .action , item , holder );
113137 holder .action .setOnClickListener (v -> popupMenu .show ());
114138 holder .itemView .setOnLongClickListener (v -> {
139+ int currentPos = holder .getBindingAdapterPosition ();
140+ if (currentPos == RecyclerView .NO_POSITION ) return false ;
115141 // Long click listener: Select/deselect an app.
116142 // 1) Turn selection mode on if this is the first item in the selection list
117143 // 2) Select between last selection position and this position (inclusive) if selection mode is on
118144 Path lastSelectedItem = mViewModel .getLastSelectedItem ();
119145 int lastSelectedItemPosition = -1 ;
120146 if (lastSelectedItem != null ) {
121- int i = 0 ;
122- for (FmItem fmItem : mAdapterList ) {
123- if (fmItem .path .equals (lastSelectedItem )) {
147+ List < FmItem > currentList = getCurrentList () ;
148+ for (int i = 0 ; i < currentList . size (); i ++ ) {
149+ if (currentList . get ( i ) .path .equals (lastSelectedItem )) {
124150 lastSelectedItemPosition = i ;
125151 break ;
126152 }
127- ++i ;
128153 }
129154 }
130155 if (lastSelectedItemPosition >= 0 ) {
131156 // Select from last selection to this selection
132- selectRange (lastSelectedItemPosition , position );
157+ selectRange (lastSelectedItemPosition , currentPos );
133158 } else {
134- toggleSelection (position );
159+ toggleSelection (currentPos );
135160 AccessibilityUtils .requestAccessibilityFocus (holder .itemView );
136161 }
137162 return true ;
@@ -180,29 +205,24 @@ private void loadAttributes(@NonNull ViewHolder holder, @NonNull FmItem item) {
180205
181206 @ Override
182207 public long getItemId (int position ) {
183- return mAdapterList .get (position ).hashCode ();
184- }
185-
186- @ Override
187- public int getItemCount () {
188- return mAdapterList .size ();
208+ return getItem (position ).hashCode ();
189209 }
190210
191211 @ Override
192212 protected boolean select (int position ) {
193- mViewModel .setSelectedItem (mAdapterList . get (position ).path , true );
213+ mViewModel .setSelectedItem (getItem (position ).path , true );
194214 return true ;
195215 }
196216
197217 @ Override
198218 protected boolean deselect (int position ) {
199- mViewModel .setSelectedItem (mAdapterList . get (position ).path , false );
219+ mViewModel .setSelectedItem (getItem (position ).path , false );
200220 return true ;
201221 }
202222
203223 @ Override
204224 protected boolean isSelected (int position ) {
205- return mViewModel .isSelected (mAdapterList . get (position ).path );
225+ return mViewModel .isSelected (getItem (position ).path );
206226 }
207227
208228 @ Override
@@ -218,10 +238,10 @@ protected int getSelectedItemCount() {
218238
219239 @ Override
220240 protected int getTotalItemCount () {
221- return mAdapterList .size ();
241+ return getCurrentList () .size ();
222242 }
223243
224- private PopupMenu getPopupMenu (@ NonNull View anchor , @ NonNull FmItem item , int position ) {
244+ private PopupMenu getPopupMenu (@ NonNull View anchor , @ NonNull FmItem item , @ NonNull ViewHolder holder ) {
225245 PopupMenu popupMenu = new PopupMenu (anchor .getContext (), anchor );
226246 popupMenu .setForceShowIcon (true );
227247 popupMenu .inflate (R .menu .fragment_fm_item_actions );
@@ -299,9 +319,12 @@ private PopupMenu getPopupMenu(@NonNull View anchor, @NonNull FmItem item, int p
299319 return true ;
300320 });
301321 selectAction .setOnMenuItemClickListener (menuItem -> {
302- select (position );
303- notifySelectionChange ();
304- notifyItemChanged (position , AdapterUtils .STUB );
322+ int currentPos = holder .getBindingAdapterPosition ();
323+ if (currentPos != RecyclerView .NO_POSITION ) {
324+ select (currentPos );
325+ notifySelectionChange ();
326+ notifyItemChanged (currentPos , AdapterUtils .STUB );
327+ }
305328 return true ;
306329 });
307330 boolean isVfs = mViewModel .getOptions ().isVfs ();
0 commit comments