Dbrant has uploaded a new change for review. ( 
https://gerrit.wikimedia.org/r/349962 )

Change subject: Make HistoryFragment use a RecyclerView instead of ListView.
......................................................................

Make HistoryFragment use a RecyclerView instead of ListView.

Bug: T162280
Change-Id: I118d97671672c0a670ba785c1686d3bdddadd9d9
---
M app/src/main/java/org/wikipedia/history/HistoryFragment.java
M app/src/main/java/org/wikipedia/views/PageItemView.java
M app/src/main/res/layout/fragment_history.xml
M app/src/main/res/layout/item_page_list_entry.xml
4 files changed, 166 insertions(+), 193 deletions(-)


  git pull ssh://gerrit.wikimedia.org:29418/apps/android/wikipedia 
refs/changes/62/349962/1

diff --git a/app/src/main/java/org/wikipedia/history/HistoryFragment.java 
b/app/src/main/java/org/wikipedia/history/HistoryFragment.java
index 6ad57b4..dd0473e 100644
--- a/app/src/main/java/org/wikipedia/history/HistoryFragment.java
+++ b/app/src/main/java/org/wikipedia/history/HistoryFragment.java
@@ -1,6 +1,5 @@
 package org.wikipedia.history;
 
-import android.content.Context;
 import android.content.DialogInterface;
 import android.database.Cursor;
 import android.net.Uri;
@@ -8,38 +7,31 @@
 import android.support.annotation.NonNull;
 import android.support.annotation.Nullable;
 import android.support.v4.app.Fragment;
+import android.support.v4.app.LoaderManager;
 import android.support.v4.content.CursorLoader;
 import android.support.v4.content.Loader;
-import android.support.v4.view.ViewCompat;
-import android.support.v4.widget.CursorAdapter;
 import android.support.v7.app.AlertDialog;
 import android.support.v7.app.AppCompatActivity;
 import android.support.v7.view.ActionMode;
+import android.support.v7.widget.LinearLayoutManager;
+import android.support.v7.widget.RecyclerView;
 import android.text.TextUtils;
-import android.util.SparseBooleanArray;
 import android.view.LayoutInflater;
 import android.view.Menu;
 import android.view.MenuInflater;
 import android.view.MenuItem;
 import android.view.View;
 import android.view.ViewGroup;
-import android.widget.AbsListView;
-import android.widget.AdapterView;
-import android.widget.ListView;
-import android.widget.TextView;
-
-import com.facebook.drawee.view.SimpleDraweeView;
 
 import org.wikipedia.BackPressedHandler;
 import org.wikipedia.R;
 import org.wikipedia.WikipediaApp;
 import org.wikipedia.activity.FragmentUtil;
-import org.wikipedia.database.CursorAdapterLoaderCallback;
 import org.wikipedia.database.contract.PageHistoryContract;
 import org.wikipedia.page.PageTitle;
-import org.wikipedia.views.GoneIfEmptyTextView;
+import org.wikipedia.views.DefaultViewHolder;
+import org.wikipedia.views.PageItemView;
 import org.wikipedia.views.SearchEmptyView;
-import org.wikipedia.views.ViewUtil;
 
 import java.text.DateFormat;
 import java.util.Date;
@@ -57,21 +49,19 @@
     }
 
     private Unbinder unbinder;
-    @BindView(R.id.history_entry_list) ListView historyEntryList;
+    @BindView(R.id.history_list) RecyclerView historyList;
     @BindView(R.id.history_empty_container) View historyEmptyView;
     @BindView(R.id.search_empty_view) SearchEmptyView searchEmptyView;
-
-    private HistoryEntryAdapter adapter;
 
     private WikipediaApp app;
 
     private String currentSearchQuery;
-    private LoaderCallback loaderCallback;
+    private LoaderCallback loaderCallback = new LoaderCallback();
+    private HistoryEntryItemAdapter adapter = new HistoryEntryItemAdapter();
 
