Niedzielski has uploaded a new change for review.

  https://gerrit.wikimedia.org/r/284822

Change subject: Add Reading List list DAO and integration
......................................................................

Add Reading List list DAO and integration

Bug: T127709, T130929
Change-Id: I6b82097b12367fc8c6c03acb2e4556ba64a3a3b1
---
M app/src/main/java/org/wikipedia/page/leadimages/LeadImagesHandler.java
M app/src/main/java/org/wikipedia/readinglist/AddToReadingListDialog.java
M app/src/main/java/org/wikipedia/readinglist/ReadingList.java
M app/src/main/java/org/wikipedia/readinglist/ReadingListData.java
M app/src/main/java/org/wikipedia/readinglist/ReadingListDetailView.java
D app/src/main/java/org/wikipedia/readinglist/ReadingListFakeData.java
M app/src/main/java/org/wikipedia/readinglist/ReadingListImageFetcher.java
M app/src/main/java/org/wikipedia/readinglist/ReadingListItemView.java
M app/src/main/java/org/wikipedia/readinglist/ReadingListsFragment.java
M app/src/main/java/org/wikipedia/readinglist/database/ReadingListRow.java
M app/src/main/java/org/wikipedia/readinglist/page/ReadingListPage.java
M app/src/main/java/org/wikipedia/readinglist/page/ReadingListPageRow.java
A 
app/src/main/java/org/wikipedia/readinglist/page/database/ReadingListDaoProxy.java
M 
app/src/main/java/org/wikipedia/readinglist/page/database/ReadingListPageDao.java
14 files changed, 505 insertions(+), 229 deletions(-)


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

diff --git 
a/app/src/main/java/org/wikipedia/page/leadimages/LeadImagesHandler.java 
b/app/src/main/java/org/wikipedia/page/leadimages/LeadImagesHandler.java
index 15b485a..7d91603 100755
--- a/app/src/main/java/org/wikipedia/page/leadimages/LeadImagesHandler.java
+++ b/app/src/main/java/org/wikipedia/page/leadimages/LeadImagesHandler.java
@@ -18,19 +18,21 @@
 import org.json.JSONException;
 import org.json.JSONObject;
 import org.wikipedia.R;
+import org.wikipedia.WikipediaApp;
 import org.wikipedia.analytics.GalleryFunnel;
+import org.wikipedia.bridge.CommunicationBridge;
+import org.wikipedia.concurrency.CallbackTask;
 import org.wikipedia.page.Page;
 import org.wikipedia.page.PageActivity;
-import org.wikipedia.page.PageTitle;
-import org.wikipedia.WikipediaApp;
-import org.wikipedia.bridge.CommunicationBridge;
 import org.wikipedia.page.PageFragment;
+import org.wikipedia.page.PageTitle;
 import org.wikipedia.page.gallery.GalleryActivity;
+import org.wikipedia.readinglist.AddToReadingListDialog;
+import org.wikipedia.readinglist.ReadingList;
+import org.wikipedia.readinglist.page.database.ReadingListDaoProxy;
 import org.wikipedia.savedpages.DeleteSavedPageTask;
 import org.wikipedia.savedpages.SavePageTask;
 import org.wikipedia.savedpages.SavedPage;
-import org.wikipedia.readinglist.AddToReadingListDialog;
-import org.wikipedia.readinglist.ReadingList;
 import org.wikipedia.util.DimenUtil;
 import org.wikipedia.util.FeedbackUtil;
 import org.wikipedia.util.ReleaseUtil;
