Bug 1130646 - Find non-hacky way to make back button dismiss reader mode popup, r=bnicholson

This commit is contained in:
Mark Capella 2015-10-07 19:55:33 -04:00
parent 670995286a
commit 466a9d607e
4 changed files with 160 additions and 21 deletions

View File

@ -33,6 +33,7 @@ import org.mozilla.gecko.util.ActivityUtils;
import org.mozilla.gecko.util.EventCallback;
import org.mozilla.gecko.util.FileUtils;
import org.mozilla.gecko.util.GeckoEventListener;
import org.mozilla.gecko.util.GeckoRequest;
import org.mozilla.gecko.util.HardwareUtils;
import org.mozilla.gecko.util.NativeEventListener;
import org.mozilla.gecko.util.NativeJSObject;
@ -2344,31 +2345,57 @@ public abstract class GeckoApp
return;
}
Tabs tabs = Tabs.getInstance();
Tab tab = tabs.getSelectedTab();
final Tabs tabs = Tabs.getInstance();
final Tab tab = tabs.getSelectedTab();
if (tab == null) {
moveTaskToBack(true);
return;
}
if (tab.doBack())
return;
// Give Gecko a chance to handle the back press first, then fallback to the Java UI.
GeckoAppShell.sendRequestToGecko(new GeckoRequest("Browser:OnBackPressed", null) {
@Override
public void onResponse(NativeJSObject nativeJSObject) {
if (!nativeJSObject.getBoolean("handled")) {
// Default behavior is Gecko didn't prevent.
onDefault();
}
}
if (tab.isExternal()) {
moveTaskToBack(true);
tabs.closeTab(tab);
return;
}
@Override
public void onError(NativeJSObject error) {
// Default behavior is Gecko didn't prevent, via failure.
onDefault();
}
int parentId = tab.getParentId();
Tab parent = tabs.getTab(parentId);
if (parent != null) {
// The back button should always return to the parent (not a sibling).
tabs.closeTab(tab, parent);
return;
}
// Return from Gecko thread, then back-press through the Java UI.
private void onDefault() {
ThreadUtils.postToUiThread(new Runnable() {
@Override
public void run() {
if (tab.doBack()) {
return;
}
moveTaskToBack(true);
if (tab.isExternal()) {
moveTaskToBack(true);
tabs.closeTab(tab);
return;
}
final int parentId = tab.getParentId();
final Tab parent = tabs.getTab(parentId);
if (parent != null) {
// The back button should always return to the parent (not a sibling).
tabs.closeTab(tab, parent);
return;
}
moveTaskToBack(true);
}
});
}
});
}
@Override

View File

