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

Change subject: Add OfflineCard/View to to the feed
......................................................................

Add OfflineCard/View to to the feed

Adds an OfflineClient to the end of the feed content loading script, and
a corresponding card to be added to the bottom of the feed when additional
content cannot be fetched because the user is offline.

TODO: Hook up the retry button.
TODO: Add unit tests for OfflineClient.

Bug: T157381
Change-Id: I3c5375d075fc4ae8f311a67f730835bc0cff3d8b
---
M app/src/main/java/org/wikipedia/feed/FeedCoordinator.java
M app/src/main/java/org/wikipedia/feed/FeedCoordinatorBase.java
M app/src/main/java/org/wikipedia/feed/model/CardType.java
A app/src/main/java/org/wikipedia/feed/offline/OfflineCard.java
A app/src/main/java/org/wikipedia/feed/offline/OfflineCardView.java
A app/src/main/java/org/wikipedia/feed/offline/OfflineClient.java
M app/src/main/java/org/wikipedia/feed/view/FeedAdapter.java
M app/src/main/java/org/wikipedia/util/ThrowableUtil.java
A app/src/main/res/layout/view_card_offline.xml
M app/src/main/res/values-qq/strings.xml
M app/src/main/res/values/strings.xml
11 files changed, 168 insertions(+), 10 deletions(-)


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

diff --git a/app/src/main/java/org/wikipedia/feed/FeedCoordinator.java 
b/app/src/main/java/org/wikipedia/feed/FeedCoordinator.java
index e8a850a..b675a88 100644
--- a/app/src/main/java/org/wikipedia/feed/FeedCoordinator.java
+++ b/app/src/main/java/org/wikipedia/feed/FeedCoordinator.java
@@ -8,6 +8,7 @@
 import org.wikipedia.feed.becauseyouread.BecauseYouReadClient;
 import org.wikipedia.feed.continuereading.ContinueReadingClient;
 import org.wikipedia.feed.mainpage.MainPageClient;
+import org.wikipedia.feed.offline.OfflineClient;
 import org.wikipedia.feed.random.RandomClient;
 import org.wikipedia.feed.searchbar.SearchClient;
 
@@ -17,8 +18,7 @@
         super(context);
     }
 
-    @Override
-    protected void buildScript(int age) {
+    @Override protected void buildScript(int age) {
         if (age == 0) {
             addPendingClient(new SearchClient());
             addPendingClient(new AnnouncementClient());
@@ -32,5 +32,10 @@
         if (age == 0) {
             addPendingClient(new RandomClient());
         }
+
+        // OfflineClient must always come last, since we rely on the last 
request in the series
+        // being a network request (and not a disk I/O request) to determine 
online/offline state
+        // for purposes of updating the feed UI.
+        addPendingClient(new OfflineClient());
     }
 }
diff --git a/app/src/main/java/org/wikipedia/feed/FeedCoordinatorBase.java 
b/app/src/main/java/org/wikipedia/feed/FeedCoordinatorBase.java
index 7ec53d7..18389a1 100644
--- a/app/src/main/java/org/wikipedia/feed/FeedCoordinatorBase.java
+++ b/app/src/main/java/org/wikipedia/feed/FeedCoordinatorBase.java
@@ -7,6 +7,8 @@
 import org.wikipedia.dataclient.WikiSite;
 import org.wikipedia.feed.dataclient.FeedClient;
 import org.wikipedia.feed.model.Card;
+import org.wikipedia.feed.offline.OfflineCard;
+import org.wikipedia.feed.offline.OfflineClient;
 import org.wikipedia.feed.progress.ProgressCard;
 import org.wikipedia.settings.Prefs;
 
@@ -63,11 +65,15 @@
         }
         pendingClients.clear();
         cards.clear();
