зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1380808 - Add Pocket to new tab with placeholders. r=mcomella
MozReview-Commit-ID: 7yqmBF1qlLR --HG-- rename : mobile/android/app/src/main/res/drawable/ic_as_bookmarked.xml => mobile/android/app/src/main/res/drawable/ic_as_trending.xml rename : mobile/android/base/java/org/mozilla/gecko/activitystream/homepanel/stream/HighlightsTitleRow.java => mobile/android/base/java/org/mozilla/gecko/activitystream/homepanel/stream/StreamTitleRow.java rename : mobile/android/base/java/org/mozilla/gecko/activitystream/homepanel/stream/HighlightItemRow.java => mobile/android/base/java/org/mozilla/gecko/activitystream/homepanel/stream/WebpageItemRow.java extra : rebase_source : 4db18bd50d8a56201531fd464ed0cae5cebf164d
This commit is contained in:
Родитель
c644d4e4dc
Коммит
29f5602d05
|
@ -0,0 +1,9 @@
|
|||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="12.0"
|
||||
android:viewportHeight="12.0">
|
||||
<path
|
||||
android:fillColor="@color/activity_stream_icon"
|
||||
android:pathData="M4.97.151l-2.819,6.5A.25.25,0,0,0,2.381,7H4.029a.25.25,0,0,1,.225.359L2,12,9.4,5.437A.25.25,0,0,0,9.234,5H6.791a.25.25,0,0,1-.19-.412L10.15.412A.25.25,0,0,0,9.959,0H5.2A.25.25,0,0,0,4.97.151Z"/>
|
||||
</vector>
|
|
@ -10,7 +10,6 @@
|
|||
android:layout_marginBottom="@dimen/activity_stream_base_margin"
|
||||
android:layout_marginRight="@dimen/activity_stream_base_margin"
|
||||
android:layout_marginEnd="@dimen/activity_stream_base_margin"
|
||||
android:text="@string/activity_stream_highlights"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:textStyle="bold"
|
||||
|
|
|
@ -34,6 +34,7 @@ public class ActivityStreamTelemetry {
|
|||
// Values
|
||||
public final static String TYPE_TOPSITES = "topsites";
|
||||
public final static String TYPE_HIGHLIGHTS = "highlights";
|
||||
public final static String TYPE_POCKET = "pocket";
|
||||
public final static String SUBTYPE_PINNED = "pinned";
|
||||
public final static String SUBTYPE_SUGGESTED = "suggested";
|
||||
public final static String SUBTYPE_TOP = "top";
|
||||
|
@ -143,6 +144,9 @@ public class ActivityStreamTelemetry {
|
|||
case BOOKMARKED:
|
||||
this.set(Contract.SOURCE_SUBTYPE, Contract.SUBTYPE_BOOKMARKED);
|
||||
break;
|
||||
case POCKET:
|
||||
this.set(Contract.SOURCE_TYPE, Contract.TYPE_POCKET);
|
||||
break;
|
||||
default:
|
||||
throw new IllegalStateException("Unknown highlight source: " + source);
|
||||
}
|
||||
|
|
|
@ -14,7 +14,8 @@ import org.mozilla.gecko.activitystream.ranking.HighlightCandidateCursorIndices;
|
|||
public class Utils {
|
||||
public enum HighlightSource {
|
||||
VISITED,
|
||||
BOOKMARKED
|
||||
BOOKMARKED,
|
||||
POCKET
|
||||
}
|
||||
|
||||
public static HighlightSource highlightSource(final Cursor cursor, final HighlightCandidateCursorIndices cursorIndices) {
|
||||
|
|
|
@ -17,7 +17,7 @@ import android.view.View;
|
|||
* ItemDecoration implementation that draws horizontal divider line between highlight items.
|
||||
*/
|
||||
/* package */ class HighlightsDividerItemDecoration extends RecyclerView.ItemDecoration {
|
||||
// We do not want to draw a divider for the first items: Top sites panel and highlights title.
|
||||
// We do not want to draw a divider for the Top Sites panel and the Welcome panel.
|
||||
private static final int START_DRAWING_AT_POSITION = 2;
|
||||
|
||||
private static final int[] ATTRS = new int[]{
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
package org.mozilla.gecko.activitystream.homepanel;
|
||||
|
||||
import android.support.annotation.NonNull;
|
||||
import org.mozilla.gecko.activitystream.homepanel.stream.HighlightItemRow;
|
||||
import org.mozilla.gecko.activitystream.homepanel.stream.WebpageItemRow;
|
||||
|
||||
/**
|
||||
* Provides a method to open the context menu for a highlight item.
|
||||
|
@ -14,5 +14,5 @@ import org.mozilla.gecko.activitystream.homepanel.stream.HighlightItemRow;
|
|||
* (I don't understand why) so it's here instead.
|
||||
*/
|
||||
public interface StreamHighlightItemRowContextMenuListener {
|
||||
void openContextMenu(HighlightItemRow highlightItem, int position, @NonNull final String interactionExtra);
|
||||
void openContextMenu(WebpageItemRow highlightItem, int position, @NonNull final String interactionExtra);
|
||||
}
|
||||
|
|
|
@ -13,21 +13,25 @@ import android.view.LayoutInflater;
|
|||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
|
||||
import org.mozilla.gecko.R;
|
||||
import org.mozilla.gecko.Telemetry;
|
||||
import org.mozilla.gecko.TelemetryContract;
|
||||
import org.mozilla.gecko.activitystream.ActivityStreamTelemetry;
|
||||
import org.mozilla.gecko.activitystream.homepanel.menu.ActivityStreamContextMenu;
|
||||
import org.mozilla.gecko.activitystream.homepanel.model.RowModel;
|
||||
import org.mozilla.gecko.activitystream.homepanel.model.WebpageRowModel;
|
||||
import org.mozilla.gecko.activitystream.homepanel.stream.TopPanelRow;
|
||||
import org.mozilla.gecko.activitystream.homepanel.model.TopStory;
|
||||
import org.mozilla.gecko.home.HomePager;
|
||||
import org.mozilla.gecko.activitystream.homepanel.model.Highlight;
|
||||
import org.mozilla.gecko.activitystream.homepanel.stream.HighlightItemRow;
|
||||
import org.mozilla.gecko.activitystream.homepanel.stream.HighlightsTitleRow;
|
||||
import org.mozilla.gecko.activitystream.homepanel.stream.WebpageItemRow;
|
||||
import org.mozilla.gecko.activitystream.homepanel.stream.StreamTitleRow;
|
||||
import org.mozilla.gecko.activitystream.homepanel.stream.StreamViewHolder;
|
||||
import org.mozilla.gecko.activitystream.homepanel.stream.WelcomePanelRow;
|
||||
import org.mozilla.gecko.util.StringUtils;
|
||||
import org.mozilla.gecko.widget.RecyclerViewClickSupport;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.EnumSet;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
|
@ -44,9 +48,10 @@ public class StreamRecyclerAdapter extends RecyclerView.Adapter<StreamViewHolder
|
|||
|
||||
private Cursor topSitesCursor;
|
||||
private List<RowModel> recyclerViewModel; // List of item types backing this RecyclerView.
|
||||
private List<TopStory> topStoriesQueue;
|
||||
|
||||
private final RowItemType[] FIXED_ROWS = {RowItemType.TOP_PANEL, RowItemType.WELCOME, RowItemType.HIGHLIGHTS_TITLE};
|
||||
private static final int HIGHLIGHTS_OFFSET = 3; // Topsites, Welcome, Highlights Title
|
||||
private final RowItemType[] FIXED_ROWS = {RowItemType.TOP_PANEL, RowItemType.WELCOME, RowItemType.TOP_STORIES_TITLE, RowItemType.HIGHLIGHTS_TITLE};
|
||||
private final int MAX_TOP_STORIES = 3;
|
||||
|
||||
private HomePager.OnUrlOpenListener onUrlOpenListener;
|
||||
private HomePager.OnUrlOpenInBackgroundListener onUrlOpenInBackgroundListener;
|
||||
|
@ -57,7 +62,9 @@ public class StreamRecyclerAdapter extends RecyclerView.Adapter<StreamViewHolder
|
|||
public enum RowItemType {
|
||||
TOP_PANEL (-2), // RecyclerView.NO_ID is -1, so start hard-coded stableIds at -2.
|
||||
WELCOME (-3),
|
||||
HIGHLIGHTS_TITLE (-4),
|
||||
TOP_STORIES_TITLE(-4),
|
||||
TOP_STORIES_ITEM(-1), // There can be multiple Top Stories items so caller should handle as a special case.
|
||||
HIGHLIGHTS_TITLE (-5),
|
||||
HIGHLIGHT_ITEM (-1); // There can be multiple Highlight Items so caller should handle as a special case.
|
||||
|
||||
public final int stableId;
|
||||
|
@ -86,6 +93,8 @@ public class StreamRecyclerAdapter extends RecyclerView.Adapter<StreamViewHolder
|
|||
for (RowItemType type : FIXED_ROWS) {
|
||||
recyclerViewModel.add(makeRowModelFromType(type));
|
||||
}
|
||||
topStoriesQueue = Collections.emptyList();
|
||||
loadTopStories();
|
||||
}
|
||||
|
||||
void setOnUrlOpenListeners(HomePager.OnUrlOpenListener onUrlOpenListener, HomePager.OnUrlOpenInBackgroundListener onUrlOpenInBackgroundListener) {
|
||||
|
@ -114,19 +123,43 @@ public class StreamRecyclerAdapter extends RecyclerView.Adapter<StreamViewHolder
|
|||
|
||||
if (type == RowItemType.TOP_PANEL.getViewType()) {
|
||||
return new TopPanelRow(inflater.inflate(TopPanelRow.LAYOUT_ID, parent, false), onUrlOpenListener, onUrlOpenInBackgroundListener);
|
||||
} else if (type == RowItemType.TOP_STORIES_TITLE.getViewType()) {
|
||||
return new StreamTitleRow(inflater.inflate(StreamTitleRow.LAYOUT_ID, parent, false), R.string.activity_stream_topstories);
|
||||
} else if (type == RowItemType.TOP_STORIES_ITEM.getViewType()) {
|
||||
return new WebpageItemRow(inflater.inflate(WebpageItemRow.LAYOUT_ID, parent, false), this);
|
||||
} else if (type == RowItemType.WELCOME.getViewType()) {
|
||||
return new WelcomePanelRow(inflater.inflate(WelcomePanelRow.LAYOUT_ID, parent, false), this);
|
||||
} else if (type == RowItemType.HIGHLIGHT_ITEM.getViewType()) {
|
||||
return new HighlightItemRow(inflater.inflate(HighlightItemRow.LAYOUT_ID, parent, false), this);
|
||||
return new WebpageItemRow(inflater.inflate(WebpageItemRow.LAYOUT_ID, parent, false), this);
|
||||
} else if (type == RowItemType.HIGHLIGHTS_TITLE.getViewType()) {
|
||||
return new HighlightsTitleRow(inflater.inflate(HighlightsTitleRow.LAYOUT_ID, parent, false));
|
||||
return new StreamTitleRow(inflater.inflate(StreamTitleRow.LAYOUT_ID, parent, false), R.string.activity_stream_highlights);
|
||||
} else {
|
||||
throw new IllegalStateException("Missing inflation for ViewType " + type);
|
||||
}
|
||||
}
|
||||
|
||||
private int getHighlightsOffsetFromRVPosition(int position) {
|
||||
return position - HIGHLIGHTS_OFFSET;
|
||||
/**
|
||||
* Returns the index of an item within highlights.
|
||||
* @param position position in adapter
|
||||
* @return index of item within highlights
|
||||
*/
|
||||
private int getHighlightsIndexFromAdapterPosition(int position) {
|
||||
if (getItemViewType(position) != RowItemType.HIGHLIGHT_ITEM.getViewType()) {
|
||||
throw new IllegalArgumentException("Item is not a highlight!");
|
||||
}
|
||||
return position - indexOfType(RowItemType.HIGHLIGHT_ITEM, recyclerViewModel);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the index of an item within top stories.
|
||||
* @param position position in adapter
|
||||
* @return index of item within top stories
|
||||
*/
|
||||
private int getTopStoriesIndexFromAdapterPosition(int position) {
|
||||
if (getItemViewType(position) != RowItemType.TOP_STORIES_ITEM.getViewType()) {
|
||||
throw new IllegalArgumentException("Item is not a topstory!");
|
||||
}
|
||||
return position - indexOfType(RowItemType.TOP_STORIES_ITEM, recyclerViewModel);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -134,25 +167,43 @@ public class StreamRecyclerAdapter extends RecyclerView.Adapter<StreamViewHolder
|
|||
int type = getItemViewType(position);
|
||||
if (type == RowItemType.HIGHLIGHT_ITEM.getViewType()) {
|
||||
final Highlight highlight = (Highlight) recyclerViewModel.get(position);
|
||||
((HighlightItemRow) holder).bind(highlight, position, tilesSize);
|
||||
((WebpageItemRow) holder).bind(highlight, position, tilesSize);
|
||||
} else if (type == RowItemType.TOP_PANEL.getViewType()) {
|
||||
((TopPanelRow) holder).bind(topSitesCursor, tiles, tilesSize);
|
||||
} else if (type == RowItemType.TOP_STORIES_ITEM.getViewType()) {
|
||||
final TopStory story = (TopStory) recyclerViewModel.get(position);
|
||||
((WebpageItemRow) holder).bind(story, position, tilesSize);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onItemClicked(RecyclerView recyclerView, int position, View v) {
|
||||
if (!onItemClickIsValidHighlightItem(position)) {
|
||||
if (!onItemClickIsValidRowItem(position)) {
|
||||
return;
|
||||
}
|
||||
|
||||
final Highlight highlight = (Highlight) recyclerViewModel.get(position);
|
||||
final WebpageRowModel model = (WebpageRowModel) recyclerViewModel.get(position);
|
||||
|
||||
final String sourceType;
|
||||
final int actionPosition;
|
||||
final int size;
|
||||
final int viewType = getItemViewType(position);
|
||||
|
||||
if (viewType == RowItemType.HIGHLIGHT_ITEM.getViewType()) {
|
||||
sourceType = ActivityStreamTelemetry.Contract.TYPE_HIGHLIGHTS;
|
||||
actionPosition = getHighlightsIndexFromAdapterPosition(position);
|
||||
size = getNumOfTypeShown(RowItemType.HIGHLIGHT_ITEM);
|
||||
} else {
|
||||
sourceType = ActivityStreamTelemetry.Contract.TYPE_POCKET;
|
||||
actionPosition = getTopStoriesIndexFromAdapterPosition(position);
|
||||
size = getNumOfTypeShown(RowItemType.TOP_STORIES_ITEM);
|
||||
}
|
||||
|
||||
ActivityStreamTelemetry.Extras.Builder extras = ActivityStreamTelemetry.Extras.builder()
|
||||
.forHighlightSource(highlight.getSource())
|
||||
.set(ActivityStreamTelemetry.Contract.SOURCE_TYPE, ActivityStreamTelemetry.Contract.TYPE_HIGHLIGHTS)
|
||||
.set(ActivityStreamTelemetry.Contract.ACTION_POSITION, getHighlightsOffsetFromRVPosition(position))
|
||||
.set(ActivityStreamTelemetry.Contract.COUNT, recyclerViewModel.size() - FIXED_ROWS.length);
|
||||
.forHighlightSource(model.getSource())
|
||||
.set(ActivityStreamTelemetry.Contract.SOURCE_TYPE, sourceType)
|
||||
.set(ActivityStreamTelemetry.Contract.ACTION_POSITION, actionPosition)
|
||||
.set(ActivityStreamTelemetry.Contract.COUNT, size);
|
||||
|
||||
Telemetry.sendUIEvent(
|
||||
TelemetryContract.Event.LOAD_URL,
|
||||
|
@ -163,22 +214,24 @@ public class StreamRecyclerAdapter extends RecyclerView.Adapter<StreamViewHolder
|
|||
// NB: This is hacky. We need to process telemetry data first, otherwise we run a risk of
|
||||
// not having a cursor to work with once url is opened and BrowserApp closes A-S home screen
|
||||
// and clears its resources (read: cursors). See Bug 1326018.
|
||||
onUrlOpenListener.onUrlOpen(highlight.getUrl(), EnumSet.of(HomePager.OnUrlOpenListener.Flags.ALLOW_SWITCH_TO_TAB));
|
||||
onUrlOpenListener.onUrlOpen(model.getUrl(), EnumSet.of(HomePager.OnUrlOpenListener.Flags.ALLOW_SWITCH_TO_TAB));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onItemLongClicked(final RecyclerView recyclerView, final int position, final View v) {
|
||||
if (!onItemClickIsValidHighlightItem(position)) {
|
||||
if (!onItemClickIsValidRowItem(position)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
final HighlightItemRow highlightItem = (HighlightItemRow) recyclerView.getChildViewHolder(v);
|
||||
final WebpageItemRow highlightItem = (WebpageItemRow) recyclerView.getChildViewHolder(v);
|
||||
openContextMenu(highlightItem, position, ActivityStreamTelemetry.Contract.INTERACTION_LONG_CLICK);
|
||||
return true;
|
||||
}
|
||||
|
||||
private boolean onItemClickIsValidHighlightItem(final int position) {
|
||||
if (getItemViewType(position) != RowItemType.HIGHLIGHT_ITEM.getViewType()) {
|
||||
private boolean onItemClickIsValidRowItem(final int position) {
|
||||
final int viewType = getItemViewType(position);
|
||||
if (viewType != RowItemType.HIGHLIGHT_ITEM.getViewType()
|
||||
&& viewType != RowItemType.TOP_STORIES_ITEM.getViewType()) {
|
||||
// Headers (containing topsites and/or the highlights title) do their own click handling as needed
|
||||
return false;
|
||||
}
|
||||
|
@ -202,23 +255,37 @@ public class StreamRecyclerAdapter extends RecyclerView.Adapter<StreamViewHolder
|
|||
}
|
||||
|
||||
@Override
|
||||
public void openContextMenu(final HighlightItemRow highlightItem, final int position, @NonNull final String interactionExtra) {
|
||||
final Highlight highlight = (Highlight) recyclerViewModel.get(position);
|
||||
public void openContextMenu(final WebpageItemRow webpageItemRow, final int position, @NonNull final String interactionExtra) {
|
||||
final WebpageRowModel model = (WebpageRowModel) recyclerViewModel.get(position);
|
||||
|
||||
final String sourceType;
|
||||
final int actionPosition;
|
||||
final ActivityStreamContextMenu.MenuMode menuMode;
|
||||
|
||||
if (model.getRowItemType() == RowItemType.HIGHLIGHT_ITEM) {
|
||||
sourceType = ActivityStreamTelemetry.Contract.TYPE_HIGHLIGHTS;
|
||||
actionPosition = getHighlightsIndexFromAdapterPosition(position);
|
||||
menuMode = ActivityStreamContextMenu.MenuMode.HIGHLIGHT;
|
||||
} else {
|
||||
sourceType = ActivityStreamTelemetry.Contract.TYPE_POCKET;
|
||||
actionPosition = getTopStoriesIndexFromAdapterPosition(position);
|
||||
menuMode = ActivityStreamContextMenu.MenuMode.TOPSTORY;
|
||||
}
|
||||
|
||||
ActivityStreamTelemetry.Extras.Builder extras = ActivityStreamTelemetry.Extras.builder()
|
||||
.set(ActivityStreamTelemetry.Contract.SOURCE_TYPE, ActivityStreamTelemetry.Contract.TYPE_HIGHLIGHTS)
|
||||
.set(ActivityStreamTelemetry.Contract.ACTION_POSITION, position - HIGHLIGHTS_OFFSET)
|
||||
.set(ActivityStreamTelemetry.Contract.SOURCE_TYPE, sourceType)
|
||||
.set(ActivityStreamTelemetry.Contract.ACTION_POSITION, actionPosition)
|
||||
.set(ActivityStreamTelemetry.Contract.INTERACTION, interactionExtra)
|
||||
.forHighlightSource(highlight.getSource());
|
||||
.forHighlightSource(model.getSource());
|
||||
|
||||
ActivityStreamContextMenu.show(highlightItem.itemView.getContext(),
|
||||
highlightItem.getContextMenuAnchor(),
|
||||
ActivityStreamContextMenu.show(webpageItemRow.itemView.getContext(),
|
||||
webpageItemRow.getContextMenuAnchor(),
|
||||
extras,
|
||||
ActivityStreamContextMenu.MenuMode.HIGHLIGHT,
|
||||
highlight,
|
||||
menuMode,
|
||||
model,
|
||||
/* shouldOverrideWithImageProvider */ true, // we use image providers in HighlightItem.pageIconLayout.
|
||||
onUrlOpenListener, onUrlOpenInBackgroundListener,
|
||||
highlightItem.getTileWidth(), highlightItem.getTileHeight());
|
||||
webpageItemRow.getTileWidth(), webpageItemRow.getTileHeight());
|
||||
|
||||
Telemetry.sendUIEvent(
|
||||
TelemetryContract.Event.SHOW,
|
||||
|
@ -233,11 +300,70 @@ public class StreamRecyclerAdapter extends RecyclerView.Adapter<StreamViewHolder
|
|||
}
|
||||
|
||||
public void swapHighlights(List<Highlight> highlights) {
|
||||
recyclerViewModel = recyclerViewModel.subList(0, HIGHLIGHTS_OFFSET);
|
||||
recyclerViewModel = recyclerViewModel.subList(0, FIXED_ROWS.length + getNumOfTypeShown(RowItemType.TOP_STORIES_ITEM));
|
||||
recyclerViewModel.addAll(highlights);
|
||||
notifyDataSetChanged();
|
||||
}
|
||||
|
||||
private void loadTopStories() {
|
||||
List<TopStory> newStories = makePlaceholderStories();
|
||||
topStoriesQueue = newStories;
|
||||
|
||||
final int insertionIndex = indexOfType(RowItemType.TOP_STORIES_TITLE, recyclerViewModel) + 1;
|
||||
for (int i = 0; i < Math.min(MAX_TOP_STORIES, newStories.size()); i++) {
|
||||
recyclerViewModel.add(insertionIndex + i, newStories.get(i));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the index of the first item of the type found.
|
||||
* @param type viewType of RowItemType
|
||||
* @param rowModelList List to be indexed into
|
||||
* @return index of first item of the type, or -1 if it none exist.
|
||||
*/
|
||||
private static int indexOfType(RowItemType type, List<RowModel> rowModelList) {
|
||||
for (int i = 0; i < rowModelList.size(); i++) {
|
||||
if (rowModelList.get(i).getRowItemType() == type) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the number of consecutive items in the adapter of the item type specified.
|
||||
*
|
||||
* This is intended to be used for counting the items that have a dynamic count
|
||||
* (such as Highlights or TopStory)
|
||||
*
|
||||
* @param type RowItemType to be counted
|
||||
* @return The number of items shown.
|
||||
*/
|
||||
private int getNumOfTypeShown(RowItemType type) {
|
||||
final int startIndex = indexOfType(type, recyclerViewModel);
|
||||
if (startIndex == -1) {
|
||||
return 0;
|
||||
}
|
||||
int count = 0;
|
||||
for (int i = startIndex; i < recyclerViewModel.size(); i++) {
|
||||
if (getItemViewType(i) == type.getViewType()) {
|
||||
count++;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
private List<TopStory> makePlaceholderStories() {
|
||||
final List<TopStory> stories = new LinkedList<>();
|
||||
final String[] TITLES = { "Placeholder 1", "Placeholder 2", "Placeholder 3"};
|
||||
for (String title : TITLES) {
|
||||
stories.add(new TopStory(title, "https://www.mozilla.org/"));
|
||||
}
|
||||
return stories;
|
||||
}
|
||||
|
||||
public void swapTopSitesCursor(Cursor cursor) {
|
||||
this.topSitesCursor = cursor;
|
||||
notifyItemChanged(0);
|
||||
|
@ -246,11 +372,12 @@ public class StreamRecyclerAdapter extends RecyclerView.Adapter<StreamViewHolder
|
|||
@Override
|
||||
public long getItemId(int position) {
|
||||
final int viewType = getItemViewType(position);
|
||||
if (viewType == RowItemType.HIGHLIGHT_ITEM.getViewType()) {
|
||||
if (viewType == RowItemType.HIGHLIGHT_ITEM.getViewType()
|
||||
|| viewType == RowItemType.TOP_STORIES_ITEM.getViewType()) {
|
||||
// Highlights are always picked from recent history - So using the history id should
|
||||
// give us a unique (positive) id.
|
||||
final Highlight highlight = (Highlight) recyclerViewModel.get(position);
|
||||
return highlight.getHistoryId();
|
||||
final WebpageRowModel model = (WebpageRowModel) recyclerViewModel.get(position);
|
||||
return model.getUniqueId();
|
||||
} else {
|
||||
return recyclerViewModel.get(position).getRowItemType().stableId;
|
||||
}
|
||||
|
|
|
@ -35,7 +35,8 @@ public abstract class ActivityStreamContextMenu
|
|||
|
||||
public enum MenuMode {
|
||||
HIGHLIGHT,
|
||||
TOPSITE
|
||||
TOPSITE,
|
||||
TOPSTORY
|
||||
}
|
||||
|
||||
private final Context context;
|
||||
|
|
|
@ -18,7 +18,7 @@ import org.mozilla.gecko.activitystream.ranking.HighlightsRanking;
|
|||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
public class Highlight implements WebpageModel, RowModel {
|
||||
public class Highlight implements WebpageRowModel {
|
||||
|
||||
/**
|
||||
* A pattern matching a json object containing the key "image_url" and extracting the value. afaik, these urls
|
||||
|
@ -214,11 +214,13 @@ public class Highlight implements WebpageModel, RowModel {
|
|||
this.isPinned = pinned;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Utils.HighlightSource getSource() {
|
||||
return source;
|
||||
}
|
||||
|
||||
public long getHistoryId() {
|
||||
@Override
|
||||
public long getUniqueId() {
|
||||
return historyId;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,68 @@
|
|||
/* -*- 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.activitystream.homepanel.model;
|
||||
|
||||
import org.mozilla.gecko.activitystream.Utils;
|
||||
import org.mozilla.gecko.activitystream.homepanel.StreamRecyclerAdapter;
|
||||
|
||||
public class TopStory implements WebpageRowModel {
|
||||
private final String title;
|
||||
private final String url;
|
||||
private final String imageUrl;
|
||||
|
||||
public TopStory(String title, String url) {
|
||||
this(title, url, null);
|
||||
}
|
||||
|
||||
public TopStory(String title, String url, String imageUrl) {
|
||||
this.title = title;
|
||||
this.url = url;
|
||||
this.imageUrl = imageUrl;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getTitle() {
|
||||
return title;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getUrl() {
|
||||
return url;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getImageUrl() {
|
||||
return imageUrl;
|
||||
}
|
||||
|
||||
@Override
|
||||
public StreamRecyclerAdapter.RowItemType getRowItemType() {
|
||||
return StreamRecyclerAdapter.RowItemType.TOP_STORIES_ITEM;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Boolean isBookmarked() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Boolean isPinned() {
|
||||
return false;
|
||||
}
|
||||
|
||||
public void updateBookmarked(boolean bookmarked) {}
|
||||
public void updatePinned(boolean pinned) {}
|
||||
|
||||
@Override
|
||||
public Utils.HighlightSource getSource() {
|
||||
return Utils.HighlightSource.POCKET;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getUniqueId() {
|
||||
return getUrl().hashCode();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,16 @@
|
|||
/* -*- 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.activitystream.homepanel.model;
|
||||
|
||||
import org.mozilla.gecko.activitystream.Utils;
|
||||
|
||||
/**
|
||||
* Model for a row in Activity Stream that represents a webpage item.
|
||||
*/
|
||||
public interface WebpageRowModel extends WebpageModel, RowModel {
|
||||
Utils.HighlightSource getSource();
|
||||
long getUniqueId();
|
||||
}
|
|
@ -5,15 +5,20 @@
|
|||
|
||||
package org.mozilla.gecko.activitystream.homepanel.stream;
|
||||
|
||||
import android.support.annotation.NonNull;
|
||||
import android.support.annotation.StringRes;
|
||||
import android.view.View;
|
||||
import android.widget.TextView;
|
||||
|
||||
import org.mozilla.gecko.R;
|
||||
|
||||
public class HighlightsTitleRow extends StreamViewHolder {
|
||||
public class StreamTitleRow extends StreamViewHolder {
|
||||
public static final int LAYOUT_ID = R.layout.activity_stream_main_highlightstitle;
|
||||
|
||||
public HighlightsTitleRow(final View itemView) {
|
||||
public StreamTitleRow(final View itemView, final @StringRes @NonNull int titleResId) {
|
||||
super(itemView);
|
||||
final TextView titleView = (TextView) itemView.findViewById(R.id.title_highlights);
|
||||
titleView.setText(titleResId);
|
||||
}
|
||||
}
|
||||
|
|
@ -17,7 +17,7 @@ import org.mozilla.gecko.R;
|
|||
import org.mozilla.gecko.activitystream.ActivityStreamTelemetry;
|
||||
import org.mozilla.gecko.activitystream.Utils;
|
||||
import org.mozilla.gecko.activitystream.homepanel.StreamHighlightItemRowContextMenuListener;
|
||||
import org.mozilla.gecko.activitystream.homepanel.model.Highlight;
|
||||
import org.mozilla.gecko.activitystream.homepanel.model.WebpageRowModel;
|
||||
import org.mozilla.gecko.util.DrawableUtil;
|
||||
import org.mozilla.gecko.util.TouchTargetUtil;
|
||||
import org.mozilla.gecko.util.URIUtils;
|
||||
|
@ -28,22 +28,23 @@ import java.net.URI;
|
|||
import java.net.URISyntaxException;
|
||||
import java.util.UUID;
|
||||
|
||||
public class HighlightItemRow extends StreamViewHolder {
|
||||
private static final String LOGTAG = "GeckoHighlightItem";
|
||||
public class WebpageItemRow extends StreamViewHolder {
|
||||
private static final String LOGTAG = "GeckoWebpageItemRow";
|
||||
|
||||
public static final int LAYOUT_ID = R.layout.activity_stream_card_history_item;
|
||||
private static final double SIZE_RATIO = 0.75;
|
||||
|
||||
private WebpageRowModel webpageModel;
|
||||
private int position;
|
||||
|
||||
private final StreamOverridablePageIconLayout pageIconLayout;
|
||||
private final TextView pageTitleView;
|
||||
private final TextView pageSourceView;
|
||||
private final TextView pageDomainView;
|
||||
private final TextView pageTitleView;
|
||||
private final ImageView pageSourceIconView;
|
||||
private final TextView pageSourceView;
|
||||
private final ImageView menuButton;
|
||||
|
||||
public HighlightItemRow(final View itemView, final StreamHighlightItemRowContextMenuListener contextMenuListener) {
|
||||
public WebpageItemRow(final View itemView, final StreamHighlightItemRowContextMenuListener contextMenuListener) {
|
||||
super(itemView);
|
||||
|
||||
pageTitleView = (TextView) itemView.findViewById(R.id.card_history_label);
|
||||
|
@ -60,17 +61,18 @@ public class HighlightItemRow extends StreamViewHolder {
|
|||
menuButton.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
contextMenuListener.openContextMenu(HighlightItemRow.this, position,
|
||||
contextMenuListener.openContextMenu(WebpageItemRow.this, position,
|
||||
ActivityStreamTelemetry.Contract.INTERACTION_MENU_BUTTON);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public void bind(Highlight highlight, int position, int tilesWidth) {
|
||||
public void bind(WebpageRowModel model, int position, int tilesWidth) {
|
||||
this.webpageModel = model;
|
||||
this.position = position;
|
||||
|
||||
final String backendHightlightTitle = highlight.getTitle();
|
||||
final String uiHighlightTitle = !TextUtils.isEmpty(backendHightlightTitle) ? backendHightlightTitle : highlight.getUrl();
|
||||
final String backendHighlightTitle = model.getTitle();
|
||||
final String uiHighlightTitle = !TextUtils.isEmpty(backendHighlightTitle) ? backendHighlightTitle : model.getUrl();
|
||||
pageTitleView.setText(uiHighlightTitle);
|
||||
|
||||
ViewGroup.LayoutParams layoutParams = pageIconLayout.getLayoutParams();
|
||||
|
@ -78,12 +80,12 @@ public class HighlightItemRow extends StreamViewHolder {
|
|||
layoutParams.height = (int) Math.floor(tilesWidth * SIZE_RATIO);
|
||||
pageIconLayout.setLayoutParams(layoutParams);
|
||||
|
||||
updateUiForSource(highlight.getSource());
|
||||
updatePageDomain(highlight);
|
||||
pageIconLayout.updateIcon(highlight.getUrl(), highlight.getImageUrl());
|
||||
updateUiForSource(model.getSource());
|
||||
updatePageDomain();
|
||||
pageIconLayout.updateIcon(model.getUrl(), model.getImageUrl());
|
||||
}
|
||||
|
||||
private void updateUiForSource(Utils.HighlightSource source) {
|
||||
public void updateUiForSource(Utils.HighlightSource source) {
|
||||
switch (source) {
|
||||
case BOOKMARKED:
|
||||
pageSourceView.setText(R.string.activity_stream_highlight_label_bookmarked);
|
||||
|
@ -95,6 +97,11 @@ public class HighlightItemRow extends StreamViewHolder {
|
|||
pageSourceView.setVisibility(View.VISIBLE);
|
||||
pageSourceIconView.setImageResource(R.drawable.ic_as_visited);
|
||||
break;
|
||||
case POCKET:
|
||||
pageSourceView.setText(R.string.activity_stream_highlight_label_trending);
|
||||
pageSourceView.setVisibility(View.VISIBLE);
|
||||
pageSourceIconView.setImageResource(R.drawable.ic_as_trending);
|
||||
break;
|
||||
default:
|
||||
pageSourceView.setVisibility(View.INVISIBLE);
|
||||
pageSourceIconView.setImageResource(0);
|
||||
|
@ -102,13 +109,13 @@ public class HighlightItemRow extends StreamViewHolder {
|
|||
}
|
||||
}
|
||||
|
||||
private void updatePageDomain(final Highlight highlight) {
|
||||
private void updatePageDomain() {
|
||||
final URI highlightURI;
|
||||
try {
|
||||
highlightURI = new URI(highlight.getUrl());
|
||||
highlightURI = new URI(webpageModel.getUrl());
|
||||
} catch (final URISyntaxException e) {
|
||||
// If the URL is invalid, there's not much extra processing we can do on it.
|
||||
pageDomainView.setText(highlight.getUrl());
|
||||
pageDomainView.setText(webpageModel.getUrl());
|
||||
return;
|
||||
}
|
||||
|
|
@ -834,6 +834,8 @@ just addresses the organization to follow, e.g. "This site is run by " -->
|
|||
<!ENTITY helper_triple_readerview_open_button "Add to Bookmarks">
|
||||
|
||||
<!ENTITY activity_stream_topsites "Top Sites">
|
||||
<!-- LOCALIZATION NOTE (activity_stream_topstories): &brandPocket is the brand of the company, Pocket, that is being used to provide suggestions for articles. -->
|
||||
<!ENTITY activity_stream_topstories "Recommended by &brandPocket;">
|
||||
<!ENTITY activity_stream_highlights "Highlights">
|
||||
|
||||
<!-- LOCALIZATION NOTE (activity_stream_highlight_label_bookmarked): This label is shown in the Activity
|
||||
|
@ -842,6 +844,8 @@ Stream list for highlights sourced from th user's bookmarks. -->
|
|||
<!-- LOCALIZATION NOTE (activity_stream_highlight_label_visited): This label is shown in the Activity
|
||||
Stream list for highlights sourced from th user's bookmarks. -->
|
||||
<!ENTITY activity_stream_highlight_label_visited "Visited">
|
||||
<!-- LOCALIZATION NOTE (activity_stream_highlight_label_trending): This label is shown in the Activity Stream list for highlights sourced from a recommendations engine. -->
|
||||
<!ENTITY activity_stream_highlight_label_trending "Trending">
|
||||
|
||||
<!-- LOCALIZATION NOTE (activity_stream_remove): This label is shown in the Activity Stream context menu,
|
||||
and allows hiding a URL/page from highlights or topsites. The page remains in history/bookmarks, but
|
||||
|
|
|
@ -516,12 +516,14 @@ gbjar.sources += ['java/org/mozilla/gecko/' + x for x in [
|
|||
'activitystream/homepanel/model/Metadata.java',
|
||||
'activitystream/homepanel/model/RowModel.java',
|
||||
'activitystream/homepanel/model/TopSite.java',
|
||||
'activitystream/homepanel/model/TopStory.java',
|
||||
'activitystream/homepanel/model/WebpageModel.java',
|
||||
'activitystream/homepanel/stream/HighlightItemRow.java',
|
||||
'activitystream/homepanel/stream/HighlightsTitleRow.java',
|
||||
'activitystream/homepanel/model/WebpageRowModel.java',
|
||||
'activitystream/homepanel/stream/StreamOverridablePageIconLayout.java',
|
||||
'activitystream/homepanel/stream/StreamTitleRow.java',
|
||||
'activitystream/homepanel/stream/StreamViewHolder.java',
|
||||
'activitystream/homepanel/stream/TopPanelRow.java',
|
||||
'activitystream/homepanel/stream/WebpageItemRow.java',
|
||||
'activitystream/homepanel/stream/WelcomePanelRow.java',
|
||||
'activitystream/homepanel/StreamHighlightItemRowContextMenuListener.java',
|
||||
'activitystream/homepanel/StreamItemAnimator.java',
|
||||
|
|
|
@ -622,9 +622,11 @@
|
|||
<string name="helper_triple_readerview_open_button">&helper_triple_readerview_open_button;</string>
|
||||
|
||||
<string name="activity_stream_topsites">&activity_stream_topsites;</string>
|
||||
<string name="activity_stream_topstories">&activity_stream_topstories;</string>
|
||||
<string name="activity_stream_highlights">&activity_stream_highlights;</string>
|
||||
<string name="activity_stream_highlight_label_bookmarked">&activity_stream_highlight_label_bookmarked;</string>
|
||||
<string name="activity_stream_highlight_label_visited">&activity_stream_highlight_label_visited;</string>
|
||||
<string name="activity_stream_highlight_label_trending">&activity_stream_highlight_label_trending;</string>
|
||||
<string name="activity_stream_remove">&activity_stream_remove;</string>
|
||||
<string name="activity_stream_delete_history">&activity_stream_delete_history;</string>
|
||||
|
||||
|
|
|
@ -4,4 +4,6 @@
|
|||
|
||||
<!ENTITY brandShortName "Firefox Beta">
|
||||
<!ENTITY brandFullName "Mozilla Firefox Beta">
|
||||
<!ENTITY vendorShortName "Mozilla">
|
||||
<!ENTITY vendorShortName "Mozilla">
|
||||
|
||||
<!ENTITY brandPocket "Pocket">
|
||||
|
|
|
@ -4,4 +4,6 @@
|
|||
|
||||
<!ENTITY brandShortName "Nightly">
|
||||
<!ENTITY brandFullName "Mozilla Nightly">
|
||||
<!ENTITY vendorShortName "Mozilla">
|
||||
<!ENTITY vendorShortName "Mozilla">
|
||||
|
||||
<!ENTITY brandPocket "Pocket">
|
||||
|
|
|
@ -4,4 +4,6 @@
|
|||
|
||||
<!ENTITY brandShortName "Nightly">
|
||||
<!ENTITY brandFullName "Mozilla Nightly">
|
||||
<!ENTITY vendorShortName "Mozilla">
|
||||
<!ENTITY vendorShortName "Mozilla">
|
||||
|
||||
<!ENTITY brandPocket "Pocket">
|
||||
|
|
|
@ -4,4 +4,6 @@
|
|||
|
||||
<!ENTITY brandShortName "Firefox">
|
||||
<!ENTITY brandFullName "Mozilla Firefox">
|
||||
<!ENTITY vendorShortName "Mozilla">
|
||||
<!ENTITY vendorShortName "Mozilla">
|
||||
|
||||
<!ENTITY brandPocket "Pocket">
|
||||
|
|
|
@ -4,4 +4,6 @@
|
|||
|
||||
<!ENTITY brandShortName "Fennec">
|
||||
<!ENTITY brandFullName "Mozilla Fennec">
|
||||
<!ENTITY vendorShortName "Mozilla">
|
||||
<!ENTITY vendorShortName "Mozilla">
|
||||
|
||||
<!ENTITY brandPocket "Pocket">
|
||||
|
|
Загрузка…
Ссылка в новой задаче