1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/mobile/android/base/home/LastTabsPanel.java Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,293 @@ 1.4 +/* -*- Mode: Java; c-basic-offset: 4; tab-width: 20; indent-tabs-mode: nil; -*- 1.5 + * This Source Code Form is subject to the terms of the Mozilla Public 1.6 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.7 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.8 + 1.9 +package org.mozilla.gecko.home; 1.10 + 1.11 +import org.mozilla.gecko.AboutPages; 1.12 +import org.mozilla.gecko.GeckoProfile; 1.13 +import org.mozilla.gecko.R; 1.14 +import org.mozilla.gecko.SessionParser; 1.15 +import org.mozilla.gecko.Telemetry; 1.16 +import org.mozilla.gecko.TelemetryContract; 1.17 +import org.mozilla.gecko.db.BrowserContract.Combined; 1.18 +import org.mozilla.gecko.home.HomePager.OnNewTabsListener; 1.19 + 1.20 +import android.app.Activity; 1.21 +import android.content.Context; 1.22 +import android.database.Cursor; 1.23 +import android.database.MatrixCursor; 1.24 +import android.database.MatrixCursor.RowBuilder; 1.25 +import android.os.Bundle; 1.26 +import android.support.v4.app.LoaderManager.LoaderCallbacks; 1.27 +import android.support.v4.content.Loader; 1.28 +import android.support.v4.widget.CursorAdapter; 1.29 +import android.view.LayoutInflater; 1.30 +import android.view.View; 1.31 +import android.view.ViewGroup; 1.32 +import android.view.ViewStub; 1.33 +import android.widget.AdapterView; 1.34 +import android.widget.ImageView; 1.35 +import android.widget.TextView; 1.36 + 1.37 +/** 1.38 + * Fragment that displays tabs from last session in a ListView. 1.39 + */ 1.40 +public class LastTabsPanel extends HomeFragment { 1.41 + // Logging tag name 1.42 + private static final String LOGTAG = "GeckoLastTabsPanel"; 1.43 + 1.44 + // Cursor loader ID for the session parser 1.45 + private static final int LOADER_ID_LAST_TABS = 0; 1.46 + 1.47 + // Adapter for the list of search results 1.48 + private LastTabsAdapter mAdapter; 1.49 + 1.50 + // The view shown by the fragment. 1.51 + private HomeListView mList; 1.52 + 1.53 + // The title for this HomeFragment panel. 1.54 + private TextView mTitle; 1.55 + 1.56 + // The button view for restoring tabs from last session. 1.57 + private View mRestoreButton; 1.58 + 1.59 + // Reference to the View to display when there are no results. 1.60 + private View mEmptyView; 1.61 + 1.62 + // Callbacks used for the search and favicon cursor loaders 1.63 + private CursorLoaderCallbacks mCursorLoaderCallbacks; 1.64 + 1.65 + // On new tabs listener 1.66 + private OnNewTabsListener mNewTabsListener; 1.67 + 1.68 + public static LastTabsPanel newInstance() { 1.69 + return new LastTabsPanel(); 1.70 + } 1.71 + 1.72 + public LastTabsPanel() { 1.73 + mNewTabsListener = null; 1.74 + } 1.75 + 1.76 + @Override 1.77 + public void onAttach(Activity activity) { 1.78 + super.onAttach(activity); 1.79 + 1.80 + try { 1.81 + mNewTabsListener = (OnNewTabsListener) activity; 1.82 + } catch (ClassCastException e) { 1.83 + throw new ClassCastException(activity.toString() 1.84 + + " must implement HomePager.OnNewTabsListener"); 1.85 + } 1.86 + } 1.87 + 1.88 + @Override 1.89 + public void onDetach() { 1.90 + super.onDetach(); 1.91 + 1.92 + mNewTabsListener = null; 1.93 + } 1.94 + 1.95 + @Override 1.96 + public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { 1.97 + return inflater.inflate(R.layout.home_last_tabs_panel, container, false); 1.98 + } 1.99 + 1.100 + @Override 1.101 + public void onViewCreated(View view, Bundle savedInstanceState) { 1.102 + mTitle = (TextView) view.findViewById(R.id.title); 1.103 + if (mTitle != null) { 1.104 + mTitle.setText(R.string.home_last_tabs_title); 1.105 + } 1.106 + 1.107 + mList = (HomeListView) view.findViewById(R.id.list); 1.108 + mList.setTag(HomePager.LIST_TAG_LAST_TABS); 1.109 + 1.110 + mList.setOnItemClickListener(new AdapterView.OnItemClickListener() { 1.111 + @Override 1.112 + public void onItemClick(AdapterView<?> parent, View view, int position, long id) { 1.113 + final Cursor c = mAdapter.getCursor(); 1.114 + if (c == null || !c.moveToPosition(position)) { 1.115 + return; 1.116 + } 1.117 + 1.118 + Telemetry.sendUIEvent(TelemetryContract.Event.LOAD_URL); 1.119 + 1.120 + final String url = c.getString(c.getColumnIndexOrThrow(Combined.URL)); 1.121 + mNewTabsListener.onNewTabs(new String[] { url }); 1.122 + } 1.123 + }); 1.124 + 1.125 + mList.setContextMenuInfoFactory(new HomeContextMenuInfo.Factory() { 1.126 + @Override 1.127 + public HomeContextMenuInfo makeInfoForCursor(View view, int position, long id, Cursor cursor) { 1.128 + final HomeContextMenuInfo info = new HomeContextMenuInfo(view, position, id); 1.129 + info.url = cursor.getString(cursor.getColumnIndexOrThrow(Combined.URL)); 1.130 + info.title = cursor.getString(cursor.getColumnIndexOrThrow(Combined.TITLE)); 1.131 + return info; 1.132 + } 1.133 + }); 1.134 + 1.135 + registerForContextMenu(mList); 1.136 + 1.137 + mRestoreButton = view.findViewById(R.id.open_all_tabs_button); 1.138 + mRestoreButton.setOnClickListener(new View.OnClickListener() { 1.139 + @Override 1.140 + public void onClick(View v) { 1.141 + openAllTabs(); 1.142 + } 1.143 + }); 1.144 + } 1.145 + 1.146 + @Override 1.147 + public void onDestroyView() { 1.148 + super.onDestroyView(); 1.149 + mList = null; 1.150 + mTitle = null; 1.151 + mEmptyView = null; 1.152 + mRestoreButton = null; 1.153 + } 1.154 + 1.155 + @Override 1.156 + public void onActivityCreated(Bundle savedInstanceState) { 1.157 + super.onActivityCreated(savedInstanceState); 1.158 + 1.159 + // Intialize adapter 1.160 + mAdapter = new LastTabsAdapter(getActivity()); 1.161 + mList.setAdapter(mAdapter); 1.162 + 1.163 + // Create callbacks before the initial loader is started 1.164 + mCursorLoaderCallbacks = new CursorLoaderCallbacks(); 1.165 + loadIfVisible(); 1.166 + } 1.167 + 1.168 + private void updateUiFromCursor(Cursor c) { 1.169 + if (c != null && c.getCount() > 0) { 1.170 + if (mTitle != null) { 1.171 + mTitle.setVisibility(View.VISIBLE); 1.172 + } 1.173 + mRestoreButton.setVisibility(View.VISIBLE); 1.174 + return; 1.175 + } 1.176 + 1.177 + // Cursor is empty, so hide the title and set the 1.178 + // empty view if it hasn't been set already. 1.179 + if (mTitle != null) { 1.180 + mTitle.setVisibility(View.GONE); 1.181 + } 1.182 + mRestoreButton.setVisibility(View.GONE); 1.183 + 1.184 + if (mEmptyView == null) { 1.185 + // Set empty panel view. We delay this so that the empty view won't flash. 1.186 + final ViewStub emptyViewStub = (ViewStub) getView().findViewById(R.id.home_empty_view_stub); 1.187 + mEmptyView = emptyViewStub.inflate(); 1.188 + 1.189 + final ImageView emptyIcon = (ImageView) mEmptyView.findViewById(R.id.home_empty_image); 1.190 + emptyIcon.setImageResource(R.drawable.icon_last_tabs_empty); 1.191 + 1.192 + final TextView emptyText = (TextView) mEmptyView.findViewById(R.id.home_empty_text); 1.193 + emptyText.setText(R.string.home_last_tabs_empty); 1.194 + 1.195 + mList.setEmptyView(mEmptyView); 1.196 + } 1.197 + } 1.198 + 1.199 + @Override 1.200 + protected void load() { 1.201 + getLoaderManager().initLoader(LOADER_ID_LAST_TABS, null, mCursorLoaderCallbacks); 1.202 + } 1.203 + 1.204 + private void openAllTabs() { 1.205 + final Cursor c = mAdapter.getCursor(); 1.206 + if (c == null || !c.moveToFirst()) { 1.207 + return; 1.208 + } 1.209 + 1.210 + final String[] urls = new String[c.getCount()]; 1.211 + 1.212 + do { 1.213 + urls[c.getPosition()] = c.getString(c.getColumnIndexOrThrow(Combined.URL)); 1.214 + } while (c.moveToNext()); 1.215 + 1.216 + Telemetry.sendUIEvent(TelemetryContract.Event.LOAD_URL, TelemetryContract.Method.BUTTON); 1.217 + 1.218 + mNewTabsListener.onNewTabs(urls); 1.219 + } 1.220 + 1.221 + private static class LastTabsCursorLoader extends SimpleCursorLoader { 1.222 + public LastTabsCursorLoader(Context context) { 1.223 + super(context); 1.224 + } 1.225 + 1.226 + @Override 1.227 + public Cursor loadCursor() { 1.228 + final Context context = getContext(); 1.229 + 1.230 + final String jsonString = GeckoProfile.get(context).readSessionFile(true); 1.231 + if (jsonString == null) { 1.232 + // No previous session data 1.233 + return null; 1.234 + } 1.235 + 1.236 + final MatrixCursor c = new MatrixCursor(new String[] { Combined._ID, 1.237 + Combined.URL, 1.238 + Combined.TITLE }); 1.239 + 1.240 + new SessionParser() { 1.241 + @Override 1.242 + public void onTabRead(SessionTab tab) { 1.243 + final String url = tab.getUrl(); 1.244 + 1.245 + // Don't show last tabs for about:home 1.246 + if (AboutPages.isAboutHome(url)) { 1.247 + return; 1.248 + } 1.249 + 1.250 + final RowBuilder row = c.newRow(); 1.251 + row.add(-1); 1.252 + row.add(url); 1.253 + 1.254 + final String title = tab.getTitle(); 1.255 + row.add(title); 1.256 + } 1.257 + }.parse(jsonString); 1.258 + 1.259 + return c; 1.260 + } 1.261 + } 1.262 + 1.263 + private static class LastTabsAdapter extends CursorAdapter { 1.264 + public LastTabsAdapter(Context context) { 1.265 + super(context, null, 0); 1.266 + } 1.267 + 1.268 + @Override 1.269 + public void bindView(View view, Context context, Cursor cursor) { 1.270 + ((TwoLinePageRow) view).updateFromCursor(cursor); 1.271 + } 1.272 + 1.273 + @Override 1.274 + public View newView(Context context, Cursor cursor, ViewGroup parent) { 1.275 + return LayoutInflater.from(context).inflate(R.layout.home_item_row, parent, false); 1.276 + } 1.277 + } 1.278 + 1.279 + private class CursorLoaderCallbacks implements LoaderCallbacks<Cursor> { 1.280 + @Override 1.281 + public Loader<Cursor> onCreateLoader(int id, Bundle args) { 1.282 + return new LastTabsCursorLoader(getActivity()); 1.283 + } 1.284 + 1.285 + @Override 1.286 + public void onLoadFinished(Loader<Cursor> loader, Cursor c) { 1.287 + mAdapter.swapCursor(c); 1.288 + updateUiFromCursor(c); 1.289 + } 1.290 + 1.291 + @Override 1.292 + public void onLoaderReset(Loader<Cursor> loader) { 1.293 + mAdapter.swapCursor(null); 1.294 + } 1.295 + } 1.296 +}