-        insertCard(progressCard, 0);
     }
 
     public void more(@NonNull WikiSite wiki) {
         this.wiki = wiki;
+
+        if (cards.size() == 0) {
+            insertCard(progressCard, 0);
+        }
+
         if (cards.size() > 1) {
             currentAge++;
         }
@@ -122,25 +128,30 @@
     }
 
     private class ClientRequestCallback implements FeedClient.Callback {
-        @Override
-        public void success(@NonNull List<? extends Card> cardList) {
+        @Override public void success(@NonNull List<? extends Card> cardList) {
             for (Card card : cardList) {
                 if (!isCardHidden(card)) {
-                    int progressPos = cards.indexOf(progressCard);
-                    insertCard(card, progressPos >= 0 ? progressPos : 
cards.size());
+                    appendCard(card);
                 }
             }
             //noinspection ConstantConditions
             requestNextCard(wiki);
         }
 
-        @Override
-        public void error(@NonNull Throwable caught) {
+        @Override public void error(@NonNull Throwable caught) {
+            if (caught instanceof OfflineClient.OfflineException) {
+                appendCard(new OfflineCard());
+            }
             //noinspection ConstantConditions
             requestNextCard(wiki);
         }
     }
 
+    private void appendCard(@NonNull Card card) {
+        int progressPos = cards.indexOf(progressCard);
+        insertCard(card, progressPos >= 0 ? progressPos : cards.size());
+    }
+
     private void insertCard(@NonNull Card card, int position) {
         cards.add(position, card);
         if (updateListener != null) {
diff --git a/app/src/main/java/org/wikipedia/feed/model/CardType.java 
b/app/src/main/java/org/wikipedia/feed/model/CardType.java
index e7fd770..d721e97 100644
--- a/app/src/main/java/org/wikipedia/feed/model/CardType.java
+++ b/app/src/main/java/org/wikipedia/feed/model/CardType.java
@@ -11,6 +11,7 @@
 import org.wikipedia.feed.mainpage.MainPageCardView;
 import org.wikipedia.feed.mostread.MostReadCardView;
 import org.wikipedia.feed.news.NewsListCardView;
+import org.wikipedia.feed.offline.OfflineCardView;
 import org.wikipedia.feed.progress.ProgressCardView;
 import org.wikipedia.feed.random.RandomCardView;
 import org.wikipedia.feed.searchbar.SearchCardView;
@@ -83,6 +84,11 @@
             return new AnnouncementCardView(ctx);
         }
     },
+    OFFLINE(98) {
+        @NonNull @Override public FeedCardView<?> newView(@NonNull Context 
ctx) {
+            return new OfflineCardView(ctx);
+        }
+    },
     PROGRESS(99) {
         @NonNull @Override public FeedCardView<?> newView(@NonNull Context 
ctx) {
             return new ProgressCardView(ctx);
diff --git a/app/src/main/java/org/wikipedia/feed/offline/OfflineCard.java 
b/app/src/main/java/org/wikipedia/feed/offline/OfflineCard.java
new file mode 100644
index 0000000..2bd4212
--- /dev/null
+++ b/app/src/main/java/org/wikipedia/feed/offline/OfflineCard.java
@@ -0,0 +1,12 @@
+package org.wikipedia.feed.offline;
+
+import android.support.annotation.NonNull;
+
+import org.wikipedia.feed.model.Card;
+import org.wikipedia.feed.model.CardType;
+
+public class OfflineCard extends Card {
+    @NonNull @Override public CardType type() {
+        return CardType.OFFLINE;
+    }
+}
diff --git a/app/src/main/java/org/wikipedia/feed/offline/OfflineCardView.java 
b/app/src/main/java/org/wikipedia/feed/offline/OfflineCardView.java
new file mode 100644
index 0000000..6434173
--- /dev/null
+++ b/app/src/main/java/org/wikipedia/feed/offline/OfflineCardView.java
@@ -0,0 +1,24 @@
+package org.wikipedia.feed.offline;
+
+import android.content.Context;
+import android.support.annotation.NonNull;
+import android.support.annotation.Nullable;
+import android.widget.LinearLayout;
+
+import org.wikipedia.R;
+import org.wikipedia.feed.model.Card;
+import org.wikipedia.feed.view.FeedAdapter;
+import org.wikipedia.feed.view.FeedCardView;
+
+public class OfflineCardView extends LinearLayout implements 
FeedCardView<Card> {
+    public OfflineCardView(Context context) {
+        super(context);
+        inflate(getContext(), R.layout.view_card_offline, this);
+    }
+
+    @Override public void setCard(@NonNull Card card) { }
+    @Override public Card getCard() {
+        return null;
+    }
+    @Override public void setCallback(@Nullable FeedAdapter.Callback callback) 
{ }
+}
diff --git a/app/src/main/java/org/wikipedia/feed/offline/OfflineClient.java 
b/app/src/main/java/org/wikipedia/feed/offline/OfflineClient.java
new file mode 100644
index 0000000..f8d1d35
--- /dev/null
+++ b/app/src/main/java/org/wikipedia/feed/offline/OfflineClient.java
@@ -0,0 +1,66 @@
+package org.wikipedia.feed.offline;
+
+import android.content.Context;
+import android.support.annotation.NonNull;
+import android.support.annotation.Nullable;
+
+import org.wikipedia.dataclient.WikiSite;
+import org.wikipedia.dataclient.retrofit.MwCachedService;
+import org.wikipedia.dataclient.retrofit.WikiCachedService;
+import org.wikipedia.feed.dataclient.FeedClient;
+import org.wikipedia.feed.model.Card;
+import org.wikipedia.util.ThrowableUtil;
+
+import java.util.Collections;
+
+import retrofit2.Call;
+import retrofit2.Response;
+import retrofit2.http.GET;
+import retrofit2.http.Headers;
+
+/* Hit an arbitrary known-good test URL to determine the current 
online/offline state. */
+public class OfflineClient implements FeedClient {
+    private static final String TEST_URL = "w/api.php";
+
+    @NonNull private final WikiCachedService<Service> cachedService = new 
MwCachedService<>(Service.class);
+    @Nullable private Call<Object> call;
+
+    @Override public void request(@NonNull Context context, @NonNull WikiSite 
wiki, int age,
+                                  @NonNull final Callback cb) {
+        cancel();
+        call = cachedService.service(wiki).get();
+        call.enqueue(new retrofit2.Callback<Object>() {
+            @Override public void onResponse(Call<Object> call, 
Response<Object> response) {
+                cb.success(Collections.<Card>emptyList());
+            }
+
+            @Override public void onFailure(Call<Object> call, Throwable 
caught) {
+                cb.error(ThrowableUtil.isOffline(caught) ? new 
OfflineException(caught) : caught);
+            }
+        });
+    }
+
+    @Override public void cancel() {
+        if (call == null) {
+            return;
+        }
+        call.cancel();
+        call = null;
+    }
+
+    public static class OfflineException extends RuntimeException {
+        private Throwable caught;
+        OfflineException(Throwable caught) {
+            this.caught = caught;
+        }
+        public Throwable caught() {
+            return caught;
+        }
+    }
+
+    private interface Service {
+        @Headers("cache-control: no-cache")
+        @GET(TEST_URL)
+        @NonNull Call<Object> get();
+    }
+}
diff --git a/app/src/main/java/org/wikipedia/feed/view/FeedAdapter.java 
b/app/src/main/java/org/wikipedia/feed/view/FeedAdapter.java
index 0d6e1e9..2646e4e 100644
--- a/app/src/main/java/org/wikipedia/feed/view/FeedAdapter.java
+++ b/app/src/main/java/org/wikipedia/feed/view/FeedAdapter.java
@@ -13,6 +13,7 @@
 import org.wikipedia.feed.model.Card;
 import org.wikipedia.feed.model.CardType;
 import org.wikipedia.feed.news.NewsListCardView;
+import org.wikipedia.feed.offline.OfflineCard;
 import org.wikipedia.feed.searchbar.SearchCardView;
 import org.wikipedia.views.DefaultRecyclerAdapter;
 import org.wikipedia.views.DefaultViewHolder;
@@ -47,6 +48,7 @@
 
         if (coordinator.finished()
                 && position == getItemCount() - 1
+                && !(item instanceof OfflineCard)
                 && callback != null) {
             callback.onRequestMore();
         }
diff --git a/app/src/main/java/org/wikipedia/util/ThrowableUtil.java 
b/app/src/main/java/org/wikipedia/util/ThrowableUtil.java
index e2d8ed2..e01ec1a 100644
--- a/app/src/main/java/org/wikipedia/util/ThrowableUtil.java
+++ b/app/src/main/java/org/wikipedia/util/ThrowableUtil.java
@@ -83,7 +83,7 @@
         return result;
     }
 
-    public static boolean isOffline(@NonNull Throwable caught) {
+    public static boolean isOffline(@Nullable Throwable caught) {
         return caught instanceof UnknownHostException
                 || caught instanceof SocketException
                 || caught instanceof SocketTimeoutException;
diff --git a/app/src/main/res/layout/view_card_offline.xml 
b/app/src/main/res/layout/view_card_offline.xml
new file mode 100644
index 0000000..05aace2
--- /dev/null
+++ b/app/src/main/res/layout/view_card_offline.xml
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout
+    xmlns:android="http://schemas.android.com/apk/res/android";
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:layout_margin="12dp"
+    android:orientation="vertical"
+    android:gravity="center">
+
+    <TextView
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:gravity="center"
+        android:text="@string/view_offline_card_text"
+        android:textColor="@color/view_wiki_error_text_color"
+        android:textAlignment="center"
+        android:textSize="16sp" />
+
+    <TextView
+        style="@style/Widget.AppCompat.Button"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_margin="8dp"
+        android:background="@android:color/white"
+        android:text="@string/page_error_retry"
+        android:minHeight="36dp"
+        android:textSize="14sp"
+        android:fontFamily="sans-serif-medium"
+        android:textColor="@color/view_wiki_error_text_color"/>
+</LinearLayout>
\ No newline at end of file
diff --git a/app/src/main/res/values-qq/strings.xml 
b/app/src/main/res/values-qq/strings.xml
index ba795cf..b335f91 100644
--- a/app/src/main/res/values-qq/strings.xml
+++ b/app/src/main/res/values-qq/strings.xml
@@ -482,6 +482,7 @@
 {{Identical|Today}}</string>
   <string name="view_announcement_card_negative_action">Button label that 
allows the user to decline the action that this card is requesting.
 {{Identical|No thanks}}</string>
+  <string name="view_offline_card_text">Message in the feed informing the user 
that additional content cannot be loaded while offline.</string>
   <string name="description_edit_text_hint">Hint text that is shown when the 
description field is empty.</string>
   <string name="description_edit_save">Hint for the button that is pressed for 
saving the new description.
 {{Identical|Publish}}</string>
diff --git a/app/src/main/res/values/strings.xml 
b/app/src/main/res/values/strings.xml
index a6ed98e..96e7674 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -370,6 +370,7 @@
     </plurals>
     <string name="view_continue_reading_card_subtitle_today">Today</string>
     <string name="view_announcement_card_negative_action">No thanks</string>
+    <string name="view_offline_card_text">Content cannot be loaded when 
offline.</string>
     <!-- /The Feed -->
 
     <!-- Description editing -->

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

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

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

Reply via email to