Bug 882716 - Implement "Last Tabs" page for new about:home (r=bnicholson)
authorLucas Rocha <lucasr@mozilla.com>
Mon, 08 Jul 2013 23:02:02 +0100
changeset 143387 950e12ef1eb85945e36ca7145fbf28ccd51d6d75
parent 143386 c4a0d7678b34089c80d42d93d83c297a04684f52
child 143388 81c3af05d10a4f91b9e6dfa738cde8cd705edfe9
push id25130
push userlrocha@mozilla.com
push dateWed, 21 Aug 2013 09:41:27 +0000
treeherdermozilla-central@b2486721572e [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbnicholson
bugs882716
milestone25.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 882716 - Implement "Last Tabs" page for new about:home (r=bnicholson)
mobile/android/base/Makefile.in
mobile/android/base/home/LastTabsPage.java
--- a/mobile/android/base/Makefile.in
+++ b/mobile/android/base/Makefile.in
@@ -223,16 +223,17 @@ FENNEC_JAVA_FILES = \
   home/BrowserSearch.java \
   home/HistoryPage.java \
   home/HomeFragment.java \
   home/HomeListView.java \
   home/HomePager.java \
   home/HomePagerTabStrip.java \
   home/FadedTextView.java \
   home/FaviconsLoader.java \
+  home/LastTabsPage.java \
   home/ReadingListPage.java \
   home/SearchEngine.java \
   home/SearchEngineRow.java \
   home/SimpleCursorLoader.java \
   home/SuggestClient.java \
   home/TopBookmarkItemView.java \
   home/TopBookmarksAdapter.java \
   home/TopBookmarksView.java \
new file mode 100644
--- /dev/null
+++ b/mobile/android/base/home/LastTabsPage.java
@@ -0,0 +1,233 @@
+/* -*- 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 org.mozilla.gecko.GeckoProfile;
+import org.mozilla.gecko.R;
+import org.mozilla.gecko.SessionParser;
+import org.mozilla.gecko.db.BrowserDB;
+import org.mozilla.gecko.db.BrowserContract.Combined;
+import org.mozilla.gecko.home.HomePager.OnUrlOpenListener;
+import org.mozilla.gecko.home.TwoLinePageRow;
+
+import android.app.Activity;
+import android.content.ContentResolver;
+import android.content.Context;
+import android.content.res.Resources;
+import android.database.Cursor;
+import android.database.MatrixCursor;
+import android.database.MatrixCursor.RowBuilder;
+import android.graphics.Bitmap;
+import android.os.Bundle;
+import android.support.v4.app.Fragment;
+import android.support.v4.app.LoaderManager;
+import android.support.v4.app.LoaderManager.LoaderCallbacks;
+import android.support.v4.content.Loader;
+import android.util.SparseArray;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.LayoutInflater;
+import android.widget.AdapterView;
+import android.widget.ListView;
+import android.widget.SimpleCursorAdapter;
+import android.widget.TextView;
+
+import java.io.ByteArrayOutputStream;
+import java.util.Date;
+
+/**
+ * Fragment that displays tabs from last session in a ListView.
+ */
+public class LastTabsPage extends HomeFragment {
+    // Logging tag name
+    private static final String LOGTAG = "GeckoLastTabsPage";
+
+    // Cursor loader ID for the session parser
+    private static final int LAST_TABS_LOADER_ID = 0;
+
+    // Adapter for the list of search results
+    private LastTabsAdapter mAdapter;
+
+    // The view shown by the fragment.
+    private ListView mList;
+
+    // Callbacks used for the search and favicon cursor loaders
+    private CursorLoaderCallbacks mCursorLoaderCallbacks;
+
+    // Inflater used by the adapter
+    private LayoutInflater mInflater;
+
+    // On URL open listener
+    private OnUrlOpenListener mUrlOpenListener;
+
+    public static LastTabsPage newInstance() {
+        return new LastTabsPage();
+    }
+
+    public LastTabsPage() {
+        mUrlOpenListener = null;
+    }
+
+    @Override
+    public void onAttach(Activity activity) {
+        super.onAttach(activity);
+
+        try {
+            mUrlOpenListener = (OnUrlOpenListener) activity;
+        } catch (ClassCastException e) {
+            throw new ClassCastException(activity.toString()
+                    + " must implement HomePager.OnUrlOpenListener");
+        }
+
+        mInflater = (LayoutInflater) activity.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
+    }
+
+    @Override
+    public void onDetach() {
+        super.onDetach();
+
+        mInflater = null;
+        mUrlOpenListener = null;
+    }
+
+    @Override
+    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
+        return inflater.inflate(R.layout.home_list_with_title, container, false);
+    }
+
+    @Override
+    public void onViewCreated(View view, Bundle savedInstanceState) {
+        final TextView title = (TextView) view.findViewById(R.id.title);
+        title.setText(R.string.abouthome_last_tabs_title);
+
+        mList = (ListView) view.findViewById(R.id.list);
+
+        mList.setOnItemClickListener(new AdapterView.OnItemClickListener() {
+            @Override
+            public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
+                final Cursor c = mAdapter.getCursor();
+                if (c == null || !c.moveToPosition(position)) {
+                    return;
+                }
+
+                final String url = c.getString(c.getColumnIndexOrThrow(Combined.URL));
+                mUrlOpenListener.onUrlOpen(url);
+            }
+        });
+
+        registerForContextMenu(mList);
+    }
+
+    @Override
+    public void onDestroyView() {
+        super.onDestroyView();
+        mList = null;
+    }
+
+    @Override
+    public void onActivityCreated(Bundle savedInstanceState) {
+        super.onActivityCreated(savedInstanceState);
+
+        // Intialize adapter
+        mAdapter = new LastTabsAdapter(getActivity());
+        mList.setAdapter(mAdapter);
+
+        // Create callbacks before the initial loader is started
+        mCursorLoaderCallbacks = new CursorLoaderCallbacks();
+
+        // Reconnect to the loader only if present
+        getLoaderManager().initLoader(LAST_TABS_LOADER_ID, null, mCursorLoaderCallbacks);
+    }
+
+    private static class LastTabsCursorLoader extends SimpleCursorLoader {
+        public LastTabsCursorLoader(Context context) {
+            super(context);
+        }
+
+        @Override
+        public Cursor loadCursor() {
+            final Context context = getContext();
+
+            final String jsonString = GeckoProfile.get(context).readSessionFile(true);
+            if (jsonString == null) {
+                // No previous session data
+                return null;
+            }
+
+            final MatrixCursor c = new MatrixCursor(new String[] { Combined._ID,
+                                                                   Combined.URL,
+                                                                   Combined.TITLE,
+                                                                   Combined.FAVICON });
+
+            new SessionParser() {
+                @Override
+                public void onTabRead(SessionTab tab) {
+                    final String url = tab.getUrl();
+
+                    // Don't show last tabs for about:home
+                    if (url.equals("about:home")) {
+                        return;
+                    }
+
+                    final RowBuilder row = c.newRow();
+                    row.add(-1);
+                    row.add(url);
+
+                    final String title = tab.getTitle();
+                    row.add(title);
+
+                    final ContentResolver cr = context.getContentResolver();
+                    final byte[] favicon = BrowserDB.getFaviconBytesForUrl(cr, url);
+                    row.add(favicon);
+                }
+            }.parse(jsonString);
+
+            return c;
+        }
+    }
+
+    private class LastTabsAdapter extends SimpleCursorAdapter {
+        public LastTabsAdapter(Context context) {
+            super(context, -1, null, new String[] {}, new int[] {});
+        }
+
+        @Override
+        public View getView(int position, View convertView, ViewGroup parent) {
+            final TwoLinePageRow row;
+            if (convertView == null) {
+                row = (TwoLinePageRow) mInflater.inflate(R.layout.home_item_row, mList, false);
+            } else {
+                row = (TwoLinePageRow) convertView;
+            }
+
+            final Cursor c = getCursor();
+            if (!c.moveToPosition(position)) {
+                throw new IllegalStateException("Couldn't move cursor to position " + position);
+            }
+
+            row.updateFromCursor(c);
+
+            return row;
+        }
+    }
+
+    private class CursorLoaderCallbacks implements LoaderCallbacks<Cursor> {
+        @Override
+        public Loader<Cursor> onCreateLoader(int id, Bundle args) {
+            return new LastTabsCursorLoader(getActivity());
+        }
+
+        @Override
+        public void onLoadFinished(Loader<Cursor> loader, Cursor c) {
+            mAdapter.swapCursor(c);
+        }
+
+        @Override
+        public void onLoaderReset(Loader<Cursor> loader) {
+            mAdapter.swapCursor(null);
+        }
+    }
+}