@@ -107,10 +109,12 @@
     }
 
     public void updateBookmark() {
-
-        // TODO: DAO interaction (does this page exist in one or more lists?)
-
-        
articleHeaderView.updateBookmark(ReadingList.DAO.anyListContainsTitle(getTitle()));
+        
ReadingList.DAO.anyListContainsTitleAsync(ReadingListDaoProxy.key(getTitle()),
+                new CallbackTask.Callback<Boolean>() {
+            @Override public void success(@NonNull Boolean bookmarked) {
+                articleHeaderView.updateBookmark(bookmarked);
+            }
+        });
     }
 
     public void updateNavigate(@Nullable Location geo) {
diff --git 
a/app/src/main/java/org/wikipedia/readinglist/AddToReadingListDialog.java 
b/app/src/main/java/org/wikipedia/readinglist/AddToReadingListDialog.java
index cc64c98..b3d347b 100644
--- a/app/src/main/java/org/wikipedia/readinglist/AddToReadingListDialog.java
+++ b/app/src/main/java/org/wikipedia/readinglist/AddToReadingListDialog.java
@@ -15,9 +15,12 @@
 import org.wikipedia.analytics.ReadingListsFunnel;
 import org.wikipedia.model.EnumCode;
 import org.wikipedia.model.EnumCodeMap;
+import org.wikipedia.concurrency.CallbackTask;
 import org.wikipedia.page.ExtendedBottomSheetDialogFragment;
 import org.wikipedia.page.PageActivity;
 import org.wikipedia.page.PageTitle;
+import org.wikipedia.readinglist.page.ReadingListPage;
+import org.wikipedia.readinglist.page.database.ReadingListDaoProxy;
 import org.wikipedia.util.FeedbackUtil;
 
 import java.util.ArrayList;
@@ -131,41 +134,63 @@
     }
 
     private void updateLists() {
-        readingLists = ReadingList.DAO.queryMruLists();
-        adapter.notifyDataSetChanged();
+        ReadingList.DAO.queryMruLists(new 
CallbackTask.Callback<List<ReadingList>>() {
+            @Override
+            public void success(List<ReadingList> rows) {
+                readingLists = rows;
+                adapter.notifyDataSetChanged();
+            }
+        });
     }
 
     private class CreateButtonClickListener implements View.OnClickListener {
         @Override
         public void onClick(View v) {
-            final ReadingList readingList = new ReadingList();
-            readingList.setTitle(getString(R.string.reading_list_name_sample));
-            AlertDialog dialog = 
ReadingListDialogs.createEditDialog(getContext(), readingList, false, new 
Runnable() {
+            String title = getString(R.string.reading_list_name_sample);
+            long now = System.currentTimeMillis();
+            final ReadingList list = ReadingList
+                    .builder()
+                    .key(ReadingListDaoProxy.listKey(title))
+                    .title(title)
+                    .mtime(now)
+                    .atime(now)
+                    .description(null)
+                    .pages(new ArrayList<ReadingListPage>())
+                    .build();
+            AlertDialog dialog = 
ReadingListDialogs.createEditDialog(getContext(), list, false, new Runnable() {
                 @Override
                 public void run() {
-                    addAndDismiss(readingList);
+                    ReadingList.DAO.addListAsync(list);
+                    addAndDismiss(list);
                 }
             }, null);
             dialog.show();
         }
     }
 
-    private void addAndDismiss(ReadingList readingList) {
-        if (ReadingList.DAO.listContainsTitle(readingList, pageTitle)) {
-            ((PageActivity) getActivity())
-                    
.showReadingListAddedSnackbar(getString(R.string.reading_list_already_exists), 
isOnboarding);
-        } else {
-            ((PageActivity) getActivity())
-                    
.showReadingListAddedSnackbar(TextUtils.isEmpty(readingList.getTitle())
-                            ? getString(R.string.reading_list_added_to_unnamed)
-                            : 
String.format(getString(R.string.reading_list_added_to_named),
-                            readingList.getTitle()), isOnboarding);
+    private void addAndDismiss(final ReadingList readingList) {
+        final ReadingListPage page = ReadingListDaoProxy.page(readingList, 
pageTitle);
+        ReadingList.DAO.listContainsTitleAsync(readingList, page, new 
CallbackTask.Callback<Boolean>() {
+            @Override
+            public void success(Boolean contains) {
+                if (isAdded()) {
+                    if (contains) {
+                        ((PageActivity) getActivity())
+                                
.showReadingListAddedSnackbar(getString(R.string.reading_list_already_exists), 
isOnboarding);
+                    } else {
+                        ((PageActivity) getActivity())
+                                
.showReadingListAddedSnackbar(TextUtils.isEmpty(readingList.getTitle())
+                                        ? 
getString(R.string.reading_list_added_to_unnamed)
+                                        : 
String.format(getString(R.string.reading_list_added_to_named),
+                                        readingList.getTitle()), isOnboarding);
 
-            new 
ReadingListsFunnel(pageTitle.getSite()).logAddToList(readingList, 
readingLists.size(), invokeSource);
-            ReadingList.DAO.addTitleToList(readingList, pageTitle);
-        }
-
-        ReadingList.DAO.makeListMostRecent(readingList);
+                        new 
ReadingListsFunnel(pageTitle.getSite()).logAddToList(readingList, 
readingLists.size(), invokeSource);
+                        ReadingList.DAO.makeListMostRecent(readingList);
+                    }
+                }
+            }
+        });
+        ReadingList.DAO.addTitleToList(readingList, page);
         dismiss();
     }
 
diff --git a/app/src/main/java/org/wikipedia/readinglist/ReadingList.java 
b/app/src/main/java/org/wikipedia/readinglist/ReadingList.java
index 0b45427..b701ddc 100644
--- a/app/src/main/java/org/wikipedia/readinglist/ReadingList.java
+++ b/app/src/main/java/org/wikipedia/readinglist/ReadingList.java
@@ -1,53 +1,115 @@
 package org.wikipedia.readinglist;
 
-import org.wikipedia.page.PageTitle;
+import android.database.Cursor;
+import android.support.annotation.NonNull;
+
+import org.apache.commons.lang3.Validate;
+import org.wikipedia.database.contract.ReadingListContract;
+import org.wikipedia.readinglist.database.ReadingListRow;
+import org.wikipedia.readinglist.page.ReadingListPage;
 
 import java.util.ArrayList;
 import java.util.List;
 
-public class ReadingList {
-    public static final ReadingListData.ReadingListDao DAO = new 
ReadingListFakeData();
+public final class ReadingList extends ReadingListRow {
+    @NonNull private final List<ReadingListPage> pages;
 
-    private String title;
-    private String description;
-    private boolean saveOffline = true;
-    private List<PageTitle> pages;
+    public static ReadingList fromCursor(@NonNull Cursor cursor) {
+        ReadingListRow list = ReadingList.DATABASE_TABLE.fromCursor(cursor);
+        List<ReadingListPage> pages = new ArrayList<>();
 
-    public ReadingList() {
-        this("", "", new ArrayList<PageTitle>());
+        cursor.moveToPrevious();
+        while (cursor.moveToNext()) {
+            ReadingListRow curList = 
ReadingList.DATABASE_TABLE.fromCursor(cursor);
+            if (!curList.key().equals(list.key())) {
+                cursor.moveToPrevious();
+                break;
+            }
+
+            boolean hasRow = 
ReadingListContract.ListWithPagesAndDisk.PAGE_KEY.val(cursor) != null;
+            if (hasRow) {
+                ReadingListPage page = ReadingListPage.fromCursor(cursor);
+                pages.add(page);
+            }
+        }
+
+        return ReadingList
+                .builder()
+                .copy(list)
+                .pages(pages)
+                .build();
     }
 
-    public ReadingList(String title, String description, List<PageTitle> 
pages) {
-        this.title = title;
-        this.description = description;
-        this.pages = pages;
+    public static Builder builder() {
+        return new Builder();
     }
 
-    public String getTitle() {
-        return title;
-    }
-
-    public void setTitle(String title) {
-        this.title = title;
-    }
-
-    public String getDescription() {
-        return description;
-    }
-
-    public void setDescription(String description) {
-        this.description = description;
-    }
-
-    public List<PageTitle> getPages() {
+    @NonNull public List<ReadingListPage> getPages() {
         return pages;
     }
 
-    public boolean getSaveOffline() {
-        return saveOffline;
+    public void remove(@NonNull ReadingListPage page) {
+        for (ReadingListPage p : pages) {
+            if (p.key().equals(page.key())) {
+                pages.remove(p);
+                return;
+            }
+        }
     }
 
-    public void setSaveOffline(boolean saveOffline) {
-        this.saveOffline = saveOffline;
+    public void add(@NonNull ReadingListPage page) {
+        for (ReadingListPage p : pages) {
+            if (p.key().equals(page.key())) {
+                return;
+            }
+        }
+        pages.add(0, page);
     }
-}
+
+    public void setTitle(@NonNull String title) {
+        title(title);
+    }
+
+    public void setDescription(@NonNull String description) {
+        description(description);
+    }
+
+    public void setSaveOffline(boolean saved) {
+        for (ReadingListPage page : pages) {
+            page.savedOrSaving(saved);
+        }
+    }
+
+    public boolean getSaveOffline() {
+        for (ReadingListPage page : pages) {
+            if (!page.savedOrSaving()) {
+                return false;
+            }
+        }
+        return true;
+    }
+
+    private ReadingList(@NonNull Builder builder) {
+        super(builder);
+        pages = new ArrayList<>(builder.pages);
+    }
+
+    public static class Builder extends ReadingListRow.Builder<Builder> {
+        private List<ReadingListPage> pages;
+
+        public Builder pages(@NonNull List<ReadingListPage> pages) {
+            this.pages = new ArrayList<>(pages);
+            return this;
+        }
+
+        @Override public ReadingList build() {
+            validate();
+            return new ReadingList(this);
+        }
+
+        @Override protected void validate() {
+            super.validate();
+            Validate.notNull(pages);
+        }
+    }
+}
\ No newline at end of file
diff --git a/app/src/main/java/org/wikipedia/readinglist/ReadingListData.java 
b/app/src/main/java/org/wikipedia/readinglist/ReadingListData.java
index 2ba0e2a..306d298 100644
--- a/app/src/main/java/org/wikipedia/readinglist/ReadingListData.java
+++ b/app/src/main/java/org/wikipedia/readinglist/ReadingListData.java
@@ -1,25 +1,191 @@
 package org.wikipedia.readinglist;
 
+import android.database.Cursor;
+import android.net.Uri;
 import android.support.annotation.NonNull;
 
-import org.wikipedia.page.PageTitle;
+import org.wikipedia.WikipediaApp;
+import org.wikipedia.concurrency.CallbackTask;
+import org.wikipedia.database.DatabaseClient;
+import org.wikipedia.database.contract.ReadingListContract;
+import org.wikipedia.readinglist.database.ReadingListRow;
+import org.wikipedia.readinglist.page.ReadingListPage;
+import org.wikipedia.readinglist.page.database.ReadingListPageDao;
 
-@SuppressWarnings("checkstyle:interfaceistype")
-public interface ReadingListData {
-    interface List {
-        @NonNull java.util.List<ReadingList> queryMruLists();
-        void addList(ReadingList list);
-        void removeList(ReadingList list);
-        void makeListMostRecent(ReadingList list);
-        void saveListInfo(ReadingList list);
+import java.util.ArrayList;
+import java.util.List;
+
+public final class ReadingListData {
+    private static final ReadingListData INSTANCE = new ReadingListData();
+
+    public static ReadingListData instance() {
+        return INSTANCE;
     }
 
-    interface Page {
-        void addTitleToList(ReadingList list, PageTitle title);
-        void removeTitleFromList(ReadingList list, PageTitle title);
-        boolean listContainsTitle(ReadingList list, PageTitle title);
-        boolean anyListContainsTitle(PageTitle title);
+    public void queryMruLists(@NonNull 
CallbackTask.Callback<List<ReadingList>> callback) {
+        CallbackTask.execute(new CallbackTask.Task<List<ReadingList>>() {
+            @Override public List<ReadingList> execute() {
+                List<ReadingList> rows = new ArrayList<>();
+                Cursor cursor = lists();
+                try {
+                    while (cursor.moveToNext()) {
+                        rows.add(ReadingList.fromCursor(cursor));
+                    }
+                } finally {
+                    cursor.close();
+                }
+                return rows;
+            }
+        }, callback);
     }
 
-    interface ReadingListDao extends List, Page {}
+    @NonNull public Cursor lists() {
+        Uri uri = ReadingListContract.ListWithPagesAndDisk.URI;
+        final String selection = null;
+        final String[] selectionArgs = null;
+        String order = ReadingListContract.ListWithPagesAndDisk.ORDER_KEY + ','
+                + ReadingListContract.ListWithPagesAndDisk.ORDER_MRU;
+        return listClient().select(uri, selection, selectionArgs, order);
+    }
+
+    public void addListAsync(@NonNull final ReadingList list) {
+        CallbackTask.execute(new CallbackTask.Task<Void>() {
+            @Override public Void execute() {
+                addList(list);
+                return null;
+            }
+        });
+    }
+
+    public void removeListAsync(@NonNull final ReadingList list) {
+        CallbackTask.execute(new CallbackTask.Task<Void>() {
+            @Override public Void execute() {
+                removeList(list);
+                return null;
+            }
+        });
+    }
+
+    public void makeListMostRecent(@NonNull final ReadingList list) {
+        long now = System.currentTimeMillis();
+        list.atime(now);
+
+        CallbackTask.execute(new CallbackTask.Task<Void>() {
+            @Override public Void execute() {
+                saveListInfo(list);
+                return null;
+            }
+        });
+    }
+
+    public void saveListInfoAsync(@NonNull final ReadingList list) {
+        CallbackTask.execute(new CallbackTask.Task<Void>() {
+            @Override public Void execute() {
+                saveListInfo(list);
+                return null;
+            }
+        });
+    }
+
+    public void addTitleToList(@NonNull final ReadingList list,
+                               @NonNull final ReadingListPage page) {
+        list.add(page);
+        page.addListKey(list.key());
+
+        CallbackTask.execute(new CallbackTask.Task<Void>() {
+            @Override public Void execute() {
+                saveListInfo(list, page);
+                return null;
+            }
+        });
+    }
+
+    public void removeTitleFromList(@NonNull final ReadingList list,
+                                    @NonNull final ReadingListPage page) {
+        list.remove(page);
+        page.removeListKey(list.key());
+
+        CallbackTask.execute(new CallbackTask.Task<Void>() {
+            @Override public Void execute() {
+                saveListInfo(list, page);
+                return null;
+            }
+        });
+    }
+
+    public void listContainsTitleAsync(@NonNull final ReadingList list,
+                                       @NonNull final ReadingListPage page,
+                                       @NonNull CallbackTask.Callback<Boolean> 
callback) {
+        CallbackTask.execute(new CallbackTask.Task<Boolean>() {
+            @Override public Boolean execute() {
+                return listContainsTitle(list.key(), page.key());
+            }
+        }, callback);
+    }
+
+    public void anyListContainsTitleAsync(@NonNull final String key,
+                                          @NonNull 
CallbackTask.Callback<Boolean> callback) {
+        CallbackTask.execute(new CallbackTask.Task<Boolean>() {
+            @Override public Boolean execute() {
+                return anyListContainsTitle(key);
+            }
+        }, callback);
+    }
+
+    private synchronized boolean anyListContainsTitle(String key) {
+        Cursor cursor = ReadingListPageDao.instance().page(key);
+        try {
+            return cursor.getCount() != 0;
+        } finally {
+            cursor.close();
+        }
+    }
+
+    private synchronized void saveListInfo(@NonNull ReadingList list) {
+        listClient().persist(list);
+    }
+
+    private synchronized void saveListInfo(@NonNull ReadingList list, @NonNull 
ReadingListPage page) {
+        listClient().persist(list);
+        ReadingListPageDao.instance().upsert(page);
+    }
+
+    private synchronized void addList(@NonNull ReadingList list) {
+        listClient().persist(list);
+        for (ReadingListPage page : list.getPages()) {
+            page.addListKey(list.key());
+            ReadingListPageDao.instance().upsert(page);
+        }
+    }
+
+    private synchronized void removeList(@NonNull ReadingList list) {
+        listClient().delete(list, 
listClient().getPrimaryKeySelectionArgs(list));
+        for (ReadingListPage page : list.getPages()) {
+            page.removeListKey(list.key());
+            ReadingListPageDao.instance().upsert(page);
+        }
+    }
+
+    private synchronized boolean listContainsTitle(@NonNull String listKey, 
@NonNull String key) {
+        Cursor cursor = ReadingListPageDao.instance().pages(listKey);
+        try {
+            while (cursor.moveToNext()) {
+                ReadingListPage page = ReadingListPage.fromCursor(cursor);
+                if (page.key().equals(key)) {
+                    return true;
+                }
+            }
+            return false;
+        } finally {
+            cursor.close();
+        }
+    }
+
+    private DatabaseClient<ReadingListRow> listClient() {
+        return client(ReadingListRow.class);
+    }
+
+    private <T> DatabaseClient<T> client(Class<T> clazz) {
+        return WikipediaApp.getInstance().getDatabaseClient(clazz);
+    }
 }
\ No newline at end of file
diff --git 
a/app/src/main/java/org/wikipedia/readinglist/ReadingListDetailView.java 
b/app/src/main/java/org/wikipedia/readinglist/ReadingListDetailView.java
index 565a866..e0c6bee 100644
--- a/app/src/main/java/org/wikipedia/readinglist/ReadingListDetailView.java
+++ b/app/src/main/java/org/wikipedia/readinglist/ReadingListDetailView.java
@@ -27,7 +27,7 @@
 import com.facebook.drawee.view.SimpleDraweeView;
 
 import org.wikipedia.R;
-import org.wikipedia.page.PageTitle;
+import org.wikipedia.readinglist.page.ReadingListPage;
 import org.wikipedia.util.DimenUtil;
 import org.wikipedia.util.FeedbackUtil;
 import org.wikipedia.util.ResourceUtil;
@@ -53,9 +53,9 @@
     private Bitmap deleteIcon = getDeleteBitmap();
 
     public interface ReadingListItemActionListener {
-        void onClick(ReadingList readingList, PageTitle title);
-        void onLongClick(ReadingList readingList, PageTitle title);
-        void onDelete(ReadingList readingList, PageTitle title);
+        void onClick(ReadingList readingList, ReadingListPage page);
+        void onLongClick(ReadingList readingList, ReadingListPage page);
+        void onDelete(ReadingList readingList, ReadingListPage page);
     }
 
     public interface ReadingListActionListener {
@@ -170,7 +170,7 @@
     }
 
     private class ReadingListPageItemHolder extends RecyclerView.ViewHolder 
implements OnClickListener, OnLongClickListener {
-        private PageTitle pageTitle;
+        private ReadingListPage page;
         private View containerView;
         private TextView titleView;
         private SimpleDraweeView thumbnailView;
@@ -186,31 +186,31 @@
             containerView.setOnClickListener(this);
         }
 
-        public void bindItem(PageTitle title) {
-            this.pageTitle = title;
-            titleView.setText(title.getDisplayText());
-            descriptionView.setText(title.getDescription());
-            ViewUtil.loadImageUrlInto(thumbnailView, title.getThumbUrl());
+        public void bindItem(ReadingListPage page) {
+            this.page = page;
+            titleView.setText(page.title());
+            descriptionView.setText(page.description());
+            ViewUtil.loadImageUrlInto(thumbnailView, page.thumbnailUrl());
         }
 
         @Override
         public void onClick(View v) {
             if (itemActionListener != null) {
-                itemActionListener.onClick(readingList, pageTitle);
+                itemActionListener.onClick(readingList, page);
             }
         }
 
         @Override
         public boolean onLongClick(View v) {
             if (itemActionListener != null) {
-                itemActionListener.onLongClick(readingList, pageTitle);
+                itemActionListener.onLongClick(readingList, page);
             }
             return true;
         }
 
         public void onDismiss() {
             if (itemActionListener != null) {
-                itemActionListener.onDelete(readingList, pageTitle);
+                itemActionListener.onDelete(readingList, page);
             }
             updateDetails();
         }
diff --git 
a/app/src/main/java/org/wikipedia/readinglist/ReadingListFakeData.java 
b/app/src/main/java/org/wikipedia/readinglist/ReadingListFakeData.java
deleted file mode 100644
index e72f6ac..0000000
--- a/app/src/main/java/org/wikipedia/readinglist/ReadingListFakeData.java
+++ /dev/null
@@ -1,70 +0,0 @@
-package org.wikipedia.readinglist;
-
-import android.support.annotation.NonNull;
-
-import org.wikipedia.page.PageTitle;
-
-import java.util.ArrayList;
-import java.util.List;
-
-public final class ReadingListFakeData implements 
ReadingListData.ReadingListDao {
-
-    private static final List<ReadingList> LISTS = new ArrayList<>();
-
-    @Override
-    @NonNull
-    public List<ReadingList> queryMruLists() {
-        return LISTS;
-    }
-
-    @Override
-    public void addList(ReadingList list) {
-        LISTS.add(0, list);
-    }
-
-    @Override
-    public void removeList(ReadingList list) {
-        LISTS.remove(list);
-    }
-
-    @Override
-    public void makeListMostRecent(ReadingList list) {
-        LISTS.remove(list);
-        LISTS.add(0, list);
-    }
-
-    @Override
-    public void saveListInfo(ReadingList list) {
-        // commit list details to DB. (name, description, whether saved 
offline)
-    }
-
-    @Override
-    public void addTitleToList(ReadingList list, PageTitle title) {
-        list.getPages().add(0, title);
-    }
-
-    @Override
-    public void removeTitleFromList(ReadingList list, PageTitle title) {
-        list.getPages().remove(title);
-    }
-
-    @Override
-    public boolean listContainsTitle(ReadingList list, PageTitle title) {
-        for (PageTitle p : list.getPages()) {
-            if (p.equals(title)) {
-                return true;
-            }
-        }
-        return false;
-    }
-
-    @Override
-    public boolean anyListContainsTitle(PageTitle title) {
-        for (ReadingList list : LISTS) {
-            if (listContainsTitle(list, title)) {
-                return true;
-            }
-        }
-        return false;
-    }
-}
\ No newline at end of file
diff --git 
a/app/src/main/java/org/wikipedia/readinglist/ReadingListImageFetcher.java 
b/app/src/main/java/org/wikipedia/readinglist/ReadingListImageFetcher.java
index ac59ebe..24e2f23 100644
--- a/app/src/main/java/org/wikipedia/readinglist/ReadingListImageFetcher.java
+++ b/app/src/main/java/org/wikipedia/readinglist/ReadingListImageFetcher.java
@@ -2,12 +2,17 @@
 
 import android.support.annotation.NonNull;
 
+import org.mediawiki.api.json.Api;
 import org.wikipedia.Site;
 import org.wikipedia.WikipediaApp;
 import org.wikipedia.page.PageTitle;
 import org.wikipedia.pageimages.PageImagesTask;
+import org.wikipedia.readinglist.page.ReadingListPage;
+import org.wikipedia.readinglist.page.database.ReadingListPageDao;
+import org.wikipedia.readinglist.page.database.ReadingListDaoProxy;
 import org.wikipedia.util.log.L;
 
+import java.util.List;
 import java.util.Map;
 
 public final class ReadingListImageFetcher {
@@ -22,16 +27,25 @@
         if (readingList.getPages().isEmpty()) {
             return;
         }
-        Site site = readingList.getPages().get(0).getSite();
-        (new PageImagesTask(WikipediaApp.getInstance().getAPIForSite(site), 
site, readingList.getPages(), WikipediaApp.PREFERRED_THUMB_SIZE) {
+        Site site = readingList.getPages().get(0).site();
+        Api api = WikipediaApp.getInstance().getAPIForSite(site);
+        List<PageTitle> titles = 
ReadingListDaoProxy.pageTitles(readingList.getPages());
+        new PageImagesTask(api, site, titles, 
WikipediaApp.PREFERRED_THUMB_SIZE) {
             @Override
             public void onFinish(Map<PageTitle, String> result) {
-                for (PageTitle title : readingList.getPages()) {
+                for (ReadingListPage page : readingList.getPages()) {
+                    PageTitle title = ReadingListDaoProxy.pageTitle(page);
                     if (result.containsKey(title)) {
                         // update this thumbnail in the db?
                         //PageImage pi = new PageImage(model.getTitle(), 
result.get(model.getTitle()));
                         //app.getDatabaseClient(PageImage.class).upsert(pi, 
PageImageDatabaseTable.Col.SELECTION);
-                        title.setThumbUrl(result.get(title));
+                        //page.setThumbUrl(result.get(title));
+                        ReadingListPage copy = ReadingListPage
+                            .builder()
+                            .copy(page)
+                            .thumbnailUrl(result.get(title))
+                            .build();
+                        ReadingListPageDao.instance().upsertAsync(copy);
                     }
                 }
                 listener.onComplete();
@@ -42,9 +56,10 @@
                 L.w(caught);
                 listener.onError(caught);
             }
-        }).execute();
+        }.execute();
     }
 
+
     private ReadingListImageFetcher() {
     }
 }
diff --git 
a/app/src/main/java/org/wikipedia/readinglist/ReadingListItemView.java 
b/app/src/main/java/org/wikipedia/readinglist/ReadingListItemView.java
index af1b65c..444bd54 100644
--- a/app/src/main/java/org/wikipedia/readinglist/ReadingListItemView.java
+++ b/app/src/main/java/org/wikipedia/readinglist/ReadingListItemView.java
@@ -16,7 +16,7 @@
 import com.facebook.drawee.view.SimpleDraweeView;
 
 import org.wikipedia.R;
-import org.wikipedia.page.PageTitle;
+import org.wikipedia.readinglist.page.ReadingListPage;
 import org.wikipedia.views.ViewUtil;
 
 import java.util.ArrayList;
@@ -127,9 +127,9 @@
     private void updateThumbnails() {
         clearThumbnails();
         List<String> thumbUrls = new ArrayList<>();
-        for (PageTitle title : readingList.getPages()) {
-            if (!TextUtils.isEmpty(title.getThumbUrl())) {
-                thumbUrls.add(title.getThumbUrl());
+        for (ReadingListPage page : readingList.getPages()) {
+            if (!TextUtils.isEmpty(page.thumbnailUrl())) {
+                thumbUrls.add(page.thumbnailUrl());
             }
         }
         int thumbIndex = 0;
diff --git 
a/app/src/main/java/org/wikipedia/readinglist/ReadingListsFragment.java 
b/app/src/main/java/org/wikipedia/readinglist/ReadingListsFragment.java
index 40f7ce0..0b91844 100644
--- a/app/src/main/java/org/wikipedia/readinglist/ReadingListsFragment.java
+++ b/app/src/main/java/org/wikipedia/readinglist/ReadingListsFragment.java
@@ -14,9 +14,12 @@
 import org.wikipedia.BackPressedHandler;
 import org.wikipedia.R;
 import org.wikipedia.analytics.ReadingListsFunnel;
+import org.wikipedia.concurrency.CallbackTask;
 import org.wikipedia.history.HistoryEntry;
 import org.wikipedia.page.PageActivity;
 import org.wikipedia.page.PageTitle;
+import org.wikipedia.readinglist.page.ReadingListPage;
+import org.wikipedia.readinglist.page.database.ReadingListDaoProxy;
 import org.wikipedia.util.FeedbackUtil;
 
 import java.util.ArrayList;
@@ -85,9 +88,14 @@
     }
 
     private void updateLists() {
-        readingLists = ReadingList.DAO.queryMruLists();
-        adapter.notifyDataSetChanged();
-        updateEmptyMessage();
+        ReadingList.DAO.queryMruLists(new 
CallbackTask.Callback<List<ReadingList>>() {
+            @Override
+            public void success(List<ReadingList> rows) {
+                readingLists = rows;
+                adapter.notifyDataSetChanged();
+                updateEmptyMessage();
+            }
+        });
     }
 
     private void updateEmptyMessage() {
@@ -97,14 +105,14 @@
     private class ReadingListActionListener implements 
ReadingListDetailView.ReadingListActionListener {
         @Override
         public void onUpdate(ReadingList readingList) {
-            ReadingList.DAO.saveListInfo(readingList);
+            ReadingList.DAO.saveListInfoAsync(readingList);
             funnel.logModifyList(readingList, readingLists.size());
         }
 
         @Override
         public void onDelete(ReadingList readingList) {
             showDeleteListUndoSnackbar(readingList);
-            ReadingList.DAO.removeList(readingList);
+            ReadingList.DAO.removeListAsync(readingList);
             funnel.logDeleteList(readingList, readingLists.size());
             pager.setCurrentItem(0);
             updateLists();
@@ -180,22 +188,24 @@
 
     private class ReadingListItemActionListener implements 
ReadingListDetailView.ReadingListItemActionListener {
         @Override
-        public void onClick(ReadingList readingList, PageTitle title) {
-            HistoryEntry newEntry = new HistoryEntry(title, 
HistoryEntry.SOURCE_READING_LIST);
+        public void onClick(ReadingList readingList, ReadingListPage page) {
+            PageTitle title = ReadingListDaoProxy.pageTitle(page);
+            HistoryEntry newEntry = new HistoryEntry(title,
+                    HistoryEntry.SOURCE_READING_LIST);
             ((PageActivity) getActivity()).loadPage(title, newEntry);
 
             ReadingList.DAO.makeListMostRecent(readingList);
         }
 
         @Override
-        public void onLongClick(ReadingList readingList, PageTitle title) {
+        public void onLongClick(ReadingList readingList, ReadingListPage page) 
{
             // TODO: implement integration with PageLongPressHandler
         }
 
         @Override
-        public void onDelete(ReadingList readingList, PageTitle title) {
-            showDeleteItemUndoSnackbar(readingList, title);
-            ReadingList.DAO.removeTitleFromList(readingList, title);
+        public void onDelete(ReadingList readingList, ReadingListPage page) {
+            showDeleteItemUndoSnackbar(readingList, page);
+            ReadingList.DAO.removeTitleFromList(readingList, page);
             funnel.logDeleteItem(readingList, readingLists.size());
             updateLists();
         }
@@ -208,22 +218,22 @@
         snackbar.setAction(R.string.reading_list_item_delete_undo, new 
View.OnClickListener() {
             @Override
             public void onClick(View v) {
-                ReadingList.DAO.addList(readingList);
+                ReadingList.DAO.addListAsync(readingList);
                 updateLists();
             }
         });
         snackbar.show();
     }
 
-    private void showDeleteItemUndoSnackbar(final ReadingList readingList, 
final PageTitle title) {
+    private void showDeleteItemUndoSnackbar(final ReadingList readingList, 
final ReadingListPage page) {
         Snackbar snackbar = FeedbackUtil.makeSnackbar(getView(),
-                String.format(getString(R.string.reading_list_item_deleted), 
title.getDisplayText()),
+                String.format(getString(R.string.reading_list_item_deleted), 
page.title()),
                 FeedbackUtil.LENGTH_DEFAULT);
         snackbar.setAction(R.string.reading_list_item_delete_undo, new 
View.OnClickListener() {
             @Override
             public void onClick(View v) {
 
-                ReadingList.DAO.addTitleToList(readingList, title);
+                ReadingList.DAO.addTitleToList(readingList, page);
                 listDetailView.updateDetails();
                 adapter.notifyDataSetChanged();
 
diff --git 
a/app/src/main/java/org/wikipedia/readinglist/database/ReadingListRow.java 
b/app/src/main/java/org/wikipedia/readinglist/database/ReadingListRow.java
index 240919d..7044e14 100644
--- a/app/src/main/java/org/wikipedia/readinglist/database/ReadingListRow.java
+++ b/app/src/main/java/org/wikipedia/readinglist/database/ReadingListRow.java
@@ -4,16 +4,19 @@
 import android.support.annotation.Nullable;
 
 import org.wikipedia.model.BaseModel;
+import org.wikipedia.readinglist.ReadingListData;
+import org.wikipedia.readinglist.page.database.ReadingListDaoProxy;
 import org.wikipedia.util.ValidateUtil;
 
 public class ReadingListRow extends BaseModel {
+    public static final ReadingListData DAO = new ReadingListData();
     public static final ReadingListTable DATABASE_TABLE = new 
ReadingListTable();
 
-    @NonNull private final String key;
-    @NonNull private final String title;
+    @NonNull private String key;
+    @NonNull private String title;
     private final long mtime;
-    private final long atime;
-    @Nullable private final String description;
+    private long atime;
+    @Nullable private String description;
 
     public static Builder<?> builder() {
         //noinspection rawtypes
@@ -28,6 +31,11 @@
         return title;
     }
 
+    public void title(@NonNull String title) {
+        this.title = title;
+        key = ReadingListDaoProxy.listKey(title);
+    }
+
     public long mtime() {
         return mtime;
     }
@@ -36,10 +44,18 @@
         return atime;
     }
 
+    public void atime(long atime) {
+        this.atime = atime;
+    }
+
     @Nullable public String getDescription() {
         return description;
     }
 
+    public void description(@Nullable String description) {
+        this.description = description;
+    }
+
     protected ReadingListRow(@NonNull Builder<?> builder) {
         key = builder.key;
         title = builder.title;
diff --git 
a/app/src/main/java/org/wikipedia/readinglist/page/ReadingListPage.java 
b/app/src/main/java/org/wikipedia/readinglist/page/ReadingListPage.java
index bc9d423..222f606 100644
--- a/app/src/main/java/org/wikipedia/readinglist/page/ReadingListPage.java
+++ b/app/src/main/java/org/wikipedia/readinglist/page/ReadingListPage.java
@@ -8,7 +8,7 @@
 import org.wikipedia.readinglist.page.database.disk.ReadingListDiskRow;
 
 public final class ReadingListPage extends ReadingListPageRow {
-    @NonNull private final DiskStatus diskStatus;
+    @NonNull private DiskStatus diskStatus;
 
     public static ReadingListPage fromCursor(@NonNull Cursor cursor) {
         ReadingListDiskRow diskRow = 
ReadingListPage.DISK_DATABASE_TABLE.fromCursor(cursor);
@@ -29,6 +29,14 @@
 
     public boolean savedOrSaving() {
         return diskStatus.savedOrSaving();
+    }
+
+    public void savedOrSaving(boolean saved) {
+        if (saved) {
+            diskStatus = diskStatus == DiskStatus.SAVED ? DiskStatus.SAVED : 
DiskStatus.OUTDATED;
+        } else {
+            diskStatus = diskStatus == DiskStatus.ONLINE ? DiskStatus.ONLINE : 
DiskStatus.UNSAVED;
+        }
     }
 
     private ReadingListPage(@NonNull Builder builder) {
@@ -60,4 +68,4 @@
             Validate.notNull(diskStatus);
         }
     }
-}
+}
\ No newline at end of file
diff --git 
a/app/src/main/java/org/wikipedia/readinglist/page/ReadingListPageRow.java 
b/app/src/main/java/org/wikipedia/readinglist/page/ReadingListPageRow.java
index 2bff678..a1b6728 100644
--- a/app/src/main/java/org/wikipedia/readinglist/page/ReadingListPageRow.java
+++ b/app/src/main/java/org/wikipedia/readinglist/page/ReadingListPageRow.java
@@ -49,6 +49,14 @@
         return Collections.unmodifiableSet(listKeys);
     }
 
+    public void addListKey(@NonNull String listKey) {
+        listKeys.add(listKey);
+    }
+
+    public void removeListKey(@NonNull String listKey) {
+        listKeys.remove(listKey);
+    }
+
     @NonNull public Namespace namespace() {
         return namespace;
     }
diff --git 
a/app/src/main/java/org/wikipedia/readinglist/page/database/ReadingListDaoProxy.java
 
b/app/src/main/java/org/wikipedia/readinglist/page/database/ReadingListDaoProxy.java
new file mode 100644
index 0000000..e4e2229
--- /dev/null
+++ 
b/app/src/main/java/org/wikipedia/readinglist/page/database/ReadingListDaoProxy.java
@@ -0,0 +1,68 @@
+package org.wikipedia.readinglist.page.database;
+
+import android.support.annotation.NonNull;
+import android.util.Base64;
+
+import org.wikipedia.page.Namespace;
+import org.wikipedia.page.PageTitle;
+import org.wikipedia.readinglist.ReadingList;
+import org.wikipedia.readinglist.page.ReadingListPage;
+import org.wikipedia.readinglist.page.database.disk.DiskStatus;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+
+public final class ReadingListDaoProxy {
+
+    public static List<PageTitle> pageTitles(@NonNull 
Collection<ReadingListPage> pages) {
+        List<PageTitle> titles = new ArrayList<>();
+        for (ReadingListPage page : pages) {
+            titles.add(pageTitle(page));
+        }
+        return titles;
+    }
+
+    @NonNull public static PageTitle pageTitle(@NonNull ReadingListPage page) {
+        return new PageTitle(page.title(), page.site(), page.thumbnailUrl(), 
page.description());
+    }
+
+    @NonNull public static ReadingListPage page(@NonNull ReadingList list, 
@NonNull PageTitle title) {
+        long now = System.currentTimeMillis();
+        return ReadingListPage
+                .builder()
+                .diskStatus(list.getSaveOffline() ? DiskStatus.OUTDATED : 
DiskStatus.ONLINE)
+                .key(key(title))
+                .listKeys(listKey(list))
+                .site(title.getSite())
+                // TODO: unmarshal namespace when received, not when used.
+                .namespace(title.getNamespace() == null ? Namespace.MAIN : 
Namespace.of(Integer.parseInt(title.getNamespace())))
+                .title(title.getDisplayText())
+                .diskPageRevision(title.hasProperties() ? 
title.getProperties().getRevisionId() : 0)
+                .mtime(now)
+                .atime(now)
+                .thumbnailUrl(title.hasProperties() ? 
title.getProperties().getLeadImageUrl() : null)
+                .description(title.getDescription())
+                .build();
+    }
+
+    @NonNull public static String key(@NonNull PageTitle title) {
+        // TODO: this should use the following but PageTitles often do not 
have Properties and page
+        //       ID is not preserved elsewhere.
+        // return "wikipedia-" + title.getSite().languageCode() + '-' + 
title.getProperties().getPageId();
+        return Base64.encodeToString((title.getSite().languageCode() + '-' + 
title.getDisplayText()).getBytes(),
+                Base64.NO_WRAP);
+    }
+
+    @NonNull public static String listKey(@NonNull ReadingList list) {
+        // TODO: we need to rekey all pages if a user changes the list title.
+        return listKey(list.getTitle());
+    }
+
+    @NonNull public static String listKey(@NonNull String title) {
+        // TODO: we need to rekey all pages if a user changes the list title.
+        return Base64.encodeToString(title.getBytes(), Base64.NO_WRAP);
+    }
+
+    private ReadingListDaoProxy() { }
+}
\ No newline at end of file
diff --git 
a/app/src/main/java/org/wikipedia/readinglist/page/database/ReadingListPageDao.java
 
b/app/src/main/java/org/wikipedia/readinglist/page/database/ReadingListPageDao.java
index b77d092..da435de 100644
--- 
a/app/src/main/java/org/wikipedia/readinglist/page/database/ReadingListPageDao.java
+++ 
b/app/src/main/java/org/wikipedia/readinglist/page/database/ReadingListPageDao.java
@@ -6,7 +6,6 @@
 
 import org.wikipedia.WikipediaApp;
 import org.wikipedia.concurrency.CallbackTask;
-import org.wikipedia.concurrency.CallbackTask.Callback;
 import org.wikipedia.concurrency.CallbackTask.Task;
 import org.wikipedia.database.BaseDao;
 import org.wikipedia.database.async.AsyncConstant;
@@ -27,24 +26,8 @@
     @NonNull private final HttpRowDao<ReadingListPageRow, 
ReadingListPageHttpRow> httpDao;
     @NonNull private final DiskRowDao<ReadingListPageRow, 
ReadingListPageDiskRow> diskDao;
 
-    // TODO: clients need the mechanism for generating page and list keys.
-
     public static ReadingListPageDao instance() {
         return INSTANCE;
-    }
-
-    // TODO: remove this method once client can consume Cursor form.
-    public void pageAsync(@NonNull final String key, @NonNull 
Callback<ReadingListPage> callback) {
-        CallbackTask.execute(new Task<ReadingListPage>() {
-            @Override public ReadingListPage execute() {
-                Cursor cursor = page(key);
-                try {
-                    return cursor.moveToNext() ? 
ReadingListPage.fromCursor(cursor) : null;
-                } finally {
-                    cursor.close();
-                }
-            }
-        }, callback);
     }
 
     @NonNull public Cursor page(@NonNull String key) {
@@ -53,25 +36,6 @@
         String[] selectionArgs = new String[] {key};
         String order = ReadingListPageContract.PageWithDisk.ORDER_ALPHABETICAL;
         return client().select(uri, selection, selectionArgs, order);
-    }
-
-    // TODO: remove this method once client can consume Cursor form.
-    public void pagesAsync(@NonNull final String listKey,
-                           @NonNull Callback<Collection<ReadingListPage>> 
callback) {
-        CallbackTask.execute(new Task<Collection<ReadingListPage>>() {
-            @Override public Collection<ReadingListPage> execute() {
-                Collection<ReadingListPage> rows = new ArrayList<>();
-                Cursor cursor = pages(listKey);
-                try {
-                    while (cursor.moveToNext()) {
-                        rows.add(ReadingListPage.fromCursor(cursor));
-                    }
-                } finally {
-                    cursor.close();
-                }
-                return rows;
-            }
-        }, callback);
     }
 
     @NonNull public Cursor pages(@NonNull String listKey) {

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

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

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

Reply via email to