[WO-475] give backbutton on android sane behavior

This commit is contained in:
Felix Hammerl 2014-07-29 19:17:03 +02:00 committed by Tankred Hase
parent 64daec7536
commit 3a713180f1
6 changed files with 154 additions and 0 deletions

View File

@ -21,6 +21,7 @@
"console",
"importScripts",
"process",
"Event",
"QUnit",
"test",
"asyncTest",

View File

@ -24,6 +24,7 @@ define(function(require) {
InvitationDAO = require('js/dao/invitation-dao'),
DeviceStorageDAO = require('js/dao/devicestorage-dao'),
UpdateHandler = require('js/util/update/update-handler'),
backBtnHandler = require('js/util/backbutton-handler'),
config = appConfig.config,
str = appConfig.string;
@ -54,6 +55,7 @@ define(function(require) {
function onDeviceReady() {
axe.debug('Starting app.');
backBtnHandler.start();
self.buildModules();
// Handle offline and online gracefully

View File

@ -23,6 +23,7 @@ requirejs([
'js/controller/navigation',
'js/crypto/util',
'js/util/error',
'js/util/backbutton-handler',
'fastclick',
'angularRoute',
'angularAnimate',
@ -49,6 +50,7 @@ requirejs([
NavigationCtrl,
util,
errorUtil,
backButtonUtil,
FastClick
) {
'use strict';
@ -113,8 +115,13 @@ requirejs([
app.run(function($rootScope) {
// global state... inherited to all child scopes
$rootScope.state = {};
// attach global error handler
errorUtil.attachHandler($rootScope);
// attach the back button handler to the root scope
backButtonUtil.attachHandler($rootScope);
// attach fastclick
FastClick.attach(document.body);
});

View File

@ -0,0 +1,58 @@
define(function(require) {
'use strict';
var axe = require('axe'),
DEBUG_TAG = 'backbutton handler';
/**
* The back button handler introduces meaningful behavior fo rthe back button:
* if there's an open lightbox, close it;
* if the reader is open in mobile mode, close it;
* if the navigation is open, close it;
* if there's nothing else open, shut down the app;
*
* @type {Object}
*/
var backBtnHandler = {
attachHandler: function(scope) {
this.scope = scope;
},
start: function() {
document.addEventListener("backbutton", handleBackButton, false);
},
stop: function() {
document.removeEventListener("backbutton", handleBackButton, false);
}
};
function handleBackButton(event) {
axe.debug(DEBUG_TAG, 'back button pressed');
// this disarms the default behavior which we NEVER want
event.preventDefault();
event.stopPropagation();
if (backBtnHandler.scope.state.lightbox) {
// closes the lightbox (error msgs, writer, ...)
backBtnHandler.scope.state.lightbox = undefined;
axe.debug(DEBUG_TAG, 'lightbox closed');
backBtnHandler.scope.$apply();
} else if (backBtnHandler.scope.state.read && backBtnHandler.scope.state.read.open) {
// closes the reader
backBtnHandler.scope.state.read.toggle(false);
axe.debug(DEBUG_TAG, 'reader closed');
backBtnHandler.scope.$apply();
} else if (backBtnHandler.scope.state.nav && backBtnHandler.scope.state.nav.open) {
// closes the navigation
backBtnHandler.scope.state.nav.toggle(false);
axe.debug(DEBUG_TAG, 'navigation closed');
backBtnHandler.scope.$apply();
} else {
// exits the app
navigator.app.exitApp();
}
}
return backBtnHandler;
});

View File

@ -0,0 +1,67 @@
define(function(require) {
'use strict';
var btnHandler = require('js/util/backbutton-handler'),
expect = chai.expect;
describe('Backbutton Handler', function() {
chai.Assertion.includeStack = true;
var scope, event;
beforeEach(function() {
scope = {
state: {},
$apply: function() {}
};
event = new CustomEvent('backbutton');
// this is a precondition for the test. throw an exception
// if this would produce side effects
expect(navigator.app).to.not.exist;
navigator.app = {};
btnHandler.attachHandler(scope);
btnHandler.start();
});
afterEach(function() {
btnHandler.stop();
delete navigator.app;
});
it('should close lightbox', function() {
scope.state.lightbox = 'asd';
document.dispatchEvent(event);
expect(scope.state.lightbox).to.be.undefined;
});
it('should close reader', function() {
scope.state.read = {
open: true,
toggle: function(state) {
scope.state.read.open = state;
}
};
document.dispatchEvent(event);
expect(scope.state.read.open).to.be.false;
});
it('should close navigation', function() {
scope.state.nav = {
open: true,
toggle: function(state) {
scope.state.nav.open = state;
}
};
document.dispatchEvent(event);
expect(scope.state.nav.open).to.be.false;
});
it('should close app', function(done) {
navigator.app.exitApp = done;
document.dispatchEvent(event);
});
});
});

View File

@ -22,6 +22,24 @@ if (!Function.prototype.bind) {
};
}
// a warm round of applause for phantomjs for missing events
(function() {
function CustomEvent(event, params) {
params = params || {
bubbles: false,
cancelable: false,
detail: undefined
};
var evt = document.createEvent('CustomEvent');
evt.initCustomEvent(event, params.bubbles, params.cancelable, params.detail);
return evt;
}
CustomEvent.prototype = window.Event.prototype;
window.CustomEvent = CustomEvent;
})();
require(['../../src/require-config'], function() {
require.config({
baseUrl: '../../src/lib',
@ -59,6 +77,7 @@ function startTests() {
'test/unit/app-controller-test',
'test/unit/pgp-test',
'test/unit/crypto-test',
'test/unit/backbutton-handler-test',
'test/unit/rest-dao-test',
'test/unit/publickey-dao-test',
'test/unit/privatekey-dao-test',