Skip to content
This repository has been archived by the owner on Nov 3, 2021. It is now read-only.

Commit

Permalink
Merge pull request #22918 from mnjul/bug_1053624_keyboard_manager_sho…
Browse files Browse the repository at this point in the history
…wall

Bug 1053624 - Extract and organize Input Management showAll(). r=timdream
  • Loading branch information
Min-Zhong "John" Lu committed Sep 3, 2014
2 parents 43c7247 + 3205968 commit e1f6d88
Show file tree
Hide file tree
Showing 3 changed files with 285 additions and 49 deletions.
96 changes: 49 additions & 47 deletions apps/system/js/keyboard_manager.js
Expand Up @@ -561,13 +561,18 @@ var KeyboardManager = {
this.showingLayoutInfo.layout = layout;
},

switchToNext: function km_switchToNext() {
/* A small helper function for maintaining timeouts */
waitForSwitchTimeout: function km_waitForSwitchTimeout(callback) {
clearTimeout(this.switchChangeTimeout);

this.switchChangeTimeout = setTimeout(callback, SWITCH_CHANGE_DELAY);
},

switchToNext: function km_switchToNext() {
var showed = this.showingLayoutInfo;
var oldLayout = showed.layout;

this.switchChangeTimeout = setTimeout(function keyboardSwitchLayout() {
this.waitForSwitchTimeout(function keyboardSwitchLayout() {
if (!this.keyboardLayouts[showed.type]) {
showed.type = 'text';
}
Expand All @@ -583,61 +588,58 @@ var KeyboardManager = {
}

this.setKeyboardToShow(showed.type, index);
}.bind(this), SWITCH_CHANGE_DELAY);
}.bind(this));
},

/*
* Callback for ImeMenu.
* If selectedIndex is defined, then some item of imeMenu was selected;
* if it's not, then it was canceled.
* The showedType param is bind()'ed by showAll (resulting in a partial func)
*/
imeMenuCallback: function km_imeMenuCallback(showedType, selectedIndex) {
if (typeof selectedIndex === 'number') {
// success: show the new keyboard
this.keyboardLayouts[showedType].activeLayout = selectedIndex;
this.setKeyboardToShow(showedType, selectedIndex);

// Hide the tray to show the app directly after user selected a new kb.
window.dispatchEvent(new CustomEvent('keyboardchanged'));
} else {
// cancel: mimic the success callback to show the current keyboard.
this.setKeyboardToShow(showedType);

// Hide the tray to show the app directly after user canceled.
window.dispatchEvent(new CustomEvent('keyboardchangecanceled'));
}
},

// Show the input method menu
showAll: function km_showAll() {
clearTimeout(this.switchChangeTimeout);

var self = this;
var showedType = this.showingLayoutInfo.type;
var activeLayout = this.keyboardLayouts[showedType].activeLayout;
var _ = navigator.mozL10n.get;
var actionMenuTitle = _('choose-option');

this.switchChangeTimeout = setTimeout(function keyboardLayoutList() {
var items = [];
self.keyboardLayouts[showedType].forEach(function(layout, index) {
var item = {
layoutName: layout.name,
appName: layout.appName,
value: index,
selected: (index === activeLayout)
};
items.push(item);
});
self.hideKeyboard();
var actionMenuTitle = navigator.mozL10n.get('choose-option');

this.waitForSwitchTimeout(function keyboardLayoutList() {
var items = this.keyboardLayouts[showedType].map(
function(layout, index) {
return {
layoutName: layout.name,
appName: layout.appName,
value: index,
selected: (index === activeLayout)
};
});

var menu = new ImeMenu(items, actionMenuTitle,
function(selectedIndex) {
if (!self.keyboardLayouts[showedType]) {
showedType = 'text';
}
self.keyboardLayouts[showedType].activeLayout = selectedIndex;
self.setKeyboardToShow(showedType, selectedIndex);

// Hide the tray to show the app directly after
// user selected a new keyboard.
window.dispatchEvent(new CustomEvent('keyboardchanged'));

// Refresh the switcher, or the labled type and layout name
// won't change.
}, function() {
if (!self.keyboardLayouts[showedType]) {
showedType = 'text';
}
this.hideKeyboard();

// Mimic the success callback to show the current keyboard
// when user canceled it.
self.setKeyboardToShow(showedType);
var menu = new ImeMenu(items, actionMenuTitle,
this.imeMenuCallback.bind(this, showedType),
this.imeMenuCallback.bind(this, showedType));

// Hide the tray to show the app directly after
// user canceled.
window.dispatchEvent(new CustomEvent('keyboardchangecanceled'));
});
menu.start();
}, SWITCH_CHANGE_DELAY);

}.bind(this));
}
};

