diff --git a/mobile/android/base/BrowserApp.java b/mobile/android/base/BrowserApp.java index 6ca55e60156d..1e0635d57e4f 100644 --- a/mobile/android/base/BrowserApp.java +++ b/mobile/android/base/BrowserApp.java @@ -1401,7 +1401,7 @@ abstract public class BrowserApp extends GeckoApp animator.setUseHardwareLayer(false); mBrowserToolbar.startEditing(url, animator); - showHomePagerWithAnimator(HomePager.Page.HISTORY, animator); + showHomePagerWithAnimator(HomePager.Page.TOP_SITES, animator); animator.start(); } diff --git a/mobile/android/base/Makefile.in b/mobile/android/base/Makefile.in index 2a93655da6c1..4be661122203 100644 --- a/mobile/android/base/Makefile.in +++ b/mobile/android/base/Makefile.in @@ -230,7 +230,6 @@ FENNEC_JAVA_FILES = \ home/FaviconsLoader.java \ home/LastTabsPage.java \ home/MostRecentPage.java \ - home/MostVisitedPage.java \ home/MultiTypeCursorAdapter.java \ home/PinBookmarkDialog.java \ home/ReadingListPage.java \ @@ -243,6 +242,7 @@ FENNEC_JAVA_FILES = \ home/TopBookmarkItemView.java \ home/TopBookmarksAdapter.java \ home/TopBookmarksView.java \ + home/TopSitesPage.java \ home/TwoLinePageRow.java \ menu/GeckoMenu.java \ menu/GeckoMenuInflater.java \ @@ -478,12 +478,12 @@ RES_LAYOUT = \ res/layout/home_last_tabs_page.xml \ res/layout/home_history_list.xml \ res/layout/home_most_recent_page.xml \ - res/layout/home_most_visited_page.xml \ res/layout/home_pager.xml \ res/layout/home_reading_list_page.xml \ res/layout/home_search_item_row.xml \ res/layout/home_banner.xml \ res/layout/home_suggestion_prompt.xml \ + res/layout/home_top_sites_page.xml \ res/layout/web_app.xml \ res/layout/launch_app_list.xml \ res/layout/launch_app_listitem.xml \ diff --git a/mobile/android/base/Tab.java b/mobile/android/base/Tab.java index 2f8499b83677..ca53b8fbd8d8 100644 --- a/mobile/android/base/Tab.java +++ b/mobile/android/base/Tab.java @@ -93,7 +93,7 @@ public class Tab { mUserSearch = ""; mExternal = external; mParentId = parentId; - mAboutHomePage = HomePager.Page.BOOKMARKS; + mAboutHomePage = HomePager.Page.TOP_SITES; mTitle = title == null ? "" : title; mFavicon = null; mFaviconUrl = null; diff --git a/mobile/android/base/home/BookmarksPage.java b/mobile/android/base/home/BookmarksPage.java index f3c28e4cb404..0ef21ea3512b 100644 --- a/mobile/android/base/home/BookmarksPage.java +++ b/mobile/android/base/home/BookmarksPage.java @@ -8,21 +8,12 @@ package org.mozilla.gecko.home; import org.mozilla.gecko.favicons.Favicons; import org.mozilla.gecko.R; import org.mozilla.gecko.Tabs; -import org.mozilla.gecko.animation.PropertyAnimator; -import org.mozilla.gecko.animation.PropertyAnimator.Property; -import org.mozilla.gecko.animation.ViewHelper; import org.mozilla.gecko.db.BrowserContract.Bookmarks; -import org.mozilla.gecko.db.BrowserContract.Thumbnails; import org.mozilla.gecko.db.BrowserDB; import org.mozilla.gecko.db.BrowserDB.URLColumns; import org.mozilla.gecko.gfx.BitmapUtils; import org.mozilla.gecko.home.BookmarksListAdapter.OnRefreshFolderListener; -import org.mozilla.gecko.home.HomeListView.HomeContextMenuInfo; import org.mozilla.gecko.home.HomePager.OnUrlOpenListener; -import org.mozilla.gecko.home.PinBookmarkDialog.OnBookmarkSelectedListener; -import org.mozilla.gecko.home.TopBookmarksAdapter.Thumbnail; -import org.mozilla.gecko.home.TopBookmarksView.OnPinBookmarkListener; -import org.mozilla.gecko.home.TopBookmarksView.TopBookmarksContextMenuInfo; import org.mozilla.gecko.util.ThreadUtils; import android.app.Activity; @@ -32,27 +23,14 @@ import android.content.res.Configuration; import android.database.Cursor; import android.graphics.Bitmap; import android.os.Bundle; -import android.support.v4.app.FragmentManager; import android.support.v4.app.LoaderManager; import android.support.v4.app.LoaderManager.LoaderCallbacks; -import android.support.v4.content.AsyncTaskLoader; import android.support.v4.content.Loader; import android.text.TextUtils; import android.util.Log; -import android.view.ContextMenu; -import android.view.ContextMenu.ContextMenuInfo; import android.view.LayoutInflater; -import android.view.MenuInflater; -import android.view.MenuItem; -import android.view.MotionEvent; import android.view.View; -import android.view.View.OnTouchListener; import android.view.ViewGroup; -import android.widget.Toast; - -import java.util.ArrayList; -import java.util.HashMap; -import java.util.Map; /** * A page in about:home that displays a ListView of bookmarks. @@ -63,57 +41,24 @@ public class BookmarksPage extends HomeFragment { // Cursor loader ID for list of bookmarks. private static final int LOADER_ID_BOOKMARKS_LIST = 0; - // Cursor loader ID for grid of bookmarks. - private static final int LOADER_ID_TOP_BOOKMARKS = 1; - - // Loader ID for thumbnails. - private static final int LOADER_ID_THUMBNAILS = 2; - // Key for bookmarks folder id. private static final String BOOKMARKS_FOLDER_KEY = "folder_id"; - // Key for thumbnail urls. - private static final String THUMBNAILS_URLS_KEY = "urls"; - // List of bookmarks. private BookmarksListView mList; - // Grid of top bookmarks. - private TopBookmarksView mTopBookmarks; - - // Banner to show snippets. - private HomeBanner mBanner; - // Adapter for list of bookmarks. private BookmarksListAdapter mListAdapter; - // Adapter for grid of bookmarks. - private TopBookmarksAdapter mTopBookmarksAdapter; - // Callback for cursor loaders. private CursorLoaderCallbacks mLoaderCallbacks; - // Callback for thumbnail loader. - private ThumbnailsLoaderCallbacks mThumbnailsLoaderCallbacks; - - // Listener for pinning bookmarks. - private PinBookmarkListener mPinBookmarkListener; - - // Raw Y value of the last event that happened on the list view. - private float mListTouchY = -1; - - // Scrolling direction of the banner. - private boolean mSnapBannerToTop; - @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { final View view = inflater.inflate(R.layout.home_bookmarks_page, container, false); mList = (BookmarksListView) view.findViewById(R.id.bookmarks_list); - mTopBookmarks = new TopBookmarksView(getActivity()); - mList.addHeaderView(mTopBookmarks); - return view; } @@ -129,26 +74,11 @@ public class BookmarksPage extends HomeFragment { + " must implement HomePager.OnUrlOpenListener"); } - mPinBookmarkListener = new PinBookmarkListener(); - mList.setTag(HomePager.LIST_TAG_BOOKMARKS); mList.setOnUrlOpenListener(listener); mList.setHeaderDividersEnabled(false); - mTopBookmarks.setOnUrlOpenListener(listener); - mTopBookmarks.setOnPinBookmarkListener(mPinBookmarkListener); - registerForContextMenu(mList); - registerForContextMenu(mTopBookmarks); - - mBanner = (HomeBanner) view.findViewById(R.id.home_banner); - mList.setOnTouchListener(new OnTouchListener() { - @Override - public boolean onTouch(View v, MotionEvent event) { - BookmarksPage.this.handleListTouchEvent(event); - return false; - } - }); } @Override @@ -157,10 +87,6 @@ public class BookmarksPage extends HomeFragment { final Activity activity = getActivity(); - // Setup the top bookmarks adapter. - mTopBookmarksAdapter = new TopBookmarksAdapter(activity, null); - mTopBookmarks.setAdapter(mTopBookmarksAdapter); - // Setup the list adapter. mListAdapter = new BookmarksListAdapter(activity, null); mListAdapter.setOnRefreshFolderListener(new OnRefreshFolderListener() { @@ -180,7 +106,6 @@ public class BookmarksPage extends HomeFragment { // Create callbacks before the initial loader is started. mLoaderCallbacks = new CursorLoaderCallbacks(activity, getLoaderManager()); - mThumbnailsLoaderCallbacks = new ThumbnailsLoaderCallbacks(); loadIfVisible(); } @@ -188,9 +113,6 @@ public class BookmarksPage extends HomeFragment { public void onDestroyView() { mList = null; mListAdapter = null; - mTopBookmarks = null; - mTopBookmarksAdapter = null; - mPinBookmarkListener = null; super.onDestroyView(); } @@ -214,204 +136,9 @@ public class BookmarksPage extends HomeFragment { } } - private void handleListTouchEvent(MotionEvent event) { - // Ignore the event if the banner is hidden for this session. - if (mBanner.isDismissed()) { - return; - } - - switch (event.getActionMasked()) { - case MotionEvent.ACTION_DOWN: { - mListTouchY = event.getRawY(); - break; - } - - case MotionEvent.ACTION_MOVE: { - // There is a chance that we won't receive ACTION_DOWN, if the touch event - // actually started on the Grid instead of the List. Treat this as first event. - if (mListTouchY == -1) { - mListTouchY = event.getRawY(); - return; - } - - final float curY = event.getRawY(); - final float delta = mListTouchY - curY; - mSnapBannerToTop = (delta > 0.0f) ? false : true; - - final float height = mBanner.getHeight(); - float newTranslationY = ViewHelper.getTranslationY(mBanner) + delta; - - // Clamp the values to be between 0 and height. - if (newTranslationY < 0.0f) { - newTranslationY = 0.0f; - } else if (newTranslationY > height) { - newTranslationY = height; - } - - ViewHelper.setTranslationY(mBanner, newTranslationY); - mListTouchY = curY; - break; - } - - case MotionEvent.ACTION_UP: - case MotionEvent.ACTION_CANCEL: { - mListTouchY = -1; - final float y = ViewHelper.getTranslationY(mBanner); - final float height = mBanner.getHeight(); - if (y > 0.0f && y < height) { - final PropertyAnimator animator = new PropertyAnimator(100); - animator.attach(mBanner, Property.TRANSLATION_Y, mSnapBannerToTop ? 0 : height); - animator.start(); - } - break; - } - } - } - - @Override - public void onCreateContextMenu(ContextMenu menu, View view, ContextMenuInfo menuInfo) { - if (menuInfo == null) { - return; - } - - // HomeFragment will handle the default case. - if (menuInfo instanceof HomeContextMenuInfo) { - super.onCreateContextMenu(menu, view, menuInfo); - } - - if (!(menuInfo instanceof TopBookmarksContextMenuInfo)) { - return; - } - - MenuInflater inflater = new MenuInflater(view.getContext()); - inflater.inflate(R.menu.top_bookmarks_contextmenu, menu); - - TopBookmarksContextMenuInfo info = (TopBookmarksContextMenuInfo) menuInfo; - menu.setHeaderTitle(info.getDisplayTitle()); - - if (!TextUtils.isEmpty(info.url)) { - if (info.isPinned) { - menu.findItem(R.id.top_bookmarks_pin).setVisible(false); - } else { - menu.findItem(R.id.top_bookmarks_unpin).setVisible(false); - } - } else { - menu.findItem(R.id.top_bookmarks_open_new_tab).setVisible(false); - menu.findItem(R.id.top_bookmarks_open_private_tab).setVisible(false); - menu.findItem(R.id.top_bookmarks_pin).setVisible(false); - menu.findItem(R.id.top_bookmarks_unpin).setVisible(false); - } - } - - @Override - public boolean onContextItemSelected(MenuItem item) { - ContextMenuInfo menuInfo = item.getMenuInfo(); - - // HomeFragment will handle the default case. - if (menuInfo == null || !(menuInfo instanceof TopBookmarksContextMenuInfo)) { - return false; - } - - TopBookmarksContextMenuInfo info = (TopBookmarksContextMenuInfo) menuInfo; - final Activity activity = getActivity(); - - final int itemId = item.getItemId(); - if (itemId == R.id.top_bookmarks_open_new_tab || itemId == R.id.top_bookmarks_open_private_tab) { - if (info.url == null) { - Log.e(LOGTAG, "Can't open in new tab because URL is null"); - return false; - } - - int flags = Tabs.LOADURL_NEW_TAB | Tabs.LOADURL_BACKGROUND; - if (item.getItemId() == R.id.top_bookmarks_open_private_tab) - flags |= Tabs.LOADURL_PRIVATE; - - Tabs.getInstance().loadUrl(info.url, flags); - Toast.makeText(activity, R.string.new_tab_opened, Toast.LENGTH_SHORT).show(); - return true; - } - - if (itemId == R.id.top_bookmarks_pin) { - final String url = info.url; - final String title = info.title; - final int position = info.position; - final Context context = getActivity().getApplicationContext(); - - ThreadUtils.postToBackgroundThread(new Runnable() { - @Override - public void run() { - BrowserDB.pinSite(context.getContentResolver(), url, title, position); - } - }); - - return true; - } - - if (itemId == R.id.top_bookmarks_unpin) { - final int position = info.position; - final Context context = getActivity().getApplicationContext(); - - ThreadUtils.postToBackgroundThread(new Runnable() { - @Override - public void run() { - BrowserDB.unpinSite(context.getContentResolver(), position); - } - }); - - return true; - } - - if (itemId == R.id.top_bookmarks_edit) { - mPinBookmarkListener.onPinBookmark(info.position); - return true; - } - - return false; - } - @Override protected void load() { - final LoaderManager manager = getLoaderManager(); - manager.initLoader(LOADER_ID_BOOKMARKS_LIST, null, mLoaderCallbacks); - manager.initLoader(LOADER_ID_TOP_BOOKMARKS, null, mLoaderCallbacks); - } - - /** - * Listener for pinning bookmarks. - */ - private class PinBookmarkListener implements OnPinBookmarkListener, - OnBookmarkSelectedListener { - // Tag for the PinBookmarkDialog fragment. - private static final String TAG_PIN_BOOKMARK = "pin_bookmark"; - - // Position of the pin. - private int mPosition; - - @Override - public void onPinBookmark(int position) { - mPosition = position; - - final FragmentManager manager = getActivity().getSupportFragmentManager(); - PinBookmarkDialog dialog = (PinBookmarkDialog) manager.findFragmentByTag(TAG_PIN_BOOKMARK); - if (dialog == null) { - dialog = PinBookmarkDialog.newInstance(); - } - - dialog.setOnBookmarkSelectedListener(this); - dialog.show(manager, TAG_PIN_BOOKMARK); - } - - @Override - public void onBookmarkSelected(final String url, final String title) { - final int position = mPosition; - final Context context = getActivity().getApplicationContext(); - ThreadUtils.postToBackgroundThread(new Runnable() { - @Override - public void run() { - BrowserDB.pinSite(context.getContentResolver(), url, title, position); - } - }); - } + getLoaderManager().initLoader(LOADER_ID_BOOKMARKS_LIST, null, mLoaderCallbacks); } /** @@ -435,21 +162,6 @@ public class BookmarksPage extends HomeFragment { } } - /** - * Loader for the grid for top bookmarks. - */ - private static class TopBookmarksLoader extends SimpleCursorLoader { - public TopBookmarksLoader(Context context) { - super(context); - } - - @Override - public Cursor loadCursor() { - final int max = getContext().getResources().getInteger(R.integer.number_of_top_sites); - return BrowserDB.getTopBookmarks(getContext().getContentResolver(), max); - } - } - /** * Loader callbacks for the LoaderManager of this fragment. */ @@ -469,10 +181,6 @@ public class BookmarksPage extends HomeFragment { } } - case LOADER_ID_TOP_BOOKMARKS: { - return new TopBookmarksLoader(getActivity()); - } - default: { return super.onCreateLoader(id, args); } @@ -490,26 +198,6 @@ public class BookmarksPage extends HomeFragment { break; } - case LOADER_ID_TOP_BOOKMARKS: { - mTopBookmarksAdapter.swapCursor(c); - - // Load the thumbnails. - if (c.getCount() > 0 && c.moveToFirst()) { - final ArrayList urls = new ArrayList(); - do { - final String url = c.getString(c.getColumnIndexOrThrow(URLColumns.URL)); - urls.add(url); - } while (c.moveToNext()); - - if (urls.size() > 0) { - Bundle bundle = new Bundle(); - bundle.putStringArrayList(THUMBNAILS_URLS_KEY, urls); - getLoaderManager().restartLoader(LOADER_ID_THUMBNAILS, bundle, mThumbnailsLoaderCallbacks); - } - } - break; - } - default: { super.onLoadFinished(loader, c); break; @@ -528,13 +216,6 @@ public class BookmarksPage extends HomeFragment { break; } - case LOADER_ID_TOP_BOOKMARKS: { - if (mTopBookmarks != null) { - mTopBookmarksAdapter.swapCursor(null); - break; - } - } - default: { super.onLoaderReset(loader); break; @@ -547,132 +228,4 @@ public class BookmarksPage extends HomeFragment { mListAdapter.notifyDataSetChanged(); } } - - /** - * An AsyncTaskLoader to load the thumbnails from a cursor. - */ - private static class ThumbnailsLoader extends AsyncTaskLoader> { - private Map mThumbnails; - private ArrayList mUrls; - - public ThumbnailsLoader(Context context, ArrayList urls) { - super(context); - mUrls = urls; - } - - @Override - public Map loadInBackground() { - if (mUrls == null || mUrls.size() == 0) { - return null; - } - - final Map thumbnails = new HashMap(); - - // Query the DB for thumbnails. - final ContentResolver cr = getContext().getContentResolver(); - final Cursor cursor = BrowserDB.getThumbnailsForUrls(cr, mUrls); - - try { - if (cursor != null && cursor.moveToFirst()) { - do { - // Try to get the thumbnail, if cursor is valid. - String url = cursor.getString(cursor.getColumnIndexOrThrow(Thumbnails.URL)); - final byte[] b = cursor.getBlob(cursor.getColumnIndexOrThrow(Thumbnails.DATA)); - final Bitmap bitmap = (b == null ? null : BitmapUtils.decodeByteArray(b)); - - if (bitmap != null) { - thumbnails.put(url, new Thumbnail(bitmap, true)); - } - } while (cursor.moveToNext()); - } - } finally { - if (cursor != null) { - cursor.close(); - } - } - - // Query the DB for favicons for the urls without thumbnails. - for (String url : mUrls) { - if (!thumbnails.containsKey(url)) { - final Bitmap bitmap = BrowserDB.getFaviconForUrl(cr, url); - if (bitmap != null) { - // Favicons.scaleImage can return several different size favicons, - // but will at least prevent this from being too large. - thumbnails.put(url, new Thumbnail(Favicons.scaleImage(bitmap), false)); - } - } - } - - return thumbnails; - } - - @Override - public void deliverResult(Map thumbnails) { - if (isReset()) { - mThumbnails = null; - return; - } - - mThumbnails = thumbnails; - - if (isStarted()) { - super.deliverResult(thumbnails); - } - } - - @Override - protected void onStartLoading() { - if (mThumbnails != null) { - deliverResult(mThumbnails); - } - - if (takeContentChanged() || mThumbnails == null) { - forceLoad(); - } - } - - @Override - protected void onStopLoading() { - cancelLoad(); - } - - @Override - public void onCanceled(Map thumbnails) { - mThumbnails = null; - } - - @Override - protected void onReset() { - super.onReset(); - - // Ensure the loader is stopped. - onStopLoading(); - - mThumbnails = null; - } - } - - /** - * Loader callbacks for the thumbnails on TopBookmarksView. - */ - private class ThumbnailsLoaderCallbacks implements LoaderCallbacks> { - @Override - public Loader> onCreateLoader(int id, Bundle args) { - return new ThumbnailsLoader(getActivity(), args.getStringArrayList(THUMBNAILS_URLS_KEY)); - } - - @Override - public void onLoadFinished(Loader> loader, Map thumbnails) { - if (mTopBookmarksAdapter != null) { - mTopBookmarksAdapter.updateThumbnails(thumbnails); - } - } - - @Override - public void onLoaderReset(Loader> loader) { - if (mTopBookmarksAdapter != null) { - mTopBookmarksAdapter.updateThumbnails(null); - } - } - } } diff --git a/mobile/android/base/home/HistoryPage.java b/mobile/android/base/home/HistoryPage.java index d57ec60b7051..af91078daf08 100644 --- a/mobile/android/base/home/HistoryPage.java +++ b/mobile/android/base/home/HistoryPage.java @@ -25,7 +25,7 @@ public class HistoryPage extends HomeFragment private static final String LOGTAG = "GeckoHistoryPage"; private IconTabWidget mTabWidget; private int mSelectedTab; - private boolean initializeVisitedPage; + private boolean initializeRecentPage; @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { @@ -38,7 +38,6 @@ public class HistoryPage extends HomeFragment mTabWidget = (IconTabWidget) view.findViewById(R.id.tab_icon_widget); - mTabWidget.addTab(R.drawable.icon_most_visited, R.string.home_most_visited_title); mTabWidget.addTab(R.drawable.icon_most_recent, R.string.home_most_recent_title); mTabWidget.addTab(R.drawable.icon_last_tabs, R.string.home_last_tabs_title); @@ -50,11 +49,11 @@ public class HistoryPage extends HomeFragment @Override public void load() { - // Show most visited page as the initial page. + // Show most recent page as the initial page. // Since we detach/attach on config change, this prevents from replacing current fragment. - if (!initializeVisitedPage) { - showMostVisitedPage(); - initializeVisitedPage = true; + if (!initializeRecentPage) { + showMostRecentPage(); + initializeRecentPage = true; } } @@ -65,10 +64,8 @@ public class HistoryPage extends HomeFragment } if (index == 0) { - showMostVisitedPage(); - } else if (index == 1) { showMostRecentPage(); - } else if (index == 2) { + } else if (index == 1) { showLastTabsPage(); } @@ -95,15 +92,10 @@ public class HistoryPage extends HomeFragment subPage.setArguments(args); getChildFragmentManager().beginTransaction() - .addToBackStack(null).replace(R.id.visited_page_container, subPage) + .addToBackStack(null).replace(R.id.history_page_container, subPage) .commitAllowingStateLoss(); } - private void showMostVisitedPage() { - final MostVisitedPage mostVisitedPage = MostVisitedPage.newInstance(); - showSubPage(mostVisitedPage); - } - private void showMostRecentPage() { final MostRecentPage mostRecentPage = MostRecentPage.newInstance(); showSubPage(mostRecentPage); diff --git a/mobile/android/base/home/HomePager.java b/mobile/android/base/home/HomePager.java index 11bea987304d..6edd29e12a91 100644 --- a/mobile/android/base/home/HomePager.java +++ b/mobile/android/base/home/HomePager.java @@ -8,6 +8,7 @@ package org.mozilla.gecko.home; import org.mozilla.gecko.R; import org.mozilla.gecko.animation.PropertyAnimator; import org.mozilla.gecko.animation.ViewHelper; +import org.mozilla.gecko.util.HardwareUtils; import android.content.Context; import android.os.Build; @@ -38,6 +39,7 @@ public class HomePager extends ViewPager { // List of pages in order. public enum Page { HISTORY, + TOP_SITES, BOOKMARKS, READING_LIST } @@ -90,9 +92,9 @@ public class HomePager extends ViewPager { super(context, attrs); mContext = context; - // This is to keep all 3 pages in memory after they are + // This is to keep all 4 pages in memory after they are // selected in the pager. - setOffscreenPageLimit(2); + setOffscreenPageLimit(3); } @Override @@ -131,14 +133,19 @@ public class HomePager extends ViewPager { // Only animate on post-HC devices, when a non-null animator is given final boolean shouldAnimate = (animator != null && Build.VERSION.SDK_INT >= 11); - // Add the pages to the adapter in order. - adapter.addTab(Page.HISTORY, HistoryPage.class, new Bundle(), - getContext().getString(R.string.home_history_title)); + adapter.addTab(Page.TOP_SITES, TopSitesPage.class, new Bundle(), + getContext().getString(R.string.home_top_sites_title)); adapter.addTab(Page.BOOKMARKS, BookmarksPage.class, new Bundle(), getContext().getString(R.string.bookmarks_title)); adapter.addTab(Page.READING_LIST, ReadingListPage.class, new Bundle(), getContext().getString(R.string.reading_list_title)); + // On phones, the history tab is the first tab. On tablets, the + // history tab is the last tab. + adapter.addTab(HardwareUtils.isTablet() ? -1 : 0, + Page.HISTORY, HistoryPage.class, new Bundle(), + getContext().getString(R.string.home_history_title)); + adapter.setCanLoadHint(!shouldAnimate); setAdapter(adapter); @@ -226,8 +233,18 @@ public class HomePager extends ViewPager { } public void addTab(Page page, Class clss, Bundle args, String title) { + addTab(-1, page, clss, args, title); + } + + public void addTab(int index, Page page, Class clss, Bundle args, String title) { TabInfo info = new TabInfo(page, clss, args, title); - mTabs.add(info); + + if (index >= 0) { + mTabs.add(index, info); + } else { + mTabs.add(info); + } + notifyDataSetChanged(); if (mDecor != null) { diff --git a/mobile/android/base/home/MostVisitedPage.java b/mobile/android/base/home/TopSitesPage.java similarity index 61% rename from mobile/android/base/home/MostVisitedPage.java rename to mobile/android/base/home/TopSitesPage.java index fff8e9ec0afb..8ce4d6905197 100644 --- a/mobile/android/base/home/MostVisitedPage.java +++ b/mobile/android/base/home/TopSitesPage.java @@ -6,6 +6,9 @@ package org.mozilla.gecko.home; import org.mozilla.gecko.R; +import org.mozilla.gecko.animation.PropertyAnimator; +import org.mozilla.gecko.animation.PropertyAnimator.Property; +import org.mozilla.gecko.animation.ViewHelper; import org.mozilla.gecko.db.BrowserDB; import org.mozilla.gecko.db.BrowserDB.URLColumns; import org.mozilla.gecko.home.HomePager.OnUrlOpenListener; @@ -13,13 +16,16 @@ import org.mozilla.gecko.home.HomePager.OnUrlOpenListener; import android.app.Activity; import android.content.ContentResolver; import android.content.Context; +import android.content.res.Configuration; 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.CursorAdapter; import android.view.LayoutInflater; +import android.view.MotionEvent; import android.view.View; +import android.view.View.OnTouchListener; import android.view.ViewGroup; import android.view.ViewStub; import android.widget.AdapterView; @@ -32,12 +38,12 @@ import java.util.EnumSet; /** * Fragment that displays frecency search results in a ListView. */ -public class MostVisitedPage extends HomeFragment { +public class TopSitesPage extends HomeFragment { // Logging tag name - private static final String LOGTAG = "GeckoMostVisitedPage"; + private static final String LOGTAG = "GeckoTopSitesPage"; // Cursor loader ID for search query - private static final int LOADER_ID_FRECENCY = 0; + private static final int LOADER_ID_TOP_SITES = 0; // Adapter for the list of search results private VisitedAdapter mAdapter; @@ -45,23 +51,29 @@ public class MostVisitedPage extends HomeFragment { // The view shown by the fragment. private ListView mList; - // The title for this HomeFragment page. - private TextView mTitle; - // Reference to the View to display when there are no results. private View mEmptyView; + // Banner to show snippets. + private HomeBanner mBanner; + + // Raw Y value of the last event that happened on the list view. + private float mListTouchY = -1; + + // Scrolling direction of the banner. + private boolean mSnapBannerToTop; + // Callbacks used for the search and favicon cursor loaders private CursorLoaderCallbacks mCursorLoaderCallbacks; // On URL open listener private OnUrlOpenListener mUrlOpenListener; - public static MostVisitedPage newInstance() { - return new MostVisitedPage(); + public static TopSitesPage newInstance() { + return new TopSitesPage(); } - public MostVisitedPage() { + public TopSitesPage() { mUrlOpenListener = null; } @@ -86,16 +98,11 @@ public class MostVisitedPage extends HomeFragment { @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { - return inflater.inflate(R.layout.home_most_visited_page, container, false); + return inflater.inflate(R.layout.home_top_sites_page, container, false); } @Override public void onViewCreated(View view, Bundle savedInstanceState) { - mTitle = (TextView) view.findViewById(R.id.title); - if (mTitle != null) { - mTitle.setText(R.string.home_most_visited_title); - } - mList = (HomeListView) view.findViewById(R.id.list); mList.setTag(HomePager.LIST_TAG_MOST_VISITED); @@ -115,16 +122,37 @@ public class MostVisitedPage extends HomeFragment { }); registerForContextMenu(mList); + + mBanner = (HomeBanner) view.findViewById(R.id.home_banner); + mList.setOnTouchListener(new OnTouchListener() { + @Override + public boolean onTouch(View v, MotionEvent event) { + TopSitesPage.this.handleListTouchEvent(event); + return false; + } + }); } @Override public void onDestroyView() { super.onDestroyView(); mList = null; - mTitle = null; mEmptyView = null; } + @Override + public void onConfigurationChanged(Configuration newConfig) { + super.onConfigurationChanged(newConfig); + + // Detach and reattach the fragment as the layout changes. + if (isVisible()) { + getFragmentManager().beginTransaction() + .detach(this) + .attach(this) + .commitAllowingStateLoss(); + } + } + @Override public void onActivityCreated(Bundle savedInstanceState) { super.onActivityCreated(savedInstanceState); @@ -142,23 +170,68 @@ public class MostVisitedPage extends HomeFragment { @Override protected void load() { - getLoaderManager().initLoader(LOADER_ID_FRECENCY, null, mCursorLoaderCallbacks); + getLoaderManager().initLoader(LOADER_ID_TOP_SITES, null, mCursorLoaderCallbacks); + } + + private void handleListTouchEvent(MotionEvent event) { + // Ignore the event if the banner is hidden for this session. + if (mBanner.isDismissed()) { + return; + } + + switch (event.getActionMasked()) { + case MotionEvent.ACTION_DOWN: { + mListTouchY = event.getRawY(); + break; + } + + case MotionEvent.ACTION_MOVE: { + // There is a chance that we won't receive ACTION_DOWN, if the touch event + // actually started on the Grid instead of the List. Treat this as first event. + if (mListTouchY == -1) { + mListTouchY = event.getRawY(); + return; + } + + final float curY = event.getRawY(); + final float delta = mListTouchY - curY; + mSnapBannerToTop = (delta > 0.0f) ? false : true; + + final float height = mBanner.getHeight(); + float newTranslationY = ViewHelper.getTranslationY(mBanner) + delta; + + // Clamp the values to be between 0 and height. + if (newTranslationY < 0.0f) { + newTranslationY = 0.0f; + } else if (newTranslationY > height) { + newTranslationY = height; + } + + ViewHelper.setTranslationY(mBanner, newTranslationY); + mListTouchY = curY; + break; + } + + case MotionEvent.ACTION_UP: + case MotionEvent.ACTION_CANCEL: { + mListTouchY = -1; + final float y = ViewHelper.getTranslationY(mBanner); + final float height = mBanner.getHeight(); + if (y > 0.0f && y < height) { + final PropertyAnimator animator = new PropertyAnimator(100); + animator.attach(mBanner, Property.TRANSLATION_Y, mSnapBannerToTop ? 0 : height); + animator.start(); + } + break; + } + } } private void updateUiFromCursor(Cursor c) { if (c != null && c.getCount() > 0) { - if (mTitle != null) { - mTitle.setVisibility(View.VISIBLE); - } return; } - // Cursor is empty, so hide the title and set the - // empty view if it hasn't been set already. - if (mTitle != null) { - mTitle.setVisibility(View.GONE); - } - if (mEmptyView == null) { // Set empty page view. We delay this so that the empty view won't flash. ViewStub emptyViewStub = (ViewStub) getView().findViewById(R.id.home_empty_view_stub); @@ -174,11 +247,11 @@ public class MostVisitedPage extends HomeFragment { } } - private static class FrecencyCursorLoader extends SimpleCursorLoader { + private static class TopSitesCursorLoader extends SimpleCursorLoader { // Max number of search results private static final int SEARCH_LIMIT = 50; - public FrecencyCursorLoader(Context context) { + public TopSitesCursorLoader(Context context) { super(context); } @@ -213,8 +286,8 @@ public class MostVisitedPage extends HomeFragment { @Override public Loader onCreateLoader(int id, Bundle args) { - if (id == LOADER_ID_FRECENCY) { - return new FrecencyCursorLoader(getActivity()); + if (id == LOADER_ID_TOP_SITES) { + return new TopSitesCursorLoader(getActivity()); } else { return super.onCreateLoader(id, args); } @@ -222,7 +295,7 @@ public class MostVisitedPage extends HomeFragment { @Override public void onLoadFinished(Loader loader, Cursor c) { - if (loader.getId() == LOADER_ID_FRECENCY) { + if (loader.getId() == LOADER_ID_TOP_SITES) { mAdapter.swapCursor(c); updateUiFromCursor(c); loadFavicons(c); @@ -233,7 +306,7 @@ public class MostVisitedPage extends HomeFragment { @Override public void onLoaderReset(Loader loader) { - if (loader.getId() == LOADER_ID_FRECENCY) { + if (loader.getId() == LOADER_ID_TOP_SITES) { mAdapter.swapCursor(null); } else { super.onLoaderReset(loader); diff --git a/mobile/android/base/locales/en-US/android_strings.dtd b/mobile/android/base/locales/en-US/android_strings.dtd index f2497fefc02f..371780c89cec 100644 --- a/mobile/android/base/locales/en-US/android_strings.dtd +++ b/mobile/android/base/locales/en-US/android_strings.dtd @@ -270,6 +270,7 @@ size. --> + diff --git a/mobile/android/base/resources/layout-large-land-v11/home_history_page.xml b/mobile/android/base/resources/layout-large-land-v11/home_history_page.xml index b21e2cf87aee..03e18fe73207 100644 --- a/mobile/android/base/resources/layout-large-land-v11/home_history_page.xml +++ b/mobile/android/base/resources/layout-large-land-v11/home_history_page.xml @@ -16,7 +16,7 @@ android:layout="@layout/home_history_tabs_indicator" gecko:display="text"/> - diff --git a/mobile/android/base/resources/layout-xlarge-v11/home_history_page.xml b/mobile/android/base/resources/layout-xlarge-v11/home_history_page.xml index b21e2cf87aee..03e18fe73207 100644 --- a/mobile/android/base/resources/layout-xlarge-v11/home_history_page.xml +++ b/mobile/android/base/resources/layout-xlarge-v11/home_history_page.xml @@ -16,7 +16,7 @@ android:layout="@layout/home_history_tabs_indicator" gecko:display="text"/> - diff --git a/mobile/android/base/resources/layout/home_bookmarks_page.xml b/mobile/android/base/resources/layout/home_bookmarks_page.xml index d55b25da0351..740fc62e3733 100644 --- a/mobile/android/base/resources/layout/home_bookmarks_page.xml +++ b/mobile/android/base/resources/layout/home_bookmarks_page.xml @@ -12,15 +12,4 @@ android:layout_width="fill_parent" android:layout_height="fill_parent"/> - - diff --git a/mobile/android/base/resources/layout/home_history_page.xml b/mobile/android/base/resources/layout/home_history_page.xml index 70e0589ad9f6..74bf48074a9a 100644 --- a/mobile/android/base/resources/layout/home_history_page.xml +++ b/mobile/android/base/resources/layout/home_history_page.xml @@ -8,7 +8,7 @@ android:layout_height="fill_parent" android:orientation="vertical"> - diff --git a/mobile/android/base/resources/layout/home_most_visited_page.xml b/mobile/android/base/resources/layout/home_most_visited_page.xml deleted file mode 100644 index 8122a0fb9d1a..000000000000 --- a/mobile/android/base/resources/layout/home_most_visited_page.xml +++ /dev/null @@ -1,13 +0,0 @@ - - - - - - - - diff --git a/mobile/android/base/resources/layout/home_top_sites_page.xml b/mobile/android/base/resources/layout/home_top_sites_page.xml new file mode 100644 index 000000000000..5a1d8bbbb5af --- /dev/null +++ b/mobile/android/base/resources/layout/home_top_sites_page.xml @@ -0,0 +1,34 @@ + + + + + + + + + + + + diff --git a/mobile/android/base/resources/values-large-v11/styles.xml b/mobile/android/base/resources/values-large-v11/styles.xml index 0daabb4c0c4c..936716bd10a2 100644 --- a/mobile/android/base/resources/values-large-v11/styles.xml +++ b/mobile/android/base/resources/values-large-v11/styles.xml @@ -62,9 +62,11 @@ - + +