mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
Bug 847435 - Redesign tab history menu. r=bnicholson
This commit is contained in:
parent
46cadf46c1
commit
436c4172e1
@ -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);
|
||||
|
@ -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);
|
||||
|
@ -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',
|
||||
|
27
mobile/android/base/resources/drawable/tab_history_bg.xml
Normal file
27
mobile/android/base/resources/drawable/tab_history_bg.xml
Normal 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>
|
@ -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>
|
@ -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"/>
|
||||
|
@ -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>
|
17
mobile/android/base/resources/layout/tab_history_layout.xml
Normal file
17
mobile/android/base/resources/layout/tab_history_layout.xml
Normal 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>
|
@ -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>
|
||||
|
||||
|
@ -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>
|
||||
|
||||
|
@ -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>
|
||||
|
119
mobile/android/base/tabs/TabHistoryController.java
Normal file
119
mobile/android/base/tabs/TabHistoryController.java
Normal 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;
|
||||
}
|
||||
}
|
161
mobile/android/base/tabs/TabHistoryFragment.java
Normal file
161
mobile/android/base/tabs/TabHistoryFragment.java
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
95
mobile/android/base/tabs/TabHistoryItemRow.java
Normal file
95
mobile/android/base/tabs/TabHistoryItemRow.java
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
60
mobile/android/base/tabs/TabHistoryPage.java
Normal file
60
mobile/android/base/tabs/TabHistoryPage.java
Normal 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];
|
||||
}
|
||||
};
|
||||
}
|
@ -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();
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -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 };
|
||||
},
|
||||
};
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user