@ -20,6 +20,39 @@ var Reader = {
return this._hasUsedToolbar = Services.prefs.getBoolPref("reader.has_used_toolbar");
},
/**
* BackPressListener (listeners / ReaderView Ids).
*/
_backPressListeners: [],
_backPressViewIds: [],
/**
* Set a backPressListener for this tabId / ReaderView Id pair.
*/
_addBackPressListener: function(tabId, viewId, listener) {
this._backPressListeners[tabId] = listener;
this._backPressViewIds[viewId] = tabId;
},
/**
* Remove a backPressListener for this ReaderView Id.
*/
_removeBackPressListener: function(viewId) {
let tabId = this._backPressViewIds[viewId];
if (tabId != undefined) {
this._backPressListeners[tabId] = null;
delete this._backPressViewIds[viewId];
}
},
/**
* If the requested tab has a backPress listener, return its results, else false.
*/
onBackPress: function(tabId) {
let listener = this._backPressListeners[tabId];
return { handled: (listener ? listener() : false) };
},
observe: function Reader_observe(aMessage, aTopic, aData) {
switch (aTopic) {
case "Reader:FetchContent": {
@ -66,6 +99,29 @@ var Reader = {
});
break;
// On DropdownClosed in ReaderView, we cleanup / clear existing BackPressListener.
case "Reader:DropdownClosed": {
this._removeBackPressListener(message.data);
break;
}
// On DropdownOpened in ReaderView, we add BackPressListener to handle a subsequent BACK request.
case "Reader:DropdownOpened": {
let tabId = BrowserApp.selectedTab.id;
this._addBackPressListener(tabId, message.data, () => {
// User hit BACK key while ReaderView has the banner font-dropdown opened.
// Close it and return prevent-default.
if (message.target.messageManager) {
message.target.messageManager.sendAsyncMessage("Reader:CloseDropdown");
return true;
}
// We can assume ReaderView banner's font-dropdown doesn't need to be closed.
return false;
});
break;
}
case "Reader:FaviconRequest": {
Messaging.sendRequestForResult({
type: "Reader:FaviconRequest",

View File

@ -193,6 +193,8 @@ lazilyLoadedObserverScripts.forEach(function (aScript) {
["Reader", [
["Reader:AddToList", false],
["Reader:ArticleGet", false],
["Reader:DropdownClosed", true], // 'true' allows us to survive mid-air cycle-collection.
["Reader:DropdownOpened", false],
["Reader:FaviconRequest", false],
["Reader:ListStatusRequest", false],
["Reader:RemoveFromList", false],
@ -4747,6 +4749,11 @@ var BrowserEventHandler = {
InitLater(() => BrowserApp.deck.addEventListener("click", InputWidgetHelper, true));
InitLater(() => BrowserApp.deck.addEventListener("click", SelectHelper, true));
// ReaderViews support backPress listeners.
Messaging.addListener(() => {
return Reader.onBackPress(BrowserApp.selectedTab.id);
}, "Browser:OnBackPressed");
},
handleEvent: function(aEvent) {

View File

@ -38,6 +38,7 @@ var AboutReader = function(mm, win, articlePromise) {
this._mm.addMessageListener("Reader:Removed", this);
this._mm.addMessageListener("Sidebar:VisibilityChange", this);
this._mm.addMessageListener("ReadingList:VisibilityStatus", this);
this._mm.addMessageListener("Reader:CloseDropdown", this);
this._docRef = Cu.getWeakReference(doc);
this._winRef = Cu.getWeakReference(win);
@ -178,6 +179,15 @@ AboutReader.prototype = {
return this._toolbarVertical = Services.prefs.getBoolPref("reader.toolbar.vertical");
},
// Provides unique view Id.
get viewId() {
let _viewId = Cc["@mozilla.org/uuid-generator;1"].
getService(Ci.nsIUUIDGenerator).generateUUID().toString();
Object.defineProperty(this, "viewId", { value: _viewId });
return _viewId;
},
receiveMessage: function (message) {
switch (message.name) {
case "Reader:Added": {
@ -190,6 +200,14 @@ AboutReader.prototype = {
}
break;
}
// Triggered by Android user pressing BACK while the banner font-dropdown is open.
case "Reader:CloseDropdown": {
// Just close it.
this._closeDropdown();
break;
}
case "Reader:Removed": {
if (message.data.url == this._article.url) {
if (this._isReadingListItem != 0) {
@ -246,10 +264,14 @@ AboutReader.prototype = {
break;
case "unload":
// Close the Banners Font-dropdown, cleanup Android BackPressListener.
this._closeDropdown();
this._mm.removeMessageListener("Reader:Added", this);
this._mm.removeMessageListener("Reader:Removed", this);
this._mm.removeMessageListener("Sidebar:VisibilityChange", this);
this._mm.removeMessageListener("ReadingList:VisibilityStatus", this);
this._mm.removeMessageListener("Reader:CloseDropdown", this);
this._windowUnloaded = true;
break;
}
@ -584,8 +606,7 @@ AboutReader.prototype = {
},
_setToolbarVisibility: function(visible) {
let dropdown = this._doc.getElementById("style-dropdown");
dropdown.classList.remove("open");
this._closeDropdown();
if (this._getToolbarVisibility() === visible) {
return;
@ -943,13 +964,41 @@ AboutReader.prototype = {
event.stopPropagation();
if (dropdown.classList.contains("open")) {
dropdown.classList.remove("open");
this._closeDropdown();
} else {
dropdown.classList.add("open");
this._openDropdown();
if (this._isToolbarVertical) {
updatePopupPosition();
}
}
}, true);
},
/*
* If the ReaderView banner font-dropdown is closed, open it.
*/
_openDropdown: function() {
let dropdown = this._doc.getElementById("style-dropdown");
if (dropdown.classList.contains("open")) {
return;
}
// Trigger BackPressListener initialization in Android.
dropdown.classList.add("open");
this._mm.sendAsyncMessage("Reader:DropdownOpened", this.viewId);
},
/*
* If the ReaderView banner font-dropdown is opened, close it.
*/
_closeDropdown: function() {
let dropdown = this._doc.getElementById("style-dropdown");
if (!dropdown.classList.contains("open")) {
return;
}
// Trigger BackPressListener cleanup in Android.
dropdown.classList.remove("open");
this._mm.sendAsyncMessage("Reader:DropdownClosed", this.viewId);
},
};