Bug 847435 - Redesign tab history menu. r=bnicholson

This commit is contained in:
vivek 2014-11-05 10:13:56 -08:00
parent 46cadf46c1
commit 436c4172e1
19 changed files with 693 additions and 78 deletions

View File

@ -58,7 +58,11 @@ import org.mozilla.gecko.preferences.GeckoPreferences;
import org.mozilla.gecko.prompts.Prompt;
import org.mozilla.gecko.prompts.PromptListItem;
import org.mozilla.gecko.sync.setup.SyncAccounts;
import org.mozilla.gecko.tabs.TabHistoryController;
import org.mozilla.gecko.tabs.TabHistoryFragment;
import org.mozilla.gecko.tabs.TabHistoryPage;
import org.mozilla.gecko.tabs.TabsPanel;
import org.mozilla.gecko.tabs.TabHistoryController.OnShowTabHistory;
import org.mozilla.gecko.toolbar.AutocompleteHandler;
import org.mozilla.gecko.toolbar.BrowserToolbar;
import org.mozilla.gecko.toolbar.ToolbarProgressView;
@ -175,9 +179,11 @@ public class BrowserApp extends GeckoApp
protected Telemetry.Timer mAboutHomeStartupTimer;
private ActionModeCompat mActionMode;
private boolean mShowActionModeEndAnimation;
private TabHistoryController tabHistoryController;
private static final int GECKO_TOOLS_MENU = -1;
private static final int ADDON_MENU_OFFSET = 1000;
public static final String TAB_HISTORY_FRAGMENT_TAG = "tabHistoryFragment";
private static class MenuItemInfo {
public int id;
public String label;
@ -461,6 +467,22 @@ public class BrowserApp extends GeckoApp
mProgressView = (ToolbarProgressView) findViewById(R.id.progress);
mBrowserToolbar.setProgressBar(mProgressView);
// Initialize Tab History Controller.
tabHistoryController = new TabHistoryController(new OnShowTabHistory() {
@Override
public void onShowHistory(final List<TabHistoryPage> historyPageList, final int toIndex) {
runOnUiThread(new Runnable() {
@Override
public void run() {
final TabHistoryFragment fragment = TabHistoryFragment.newInstance(historyPageList, toIndex);
final FragmentManager fragmentManager = getSupportFragmentManager();
fragment.show(R.id.tab_history_panel, fragmentManager.beginTransaction(), TAB_HISTORY_FRAGMENT_TAG);
}
});
}
});
mBrowserToolbar.setTabHistoryController(tabHistoryController);
final String action = intent.getAction();
if (Intent.ACTION_VIEW.equals(action)) {
// Show the target URL immediately in the toolbar.
@ -650,7 +672,6 @@ public class BrowserApp extends GeckoApp
return null;
}
@Override
public void onBackPressed() {
if (getSupportFragmentManager().getBackStackEntryCount() > 0) {
@ -2574,6 +2595,12 @@ public class BrowserApp extends GeckoApp
if (aMenu == null)
return false;
// Hide the tab history panel when hardware menu button is pressed.
TabHistoryFragment frag = (TabHistoryFragment) getSupportFragmentManager().findFragmentByTag(TAB_HISTORY_FRAGMENT_TAG);
if (frag != null) {
frag.dismiss();
}
if (!GeckoThread.checkLaunchState(GeckoThread.LaunchState.GeckoRunning)) {
aMenu.findItem(R.id.settings).setEnabled(false);
aMenu.findItem(R.id.help).setEnabled(false);
@ -2935,9 +2962,15 @@ public class BrowserApp extends GeckoApp
@Override
public boolean onKeyLongPress(int keyCode, KeyEvent event) {
if (keyCode == KeyEvent.KEYCODE_BACK) {
// If the tab search history is already shown, do nothing.
TabHistoryFragment frag = (TabHistoryFragment) getSupportFragmentManager().findFragmentByTag(TAB_HISTORY_FRAGMENT_TAG);
if (frag != null) {
return false;
}
Tab tab = Tabs.getInstance().getSelectedTab();
if (tab != null) {
return tab.showAllHistory();
return tabHistoryController.showTabHistory(tab, TabHistoryController.HistoryAction.ALL);
}
}
return super.onKeyLongPress(keyCode, event);

View File

@ -15,7 +15,6 @@ import java.util.regex.Pattern;
import org.json.JSONException;
import org.json.JSONObject;
import org.mozilla.gecko.db.BrowserContract.Bookmarks;
import org.mozilla.gecko.db.BrowserDB;
import org.mozilla.gecko.db.URLMetadata;
import org.mozilla.gecko.favicons.Favicons;
@ -74,7 +73,6 @@ public class Tab {
private boolean mEnteringReaderMode;
private final Context mAppContext;
private ErrorType mErrorType = ErrorType.NONE;
private static final int MAX_HISTORY_LIST_SIZE = 50;
private volatile int mLoadProgress;
private volatile int mRecordingCount;
private String mMostRecentHomePanel;
@ -91,6 +89,7 @@ public class Tab {
public static final int LOAD_PROGRESS_STOP = 100;
private static final int DEFAULT_BACKGROUND_COLOR = Color.WHITE;
public static final int MAX_HISTORY_LIST_SIZE = 50;
public enum ErrorType {
CERT_ERROR, // Pages with certificate problems
@ -321,6 +320,14 @@ public class Tab {
return mContentType;
}
public int getHistoryIndex() {
return mHistoryIndex;
}
public int getHistorySize() {
return mHistorySize;
}
public synchronized void updateTitle(String title) {
// Keep the title unchanged while entering reader mode.
if (mEnteringReaderMode) {
@ -545,53 +552,6 @@ public class Tab {
return true;
}
public boolean showBackHistory() {
if (!canDoBack())
return false;
return this.showHistory(Math.max(mHistoryIndex - MAX_HISTORY_LIST_SIZE, 0), mHistoryIndex, mHistoryIndex);
}
public boolean showForwardHistory() {
if (!canDoForward())
return false;
return this.showHistory(mHistoryIndex, Math.min(mHistorySize - 1, mHistoryIndex + MAX_HISTORY_LIST_SIZE), mHistoryIndex);
}
public boolean showAllHistory() {
if (!canDoForward() && !canDoBack())
return false;
int min = mHistoryIndex - MAX_HISTORY_LIST_SIZE / 2;
int max = mHistoryIndex + MAX_HISTORY_LIST_SIZE / 2;
if (min < 0) {
max -= min;
}
if (max > mHistorySize - 1) {
min -= max - (mHistorySize - 1);
max = mHistorySize - 1;
}
min = Math.max(min, 0);
return this.showHistory(min, max, mHistoryIndex);
}
/**
* This method will show the history starting on fromIndex until toIndex of the history.
*/
public boolean showHistory(int fromIndex, int toIndex, int selIndex) {
JSONObject json = new JSONObject();
try {
json.put("fromIndex", fromIndex);
json.put("toIndex", toIndex);
json.put("selIndex", selIndex);
} catch (JSONException e) {
Log.e(LOGTAG, "JSON error", e);
}
GeckoEvent e = GeckoEvent.createBroadcastEvent("Session:ShowHistory", json.toString());
GeckoAppShell.sendEventToGecko(e);
return true;
}
public void doStop() {
GeckoEvent e = GeckoEvent.createBroadcastEvent("Session:Stop", "");
GeckoAppShell.sendEventToGecko(e);

View File

@ -395,6 +395,10 @@ gbjar.sources += [
'tabs/PrivateTabsPanel.java',
'tabs/RemoteTabsPanel.java',
'tabs/TabCurve.java',
'tabs/TabHistoryController.java',
'tabs/TabHistoryFragment.java',
'tabs/TabHistoryItemRow.java',
'tabs/TabHistoryPage.java',
'tabs/TabsGridLayout.java',
'tabs/TabsLayoutAdapter.java',
'tabs/TabsLayoutItemView.java',

View File

@ -0,0 +1,27 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- 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/. -->
<layer-list xmlns:android="http://schemas.android.com/apk/res/android" >
<item>
<shape
xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle" >
<stroke
android:width="@dimen/tab_history_bg_width"
android:color="@color/tab_history_border_color" />
<padding android:top="@dimen/tab_history_border_padding" />
</shape>
</item>
<item>
<shape
xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle" >
<solid
android:width="@dimen/tab_history_bg_width"
android:color="@color/tab_history_background" />
</shape>
</item>
</layer-list>

View File

@ -0,0 +1,24 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- 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/. -->
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_enabled="false">
<shape>
<solid android:color="@color/tab_history_favicon_background" />
<stroke android:width="@dimen/tab_history_favicon_border_disabled"
android:color="@color/tab_history_favicon_border" />
</shape>
</item>
<item android:state_enabled="true">
<shape>
<solid android:color="@color/tab_history_favicon_background" />
<stroke android:width="@dimen/tab_history_favicon_border_enabled"
android:color="@color/tab_history_favicon_border" />
</shape>
</item>
</selector>

View File

@ -124,6 +124,12 @@
</view>
<FrameLayout android:id="@+id/tab_history_panel"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_alignParentBottom="true"
android:visibility="gone" />
<ViewStub android:id="@+id/toast_stub"
android:layout="@layout/button_toast"
style="@style/Toast"/>

View File

@ -0,0 +1,56 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- 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/. -->
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:gecko= "http://schemas.android.com/apk/res-auto"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:background="@drawable/action_bar_button" >
<LinearLayout android:id="@+id/tab_history_timeline_combo"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical"
android:layout_marginLeft="@dimen/tab_history_combo_margin_left"
android:layout_marginRight="@dimen/tab_history_combo_margin_right" >
<ImageView android:id="@+id/tab_history_timeline_top"
android:layout_width="@dimen/tab_history_timeline_width"
android:layout_height="@dimen/tab_history_timeline_height"
android:layout_gravity="center_horizontal"
android:background="@color/tab_history_timeline_separator" />
<org.mozilla.gecko.widget.FaviconView android:id="@+id/tab_history_icon"
android:layout_width="@dimen/tab_history_favicon_bg"
android:layout_height="@dimen/tab_history_favicon_bg"
android:background="@drawable/tab_history_icon_state"
android:padding="@dimen/tab_history_favicon_padding"
android:scaleType="centerInside"
gecko:overrideScaleType="false"
gecko:dominantBorderEnabled="false" />
<ImageView android:id="@+id/tab_history_timeline_bottom"
android:layout_width="@dimen/tab_history_timeline_width"
android:layout_height="@dimen/tab_history_timeline_height"
android:layout_gravity="center_horizontal"
android:background="@color/tab_history_timeline_separator" />
</LinearLayout>
<org.mozilla.gecko.widget.FadedTextView
android:id="@+id/tab_history_title"
style="@style/Widget.TwoLinePageRow.Title"
android:layout_centerVertical="true"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:paddingRight="@dimen/tab_history_title_margin_right"
android:text="@+id/tab_history_title"
android:textSize="@dimen/tab_history_title_text_size"
android:textColor="@color/tab_history_title_color"
gecko:fadeWidth="@dimen/tab_history_title_fading_width"/>
</LinearLayout>

View File

@ -0,0 +1,17 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- 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/. -->
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent" >
<ListView android:id="@+id/tab_history_list"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:background="@drawable/tab_history_bg"
android:divider="@null" />
</RelativeLayout>

View File

@ -182,5 +182,10 @@
<attr name="floatingHintEditTextStyle" format="reference" />
</declare-styleable>
<declare-styleable name="FaviconView">
<attr name="dominantBorderEnabled" format="boolean" />
<attr name="overrideScaleType" format="boolean" />
</declare-styleable>
</resources>

View File

@ -142,6 +142,14 @@
<color name="toast_button_pressed">#DD2C3136</color>
<color name="toast_button_text">#FFFFFFFF</color>
<!-- Tab History colors. -->
<color name="tab_history_background">#EBEBF0</color>
<color name="tab_history_timeline_separator">#D7D9DB</color>
<color name="tab_history_favicon_border">#D7D9DB</color>
<color name="tab_history_favicon_background">#FFFFFF</color>
<color name="tab_history_title_color">#363B40</color>
<color name="tab_history_border_color">#DADADF</color>
<!-- Colour used for Find-In-Page dialog -->
<color name="find_status_default">#AFB1B3</color>

View File

@ -163,6 +163,21 @@
<dimen name="arrow_popup_arrow_height">12dip</dimen>
<dimen name="arrow_popup_arrow_offset">8dp</dimen>
<!-- TabHistoryItemRow dimensions. -->
<dimen name="tab_history_timeline_width">3dp</dimen>
<dimen name="tab_history_timeline_height">14dp</dimen>
<dimen name="tab_history_favicon_bg">32dp</dimen>
<dimen name="tab_history_favicon_padding">5dp</dimen>
<dimen name="tab_history_favicon_border_enabled">3dp</dimen>
<dimen name="tab_history_favicon_border_disabled">1dp</dimen>
<dimen name="tab_history_combo_margin_left">15dp</dimen>
<dimen name="tab_history_combo_margin_right">15dp</dimen>
<dimen name="tab_history_title_fading_width">50dp</dimen>
<dimen name="tab_history_title_margin_right">15dp</dimen>
<dimen name="tab_history_title_text_size">14sp</dimen>
<dimen name="tab_history_bg_width">2dp</dimen>
<dimen name="tab_history_border_padding">2dp</dimen>
<!-- Find-In-Page dialog dimensions. -->
<dimen name="find_in_page_text_margin_left">5dip</dimen>
<dimen name="find_in_page_text_margin_right">12dip</dimen>

View File

@ -0,0 +1,119 @@
/* 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.tabs;
import java.util.ArrayList;
import java.util.List;
import org.json.JSONException;
import org.json.JSONObject;
import org.mozilla.gecko.GeckoAppShell;
import org.mozilla.gecko.Tab;
import org.mozilla.gecko.util.GeckoRequest;
import org.mozilla.gecko.util.NativeJSObject;
import android.util.Log;
public class TabHistoryController {
private static final String LOGTAG = "TabHistoryController";
private final OnShowTabHistory showTabHistoryListener;
public static enum HistoryAction {
ALL,
BACK,
FORWARD
};
public interface OnShowTabHistory {
void onShowHistory(List<TabHistoryPage>historyPageList, int toIndex);
}
public TabHistoryController(OnShowTabHistory showTabHistoryListener) {
this.showTabHistoryListener = showTabHistoryListener;
}
public boolean showTabHistory(final Tab tab, final HistoryAction action) {
int historyIndex = tab.getHistoryIndex();
int historySize = tab.getHistorySize();
switch(action) {
case BACK:
if (!tab.canDoBack()) {
return false;
}
return showHistory(Math.max(historyIndex - Tab.MAX_HISTORY_LIST_SIZE, 0), historyIndex, historyIndex);
case FORWARD:
if (!tab.canDoForward()) {
return false;
}
return showHistory(historyIndex, Math.min(historySize - 1, historyIndex + Tab.MAX_HISTORY_LIST_SIZE), historyIndex);
case ALL:
if (!tab.canDoForward() && !tab.canDoBack()) {
return false;
}
int min = historyIndex - Tab.MAX_HISTORY_LIST_SIZE / 2;
int max = historyIndex + Tab.MAX_HISTORY_LIST_SIZE / 2;
if (min < 0) {
max -= min;
}
if (max > historySize - 1) {
min -= max - (historySize - 1);
max = historySize - 1;
}
min = Math.max(min, 0);
return showHistory(min, max, historyIndex);
default:
return false;
}
}
/**
* This method will show the history starting on fromIndex until toIndex of the history.
*/
private boolean showHistory(final int fromIndex, final int toIndex, final int selIndex) {
JSONObject json = new JSONObject();
try {
json.put("fromIndex", fromIndex);
json.put("toIndex", toIndex);
json.put("selIndex", selIndex);
} catch (JSONException e) {
Log.e(LOGTAG, "JSON error", e);
}
GeckoAppShell.sendRequestToGecko(new GeckoRequest("Session:GetHistory", json) {
@Override
public void onResponse(NativeJSObject nativeJSObject) {
/*
* The response from gecko request is of the form
* "urls" : [
* {
* "title": "google",
* "url": "google.com",
* "selected": false
* }
* ]
*/
final NativeJSObject[] historyItems = nativeJSObject.getObjectArray("historyItems");
final List<TabHistoryPage> historyPageList = new ArrayList<>(historyItems.length);
for (NativeJSObject obj : historyItems) {
final String title = obj.getString("title");
final String url = obj.getString("url");
final boolean selected = obj.getBoolean("selected");
historyPageList.add(new TabHistoryPage(title, url, selected));
}
showTabHistoryListener.onShowHistory(historyPageList, toIndex);
}
});
return true;
}
}

View File

@ -0,0 +1,161 @@
/* 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.tabs;
import java.util.ArrayList;
import java.util.List;
import org.mozilla.gecko.GeckoAppShell;
import org.mozilla.gecko.GeckoEvent;
import org.mozilla.gecko.R;
import android.content.Context;
import android.content.DialogInterface;
import android.os.Bundle;
import android.os.Parcelable;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentManager;
import android.support.v4.app.FragmentTransaction;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.ViewGroup;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemClickListener;
import android.widget.ArrayAdapter;
import android.widget.ListView;
public class TabHistoryFragment extends Fragment implements OnItemClickListener, OnClickListener {
private static final String ARG_LIST = "historyPageList";
private static final String ARG_INDEX = "index";
private static final String BACK_STACK_ID = "backStateId";
private List<TabHistoryPage> historyPageList;
private int toIndex;
private ListView dialogList;
private int backStackId = -1;
private ViewGroup parent;
private boolean dismissed;
public TabHistoryFragment() {
}
public static TabHistoryFragment newInstance(List<TabHistoryPage> historyPageList, int toIndex) {
final TabHistoryFragment fragment = new TabHistoryFragment();
final Bundle args = new Bundle();
args.putParcelableArrayList(ARG_LIST, (ArrayList<? extends Parcelable>) historyPageList);
args.putInt(ARG_INDEX, toIndex);
fragment.setArguments(args);
return fragment;
}
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if (savedInstanceState != null) {
backStackId = savedInstanceState.getInt(BACK_STACK_ID, -1);
}
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
this.parent = container;
parent.setVisibility(View.VISIBLE);
View view = inflater.inflate(R.layout.tab_history_layout, container, false);
view.setOnClickListener(this);
dialogList = (ListView) view.findViewById(R.id.tab_history_list);
dialogList.setDivider(null);
return view;
}
@Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
Bundle bundle = getArguments();
historyPageList = bundle.getParcelableArrayList(ARG_LIST);
toIndex = bundle.getInt(ARG_INDEX);
final ArrayAdapter<TabHistoryPage> urlAdapter = new TabHistoryAdapter(getActivity(), historyPageList);
dialogList.setAdapter(urlAdapter);
dialogList.setOnItemClickListener(this);
}
@Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
String index = String.valueOf(toIndex - position);
GeckoAppShell.sendEventToGecko(GeckoEvent.createBroadcastEvent("Session:Navigate", index));
dismiss();
}
@Override
public void onClick(View v) {
// Since the fragment view fills the entire screen, any clicks outside of the history
// ListView will end up here.
dismiss();
}
@Override
public void onDestroy() {
super.onDestroy();
dismiss();
}
@Override
public void onSaveInstanceState(Bundle outState) {
if (backStackId >= 0) {
outState.putInt(BACK_STACK_ID, backStackId);
}
}
// Function to add this fragment to activity state with containerViewId as parent.
// This similar in functionality to DialogFragment.show() except that containerId is provided here.
public void show(final int containerViewId, final FragmentTransaction transaction, final String tag) {
dismissed = false;
transaction.add(containerViewId, this, tag);
transaction.addToBackStack(tag);
backStackId = transaction.commit();
}
// Pop the fragment from backstack if it exists.
public void dismiss() {
if (dismissed) {
return;
}
dismissed = true;
if (backStackId >= 0) {
getFragmentManager().popBackStackImmediate(backStackId, FragmentManager.POP_BACK_STACK_INCLUSIVE);
backStackId = -1;
}
if (parent != null) {
parent.setVisibility(View.GONE);
}
}
private static class TabHistoryAdapter extends ArrayAdapter<TabHistoryPage> {
private final List<TabHistoryPage> pages;
private final Context context;
public TabHistoryAdapter(Context context, List<TabHistoryPage> pages) {
super(context, R.layout.tab_history_item_row, pages);
this.context = context;
this.pages = pages;
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
TabHistoryItemRow row = (TabHistoryItemRow) convertView;
if (row == null) {
row = new TabHistoryItemRow(context, null);
}
row.update(pages.get(position), position == 0, position == pages.size() - 1);
return row;
}
}
}

View File

@ -0,0 +1,95 @@
/* 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.tabs;
import java.lang.ref.WeakReference;
import org.mozilla.gecko.R;
import org.mozilla.gecko.favicons.Favicons;
import org.mozilla.gecko.favicons.OnFaviconLoadedListener;
import org.mozilla.gecko.util.ThreadUtils;
import org.mozilla.gecko.widget.FaviconView;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Typeface;
import android.util.AttributeSet;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.ImageView;
import android.widget.RelativeLayout;
import android.widget.TextView;
public class TabHistoryItemRow extends RelativeLayout {
private final FaviconView favicon;
private final TextView title;
private final ImageView timeLineTop;
private final ImageView timeLineBottom;
// Listener for handling Favicon loads.
private final OnFaviconLoadedListener faviconListener;
private int loadFaviconJobId = Favicons.NOT_LOADING;
public TabHistoryItemRow(Context context, AttributeSet attrs) {
super(context, attrs);
LayoutInflater.from(context).inflate(R.layout.tab_history_item_row, this);
favicon = (FaviconView) findViewById(R.id.tab_history_icon);
title = (TextView) findViewById(R.id.tab_history_title);
timeLineTop = (ImageView) findViewById(R.id.tab_history_timeline_top);
timeLineBottom = (ImageView) findViewById(R.id.tab_history_timeline_bottom);
faviconListener = new UpdateViewFaviconLoadedListener(favicon);
}
// Update the views with historic page detail.
public void update(final TabHistoryPage historyPage, boolean isFirstElement, boolean isLastElement) {
ThreadUtils.assertOnUiThread();
timeLineTop.setVisibility(isFirstElement ? View.INVISIBLE : View.VISIBLE);
timeLineBottom.setVisibility(isLastElement ? View.INVISIBLE : View.VISIBLE);
title.setText(historyPage.getTitle());
if (historyPage.isSelected()) {
// Highlight title with bold font.
title.setTypeface(null, Typeface.BOLD);
} else {
// Clear previously set bold font.
title.setTypeface(null, Typeface.NORMAL);
}
favicon.setEnabled(historyPage.isSelected());
favicon.clearImage();
Favicons.cancelFaviconLoad(loadFaviconJobId);
loadFaviconJobId = Favicons.getSizedFaviconForPageFromLocal(getContext(), historyPage.getUrl(), faviconListener);
}
// Only holds a reference to the FaviconView itself, so if the row gets
// discarded while a task is outstanding, we'll leak less memory.
private static class UpdateViewFaviconLoadedListener implements OnFaviconLoadedListener {
private final WeakReference<FaviconView> view;
public UpdateViewFaviconLoadedListener(FaviconView view) {
this.view = new WeakReference<FaviconView>(view);
}
/**
* Update this row's favicon.
* <p>
* This method is always invoked on the UI thread.
*/
@Override
public void onFaviconLoaded(String url, String faviconURL, Bitmap favicon) {
FaviconView v = view.get();
if (v == null) {
return;
}
if (favicon == null) {
v.showDefaultFavicon();
return;
}
v.updateImage(favicon, faviconURL);
}
}
}

View File

@ -0,0 +1,60 @@
/* 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.tabs;
import android.os.Parcel;
import android.os.Parcelable;
public class TabHistoryPage implements Parcelable {
private final String title;
private final String url;
private final boolean selected;
public TabHistoryPage(String title, String url, boolean selected) {
this.title = title;
this.url = url;
this.selected = selected;
}
public String getTitle() {
return title;
}
public String getUrl() {
return url;
}
public boolean isSelected() {
return selected;
}
@Override
public int describeContents() {
return 0;
}
public void writeToParcel(Parcel dest, int flags) {
dest.writeString(title);
dest.writeString(url);
dest.writeInt(selected ? 1 : 0);
}
public static final Parcelable.Creator<TabHistoryPage> CREATOR = new Parcelable.Creator<TabHistoryPage>() {
@Override
public TabHistoryPage createFromParcel(final Parcel source) {
final String title = source.readString();
final String url = source.readString();
final boolean selected = source.readByte() != 0;
final TabHistoryPage page = new TabHistoryPage(title, url, selected);
return page;
}
@Override
public TabHistoryPage[] newArray(int size) {
return new TabHistoryPage[size];
}
};
}

View File

@ -26,6 +26,7 @@ import org.mozilla.gecko.animation.PropertyAnimator.PropertyAnimationListener;
import org.mozilla.gecko.animation.ViewHelper;
import org.mozilla.gecko.menu.GeckoMenu;
import org.mozilla.gecko.menu.MenuPopup;
import org.mozilla.gecko.tabs.TabHistoryController;
import org.mozilla.gecko.toolbar.ToolbarDisplayLayout.OnStopListener;
import org.mozilla.gecko.toolbar.ToolbarDisplayLayout.OnTitleChangeListener;
import org.mozilla.gecko.toolbar.ToolbarDisplayLayout.UpdateFlags;
@ -132,6 +133,7 @@ public abstract class BrowserToolbar extends ThemedRelativeLayout
protected boolean hasSoftMenuButton;
protected UIMode uiMode;
protected TabHistoryController tabHistoryController;
private final Paint shadowPaint;
private final int shadowSize;
@ -372,6 +374,10 @@ public abstract class BrowserToolbar extends ThemedRelativeLayout
this.progressBar = progressBar;
}
public void setTabHistoryController(TabHistoryController tabHistoryController) {
this.tabHistoryController = tabHistoryController;
}
public void refresh() {
urlDisplayLayout.dismissSiteIdentityPopup();
}

View File

@ -11,6 +11,7 @@ import org.mozilla.gecko.NewTabletUI;
import org.mozilla.gecko.R;
import org.mozilla.gecko.Tab;
import org.mozilla.gecko.Tabs;
import org.mozilla.gecko.tabs.TabHistoryController;
import org.mozilla.gecko.menu.MenuItemActionBar;
import android.content.Context;
@ -65,7 +66,8 @@ abstract class BrowserToolbarTabletBase extends BrowserToolbar {
backButton.setOnLongClickListener(new Button.OnLongClickListener() {
@Override
public boolean onLongClick(View view) {
return Tabs.getInstance().getSelectedTab().showBackHistory();
return tabHistoryController.showTabHistory(Tabs.getInstance().getSelectedTab(),
TabHistoryController.HistoryAction.BACK);
}
});
@ -78,7 +80,8 @@ abstract class BrowserToolbarTabletBase extends BrowserToolbar {
forwardButton.setOnLongClickListener(new Button.OnLongClickListener() {
@Override
public boolean onLongClick(View view) {
return Tabs.getInstance().getSelectedTab().showForwardHistory();
return tabHistoryController.showTabHistory(Tabs.getInstance().getSelectedTab(),
TabHistoryController.HistoryAction.FORWARD);
}
});
}

View File

@ -9,6 +9,7 @@ import org.mozilla.gecko.R;
import org.mozilla.gecko.favicons.Favicons;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Paint;
@ -56,6 +57,12 @@ public class FaviconView extends ImageView {
// Size of the background rectangle.
private final RectF mBackgroundRect;
// Type of the border whose value is defined in attrs.xml .
private final boolean isDominantBorderEnabled;
// boolean switch for overriding scaletype, whose value is defined in attrs.xml .
private final boolean isOverrideScaleTypeEnabled;
// Initializing the static paints.
static {
sStrokePaint = new Paint(Paint.ANTI_ALIAS_FLAG);
@ -67,7 +74,18 @@ public class FaviconView extends ImageView {
public FaviconView(Context context, AttributeSet attrs) {
super(context, attrs);
setScaleType(ImageView.ScaleType.CENTER);
TypedArray a = context.getTheme().obtainStyledAttributes(attrs, R.styleable.FaviconView, 0, 0);
try {
isDominantBorderEnabled = a.getBoolean(R.styleable.FaviconView_dominantBorderEnabled, true);
isOverrideScaleTypeEnabled = a.getBoolean(R.styleable.FaviconView_overrideScaleType, true);
} finally {
a.recycle();
}
if (isOverrideScaleTypeEnabled) {
setScaleType(ImageView.ScaleType.CENTER);
}
mStrokeRect = new RectF();
mBackgroundRect = new RectF();
@ -105,12 +123,14 @@ public class FaviconView extends ImageView {
public void onDraw(Canvas canvas) {
super.onDraw(canvas);
// 27.5% transparent dominant color.
sBackgroundPaint.setColor(mDominantColor & 0x46FFFFFF);
canvas.drawRect(mStrokeRect, sBackgroundPaint);
if (isDominantBorderEnabled) {
// 27.5% transparent dominant color.
sBackgroundPaint.setColor(mDominantColor & 0x46FFFFFF);
canvas.drawRect(mStrokeRect, sBackgroundPaint);
sStrokePaint.setColor(mDominantColor);
canvas.drawRoundRect(mStrokeRect, sStrokeWidth, sStrokeWidth, sStrokePaint);
sStrokePaint.setColor(mDominantColor);
canvas.drawRoundRect(mStrokeRect, sStrokeWidth, sStrokeWidth, sStrokePaint);
}
}
/**

View File

@ -350,8 +350,8 @@ var BrowserApp = {
Services.obs.addObserver(this, "Tab:Selected", false);
Services.obs.addObserver(this, "Tab:Closed", false);
Services.obs.addObserver(this, "Session:Back", false);
Services.obs.addObserver(this, "Session:ShowHistory", false);
Services.obs.addObserver(this, "Session:Forward", false);
Services.obs.addObserver(this, "Session:Navigate", false);
Services.obs.addObserver(this, "Session:Reload", false);
Services.obs.addObserver(this, "Session:Stop", false);
Services.obs.addObserver(this, "SaveAs:PDF", false);
@ -377,6 +377,7 @@ var BrowserApp = {
Services.obs.addObserver(this, "Webapps:Load", false);
Services.obs.addObserver(this, "Webapps:AutoUninstall", false);
Services.obs.addObserver(this, "sessionstore-state-purge-complete", false);
Messaging.addListener(this.getHistory.bind(this), "Session:GetHistory");
function showFullScreenWarning() {
NativeWindow.toast.show(Strings.browser.GetStringFromName("alertFullScreenToast"), "short");
@ -1573,6 +1574,11 @@ var BrowserApp = {
browser.goForward();
break;
case "Session:Navigate":
let index = JSON.parse(aData);
browser.gotoIndex(index);
break;
case "Session:Reload": {
let flags = Ci.nsIWebNavigation.LOAD_FLAGS_BYPASS_PROXY | Ci.nsIWebNavigation.LOAD_FLAGS_BYPASS_CACHE;
@ -1606,12 +1612,6 @@ var BrowserApp = {
browser.stop();
break;
case "Session:ShowHistory": {
let data = JSON.parse(aData);
this.showHistory(data.fromIndex, data.toIndex, data.selIndex);
break;
}
case "Tab:Load": {
let data = JSON.parse(aData);
let url = data.url;
@ -1978,30 +1978,26 @@ var BrowserApp = {
this._prefObservers = newPrefObservers;
},
// This method will print a list from fromIndex to toIndex, optionally
// This method will return a list of history items from fromIndex to toIndex, optionally
// selecting selIndex(if fromIndex<=selIndex<=toIndex)
showHistory: function(fromIndex, toIndex, selIndex) {
getHistory: function(data) {
let fromIndex = data.fromIndex;
let toIndex = data.toIndex;
let selIndex = data.selIndex;
let browser = this.selectedBrowser;
let hist = browser.sessionHistory;
let listitems = [];
for (let i = toIndex; i >= fromIndex; i--) {
let entry = hist.getEntryAtIndex(i, false);
let item = {
label: entry.title || entry.URI.spec,
title: entry.title || entry.URI.spec,
url: entry.URI.spec,
selected: (i == selIndex)
};
listitems.push(item);
}
let p = new Prompt({
window: browser.contentWindow
}).setSingleChoiceItems(listitems).show(function(data) {
let selected = data.button;
if (selected == -1)
return;
browser.gotoIndex(toIndex-selected);
});
return { "historyItems" : listitems };
},
};