diff --git a/mobile/android/base/GeckoViewsFactory.java b/mobile/android/base/GeckoViewsFactory.java index a83b641271d..4e8046ab40c 100644 --- a/mobile/android/base/GeckoViewsFactory.java +++ b/mobile/android/base/GeckoViewsFactory.java @@ -6,6 +6,7 @@ package org.mozilla.gecko; import org.mozilla.gecko.gfx.LayerView; import org.mozilla.gecko.home.BookmarkFolderView; +import org.mozilla.gecko.home.BookmarkThumbnailView; import org.mozilla.gecko.home.BookmarksListView; import org.mozilla.gecko.home.FadedTextView; import org.mozilla.gecko.home.TwoLinePageRow; @@ -90,6 +91,7 @@ public final class GeckoViewsFactory implements LayoutInflater.Factory { 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.BookmarkThumbnailView", BookmarkThumbnailView.class.getConstructor(arg1Class, arg2Class)); mFactoryMap.put("home.BookmarksListView", BookmarksListView.class.getConstructor(arg1Class, arg2Class)); mFactoryMap.put("home.FadedTextView", FadedTextView.class.getConstructor(arg1Class, arg2Class)); mFactoryMap.put("home.TwoLinePageRow", TwoLinePageRow.class.getConstructor(arg1Class, arg2Class)); diff --git a/mobile/android/base/Makefile.in b/mobile/android/base/Makefile.in index a4fff2121b4..e23110bf5ac 100644 --- a/mobile/android/base/Makefile.in +++ b/mobile/android/base/Makefile.in @@ -216,11 +216,14 @@ FENNEC_JAVA_FILES = \ home/BookmarksListView.java \ home/BookmarksPage.java \ home/BookmarkFolderView.java \ + home/BookmarkThumbnailView.java \ home/HomeFragment.java \ home/HomeListView.java \ home/HomePager.java \ home/HomePagerTabStrip.java \ home/FadedTextView.java \ + home/TopBookmarkItemView.java \ + home/TopBookmarksView.java \ home/TwoLinePageRow.java \ menu/GeckoMenu.java \ menu/GeckoMenuInflater.java \ @@ -464,6 +467,7 @@ RES_LAYOUT = \ res/layout/tabs_item_cell.xml \ res/layout/tabs_item_row.xml \ res/layout/text_selection_handles.xml \ + res/layout/top_bookmark_item_view.xml \ res/layout/two_line_page_row.xml \ res/layout/list_item_header.xml \ res/layout/select_dialog_list.xml \ @@ -982,6 +986,7 @@ RES_COLOR = \ res/color/select_item_multichoice.xml \ res/color/tertiary_text.xml \ res/color/tertiary_text_inverse.xml \ + res/color/top_bookmark_item_title.xml \ res/color/url_bar_title.xml \ res/color/url_bar_title_hint.xml \ $(NULL) diff --git a/mobile/android/base/home/BookmarkThumbnailView.java b/mobile/android/base/home/BookmarkThumbnailView.java new file mode 100644 index 00000000000..f544b3d3b7b --- /dev/null +++ b/mobile/android/base/home/BookmarkThumbnailView.java @@ -0,0 +1,71 @@ +/* -*- 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; + +import android.content.Context; +import android.graphics.PorterDuff.Mode; +import android.graphics.drawable.Drawable; +import android.util.AttributeSet; +import android.widget.ImageView; + +/** + * A height constrained ImageView to show thumbnails of top bookmarks. + */ +public class BookmarkThumbnailView extends ImageView { + private static final String LOGTAG = "GeckoBookmarkThumbnailView"; + + // 27.34% opacity filter for the dominant color. + private static final int COLOR_FILTER = 0x46FFFFFF; + + // Default filter color for "Add a bookmark" views. + private static final int DEFAULT_COLOR = 0x46ECF0F3; + + public BookmarkThumbnailView(Context context) { + this(context, null); + } + + public BookmarkThumbnailView(Context context, AttributeSet attrs) { + this(context, attrs, R.attr.bookmarkThumbnailViewStyle); + } + + public BookmarkThumbnailView(Context context, AttributeSet attrs, int defStyle) { + super(context, attrs, defStyle); + } + + /** + * Measure the view to determine the measured width and height. + * The height is constrained by the measured width. + * + * @param widthMeasureSpec horizontal space requirements as imposed by the parent. + * @param heightMeasureSpec vertical space requirements as imposed by the parent, but ignored. + */ + @Override + protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { + // Default measuring. + super.onMeasure(widthMeasureSpec, heightMeasureSpec); + + // Force the height based on the aspect ratio. + final int width = getMeasuredWidth(); + final int height = (int) (width * ThumbnailHelper.THUMBNAIL_ASPECT_RATIO); + setMeasuredDimension(width, height); + } + + /** + * Sets the background to a Drawable by applying the specified color as a filter. + * + * @param color the color filter to apply over the drawable. + */ + @Override + public void setBackgroundColor(int color) { + int colorFilter = color == 0 ? DEFAULT_COLOR : color & COLOR_FILTER; + Drawable drawable = getResources().getDrawable(R.drawable.favicon_bg); + drawable.setColorFilter(colorFilter, Mode.SRC_ATOP); + setBackgroundDrawable(drawable); + } +} diff --git a/mobile/android/base/home/TopBookmarkItemView.java b/mobile/android/base/home/TopBookmarkItemView.java new file mode 100644 index 00000000000..67a92580e8e --- /dev/null +++ b/mobile/android/base/home/TopBookmarkItemView.java @@ -0,0 +1,221 @@ +/* -*- 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 android.content.Context; +import android.graphics.Bitmap; +import android.graphics.Paint; +import android.graphics.Path; +import android.graphics.drawable.Drawable; +import android.graphics.drawable.ShapeDrawable; +import android.graphics.drawable.shapes.PathShape; +import android.text.TextUtils; +import android.util.AttributeSet; +import android.view.LayoutInflater; +import android.widget.ImageView; +import android.widget.RelativeLayout; +import android.widget.TextView; + +/** + * A view that displays the thumbnail and the title/url for a bookmark. + * If the title/url is longer than the width of the view, they are faded out. + * If there is no valid url, a default string is shown at 50% opacity. + * This is denoted by the empty state. + */ +public class TopBookmarkItemView extends RelativeLayout { + private static final String LOGTAG = "GeckoTopBookmarkItemView"; + + // Empty state, to denote there is no valid url. + private static final int[] STATE_EMPTY = { android.R.attr.state_empty }; + + // A Pin Drawable to denote pinned sites. + private static Drawable sPinDrawable = null; + + // Child views. + private final TextView mTitleView; + private final ImageView mThumbnailView; + private final ImageView mPinView; + + // Data backing this view. + private String mTitle; + private String mUrl; + + // Pinned state. + private boolean mIsPinned = false; + + // Empty state. + private boolean mIsEmpty = true; + + public TopBookmarkItemView(Context context) { + this(context, null); + } + + public TopBookmarkItemView(Context context, AttributeSet attrs) { + this(context, attrs, R.attr.topBookmarkItemViewStyle); + } + + public TopBookmarkItemView(Context context, AttributeSet attrs, int defStyle) { + super(context, attrs, defStyle); + + LayoutInflater.from(context).inflate(R.layout.top_bookmark_item_view, this); + + mTitleView = (TextView) findViewById(R.id.title); + mThumbnailView = (ImageView) findViewById(R.id.thumbnail); + mPinView = (ImageView) findViewById(R.id.pin); + } + + /** + * {@inheritDoc} + */ + @Override + public int[] onCreateDrawableState(int extraSpace) { + final int[] drawableState = super.onCreateDrawableState(extraSpace + 1); + + if (mIsEmpty) { + mergeDrawableStates(drawableState, STATE_EMPTY); + } + + return drawableState; + } + + /** + * @return The title shown by this view. + */ + public String getTitle() { + return (!TextUtils.isEmpty(mTitle) ? mTitle : mUrl); + } + + /** + * @return The url shown by this view. + */ + public String getUrl() { + return mUrl; + } + + /** + * @return true, if this view is pinned, false otherwise. + */ + public boolean isPinned() { + return mIsPinned; + } + + /** + * @param title The title for this view. + */ + public void setTitle(String title) { + if (mTitle != null && mTitle.equals(title)) { + return; + } + + mTitle = title; + updateTitleView(); + } + + /** + * @param url The url for this view. + */ + public void setUrl(String url) { + if (mUrl != null && mUrl.equals(url)) { + return; + } + + mUrl = url; + updateTitleView(); + } + + /** + * @param pinned The pinned state of this view. + */ + public void setPinned(boolean pinned) { + mIsPinned = pinned; + mPinView.setBackgroundDrawable(pinned ? getPinDrawable() : null); + } + + /** + * Display the thumbnail from a resource. + * + * @param resId Resource ID of the drawable to show. + */ + public void displayThumbnail(int resId) { + mThumbnailView.setImageResource(resId); + mThumbnailView.setBackgroundColor(0x0); + } + + /** + * Display the thumbnail from a bitmap. + * + * @param thumbnail The bitmap to show as thumbnail. + */ + public void displayThumbnail(Bitmap thumbnail) { + if (thumbnail == null) { + // Show a favicon based view instead. + displayThumbnail(R.drawable.favicon); + return; + } + + mThumbnailView.setImageBitmap(thumbnail); + mThumbnailView.setBackgroundDrawable(null); + } + + /** + * Display the thumbnail from a favicon. + * + * @param favicon The favicon to show as thumbnail. + */ + public void displayFavicon(Bitmap favicon) { + if (favicon == null) { + // Should show default favicon. + displayThumbnail(R.drawable.favicon); + return; + } + + mThumbnailView.setImageBitmap(favicon); + mThumbnailView.setBackgroundColor(Favicons.getInstance().getFaviconColor(favicon, mUrl)); + } + + /** + * Update the title shown by this view. If both title and url + * are empty, mark the state as STATE_EMPTY and show a default text. + */ + private void updateTitleView() { + String title = getTitle(); + if (!TextUtils.isEmpty(title)) { + mTitleView.setText(title); + mIsEmpty = false; + } else { + mTitleView.setText(R.string.bookmark_add); + mIsEmpty = true; + } + + // Refresh for state change. + refreshDrawableState(); + } + + /** + * @return Drawable to be used as a pin. + */ + private Drawable getPinDrawable() { + if (sPinDrawable == null) { + int size = getResources().getDimensionPixelSize(R.dimen.abouthome_topsite_pinsize); + + // Draw a little triangle in the upper right corner. + Path path = new Path(); + path.moveTo(0, 0); + path.lineTo(size, 0); + path.lineTo(size, size); + path.close(); + + sPinDrawable = new ShapeDrawable(new PathShape(path, size, size)); + Paint p = ((ShapeDrawable) sPinDrawable).getPaint(); + p.setColor(getResources().getColor(R.color.abouthome_topsite_pin)); + } + + return sPinDrawable; + } +} diff --git a/mobile/android/base/home/TopBookmarksView.java b/mobile/android/base/home/TopBookmarksView.java new file mode 100644 index 00000000000..c21189261d4 --- /dev/null +++ b/mobile/android/base/home/TopBookmarksView.java @@ -0,0 +1,384 @@ +/* -*- 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; +import org.mozilla.gecko.db.BrowserContract.Thumbnails; +import org.mozilla.gecko.db.BrowserDB; +import org.mozilla.gecko.db.BrowserDB.TopSitesCursorWrapper; +import org.mozilla.gecko.db.BrowserDB.URLColumns; +import org.mozilla.gecko.gfx.BitmapUtils; +import org.mozilla.gecko.home.HomePager.OnUrlOpenListener; +import org.mozilla.gecko.util.ThreadUtils; +import org.mozilla.gecko.util.UiAsyncTask; + +import android.content.Context; +import android.database.Cursor; +import android.graphics.Bitmap; +import android.text.TextUtils; +import android.util.AttributeSet; +import android.view.View; +import android.view.ViewGroup; +import android.widget.AbsListView; +import android.widget.AdapterView; +import android.widget.CursorAdapter; +import android.widget.GridView; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * 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"; + + // Max number of bookmarks that needs to be shown. + private int mMaxBookmarks; + + // Number of columns to show. + private int mNumColumns; + + // On URL open listener. + private OnUrlOpenListener mUrlOpenListener; + + // A cursor based adapter backing this view. + protected TopBookmarksAdapter mAdapter; + + // Temporary cache to store the thumbnails until the next layout pass. + private Map mThumbnailsCache; + + 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); + } + + /** + * {@inheritDoc} + */ + @Override + public void onAttachedToWindow() { + super.onAttachedToWindow(); + + // Initialize the adapter. + mAdapter = new TopBookmarksAdapter(getContext(), null); + setAdapter(mAdapter); + + setOnItemClickListener(new AdapterView.OnItemClickListener() { + @Override + public void onItemClick(AdapterView parent, View view, int position, long id) { + TopBookmarkItemView row = (TopBookmarkItemView) view; + String url = row.getUrl(); + + if (mUrlOpenListener != null && !TextUtils.isEmpty(url)) { + mUrlOpenListener.onUrlOpen(url); + } + } + }); + + // Load the bookmarks. + new LoadBookmarksTask().execute(); + } + + /** + * {@inheritDoc} + */ + @Override + public void onDetachedFromWindow() { + super.onDetachedFromWindow(); + if (mAdapter != null) { + setAdapter(null); + final Cursor cursor = mAdapter.getCursor(); + + ThreadUtils.postToBackgroundThread(new Runnable() { + @Override + public void run() { + if (cursor != null && !cursor.isClosed()) + cursor.close(); + } + }); + } + } + + /** + * {@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. + return (getMeasuredWidth() - getPaddingLeft() - getPaddingRight()) / mNumColumns; + } + + /** + * {@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(); + final int count = (adapter == null ? 0 : adapter.getCount()); + + if (adapter != null && count > 0) { + // 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); + } + + // 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(); + } + } + + // 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; + + // Total height of this view. + final int measuredHeight = childrenHeight + getPaddingTop() + getPaddingBottom(); + setMeasuredDimension(measuredWidth, measuredHeight); + } + + /** + * {@inheritDoc} + */ + @Override + protected void onLayout(boolean changed, int left, int top, int right, int bottom) { + super.onLayout(changed, left, top, right, bottom); + + // If there are thumnails in the cache, update them. + if (mThumbnailsCache != null) { + updateThumbnails(mThumbnailsCache); + mThumbnailsCache = null; + } + } + + /** + * 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; + } + + /** + * Update the thumbnails returned by the db. + * + * @param thumbnails A map of urls and their thumbnail bitmaps. + */ + private void updateThumbnails(Map thumbnails) { + final int count = mAdapter.getCount(); + for (int i = 0; i < count; i++) { + final View child = getChildAt(i); + + // The grid view might get temporarily out of sync with the + // adapter refreshes (e.g. on device rotation). + if (child == null) { + continue; + } + + TopBookmarkItemView view = (TopBookmarkItemView) child; + final String url = view.getUrl(); + + // If there is no url, then show "add bookmark". + if (TextUtils.isEmpty(url)) { + view.displayThumbnail(R.drawable.abouthome_thumbnail_add); + } else { + // Show the thumbnail. + Bitmap bitmap = (thumbnails != null ? thumbnails.get(url) : null); + view.displayThumbnail(bitmap); + } + } + } + + /** + * A cursor adapter holding the pinned and top bookmarks. + */ + public class TopBookmarksAdapter extends CursorAdapter { + public TopBookmarksAdapter(Context context, Cursor cursor) { + super(context, cursor); + } + + /** + * {@inheritDoc} + */ + @Override + public int getCount() { + return Math.min(super.getCount(), mMaxBookmarks); + } + + /** + * {@inheritDoc} + */ + @Override + protected void onContentChanged () { + // Don't do anything. We don't want to regenerate every time + // our database is updated. + return; + } + + /** + * {@inheritDoc} + */ + @Override + public void bindView(View bindView, Context context, Cursor cursor) { + String url = ""; + String title = ""; + boolean pinned = false; + + // Cursor is already moved to required position. + if (!cursor.isAfterLast()) { + url = cursor.getString(cursor.getColumnIndexOrThrow(URLColumns.URL)); + title = cursor.getString(cursor.getColumnIndexOrThrow(URLColumns.TITLE)); + pinned = ((TopSitesCursorWrapper) cursor).isPinned(); + } + + TopBookmarkItemView view = (TopBookmarkItemView) bindView; + view.setTitle(title); + view.setUrl(url); + view.setPinned(pinned); + } + + /** + * {@inheritDoc} + */ + @Override + public View newView(Context context, Cursor cursor, ViewGroup parent) { + return new TopBookmarkItemView(context); + } + } + + /** + * An AsyncTask to load the top bookmarks from the db. + */ + private class LoadBookmarksTask extends UiAsyncTask { + public LoadBookmarksTask() { + super(ThreadUtils.getBackgroundHandler()); + } + + @Override + protected Cursor doInBackground(Void... params) { + return BrowserDB.getTopSites(getContext().getContentResolver(), mMaxBookmarks); + } + + @Override + public void onPostExecute(Cursor cursor) { + // Change to new cursor. + mAdapter.changeCursor(cursor); + + // Load the thumbnails. + if (mAdapter.getCount() > 0) { + new LoadThumbnailsTask().execute(cursor); + } + } + } + + /** + * An AsyncTask to load the thumbnails from a cursor. + */ + private class LoadThumbnailsTask extends UiAsyncTask> { + public LoadThumbnailsTask() { + super(ThreadUtils.getBackgroundHandler()); + } + + @Override + protected Map doInBackground(Cursor... params) { + // TopBookmarksAdapter's cursor. + final Cursor adapterCursor = params[0]; + if (adapterCursor == null || !adapterCursor.moveToFirst()) { + return null; + } + + final List urls = new ArrayList(); + do { + final String url = adapterCursor.getString(adapterCursor.getColumnIndexOrThrow(URLColumns.URL)); + urls.add(url); + } while(adapterCursor.moveToNext()); + + if (urls.size() == 0) { + return null; + } + + Map thumbnails = new HashMap(); + Cursor cursor = BrowserDB.getThumbnailsForUrls(getContext().getContentResolver(), urls); + if (cursor == null || !cursor.moveToFirst()) { + return null; + } + + try { + do { + final String url = cursor.getString(cursor.getColumnIndexOrThrow(Thumbnails.URL)); + final byte[] b = cursor.getBlob(cursor.getColumnIndexOrThrow(Thumbnails.DATA)); + if (b == null) { + continue; + } + + Bitmap thumbnail = BitmapUtils.decodeByteArray(b); + if (thumbnail == null) { + continue; + } + + thumbnails.put(url, thumbnail); + } while (cursor.moveToNext()); + } finally { + if (cursor != null) { + cursor.close(); + } + } + + return thumbnails; + } + + @Override + public void onPostExecute(Map thumbnails) { + // If there's a layout scheduled on this view, wait for it to happen + // by storing the thumbnails in a cache. If not, update them right away. + if (isLayoutRequested()) { + mThumbnailsCache = thumbnails; + } else { + updateThumbnails(thumbnails); + } + } + } +} diff --git a/mobile/android/base/locales/en-US/android_strings.dtd b/mobile/android/base/locales/en-US/android_strings.dtd index 534e2813968..4b2d9862338 100644 --- a/mobile/android/base/locales/en-US/android_strings.dtd +++ b/mobile/android/base/locales/en-US/android_strings.dtd @@ -210,6 +210,7 @@ size. --> + diff --git a/mobile/android/base/resources/color/top_bookmark_item_title.xml b/mobile/android/base/resources/color/top_bookmark_item_title.xml new file mode 100644 index 00000000000..29357302539 --- /dev/null +++ b/mobile/android/base/resources/color/top_bookmark_item_title.xml @@ -0,0 +1,14 @@ + + + + + + + + + + + + diff --git a/mobile/android/base/resources/drawable-hdpi/abouthome_thumbnail_add.png b/mobile/android/base/resources/drawable-hdpi/abouthome_thumbnail_add.png index afa1421f72e..59427ab670b 100644 Binary files a/mobile/android/base/resources/drawable-hdpi/abouthome_thumbnail_add.png and b/mobile/android/base/resources/drawable-hdpi/abouthome_thumbnail_add.png differ diff --git a/mobile/android/base/resources/drawable-mdpi/abouthome_thumbnail_add.png b/mobile/android/base/resources/drawable-mdpi/abouthome_thumbnail_add.png index c1b22593e18..1f1a4ae5cfd 100644 Binary files a/mobile/android/base/resources/drawable-mdpi/abouthome_thumbnail_add.png and b/mobile/android/base/resources/drawable-mdpi/abouthome_thumbnail_add.png differ diff --git a/mobile/android/base/resources/drawable-xhdpi/abouthome_thumbnail_add.png b/mobile/android/base/resources/drawable-xhdpi/abouthome_thumbnail_add.png index 14bf1ffa3b4..da9f1907a51 100644 Binary files a/mobile/android/base/resources/drawable-xhdpi/abouthome_thumbnail_add.png and b/mobile/android/base/resources/drawable-xhdpi/abouthome_thumbnail_add.png differ diff --git a/mobile/android/base/resources/layout/top_bookmark_item_view.xml b/mobile/android/base/resources/layout/top_bookmark_item_view.xml new file mode 100644 index 00000000000..56bbec0315d --- /dev/null +++ b/mobile/android/base/resources/layout/top_bookmark_item_view.xml @@ -0,0 +1,31 @@ + + + + + + + + + + + + diff --git a/mobile/android/base/resources/values-v11/styles.xml b/mobile/android/base/resources/values-v11/styles.xml index 59f74e919fe..5ca2be7016f 100644 --- a/mobile/android/base/resources/values-v11/styles.xml +++ b/mobile/android/base/resources/values-v11/styles.xml @@ -21,6 +21,8 @@ diff --git a/mobile/android/base/resources/values/attrs.xml b/mobile/android/base/resources/values/attrs.xml index cf203cd5be3..faa18c61a92 100644 --- a/mobile/android/base/resources/values/attrs.xml +++ b/mobile/android/base/resources/values/attrs.xml @@ -5,6 +5,20 @@ + + + + + + + + + + + + + + diff --git a/mobile/android/base/resources/values/styles.xml b/mobile/android/base/resources/values/styles.xml index 9bc0297251e..2c58124ad37 100644 --- a/mobile/android/base/resources/values/styles.xml +++ b/mobile/android/base/resources/values/styles.xml @@ -18,6 +18,8 @@ + + + + + + + + + + + +