This commit is contained in:
Wes Kocher 2014-03-18 14:03:25 -07:00
Родитель 93246f1384 c1739b687d
Коммит 8730e90647
63 изменённых файлов: 765 добавлений и 236 удалений

Просмотреть файл

@ -920,3 +920,9 @@ pref("browser.autofocus", false);
// Enable wakelock
pref("dom.wakelock.enabled", true);
// Enable sync and mozId with Firefox Accounts.
#ifdef MOZ_SERVICES_FXACCOUNTS
pref("services.sync.fxaccounts.enabled", true);
pref("identity.fxaccounts.enabled", true);
#endif

Просмотреть файл

@ -22,7 +22,7 @@ if CONFIG['ENABLE_MARIONETTE']:
DEFINES['XPCOM_GLUE'] = True
for var in ('MOZ_APP_NAME', 'MOZ_APP_VERSION', 'MOZ_UPDATER'):
for var in ('MOZ_APP_NAME', 'MOZ_APP_VERSION', 'MOZ_UPDATER', 'MOZ_SERVICES_FXACCOUNTS'):
DEFINES[var] = CONFIG[var]
GENERATED_INCLUDES += [

Просмотреть файл

@ -23,7 +23,10 @@ Cu.import('resource://gre/modules/NetworkStatsService.jsm');
// Identity
Cu.import('resource://gre/modules/SignInToWebsite.jsm');
SignInToWebsiteController.init();
#ifdef MOZ_SERVICES_FXACCOUNTS
Cu.import('resource://gre/modules/FxAccountsMgmtService.jsm');
#endif
Cu.import('resource://gre/modules/DownloadsAPI.jsm');

Просмотреть файл

@ -72,9 +72,11 @@ component {637b0f77-2429-49a0-915f-abf5d0db8b9a} WebappsUpdateTimer.js
contract @mozilla.org/b2g/webapps-update-timer;1 {637b0f77-2429-49a0-915f-abf5d0db8b9a}
category update-timer WebappsUpdateTimer @mozilla.org/b2g/webapps-update-timer;1,getService,background-update-timer,webapps.update.interval,86400
#ifdef MOZ_SERVICES_FXACCOUNTS
# FxAccountsUIGlue.js
component {51875c14-91d7-4b8c-b65d-3549e101228c} FxAccountsUIGlue.js
contract @mozilla.org/fxaccounts/fxaccounts-ui-glue;1 {51875c14-91d7-4b8c-b65d-3549e101228c}
#endif
# HelperAppDialog.js
component {710322af-e6ae-4b0c-b2c9-1474a87b077e} HelperAppDialog.js

Просмотреть файл

@ -13,7 +13,6 @@ EXTRA_COMPONENTS += [
'ContentHandler.js',
'ContentPermissionPrompt.js',
'FilePicker.js',
'FxAccountsUIGlue.js',
'HelperAppDialog.js',
'MailtoProtocolHandler.js',
'PaymentGlue.js',
@ -37,8 +36,15 @@ if CONFIG['MOZ_UPDATER']:
EXTRA_JS_MODULES += [
'ErrorPage.jsm',
'FxAccountsMgmtService.jsm',
'SignInToWebsite.jsm',
'TelURIParser.jsm',
'WebappsUpdater.jsm',
]
if CONFIG['MOZ_SERVICES_FXACCOUNTS']:
EXTRA_COMPONENTS += [
'FxAccountsUIGlue.js'
]
EXTRA_JS_MODULES += [
'FxAccountsMgmtService.jsm'
]

Просмотреть файл

@ -63,3 +63,5 @@ fi
MOZ_FOLD_LIBS=1
MOZ_JSDOWNLOADS=1
MOZ_SERVICES_FXACCOUNTS=1

Просмотреть файл

@ -783,16 +783,18 @@ bin/components/@DLL_PREFIX@nkgnomevfs@DLL_SUFFIX@
@BINPATH@/components/TelProtocolHandler.js
@BINPATH@/components/B2GAboutRedirector.js
@BINPATH@/components/FilePicker.js
@BINPATH@/components/FxAccountsUIGlue.js
@BINPATH@/components/HelperAppDialog.js
@BINPATH@/components/DownloadsUI.js
#ifdef MOZ_SERVICES_FXACCOUNTS
@BINPATH@/components/FxAccountsUIGlue.js
@BINPATH@/components/services_fxaccounts.xpt
#endif
@BINPATH@/components/DataStore.manifest
@BINPATH@/components/DataStoreService.js
@BINPATH@/components/dom_datastore.xpt
@BINPATH@/components/services_fxaccounts.xpt
#ifdef MOZ_WEBSPEECH
@BINPATH@/components/dom_webspeechsynth.xpt
#endif

Просмотреть файл

@ -28,6 +28,7 @@ MOZ_DISABLE_EXPORT_JS=1
MOZ_SAFE_BROWSING=1
MOZ_SERVICES_COMMON=1
MOZ_SERVICES_CRYPTO=1
MOZ_SERVICES_FXACCOUNTS=1
MOZ_SERVICES_HEALTHREPORT=1
MOZ_SERVICES_METRICS=1
MOZ_SERVICES_SYNC=1
@ -67,4 +68,3 @@ MOZ_JSDOWNLOADS=1
MOZ_WEBM_ENCODER=1
# Enable exact rooting on desktop.
JSGC_USE_EXACT_ROOTING=1

Просмотреть файл

@ -11,8 +11,8 @@ const { classes: Cc, interfaces: Ci, utils: Cu } = Components;
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
Cu.import("resource://gre/modules/Services.jsm");
Cu.import("resource://gre/modules/devtools/event-emitter.js");
let promise = Cu.import("resource://gre/modules/commonjs/sdk/core/promise.js").Promise;
Cu.import("resource://gre/modules/devtools/Loader.jsm");
const { Promise: promise } = Cu.import("resource://gre/modules/Promise.jsm", {});
var ProfilerController = devtools.require("devtools/profiler/controller");

Просмотреть файл

@ -8,7 +8,7 @@ const {Cu} = require("chrome");
Cu.import("resource://gre/modules/Services.jsm");
var promise = require("sdk/core/promise");
var {Promise: promise} = require("resource://gre/modules/Promise.jsm");
var EventEmitter = require("devtools/toolkit/event-emitter");
var Telemetry = require("devtools/shared/telemetry");

Просмотреть файл

@ -5,9 +5,8 @@
"use strict";
const {Cc, Ci, Cu} = require("chrome");
var promise = require("sdk/core/promise");
var EventEmitter = require("devtools/toolkit/event-emitter");
const {Promise: promise} = require("resource://gre/modules/Promise.jsm");
const EventEmitter = require("devtools/toolkit/event-emitter");
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "DebuggerServer",

Просмотреть файл

@ -5,10 +5,8 @@
"use strict";
const {Cu} = require("chrome");
let promise = require("sdk/core/promise");
let EventEmitter = require("devtools/toolkit/event-emitter");
const EventEmitter = require("devtools/toolkit/event-emitter");
const {Promise: promise} = require("resource://gre/modules/Promise.jsm");
Cu.import("resource://gre/modules/Services.jsm");
Cu.import("resource:///modules/devtools/DOMHelpers.jsm");
@ -217,6 +215,7 @@ WindowHost.prototype = {
let frameLoad = function(event) {
win.removeEventListener("load", frameLoad, true);
win.focus();
this.frame = win.document.getElementById("toolbox-iframe");
this.emit("ready", this.frame);
@ -226,8 +225,6 @@ WindowHost.prototype = {
win.addEventListener("load", frameLoad, true);
win.addEventListener("unload", this._boundUnload);
win.focus();
this._window = win;
return deferred.promise;

Просмотреть файл

@ -5,11 +5,9 @@
"use strict";
const {Cu, Cc, Ci} = require("chrome");
let promise = require("sdk/core/promise");
let EventEmitter = require("devtools/toolkit/event-emitter");
Cu.import('resource://gre/modules/XPCOMUtils.jsm');
const EventEmitter = require("devtools/toolkit/event-emitter");
const {Promise: promise} = require("resource://gre/modules/Promise.jsm");
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
Cu.import("resource://gre/modules/Services.jsm");
Cu.import("resource:///modules/devtools/gDevTools.jsm");

Просмотреть файл

@ -10,7 +10,7 @@ const MIN_ZOOM = 0.5;
const MAX_ZOOM = 2;
let {Cc, Ci, Cu} = require("chrome");
let promise = require("sdk/core/promise");
let {Promise: promise} = require("resource://gre/modules/Promise.jsm");
let EventEmitter = require("devtools/toolkit/event-emitter");
let Telemetry = require("devtools/shared/telemetry");
let HUDService = require("devtools/webconsole/hudservice");
@ -1433,20 +1433,20 @@ ToolboxHighlighterUtils.prototype = {
* @return a promise that resolves when the highlighter is hidden
*/
unhighlight: function(forceHide=false) {
if (this.isRemoteHighlightable) {
// If the remote highlighter exists on the target, use it
return this.toolbox.initInspector().then(() => {
let autohide = forceHide || !gDevTools.testing;
let unhighlightPromise;
forceHide = forceHide || !gDevTools.testing;
if (autohide) {
return this.toolbox.highlighter.hideBoxModel();
}
return promise.resolve();
});
if (forceHide && this.isRemoteHighlightable && this.toolbox.highlighter) {
// If the remote highlighter exists on the target, use it
unhighlightPromise = this.toolbox.highlighter.hideBoxModel();
} else {
// If not, no need to unhighlight as the older highlight method uses a
// setTimeout to hide itself
return promise.resolve();
unhighlightPromise = promise.resolve();
}
return unhighlightPromise.then(() => {
this.toolbox.emit("node-unhighlight");
});
}
};

Просмотреть файл

@ -692,8 +692,10 @@ HTMLBreadcrumbs.prototype = {
// Make sure the selected node and its neighbours are visible.
this.scroll();
this.inspector.emit("breadcrumbs-updated", this.selection.nodeFront);
doneUpdating();
return resolveNextTick().then(() => {
this.inspector.emit("breadcrumbs-updated", this.selection.nodeFront);
doneUpdating();
});
}).then(null, err => {
doneUpdating(this.selection.nodeFront);
this.selectionGuardEnd(err);

Просмотреть файл

@ -470,9 +470,8 @@ InspectorPanel.prototype = {
*/
destroy: function InspectorPanel__destroy() {
if (this._panelDestroyer) {
return this._panelDestroyer.promise;
return this._panelDestroyer;
}
this._panelDestroyer = promise.defer();
if (this.walker) {
this.walker.off("new-root", this.onNewRoot);
@ -506,7 +505,7 @@ InspectorPanel.prototype = {
this.selection.off("before-new-node", this.onBeforeNewSelection);
this.selection.off("before-new-node-front", this.onBeforeNewSelection);
this.selection.off("detached-front", this.onDetached);
this._destroyMarkup();
this._panelDestroyer = this._destroyMarkup();
this.panelWin.inspector = null;
this.target = null;
this.panelDoc = null;
@ -517,8 +516,7 @@ InspectorPanel.prototype = {
this.nodemenu = null;
this._toolbox = null;
this._panelDestroyer.resolve(null);
return this._panelDestroyer.promise;
return this._panelDestroyer;
},
/**
@ -643,14 +641,18 @@ InspectorPanel.prototype = {
},
_destroyMarkup: function InspectorPanel__destroyMarkup() {
let destroyPromise;
if (this._boundMarkupFrameLoad) {
this._markupFrame.removeEventListener("load", this._boundMarkupFrameLoad, true);
this._boundMarkupFrameLoad = null;
}
if (this.markup) {
this.markup.destroy();
destroyPromise = this.markup.destroy();
this.markup = null;
} else {
destroyPromise = promise.resolve();
}
if (this._markupFrame) {
@ -659,6 +661,8 @@ InspectorPanel.prototype = {
}
this._markupBox = null;
return destroyPromise;
},
/**

Просмотреть файл

@ -19,6 +19,9 @@ function test()
let nodes;
let cursor;
let inspector;
let target;
let panel;
let container;
gBrowser.selectedTab = gBrowser.addTab();
gBrowser.selectedBrowser.addEventListener("load", function onload() {
@ -43,6 +46,9 @@ function test()
function runTests(aInspector)
{
inspector = aInspector;
target = TargetFactory.forTab(gBrowser.selectedTab);
panel = gDevTools.getToolbox(target).getPanel("inspector");
container = panel.panelDoc.getElementById("inspector-breadcrumbs");
cursor = 0;
inspector.on("breadcrumbs-updated", nodeSelected);
executeSoon(function() {
@ -69,9 +75,6 @@ function test()
function performTest()
{
let target = TargetFactory.forTab(gBrowser.selectedTab);
let panel = gDevTools.getToolbox(target).getPanel("inspector");
let container = panel.panelDoc.getElementById("inspector-breadcrumbs");
let buttonsLabelIds = nodes[cursor].result.split(" ");
// html > body > …

Просмотреть файл

@ -39,7 +39,7 @@ function createDocument() {
inspector = aInspector;
markupView = inspector.markup;
inspector.sidebar.once("ruleview-ready", function() {
waitForView("ruleview", () => {
ruleView = inspector.sidebar.getWindowForTab("ruleview").ruleview.view;
inspector.sidebar.select("ruleview");
startTests();

Просмотреть файл

@ -21,7 +21,8 @@ function test() {
let ruleview = inspector.sidebar.getWindowForTab("ruleview").ruleview.view;
let inlineStyles = ruleview._elementStyle.rules[0];
for each (let prop in inlineStyles.textProps) {
for (let key in inlineStyles.textProps) {
let prop = inlineStyles.textProps[key];
if (prop.name == aName) {
return prop;
}
@ -32,7 +33,8 @@ function test() {
function runInspectorTests(aInspector)
{
inspector = aInspector;
inspector.sidebar.once("computedview-ready", () => {
waitForView("computedview", () => {
info("Computed View ready");
inspector.sidebar.select("computedview");

Просмотреть файл

@ -51,7 +51,8 @@ function createDocument()
function selectNode(aInspector)
{
inspector = aInspector;
inspector.sidebar.once("ruleview-ready", function() {
waitForView("ruleview", () => {
ruleview = inspector.sidebar.getWindowForTab("ruleview").ruleview.view;
inspector.sidebar.select("ruleview");
inspector.selection.setNode(div, "test");

Просмотреть файл

@ -202,6 +202,15 @@ function getComputedView() {
return inspector.sidebar.getWindowForTab("computedview").computedview.view;
}
function waitForView(aName, aCallback) {
let inspector = getActiveInspector();
if (inspector.sidebar.getTab(aName)) {
aCallback();
} else {
inspector.sidebar.once(aName + "-ready", aCallback);
}
}
function synthesizeKeyFromKeyTag(aKeyId) {
let key = document.getElementById(aKeyId);
isnot(key, null, "Successfully retrieved the <key> node");

Просмотреть файл

@ -177,7 +177,7 @@ MarkupView.prototype = {
},
_hideBoxModel: function(forceHide) {
this._inspector.toolbox.highlighterUtils.unhighlight(forceHide);
return this._inspector.toolbox.highlighterUtils.unhighlight(forceHide);
},
_briefBoxModelTimer: null,
@ -1044,9 +1044,13 @@ MarkupView.prototype = {
* Tear down the markup panel.
*/
destroy: function() {
if (this._destroyer) {
return this._destroyer;
}
// Note that if the toolbox is closed, this will work fine, but will fail
// in case the browser is closed and will trigger a noSuchActor message.
this._hideBoxModel();
this._destroyer = this._hideBoxModel();
this._hoveredNode = null;
this._inspector.toolbox.off("picker-node-hovered", this._onToolboxPickerHover);
@ -1100,6 +1104,8 @@ MarkupView.prototype = {
this.tooltip.destroy();
this.tooltip = null;
return this._destroyer;
},
/**

Просмотреть файл

@ -53,7 +53,7 @@ function test() {
instance.setSize(500, 500);
openComputedView(onInspectorUIOpen);
openView("computedview", onInspectorUIOpen);
}
function onInspectorUIOpen(aInspector, aComputedView) {

Просмотреть файл

@ -49,7 +49,7 @@ function test() {
instance.setSize(500, 500);
openRuleView(onInspectorUIOpen);
openView("ruleview", onInspectorUIOpen);
}
function onInspectorUIOpen(aInspector, aRuleView) {

Просмотреть файл

@ -23,23 +23,19 @@ function openInspector(callback)
});
}
function openComputedView(callback)
function openView(name, callback)
{
openInspector(inspector => {
inspector.sidebar.once("computedview-ready", () => {
inspector.sidebar.select("computedview");
let ruleView = inspector.sidebar.getWindowForTab("computedview").computedview.view;
callback(inspector, ruleView);
})
});
}
function openRuleView(callback)
{
openInspector(inspector => {
inspector.sidebar.once("ruleview-ready", () => {
inspector.sidebar.select("ruleview");
let ruleView = inspector.sidebar.getWindowForTab("ruleview").ruleview.view;
callback(inspector, ruleView);
})
function onReady() {
inspector.sidebar.select(name);
let { view } = inspector.sidebar.getWindowForTab(name)[name];
callback(inspector, view);
}
if (inspector.sidebar.getTab(name)) {
onReady();
} else {
inspector.sidebar.once(name + "-ready", onReady);
}
});
}

Просмотреть файл

@ -25,11 +25,11 @@ function test() {
function createDocument() {
contentDoc.body.innerHTML = PAGE_CONTENT;
openRuleView((aInspector, aRuleView) => {
openView("ruleview", (aInspector, aRuleView) => {
inspector = aInspector;
ruleView = aRuleView;
inspector.sidebar.once("computedview-ready", () => {
computedView = inspector.sidebar.getWindowForTab("computedview").computedview.view;
openView("computedview", (_, aComputedView) => {
computedView = aComputedView;
startTests();
});
});
@ -66,7 +66,6 @@ function testComputedView() {
info("Testing computed view tooltip closes on new selection");
inspector.sidebar.select("computedview");
computedView = inspector.sidebar.getWindowForTab("computedview").computedview.view;
// Show the computed view tooltip
let tooltip = computedView.tooltip;

Просмотреть файл

@ -47,7 +47,7 @@ function highlightNode()
// Highlight a node.
let div = content.document.getElementsByTagName("div")[0];
inspector.selection.setNode(div);
inspector.selection.setNode(div, "test");
inspector.once("inspector-updated", () => {
is(inspector.selection.node, div, "selection matches the div element");
testRuleViewLink();

Просмотреть файл

@ -65,26 +65,31 @@ function getActiveInspector()
return gDevTools.getToolbox(target).getPanel("inspector");
}
function openRuleView(callback)
function openView(name, callback)
{
openInspector(inspector => {
inspector.sidebar.once("ruleview-ready", () => {
inspector.sidebar.select("ruleview");
let ruleView = inspector.sidebar.getWindowForTab("ruleview").ruleview.view;
callback(inspector, ruleView);
})
function onReady() {
inspector.sidebar.select(name);
let { view } = inspector.sidebar.getWindowForTab(name)[name];
callback(inspector, view);
}
if (inspector.sidebar.getTab(name)) {
onReady();
} else {
inspector.sidebar.once(name + "-ready", onReady);
}
});
}
function openRuleView(callback)
{
openView("ruleview", callback);
}
function openComputedView(callback)
{
openInspector(inspector => {
inspector.sidebar.once("computedview-ready", () => {
inspector.sidebar.select("computedview");
let computedView = inspector.sidebar.getWindowForTab("computedview").computedview.view;
callback(inspector, computedView);
})
});
openView("computedview", callback);
}
/**

Просмотреть файл

@ -30,6 +30,7 @@
.widget-overflow-list .toolbarbutton-1 > .toolbarbutton-menubutton-button {
-moz-appearance: none;
border: 0;
-moz-margin-start: 3px;
}
.widget-overflow-list .toolbarbutton-1 > .toolbarbutton-menubutton-dropmarker {

Просмотреть файл

@ -506,7 +506,7 @@ toolbar .toolbarbutton-1[checked]:not(:active):hover {
transition: background-color 250ms;
}
.toolbarbutton-1[type="menu-button"] {
.toolbarbutton-1[type="menu-button"]:not([overflowedItem=true]) {
padding: 0;
}

Просмотреть файл

@ -946,7 +946,7 @@ toolbaritem[overflowedItem=true],
-moz-box-orient: horizontal;
}
.widget-overflow-list .toolbarbutton-1 > .toolbarbutton-text,
.widget-overflow-list .toolbarbutton-1:not(.toolbarbutton-combined) > .toolbarbutton-text,
.widget-overflow-list .toolbarbutton-1 > .toolbarbutton-menubutton-button > .toolbarbutton-text {
text-align: start;
-moz-padding-start: .5em;

Просмотреть файл

@ -7409,6 +7409,14 @@ if test -n "$MOZ_AUDIO_CHANNEL_MANAGER"; then
fi
AC_SUBST(MOZ_AUDIO_CHANNEL_MANAGER)
dnl ========================================================
dnl = Enable Support for Firefox Accounts in B2G
dnl ========================================================
if test -n "$MOZ_SERVICES_FXACCOUNTS"; then
AC_DEFINE(MOZ_SERVICES_FXACCOUNTS)
fi
AC_SUBST(MOZ_SERVICES_FXACCOUNTS)
dnl ========================================================
dnl = Support for demangling undefined symbols
dnl ========================================================

Просмотреть файл

@ -22,7 +22,7 @@ Cu.import("resource://gre/modules/Services.jsm");
Cu.import("resource://gre/modules/IndexedDBHelper.jsm");
Cu.import("resource://gre/modules/Timer.jsm");
Cu.import("resource://gre/modules/Preferences.jsm");
Cu.import("resource://gre/modules/commonjs/sdk/core/promise.js");
Cu.import("resource://gre/modules/Promise.jsm");
Cu.importGlobalProperties(["indexedDB"]);
XPCOMUtils.defineLazyModuleGetter(this, "AlarmService",

Просмотреть файл

@ -175,7 +175,7 @@ public class DynamicPanel extends HomeFragment {
bundle.putParcelable(DATASET_REQUEST, request);
// Ensure one loader per dataset
final int loaderId = generateLoaderId(request.datasetId);
final int loaderId = generateLoaderId(request.getDatasetId());
getLoaderManager().restartLoader(loaderId, bundle, mLoaderCallbacks);
}
@ -218,12 +218,12 @@ public class DynamicPanel extends HomeFragment {
final String[] selectionArgs;
// Null represents the root filter
if (mRequest.filter == null) {
if (mRequest.getFilter() == null) {
selection = DBUtils.concatenateWhere(HomeItems.DATASET_ID + " = ?", HomeItems.FILTER + " IS NULL");
selectionArgs = new String[] { mRequest.datasetId };
selectionArgs = new String[] { mRequest.getDatasetId() };
} else {
selection = DBUtils.concatenateWhere(HomeItems.DATASET_ID + " = ?", HomeItems.FILTER + " = ?");
selectionArgs = new String[] { mRequest.datasetId, mRequest.filter };
selectionArgs = new String[] { mRequest.getDatasetId(), mRequest.getFilter() };
}
// XXX: You can use CONTENT_FAKE_URI for development to pull items from fake_home_items.json.
@ -256,7 +256,7 @@ public class DynamicPanel extends HomeFragment {
final DatasetRequest request = getRequestFromLoader(loader);
Log.d(LOGTAG, "Resetting loader for request: " + request);
if (mLayout != null) {
mLayout.releaseDataset(request.datasetId);
mLayout.releaseDataset(request.getDatasetId());
}
}

Просмотреть файл

@ -16,6 +16,7 @@ import org.mozilla.gecko.animation.ViewHelper;
import org.mozilla.gecko.gfx.BitmapUtils;
import org.mozilla.gecko.util.GeckoEventListener;
import org.mozilla.gecko.util.ThreadUtils;
import org.mozilla.gecko.widget.EllipsisTextView;
import android.content.Context;
import android.graphics.drawable.Drawable;
@ -53,7 +54,9 @@ public class HomeBanner extends LinearLayout
// switches back to the default page.
private boolean mUserSwipedDown = false;
private final TextView mTextView;
// We must use this custom TextView to address an issue on 2.3 and lower where ellipsized text
// will not wrap more than 2 lines.
private final EllipsisTextView mTextView;
private final ImageView mIconView;
// Listener that gets called when the banner is dismissed from the close button.
@ -72,7 +75,7 @@ public class HomeBanner extends LinearLayout
LayoutInflater.from(context).inflate(R.layout.home_banner_content, this);
mTextView = (TextView) findViewById(R.id.text);
mTextView = (EllipsisTextView) findViewById(R.id.text);
mIconView = (ImageView) findViewById(R.id.icon);
}
@ -167,7 +170,7 @@ public class HomeBanner extends LinearLayout
// Store the current message id to pass back to JS in the view's OnClickListener.
setTag(id);
mTextView.setText(Html.fromHtml(text));
mTextView.setOriginalText(Html.fromHtml(text));
BitmapUtils.getDrawable(getContext(), iconURI, new BitmapUtils.BitmapLoader() {
@Override

Просмотреть файл

@ -583,17 +583,20 @@ public final class HomeConfig {
private final String mDatasetId;
private final ItemType mItemType;
private final ItemHandler mItemHandler;
private final String mBackImageUrl;
private static final String JSON_KEY_TYPE = "type";
private static final String JSON_KEY_DATASET = "dataset";
private static final String JSON_KEY_ITEM_TYPE = "itemType";
private static final String JSON_KEY_ITEM_HANDLER = "itemHandler";
private static final String JSON_KEY_BACK_IMAGE_URL = "backImageUrl";
public ViewConfig(JSONObject json) throws JSONException, IllegalArgumentException {
mType = ViewType.fromId(json.getString(JSON_KEY_TYPE));
mDatasetId = json.getString(JSON_KEY_DATASET);
mItemType = ItemType.fromId(json.getString(JSON_KEY_ITEM_TYPE));
mItemHandler = ItemHandler.fromId(json.getString(JSON_KEY_ITEM_HANDLER));
mBackImageUrl = json.optString(JSON_KEY_BACK_IMAGE_URL, null);
validate();
}
@ -604,6 +607,7 @@ public final class HomeConfig {
mDatasetId = in.readString();
mItemType = (ItemType) in.readParcelable(getClass().getClassLoader());
mItemHandler = (ItemHandler) in.readParcelable(getClass().getClassLoader());
mBackImageUrl = in.readString();
validate();
}
@ -613,15 +617,18 @@ public final class HomeConfig {
mDatasetId = viewConfig.mDatasetId;
mItemType = viewConfig.mItemType;
mItemHandler = viewConfig.mItemHandler;
mBackImageUrl = viewConfig.mBackImageUrl;
validate();
}
public ViewConfig(ViewType type, String datasetId, ItemType itemType, ItemHandler itemHandler) {
public ViewConfig(ViewType type, String datasetId, ItemType itemType,
ItemHandler itemHandler, String backImageUrl) {
mType = type;
mDatasetId = datasetId;
mItemType = itemType;
mItemHandler = itemHandler;
mBackImageUrl = backImageUrl;
validate();
}
@ -660,6 +667,10 @@ public final class HomeConfig {
return mItemHandler;
}
public String getBackImageUrl() {
return mBackImageUrl;
}
public JSONObject toJSON() throws JSONException {
final JSONObject json = new JSONObject();
@ -668,6 +679,10 @@ public final class HomeConfig {
json.put(JSON_KEY_ITEM_TYPE, mItemType.toString());
json.put(JSON_KEY_ITEM_HANDLER, mItemHandler.toString());
if (!TextUtils.isEmpty(mBackImageUrl)) {
json.put(JSON_KEY_BACK_IMAGE_URL, mBackImageUrl);
}
return json;
}
@ -682,6 +697,7 @@ public final class HomeConfig {
dest.writeString(mDatasetId);
dest.writeParcelable(mItemType, 0);
dest.writeParcelable(mItemHandler, 0);
dest.writeString(mBackImageUrl);
}
public static final Creator<ViewConfig> CREATOR = new Creator<ViewConfig>() {

Просмотреть файл

@ -0,0 +1,43 @@
/* -*- Mode: Java; c-basic-offset: 4; tab-width: 20; indent-tabs-mode: nil; -*-
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
package org.mozilla.gecko.home;
import org.mozilla.gecko.R;
import org.mozilla.gecko.home.PanelLayout.FilterDetail;
import android.content.Context;
import android.text.TextUtils;
import android.view.LayoutInflater;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.TextView;
import com.squareup.picasso.Picasso;
class PanelBackItemView extends LinearLayout {
private final TextView title;
public PanelBackItemView(Context context, String backImageUrl) {
super(context);
LayoutInflater.from(context).inflate(R.layout.panel_back_item, this);
setOrientation(HORIZONTAL);
title = (TextView) findViewById(R.id.title);
final ImageView image = (ImageView) findViewById(R.id.image);
Picasso.with(getContext())
.load(backImageUrl)
.placeholder(R.drawable.folder_up)
.into(image);
}
public void updateFromFilter(FilterDetail filter) {
final String backText = getResources()
.getString(R.string.home_move_up_to_filter, filter.title);
title.setText(backText);
}
}

Просмотреть файл

@ -13,6 +13,8 @@ import org.mozilla.gecko.home.HomeConfig.ItemHandler;
import org.mozilla.gecko.home.HomeConfig.ViewConfig;
import org.mozilla.gecko.home.HomePager.OnUrlOpenListener;
import org.mozilla.gecko.home.PanelLayout.DatasetBacked;
import org.mozilla.gecko.home.PanelLayout.FilterManager;
import org.mozilla.gecko.home.PanelLayout.OnItemOpenListener;
import org.mozilla.gecko.home.PanelLayout.PanelView;
import android.content.Context;
@ -27,15 +29,15 @@ public class PanelGridView extends GridView
private final ViewConfig mViewConfig;
private final PanelViewAdapter mAdapter;
private PanelViewUrlHandler mUrlHandler;
private PanelViewItemHandler mItemHandler;
public PanelGridView(Context context, ViewConfig viewConfig) {
super(context, null, R.attr.panelGridViewStyle);
mViewConfig = viewConfig;
mUrlHandler = new PanelViewUrlHandler(viewConfig);
mItemHandler = new PanelViewItemHandler(viewConfig);
mAdapter = new PanelViewAdapter(context, viewConfig.getItemType());
mAdapter = new PanelViewAdapter(context, viewConfig);
setAdapter(mAdapter);
setOnItemClickListener(new PanelGridItemClickListener());
@ -44,7 +46,7 @@ public class PanelGridView extends GridView
@Override
public void onDetachedFromWindow() {
super.onDetachedFromWindow();
mUrlHandler.setOnUrlOpenListener(null);
mItemHandler.setOnItemOpenListener(null);
}
@Override
@ -53,14 +55,20 @@ public class PanelGridView extends GridView
}
@Override
public void setOnUrlOpenListener(OnUrlOpenListener listener) {
mUrlHandler.setOnUrlOpenListener(listener);
public void setOnItemOpenListener(OnItemOpenListener listener) {
mItemHandler.setOnItemOpenListener(listener);
}
@Override
public void setFilterManager(FilterManager filterManager) {
mAdapter.setFilterManager(filterManager);
mItemHandler.setFilterManager(filterManager);
}
private class PanelGridItemClickListener implements AdapterView.OnItemClickListener {
@Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
mUrlHandler.openUrlAtPosition(mAdapter.getCursor(), position);
mItemHandler.openItemAtPosition(mAdapter.getCursor(), position);
}
}
}

Просмотреть файл

@ -5,7 +5,9 @@
package org.mozilla.gecko.home;
import org.mozilla.gecko.R;
import org.mozilla.gecko.home.HomePager.OnUrlOpenListener;
import org.mozilla.gecko.home.HomeConfig.ItemHandler;
import org.mozilla.gecko.home.HomeConfig.PanelConfig;
import org.mozilla.gecko.home.HomeConfig.ViewConfig;
import org.mozilla.gecko.util.StringUtils;
@ -20,7 +22,6 @@ import android.view.KeyEvent;
import android.view.View;
import android.widget.FrameLayout;
import java.util.Deque;
import java.util.EnumSet;
import java.util.LinkedList;
import java.util.Map;
@ -65,6 +66,7 @@ abstract class PanelLayout extends FrameLayout {
private static final String LOGTAG = "GeckoPanelLayout";
protected final Map<View, ViewState> mViewStateMap;
private final PanelConfig mPanelConfig;
private final DatasetHandler mDatasetHandler;
private final OnUrlOpenListener mUrlOpenListener;
@ -74,6 +76,7 @@ abstract class PanelLayout extends FrameLayout {
*/
public interface DatasetBacked {
public void setDataset(Cursor cursor);
public void setFilterManager(FilterManager manager);
}
/**
@ -81,17 +84,68 @@ abstract class PanelLayout extends FrameLayout {
* filter for queries on the database.
*/
public static class DatasetRequest implements Parcelable {
public final String datasetId;
public final String filter;
public enum Type implements Parcelable {
DATASET_LOAD,
FILTER_PUSH,
FILTER_POP;
private DatasetRequest(Parcel in) {
this.datasetId = in.readString();
this.filter = in.readString();
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeInt(ordinal());
}
public static final Creator<Type> CREATOR = new Creator<Type>() {
@Override
public Type createFromParcel(final Parcel source) {
return Type.values()[source.readInt()];
}
@Override
public Type[] newArray(final int size) {
return new Type[size];
}
};
}
public DatasetRequest(String datasetId, String filter) {
this.datasetId = datasetId;
this.filter = filter;
private final Type mType;
private final String mDatasetId;
private final FilterDetail mFilterDetail;
private DatasetRequest(Parcel in) {
this.mType = (Type) in.readParcelable(getClass().getClassLoader());
this.mDatasetId = in.readString();
this.mFilterDetail = (FilterDetail) in.readParcelable(getClass().getClassLoader());
}
public DatasetRequest(String datasetId, FilterDetail filterDetail) {
this(Type.DATASET_LOAD, datasetId, filterDetail);
}
public DatasetRequest(Type type, String datasetId, FilterDetail filterDetail) {
this.mType = type;
this.mDatasetId = datasetId;
this.mFilterDetail = filterDetail;
}
public Type getType() {
return mType;
}
public String getDatasetId() {
return mDatasetId;
}
public String getFilter() {
return (mFilterDetail != null ? mFilterDetail.filter : null);
}
public FilterDetail getFilterDetail() {
return mFilterDetail;
}
@Override
@ -101,12 +155,13 @@ abstract class PanelLayout extends FrameLayout {
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeString(datasetId);
dest.writeString(filter);
dest.writeParcelable(mType, 0);
dest.writeString(mDatasetId);
dest.writeParcelable(mFilterDetail, 0);
}
public String toString() {
return "{dataset: " + datasetId + ", filter: " + filter + "}";
return "{type: " + mType + " dataset: " + mDatasetId + ", filter: " + mFilterDetail + "}";
}
public static final Creator<DatasetRequest> CREATOR = new Creator<DatasetRequest>() {
@ -140,12 +195,20 @@ abstract class PanelLayout extends FrameLayout {
}
public interface PanelView {
public void setOnUrlOpenListener(OnUrlOpenListener listener);
public void setOnItemOpenListener(OnItemOpenListener listener);
public void setOnKeyListener(OnKeyListener listener);
}
public interface FilterManager {
public FilterDetail getPreviousFilter();
public boolean canGoBack();
public void goBack();
}
public PanelLayout(Context context, PanelConfig panelConfig, DatasetHandler datasetHandler, OnUrlOpenListener urlOpenListener) {
super(context);
mViewStateMap = new WeakHashMap<View, ViewState>();
mPanelConfig = panelConfig;
mDatasetHandler = datasetHandler;
mUrlOpenListener = urlOpenListener;
}
@ -157,7 +220,7 @@ abstract class PanelLayout extends FrameLayout {
*/
public final void deliverDataset(DatasetRequest request, Cursor cursor) {
Log.d(LOGTAG, "Delivering request: " + request);
updateViewsWithDataset(request.datasetId, cursor);
updateViewsFromRequest(request, cursor);
}
/**
@ -165,8 +228,8 @@ abstract class PanelLayout extends FrameLayout {
* existing panel views.
*/
public final void releaseDataset(String datasetId) {
Log.d(LOGTAG, "Resetting dataset: " + datasetId);
updateViewsWithDataset(datasetId, null);
Log.d(LOGTAG, "Releasing dataset: " + datasetId);
releaseViewsWithDataset(datasetId);
}
/**
@ -214,8 +277,14 @@ abstract class PanelLayout extends FrameLayout {
// TODO: Push initial filter here onto ViewState
mViewStateMap.put(view, state);
((PanelView) view).setOnUrlOpenListener(new PanelUrlOpenListener(state));
view.setOnKeyListener(new PanelKeyListener(state));
PanelView panelView = (PanelView) view;
panelView.setOnItemOpenListener(new PanelOnItemOpenListener(state));
panelView.setOnKeyListener(new PanelKeyListener(state));
if (view instanceof DatasetBacked) {
DatasetBacked datasetBacked = (DatasetBacked) view;
datasetBacked.setFilterManager(new PanelFilterManager(state));
}
return view;
}
@ -236,18 +305,39 @@ abstract class PanelLayout extends FrameLayout {
}
}
private void updateViewsWithDataset(String datasetId, Cursor cursor) {
private void updateViewsFromRequest(DatasetRequest request, Cursor cursor) {
for (Map.Entry<View, ViewState> entry : mViewStateMap.entrySet()) {
final ViewState detail = entry.getValue();
// Update any views associated with the given dataset ID
if (TextUtils.equals(detail.getDatasetId(), datasetId)) {
if (TextUtils.equals(detail.getDatasetId(), request.getDatasetId())) {
switch (request.getType()) {
case FILTER_PUSH:
detail.pushFilter(request.getFilterDetail());
break;
case FILTER_POP:
detail.popFilter();
break;
}
final View view = entry.getKey();
maybeSetDataset(view, cursor);
}
}
}
private void releaseViewsWithDataset(String datasetId) {
for (Map.Entry<View, ViewState> entry : mViewStateMap.entrySet()) {
final ViewState detail = entry.getValue();
// Release the cursor on views associated with the given dataset ID
if (TextUtils.equals(detail.getDatasetId(), datasetId)) {
final View view = entry.getKey();
maybeSetDataset(view, null);
}
}
}
private void maybeSetDataset(View view, Cursor cursor) {
if (view instanceof DatasetBacked) {
final DatasetBacked dsb = (DatasetBacked) view;
@ -266,9 +356,9 @@ abstract class PanelLayout extends FrameLayout {
* Represents a 'live' instance of a panel view associated with
* the {@code PanelLayout}. Is responsible for tracking the history stack of filters.
*/
protected static class ViewState {
protected class ViewState {
private final ViewConfig mViewConfig;
private Deque<String> mFilterStack;
private LinkedList<FilterDetail> mFilterStack;
public ViewState(ViewConfig viewConfig) {
mViewConfig = viewConfig;
@ -278,10 +368,14 @@ abstract class PanelLayout extends FrameLayout {
return mViewConfig.getDatasetId();
}
public ItemHandler getItemHandler() {
return mViewConfig.getItemHandler();
}
/**
* Used to find the current filter that this view is displaying, or null if none.
* Get the current filter that this view is displaying, or null if none.
*/
public String getCurrentFilter() {
public FilterDetail getCurrentFilter() {
if (mFilterStack == null) {
return null;
} else {
@ -289,32 +383,94 @@ abstract class PanelLayout extends FrameLayout {
}
}
/**
* Get the previous filter that this view was displaying, or null if none.
*/
public FilterDetail getPreviousFilter() {
if (!canPopFilter()) {
return null;
}
return mFilterStack.get(1);
}
/**
* Adds a filter to the history stack for this view.
*/
public void pushFilter(String filter) {
public void pushFilter(FilterDetail filter) {
if (mFilterStack == null) {
mFilterStack = new LinkedList<String>();
mFilterStack = new LinkedList<FilterDetail>();
// Initialize with a null filter.
// TODO: use initial filter from ViewConfig
mFilterStack.push(new FilterDetail(null, mPanelConfig.getTitle()));
}
mFilterStack.push(filter);
}
public String popFilter() {
if (getCurrentFilter() != null) {
mFilterStack.pop();
/**
* Remove the most recent filter from the stack.
*
* @return whether the filter was popped
*/
public boolean popFilter() {
if (!canPopFilter()) {
return false;
}
return getCurrentFilter();
mFilterStack.pop();
return true;
}
public boolean canPopFilter() {
return (mFilterStack != null && mFilterStack.size() > 1);
}
}
static class FilterDetail implements Parcelable {
final String filter;
final String title;
private FilterDetail(Parcel in) {
this.filter = in.readString();
this.title = in.readString();
}
public FilterDetail(String filter, String title) {
this.filter = filter;
this.title = title;
}
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeString(filter);
dest.writeString(title);
}
public static final Creator<FilterDetail> CREATOR = new Creator<FilterDetail>() {
public FilterDetail createFromParcel(Parcel in) {
return new FilterDetail(in);
}
public FilterDetail[] newArray(int size) {
return new FilterDetail[size];
}
};
}
/**
* Pushes filter to {@code ViewState}'s stack and makes request for new filter value.
*/
private void pushFilterOnView(ViewState viewState, String filter) {
viewState.pushFilter(filter);
mDatasetHandler.requestDataset(new DatasetRequest(viewState.getDatasetId(), filter));
private void pushFilterOnView(ViewState viewState, FilterDetail filterDetail) {
final String datasetId = viewState.getDatasetId();
mDatasetHandler.requestDataset(
new DatasetRequest(DatasetRequest.Type.FILTER_PUSH, datasetId, filterDetail));
}
/**
@ -323,33 +479,39 @@ abstract class PanelLayout extends FrameLayout {
* @return whether the filter has changed
*/
private boolean popFilterOnView(ViewState viewState) {
String currentFilter = viewState.getCurrentFilter();
String filter = viewState.popFilter();
if (!TextUtils.equals(currentFilter, filter)) {
mDatasetHandler.requestDataset(new DatasetRequest(viewState.getDatasetId(), filter));
if (viewState.canPopFilter()) {
final FilterDetail filterDetail = viewState.getPreviousFilter();
final String datasetId = viewState.getDatasetId();
mDatasetHandler.requestDataset(
new DatasetRequest(DatasetRequest.Type.FILTER_POP, datasetId, filterDetail));
return true;
} else {
return false;
}
}
/**
* Custom listener so that we can intercept any filter URLs and make a new dataset request
* rather than forwarding them to the default listener.
*/
private class PanelUrlOpenListener implements OnUrlOpenListener {
public interface OnItemOpenListener {
public void onItemOpen(String url, String title);
}
private class PanelOnItemOpenListener implements OnItemOpenListener {
private ViewState mViewState;
public PanelUrlOpenListener(ViewState viewState) {
public PanelOnItemOpenListener(ViewState viewState) {
mViewState = viewState;
}
@Override
public void onUrlOpen(String url, EnumSet<Flags> flags) {
public void onItemOpen(String url, String title) {
if (StringUtils.isFilterUrl(url)) {
pushFilterOnView(mViewState, StringUtils.getFilterFromUrl(url));
FilterDetail filterDetail = new FilterDetail(StringUtils.getFilterFromUrl(url), title);
pushFilterOnView(mViewState, filterDetail);
} else {
EnumSet<OnUrlOpenListener.Flags> flags = EnumSet.noneOf(OnUrlOpenListener.Flags.class);
if (mViewState.getItemHandler() == ItemHandler.INTENT) {
flags.add(OnUrlOpenListener.Flags.OPEN_WITH_INTENT);
}
mUrlOpenListener.onUrlOpen(url, flags);
}
}
@ -371,4 +533,27 @@ abstract class PanelLayout extends FrameLayout {
return false;
}
}
private class PanelFilterManager implements FilterManager {
private final ViewState mViewState;
public PanelFilterManager(ViewState viewState) {
mViewState = viewState;
}
@Override
public FilterDetail getPreviousFilter() {
return mViewState.getPreviousFilter();
}
@Override
public boolean canGoBack() {
return mViewState.canPopFilter();
}
@Override
public void goBack() {
popFilterOnView(mViewState);
}
}
}

Просмотреть файл

@ -12,6 +12,8 @@ import org.mozilla.gecko.home.HomeConfig.ItemHandler;
import org.mozilla.gecko.home.HomeConfig.ViewConfig;
import org.mozilla.gecko.home.HomePager.OnUrlOpenListener;
import org.mozilla.gecko.home.PanelLayout.DatasetBacked;
import org.mozilla.gecko.home.PanelLayout.FilterManager;
import org.mozilla.gecko.home.PanelLayout.OnItemOpenListener;
import org.mozilla.gecko.home.PanelLayout.PanelView;
import android.content.Context;
@ -27,20 +29,26 @@ public class PanelListView extends HomeListView
private final PanelViewAdapter mAdapter;
private final ViewConfig mViewConfig;
private final PanelViewUrlHandler mUrlHandler;
private final PanelViewItemHandler mItemHandler;
public PanelListView(Context context, ViewConfig viewConfig) {
super(context);
mViewConfig = viewConfig;
mUrlHandler = new PanelViewUrlHandler(viewConfig);
mItemHandler = new PanelViewItemHandler(viewConfig);
mAdapter = new PanelViewAdapter(context, viewConfig.getItemType());
mAdapter = new PanelViewAdapter(context, viewConfig);
setAdapter(mAdapter);
setOnItemClickListener(new PanelListItemClickListener());
}
@Override
public void onDetachedFromWindow() {
super.onDetachedFromWindow();
mItemHandler.setOnItemOpenListener(null);
}
@Override
public void setDataset(Cursor cursor) {
Log.d(LOGTAG, "Setting dataset: " + mViewConfig.getDatasetId());
@ -48,15 +56,20 @@ public class PanelListView extends HomeListView
}
@Override
public void setOnUrlOpenListener(OnUrlOpenListener listener) {
super.setOnUrlOpenListener(listener);
mUrlHandler.setOnUrlOpenListener(listener);
public void setOnItemOpenListener(OnItemOpenListener listener) {
mItemHandler.setOnItemOpenListener(listener);
}
@Override
public void setFilterManager(FilterManager filterManager) {
mAdapter.setFilterManager(filterManager);
mItemHandler.setFilterManager(filterManager);
}
private class PanelListItemClickListener implements AdapterView.OnItemClickListener {
@Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
mUrlHandler.openUrlAtPosition(mAdapter.getCursor(), position);
mItemHandler.openItemAtPosition(mAdapter.getCursor(), position);
}
}
}

Просмотреть файл

@ -6,6 +6,10 @@
package org.mozilla.gecko.home;
import org.mozilla.gecko.home.HomeConfig.ItemType;
import org.mozilla.gecko.home.HomeConfig.ViewConfig;
import org.mozilla.gecko.home.PanelLayout.FilterManager;
import org.mozilla.gecko.R;
import android.content.Context;
import android.database.Cursor;
@ -14,23 +18,96 @@ import android.view.View;
import android.view.ViewGroup;
class PanelViewAdapter extends CursorAdapter {
private final Context mContext;
private final ItemType mItemType;
private static final int VIEW_TYPE_ITEM = 0;
private static final int VIEW_TYPE_BACK = 1;
public PanelViewAdapter(Context context, ItemType itemType) {
private final Context mContext;
private final ViewConfig mViewConfig;
private FilterManager mFilterManager;
public PanelViewAdapter(Context context, ViewConfig viewConfig) {
super(context, null, 0);
mContext = context;
mItemType = itemType;
mViewConfig = viewConfig;
}
public void setFilterManager(FilterManager manager) {
mFilterManager = manager;
}
@Override
public void bindView(View view, Context context, Cursor cursor) {
public final int getViewTypeCount() {
return 2;
}
@Override
public int getCount() {
return super.getCount() + (isShowingBack() ? 1 : 0);
}
@Override
public int getItemViewType(int position) {
if (isShowingBack() && position == 0) {
return VIEW_TYPE_BACK;
} else {
return VIEW_TYPE_ITEM;
}
}
@Override
public final View getView(int position, View convertView, ViewGroup parent) {
if (convertView == null) {
convertView = newView(parent.getContext(), position, parent);
}
bindView(convertView, position);
return convertView;
}
private View newView(Context context, int position, ViewGroup parent) {
if (getItemViewType(position) == VIEW_TYPE_BACK) {
return new PanelBackItemView(context, mViewConfig.getBackImageUrl());
} else {
return PanelItemView.create(context, mViewConfig.getItemType());
}
}
private void bindView(View view, int position) {
if (isShowingBack()) {
if (position == 0) {
final PanelBackItemView item = (PanelBackItemView) view;
item.updateFromFilter(mFilterManager.getPreviousFilter());
return;
}
position--;
}
final Cursor cursor = getCursor(position);
final PanelItemView item = (PanelItemView) view;
item.updateFromCursor(cursor);
}
private boolean isShowingBack() {
return (mFilterManager != null ? mFilterManager.canGoBack() : false);
}
private final Cursor getCursor(int position) {
final Cursor cursor = getCursor();
if (cursor == null || !cursor.moveToPosition(position)) {
throw new IllegalStateException("Couldn't move cursor to position " + position);
}
return cursor;
}
@Override
public View newView(Context context, Cursor cursor, ViewGroup parent) {
return PanelItemView.create(mContext, mItemType);
public final void bindView(View view, Context context, Cursor cursor) {
// Do nothing.
}
@Override
public final View newView(Context context, Cursor cursor, ViewGroup parent) {
return null;
}
}

Просмотреть файл

@ -0,0 +1,64 @@
/* -*- Mode: Java; c-basic-offset: 4; tab-width: 20; indent-tabs-mode: nil; -*-
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
package org.mozilla.gecko.home;
import org.mozilla.gecko.db.BrowserContract.HomeItems;
import org.mozilla.gecko.home.HomeConfig.ViewConfig;
import org.mozilla.gecko.home.HomePager.OnUrlOpenListener;
import org.mozilla.gecko.home.PanelLayout.FilterManager;
import org.mozilla.gecko.home.PanelLayout.OnItemOpenListener;
import android.database.Cursor;
import java.util.EnumSet;
class PanelViewItemHandler {
private final ViewConfig mViewConfig;
private OnItemOpenListener mItemOpenListener;
private FilterManager mFilterManager;
public PanelViewItemHandler(ViewConfig viewConfig) {
mViewConfig = viewConfig;
}
public void setOnItemOpenListener(OnItemOpenListener listener) {
mItemOpenListener = listener;
}
public void setFilterManager(FilterManager manager) {
mFilterManager = manager;
}
/**
* If item at this position is a back item, perform the go back action via the
* {@code FilterManager}. Otherwise, prepare the url to be opened by the
* {@code OnUrlOpenListener}.
*/
public void openItemAtPosition(Cursor cursor, int position) {
if (mFilterManager != null && mFilterManager.canGoBack()) {
if (position == 0) {
mFilterManager.goBack();
return;
}
position--;
}
if (cursor == null || !cursor.moveToPosition(position)) {
throw new IllegalStateException("Couldn't move cursor to position " + position);
}
int urlIndex = cursor.getColumnIndexOrThrow(HomeItems.URL);
final String url = cursor.getString(urlIndex);
int titleIndex = cursor.getColumnIndexOrThrow(HomeItems.TITLE);
final String title = cursor.getString(titleIndex);
if (mItemOpenListener != null) {
mItemOpenListener.onItemOpen(url, title);
}
}
}

Просмотреть файл

@ -1,46 +0,0 @@
/* -*- Mode: Java; c-basic-offset: 4; tab-width: 20; indent-tabs-mode: nil; -*-
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
package org.mozilla.gecko.home;
import org.mozilla.gecko.db.BrowserContract.HomeItems;
import org.mozilla.gecko.home.HomeConfig.ItemHandler;
import org.mozilla.gecko.home.HomeConfig.ViewConfig;
import org.mozilla.gecko.home.HomePager.OnUrlOpenListener;
import android.database.Cursor;
import java.util.EnumSet;
class PanelViewUrlHandler {
private final ViewConfig mViewConfig;
private OnUrlOpenListener mUrlOpenListener;
public PanelViewUrlHandler(ViewConfig viewConfig) {
mViewConfig = viewConfig;
}
public void setOnUrlOpenListener(OnUrlOpenListener listener) {
mUrlOpenListener = listener;
}
public void openUrlAtPosition(Cursor cursor, int position) {
if (cursor == null || !cursor.moveToPosition(position)) {
throw new IllegalStateException("Couldn't move cursor to position " + position);
}
int urlIndex = cursor.getColumnIndexOrThrow(HomeItems.URL);
final String url = cursor.getString(urlIndex);
EnumSet<OnUrlOpenListener.Flags> flags = EnumSet.noneOf(OnUrlOpenListener.Flags.class);
if (mViewConfig.getItemHandler() == ItemHandler.INTENT) {
flags.add(OnUrlOpenListener.Flags.OPEN_WITH_INTENT);
}
if (mUrlOpenListener != null) {
mUrlOpenListener.onUrlOpen(url, flags);
}
}
}

Просмотреть файл

@ -339,6 +339,10 @@ size. -->
is no data to show in an about:home panel that was created by an add-on. -->
<!ENTITY home_default_empty "No content could be found for this panel.">
<!-- Localization note (home_move_up_to_filter): The variable is replaced by the name of the
previous location in the navigation, such as the previous folder -->
<!ENTITY home_move_up_to_filter "Up to &formatS;">
<!ENTITY pin_site_dialog_hint "Enter a search keyword">
<!ENTITY filepicker_title "Choose File">
@ -418,6 +422,12 @@ just addresses the organization to follow, e.g. "This site is run by " -->
<!ENTITY exit_guest_session_title "&brandShortName; will now restart">
<!ENTITY exit_guest_session_text "The browsing data from this session will be deleted.">
<!-- Miscellaneous -->
<!-- LOCALIZATION NOTE (ellipsis): This text is appended to a piece of text that does not fit in the
designated space. Use the unicode ellipsis char, \u2026, or use "..." if \u2026 doesn't suit
traditions in your locale. -->
<!ENTITY ellipsis "…">
<!-- These are only used for accessiblity for the done and overflow-menu buttons in the actionbar.
They are never shown to users -->
<!ENTITY actionbar_menu "Menu">

Просмотреть файл

@ -246,13 +246,14 @@ gbjar.sources += [
'home/LastTabsPanel.java',
'home/MostRecentPanel.java',
'home/MultiTypeCursorAdapter.java',
'home/PanelBackItemView.java',
'home/PanelGridView.java',
'home/PanelItemView.java',
'home/PanelLayout.java',
'home/PanelListView.java',
'home/PanelManager.java',
'home/PanelViewAdapter.java',
'home/PanelViewUrlHandler.java',
'home/PanelViewItemHandler.java',
'home/PinSiteDialog.java',
'home/ReadingListPanel.java',
'home/SearchEngine.java',
@ -381,6 +382,7 @@ gbjar.sources += [
'widget/DateTimePicker.java',
'widget/Divider.java',
'widget/DoorHanger.java',
'widget/EllipsisTextView.java',
'widget/FaviconView.java',
'widget/FlowLayout.java',
'widget/GeckoActionProvider.java',

Двоичные данные
mobile/android/base/resources/drawable-hdpi/folder_up.png Normal file

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 3.4 KiB

Двоичные данные
mobile/android/base/resources/drawable-mdpi/folder_up.png Normal file

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 3.4 KiB

Двоичные данные
mobile/android/base/resources/drawable-xhdpi/folder_up.png Normal file

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 3.6 KiB

Просмотреть файл

@ -1 +0,0 @@
blassey@flyingfox.local.319

Просмотреть файл

@ -3,7 +3,8 @@
- 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/. -->
<merge xmlns:android="http://schemas.android.com/apk/res/android">
<merge xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:gecko="http://schemas.android.com/apk/res-auto">
<ImageView android:id="@+id/icon"
android:layout_width="48dip"
@ -11,19 +12,18 @@
android:layout_marginLeft="10dp"
android:scaleType="centerInside"/>
<TextView android:id="@+id/text"
android:layout_width="0dip"
android:layout_height="fill_parent"
android:layout_weight="1"
android:layout_marginLeft="10dp"
android:paddingTop="7dp"
android:paddingBottom="7dp"
android:textAppearance="@style/TextAppearance.Widget.HomeBanner"
android:layout_gravity="bottom"
android:singleLine="false"
android:maxLines="3"
android:ellipsize="end"
android:gravity="center_vertical"/>
<org.mozilla.gecko.widget.EllipsisTextView
android:id="@+id/text"
android:layout_width="0dip"
android:layout_height="fill_parent"
android:layout_weight="1"
android:layout_marginLeft="10dp"
android:paddingTop="7dp"
android:paddingBottom="7dp"
android:textAppearance="@style/TextAppearance.Widget.HomeBanner"
android:layout_gravity="bottom"
android:gravity="center_vertical"
gecko:ellipsizeAtLine="3"/>
<ImageButton android:id="@+id/close"
android:layout_width="34dip"

Просмотреть файл

@ -0,0 +1,26 @@
<?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/. -->
<merge xmlns:android="http://schemas.android.com/apk/res/android">
<ImageView android:id="@+id/image"
android:layout_width="54dp"
android:layout_height="44dp"
android:layout_marginTop="10dip"
android:layout_marginLeft="10dip"
android:scaleType="centerCrop"/>
<TextView android:id="@+id/title"
style="@style/Widget.PanelItemView.Title"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:paddingTop="5dip"
android:paddingBottom="5dip"
android:paddingLeft="10dip"
android:paddingRight="10dip"
android:minHeight="@dimen/page_row_height"
android:gravity="center_vertical"/>
</merge>

Просмотреть файл

@ -236,5 +236,9 @@
<attr name="strip" format="reference"/>
</declare-styleable>
<declare-styleable name="EllipsisTextView">
<attr name="ellipsizeAtLine" format="integer"/>
</declare-styleable>
</resources>

Просмотреть файл

@ -305,6 +305,7 @@
<string name="home_reading_list_hint">&home_reading_list_hint2;</string>
<string name="home_reading_list_hint_accessible">&home_reading_list_hint_accessible;</string>
<string name="home_default_empty">&home_default_empty;</string>
<string name="home_move_up_to_filter">&home_move_up_to_filter;</string>
<string name="pin_site_dialog_hint">&pin_site_dialog_hint;</string>
<string name="filepicker_title">&filepicker_title;</string>
@ -397,4 +398,7 @@
<string name="actionbar_menu">&actionbar_menu;</string>
<string name="actionbar_done">&actionbar_done;</string>
<!-- Miscellaneous -->
<string name="ellipsis">&ellipsis;</string>
</resources>

Просмотреть файл

@ -0,0 +1,65 @@
/* -*- 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.widget;
import org.mozilla.gecko.R;
import android.content.Context;
import android.content.res.TypedArray;
import android.util.AttributeSet;
import android.widget.TextView;
/**
* Text view that correctly handles maxLines and ellipsizing for Android < 2.3.
*/
public class EllipsisTextView extends TextView {
private final String ellipsis;
private int maxLines;
private CharSequence originalText;
public EllipsisTextView(Context context) {
this(context, null);
}
public EllipsisTextView(Context context, AttributeSet attrs) {
this(context, attrs, android.R.attr.textViewStyle);
}
public EllipsisTextView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
ellipsis = getResources().getString(R.string.ellipsis);
TypedArray a = context.getTheme()
.obtainStyledAttributes(attrs, R.styleable.EllipsisTextView, 0, 0);
maxLines = a.getInteger(R.styleable.EllipsisTextView_ellipsizeAtLine, 1);
a.recycle();
}
public void setOriginalText(CharSequence text) {
originalText = text;
setText(text);
}
@Override
public void onLayout(boolean changed, int left, int top, int right, int bottom) {
super.onLayout(changed, left, top, right, bottom);
// There is extra space, start over with the original text
if (getLineCount() < maxLines) {
setText(originalText);
}
// If we are over the max line attribute, ellipsize
if (getLineCount() > maxLines) {
final int endIndex = getLayout().getLineEnd(maxLines - 1) - 1 - ellipsis.length();
final String text = getText().subSequence(0, endIndex) + ellipsis;
// Make sure that we don't change originalText
setText(text);
}
}
}

Просмотреть файл

@ -58,6 +58,9 @@ MOZ_PAY=1
# Enable UI for healthreporter
MOZ_SERVICES_HEALTHREPORT=1
# Enable FirefoxAccounts
MOZ_SERVICES_FXACCOUNTS=1
# Wifi-AP/cell tower data reporting is enabled on non-release builds.
if test ! "$RELEASE_BUILD"; then
MOZ_DATA_REPORTING=1

Просмотреть файл

@ -2,7 +2,7 @@
* 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/. */
Cu.import("resource://gre/modules/commonjs/sdk/core/promise.js");
Cu.import("resource://gre/modules/Promise.jsm");
let cs = Cc["@mozilla.org/consoleservice;1"].
getService(Ci.nsIConsoleService);

Просмотреть файл

@ -17,7 +17,7 @@ XPCOMUtils.defineLazyModuleGetter(this, "FileUtils",
XPCOMUtils.defineLazyModuleGetter(this, "NetUtil",
"resource://gre/modules/NetUtil.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "Promise",
"resource://gre/modules/commonjs/sdk/core/promise.js");
"resource://gre/modules/Promise.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "Task",
"resource://gre/modules/Task.jsm");

Просмотреть файл

@ -18,7 +18,7 @@ XPCOMUtils.defineLazyModuleGetter(this, "FileUtils",
XPCOMUtils.defineLazyModuleGetter(this, "NetUtil",
"resource://gre/modules/NetUtil.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "Promise",
"resource://gre/modules/commonjs/sdk/core/promise.js");
"resource://gre/modules/Promise.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "Task",
"resource://gre/modules/Task.jsm");

Просмотреть файл

@ -9,7 +9,7 @@ const { 'classes': Cc, 'interfaces': Ci, 'utils': Cu, 'results': Cr } = Componen
let { NetUtil } = Cu.import("resource://gre/modules/NetUtil.jsm", {});
let { FileUtils } = Cu.import("resource://gre/modules/FileUtils.jsm", {});
let { Services } = Cu.import("resource://gre/modules/Services.jsm", {});
let { Promise } = Cu.import("resource://gre/modules/commonjs/sdk/core/promise.js", {});
let { Promise } = Cu.import("resource://gre/modules/Promise.jsm", {});
let { HttpServer } = Cu.import("resource://testing-common/httpd.js", {});
let { ctypes } = Cu.import("resource://gre/modules/ctypes.jsm");

Просмотреть файл

@ -7,7 +7,6 @@
PARALLEL_DIRS += [
'common',
'crypto',
'fxaccounts',
]
if CONFIG['MOZ_WIDGET_TOOLKIT'] != 'android':
@ -22,6 +21,9 @@ if CONFIG['MOZ_WIDGET_TOOLKIT'] != 'android':
if CONFIG['MOZ_SERVICES_METRICS']:
PARALLEL_DIRS += ['metrics']
if CONFIG['MOZ_SERVICES_FXACCOUNTS']:
PARALLEL_DIRS += ['fxaccounts']
if CONFIG['MOZ_SERVICES_SYNC']:
PARALLEL_DIRS += ['sync']

Просмотреть файл

@ -9,7 +9,7 @@ const Cu = Components.utils;
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "Promise",
"resource://gre/modules/commonjs/sdk/core/promise.js");
"resource://gre/modules/Promise.jsm");
do_get_profile();

Просмотреть файл

@ -16,7 +16,7 @@ Cu.import("resource://gre/modules/Services.jsm");
Cu.import("resource://testing-common/httpd.js");
Cu.import("resource://gre/modules/PlacesUtils.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "Promise",
"resource://gre/modules/commonjs/sdk/core/promise.js");
"resource://gre/modules/Promise.jsm");
var downloadUtils = { };
XPCOMUtils.defineLazyServiceGetter(downloadUtils,

Просмотреть файл

@ -18,7 +18,7 @@ XPCOMUtils.defineLazyModuleGetter(this, "FileUtils",
XPCOMUtils.defineLazyModuleGetter(this, "NetUtil",
"resource://gre/modules/NetUtil.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "Promise",
"resource://gre/modules/commonjs/sdk/core/promise.js");
"resource://gre/modules/Promise.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "Task",
"resource://gre/modules/Task.jsm");