+    private ItemCallback itemCallback = new ItemCallback();
     private ActionMode actionMode;
     private SearchActionModeCallback searchActionModeCallback = new 
HistorySearchCallback();
-    private HistoryItemClickListener itemClickListener = new 
HistoryItemClickListener();
-    private HistoryItemLongClickListener itemLongClickListener = new 
HistoryItemLongClickListener();
 
     @NonNull public static HistoryFragment newInstance() {
         return new HistoryFragment();
@@ -82,7 +72,6 @@
         super.onCreate(savedInstanceState);
         setRetainInstance(true);
         app = WikipediaApp.getInstance();
-        adapter = new HistoryEntryAdapter(getContext(), null, true);
     }
 
     @Override
@@ -91,14 +80,11 @@
         unbinder = ButterKnife.bind(this, view);
 
         searchEmptyView.setEmptyText(R.string.search_history_no_results);
-        ViewCompat.setNestedScrollingEnabled(historyEntryList, true); // 
NavTabLayout coordination
-        historyEntryList.setAdapter(adapter);
-        historyEntryList.setOnItemClickListener(itemClickListener);
-        historyEntryList.setOnItemLongClickListener(itemLongClickListener);
 
-        loaderCallback = new LoaderCallback(getContext(), adapter);
+        historyList.setLayoutManager(new LinearLayoutManager(getContext()));
+        historyList.setAdapter(adapter);
+
         
getActivity().getSupportLoaderManager().initLoader(HISTORY_FRAGMENT_LOADER_ID, 
null, loaderCallback);
-
         return view;
     }
 
@@ -110,11 +96,9 @@
 
     @Override
     public void onDestroyView() {
-        historyEntryList.setEmptyView(null);
         
getActivity().getSupportLoaderManager().destroyLoader(HISTORY_FRAGMENT_LOADER_ID);
-        historyEntryList.setOnItemClickListener(null);
-        historyEntryList.setOnItemLongClickListener(null);
-        historyEntryList.setAdapter(null);
+        historyList.setAdapter(null);
+        adapter.setCursor(null);
         unbinder.unbind();
         unbinder = null;
         super.onDestroyView();
@@ -147,54 +131,7 @@
             searchEmptyView.setVisibility(adapter.isEmpty() ? View.VISIBLE : 
View.GONE);
             historyEmptyView.setVisibility(View.GONE);
         }
-        historyEntryList.setVisibility(adapter.isEmpty() ? View.GONE : 
View.VISIBLE);
-    }
-
-    private class HistoryEntryAdapter extends CursorAdapter {
-        HistoryEntryAdapter(Context context, Cursor c, boolean autoRequery) {
-            super(context, c, autoRequery);
-        }
-
-        @Override
-        public View newView(Context context, Cursor cursor, ViewGroup 
viewGroup) {
-            View view = 
getActivity().getLayoutInflater().inflate(R.layout.item_page_list_entry, 
viewGroup, false);
-            view.setBackgroundResource(R.drawable.selectable_item_background);
-            return view;
-        }
-
-        private String getDateString(Date date) {
-            return DateFormat.getDateInstance().format(date);
-        }
-
-        @Override
-        public void bindView(View view, Context context, Cursor cursor) {
-            TextView title = (TextView) 
view.findViewById(R.id.page_list_item_title);
-            GoneIfEmptyTextView description = (GoneIfEmptyTextView) 
view.findViewById(R.id.page_list_item_description);
-            HistoryEntry entry = 
HistoryEntry.DATABASE_TABLE.fromCursor(cursor);
-            title.setText(entry.getTitle().getDisplayText());
-            description.setText(null);
-            view.setTag(entry);
-            ViewUtil.loadImageUrlInto((SimpleDraweeView) 
view.findViewById(R.id.page_list_item_image),
-                    PageHistoryContract.PageWithImage.IMAGE_NAME.val(cursor));
-
-            // Check the previous item, see if the times differ enough
-            // If they do, display the section header.
-            // Always do it this is the first item.
-            String curTime, prevTime = "";
-            if (cursor.getPosition() != 0) {
-                Cursor prevCursor = (Cursor) getItem(cursor.getPosition() - 1);
-                HistoryEntry prevEntry = 
HistoryEntry.DATABASE_TABLE.fromCursor(prevCursor);
-                prevTime = getDateString(prevEntry.getTimestamp());
-            }
-            curTime = getDateString(entry.getTimestamp());
-            TextView sectionHeader = (TextView) 
view.findViewById(R.id.page_list_header_text);
-            if (!curTime.equals(prevTime)) {
-                sectionHeader.setText(curTime);
-                sectionHeader.setVisibility(View.VISIBLE);
-            } else {
-                sectionHeader.setVisibility(View.GONE);
-            }
-        }
+        historyList.setVisibility(adapter.isEmpty() ? View.GONE : 
View.VISIBLE);
     }
 
     @Override
