diff --git a/mobile/android/base/BrowserApp.java b/mobile/android/base/BrowserApp.java index 1a0cda6fb00..a14fd9a6ac0 100644 --- a/mobile/android/base/BrowserApp.java +++ b/mobile/android/base/BrowserApp.java @@ -1317,7 +1317,7 @@ abstract public class BrowserApp extends GeckoApp animator.setUseHardwareLayer(false); mBrowserToolbar.startEditing(url, animator); - showHomePagerWithAnimator(HomePager.Page.VISITED, animator); + showHomePagerWithAnimator(HomePager.Page.HISTORY, animator); animator.start(); } @@ -1396,14 +1396,6 @@ abstract public class BrowserApp extends GeckoApp return; } - // Hide any visible homepager subpages - final FragmentManager fm = getSupportFragmentManager(); - final Fragment subPage = fm.findFragmentByTag(HomePager.SUBPAGE_TAG); - if (subPage != null) { - fm.beginTransaction().remove(subPage).commitAllowingStateLoss(); - fm.popBackStack(); - } - // FIXME: do animation if animate is true mHomePager.hide(); diff --git a/mobile/android/base/Makefile.in b/mobile/android/base/Makefile.in index d4e6fad6a7f..1819eef03e8 100644 --- a/mobile/android/base/Makefile.in +++ b/mobile/android/base/Makefile.in @@ -229,6 +229,8 @@ FENNEC_JAVA_FILES = \ home/FadedTextView.java \ home/FaviconsLoader.java \ home/LastTabsPage.java \ + home/MostRecentPage.java \ + home/MostVisitedPage.java \ home/PinBookmarkDialog.java \ home/ReadingListPage.java \ home/SearchEngine.java \ @@ -240,7 +242,6 @@ FENNEC_JAVA_FILES = \ home/TopBookmarksAdapter.java \ home/TopBookmarksView.java \ home/TwoLinePageRow.java \ - home/VisitedPage.java \ menu/GeckoMenu.java \ menu/GeckoMenuInflater.java \ menu/GeckoMenuItem.java \ @@ -465,11 +466,13 @@ RES_LAYOUT = \ res/layout/home_item_row.xml \ res/layout/home_header_row.xml \ res/layout/home_history_page.xml \ + res/layout/home_history_tabs_indicator.xml \ res/layout/home_last_tabs_page.xml \ res/layout/home_list_with_title.xml \ + res/layout/home_most_recent_page.xml \ + res/layout/home_most_visited_page.xml \ res/layout/home_search_item_row.xml \ res/layout/home_suggestion_prompt.xml \ - res/layout/home_visited_page.xml \ res/layout/web_app.xml \ res/layout/launch_app_list.xml \ res/layout/launch_app_listitem.xml \ @@ -632,6 +635,9 @@ RES_DRAWABLE_MDPI = \ res/drawable-mdpi/ic_url_bar_search.png \ res/drawable-mdpi/ic_url_bar_star.png \ res/drawable-mdpi/ic_url_bar_tab.png \ + res/drawable-mdpi/icon_last_tabs.png \ + res/drawable-mdpi/icon_most_recent.png \ + res/drawable-mdpi/icon_most_visited.png \ res/drawable-mdpi/icon_pageaction.png \ res/drawable-mdpi/progress_spinner_1.png \ res/drawable-mdpi/progress_spinner_2.png \ @@ -704,6 +710,7 @@ RES_DRAWABLE_MDPI = \ res/drawable-mdpi/shadow.png \ res/drawable-mdpi/start.png \ res/drawable-mdpi/marketplace.png \ + res/drawable-mdpi/history_tabs_indicator_selected.9.png \ res/drawable-mdpi/warning.png \ res/drawable-mdpi/warning_doorhanger.png \ $(NULL) @@ -744,6 +751,9 @@ RES_DRAWABLE_HDPI = \ res/drawable-hdpi/ic_url_bar_search.png \ res/drawable-hdpi/ic_url_bar_star.png \ res/drawable-hdpi/ic_url_bar_tab.png \ + res/drawable-hdpi/icon_last_tabs.png \ + res/drawable-hdpi/icon_most_recent.png \ + res/drawable-hdpi/icon_most_visited.png \ res/drawable-hdpi/icon_pageaction.png \ res/drawable-hdpi/tab_indicator_divider.9.png \ res/drawable-hdpi/tab_indicator_selected.9.png \ @@ -794,6 +804,7 @@ RES_DRAWABLE_HDPI = \ res/drawable-hdpi/handle_end.png \ res/drawable-hdpi/handle_middle.png \ res/drawable-hdpi/handle_start.png \ + res/drawable-hdpi/history_tabs_indicator_selected.9.png \ res/drawable-hdpi/warning.png \ res/drawable-hdpi/warning_doorhanger.png \ $(NULL) @@ -832,6 +843,9 @@ RES_DRAWABLE_XHDPI = \ res/drawable-xhdpi/ic_url_bar_search.png \ res/drawable-xhdpi/ic_url_bar_star.png \ res/drawable-xhdpi/ic_url_bar_tab.png \ + res/drawable-xhdpi/icon_last_tabs.png \ + res/drawable-xhdpi/icon_most_recent.png \ + res/drawable-xhdpi/icon_most_visited.png \ res/drawable-xhdpi/icon_pageaction.png \ res/drawable-xhdpi/spinner_default.9.png \ res/drawable-xhdpi/spinner_focused.9.png \ @@ -877,6 +891,7 @@ RES_DRAWABLE_XHDPI = \ res/drawable-xhdpi/handle_end.png \ res/drawable-xhdpi/handle_middle.png \ res/drawable-xhdpi/handle_start.png \ + res/drawable-xhdpi/history_tabs_indicator_selected.9.png \ res/drawable-xhdpi/warning.png \ res/drawable-xhdpi/warning_doorhanger.png \ $(NULL) @@ -1051,6 +1066,8 @@ RES_DRAWABLE += \ res/drawable/favicon_bg.xml \ res/drawable/handle_end_level.xml \ res/drawable/handle_start_level.xml \ + res/drawable/home_history_tabs_indicator.xml \ + res/drawable/home_page_title_background.xml \ res/drawable/ic_menu_back.xml \ res/drawable/ic_menu_desktop_mode_off.xml \ res/drawable/ic_menu_desktop_mode_on.xml \ diff --git a/mobile/android/base/home/HistoryPage.java b/mobile/android/base/home/HistoryPage.java index c53f1a04caa..8df09988df1 100644 --- a/mobile/android/base/home/HistoryPage.java +++ b/mobile/android/base/home/HistoryPage.java @@ -6,100 +6,24 @@ package org.mozilla.gecko.home; import org.mozilla.gecko.R; -import org.mozilla.gecko.db.BrowserDB; -import org.mozilla.gecko.db.BrowserDB.URLColumns; -import org.mozilla.gecko.home.HomePager.OnUrlOpenListener; -import org.mozilla.gecko.home.TwoLinePageRow; - -import android.app.Activity; -import android.content.ContentResolver; +import org.mozilla.gecko.widget.IconTabWidget; +import android.support.v4.app.Fragment; import android.content.Context; import android.content.res.Resources; -import android.database.Cursor; import android.os.Bundle; -import android.support.v4.app.LoaderManager.LoaderCallbacks; -import android.support.v4.content.Loader; -import android.support.v4.widget.SimpleCursorAdapter; -import android.util.SparseArray; +import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.view.LayoutInflater; -import android.widget.AdapterView; -import android.widget.ListView; -import android.widget.TextView; -import java.util.Date; +import android.widget.ImageButton; -/** - * Fragment that displays recent history in a ListView. - */ -public class HistoryPage extends HomeFragment { +public class HistoryPage extends HomeFragment + implements IconTabWidget.OnTabChangedListener { // Logging tag name private static final String LOGTAG = "GeckoHistoryPage"; - - // Cursor loader ID for history query - private static final int HISTORY_LOADER_ID = 0; - - // For the time sections in history - private static final long MS_PER_DAY = 86400000; - private static final long MS_PER_WEEK = MS_PER_DAY * 7; - - // The time ranges for each section - private static enum HistorySection { - TODAY, - YESTERDAY, - WEEK, - OLDER - }; - - // Maps headers in the list with their respective sections - private SparseArray mHistorySections; - - // Adapter for the list of search results - private HistoryAdapter 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 HistoryPage newInstance() { - return new HistoryPage(); - } - - public HistoryPage() { - 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(); - - mHistorySections = null; - mInflater = null; - mUrlOpenListener = null; - } + private IconTabWidget mTabWidget; + private int mSelectedTab; @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { @@ -108,258 +32,68 @@ public class HistoryPage extends HomeFragment { @Override public void onViewCreated(View view, Bundle savedInstanceState) { - final TextView title = (TextView) view.findViewById(R.id.title); - title.setText(R.string.history_title); + super.onViewCreated(view, savedInstanceState); - mList = (ListView) view.findViewById(R.id.list); + mTabWidget = (IconTabWidget) view.findViewById(R.id.tab_icon_widget); - mList.setOnItemClickListener(new AdapterView.OnItemClickListener() { - @Override - public void onItemClick(AdapterView parent, View view, int position, long id) { - position -= getHistorySectionsCountBefore(position); + ImageButton button; + final Resources resources = view.getContext().getResources(); - final Cursor c = mAdapter.getCursor(); - if (c == null || !c.moveToPosition(position)) { - return; - } + button = mTabWidget.addTab(R.drawable.icon_most_visited); + button.setContentDescription(resources.getString(R.string.home_most_visited_title)); - final String url = c.getString(c.getColumnIndexOrThrow(URLColumns.URL)); - mUrlOpenListener.onUrlOpen(url); - } - }); + button = mTabWidget.addTab(R.drawable.icon_most_recent); + button.setContentDescription(resources.getString(R.string.home_most_recent_title)); - registerForContextMenu(mList); + button = mTabWidget.addTab(R.drawable.icon_last_tabs); + button.setContentDescription(resources.getString(R.string.home_last_tabs_title)); + + //Show most visited page as the initial page + showMostVisitedPage(); + + mTabWidget.setTabSelectionListener(this); + mTabWidget.setCurrentTab(mSelectedTab); } @Override - public void onDestroyView() { - super.onDestroyView(); - mList = null; - } + public void load() {} @Override - public void onActivityCreated(Bundle savedInstanceState) { - super.onActivityCreated(savedInstanceState); - - // Initialize map of history sections - mHistorySections = new SparseArray(); - - // Intialize adapter - mAdapter = new HistoryAdapter(getActivity()); - mList.setAdapter(mAdapter); - - // Create callbacks before the initial loader is started - mCursorLoaderCallbacks = new CursorLoaderCallbacks(); - loadIfVisible(); - } - - @Override - protected void load() { - getLoaderManager().initLoader(HISTORY_LOADER_ID, null, mCursorLoaderCallbacks); - } - - private String getHistorySectionTitle(HistorySection section) { - final Resources resources = getActivity().getResources(); - - switch (section) { - case TODAY: - return resources.getString(R.string.history_today_section); - case YESTERDAY: - return resources.getString(R.string.history_yesterday_section); - case WEEK: - return resources.getString(R.string.history_week_section); - case OLDER: - return resources.getString(R.string.history_older_section); - } - - throw new IllegalStateException("Unrecognized history section"); - } - - private int getHistorySectionsCountBefore(int position) { - // Account for the number headers before the given position - int sectionsBefore = 0; - - final int historySectionsCount = mHistorySections.size(); - for (int i = 0; i < historySectionsCount; i++) { - final int sectionPosition = mHistorySections.keyAt(i); - if (sectionPosition > position) { - break; - } - - sectionsBefore++; - } - - return sectionsBefore; - } - - private HistorySection getHistorySectionForTime(long from, long time) { - long delta = from - time; - - if (delta < 0) { - return HistorySection.TODAY; - } - - if (delta < MS_PER_DAY) { - return HistorySection.YESTERDAY; - } - - if (delta < MS_PER_WEEK) { - return HistorySection.WEEK; - } - - return HistorySection.OLDER; - } - - private void loadHistorySections(Cursor c) { - if (c == null || !c.moveToFirst()) { + public void onTabChanged(int index) { + if (index == mSelectedTab) { return; } - // Clear any history sections that may have been loaded before. - mHistorySections.clear(); + if (index == 0) { + showMostVisitedPage(); + } else if (index == 1) { + showMostRecentPage(); + } else if (index == 2) { + showLastTabsPage(); + } - final Date now = new Date(); - now.setHours(0); - now.setMinutes(0); - now.setSeconds(0); - - final long today = now.getTime(); - HistorySection section = null; - - do { - final int position = c.getPosition(); - final long time = c.getLong(c.getColumnIndexOrThrow(URLColumns.DATE_LAST_VISITED)); - final HistorySection itemSection = getHistorySectionForTime(today, time); - - if (section != itemSection) { - section = itemSection; - mHistorySections.append(position + mHistorySections.size(), section); - } - - // Reached the last section, no need to continue - if (section == HistorySection.OLDER) { - break; - } - } while (c.moveToNext()); + mTabWidget.setCurrentTab(index); + mSelectedTab = index; } - private static class HistoryCursorLoader extends SimpleCursorLoader { - // Max number of history results - private static final int HISTORY_LIMIT = 100; - - public HistoryCursorLoader(Context context) { - super(context); - } - - @Override - public Cursor loadCursor() { - final ContentResolver cr = getContext().getContentResolver(); - return BrowserDB.getRecentHistory(cr, HISTORY_LIMIT); - } + private void showSubPage(Fragment subPage) { + getChildFragmentManager().beginTransaction() + .addToBackStack(null).replace(R.id.visited_page_container, subPage) + .commitAllowingStateLoss(); } - private class HistoryAdapter extends SimpleCursorAdapter { - private static final int ROW_HEADER = 0; - private static final int ROW_STANDARD = 1; - - private static final int ROW_TYPE_COUNT = 2; - - public HistoryAdapter(Context context) { - super(context, -1, null, new String[] {}, new int[] {}); - } - - @Override - public Object getItem(int position) { - final int type = getItemViewType(position); - - // Header items are not in the cursor - if (type == ROW_HEADER) { - return null; - } - - return super.getItem(position - getHistorySectionsCountBefore(position)); - } - - @Override - public int getItemViewType(int position) { - if (mHistorySections.get(position) != null) { - return ROW_HEADER; - } - - return ROW_STANDARD; - } - - @Override - public int getViewTypeCount() { - // view can be either a standard page row, or a header row - return ROW_TYPE_COUNT; - } - - @Override - public boolean isEnabled(int position) { - return (getItemViewType(position) == ROW_STANDARD); - } - - @Override - public int getCount() { - // Add the history section headers to the number of reported results. - return super.getCount() + mHistorySections.size(); - } - - @Override - public View getView(int position, View convertView, ViewGroup parent) { - final int type = getItemViewType(position); - - if (type == ROW_HEADER) { - final TextView row; - if (convertView == null) { - row = (TextView) mInflater.inflate(R.layout.home_header_row, mList, false); - } else { - row = (TextView) convertView; - } - - final HistorySection section = mHistorySections.get(position); - row.setText(getHistorySectionTitle(section)); - - return row; - } else { - final TwoLinePageRow row; - if (convertView == null) { - row = (TwoLinePageRow) mInflater.inflate(R.layout.home_item_row, mList, false); - } else { - row = (TwoLinePageRow) convertView; - } - - // Account for the search engines - position -= getHistorySectionsCountBefore(position); - - final Cursor c = getCursor(); - if (!c.moveToPosition(position)) { - throw new IllegalStateException("Couldn't move cursor to position " + position); - } - - row.updateFromCursor(c); - - return row; - } - } + private void showMostVisitedPage() { + final MostVisitedPage mostVisitedPage = MostVisitedPage.newInstance(); + showSubPage(mostVisitedPage); } - private class CursorLoaderCallbacks implements LoaderCallbacks { - @Override - public Loader onCreateLoader(int id, Bundle args) { - return new HistoryCursorLoader(getActivity()); - } + private void showMostRecentPage() { + final MostRecentPage mostRecentPage = MostRecentPage.newInstance(); + showSubPage(mostRecentPage); + } - @Override - public void onLoadFinished(Loader loader, Cursor c) { - loadHistorySections(c); - mAdapter.swapCursor(c); - } - - @Override - public void onLoaderReset(Loader loader) { - mAdapter.swapCursor(null); - } + private void showLastTabsPage() { + final LastTabsPage lastTabsPage = LastTabsPage.newInstance(); + showSubPage(lastTabsPage); } } diff --git a/mobile/android/base/home/HomeFragment.java b/mobile/android/base/home/HomeFragment.java index 296bab150d3..51e37e81f2c 100644 --- a/mobile/android/base/home/HomeFragment.java +++ b/mobile/android/base/home/HomeFragment.java @@ -46,12 +46,6 @@ abstract class HomeFragment extends Fragment { // URL to Title replacement regex. private static final String REGEX_URL_TO_TITLE = "^([a-z]+://)?(www\\.)?"; - protected void showSubPage(Fragment subPage) { - getActivity().getSupportFragmentManager().beginTransaction() - .addToBackStack(null).replace(R.id.home_pager_container, subPage, HomePager.SUBPAGE_TAG) - .commitAllowingStateLoss(); - } - @Override public void onCreateContextMenu(ContextMenu menu, View view, ContextMenuInfo menuInfo) { if (menuInfo == null || !(menuInfo instanceof HomeContextMenuInfo)) { diff --git a/mobile/android/base/home/HomePager.java b/mobile/android/base/home/HomePager.java index 80f4a674b97..a7f0ee342f7 100644 --- a/mobile/android/base/home/HomePager.java +++ b/mobile/android/base/home/HomePager.java @@ -32,7 +32,7 @@ public class HomePager extends ViewPager { // List of pages in order. public enum Page { - VISITED, + HISTORY, BOOKMARKS, READING_LIST } @@ -70,7 +70,7 @@ public class HomePager extends ViewPager { TabsAdapter adapter = new TabsAdapter(fm); // Add the pages to the adapter in order. - adapter.addTab(Page.VISITED, VisitedPage.class, null, getContext().getString(R.string.visited_title)); + adapter.addTab(Page.HISTORY, HistoryPage.class, null, getContext().getString(R.string.home_history_title)); adapter.addTab(Page.BOOKMARKS, BookmarksPage.class, null, getContext().getString(R.string.bookmarks_title)); adapter.addTab(Page.READING_LIST, ReadingListPage.class, null, getContext().getString(R.string.reading_list)); diff --git a/mobile/android/base/home/MostRecentPage.java b/mobile/android/base/home/MostRecentPage.java new file mode 100644 index 00000000000..8dc0bd9656a --- /dev/null +++ b/mobile/android/base/home/MostRecentPage.java @@ -0,0 +1,365 @@ +/* -*- 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.R; +import org.mozilla.gecko.db.BrowserDB; +import org.mozilla.gecko.db.BrowserDB.URLColumns; +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.os.Bundle; +import android.support.v4.app.LoaderManager.LoaderCallbacks; +import android.support.v4.content.Loader; +import android.support.v4.widget.SimpleCursorAdapter; +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.TextView; + +import java.util.Date; + +/** + * Fragment that displays recent history in a ListView. + */ +public class MostRecentPage extends HomeFragment { + // Logging tag name + private static final String LOGTAG = "GeckoMostRecentPage"; + + // Cursor loader ID for history query + private static final int HISTORY_LOADER_ID = 0; + + // For the time sections in history + private static final long MS_PER_DAY = 86400000; + private static final long MS_PER_WEEK = MS_PER_DAY * 7; + + // The time ranges for each section + private static enum MostRecentSection { + TODAY, + YESTERDAY, + WEEK, + OLDER + }; + + // Maps headers in the list with their respective sections + private SparseArray mMostRecentSections; + + // Adapter for the list of search results + private MostRecentAdapter 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 MostRecentPage newInstance() { + return new MostRecentPage(); + } + + public MostRecentPage() { + 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(); + + mMostRecentSections = null; + mInflater = null; + mUrlOpenListener = null; + } + + @Override + public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { + return inflater.inflate(R.layout.home_most_recent_page, container, false); + } + + @Override + public void onViewCreated(View view, Bundle savedInstanceState) { + final TextView title = (TextView) view.findViewById(R.id.title); + title.setText(R.string.home_most_recent_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) { + position -= getMostRecentSectionsCountBefore(position); + + final Cursor c = mAdapter.getCursor(); + if (c == null || !c.moveToPosition(position)) { + return; + } + + final String url = c.getString(c.getColumnIndexOrThrow(URLColumns.URL)); + mUrlOpenListener.onUrlOpen(url); + } + }); + + registerForContextMenu(mList); + } + + @Override + public void onDestroyView() { + super.onDestroyView(); + mList = null; + } + + @Override + public void onActivityCreated(Bundle savedInstanceState) { + super.onActivityCreated(savedInstanceState); + + // Initialize map of history sections + mMostRecentSections = new SparseArray(); + + // Intialize adapter + mAdapter = new MostRecentAdapter(getActivity()); + mList.setAdapter(mAdapter); + + // Create callbacks before the initial loader is started + mCursorLoaderCallbacks = new CursorLoaderCallbacks(); + loadIfVisible(); + } + + @Override + protected void load() { + getLoaderManager().initLoader(HISTORY_LOADER_ID, null, mCursorLoaderCallbacks); + } + + private String getMostRecentSectionTitle(MostRecentSection section) { + final Resources resources = getActivity().getResources(); + + switch (section) { + case TODAY: + return resources.getString(R.string.history_today_section); + case YESTERDAY: + return resources.getString(R.string.history_yesterday_section); + case WEEK: + return resources.getString(R.string.history_week_section); + case OLDER: + return resources.getString(R.string.history_older_section); + } + + throw new IllegalStateException("Unrecognized history section"); + } + + private int getMostRecentSectionsCountBefore(int position) { + // Account for the number headers before the given position + int sectionsBefore = 0; + + final int historySectionsCount = mMostRecentSections.size(); + for (int i = 0; i < historySectionsCount; i++) { + final int sectionPosition = mMostRecentSections.keyAt(i); + if (sectionPosition > position) { + break; + } + + sectionsBefore++; + } + + return sectionsBefore; + } + + private MostRecentSection getMostRecentSectionForTime(long from, long time) { + long delta = from - time; + + if (delta < 0) { + return MostRecentSection.TODAY; + } + + if (delta < MS_PER_DAY) { + return MostRecentSection.YESTERDAY; + } + + if (delta < MS_PER_WEEK) { + return MostRecentSection.WEEK; + } + + return MostRecentSection.OLDER; + } + + private void loadMostRecentSections(Cursor c) { + if (c == null || !c.moveToFirst()) { + return; + } + + // Clear any history sections that may have been loaded before. + mMostRecentSections.clear(); + + final Date now = new Date(); + now.setHours(0); + now.setMinutes(0); + now.setSeconds(0); + + final long today = now.getTime(); + MostRecentSection section = null; + + do { + final int position = c.getPosition(); + final long time = c.getLong(c.getColumnIndexOrThrow(URLColumns.DATE_LAST_VISITED)); + final MostRecentSection itemSection = getMostRecentSectionForTime(today, time); + + if (section != itemSection) { + section = itemSection; + mMostRecentSections.append(position + mMostRecentSections.size(), section); + } + + // Reached the last section, no need to continue + if (section == MostRecentSection.OLDER) { + break; + } + } while (c.moveToNext()); + } + + private static class MostRecentCursorLoader extends SimpleCursorLoader { + // Max number of history results + private static final int HISTORY_LIMIT = 100; + + public MostRecentCursorLoader(Context context) { + super(context); + } + + @Override + public Cursor loadCursor() { + final ContentResolver cr = getContext().getContentResolver(); + return BrowserDB.getRecentHistory(cr, HISTORY_LIMIT); + } + } + + private class MostRecentAdapter extends SimpleCursorAdapter { + private static final int ROW_HEADER = 0; + private static final int ROW_STANDARD = 1; + + private static final int ROW_TYPE_COUNT = 2; + + public MostRecentAdapter(Context context) { + super(context, -1, null, new String[] {}, new int[] {}); + } + + @Override + public Object getItem(int position) { + final int type = getItemViewType(position); + + // Header items are not in the cursor + if (type == ROW_HEADER) { + return null; + } + + return super.getItem(position - getMostRecentSectionsCountBefore(position)); + } + + @Override + public int getItemViewType(int position) { + if (mMostRecentSections.get(position) != null) { + return ROW_HEADER; + } + + return ROW_STANDARD; + } + + @Override + public int getViewTypeCount() { + // view can be either a standard page row, or a header row + return ROW_TYPE_COUNT; + } + + @Override + public boolean isEnabled(int position) { + return (getItemViewType(position) == ROW_STANDARD); + } + + @Override + public int getCount() { + // Add the history section headers to the number of reported results. + return super.getCount() + mMostRecentSections.size(); + } + + @Override + public View getView(int position, View convertView, ViewGroup parent) { + final int type = getItemViewType(position); + + if (type == ROW_HEADER) { + final TextView row; + if (convertView == null) { + row = (TextView) mInflater.inflate(R.layout.home_header_row, mList, false); + } else { + row = (TextView) convertView; + } + + final MostRecentSection section = mMostRecentSections.get(position); + row.setText(getMostRecentSectionTitle(section)); + + return row; + } else { + final TwoLinePageRow row; + if (convertView == null) { + row = (TwoLinePageRow) mInflater.inflate(R.layout.home_item_row, mList, false); + } else { + row = (TwoLinePageRow) convertView; + } + + // Account for the search engines + position -= getMostRecentSectionsCountBefore(position); + + 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 { + @Override + public Loader onCreateLoader(int id, Bundle args) { + return new MostRecentCursorLoader(getActivity()); + } + + @Override + public void onLoadFinished(Loader loader, Cursor c) { + loadMostRecentSections(c); + mAdapter.swapCursor(c); + } + + @Override + public void onLoaderReset(Loader loader) { + mAdapter.swapCursor(null); + } + } +} diff --git a/mobile/android/base/home/VisitedPage.java b/mobile/android/base/home/MostVisitedPage.java similarity index 81% rename from mobile/android/base/home/VisitedPage.java rename to mobile/android/base/home/MostVisitedPage.java index 691fcb3bd6e..4f7db5e65a6 100644 --- a/mobile/android/base/home/VisitedPage.java +++ b/mobile/android/base/home/MostVisitedPage.java @@ -23,13 +23,14 @@ import android.view.View; import android.view.ViewGroup; import android.widget.AdapterView; import android.widget.ListView; +import android.widget.TextView; /** * Fragment that displays frecency search results in a ListView. */ -public class VisitedPage extends HomeFragment { +public class MostVisitedPage extends HomeFragment { // Logging tag name - private static final String LOGTAG = "GeckoVisitedPage"; + private static final String LOGTAG = "GeckoMostVisitedPage"; // Cursor loader ID for search query private static final int FRECENCY_LOADER_ID = 0; @@ -46,16 +47,17 @@ public class VisitedPage extends HomeFragment { // Empty message view private View mEmptyMessage; - // Buttons container - private View mButtonsContainer; - // Callbacks used for the search and favicon cursor loaders private CursorLoaderCallbacks mCursorLoaderCallbacks; // On URL open listener private OnUrlOpenListener mUrlOpenListener; - public VisitedPage() { + public static MostVisitedPage newInstance() { + return new MostVisitedPage(); + } + + public MostVisitedPage() { mUrlOpenListener = null; } @@ -80,14 +82,16 @@ public class VisitedPage extends HomeFragment { @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { - return inflater.inflate(R.layout.home_visited_page, container, false); + return inflater.inflate(R.layout.home_most_visited_page, container, false); } @Override public void onViewCreated(View view, Bundle savedInstanceState) { - super.onViewCreated(view, savedInstanceState); + final TextView title = (TextView) view.findViewById(R.id.title); + title.setText(R.string.home_most_visited_title); - mList = (HomeListView) view.findViewById(R.id.visited_list); + mEmptyMessage = view.findViewById(R.id.empty_message); + mList = (HomeListView) view.findViewById(R.id.list); mList.setOnItemClickListener(new AdapterView.OnItemClickListener() { @Override @@ -103,32 +107,12 @@ public class VisitedPage extends HomeFragment { }); registerForContextMenu(mList); - - mEmptyMessage = view.findViewById(R.id.empty_message); - mButtonsContainer = view.findViewById(R.id.buttons_container); - - final View historyButton = view.findViewById(R.id.history_button); - historyButton.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View v) { - showHistoryPage(); - } - }); - - final View tabsButton = view.findViewById(R.id.tabs_button); - tabsButton.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View v) { - showTabsPage(); - } - }); } @Override public void onDestroyView() { super.onDestroyView(); mList = null; - mButtonsContainer = null; mEmptyMessage = null; } @@ -150,16 +134,6 @@ public class VisitedPage extends HomeFragment { getLoaderManager().initLoader(FRECENCY_LOADER_ID, null, mCursorLoaderCallbacks); } - private void showHistoryPage() { - final HistoryPage historyPage = HistoryPage.newInstance(); - showSubPage(historyPage); - } - - private void showTabsPage() { - final LastTabsPage lastTabsPage = LastTabsPage.newInstance(); - showSubPage(lastTabsPage); - } - private static class FrecencyCursorLoader extends SimpleCursorLoader { // Max number of search results private static final int SEARCH_LIMIT = 50; @@ -215,9 +189,6 @@ public class VisitedPage extends HomeFragment { // flashing the empty message before loading. mList.setEmptyView(mEmptyMessage); - final int buttonsVisibility = (c.getCount() == 0 ? View.GONE : View.VISIBLE); - mButtonsContainer.setVisibility(buttonsVisibility); - mAdapter.swapCursor(c); FaviconsLoader.restartFromCursor(getLoaderManager(), FAVICONS_LOADER_ID, @@ -246,4 +217,4 @@ public class VisitedPage extends HomeFragment { } } } -} \ No newline at end of file +} diff --git a/mobile/android/base/locales/en-US/android_strings.dtd b/mobile/android/base/locales/en-US/android_strings.dtd index cadbbd13fc4..7070cc47b0e 100644 --- a/mobile/android/base/locales/en-US/android_strings.dtd +++ b/mobile/android/base/locales/en-US/android_strings.dtd @@ -8,9 +8,7 @@ - - @@ -269,8 +267,11 @@ size. --> - + + + + diff --git a/mobile/android/base/resources/drawable-hdpi/history_tabs_indicator_selected.9.png b/mobile/android/base/resources/drawable-hdpi/history_tabs_indicator_selected.9.png new file mode 100644 index 00000000000..03ead6aa8b1 Binary files /dev/null and b/mobile/android/base/resources/drawable-hdpi/history_tabs_indicator_selected.9.png differ diff --git a/mobile/android/base/resources/drawable-hdpi/icon_last_tabs.png b/mobile/android/base/resources/drawable-hdpi/icon_last_tabs.png new file mode 100644 index 00000000000..8c2b41f9d2f Binary files /dev/null and b/mobile/android/base/resources/drawable-hdpi/icon_last_tabs.png differ diff --git a/mobile/android/base/resources/drawable-hdpi/icon_most_recent.png b/mobile/android/base/resources/drawable-hdpi/icon_most_recent.png new file mode 100644 index 00000000000..1924bc4a044 Binary files /dev/null and b/mobile/android/base/resources/drawable-hdpi/icon_most_recent.png differ diff --git a/mobile/android/base/resources/drawable-hdpi/icon_most_visited.png b/mobile/android/base/resources/drawable-hdpi/icon_most_visited.png new file mode 100644 index 00000000000..e7cf2b3b9ce Binary files /dev/null and b/mobile/android/base/resources/drawable-hdpi/icon_most_visited.png differ diff --git a/mobile/android/base/resources/drawable-mdpi/history_tabs_indicator_selected.9.png b/mobile/android/base/resources/drawable-mdpi/history_tabs_indicator_selected.9.png new file mode 100644 index 00000000000..5def384c138 Binary files /dev/null and b/mobile/android/base/resources/drawable-mdpi/history_tabs_indicator_selected.9.png differ diff --git a/mobile/android/base/resources/drawable-mdpi/icon_last_tabs.png b/mobile/android/base/resources/drawable-mdpi/icon_last_tabs.png new file mode 100644 index 00000000000..f577fd4c46c Binary files /dev/null and b/mobile/android/base/resources/drawable-mdpi/icon_last_tabs.png differ diff --git a/mobile/android/base/resources/drawable-mdpi/icon_most_recent.png b/mobile/android/base/resources/drawable-mdpi/icon_most_recent.png new file mode 100644 index 00000000000..e0cfd2ddb78 Binary files /dev/null and b/mobile/android/base/resources/drawable-mdpi/icon_most_recent.png differ diff --git a/mobile/android/base/resources/drawable-mdpi/icon_most_visited.png b/mobile/android/base/resources/drawable-mdpi/icon_most_visited.png new file mode 100644 index 00000000000..e7ef4b91e47 Binary files /dev/null and b/mobile/android/base/resources/drawable-mdpi/icon_most_visited.png differ diff --git a/mobile/android/base/resources/drawable-xhdpi/history_tabs_indicator_selected.9.png b/mobile/android/base/resources/drawable-xhdpi/history_tabs_indicator_selected.9.png new file mode 100644 index 00000000000..86ba13a5f6f Binary files /dev/null and b/mobile/android/base/resources/drawable-xhdpi/history_tabs_indicator_selected.9.png differ diff --git a/mobile/android/base/resources/drawable-xhdpi/icon_last_tabs.png b/mobile/android/base/resources/drawable-xhdpi/icon_last_tabs.png new file mode 100644 index 00000000000..c102e8c9e17 Binary files /dev/null and b/mobile/android/base/resources/drawable-xhdpi/icon_last_tabs.png differ diff --git a/mobile/android/base/resources/drawable-xhdpi/icon_most_recent.png b/mobile/android/base/resources/drawable-xhdpi/icon_most_recent.png new file mode 100644 index 00000000000..7cd12a9abea Binary files /dev/null and b/mobile/android/base/resources/drawable-xhdpi/icon_most_recent.png differ diff --git a/mobile/android/base/resources/drawable-xhdpi/icon_most_visited.png b/mobile/android/base/resources/drawable-xhdpi/icon_most_visited.png new file mode 100644 index 00000000000..ccdb7fca621 Binary files /dev/null and b/mobile/android/base/resources/drawable-xhdpi/icon_most_visited.png differ diff --git a/mobile/android/base/resources/drawable/home_history_tabs_indicator.xml b/mobile/android/base/resources/drawable/home_history_tabs_indicator.xml new file mode 100644 index 00000000000..d0c3e6b75db --- /dev/null +++ b/mobile/android/base/resources/drawable/home_history_tabs_indicator.xml @@ -0,0 +1,48 @@ + + + + + + + + + + + + + + + + + + + + + + diff --git a/mobile/android/base/resources/drawable/home_page_title_background.xml b/mobile/android/base/resources/drawable/home_page_title_background.xml new file mode 100644 index 00000000000..1e98bd082ae --- /dev/null +++ b/mobile/android/base/resources/drawable/home_page_title_background.xml @@ -0,0 +1,15 @@ + + + + + + + + + + + + diff --git a/mobile/android/base/resources/layout/home_history_page.xml b/mobile/android/base/resources/layout/home_history_page.xml index 8fb5a82f79c..70e0589ad9f 100644 --- a/mobile/android/base/resources/layout/home_history_page.xml +++ b/mobile/android/base/resources/layout/home_history_page.xml @@ -6,9 +6,19 @@ + android:orientation="vertical"> - + - \ No newline at end of file + + + diff --git a/mobile/android/base/resources/layout/home_history_tabs_indicator.xml b/mobile/android/base/resources/layout/home_history_tabs_indicator.xml new file mode 100644 index 00000000000..5ef9f2765df --- /dev/null +++ b/mobile/android/base/resources/layout/home_history_tabs_indicator.xml @@ -0,0 +1,9 @@ + + + + diff --git a/mobile/android/base/resources/layout/home_last_tabs_page.xml b/mobile/android/base/resources/layout/home_last_tabs_page.xml index 7fd346fc0ce..b8e236839d2 100644 --- a/mobile/android/base/resources/layout/home_last_tabs_page.xml +++ b/mobile/android/base/resources/layout/home_last_tabs_page.xml @@ -13,7 +13,7 @@ + android:background="@color/home_last_tab_bar_bg">