Bug 1261527 - Add refresh layout. r=sebastian
authorChenxia Liu <liuche@mozilla.com>
Wed, 27 Apr 2016 17:54:29 -0700
changeset 362662 94f101332e2a6f5201147da0a92e28a3b6bc8214
parent 362661 5bfba3bf6c1bc2970ae0269dcfe10ae99dc690d6
child 362663 0a954f87f3ac86edef9313c9b617bb358a7c4e73
push id17008
push userbgrinstead@mozilla.com
push dateMon, 02 May 2016 21:59:43 +0000
reviewerssebastian
bugs1261527
milestone49.0a1
Bug 1261527 - Add refresh layout. r=sebastian MozReview-Commit-ID: 5uDpe3OQAjG
mobile/android/base/java/org/mozilla/gecko/home/CombinedHistoryPanel.java
mobile/android/base/resources/layout/home_combined_history_panel.xml
--- a/mobile/android/base/java/org/mozilla/gecko/home/CombinedHistoryPanel.java
+++ b/mobile/android/base/java/org/mozilla/gecko/home/CombinedHistoryPanel.java
@@ -1,23 +1,25 @@
 /* -*- Mode: Java; c-basic-offset: 4; tab-width: 20; indent-tabs-mode: nil; -*-
  * This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 package org.mozilla.gecko.home;
 
+import android.accounts.Account;
 import android.app.AlertDialog;
 import android.content.ContentResolver;
 import android.content.Context;
 import android.content.DialogInterface;
 import android.database.Cursor;
 import android.os.Bundle;
 import android.support.v4.app.LoaderManager;
 import android.support.v4.content.Loader;
+import android.support.v4.widget.SwipeRefreshLayout;
 import android.support.v7.widget.DefaultItemAnimator;
 import android.text.SpannableStringBuilder;
 import android.text.TextPaint;
 import android.text.method.LinkMovementMethod;
 import android.text.style.ClickableSpan;
 import android.text.style.UnderlineSpan;
 import android.util.Log;
 import android.view.ContextMenu;
@@ -32,30 +34,34 @@ import android.widget.ImageView;
 import android.widget.TextView;
 import org.json.JSONException;
 import org.json.JSONObject;
 import org.mozilla.gecko.EventDispatcher;
 import org.mozilla.gecko.GeckoAppShell;
 import org.mozilla.gecko.GeckoProfile;
 import org.mozilla.gecko.R;
 import org.mozilla.gecko.RemoteClientsDialogFragment;
+import org.mozilla.gecko.fxa.FirefoxAccounts;
+import org.mozilla.gecko.fxa.SyncStatusListener;
 import org.mozilla.gecko.restrictions.Restrictions;
 import org.mozilla.gecko.Telemetry;
 import org.mozilla.gecko.TelemetryContract;
 import org.mozilla.gecko.db.BrowserDB;
 import org.mozilla.gecko.db.RemoteClient;
 import org.mozilla.gecko.restrictions.Restrictable;
 import org.mozilla.gecko.widget.DividerItemDecoration;
 
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.List;
 
 public class CombinedHistoryPanel extends HomeFragment implements RemoteClientsDialogFragment.RemoteClientsListener {
     private static final String LOGTAG = "GeckoCombinedHistoryPnl";
+
+    private static final String[] STAGES_TO_SYNC_ON_REFRESH = new String[] { "clients", "tabs" };
     private final int LOADER_ID_HISTORY = 0;
     private final int LOADER_ID_REMOTE = 1;
 
     // String placeholders to mark formatting.
     private final static String FORMAT_S1 = "%1$s";
     private final static String FORMAT_S2 = "%2$s";
 
     // Number of smart folders for determining practical empty state.
@@ -64,16 +70,22 @@ public class CombinedHistoryPanel extend
     private CombinedHistoryRecyclerView mRecyclerView;
     private CombinedHistoryAdapter mHistoryAdapter;
     private ClientsAdapter mClientsAdapter;
     private CursorLoaderCallbacks mCursorLoaderCallbacks;
 
     private OnPanelLevelChangeListener.PanelLevel mPanelLevel;
     private Button mPanelFooterButton;
 
+    // Child refresh layout view.
+    protected SwipeRefreshLayout mRefreshLayout;
+
+    // Sync listener that stops refreshing when a sync is completed.
+    protected RemoteTabsSyncListener mSyncStatusListener;
+
     // Reference to the View to display when there are no results.
     private View mEmptyView;
 
     public interface OnPanelLevelChangeListener {
         enum PanelLevel {
         PARENT, CHILD
     }
 
@@ -86,30 +98,36 @@ public class CombinedHistoryPanel extend
     }
 
     @Override
     public void onCreate(Bundle savedInstance) {
         super.onCreate(savedInstance);
 
         mHistoryAdapter = new CombinedHistoryAdapter(getResources());
         mClientsAdapter = new ClientsAdapter(getContext());
+
+        mSyncStatusListener = new RemoteTabsSyncListener();
+        FirefoxAccounts.addSyncStatusListener(mSyncStatusListener);
     }
 
     @Override
     public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
         return inflater.inflate(R.layout.home_combined_history_panel, container, false);
     }
 
     @Override
     public void onViewCreated(View view, Bundle savedInstanceState) {
         super.onViewCreated(view, savedInstanceState);
 
         mRecyclerView = (CombinedHistoryRecyclerView) view.findViewById(R.id.combined_recycler_view);
         setUpRecyclerView();
 
+        mRefreshLayout = (SwipeRefreshLayout) view.findViewById(R.id.refresh_layout);
+        setUpRefreshLayout();
+
         mPanelFooterButton = (Button) view.findViewById(R.id.clear_history_button);
         mPanelFooterButton.setOnClickListener(new OnFooterButtonClickListener());
     }
 
     private void setUpRecyclerView() {
         if (mPanelLevel == null) {
             mPanelLevel = OnPanelLevelChangeListener.PanelLevel.PARENT;
         }
@@ -119,16 +137,21 @@ public class CombinedHistoryPanel extend
         mRecyclerView.setItemAnimator(new DefaultItemAnimator());
         mRecyclerView.addItemDecoration(new DividerItemDecoration(getContext()));
         mRecyclerView.setOnHistoryClickedListener(mUrlOpenListener);
         mRecyclerView.setOnPanelLevelChangeListener(new OnLevelChangeListener());
         mRecyclerView.setHiddenClientsDialogBuilder(new HiddenClientsHelper());
         registerForContextMenu(mRecyclerView);
     }
 
+    private void setUpRefreshLayout() {
+        mRefreshLayout.setColorSchemeResources(R.color.fennec_ui_orange, R.color.action_orange);
+        mRefreshLayout.setOnRefreshListener(new RemoteTabsRefreshListener());
+    }
+
     @Override
     public void onActivityCreated(Bundle savedInstanceState) {
         super.onActivityCreated(savedInstanceState);
         mCursorLoaderCallbacks = new CursorLoaderCallbacks();
     }
 
     @Override
     protected void load() {
@@ -194,16 +217,17 @@ public class CombinedHistoryPanel extend
                 case LOADER_ID_HISTORY:
                     mHistoryAdapter.setHistory(c);
                     break;
 
                 case LOADER_ID_REMOTE:
                     final List<RemoteClient> clients = mDB.getTabsAccessor().getClientsFromCursor(c);
                     mHistoryAdapter.getDeviceUpdateHandler().onDeviceCountUpdated(clients.size());
                     mClientsAdapter.setClients(clients);
+                    mRefreshLayout.setEnabled(clients.size() > 0);
                     break;
             }
 
             updateEmptyView();
             updateButtonFromLevel();
         }
 
         @Override
@@ -221,17 +245,17 @@ public class CombinedHistoryPanel extend
             }
 
             mPanelLevel = level;
             switch (level) {
                 case PARENT:
                     mRecyclerView.swapAdapter(mHistoryAdapter, false);
                     break;
                 case CHILD:
-                    mRecyclerView.swapAdapter(mClientsAdapter, false);
+                    mRecyclerView.swapAdapter(mClientsAdapter, true);
                     break;
             }
 
             updateEmptyView();
             updateButtonFromLevel();
             return true;
         }
     }
@@ -457,9 +481,52 @@ public class CombinedHistoryPanel extend
     protected static class RemoteTabsClientContextMenuInfo extends HomeContextMenuInfo {
         protected final RemoteClient client;
 
         public RemoteTabsClientContextMenuInfo(View targetView, int position, long id, RemoteClient client) {
             super(targetView, position, id);
             this.client = client;
         }
     }
+
+    protected class RemoteTabsRefreshListener implements SwipeRefreshLayout.OnRefreshListener {
+        @Override
+        public void onRefresh() {
+            if (FirefoxAccounts.firefoxAccountsExist(getActivity())) {
+                final Account account = FirefoxAccounts.getFirefoxAccount(getActivity());
+                FirefoxAccounts.requestImmediateSync(account, STAGES_TO_SYNC_ON_REFRESH, null);
+            } else {
+                Log.wtf(LOGTAG, "No Firefox Account found; this should never happen. Ignoring.");
+                mRefreshLayout.setRefreshing(false);
+            }
+        }
+    }
+
+    protected class RemoteTabsSyncListener implements SyncStatusListener {
+        @Override
+        public Context getContext() {
+            return getActivity();
+        }
+
+        @Override
+        public Account getAccount() {
+            return FirefoxAccounts.getFirefoxAccount(getContext());
+        }
+
+        @Override
+        public void onSyncStarted() {
+        }
+
+        @Override
+        public void onSyncFinished() {
+            mRefreshLayout.setRefreshing(false);
+        }
+    }
+
+    @Override
+    public void onDestroy() {
+        super.onDestroy();
+        if (mSyncStatusListener != null) {
+            FirefoxAccounts.removeSyncStatusListener(mSyncStatusListener);
+            mSyncStatusListener = null;
+        }
+    }
 }
--- a/mobile/android/base/resources/layout/home_combined_history_panel.xml
+++ b/mobile/android/base/resources/layout/home_combined_history_panel.xml
@@ -13,21 +13,28 @@
               android:layout_width="match_parent"
               android:layout_height="match_parent"/>
 
     <ViewStub android:id="@+id/home_sync_empty_view_stub"
               android:layout="@layout/remote_tabs_setup"
               android:layout_width="match_parent"
               android:layout_height="match_parent"/>
 
-    <org.mozilla.gecko.home.CombinedHistoryRecyclerView
-            android:id="@+id/combined_recycler_view"
+    <android.support.v4.widget.SwipeRefreshLayout
+            android:id="@+id/refresh_layout"
             android:layout_width="match_parent"
             android:layout_height="0dp"
-            android:layout_weight="1"/>
+            android:layout_weight="1">
+
+        <org.mozilla.gecko.home.CombinedHistoryRecyclerView
+                android:id="@+id/combined_recycler_view"
+                android:layout_height="match_parent"
+                android:layout_width="match_parent"/>
+
+    </android.support.v4.widget.SwipeRefreshLayout>
 
     <Button android:id="@+id/clear_history_button"
             style="@style/Widget.Home.ActionButton"
             android:text="@string/home_clear_history_button"
             android:layout_width="match_parent"
             android:layout_height="48dp"
             android:visibility="gone" />