mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-10-31 14:15:30 +00:00
Bug 862796: Add a bookmarks panel to about:home. [r=bnicholson]
This commit is contained in:
parent
b1a357077c
commit
aacf1125e2
@ -5,6 +5,8 @@
|
||||
package org.mozilla.gecko;
|
||||
|
||||
import org.mozilla.gecko.gfx.LayerView;
|
||||
import org.mozilla.gecko.home.BookmarkFolderView;
|
||||
import org.mozilla.gecko.home.TwoLinePageRow;
|
||||
import org.mozilla.gecko.menu.MenuItemDefault;
|
||||
import org.mozilla.gecko.widget.AboutHomeView;
|
||||
import org.mozilla.gecko.widget.AddonsSection;
|
||||
@ -87,6 +89,8 @@ public final class GeckoViewsFactory implements LayoutInflater.Factory {
|
||||
mFactoryMap.put("TextSwitcher", GeckoTextSwitcher.class.getConstructor(arg1Class, arg2Class));
|
||||
mFactoryMap.put("TextView", GeckoTextView.class.getConstructor(arg1Class, arg2Class));
|
||||
mFactoryMap.put("FaviconView", FaviconView.class.getConstructor(arg1Class, arg2Class));
|
||||
mFactoryMap.put("home.BookmarkFolderView", BookmarkFolderView.class.getConstructor(arg1Class, arg2Class));
|
||||
mFactoryMap.put("home.TwoLinePageRow", TwoLinePageRow.class.getConstructor(arg1Class, arg2Class));
|
||||
} catch (NoSuchMethodException nsme) {
|
||||
Log.e(LOGTAG, "Unable to initialize views factory", nsme);
|
||||
}
|
||||
|
@ -214,8 +214,11 @@ FENNEC_JAVA_FILES = \
|
||||
gfx/TouchEventHandler.java \
|
||||
gfx/ViewTransform.java \
|
||||
gfx/VirtualLayer.java \
|
||||
home/BookmarksPage.java \
|
||||
home/BookmarkFolderView.java \
|
||||
home/HomePager.java \
|
||||
home/HomePagerTabStrip.java \
|
||||
home/TwoLinePageRow.java \
|
||||
menu/GeckoMenu.java \
|
||||
menu/GeckoMenuInflater.java \
|
||||
menu/GeckoMenuItem.java \
|
||||
@ -430,6 +433,8 @@ RES_LAYOUT = \
|
||||
res/layout/awesomebar_tab_indicator.xml \
|
||||
res/layout/awesomebar_tabs.xml \
|
||||
res/layout/bookmark_edit.xml \
|
||||
res/layout/bookmark_folder_row.xml \
|
||||
res/layout/bookmark_item_row.xml \
|
||||
res/layout/browser_toolbar.xml \
|
||||
res/layout/datetime_picker.xml \
|
||||
res/layout/doorhangerpopup.xml \
|
||||
@ -460,6 +465,7 @@ RES_LAYOUT = \
|
||||
res/layout/tabs_item_cell.xml \
|
||||
res/layout/tabs_item_row.xml \
|
||||
res/layout/text_selection_handles.xml \
|
||||
res/layout/two_line_page_row.xml \
|
||||
res/layout/list_item_header.xml \
|
||||
res/layout/select_dialog_list.xml \
|
||||
res/layout/select_dialog_multichoice.xml \
|
||||
@ -544,6 +550,10 @@ RES_VALUES_V14 = \
|
||||
res/values-v14/styles.xml \
|
||||
$(NULL)
|
||||
|
||||
RES_VALUES_V16 = \
|
||||
res/values-v16/styles.xml \
|
||||
$(NULL)
|
||||
|
||||
RES_XML = \
|
||||
res/xml/preferences_datareporting.xml \
|
||||
$(SYNC_RES_XML) \
|
||||
@ -591,6 +601,8 @@ RES_DRAWABLE_MDPI = \
|
||||
res/drawable-mdpi/awesomebar_tab_right.9.png \
|
||||
res/drawable-mdpi/awesomebar_sep_left.9.png \
|
||||
res/drawable-mdpi/awesomebar_sep_right.9.png \
|
||||
res/drawable-mdpi/bookmark_folder_closed.png \
|
||||
res/drawable-mdpi/bookmark_folder_opened.png \
|
||||
res/drawable-mdpi/desktop_notification.png \
|
||||
res/drawable-mdpi/ic_addons_empty.png \
|
||||
res/drawable-mdpi/ic_menu_addons_filler.png \
|
||||
@ -705,6 +717,8 @@ RES_DRAWABLE_HDPI = \
|
||||
res/drawable-hdpi/awesomebar_tab_right.9.png \
|
||||
res/drawable-hdpi/awesomebar_sep_left.9.png \
|
||||
res/drawable-hdpi/awesomebar_sep_right.9.png \
|
||||
res/drawable-hdpi/bookmark_folder_closed.png \
|
||||
res/drawable-hdpi/bookmark_folder_opened.png \
|
||||
res/drawable-hdpi/ic_addons_empty.png \
|
||||
res/drawable-hdpi/ic_menu_addons_filler.png \
|
||||
res/drawable-hdpi/ic_menu_bookmark_add.png \
|
||||
@ -799,6 +813,8 @@ RES_DRAWABLE_XHDPI = \
|
||||
res/drawable-xhdpi/awesomebar_tab_right.9.png \
|
||||
res/drawable-xhdpi/awesomebar_sep_left.9.png \
|
||||
res/drawable-xhdpi/awesomebar_sep_right.9.png \
|
||||
res/drawable-xhdpi/bookmark_folder_closed.png \
|
||||
res/drawable-xhdpi/bookmark_folder_opened.png \
|
||||
res/drawable-xhdpi/ic_addons_empty.png \
|
||||
res/drawable-xhdpi/ic_menu_addons_filler.png \
|
||||
res/drawable-xhdpi/ic_menu_bookmark_add.png \
|
||||
@ -1040,6 +1056,7 @@ MOZ_ANDROID_DRAWABLES += \
|
||||
mobile/android/base/resources/drawable/awesomebar_tab_indicator.xml \
|
||||
mobile/android/base/resources/drawable/awesomebar_tab_selected.xml \
|
||||
mobile/android/base/resources/drawable/awesomebar_tab_unselected.xml \
|
||||
mobile/android/base/resources/drawable/bookmark_folder.xml \
|
||||
mobile/android/base/resources/drawable/favicon_bg.xml \
|
||||
mobile/android/base/resources/drawable/handle_end_level.xml \
|
||||
mobile/android/base/resources/drawable/handle_start_level.xml \
|
||||
@ -1065,7 +1082,7 @@ MOZ_ANDROID_DRAWABLES += \
|
||||
|
||||
MOZ_BRANDING_DRAWABLE_MDPI = $(shell if test -e $(topsrcdir)/$(MOZ_BRANDING_DIRECTORY)/android-resources.mn; then cat $(topsrcdir)/$(MOZ_BRANDING_DIRECTORY)/android-resources.mn | tr '\n' ' '; fi)
|
||||
|
||||
RESOURCES=$(RES_LAYOUT) $(RES_LAYOUT_LARGE_LAND_V11) $(RES_LAYOUT_LARGE_V11) $(RES_LAYOUT_XLARGE_V11) $(RES_LAYOUT_XLARGE_LAND_V11) $(RES_VALUES) $(RES_VALUES_LAND) $(RES_VALUES_V11) $(RES_VALUES_LARGE_V11) $(RES_VALUES_LARGE_LAND_V11) $(RES_VALUES_XLARGE_V11) $(RES_VALUES_LAND_V14) $(RES_VALUES_V14) $(RES_XML) $(RES_XML_V11) $(RES_ANIM) $(RES_DRAWABLE_MDPI) $(RES_DRAWABLE_LDPI) $(RES_DRAWABLE_HDPI) $(RES_DRAWABLE_XHDPI) $(RES_DRAWABLE_MDPI_V11) $(RES_DRAWABLE_HDPI_V11) $(RES_DRAWABLE_XHDPI_V11) $(RES_DRAWABLE_LARGE_MDPI_V11) $(RES_DRAWABLE_LARGE_HDPI_V11) $(RES_DRAWABLE_LARGE_XHDPI_V11) $(RES_DRAWABLE_XLARGE_MDPI_V11) $(RES_DRAWABLE_XLARGE_HDPI_V11) $(RES_DRAWABLE_XLARGE_XHDPI_V11) $(RES_COLOR) $(RES_MENU)
|
||||
RESOURCES=$(RES_LAYOUT) $(RES_LAYOUT_LARGE_LAND_V11) $(RES_LAYOUT_LARGE_V11) $(RES_LAYOUT_XLARGE_V11) $(RES_LAYOUT_XLARGE_LAND_V11) $(RES_VALUES) $(RES_VALUES_LAND) $(RES_VALUES_V11) $(RES_VALUES_LARGE_V11) $(RES_VALUES_LARGE_LAND_V11) $(RES_VALUES_XLARGE_V11) $(RES_VALUES_LAND_V14) $(RES_VALUES_V14) $(RES_VALUES_V16) $(RES_XML) $(RES_XML_V11) $(RES_ANIM) $(RES_DRAWABLE_MDPI) $(RES_DRAWABLE_LDPI) $(RES_DRAWABLE_HDPI) $(RES_DRAWABLE_XHDPI) $(RES_DRAWABLE_MDPI_V11) $(RES_DRAWABLE_HDPI_V11) $(RES_DRAWABLE_XHDPI_V11) $(RES_DRAWABLE_LARGE_MDPI_V11) $(RES_DRAWABLE_LARGE_HDPI_V11) $(RES_DRAWABLE_LARGE_XHDPI_V11) $(RES_DRAWABLE_XLARGE_MDPI_V11) $(RES_DRAWABLE_XLARGE_HDPI_V11) $(RES_DRAWABLE_XLARGE_XHDPI_V11) $(RES_COLOR) $(RES_MENU)
|
||||
|
||||
RES_DIRS= \
|
||||
res/layout \
|
||||
@ -1077,7 +1094,8 @@ RES_DIRS= \
|
||||
res/values-v11 \
|
||||
res/values-large-v11 \
|
||||
res/values-xlarge-v11 \
|
||||
res/values-land-v14 \
|
||||
res/values-v14 \
|
||||
res/values-v16 \
|
||||
res/xml \
|
||||
res/xml-v11 \
|
||||
res/anim \
|
||||
|
55
mobile/android/base/home/BookmarkFolderView.java
Normal file
55
mobile/android/base/home/BookmarkFolderView.java
Normal file
@ -0,0 +1,55 @@
|
||||
/* -*- 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 android.content.Context;
|
||||
import android.util.AttributeSet;
|
||||
import android.widget.TextView;
|
||||
|
||||
public class BookmarkFolderView extends TextView {
|
||||
private static final int[] STATE_OPEN = { R.attr.state_open };
|
||||
|
||||
private boolean mIsOpen = false;
|
||||
|
||||
public BookmarkFolderView(Context context) {
|
||||
super(context);
|
||||
}
|
||||
|
||||
public BookmarkFolderView(Context context, AttributeSet attrs) {
|
||||
super(context, attrs);
|
||||
}
|
||||
|
||||
public BookmarkFolderView(Context context, AttributeSet attrs, int defStyle) {
|
||||
super(context, attrs, defStyle);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int[] onCreateDrawableState(int extraSpace) {
|
||||
final int[] drawableState = super.onCreateDrawableState(extraSpace + 1);
|
||||
|
||||
if (mIsOpen) {
|
||||
mergeDrawableStates(drawableState, STATE_OPEN);
|
||||
}
|
||||
|
||||
return drawableState;
|
||||
}
|
||||
|
||||
public void open() {
|
||||
if (!mIsOpen) {
|
||||
mIsOpen = true;
|
||||
refreshDrawableState();
|
||||
}
|
||||
}
|
||||
|
||||
public void close() {
|
||||
if (mIsOpen) {
|
||||
mIsOpen = false;
|
||||
refreshDrawableState();
|
||||
}
|
||||
}
|
||||
}
|
443
mobile/android/base/home/BookmarksPage.java
Normal file
443
mobile/android/base/home/BookmarksPage.java
Normal file
@ -0,0 +1,443 @@
|
||||
/* -*- 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.Tab;
|
||||
import org.mozilla.gecko.Tabs;
|
||||
import org.mozilla.gecko.db.BrowserContract.Bookmarks;
|
||||
import org.mozilla.gecko.db.BrowserContract.Combined;
|
||||
import org.mozilla.gecko.db.BrowserDB;
|
||||
import org.mozilla.gecko.db.BrowserDB.URLColumns;
|
||||
import org.mozilla.gecko.util.GamepadUtils;
|
||||
import org.mozilla.gecko.util.ThreadUtils;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.content.Context;
|
||||
import android.content.res.Configuration;
|
||||
import android.database.Cursor;
|
||||
import android.graphics.Bitmap;
|
||||
import android.os.AsyncTask;
|
||||
import android.os.Bundle;
|
||||
import android.support.v4.app.Fragment;
|
||||
import android.util.Log;
|
||||
import android.util.Pair;
|
||||
import android.view.ContextMenu;
|
||||
import android.view.ContextMenu.ContextMenuInfo;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.MenuInflater;
|
||||
import android.view.MotionEvent;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.AdapterView;
|
||||
import android.widget.ListView;
|
||||
import android.widget.SimpleCursorAdapter;
|
||||
import android.widget.TextView;
|
||||
|
||||
import java.util.LinkedList;
|
||||
|
||||
/**
|
||||
* A page in about:home that displays a ListView of bookmarks.
|
||||
*/
|
||||
public class BookmarksPage extends Fragment {
|
||||
public static final String LOGTAG = "GeckoBookmarksPage";
|
||||
|
||||
private int mFolderId = Bookmarks.FIXED_ROOT_ID;
|
||||
private String mFolderTitle = "";
|
||||
|
||||
private BookmarksListAdapter mCursorAdapter = null;
|
||||
private BookmarksQueryTask mQueryTask = null;
|
||||
|
||||
// The view shown by the fragment.
|
||||
private ListView mList;
|
||||
|
||||
// Folder title for the currently shown list of bookmarks.
|
||||
private BookmarkFolderView mFolderView;
|
||||
|
||||
@Override
|
||||
public void onAttach(Activity activity) {
|
||||
super.onAttach(activity);
|
||||
|
||||
// Intialize the adapter.
|
||||
mCursorAdapter = new BookmarksListAdapter(getActivity());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDetach() {
|
||||
super.onDetach();
|
||||
|
||||
// Can't use getters for adapter. It will create one if null.
|
||||
if (mCursorAdapter != null) {
|
||||
final Cursor cursor = mCursorAdapter.getCursor();
|
||||
mCursorAdapter = null;
|
||||
|
||||
// Gingerbread locks the DB when closing a cursor, so do it in the background.
|
||||
ThreadUtils.postToBackgroundThread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
if (cursor != null && !cursor.isClosed())
|
||||
cursor.close();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
|
||||
super.onCreateView(inflater, container, savedInstanceState);
|
||||
|
||||
// All list views are styled to look the same with a global activity theme.
|
||||
// If the style of the list changes, inflate it from an XML.
|
||||
mList = new ListView(container.getContext());
|
||||
return mList;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onViewCreated(View view, Bundle savedInstanceState) {
|
||||
super.onViewCreated(view, savedInstanceState);
|
||||
|
||||
// Folder title view, is always in open state.
|
||||
mFolderView = (BookmarkFolderView) LayoutInflater.from(getActivity()).inflate(R.layout.bookmark_folder_row, null);
|
||||
mFolderView.open();
|
||||
|
||||
// We need to add the header before we set the adapter, hence make it null
|
||||
refreshListWithCursor(null);
|
||||
|
||||
EventHandlers eventHandlers = new EventHandlers();
|
||||
mList.setOnTouchListener(eventHandlers);
|
||||
mList.setOnItemClickListener(eventHandlers);
|
||||
mList.setOnCreateContextMenuListener(eventHandlers);
|
||||
mList.setOnKeyListener(GamepadUtils.getListItemClickDispatcher());
|
||||
|
||||
mQueryTask = new BookmarksQueryTask();
|
||||
mQueryTask.execute();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDestroyView() {
|
||||
mList = null;
|
||||
mFolderView = null;
|
||||
super.onDestroyView();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onConfigurationChanged(Configuration newConfig) {
|
||||
super.onConfigurationChanged(newConfig);
|
||||
|
||||
// Reattach the fragment, forcing a reinflation of its view.
|
||||
// We use commitAllowingStateLoss() instead of commit() here to avoid
|
||||
// an IllegalStateException. If the phone is rotated while Fennec
|
||||
// is in the background, onConfigurationChanged() is fired.
|
||||
// onConfigurationChanged() is called before onResume(), so
|
||||
// using commit() would throw an IllegalStateException since it can't
|
||||
// be used between the Activity's onSaveInstanceState() and
|
||||
// onResume().
|
||||
if (isVisible()) {
|
||||
getFragmentManager().beginTransaction()
|
||||
.detach(this)
|
||||
.attach(this)
|
||||
.commitAllowingStateLoss();
|
||||
}
|
||||
}
|
||||
|
||||
private void refreshListWithCursor(Cursor cursor) {
|
||||
// We need to add the header before we set the adapter, hence making it null.
|
||||
mList.setAdapter(null);
|
||||
|
||||
// Add a header view based on the root folder.
|
||||
if (mFolderId == Bookmarks.FIXED_ROOT_ID) {
|
||||
if (mList.getHeaderViewsCount() == 1) {
|
||||
mList.removeHeaderView(mFolderView);
|
||||
}
|
||||
} else {
|
||||
if (mList.getHeaderViewsCount() == 0) {
|
||||
mList.addHeaderView(mFolderView, null, true);
|
||||
}
|
||||
|
||||
mFolderView.setText(mFolderTitle);
|
||||
}
|
||||
|
||||
// This will update the cursorAdapter to use the new one if it already exists.
|
||||
mCursorAdapter.changeCursor(cursor);
|
||||
mList.setAdapter(mCursorAdapter);
|
||||
|
||||
// Reset the task.
|
||||
mQueryTask = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Internal class to handle different event listeners on the ListView.
|
||||
*/
|
||||
private class EventHandlers implements AdapterView.OnItemClickListener,
|
||||
View.OnCreateContextMenuListener,
|
||||
View.OnTouchListener {
|
||||
@Override
|
||||
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
|
||||
final ListView list = (ListView) parent;
|
||||
final int headerCount = list.getHeaderViewsCount();
|
||||
|
||||
// If we tap on the header view, move back to parent folder.
|
||||
if (headerCount == 1 && position == 0) {
|
||||
mCursorAdapter.moveToParentFolder();
|
||||
return;
|
||||
}
|
||||
|
||||
Cursor cursor = mCursorAdapter.getCursor();
|
||||
if (cursor == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
// The header view takes up a spot in the list
|
||||
if (headerCount == 1) {
|
||||
position--;
|
||||
}
|
||||
|
||||
cursor.moveToPosition(position);
|
||||
|
||||
int type = cursor.getInt(cursor.getColumnIndexOrThrow(Bookmarks.TYPE));
|
||||
if (type == Bookmarks.TYPE_FOLDER) {
|
||||
// If we're clicking on a folder, update adapter to move to that folder
|
||||
int folderId = cursor.getInt(cursor.getColumnIndexOrThrow(Bookmarks._ID));
|
||||
String folderTitle = mCursorAdapter.getFolderTitle(position);
|
||||
mCursorAdapter.moveToChildFolder(folderId, folderTitle);
|
||||
} else {
|
||||
// Otherwise, just open the URL
|
||||
String url = cursor.getString(cursor.getColumnIndexOrThrow(URLColumns.URL));
|
||||
Tabs.getInstance().loadUrl(url);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onTouch(View view, MotionEvent event) {
|
||||
if (event.getActionMasked() == MotionEvent.ACTION_DOWN) {
|
||||
// take focus away from awesome bar to hide the keyboard
|
||||
view.requestFocus();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCreateContextMenu(ContextMenu menu, View view, ContextMenuInfo menuInfo) {
|
||||
ListView list = (ListView) view;
|
||||
AdapterView.AdapterContextMenuInfo info = (AdapterView.AdapterContextMenuInfo) menuInfo;
|
||||
Object selectedItem = list.getItemAtPosition(info.position);
|
||||
Cursor cursor = (Cursor) selectedItem;
|
||||
|
||||
// Don't show the context menu for folders
|
||||
if (cursor.getInt(cursor.getColumnIndexOrThrow(Bookmarks.TYPE)) == Bookmarks.TYPE_FOLDER) {
|
||||
return;
|
||||
}
|
||||
|
||||
String keyword = null;
|
||||
int keywordCol = cursor.getColumnIndex(URLColumns.KEYWORD);
|
||||
if (keywordCol != -1) {
|
||||
keyword = cursor.getString(keywordCol);
|
||||
}
|
||||
|
||||
int id = cursor.getInt(cursor.getColumnIndexOrThrow(Bookmarks._ID));
|
||||
String url = cursor.getString(cursor.getColumnIndexOrThrow(URLColumns.URL));
|
||||
String title = cursor.getString(cursor.getColumnIndexOrThrow(URLColumns.TITLE));
|
||||
byte[] favicon = cursor.getBlob(cursor.getColumnIndexOrThrow(URLColumns.FAVICON));
|
||||
int display = Combined.DISPLAY_NORMAL;
|
||||
|
||||
MenuInflater inflater = new MenuInflater(list.getContext());
|
||||
inflater.inflate(R.menu.awesomebar_contextmenu, menu);
|
||||
|
||||
// Show Open Private Tab if we're in private mode, Open New Tab otherwise
|
||||
boolean isPrivate = false;
|
||||
Tab tab = Tabs.getInstance().getSelectedTab();
|
||||
if (tab != null) {
|
||||
isPrivate = tab.isPrivate();
|
||||
}
|
||||
menu.findItem(R.id.open_new_tab).setVisible(!isPrivate);
|
||||
menu.findItem(R.id.open_private_tab).setVisible(isPrivate);
|
||||
|
||||
// Hide "Remove" item if there isn't a valid history ID
|
||||
if (id < 0) {
|
||||
menu.findItem(R.id.remove_history).setVisible(false);
|
||||
}
|
||||
menu.setHeaderTitle(title);
|
||||
|
||||
menu.findItem(R.id.remove_history).setVisible(false);
|
||||
menu.findItem(R.id.open_in_reader).setVisible(false);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Adapter to back the ListView with a list of bookmarks.
|
||||
*/
|
||||
private class BookmarksListAdapter extends SimpleCursorAdapter {
|
||||
private static final int VIEW_TYPE_ITEM = 0;
|
||||
private static final int VIEW_TYPE_FOLDER = 1;
|
||||
|
||||
private static final int VIEW_TYPE_COUNT = 2;
|
||||
|
||||
// mParentStack holds folder id/title pairs that allow us to navigate
|
||||
// back up the folder heirarchy.
|
||||
private LinkedList<Pair<Integer, String>> mParentStack;
|
||||
|
||||
public BookmarksListAdapter(Context context) {
|
||||
// Initializing with a null cursor.
|
||||
super(context, -1, null, new String[] {}, new int[] {});
|
||||
|
||||
mParentStack = new LinkedList<Pair<Integer, String>>();
|
||||
|
||||
// Add the root folder to the stack
|
||||
Pair<Integer, String> rootFolder = new Pair<Integer, String>(mFolderId, mFolderTitle);
|
||||
mParentStack.addFirst(rootFolder);
|
||||
}
|
||||
|
||||
// Refresh the current folder by executing a new task.
|
||||
private void refreshCurrentFolder() {
|
||||
// Cancel any pre-existing async refresh tasks
|
||||
if (mQueryTask != null) {
|
||||
mQueryTask.cancel(false);
|
||||
}
|
||||
|
||||
Pair<Integer, String> folderPair = mParentStack.getFirst();
|
||||
mFolderId = folderPair.first;
|
||||
mFolderTitle = folderPair.second;
|
||||
|
||||
mQueryTask = new BookmarksQueryTask();
|
||||
mQueryTask.execute(new Integer(mFolderId));
|
||||
}
|
||||
|
||||
/**
|
||||
* Moves to parent folder, if one exists.
|
||||
*/
|
||||
public void moveToParentFolder() {
|
||||
// If we're already at the root, we can't move to a parent folder
|
||||
if (mParentStack.size() != 1) {
|
||||
mParentStack.removeFirst();
|
||||
refreshCurrentFolder();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Moves to child folder, given a folderId.
|
||||
*
|
||||
* @param folderId The id of the folder to show.
|
||||
* @param folderTitle The title of the folder to show.
|
||||
*/
|
||||
public void moveToChildFolder(int folderId, String folderTitle) {
|
||||
Pair<Integer, String> folderPair = new Pair<Integer, String>(folderId, folderTitle);
|
||||
mParentStack.addFirst(folderPair);
|
||||
refreshCurrentFolder();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public int getItemViewType(int position) {
|
||||
Cursor c = getCursor();
|
||||
|
||||
if (c.moveToPosition(position) &&
|
||||
c.getInt(c.getColumnIndexOrThrow(Bookmarks.TYPE)) == Bookmarks.TYPE_FOLDER) {
|
||||
return VIEW_TYPE_FOLDER;
|
||||
}
|
||||
|
||||
// Default to retuning normal item type
|
||||
return VIEW_TYPE_ITEM;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public int getViewTypeCount() {
|
||||
return VIEW_TYPE_COUNT;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the title of the folder for a given position.
|
||||
*
|
||||
* @param position The position of the view.
|
||||
* @return The title of the folder at the position.
|
||||
*/
|
||||
public String getFolderTitle(int position) {
|
||||
Cursor c = getCursor();
|
||||
if (!c.moveToPosition(position)) {
|
||||
return "";
|
||||
}
|
||||
|
||||
String guid = c.getString(c.getColumnIndexOrThrow(Bookmarks.GUID));
|
||||
|
||||
// If we don't have a special GUID, just return the folder title from the DB.
|
||||
if (guid == null || guid.length() == 12) {
|
||||
return c.getString(c.getColumnIndexOrThrow(Bookmarks.TITLE));
|
||||
}
|
||||
|
||||
// Use localized strings for special folder names.
|
||||
if (guid.equals(Bookmarks.FAKE_DESKTOP_FOLDER_GUID)) {
|
||||
return getResources().getString(R.string.bookmarks_folder_desktop);
|
||||
} else if (guid.equals(Bookmarks.MENU_FOLDER_GUID)) {
|
||||
return getResources().getString(R.string.bookmarks_folder_menu);
|
||||
} else if (guid.equals(Bookmarks.TOOLBAR_FOLDER_GUID)) {
|
||||
return getResources().getString(R.string.bookmarks_folder_toolbar);
|
||||
} else if (guid.equals(Bookmarks.UNFILED_FOLDER_GUID)) {
|
||||
return getResources().getString(R.string.bookmarks_folder_unfiled);
|
||||
}
|
||||
|
||||
// If for some reason we have a folder with a special GUID, but it's not one of
|
||||
// the special folders we expect in the UI, just return the title from the DB.
|
||||
return c.getString(c.getColumnIndexOrThrow(Bookmarks.TITLE));
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public View getView(int position, View convertView, ViewGroup parent) {
|
||||
final int viewType = getItemViewType(position);
|
||||
|
||||
if (convertView == null) {
|
||||
if (viewType == VIEW_TYPE_ITEM) {
|
||||
convertView = LayoutInflater.from(parent.getContext()).inflate(R.layout.bookmark_item_row, null);
|
||||
} else {
|
||||
convertView = LayoutInflater.from(parent.getContext()).inflate(R.layout.bookmark_folder_row, null);
|
||||
}
|
||||
}
|
||||
|
||||
Cursor cursor = getCursor();
|
||||
if (!cursor.moveToPosition(position)) {
|
||||
throw new IllegalStateException("Couldn't move cursor to position " + position);
|
||||
}
|
||||
|
||||
if (viewType == VIEW_TYPE_ITEM) {
|
||||
TwoLinePageRow row = (TwoLinePageRow) convertView;
|
||||
row.updateFromCursor(cursor);
|
||||
} else {
|
||||
BookmarkFolderView row = (BookmarkFolderView) convertView;
|
||||
row.setText(getFolderTitle(position));
|
||||
}
|
||||
|
||||
return convertView;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* AsyncTask to query the DB for bookmarks.
|
||||
*/
|
||||
private class BookmarksQueryTask extends AsyncTask<Integer, Void, Cursor> {
|
||||
@Override
|
||||
protected Cursor doInBackground(Integer... folderIds) {
|
||||
int folderId = Bookmarks.FIXED_ROOT_ID;
|
||||
|
||||
if (folderIds.length != 0) {
|
||||
folderId = folderIds[0].intValue();
|
||||
}
|
||||
|
||||
return BrowserDB.getBookmarksInFolder(getActivity().getContentResolver(), folderId);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onPostExecute(final Cursor cursor) {
|
||||
refreshListWithCursor(cursor);
|
||||
}
|
||||
}
|
||||
}
|
@ -5,6 +5,7 @@
|
||||
|
||||
package org.mozilla.gecko.home;
|
||||
|
||||
import org.mozilla.gecko.R;
|
||||
import org.mozilla.gecko.widget.AboutHome;
|
||||
|
||||
import android.content.Context;
|
||||
@ -26,6 +27,7 @@ public class HomePager extends ViewPager {
|
||||
|
||||
// List of pages in order.
|
||||
private enum Page {
|
||||
BOOKMARKS
|
||||
}
|
||||
|
||||
private EnumMap<Page, Fragment> mPages = new EnumMap<Page, Fragment>(Page.class);
|
||||
@ -50,6 +52,7 @@ public class HomePager extends ViewPager {
|
||||
TabsAdapter adapter = new TabsAdapter(fm);
|
||||
|
||||
// Add the pages to the adapter in order.
|
||||
adapter.addTab(Page.BOOKMARKS, BookmarksPage.class, null, getContext().getString(R.string.bookmarks_title));
|
||||
|
||||
setAdapter(adapter);
|
||||
setVisibility(VISIBLE);
|
||||
|
92
mobile/android/base/home/TwoLinePageRow.java
Normal file
92
mobile/android/base/home/TwoLinePageRow.java
Normal file
@ -0,0 +1,92 @@
|
||||
/* -*- 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.Favicons;
|
||||
import org.mozilla.gecko.R;
|
||||
import org.mozilla.gecko.db.BrowserDB.URLColumns;
|
||||
import org.mozilla.gecko.gfx.BitmapUtils;
|
||||
import org.mozilla.gecko.widget.FaviconView;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.res.TypedArray;
|
||||
import android.database.Cursor;
|
||||
import android.graphics.Bitmap;
|
||||
import android.text.TextUtils;
|
||||
import android.util.AttributeSet;
|
||||
import android.view.Gravity;
|
||||
import android.view.LayoutInflater;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.LinearLayout;
|
||||
import android.widget.TextView;
|
||||
|
||||
public class TwoLinePageRow extends LinearLayout {
|
||||
|
||||
private final TextView mTitle;
|
||||
private final TextView mUrl;
|
||||
private final FaviconView mFavicon;
|
||||
|
||||
public TwoLinePageRow(Context context) {
|
||||
this(context, null);
|
||||
}
|
||||
|
||||
public TwoLinePageRow(Context context, AttributeSet attrs) {
|
||||
this(context, attrs, 0);
|
||||
}
|
||||
|
||||
public TwoLinePageRow(Context context, AttributeSet attrs, int defStyle) {
|
||||
super(context, attrs, defStyle);
|
||||
|
||||
setGravity(Gravity.CENTER_VERTICAL);
|
||||
|
||||
LayoutInflater.from(context).inflate(R.layout.two_line_page_row, this);
|
||||
mTitle = (TextView) findViewById(R.id.title);
|
||||
mUrl = (TextView) findViewById(R.id.url);
|
||||
mFavicon = (FaviconView) findViewById(R.id.favicon);
|
||||
}
|
||||
|
||||
public void updateFromCursor(Cursor cursor) {
|
||||
if (cursor == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
int titleIndex = cursor.getColumnIndexOrThrow(URLColumns.TITLE);
|
||||
final String title = cursor.getString(titleIndex);
|
||||
|
||||
int urlIndex = cursor.getColumnIndexOrThrow(URLColumns.URL);
|
||||
final String url = cursor.getString(urlIndex);
|
||||
|
||||
// Use the URL instead of an empty title for consistency with the normal URL
|
||||
// bar view - this is the equivalent of getDisplayTitle() in Tab.java
|
||||
if (TextUtils.isEmpty(title)) {
|
||||
mTitle.setText(url);
|
||||
} else {
|
||||
mTitle.setText(title);
|
||||
}
|
||||
|
||||
// Update the url with "Switch to tab" if needed.
|
||||
// FIXME: Bug 877469: Add back switch to tab functionality.
|
||||
Integer tabId = null;
|
||||
if (tabId != null) {
|
||||
mUrl.setText(R.string.switch_to_tab);
|
||||
mUrl.setCompoundDrawablesWithIntrinsicBounds(R.drawable.ic_url_bar_tab, 0, 0, 0);
|
||||
} else {
|
||||
mUrl.setText(url);
|
||||
mUrl.setCompoundDrawablesWithIntrinsicBounds(0, 0, 0, 0);
|
||||
}
|
||||
|
||||
byte[] b = cursor.getBlob(cursor.getColumnIndexOrThrow(URLColumns.FAVICON));
|
||||
Bitmap favicon = null;
|
||||
if (b != null) {
|
||||
Bitmap bitmap = BitmapUtils.decodeByteArray(b);
|
||||
if (bitmap != null) {
|
||||
favicon = Favicons.getInstance().scaleImage(bitmap);
|
||||
}
|
||||
}
|
||||
|
||||
mFavicon.updateImage(favicon, url);
|
||||
}
|
||||
}
|
Binary file not shown.
After Width: | Height: | Size: 264 B |
Binary file not shown.
After Width: | Height: | Size: 506 B |
Binary file not shown.
After Width: | Height: | Size: 205 B |
Binary file not shown.
After Width: | Height: | Size: 375 B |
Binary file not shown.
After Width: | Height: | Size: 347 B |
Binary file not shown.
After Width: | Height: | Size: 662 B |
16
mobile/android/base/resources/drawable/bookmark_folder.xml
Normal file
16
mobile/android/base/resources/drawable/bookmark_folder.xml
Normal file
@ -0,0 +1,16 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- 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/. -->
|
||||
|
||||
<selector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:gecko="http://schemas.android.com/apk/res-auto">
|
||||
|
||||
<!-- state open -->
|
||||
<item gecko:state_open="true"
|
||||
android:drawable="@drawable/bookmark_folder_opened"/>
|
||||
|
||||
<!-- state close -->
|
||||
<item android:drawable="@drawable/bookmark_folder_closed"/>
|
||||
|
||||
</selector>
|
11
mobile/android/base/resources/layout/bookmark_folder_row.xml
Normal file
11
mobile/android/base/resources/layout/bookmark_folder_row.xml
Normal file
@ -0,0 +1,11 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- 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/. -->
|
||||
|
||||
<org.mozilla.gecko.home.BookmarkFolderView xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
style="@style/Widget.BookmarkFolderView"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="@dimen/page_row_height"
|
||||
android:minHeight="@dimen/page_row_height"
|
||||
android:gravity="center_vertical"/>
|
@ -0,0 +1,9 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- 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/. -->
|
||||
|
||||
<org.mozilla.gecko.home.TwoLinePageRow xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="@dimen/page_row_height"
|
||||
android:minHeight="@dimen/page_row_height"/>
|
30
mobile/android/base/resources/layout/two_line_page_row.xml
Normal file
30
mobile/android/base/resources/layout/two_line_page_row.xml
Normal file
@ -0,0 +1,30 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- 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/. -->
|
||||
|
||||
<merge xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
|
||||
<org.mozilla.gecko.widget.FaviconView android:id="@+id/favicon"
|
||||
android:layout_width="@dimen/favicon_bg"
|
||||
android:layout_height="@dimen/favicon_bg"
|
||||
android:layout_marginLeft="10dip"
|
||||
android:layout_marginRight="10dip"/>
|
||||
|
||||
<LinearLayout android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical">
|
||||
|
||||
<TextView android:id="@+id/title"
|
||||
style="@style/Widget.TwoLinePageRow.Title"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"/>
|
||||
|
||||
<TextView android:id="@+id/url"
|
||||
style="@style/Widget.TwoLinePageRow.Url"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"/>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</merge>
|
15
mobile/android/base/resources/values-v16/styles.xml
Normal file
15
mobile/android/base/resources/values-v16/styles.xml
Normal file
@ -0,0 +1,15 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- 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/. -->
|
||||
|
||||
<resources xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
|
||||
<style name="Widget.TwoLinePageRow.Title">
|
||||
<item name="android:textAppearance">@style/TextAppearance.Widget.TwoLinePageRow.Title</item>
|
||||
<item name="android:fontFamily">sans-serif-light</item>
|
||||
<item name="android:singleLine">true</item>
|
||||
<item name="android:ellipsize">middle</item>
|
||||
</style>
|
||||
|
||||
</resources>
|
@ -168,5 +168,9 @@
|
||||
<attr name="tabIndicatorColor" format="color"/>
|
||||
</declare-styleable>
|
||||
|
||||
<declare-styleable name="BookmarkFolderView">
|
||||
<attr name="state_open" format="boolean"/>
|
||||
</declare-styleable>
|
||||
|
||||
</resources>
|
||||
|
||||
|
@ -37,7 +37,7 @@
|
||||
-->
|
||||
<!-- Default colors -->
|
||||
<color name="text_color_primary">#222222</color>
|
||||
<color name="text_color_secondary">#666666</color>
|
||||
<color name="text_color_secondary">#777777</color>
|
||||
<color name="text_color_tertiary">#9198A1</color>
|
||||
|
||||
<!-- Default inverse colors -->
|
||||
|
@ -36,6 +36,9 @@
|
||||
<dimen name="favicon_bg">32dp</dimen>
|
||||
<dimen name="favicon_bg_radius">1dp</dimen>
|
||||
|
||||
<!-- Page Row height -->
|
||||
<dimen name="page_row_height">64dp</dimen>
|
||||
|
||||
<!-- Max width of the doorhanger on tablets -->
|
||||
<dimen name="doorhanger_width">400dp</dimen>
|
||||
|
||||
|
@ -69,6 +69,27 @@
|
||||
<item name="android:ellipsize">marquee</item>
|
||||
</style>
|
||||
|
||||
<style name="Widget.TwoLinePageRow" />
|
||||
|
||||
<style name="Widget.TwoLinePageRow.Title">
|
||||
<item name="android:textAppearance">@style/TextAppearance.Widget.TwoLinePageRow.Title</item>
|
||||
<item name="android:singleLine">true</item>
|
||||
<item name="android:ellipsize">middle</item>
|
||||
</style>
|
||||
|
||||
<style name="Widget.TwoLinePageRow.Url">
|
||||
<item name="android:textAppearance">@style/TextAppearance.Widget.TwoLinePageRow.Url</item>
|
||||
<item name="android:includeFontPadding">false</item>
|
||||
<item name="android:singleLine">true</item>
|
||||
<item name="android:ellipsize">middle</item>
|
||||
</style>
|
||||
|
||||
<style name="Widget.BookmarkFolderView" parent="Widget.TwoLinePageRow.Title">
|
||||
<item name="android:paddingLeft">10dip</item>
|
||||
<item name="android:drawablePadding">10dip</item>
|
||||
<item name="android:drawableLeft">@drawable/bookmark_folder</item>
|
||||
</style>
|
||||
|
||||
<!--
|
||||
TextAppearance
|
||||
Note: Gecko uses light theme as default, while Android uses dark.
|
||||
@ -161,6 +182,14 @@
|
||||
<item name="android:textColor">?android:attr/textColorHint</item>
|
||||
</style>
|
||||
|
||||
<style name="TextAppearance.Widget.TwoLinePageRow" />
|
||||
|
||||
<style name="TextAppearance.Widget.TwoLinePageRow.Title" parent="TextAppearance.Medium"/>
|
||||
|
||||
<style name="TextAppearance.Widget.TwoLinePageRow.Url" parent="TextAppearance.Micro">
|
||||
<item name="android:textColor">?android:attr/textColorSecondary</item>
|
||||
</style>
|
||||
|
||||
<!-- BrowserToolbar -->
|
||||
<style name="BrowserToolbar">
|
||||
<item name="android:layout_width">fill_parent</item>
|
||||
|
Loading…
Reference in New Issue
Block a user