Expand Down
195 changes: 193 additions & 2 deletions apps/system/test/unit/keyboard_manager_test.js
@@ -1,15 +1,18 @@
/*global KeyboardManager, sinon, KeyboardHelper, MockKeyboardHelper,
MocksHelper, TransitionEvent, MockNavigatorSettings, Applications, Promise */
MocksHelper, TransitionEvent, MockNavigatorSettings, Applications, Promise,
MockL10n, MockImeMenu */
'use strict';

require('/shared/test/unit/mocks/mock_lazy_loader.js');
require('/shared/test/unit/mocks/mock_keyboard_helper.js');
require('/shared/test/unit/mocks/mock_settings_listener.js');
require('/shared/test/unit/mocks/mock_navigator_moz_settings.js');
require('/shared/test/unit/mocks/mock_l10n.js');
require('/test/unit/mock_applications.js');
require('/test/unit/mock_homescreen_launcher.js');
require('/test/unit/mock_ime_switcher.js');
require('/test/unit/mock_input_frame_manager.js');
require('/test/unit/mock_ime_menu.js');
require('/js/input_transition.js');
require('/js/keyboard_manager.js');

Expand All @@ -19,7 +22,9 @@ var mocksHelperForKeyboardManager = new MocksHelper([
'LazyLoader',
'Applications',
'IMESwitcher',
'InputFrameManager'
'InputFrameManager',
'ImeMenu',
'L10n'
]).init();

suite('KeyboardManager', function() {
Expand Down Expand Up @@ -1023,4 +1028,190 @@ suite('KeyboardManager', function() {
assert.equal(KeyboardManager.showingLayoutInfo.layout, 'someLayout');
});
});

