Bug 817869 - Consolidate Android and B2G touch adapter. r=davidb

This commit is contained in:
Eitan Isaacson 2012-12-11 10:12:12 -08:00
parent 0423afe5fe
commit b40acfa315
2 changed files with 151 additions and 116 deletions

View File

@ -14,7 +14,6 @@ this.EXPORTED_SYMBOLS = ['AccessFu'];
Cu.import('resource://gre/modules/Services.jsm');
Cu.import('resource://gre/modules/accessibility/Utils.jsm');
Cu.import('resource://gre/modules/accessibility/TouchAdapter.jsm');
const ACCESSFU_DISABLE = 0;
const ACCESSFU_ENABLE = 1;
@ -76,9 +75,6 @@ this.AccessFu = {
Logger.info('enable');
this.touchAdapter = (Utils.MozBuildApp == 'mobile/android') ?
AndroidTouchAdapter : TouchAdapter;
for each (let mm in Utils.getAllMessageManagers(this.chromeWin))
this._loadFrameScript(mm);
@ -91,7 +87,7 @@ this.AccessFu = {
Input.attach(this.chromeWin);
Output.attach(this.chromeWin);
this.touchAdapter.attach(this.chromeWin);
TouchAdapter.attach(this.chromeWin);
Services.obs.addObserver(this, 'remote-browser-frame-shown', false);
Services.obs.addObserver(this, 'Accessibility:NextObject', false);
@ -117,7 +113,7 @@ this.AccessFu = {
mm.sendAsyncMessage('AccessFu:Stop');
Input.detach();
this.touchAdapter.detach(this.chromeWin);
TouchAdapter.detach(this.chromeWin);
this.chromeWin.removeEventListener('TabOpen', this);
this.chromeWin.removeEventListener('TabSelect', this);
@ -389,7 +385,7 @@ var Input = {
this._handleKeypress(aEvent);
break;
case 'mozAccessFuGesture':
this._handleGesture(aEvent);
this._handleGesture(aEvent.detail);
break;
}
} catch (x) {
@ -397,44 +393,42 @@ var Input = {
}
},
_handleGesture: function _handleGesture(aEvent) {
let detail = aEvent.detail;
Logger.info('Gesture', detail.type,
'(fingers: ' + detail.touches.length + ')');
_handleGesture: function _handleGesture(aGesture) {
let gestureName = aGesture.type + aGesture.touches.length;
Logger.info('Gesture', aGesture.type,
'(fingers: ' + aGesture.touches.length + ')');
if (detail.touches.length == 1) {
switch (detail.type) {
case 'swiperight':
this.moveCursor('moveNext', 'Simple', 'gestures');
break;
case 'swipeleft':
this.moveCursor('movePrevious', 'Simple', 'gesture');
break;
case 'doubletap':
this.activateCurrent();
break;
case 'explore':
this.moveCursor('moveToPoint', 'Simple', 'gesture',
detail.x, detail.y);
break;
}
}
if (detail.touches.length == 3) {
switch (detail.type) {
case 'swiperight':
this.scroll(-1, true);
break;
case 'swipedown':
this.scroll(-1);
break;
case 'swipeleft':
this.scroll(1, true);
break;
case 'swipeup':
this.scroll(1);
break;
}
switch (gestureName) {
case 'dwell1':
case 'explore1':
this.moveCursor('moveToPoint', 'Simple', 'gesture',
aGesture.x, aGesture.y);
break;
case 'doubletap1':
this.activateCurrent();
break;
case 'swiperight1':
this.moveCursor('moveNext', 'Simple', 'gestures');
break;
case 'swipeleft1':
this.moveCursor('movePrevious', 'Simple', 'gesture');
break;
case 'swiperight2':
this.scroll(-1, true);
break;
case 'swipedown2':
this.scroll(-1);
break;
case 'swipeleft2':
this.scroll(1, true);
break;
case 'swipeup2':
this.scroll(1);
break;
case 'explore2':
Utils.getCurrentBrowser(this.chromeWin).contentWindow.scrollBy(
-aGesture.deltaX, -aGesture.deltaY);
break;
}
},

View File

@ -9,7 +9,7 @@ const Ci = Components.interfaces;
const Cu = Components.utils;
const Cr = Components.results;
this.EXPORTED_SYMBOLS = ['TouchAdapter', 'AndroidTouchAdapter'];
this.EXPORTED_SYMBOLS = ['TouchAdapter'];
Cu.import('resource://gre/modules/accessibility/Utils.jsm');
@ -39,6 +39,9 @@ this.TouchAdapter = {
// maximum distance the mouse could move during a tap in inches
TAP_MAX_RADIUS: 0.2,
// The virtual touch ID generated by an Android hover event.
HOVER_ID: 'hover',
attach: function TouchAdapter_attach(aWindow) {
if (this.chromeWin)
return;
@ -53,14 +56,26 @@ this.TouchAdapter = {
this._dpi = this.chromeWin.QueryInterface(Ci.nsIInterfaceRequestor).
getInterface(Ci.nsIDOMWindowUtils).displayDPI;
this.glass = this.chromeWin.document.
createElementNS('http://www.w3.org/1999/xhtml', 'div');
this.glass.id = 'accessfu-glass';
this.chromeWin.document.documentElement.appendChild(this.glass);
let target = this.chromeWin;
this.glass.addEventListener('touchend', this, true, true);
this.glass.addEventListener('touchmove', this, true, true);
this.glass.addEventListener('touchstart', this, true, true);
if (Utils.MozBuildApp == 'b2g') {
this.glass = this.chromeWin.document.
createElementNS('http://www.w3.org/1999/xhtml', 'div');
this.glass.id = 'accessfu-glass';
this.chromeWin.document.documentElement.appendChild(this.glass);
target = this.glass;
}
target.addEventListener('mousemove', this, true, true);
target.addEventListener('mouseenter', this, true, true);
target.addEventListener('mouseleave', this, true, true);
target.addEventListener('mousedown', this, true, true);
target.addEventListener('mouseup', this, true, true);
target.addEventListener('click', this, true, true);
target.addEventListener('touchend', this, true, true);
target.addEventListener('touchmove', this, true, true);
target.addEventListener('touchstart', this, true, true);
if (Utils.OS != 'Android')
Mouse2Touch.attach(aWindow);
@ -72,10 +87,23 @@ this.TouchAdapter = {
Logger.info('TouchAdapter.detach');
this.glass.removeEventListener('touchend', this, true, true);
this.glass.removeEventListener('touchmove', this, true, true);
this.glass.removeEventListener('touchstart', this, true, true);
this.glass.parentNode.removeChild(this.glass);
let target = this.chromeWin;
if (Utils.MozBuildApp == 'b2g') {
target = this.glass;
this.glass.parentNode.removeChild(this.glass);
}
target.removeEventListener('mousemove', this, true, true);
target.removeEventListener('mouseenter', this, true, true);
target.removeEventListener('mouseleave', this, true, true);
target.removeEventListener('mousedown', this, true, true);
target.removeEventListener('mouseup', this, true, true);
target.removeEventListener('click', this, true, true);
target.removeEventListener('touchend', this, true, true);
target.removeEventListener('touchmove', this, true, true);
target.removeEventListener('touchstart', this, true, true);
if (Utils.OS != 'Android')
Mouse2Touch.detach(aWindow);
@ -84,16 +112,23 @@ this.TouchAdapter = {
},
handleEvent: function TouchAdapter_handleEvent(aEvent) {
let touches = aEvent.changedTouches;
if (this._delayedEvent) {
this.chromeWin.clearTimeout(this._delayedEvent);
delete this._delayedEvent;
}
let changedTouches = aEvent.changedTouches || [aEvent];
// XXX: Until bug 77992 is resolved, on desktop we get microseconds
// instead of milliseconds.
let timeStamp = (Utils.OS == 'Android') ? aEvent.timeStamp : Date.now();
switch (aEvent.type) {
case 'mouseenter':
case 'touchstart':
for (var i = 0; i < touches.length; i++) {
let touch = touches[i];
for (var i = 0; i < changedTouches.length; i++) {
let touch = changedTouches[i];
let touchPoint = new TouchPoint(touch, timeStamp, this._dpi);
this._touchPoints[touch.identifier] = touchPoint;
this._touchPoints[touch.identifier || this.HOVER_ID] = touchPoint;
this._lastExploreTime = timeStamp + this.SWIPE_MAX_DURATION;
}
this._dwellTimeout = this.chromeWin.setTimeout(
@ -101,27 +136,34 @@ this.TouchAdapter = {
this.compileAndEmit(timeStamp + this.DWELL_THRESHOLD);
}).bind(this), this.DWELL_THRESHOLD);
break;
case 'mousemove':
case 'touchmove':
for (var i = 0; i < touches.length; i++) {
let touch = touches[i];
let touchPoint = this._touchPoints[touch.identifier];
touchPoint.update(touch, timeStamp);
for (var i = 0; i < changedTouches.length; i++) {
let touch = changedTouches[i];
let touchPoint = this._touchPoints[touch.identifier || this.HOVER_ID];
if (touchPoint)
touchPoint.update(touch, timeStamp);
}
if (timeStamp - this._lastExploreTime >= EXPLORE_THROTTLE) {
this.compileAndEmit(timeStamp);
this._lastExploreTime = timeStamp;
}
break;
case 'mouseleave':
case 'touchend':
for (var i = 0; i < touches.length; i++) {
let touch = touches[i];
let touchPoint = this._touchPoints[touch.identifier];
touchPoint.update(touch, timeStamp);
touchPoint.finish();
for (var i = 0; i < changedTouches.length; i++) {
let touch = changedTouches[i];
let touchPoint = this._touchPoints[touch.identifier || this.HOVER_ID];
if (touchPoint) {
touchPoint.update(touch, timeStamp);
touchPoint.finish();
}
}
this.compileAndEmit(timeStamp);
break;
}
aEvent.preventDefault();
},
cleanupTouches: function cleanupTouches() {
@ -170,12 +212,22 @@ this.TouchAdapter = {
if (timeDelta > this.MAX_CONSECUTIVE_GESTURE_DELAY) {
delete this._prevGestures[idhash];
} else {
if (details.type == 'tap' && prevGesture.type == 'tap')
details.type = 'doubletap';
if (details.type == 'tap' && prevGesture.type == 'doubletap')
details.type = 'tripletap';
if (details.type == 'dwell' && prevGesture.type == 'tap')
details.type = 'taphold';
let sequence = prevGesture.type + '-' + details.type;
switch (sequence) {
case 'tap-tap':
details.type = 'doubletap';
break;
case 'doubletap-tap':
details.type = 'tripletap';
break;
case 'tap-dwell':
details.type = 'taphold';
break;
case 'explore-explore':
details.deltaX = details.x - prevGesture.x;
details.deltaY = details.y - prevGesture.y;
break;
}
}
}
@ -189,9 +241,36 @@ this.TouchAdapter = {
},
emitGesture: function TouchAdapter_emitGesture(aDetails) {
let evt = this.chromeWin.document.createEvent('CustomEvent');
evt.initCustomEvent('mozAccessFuGesture', true, true, aDetails);
this.chromeWin.dispatchEvent(evt);
let emitDelay = 0;
// Unmutate gestures we are getting from Android when EBT is enabled.
// Two finger gestures are translated to one. Double taps are translated
// to single taps.
if (Utils.MozBuildApp == 'mobile/android' &&
Utils.AndroidSdkVersion >= 14 &&
aDetails.touches[0] != this.HOVER_ID) {
if (aDetails.touches.length == 1) {
if (aDetails.type == 'tap') {
emitDelay = 50;
aDetails.type = 'doubletap';
} else {
aDetails.touches.push(this.HOVER_ID);
}
}
}
let emit = function emit() {
let evt = this.chromeWin.document.createEvent('CustomEvent');
evt.initCustomEvent('mozAccessFuGesture', true, true, aDetails);
this.chromeWin.dispatchEvent(evt);
delete this._delayedEvent;
}.bind(this);
if (emitDelay) {
this._delayedEvent = this.chromeWin.setTimeout(emit, emitDelay);
} else {
emit();
}
},
compileAndEmit: function TouchAdapter_compileAndEmit(aTime) {
@ -364,41 +443,3 @@ var Mouse2Touch = {
aEvent.stopImmediatePropagation();
}
};
this.AndroidTouchAdapter = {
attach: function AndroidTouchAdapter_attach(aWindow) {
if (this.chromeWin)
return;
Logger.info('AndroidTouchAdapter.attach');
this.chromeWin = aWindow;
this.chromeWin.addEventListener('mousemove', this, true, true);
this._lastExploreTime = 0;
},
detach: function AndroidTouchAdapter_detach(aWindow) {
if (!this.chromeWin)
return;
Logger.info('AndroidTouchAdapter.detach');
this.chromeWin.removeEventListener('mousemove', this, true, true);
delete this.chromeWin;
},
handleEvent: function AndroidTouchAdapter_handleEvent(aEvent) {
// On non-Android we use the shift key to simulate touch.
if (Utils.MozBuildApp != 'mobile/android' && !aEvent.shiftKey)
return;
if (aEvent.timeStamp - this._lastExploreTime >= EXPLORE_THROTTLE) {
let evt = this.chromeWin.document.createEvent('CustomEvent');
evt.initCustomEvent(
'mozAccessFuGesture', true, true,
{type: 'explore', x: aEvent.screenX, y: aEvent.screenY, touches: [1]});
this.chromeWin.dispatchEvent(evt);
this._lastExploreTime = aEvent.timeStamp;
}
}
};