@@ -211,7 +148,7 @@
     @Override
     public void onPrepareOptionsMenu(Menu menu) {
         super.onPrepareOptionsMenu(menu);
-        boolean isHistoryAvailable = historyEntryList.getCount() > 0;
+        boolean isHistoryAvailable = !adapter.isEmpty();
         menu.findItem(R.id.menu_clear_all_history)
                 .setVisible(isHistoryAvailable)
                 .setEnabled(isHistoryAvailable);
@@ -245,116 +182,6 @@
         }
     }
 
-    private class HistoryItemClickListener implements 
AdapterView.OnItemClickListener {
-        @Override
-        public void onItemClick(AdapterView<?> parent, View view, int 
position, long id) {
-            if (actionMode == null
-                    || 
SearchActionModeCallback.ACTION_MODE_TAG.equals(actionMode.getTag())) {
-                HistoryEntry oldEntry = (HistoryEntry) view.getTag();
-                HistoryEntry newEntry = new HistoryEntry(oldEntry.getTitle(), 
HistoryEntry.SOURCE_HISTORY);
-                onPageClick(oldEntry.getTitle(), newEntry);
-            } else {
-                actionMode.invalidate();
-            }
-        }
-    }
-
-    private class HistoryItemLongClickListener implements 
AdapterView.OnItemLongClickListener {
-        @Override
-        public boolean onItemLongClick(AdapterView<?> parent, View view, int 
position, long id) {
-            if (actionMode != null) {
-                return false;
-            }
-            historyEntryList.setChoiceMode(AbsListView.CHOICE_MODE_MULTIPLE);
-            actionMode = 
((AppCompatActivity)getActivity()).startSupportActionMode(new 
ActionMode.Callback() {
-                private final String actionModeTag = "actionModeHistory";
-                @Override
-                public boolean onCreateActionMode(ActionMode mode, Menu menu) {
-                    
mode.getMenuInflater().inflate(R.menu.menu_history_context, menu);
-                    
setActionModeIntTitle(historyEntryList.getCheckedItemCount() + 1, mode);
-                    return true;
-                }
-
-                @Override
-                public boolean onPrepareActionMode(ActionMode mode, Menu menu) 
{
-                    mode.setTag(actionModeTag);
-                    int count = historyEntryList.getCheckedItemCount();
-                    if (actionMode != null) {
-                        if (count == 0) {
-                            mode.finish();
-                        } else {
-                            setActionModeIntTitle(count, mode);
-                        }
-                    }
-                    return false;
-                }
-
-                @Override
-                public boolean onActionItemClicked(ActionMode mode, MenuItem 
item) {
-                    if (item.getItemId() == R.id.menu_delete_selected_history) 
{
-                        SparseBooleanArray checkedItems = 
historyEntryList.getCheckedItemPositions();
-                        for (int i = 0; i < checkedItems.size(); i++) {
-                            if (checkedItems.valueAt(i)) {
-                                
app.getDatabaseClient(HistoryEntry.class).delete(
-                                        
HistoryEntry.DATABASE_TABLE.fromCursor((Cursor) 
adapter.getItem(checkedItems.keyAt(i))),
-                                        
PageHistoryContract.PageWithImage.SELECTION);
-                            }
-                        }
-                        mode.finish();
-                        return true;
-                    } else {
-                        throw new RuntimeException("Unknown context menu item 
clicked");
-                    }
-                }
-
-                @Override
-                public void onDestroyActionMode(ActionMode mode) {
-                    
historyEntryList.setChoiceMode(AbsListView.CHOICE_MODE_SINGLE);
-                    actionMode = null;
-                    // Clear all selections
-                    historyEntryList.clearChoices();
-                    historyEntryList.requestLayout(); // Required to 
immediately redraw unchecked states
-                }
-            });
-            historyEntryList.setItemChecked(position, true);
-            return true;
-        }
-    }
-
-    private class LoaderCallback extends CursorAdapterLoaderCallback {
-        LoaderCallback(@NonNull Context context, @NonNull CursorAdapter 
adapter) {
-            super(context, adapter);
-        }
-
-        @Override
-        public Loader<Cursor> onCreateLoader(int id, Bundle args) {
-            String titleCol = 
PageHistoryContract.PageWithImage.TITLE.qualifiedName();
-            String selection = null;
-            String[] selectionArgs = null;
-            String searchStr = currentSearchQuery;
-            if (!TextUtils.isEmpty(searchStr)) {
-                searchStr = searchStr.replace("\\", "\\\\").replace("%", 
"\\%").replace("_", "\\_");
-                selection = "UPPER(" + titleCol + ") LIKE UPPER(?) ESCAPE 
'\\'";
-                selectionArgs = new String[]{"%" + searchStr + "%"};
-            }
-
-            Uri uri = PageHistoryContract.PageWithImage.URI;
-            final String[] projection = null;
-            String order = PageHistoryContract.PageWithImage.ORDER_MRU;
-            return new CursorLoader(context(), uri, projection, selection, 
selectionArgs, order);
-        }
-
-        @Override
-        public void onLoadFinished(Loader<Cursor> cursorLoader, Cursor cursor) 
{
-            super.onLoadFinished(cursorLoader, cursor);
-            if (!isAdded()) {
-                return;
-            }
-            updateEmptyState(currentSearchQuery);
-            getActivity().supportInvalidateOptionsMenu();
-        }
-    }
-
     private void setActionModeIntTitle(int count, ActionMode mode) {
         mode.setTitle(getString(R.string.multi_select_items_selected, count));
     }
@@ -377,6 +204,148 @@
         
getActivity().getSupportLoaderManager().restartLoader(HISTORY_FRAGMENT_LOADER_ID,
 null, loaderCallback);
     }
 
