mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
471 lines
18 KiB
Java
471 lines
18 KiB
Java
/* -*- 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;
|
|
|
|
import org.mozilla.gecko.AwesomeBar.ContextMenuSubject;
|
|
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.gfx.BitmapUtils;
|
|
import org.mozilla.gecko.util.GamepadUtils;
|
|
import org.mozilla.gecko.util.ThreadUtils;
|
|
import org.mozilla.gecko.widget.FaviconView;
|
|
|
|
import android.app.Activity;
|
|
import android.content.Context;
|
|
import android.database.Cursor;
|
|
import android.graphics.Bitmap;
|
|
import android.os.AsyncTask;
|
|
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.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;
|
|
|
|
public class BookmarksTab extends AwesomeBarTab {
|
|
public static final String LOGTAG = "BOOKMARKS_TAB";
|
|
public static final String TAG = "bookmarks";
|
|
private int mFolderId;
|
|
private String mFolderTitle;
|
|
private BookmarksListAdapter mCursorAdapter = null;
|
|
private BookmarksQueryTask mQueryTask = null;
|
|
private boolean mShowReadingList = false;
|
|
|
|
@Override
|
|
public int getTitleStringId() {
|
|
return R.string.awesomebar_bookmarks_title;
|
|
}
|
|
|
|
@Override
|
|
public String getTag() {
|
|
return TAG;
|
|
}
|
|
|
|
public BookmarksTab(Context context) {
|
|
super(context);
|
|
}
|
|
|
|
@Override
|
|
public View getView() {
|
|
if (mView == null) {
|
|
mView = new ListView(mContext, null);
|
|
((Activity)mContext).registerForContextMenu(mView);
|
|
mView.setTag(TAG);
|
|
mView.setOnTouchListener(mListListener);
|
|
|
|
// We need to add the header before we set the adapter, hence make it null
|
|
ListView list = (ListView)mView;
|
|
list.setAdapter(null);
|
|
list.setAdapter(getCursorAdapter());
|
|
list.setOnItemClickListener(new AdapterView.OnItemClickListener() {
|
|
@Override
|
|
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
|
|
handleItemClick(parent, view, position, id);
|
|
}
|
|
});
|
|
|
|
if (mShowReadingList) {
|
|
String title = getResources().getString(R.string.bookmarks_folder_reading_list);
|
|
getCursorAdapter().moveToChildFolder(Bookmarks.FIXED_READING_LIST_ID, title);
|
|
} else {
|
|
BookmarksQueryTask task = getQueryTask();
|
|
task.execute();
|
|
}
|
|
}
|
|
return (ListView)mView;
|
|
}
|
|
|
|
public void setShowReadingList(boolean showReadingList) {
|
|
mShowReadingList = showReadingList;
|
|
}
|
|
|
|
@Override
|
|
public void destroy() {
|
|
super.destroy();
|
|
// Can't use getters for adapter. It will create one if null.
|
|
if (mCursorAdapter != null && mView != null) {
|
|
ListView list = (ListView)mView;
|
|
list.setAdapter(null);
|
|
final Cursor cursor = mCursorAdapter.getCursor();
|
|
// 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 boolean onBackPressed() {
|
|
// If the soft keyboard is visible in the bookmarks or history tab, the user
|
|
// must have explictly brought it up, so we should try hiding it instead of
|
|
// exiting the activity or going up a bookmarks folder level.
|
|
if (hideSoftInput(getView()))
|
|
return true;
|
|
|
|
return moveToParentFolder();
|
|
}
|
|
|
|
protected BookmarksListAdapter getCursorAdapter() {
|
|
return getCursorAdapter(null);
|
|
}
|
|
|
|
protected BookmarksListAdapter getCursorAdapter(Cursor c) {
|
|
if (mCursorAdapter == null) {
|
|
mCursorAdapter = new BookmarksListAdapter(mContext, c);
|
|
} else if (c != null) {
|
|
mCursorAdapter.changeCursor(c);
|
|
} else {
|
|
// do a quick return if just asking for the cached adapter
|
|
return mCursorAdapter;
|
|
}
|
|
|
|
TextView headerView = mCursorAdapter.getHeaderView();
|
|
if (headerView == null) {
|
|
headerView = (TextView) getInflater().inflate(R.layout.awesomebar_header_row, null);
|
|
mCursorAdapter.setHeaderView(headerView);
|
|
}
|
|
|
|
// Add/Remove header based on the root folder
|
|
if (mView != null) {
|
|
ListView list = (ListView)mView;
|
|
if (mFolderId == Bookmarks.FIXED_ROOT_ID) {
|
|
if (list.getHeaderViewsCount() == 1) {
|
|
list.removeHeaderView(headerView);
|
|
}
|
|
} else {
|
|
if (list.getHeaderViewsCount() == 0) {
|
|
list.addHeaderView(headerView, null, true);
|
|
}
|
|
headerView.setText(mFolderTitle);
|
|
}
|
|
}
|
|
|
|
return mCursorAdapter;
|
|
}
|
|
|
|
protected BookmarksQueryTask getQueryTask() {
|
|
if (mQueryTask == null) {
|
|
mQueryTask = new BookmarksQueryTask();
|
|
}
|
|
return mQueryTask;
|
|
}
|
|
|
|
public void handleItemClick(AdapterView<?> parent, View view, int position, long id) {
|
|
ListView list = (ListView)getView();
|
|
if (list == null)
|
|
return;
|
|
|
|
int headerCount = list.getHeaderViewsCount();
|
|
// If we tap on the header view, there's nothing to do
|
|
if (headerCount == 1 && position == 0)
|
|
return;
|
|
|
|
BookmarksListAdapter adapter = getCursorAdapter();
|
|
if (adapter == null)
|
|
return;
|
|
|
|
Cursor cursor = adapter.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 = adapter.getFolderTitle(position);
|
|
|
|
adapter.moveToChildFolder(folderId, folderTitle);
|
|
return;
|
|
}
|
|
|
|
// Otherwise, just open the URL
|
|
String url = cursor.getString(cursor.getColumnIndexOrThrow(URLColumns.URL));
|
|
String title = cursor.getString(cursor.getColumnIndexOrThrow(URLColumns.TITLE));
|
|
long parentId = cursor.getLong(cursor.getColumnIndexOrThrow(Bookmarks.PARENT));
|
|
if (parentId == Bookmarks.FIXED_READING_LIST_ID) {
|
|
url = ReaderModeUtils.getAboutReaderForUrl(url, true);
|
|
}
|
|
sendToListener(url, title);
|
|
}
|
|
|
|
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;
|
|
|
|
private LinkedList<Pair<Integer, String>> mParentStack;
|
|
private TextView mBookmarksTitleView;
|
|
|
|
public BookmarksListAdapter(Context context, Cursor c) {
|
|
super(context, -1, c, new String[] {}, new int[] {});
|
|
|
|
// mParentStack holds folder id/title pairs that allow us to navigate
|
|
// back up the folder heirarchy
|
|
mParentStack = new LinkedList<Pair<Integer, String>>();
|
|
|
|
// Add the root folder to the stack
|
|
Pair<Integer, String> rootFolder = new Pair<Integer, String>(Bookmarks.FIXED_ROOT_ID, "");
|
|
mParentStack.addFirst(rootFolder);
|
|
}
|
|
|
|
public void refreshCurrentFolder() {
|
|
// Cancel any pre-existing async refresh tasks
|
|
if (mQueryTask != null)
|
|
mQueryTask.cancel(false);
|
|
|
|
Pair<Integer, String> folderPair = mParentStack.getFirst();
|
|
mQueryTask = new BookmarksQueryTask(folderPair.first, folderPair.second);
|
|
mQueryTask.execute();
|
|
}
|
|
|
|
// Returns false if there is no parent folder to move to
|
|
public boolean moveToParentFolder() {
|
|
// If we're already at the root, we can't move to a parent folder
|
|
if (mParentStack.size() == 1)
|
|
return false;
|
|
|
|
mParentStack.removeFirst();
|
|
refreshCurrentFolder();
|
|
return true;
|
|
}
|
|
|
|
public void moveToChildFolder(int folderId, String folderTitle) {
|
|
Pair<Integer, String> folderPair = new Pair<Integer, String>(folderId, folderTitle);
|
|
mParentStack.addFirst(folderPair);
|
|
refreshCurrentFolder();
|
|
}
|
|
|
|
public boolean isInReadingList() {
|
|
Pair<Integer, String> folderPair = mParentStack.getFirst();
|
|
return (folderPair.first == Bookmarks.FIXED_READING_LIST_ID);
|
|
}
|
|
|
|
@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;
|
|
}
|
|
|
|
@Override
|
|
public int getViewTypeCount() {
|
|
return VIEW_TYPE_COUNT;
|
|
}
|
|
|
|
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);
|
|
else if (guid.equals(Bookmarks.READING_LIST_FOLDER_GUID))
|
|
return getResources().getString(R.string.bookmarks_folder_reading_list);
|
|
|
|
// 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));
|
|
}
|
|
|
|
@Override
|
|
public View getView(int position, View convertView, ViewGroup parent) {
|
|
int viewType = getItemViewType(position);
|
|
AwesomeEntryViewHolder viewHolder = null;
|
|
|
|
if (convertView == null) {
|
|
if (viewType == VIEW_TYPE_ITEM)
|
|
convertView = getInflater().inflate(R.layout.awesomebar_row, null);
|
|
else
|
|
convertView = getInflater().inflate(R.layout.awesomebar_folder_row, null);
|
|
|
|
viewHolder = new AwesomeEntryViewHolder();
|
|
viewHolder.titleView = (TextView) convertView.findViewById(R.id.title);
|
|
viewHolder.faviconView = (FaviconView) convertView.findViewById(R.id.favicon);
|
|
|
|
if (viewType == VIEW_TYPE_ITEM)
|
|
viewHolder.urlView = (TextView) convertView.findViewById(R.id.url);
|
|
|
|
convertView.setTag(viewHolder);
|
|
} else {
|
|
viewHolder = (AwesomeEntryViewHolder) convertView.getTag();
|
|
}
|
|
|
|
Cursor cursor = getCursor();
|
|
if (!cursor.moveToPosition(position))
|
|
throw new IllegalStateException("Couldn't move cursor to position " + position);
|
|
|
|
if (viewType == VIEW_TYPE_ITEM) {
|
|
updateTitle(viewHolder.titleView, cursor);
|
|
updateUrl(viewHolder, cursor);
|
|
|
|
byte[] b = cursor.getBlob(cursor.getColumnIndexOrThrow(URLColumns.FAVICON));
|
|
Bitmap favicon = null;
|
|
if (b != null && b.length > 0) {
|
|
Bitmap bitmap = BitmapUtils.decodeByteArray(b);
|
|
if (bitmap != null) {
|
|
favicon = Favicons.getInstance().scaleImage(bitmap);
|
|
}
|
|
}
|
|
String url = cursor.getString(cursor.getColumnIndexOrThrow(URLColumns.URL));
|
|
updateFavicon(viewHolder.faviconView, favicon, url);
|
|
} else {
|
|
viewHolder.titleView.setText(getFolderTitle(position));
|
|
}
|
|
|
|
return convertView;
|
|
}
|
|
|
|
public TextView getHeaderView() {
|
|
return mBookmarksTitleView;
|
|
}
|
|
|
|
public void setHeaderView(TextView titleView) {
|
|
mBookmarksTitleView = titleView;
|
|
}
|
|
}
|
|
|
|
private class BookmarksQueryTask extends AsyncTask<Void, Void, Cursor> {
|
|
public BookmarksQueryTask() {
|
|
mFolderId = Bookmarks.FIXED_ROOT_ID;
|
|
mFolderTitle = "";
|
|
}
|
|
|
|
public BookmarksQueryTask(int folderId, String folderTitle) {
|
|
mFolderId = folderId;
|
|
mFolderTitle = folderTitle;
|
|
}
|
|
|
|
@Override
|
|
protected Cursor doInBackground(Void... arg0) {
|
|
return BrowserDB.getBookmarksInFolder(getContentResolver(), mFolderId);
|
|
}
|
|
|
|
@Override
|
|
protected void onPostExecute(final Cursor cursor) {
|
|
// Hack: force this to the main thread, even though it should already be on it
|
|
ThreadUtils.postToUiThread(new Runnable() {
|
|
@Override
|
|
public void run() {
|
|
// this will update the cursorAdapter to use the new one if it already exists
|
|
// We need to add the header before we set the adapter, hence make it null
|
|
ListView list = (ListView)mView;
|
|
list.setAdapter(null);
|
|
list.setAdapter(getCursorAdapter(cursor));
|
|
}
|
|
});
|
|
mQueryTask = null;
|
|
}
|
|
}
|
|
|
|
public boolean moveToParentFolder() {
|
|
// If we're not in the bookmarks tab, we have nothing to do. We should
|
|
// also return false if mBookmarksAdapter hasn't been initialized yet.
|
|
BookmarksListAdapter adapter = getCursorAdapter();
|
|
if (adapter == null)
|
|
return false;
|
|
|
|
return adapter.moveToParentFolder();
|
|
}
|
|
|
|
/**
|
|
* Whether the user is in the Reading List bookmarks directory in the
|
|
* AwesomeScreen UI.
|
|
*/
|
|
public boolean isInReadingList() {
|
|
if (mCursorAdapter == null)
|
|
return false;
|
|
|
|
return mCursorAdapter.isInReadingList();
|
|
}
|
|
|
|
@Override
|
|
public ContextMenuSubject getSubject(ContextMenu menu, View view, ContextMenuInfo menuInfo) {
|
|
ContextMenuSubject subject = null;
|
|
|
|
if (!(menuInfo instanceof AdapterView.AdapterContextMenuInfo)) {
|
|
Log.e(LOGTAG, "menuInfo is not AdapterContextMenuInfo");
|
|
return subject;
|
|
}
|
|
|
|
ListView list = (ListView)view;
|
|
AdapterView.AdapterContextMenuInfo info = (AdapterView.AdapterContextMenuInfo) menuInfo;
|
|
Object selectedItem = list.getItemAtPosition(info.position);
|
|
|
|
if (!(selectedItem instanceof Cursor)) {
|
|
Log.e(LOGTAG, "item at " + info.position + " is not a Cursor");
|
|
return subject;
|
|
}
|
|
|
|
Cursor cursor = (Cursor) selectedItem;
|
|
|
|
// Don't show the context menu for folders
|
|
if (!(cursor.getInt(cursor.getColumnIndexOrThrow(Bookmarks.TYPE)) == Bookmarks.TYPE_FOLDER)) {
|
|
String keyword = null;
|
|
int keywordCol = cursor.getColumnIndex(URLColumns.KEYWORD);
|
|
if (keywordCol != -1)
|
|
keyword = cursor.getString(keywordCol);
|
|
|
|
int id = cursor.getInt(cursor.getColumnIndexOrThrow(Bookmarks._ID));
|
|
|
|
subject = new ContextMenuSubject(id,
|
|
cursor.getString(cursor.getColumnIndexOrThrow(URLColumns.URL)),
|
|
cursor.getBlob(cursor.getColumnIndexOrThrow(URLColumns.FAVICON)),
|
|
cursor.getString(cursor.getColumnIndexOrThrow(URLColumns.TITLE)),
|
|
keyword,
|
|
isInReadingList() ? Combined.DISPLAY_READER : Combined.DISPLAY_NORMAL);
|
|
}
|
|
|
|
if (subject == null)
|
|
return subject;
|
|
|
|
setupMenu(menu, subject);
|
|
|
|
menu.findItem(R.id.remove_history).setVisible(false);
|
|
menu.findItem(R.id.open_in_reader).setVisible(false);
|
|
|
|
return subject;
|
|
}
|
|
}
|