Bug 1251362 - Part 4 - Add a Recent Tabs folder to the Combined History panel. r=liuche

This folder can be opened and closed to get back to the history view, however it doesn't contain any actual content (closed tabs) yet. Its empty view is based on the original empty view of the Recent Tabs panel.

For displaying the recently closed tabs count within the smart folder similarly to how we display the number of synced devices, two new strings need to be added.

MozReview-Commit-ID: IAL0yDrc2Ld

--HG--
extra : transplant_source : %A1%80jZ%1Eg%14p%7D%DD%DD%EA%E8%7E%CA%0E%CD%28%99%3C
This commit is contained in:
Jan Henning 2016-05-13 23:52:24 +02:00
parent 0df88b27db
commit 2b737b8819
9 changed files with 150 additions and 21 deletions

View File

@ -17,7 +17,8 @@ import org.mozilla.gecko.R;
import org.mozilla.gecko.db.BrowserContract;
public class CombinedHistoryAdapter extends RecyclerView.Adapter<CombinedHistoryItem> implements CombinedHistoryRecyclerView.AdapterContextMenuBuilder {
private static final int SYNCED_DEVICES_SMARTFOLDER_INDEX = 0;
private static final int RECENT_TABS_SMARTFOLDER_INDEX = 0;
private static final int SYNCED_DEVICES_SMARTFOLDER_INDEX = 1;
// Array for the time ranges in milliseconds covered by each section.
static final HistorySectionsHelper.SectionDateRange[] sectionDateRangeArray = new HistorySectionsHelper.SectionDateRange[SectionHeader.values().length];
@ -39,6 +40,7 @@ public class CombinedHistoryAdapter extends RecyclerView.Adapter<CombinedHistory
private Cursor historyCursor;
private DevicesUpdateHandler devicesUpdateHandler;
private int deviceCount = 0;
private int recentTabsCount = 0;
// We use a sparse array to store each section header's position in the panel [more cheaply than a HashMap].
private final SparseArray<SectionHeader> sectionHeaders;
@ -65,7 +67,7 @@ public class CombinedHistoryAdapter extends RecyclerView.Adapter<CombinedHistory
@Override
public void onDeviceCountUpdated(int count) {
deviceCount = count;
notifyItemChanged(0);
notifyItemChanged(SYNCED_DEVICES_SMARTFOLDER_INDEX);
}
};
}
@ -80,6 +82,7 @@ public class CombinedHistoryAdapter extends RecyclerView.Adapter<CombinedHistory
final CombinedHistoryItem.ItemType itemType = CombinedHistoryItem.ItemType.viewTypeToItemType(viewType);
switch (itemType) {
case RECENT_TABS:
case SYNCED_DEVICES:
view = inflater.inflate(R.layout.home_smartfolder, viewGroup, false);
return new CombinedHistoryItem.SmartFolder(view);
@ -102,6 +105,10 @@ public class CombinedHistoryAdapter extends RecyclerView.Adapter<CombinedHistory
final int localPosition = transformAdapterPositionForDataStructure(itemType, position);
switch (itemType) {
case RECENT_TABS:
((CombinedHistoryItem.SmartFolder) viewHolder).bind(R.drawable.icon_recent, R.string.home_closed_tabs_title2, R.string.home_closed_tabs_one, R.string.home_closed_tabs_number, recentTabsCount);
break;
case SYNCED_DEVICES:
((CombinedHistoryItem.SmartFolder) viewHolder).bind(R.drawable.cloud, R.string.home_synced_devices_smartfolder, R.string.home_synced_devices_one, R.string.home_synced_devices_number, deviceCount);
break;
@ -140,6 +147,9 @@ public class CombinedHistoryAdapter extends RecyclerView.Adapter<CombinedHistory
}
private CombinedHistoryItem.ItemType getItemTypeForPosition(int position) {
if (position == RECENT_TABS_SMARTFOLDER_INDEX) {
return CombinedHistoryItem.ItemType.RECENT_TABS;
}
if (position == SYNCED_DEVICES_SMARTFOLDER_INDEX) {
return CombinedHistoryItem.ItemType.SYNCED_DEVICES;
}

View File

@ -24,7 +24,8 @@ public abstract class CombinedHistoryItem extends RecyclerView.ViewHolder {
}
public enum ItemType {
CLIENT, HIDDEN_DEVICES, SECTION_HEADER, HISTORY, NAVIGATION_BACK, CHILD, SYNCED_DEVICES;
CLIENT, HIDDEN_DEVICES, SECTION_HEADER, HISTORY, NAVIGATION_BACK, CHILD, SYNCED_DEVICES,
RECENT_TABS, CLOSED_TAB;
public static ItemType viewTypeToItemType(int viewType) {
if (viewType >= ItemType.values().length) {

View File

@ -69,11 +69,12 @@ public class CombinedHistoryPanel extends HomeFragment implements RemoteClientsD
private final static String FORMAT_S2 = "%2$s";
// Number of smart folders for determining practical empty state.
public static final int NUM_SMART_FOLDERS = 1;
public static final int NUM_SMART_FOLDERS = 2;
private CombinedHistoryRecyclerView mRecyclerView;
private CombinedHistoryAdapter mHistoryAdapter;
private ClientsAdapter mClientsAdapter;
private RecentTabsAdapter mRecentTabsAdapter;
private CursorLoaderCallbacks mCursorLoaderCallbacks;
private PanelLevel mPanelLevel;
@ -88,10 +89,11 @@ public class CombinedHistoryPanel extends HomeFragment implements RemoteClientsD
// Reference to the View to display when there are no results.
private View mHistoryEmptyView;
private View mClientsEmptyView;
private View mRecentTabsEmptyView;
public interface OnPanelLevelChangeListener {
enum PanelLevel {
PARENT, CHILD
PARENT, CHILD_SYNC, CHILD_RECENT_TABS
}
/**
@ -108,6 +110,7 @@ public class CombinedHistoryPanel extends HomeFragment implements RemoteClientsD
mHistoryAdapter = new CombinedHistoryAdapter(getResources());
mClientsAdapter = new ClientsAdapter(getContext());
mRecentTabsAdapter = new RecentTabsAdapter();
mSyncStatusListener = new RemoteTabsSyncListener();
FirefoxAccounts.addSyncStatusListener(mSyncStatusListener);
@ -130,6 +133,7 @@ public class CombinedHistoryPanel extends HomeFragment implements RemoteClientsD
mClientsEmptyView = view.findViewById(R.id.home_clients_empty_view);
mHistoryEmptyView = view.findViewById(R.id.home_history_empty_view);
mRecentTabsEmptyView = view.findViewById(R.id.home_recent_tabs_empty_view);
setUpEmptyViews();
mPanelFooterButton = (Button) view.findViewById(R.id.clear_history_button);
@ -141,7 +145,8 @@ public class CombinedHistoryPanel extends HomeFragment implements RemoteClientsD
mPanelLevel = PanelLevel.PARENT;
}
mRecyclerView.setAdapter(mPanelLevel == PanelLevel.PARENT ? mHistoryAdapter : mClientsAdapter);
mRecyclerView.setAdapter(mPanelLevel == PanelLevel.PARENT ? mHistoryAdapter :
mPanelLevel == PanelLevel.CHILD_SYNC ? mClientsAdapter : mRecentTabsAdapter);
final RecyclerView.ItemAnimator animator = new DefaultItemAnimator();
animator.setAddDuration(100);
@ -175,23 +180,23 @@ public class CombinedHistoryPanel extends HomeFragment implements RemoteClientsD
private void setUpEmptyViews() {
// Set up history empty view.
final ImageView emptyIcon = (ImageView) mHistoryEmptyView.findViewById(R.id.home_empty_image);
emptyIcon.setVisibility(View.GONE);
final ImageView historyIcon = (ImageView) mHistoryEmptyView.findViewById(R.id.home_empty_image);
historyIcon.setVisibility(View.GONE);
final TextView emptyText = (TextView) mHistoryEmptyView.findViewById(R.id.home_empty_text);
emptyText.setText(R.string.home_most_recent_empty);
final TextView historyText = (TextView) mHistoryEmptyView.findViewById(R.id.home_empty_text);
historyText.setText(R.string.home_most_recent_empty);
final TextView emptyHint = (TextView) mHistoryEmptyView.findViewById(R.id.home_empty_hint);
final TextView historyHint = (TextView) mHistoryEmptyView.findViewById(R.id.home_empty_hint);
if (!Restrictions.isAllowed(getActivity(), Restrictable.PRIVATE_BROWSING)) {
emptyHint.setVisibility(View.GONE);
historyHint.setVisibility(View.GONE);
} else {
final String hintText = getResources().getString(R.string.home_most_recent_emptyhint);
final SpannableStringBuilder hintBuilder = formatHintText(hintText);
if (hintBuilder != null) {
emptyHint.setText(hintBuilder);
emptyHint.setMovementMethod(LinkMovementMethod.getInstance());
emptyHint.setVisibility(View.VISIBLE);
historyHint.setText(hintBuilder);
historyHint.setMovementMethod(LinkMovementMethod.getInstance());
historyHint.setVisibility(View.VISIBLE);
}
}
@ -206,6 +211,13 @@ public class CombinedHistoryPanel extends HomeFragment implements RemoteClientsD
startActivity(intent);
}
});
// Set up Recent Tabs empty view.
final ImageView recentTabsIcon = (ImageView) mRecentTabsEmptyView.findViewById(R.id.home_empty_image);
recentTabsIcon.setImageResource(R.drawable.icon_remote_tabs_empty);
final TextView recentTabsText = (TextView) mRecentTabsEmptyView.findViewById(R.id.home_empty_text);
recentTabsText.setText(R.string.home_last_tabs_empty);
}
@Override
@ -310,9 +322,12 @@ public class CombinedHistoryPanel extends HomeFragment implements RemoteClientsD
case PARENT:
mRecyclerView.swapAdapter(mHistoryAdapter, true);
break;
case CHILD:
case CHILD_SYNC:
mRecyclerView.swapAdapter(mClientsAdapter, true);
break;
case CHILD_RECENT_TABS:
mRecyclerView.swapAdapter(mRecentTabsAdapter, true);
break;
}
updateEmptyView();
@ -331,7 +346,8 @@ public class CombinedHistoryPanel extends HomeFragment implements RemoteClientsD
mPanelFooterButton.setVisibility(View.VISIBLE);
}
break;
case CHILD:
case CHILD_SYNC:
case CHILD_RECENT_TABS:
mPanelFooterButton.setVisibility(View.GONE);
break;
}
@ -375,21 +391,27 @@ public class CombinedHistoryPanel extends HomeFragment implements RemoteClientsD
private void updateEmptyView() {
boolean showEmptyHistoryView = false;
boolean showEmptyClientsView = false;
boolean showEmptyRecentTabsView = false;
switch (mPanelLevel) {
case PARENT:
showEmptyHistoryView = mHistoryAdapter.getItemCount() == NUM_SMART_FOLDERS;
break;
case CHILD:
case CHILD_SYNC:
showEmptyClientsView = mClientsAdapter.getItemCount() == 1;
break;
case CHILD_RECENT_TABS:
showEmptyRecentTabsView = mRecentTabsAdapter.getItemCount() == 1;
break;
}
final boolean showEmptyView = showEmptyClientsView || showEmptyHistoryView;
final boolean showEmptyView = showEmptyClientsView || showEmptyHistoryView || showEmptyRecentTabsView;
mRecyclerView.setOverScrollMode(showEmptyView ? View.OVER_SCROLL_NEVER : View.OVER_SCROLL_IF_CONTENT_SCROLLS);
mClientsEmptyView.setVisibility(showEmptyClientsView ? View.VISIBLE : View.GONE);
mHistoryEmptyView.setVisibility(showEmptyHistoryView ? View.VISIBLE : View.GONE);
mRecentTabsEmptyView.setVisibility(showEmptyRecentTabsView ? View.VISIBLE : View.GONE);
}
/**

View File

@ -19,7 +19,8 @@ import org.mozilla.gecko.widget.RecyclerViewClickSupport;
import java.util.EnumSet;
import static org.mozilla.gecko.home.CombinedHistoryPanel.OnPanelLevelChangeListener.PanelLevel.CHILD;
import static org.mozilla.gecko.home.CombinedHistoryPanel.OnPanelLevelChangeListener.PanelLevel.CHILD_RECENT_TABS;
import static org.mozilla.gecko.home.CombinedHistoryPanel.OnPanelLevelChangeListener.PanelLevel.CHILD_SYNC;
import static org.mozilla.gecko.home.CombinedHistoryPanel.OnPanelLevelChangeListener.PanelLevel.PARENT;
public class CombinedHistoryRecyclerView extends RecyclerView
@ -91,8 +92,12 @@ public class CombinedHistoryRecyclerView extends RecyclerView
final CombinedHistoryItem.ItemType itemType = CombinedHistoryItem.ItemType.viewTypeToItemType(viewType);
switch (itemType) {
case RECENT_TABS:
mOnPanelLevelChangeListener.changeLevel(CHILD_RECENT_TABS);
break;
case SYNCED_DEVICES:
mOnPanelLevelChangeListener.changeLevel(CHILD);
mOnPanelLevelChangeListener.changeLevel(CHILD_SYNC);
break;
case CLIENT:
@ -117,6 +122,9 @@ public class CombinedHistoryRecyclerView extends RecyclerView
mOnUrlOpenListener.onUrlOpen(historyItem.getUrl(), EnumSet.of(HomePager.OnUrlOpenListener.Flags.ALLOW_SWITCH_TO_TAB));
}
break;
case CLOSED_TAB:
break;
}
}

View File

@ -0,0 +1,73 @@
/* -*- 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 android.support.v7.widget.RecyclerView;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import org.mozilla.gecko.R;
import static org.mozilla.gecko.home.CombinedHistoryItem.ItemType;
public class RecentTabsAdapter extends RecyclerView.Adapter<CombinedHistoryItem> implements CombinedHistoryRecyclerView.AdapterContextMenuBuilder {
private static final String LOGTAG = "GeckoRecentTabsAdapter";
@Override
public CombinedHistoryItem onCreateViewHolder(ViewGroup parent, int viewType) {
final LayoutInflater inflater = LayoutInflater.from(parent.getContext());
final View view;
final CombinedHistoryItem.ItemType itemType = CombinedHistoryItem.ItemType.viewTypeToItemType(viewType);
switch (itemType) {
case NAVIGATION_BACK:
view = inflater.inflate(R.layout.home_combined_back_item, parent, false);
return new CombinedHistoryItem.HistoryItem(view);
case SECTION_HEADER:
view = inflater.inflate(R.layout.home_header_row, parent, false);
return new CombinedHistoryItem.BasicItem(view);
case CLOSED_TAB:
view = inflater.inflate(R.layout.home_item_row, parent, false);
return new CombinedHistoryItem.HistoryItem(view);
}
return null;
}
@Override
public void onBindViewHolder(CombinedHistoryItem holder, final int position) {
final CombinedHistoryItem.ItemType itemType = getItemTypeForPosition(position);
}
@Override
public int getItemCount() {
return 1;
}
private CombinedHistoryItem.ItemType getItemTypeForPosition(int position) {
if (position == 0) {
return ItemType.NAVIGATION_BACK;
}
return ItemType.CLOSED_TAB;
}
@Override
public int getItemViewType(int position) {
return CombinedHistoryItem.ItemType.itemTypeToViewType(getItemTypeForPosition(position));
}
@Override
public HomeContextMenuInfo makeContextMenuInfoFromPosition(View view, int position) {
final CombinedHistoryItem.ItemType itemType = getItemTypeForPosition(position);
final HomeContextMenuInfo info;
return null;
}
}

View File

@ -571,9 +571,13 @@ size. -->
<!ENTITY home_clear_history_confirm "Are you sure you want to clear your history?">
<!ENTITY home_bookmarks_empty "Bookmarks you save show up here.">
<!ENTITY home_closed_tabs_title "Recently closed tabs">
<!ENTITY home_closed_tabs_title2 "Recently closed">
<!ENTITY home_last_tabs_title "Tabs from last time">
<!ENTITY home_last_tabs_empty "Your recent tabs show up here.">
<!ENTITY home_open_all "Open all">
<!ENTITY home_closed_tabs_number "&formatD; tabs">
<!-- Localization note (home_closed_tabs_one): This is the singular version of home_closed_tabs_number, referring to the number of recently closed tabs available. -->
<!ENTITY home_closed_tabs_one "1 tab">
<!ENTITY home_most_recent_empty "Websites you visited most recently show up here.">
<!-- Localization note (home_most_recent_emptyhint2): "Psst" is a sound that might be used to attract someone's attention unobtrusively, and intended to hint at Private Browsing to the user.
The placeholders &formatS1; and &formatS2; are used to mark the location of text underlining. -->

View File

@ -431,6 +431,7 @@ gbjar.sources += ['java/org/mozilla/gecko/' + x for x in [
'home/PanelViewAdapter.java',
'home/PanelViewItemHandler.java',
'home/PinSiteDialog.java',
'home/RecentTabsAdapter.java',
'home/RecentTabsPanel.java',
'home/RemoteTabsExpandableListState.java',
'home/SearchEngine.java',

View File

@ -36,6 +36,13 @@
android:layout_weight="3"
android:visibility="gone"/>
<include android:id="@+id/home_recent_tabs_empty_view"
layout="@layout/home_empty_panel"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="3"
android:visibility="gone"/>
<Button android:id="@+id/clear_history_button"
style="@style/Widget.Home.ActionButton"
android:text="@string/home_clear_history_button"

View File

@ -453,9 +453,12 @@
<string name="home_clear_history_confirm">&home_clear_history_confirm;</string>
<string name="home_bookmarks_empty">&home_bookmarks_empty;</string>
<string name="home_closed_tabs_title">&home_closed_tabs_title;</string>
<string name="home_closed_tabs_title2">&home_closed_tabs_title2;</string>
<string name="home_last_tabs_title">&home_last_tabs_title;</string>
<string name="home_last_tabs_empty">&home_last_tabs_empty;</string>
<string name="home_open_all">&home_open_all;</string>
<string name="home_closed_tabs_number">&home_closed_tabs_number;</string>
<string name="home_closed_tabs_one">&home_closed_tabs_one;</string>
<string name="home_most_recent_empty">&home_most_recent_empty;</string>
<string name="home_most_recent_emptyhint">&home_most_recent_emptyhint2;</string>
<string name="home_default_empty">&home_default_empty;</string>