+    private class LoaderCallback implements 
LoaderManager.LoaderCallbacks<Cursor> {
+        @Override
+        public Loader<Cursor> onCreateLoader(int id, Bundle args) {
+            String titleCol = 
PageHistoryContract.PageWithImage.TITLE.qualifiedName();
+            String selection = null;
+            String[] selectionArgs = null;
+            String searchStr = currentSearchQuery;
+            if (!TextUtils.isEmpty(searchStr)) {
+                searchStr = searchStr.replace("\\", "\\\\").replace("%", 
"\\%").replace("_", "\\_");
+                selection = "UPPER(" + titleCol + ") LIKE UPPER(?) ESCAPE 
'\\'";
+                selectionArgs = new String[]{"%" + searchStr + "%"};
+            }
+
+            Uri uri = PageHistoryContract.PageWithImage.URI;
+            final String[] projection = null;
+            String order = PageHistoryContract.PageWithImage.ORDER_MRU;
+            return new CursorLoader(getContext().getApplicationContext(),
+                    uri, projection, selection, selectionArgs, order);
+        }
+
+        @Override
+        public void onLoadFinished(Loader<Cursor> cursorLoader, Cursor cursor) 
{
+            adapter.setCursor(cursor);
+            if (!isAdded()) {
+                return;
+            }
+            updateEmptyState(currentSearchQuery);
+            getActivity().supportInvalidateOptionsMenu();
+        }
+
+        @Override
+        public void onLoaderReset(Loader<Cursor> loader) {
+            adapter.setCursor(null);
+        }
+    }
+
+    private class HistoryEntryItemHolder extends 
DefaultViewHolder<PageItemView<HistoryEntry>> {
+        private HistoryEntry entry;
+
+        HistoryEntryItemHolder(PageItemView<HistoryEntry> itemView) {
+            super(itemView);
+        }
+
+        void bindItem(@NonNull Cursor cursor) {
+            entry = HistoryEntry.DATABASE_TABLE.fromCursor(cursor);
+            getView().setItem(entry);
+            getView().setTitle(entry.getTitle().getDisplayText());
+            getView().setDescription(entry.getTitle().getDescription());
+            
getView().setImageUrl(PageHistoryContract.PageWithImage.IMAGE_NAME.val(cursor));
+
+            // Check the previous item, see if the times differ enough
+            // If they do, display the section header.
+            // Always do it this is the first item.
+            String curTime = getDateString(entry.getTimestamp());
+            String prevTime = "";
+            if (cursor.getPosition() != 0) {
+                cursor.moveToPrevious();
+                HistoryEntry prevEntry = 
HistoryEntry.DATABASE_TABLE.fromCursor(cursor);
+                prevTime = getDateString(prevEntry.getTimestamp());
+                cursor.moveToNext();
+            }
+            getView().setHeaderText(curTime.equals(prevTime) ? null : curTime);
+        }
+
+        private String getDateString(Date date) {
+            return DateFormat.getDateInstance().format(date);
+        }
+    }
+
+    private final class HistoryEntryItemAdapter extends 
RecyclerView.Adapter<HistoryEntryItemHolder> {
+        @Nullable private Cursor cursor;
+
+        @Override
+        public int getItemCount() {
+            return cursor == null ? 0 : cursor.getCount();
+        }
+
+        public boolean isEmpty() {
+            return getItemCount() == 0;
+        }
+
+        public void setCursor(@Nullable Cursor newCursor) {
+            if (cursor == newCursor) {
+                return;
+            }
+            if (cursor != null) {
+                cursor.close();
+            }
+            cursor = newCursor;
+            this.notifyDataSetChanged();
+        }
+
+        @Override
+        public HistoryEntryItemHolder onCreateViewHolder(ViewGroup parent, int 
type) {
+            return new HistoryEntryItemHolder(new 
PageItemView<HistoryEntry>(getContext()));
+        }
+
+        @Override
+        public void onBindViewHolder(HistoryEntryItemHolder holder, int pos) {
+            if (cursor == null) {
+                return;
+            }
+            cursor.moveToPosition(pos);
+            holder.bindItem(cursor);
+        }
+
+        @Override public void onViewAttachedToWindow(HistoryEntryItemHolder 
holder) {
+            super.onViewAttachedToWindow(holder);
+            holder.getView().setCallback(itemCallback);
+        }
+
+        @Override public void onViewDetachedFromWindow(HistoryEntryItemHolder 
holder) {
+            holder.getView().setCallback(null);
+            super.onViewDetachedFromWindow(holder);
+        }
+    }
+
+    private class ItemCallback implements PageItemView.Callback<HistoryEntry> {
+        @Override
+        public void onClick(@Nullable HistoryEntry entry) {
+            if (entry != null) {
+                HistoryEntry newEntry = new HistoryEntry(entry.getTitle(), 
HistoryEntry.SOURCE_HISTORY);
+                onPageClick(entry.getTitle(), newEntry);
+            }
+        }
+
+        @Override
+        public boolean onLongClick(@Nullable HistoryEntry entry) {
+            // TODO: multi-select
+            return true;
+        }
+
+        @Override
+        public void onThumbClick(@Nullable HistoryEntry entry) {
+            onClick(entry);
+        }
+
+        @Override
+        public void onActionClick(@Nullable HistoryEntry entry, @NonNull 
PageItemView view) {
+        }
+    }
+
     private class HistorySearchCallback extends SearchActionModeCallback {
         @Override
         public boolean onCreateActionMode(ActionMode mode, Menu menu) {
diff --git a/app/src/main/java/org/wikipedia/views/PageItemView.java 
b/app/src/main/java/org/wikipedia/views/PageItemView.java
index ae12c8f..3466789 100644
--- a/app/src/main/java/org/wikipedia/views/PageItemView.java
+++ b/app/src/main/java/org/wikipedia/views/PageItemView.java
@@ -41,6 +41,7 @@
     @BindView(R.id.page_list_item_image) SimpleDraweeView imageView;
     @BindView(R.id.page_list_item_action_button) ImageView actionView;
     @BindView(R.id.page_list_item_selected_image) View imageSelectedView;
+    @BindView(R.id.page_list_header_text) GoneIfEmptyTextView headerView;
 
     @Nullable private Callback<T> callback;
     @Nullable private T item;
@@ -80,6 +81,10 @@
         actionView.setContentDescription(getContext().getString(id));
     }
 
+    public void setHeaderText(@Nullable CharSequence text) {
+        headerView.setText(text);
+    }
+
     public void setSelected(boolean selected) {
         if (this.selected != selected) {
             this.selected = selected;
diff --git a/app/src/main/res/layout/fragment_history.xml 
b/app/src/main/res/layout/fragment_history.xml
index f802cb1..d269807 100644
--- a/app/src/main/res/layout/fragment_history.xml
+++ b/app/src/main/res/layout/fragment_history.xml
@@ -50,8 +50,8 @@
         android:layout_gravity="center"
         android:visibility="gone"/>
 
-    <ListView
-        android:id="@+id/history_entry_list"
+    <android.support.v7.widget.RecyclerView
+        android:id="@+id/history_list"
         android:layout_width="match_parent"
         android:layout_height="match_parent"
         android:background="?attr/window_background_color"/>
diff --git a/app/src/main/res/layout/item_page_list_entry.xml 
b/app/src/main/res/layout/item_page_list_entry.xml
index b60b48c..3382da2 100644
--- a/app/src/main/res/layout/item_page_list_entry.xml
+++ b/app/src/main/res/layout/item_page_list_entry.xml
@@ -18,13 +18,12 @@
     <!-- TODO: this probably shouldn't be part of an item cell since it has to 
do with a collection
                of cells and nothing to do with an individual. This manifests 
visually when the cell
                is interacted with and the additional spacing is exposed. -->
-    <TextView
+    <org.wikipedia.views.GoneIfEmptyTextView
         android:id="@+id/page_list_header_text"
         android:layout_width="match_parent"
         android:layout_height="wrap_content"
         android:layout_marginBottom="16dp"
         android:gravity="center_vertical"
-        android:visibility="gone"
         tools:visibility="visible"
         tools:text="Header text"
         />

-- 
To view, visit https://gerrit.wikimedia.org/r/349962
To unsubscribe, visit https://gerrit.wikimedia.org/r/settings

Gerrit-MessageType: newchange
Gerrit-Change-Id: I118d97671672c0a670ba785c1686d3bdddadd9d9
Gerrit-PatchSet: 1
Gerrit-Project: apps/android/wikipedia
Gerrit-Branch: master
Gerrit-Owner: Dbrant <[email protected]>

_______________________________________________
MediaWiki-commits mailing list
[email protected]
https://lists.wikimedia.org/mailman/listinfo/mediawiki-commits

Reply via email to