2013-06-20 18:45:58 +00:00
|
|
|
/* -*- 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.ThumbnailHelper;
|
2013-07-22 20:08:38 +00:00
|
|
|
import org.mozilla.gecko.db.BrowserDB.TopSitesCursorWrapper;
|
|
|
|
import org.mozilla.gecko.db.BrowserDB.URLColumns;
|
2013-06-20 18:45:58 +00:00
|
|
|
import org.mozilla.gecko.home.HomePager.OnUrlOpenListener;
|
|
|
|
|
|
|
|
import android.content.Context;
|
2013-08-02 17:03:21 +00:00
|
|
|
import android.content.res.TypedArray;
|
2013-07-22 20:08:38 +00:00
|
|
|
import android.database.Cursor;
|
2013-06-20 18:45:58 +00:00
|
|
|
import android.text.TextUtils;
|
|
|
|
import android.util.AttributeSet;
|
2013-07-22 20:08:38 +00:00
|
|
|
import android.view.ContextMenu.ContextMenuInfo;
|
2013-06-20 18:45:58 +00:00
|
|
|
import android.view.View;
|
2013-08-02 19:29:45 +00:00
|
|
|
import android.view.animation.AnimationUtils;
|
|
|
|
import android.view.animation.GridLayoutAnimationController;
|
2013-06-20 18:45:58 +00:00
|
|
|
import android.widget.AbsListView;
|
|
|
|
import android.widget.AdapterView;
|
|
|
|
import android.widget.GridView;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* A grid view of top bookmarks and pinned tabs.
|
|
|
|
* Each cell in the grid is a TopBookmarkItemView.
|
|
|
|
*/
|
|
|
|
public class TopBookmarksView extends GridView {
|
|
|
|
private static final String LOGTAG = "GeckoTopBookmarksView";
|
|
|
|
|
2013-07-22 19:49:36 +00:00
|
|
|
// Listener for pinning bookmarks.
|
|
|
|
public static interface OnPinBookmarkListener {
|
|
|
|
public void onPinBookmark(int position);
|
|
|
|
}
|
|
|
|
|
2013-06-20 18:45:58 +00:00
|
|
|
// Max number of bookmarks that needs to be shown.
|
2013-07-02 21:31:34 +00:00
|
|
|
private final int mMaxBookmarks;
|
2013-06-20 18:45:58 +00:00
|
|
|
|
|
|
|
// Number of columns to show.
|
2013-07-02 21:31:34 +00:00
|
|
|
private final int mNumColumns;
|
2013-06-20 18:45:58 +00:00
|
|
|
|
2013-08-07 16:39:26 +00:00
|
|
|
// Horizontal spacing in between the rows.
|
|
|
|
private final int mHorizontalSpacing;
|
|
|
|
|
|
|
|
// Vertical spacing in between the rows.
|
2013-08-02 17:03:21 +00:00
|
|
|
private final int mVerticalSpacing;
|
|
|
|
|
2013-06-20 18:45:58 +00:00
|
|
|
// On URL open listener.
|
|
|
|
private OnUrlOpenListener mUrlOpenListener;
|
|
|
|
|
2013-07-22 19:49:36 +00:00
|
|
|
// Pin bookmark listener.
|
|
|
|
private OnPinBookmarkListener mPinBookmarkListener;
|
|
|
|
|
2013-07-22 20:08:38 +00:00
|
|
|
// Context menu info.
|
|
|
|
private TopBookmarksContextMenuInfo mContextMenuInfo;
|
|
|
|
|
2013-06-20 18:45:58 +00:00
|
|
|
public TopBookmarksView(Context context) {
|
|
|
|
this(context, null);
|
|
|
|
}
|
|
|
|
|
|
|
|
public TopBookmarksView(Context context, AttributeSet attrs) {
|
|
|
|
this(context, attrs, R.attr.topBookmarksViewStyle);
|
|
|
|
}
|
|
|
|
|
|
|
|
public TopBookmarksView(Context context, AttributeSet attrs, int defStyle) {
|
|
|
|
super(context, attrs, defStyle);
|
|
|
|
mMaxBookmarks = getResources().getInteger(R.integer.number_of_top_sites);
|
|
|
|
mNumColumns = getResources().getInteger(R.integer.number_of_top_sites_cols);
|
|
|
|
setNumColumns(mNumColumns);
|
2013-08-02 17:03:21 +00:00
|
|
|
|
|
|
|
TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.TopBookmarksView, defStyle, 0);
|
2013-08-07 16:39:26 +00:00
|
|
|
mHorizontalSpacing = a.getDimensionPixelOffset(R.styleable.TopBookmarksView_android_horizontalSpacing, 0x00);
|
2013-08-02 17:03:21 +00:00
|
|
|
mVerticalSpacing = a.getDimensionPixelOffset(R.styleable.TopBookmarksView_android_verticalSpacing, 0x00);
|
|
|
|
a.recycle();
|
2013-06-20 18:45:58 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* {@inheritDoc}
|
|
|
|
*/
|
|
|
|
@Override
|
|
|
|
public void onAttachedToWindow() {
|
|
|
|
super.onAttachedToWindow();
|
|
|
|
|
|
|
|
setOnItemClickListener(new AdapterView.OnItemClickListener() {
|
|
|
|
@Override
|
|
|
|
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
|
|
|
|
TopBookmarkItemView row = (TopBookmarkItemView) view;
|
|
|
|
String url = row.getUrl();
|
|
|
|
|
2013-07-22 19:49:36 +00:00
|
|
|
// If the url is empty, the user can pin a bookmark.
|
|
|
|
// If not, navigate to the page given by the url.
|
|
|
|
if (!TextUtils.isEmpty(url)) {
|
|
|
|
if (mUrlOpenListener != null) {
|
|
|
|
mUrlOpenListener.onUrlOpen(url);
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
if (mPinBookmarkListener != null) {
|
|
|
|
mPinBookmarkListener.onPinBookmark(position);
|
|
|
|
}
|
2013-06-20 18:45:58 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
});
|
2013-07-22 20:08:38 +00:00
|
|
|
|
|
|
|
setOnItemLongClickListener(new AdapterView.OnItemLongClickListener() {
|
|
|
|
@Override
|
|
|
|
public boolean onItemLongClick(AdapterView<?> parent, View view, int position, long id) {
|
|
|
|
Cursor cursor = (Cursor) parent.getItemAtPosition(position);
|
|
|
|
mContextMenuInfo = new TopBookmarksContextMenuInfo(view, position, id, cursor);
|
|
|
|
return showContextMenuForChild(TopBookmarksView.this);
|
|
|
|
}
|
|
|
|
});
|
2013-08-02 19:29:45 +00:00
|
|
|
|
|
|
|
final GridLayoutAnimationController controller = new GridLayoutAnimationController(AnimationUtils.loadAnimation(getContext(), R.anim.grow_fade_in_center));
|
|
|
|
setLayoutAnimation(controller);
|
2013-06-20 18:45:58 +00:00
|
|
|
}
|
|
|
|
|
2013-07-22 19:49:56 +00:00
|
|
|
@Override
|
|
|
|
public void onDetachedFromWindow() {
|
|
|
|
super.onDetachedFromWindow();
|
|
|
|
|
|
|
|
mUrlOpenListener = null;
|
|
|
|
mPinBookmarkListener = null;
|
|
|
|
}
|
|
|
|
|
2013-06-20 18:45:58 +00:00
|
|
|
/**
|
|
|
|
* {@inheritDoc}
|
|
|
|
*/
|
|
|
|
@Override
|
|
|
|
public int getColumnWidth() {
|
|
|
|
// This method will be called from onMeasure() too.
|
|
|
|
// It's better to use getMeasuredWidth(), as it is safe in this case.
|
2013-08-07 16:39:26 +00:00
|
|
|
final int totalHorizontalSpacing = mNumColumns > 0 ? (mNumColumns - 1) * mHorizontalSpacing : 0;
|
|
|
|
return (getMeasuredWidth() - getPaddingLeft() - getPaddingRight() - totalHorizontalSpacing) / mNumColumns;
|
2013-06-20 18:45:58 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* {@inheritDoc}
|
|
|
|
*/
|
|
|
|
@Override
|
|
|
|
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
|
|
|
|
// Sets the padding for this view.
|
|
|
|
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
|
|
|
|
|
|
|
|
final int measuredWidth = getMeasuredWidth();
|
|
|
|
final int childWidth = getColumnWidth();
|
|
|
|
int childHeight = 0;
|
|
|
|
|
|
|
|
// Set the column width as the thumbnail width.
|
|
|
|
ThumbnailHelper.getInstance().setThumbnailWidth(childWidth);
|
|
|
|
|
|
|
|
// If there's an adapter, use it to calculate the height of this view.
|
|
|
|
final TopBookmarksAdapter adapter = (TopBookmarksAdapter) getAdapter();
|
2013-08-02 23:01:49 +00:00
|
|
|
final int count;
|
|
|
|
|
|
|
|
// There shouldn't be any inherent size (due to padding) if there are no child views.
|
|
|
|
if (adapter == null || (count = adapter.getCount()) == 0) {
|
|
|
|
setMeasuredDimension(0, 0);
|
|
|
|
return;
|
|
|
|
}
|
2013-06-20 18:45:58 +00:00
|
|
|
|
2013-08-02 23:01:49 +00:00
|
|
|
// Get the first child from the adapter.
|
|
|
|
final View child = adapter.getView(0, null, this);
|
|
|
|
if (child != null) {
|
|
|
|
// Set a default LayoutParams on the child, if it doesn't have one on its own.
|
|
|
|
AbsListView.LayoutParams params = (AbsListView.LayoutParams) child.getLayoutParams();
|
|
|
|
if (params == null) {
|
|
|
|
params = new AbsListView.LayoutParams(AbsListView.LayoutParams.WRAP_CONTENT,
|
|
|
|
AbsListView.LayoutParams.WRAP_CONTENT);
|
|
|
|
child.setLayoutParams(params);
|
2013-06-20 18:45:58 +00:00
|
|
|
}
|
2013-08-02 23:01:49 +00:00
|
|
|
|
|
|
|
// Measure the exact width of the child, and the height based on the width.
|
|
|
|
// Note: the child (and BookmarkThumbnailView) takes care of calculating its height.
|
|
|
|
int childWidthSpec = MeasureSpec.makeMeasureSpec(childWidth, MeasureSpec.EXACTLY);
|
|
|
|
int childHeightSpec = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED);
|
|
|
|
child.measure(childWidthSpec, childHeightSpec);
|
|
|
|
childHeight = child.getMeasuredHeight();
|
2013-06-20 18:45:58 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Find the minimum of bookmarks we need to show, and the one given by the cursor.
|
|
|
|
final int total = Math.min(count > 0 ? count : Integer.MAX_VALUE, mMaxBookmarks);
|
|
|
|
|
|
|
|
// Number of rows required to show these bookmarks.
|
|
|
|
final int rows = (int) Math.ceil((double) total / mNumColumns);
|
|
|
|
final int childrenHeight = childHeight * rows;
|
2013-08-02 17:03:21 +00:00
|
|
|
final int totalVerticalSpacing = rows > 0 ? (rows - 1) * mVerticalSpacing : 0;
|
2013-06-20 18:45:58 +00:00
|
|
|
|
|
|
|
// Total height of this view.
|
2013-08-02 17:03:21 +00:00
|
|
|
final int measuredHeight = childrenHeight + getPaddingTop() + getPaddingBottom() + totalVerticalSpacing;
|
2013-06-20 18:45:58 +00:00
|
|
|
setMeasuredDimension(measuredWidth, measuredHeight);
|
|
|
|
}
|
|
|
|
|
2013-07-22 20:08:38 +00:00
|
|
|
@Override
|
|
|
|
public ContextMenuInfo getContextMenuInfo() {
|
|
|
|
return mContextMenuInfo;
|
|
|
|
}
|
|
|
|
|
2013-06-20 18:45:58 +00:00
|
|
|
/**
|
|
|
|
* Set an url open listener to be used by this view.
|
|
|
|
*
|
|
|
|
* @param listener An url open listener for this view.
|
|
|
|
*/
|
|
|
|
public void setOnUrlOpenListener(OnUrlOpenListener listener) {
|
|
|
|
mUrlOpenListener = listener;
|
|
|
|
}
|
|
|
|
|
2013-07-22 19:49:36 +00:00
|
|
|
/**
|
|
|
|
* Set a pin bookmark listener to be used by this view.
|
|
|
|
*
|
|
|
|
* @param listener A pin bookmark listener for this view.
|
|
|
|
*/
|
|
|
|
public void setOnPinBookmarkListener(OnPinBookmarkListener listener) {
|
|
|
|
mPinBookmarkListener = listener;
|
|
|
|
}
|
|
|
|
|
2013-07-22 20:08:38 +00:00
|
|
|
/**
|
|
|
|
* A ContextMenuInfo for TopBoomarksView that adds details from the cursor.
|
|
|
|
*/
|
|
|
|
public static class TopBookmarksContextMenuInfo extends AdapterContextMenuInfo {
|
|
|
|
public String url;
|
|
|
|
public String title;
|
|
|
|
public boolean isPinned;
|
|
|
|
|
|
|
|
public TopBookmarksContextMenuInfo(View targetView, int position, long id, Cursor cursor) {
|
|
|
|
super(targetView, position, id);
|
|
|
|
|
|
|
|
if (cursor == null) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
url = cursor.getString(cursor.getColumnIndexOrThrow(URLColumns.URL));
|
|
|
|
title = cursor.getString(cursor.getColumnIndexOrThrow(URLColumns.TITLE));
|
|
|
|
isPinned = ((TopSitesCursorWrapper) cursor).isPinned();
|
|
|
|
}
|
|
|
|
}
|
2013-06-20 18:45:58 +00:00
|
|
|
}
|