suite('Switching keyboards within same type', function() {
var oldKeyboardLayouts;
var oldShowingLayoutInfoType;

setup(function() {
oldKeyboardLayouts = KeyboardManager.keyboardLayouts;
KeyboardManager.keyboardLayouts = {
text: [
{
name: 'English',
appName: 'Built-in Keyboard',
manifestURL: 'app://keyboard.gaiamobile.org/manifest.webapp'
},{
name: 'French',
appName: 'Built-out Keyboard',
manifestURL: 'app://anotherkb.gaiamobile.org/manifest.webapp'
},{
name: 'Chinese',
appName: 'Built-inout Keyboard',
manifestURL: 'app://keyboard.gaiamobile.org/manifest.webapp'
}
]
};

KeyboardManager.keyboardLayouts.text.activeLayout = 2;

oldShowingLayoutInfoType = KeyboardManager.showingLayoutInfo.type;
KeyboardManager.showingLayoutInfo.type = 'text';
});

teardown(function() {
KeyboardManager.showingLayoutInfo.type = oldShowingLayoutInfoType;
KeyboardManager.keyboardLayouts = oldKeyboardLayouts;
});

test('showAll / call to ImeMenu', function(){
var oldMozL10n;
var stubWaitForSwitchTimeout;
var stubHideKeyboard;
var stubImeMenuCallback;

oldMozL10n = navigator.mozL10n;
navigator.mozL10n = MockL10n;

stubWaitForSwitchTimeout =
this.sinon.stub(KeyboardManager, 'waitForSwitchTimeout');

stubHideKeyboard = this.sinon.stub(KeyboardManager, 'hideKeyboard');

stubImeMenuCallback =
this.sinon.stub(KeyboardManager, 'imeMenuCallback');

MockImeMenu.mSetup();

KeyboardManager.showAll();

stubWaitForSwitchTimeout.getCall(0).args[0]();

assert.isTrue(stubHideKeyboard.called);

var imeMenu = MockImeMenu.instances[0];
assert.deepEqual(imeMenu.listItems,
[{
layoutName: 'English',
appName: 'Built-in Keyboard',
value: 0,
selected: false
}, {
layoutName: 'French',
appName: 'Built-out Keyboard',
value: 1,
selected: false
}, {
layoutName: 'Chinese',
appName: 'Built-inout Keyboard',
value: 2,
selected: true
}]);

imeMenu.onselected();
imeMenu.oncancel();
assert.isTrue(stubImeMenuCallback.alwaysCalledWith('text'));
assert.equal(stubImeMenuCallback.callCount, 2);

assert.equal(imeMenu.title, 'choose-option');

imeMenu.mTeardown();

navigator.mozL10n = oldMozL10n;
});

suite('imeMenuCallback', function() {
var stubSetKeyboardToShow;
var stubDispatchEvent;

setup(function() {
stubSetKeyboardToShow =
this.sinon.stub(KeyboardManager, 'setKeyboardToShow');
stubDispatchEvent = this.sinon.stub(window, 'dispatchEvent');
});

test('success', function(){
KeyboardManager.imeMenuCallback('text', 1);
assert.equal(KeyboardManager.keyboardLayouts.text.activeLayout, 1);
assert.isTrue(stubSetKeyboardToShow.calledWith('text', 1));
assert.equal(stubDispatchEvent.getCall(0).args[0].type,
'keyboardchanged');
});

test('cancel', function(){
KeyboardManager.imeMenuCallback('text');
assert.equal(KeyboardManager.keyboardLayouts.text.activeLayout, 2);
assert.isTrue(stubSetKeyboardToShow.calledWithExactly('text'));
assert.equal(stubDispatchEvent.getCall(0).args[0].type,
'keyboardchangecanceled');
});
});

suite('switchToNext', function() {
var oldShowingLayoutInfo;
var stubWaitForSwitchTimeout;
var stubSetKeyboardToShow;
setup(function() {
oldShowingLayoutInfo = KeyboardManager.showingLayoutInfo;
KeyboardManager.showingLayoutInfo = {
type: 'text',
index: 2,
layout: {
name: 'Chinese',
appName: 'Built-inout Keyboard',
manifestURL: 'app://keyboard.gaiamobile.org/manifest.webapp'
}
};

stubWaitForSwitchTimeout =
this.sinon.stub(KeyboardManager, 'waitForSwitchTimeout');

stubSetKeyboardToShow =
this.sinon.stub(KeyboardManager, 'setKeyboardToShow');
});

test('to same kb app', function(){
KeyboardManager.switchToNext();

stubWaitForSwitchTimeout.getCall(0).args[0]();

assert.strictEqual(
KeyboardManager.keyboardLayouts.text.activeLayout, 0);
assert.isTrue(stubSetKeyboardToShow.calledWith('text', 0));
});

test('to different kb app', function(){
var stubResetShowingKeybaord =
this.sinon.stub(KeyboardManager, 'resetShowingKeyboard');

KeyboardManager.showingLayoutInfo.index = 0;

KeyboardManager.switchToNext();

stubWaitForSwitchTimeout.getCall(0).args[0]();

assert.strictEqual(
KeyboardManager.keyboardLayouts.text.activeLayout, 1);
assert.isTrue(stubResetShowingKeybaord.called);
assert.isTrue(stubSetKeyboardToShow.calledWith('text', 1));
});
});

test('waitForSwitchTimeout helper', function(done) {
var oldSwitchChangeTimeout = KeyboardManager.switchChangeTimeout;
KeyboardManager.switchChangeTimeout = 1234;

var stubClearTimeout = this.sinon.stub(window, 'clearTimeout');

KeyboardManager.waitForSwitchTimeout(function(){
done();
});

assert.isTrue(stubClearTimeout.calledWith(1234));

this.sinon.clock.tick(SWITCH_CHANGE_DELAY);

KeyboardManager.switchChangeTimeout = oldSwitchChangeTimeout;
});
});
});
43 changes: 43 additions & 0 deletions apps/system/test/unit/mock_ime_menu.js
@@ -0,0 +1,43 @@
'use strict';

(function(exports) {
var instanceCounter = 0;

var MockImeMenu = function(listItems, title, successCb, cancelCb) {
this.onselected = successCb || function() {};
this.oncancel = cancelCb || function() {};
this.listItems = listItems;
this.title = title;

this.instanceId = instanceCounter;

MockImeMenu.instances[instanceCounter] = this;

instanceCounter++;

return this;
};

MockImeMenu.instances = [];

MockImeMenu.mSetup = function () {
MockImeMenu.instances = [];
instanceCounter = 0;
};

MockImeMenu.prototype = {
start: function mim_start() {
},

mTeardown: function mim_mTeardown() {
this.onselected = undefined;
this.oncancel = undefined;
this.listItems = undefined;
this.title = undefined;

MockImeMenu.instances[this.instanceId] = null;
}
};

exports.MockImeMenu = MockImeMenu;
}(window));

0 comments on commit e1f6d88

Please sign in to comment.