diff --git a/js/js-resources.gresource.xml b/js/js-resources.gresource.xml
index 3fed6a4fb..319a9de25 100644
--- a/js/js-resources.gresource.xml
+++ b/js/js-resources.gresource.xml
@@ -32,6 +32,7 @@
ui/animation.js
ui/appDisplay.js
ui/appFavorites.js
+ ui/appSwitchAction.js
ui/backgroundMenu.js
ui/background.js
ui/boxpointer.js
diff --git a/js/ui/appSwitchAction.js b/js/ui/appSwitchAction.js
new file mode 100644
index 000000000..018f17408
--- /dev/null
+++ b/js/ui/appSwitchAction.js
@@ -0,0 +1,64 @@
+// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
+
+const Lang = imports.lang;
+const Signals = imports.signals;
+const Clutter = imports.gi.Clutter;
+
+//in milliseconds
+let LONG_PRESS_TIMEOUT = 250;
+
+let MOTION_THRESHOLD = 30;
+
+const AppSwitchAction = new Lang.Class({
+ Name: 'AppSwitchAction',
+ Extends: Clutter.GestureAction,
+
+ _init : function() {
+ this.parent();
+ this.set_n_touch_points (3);
+ global.display.connect('grab-op-begin', Lang.bind(this, this.cancel));
+ global.display.connect('grab-op-end', Lang.bind(this, this.cancel));
+ },
+
+ vfunc_gesture_prepare : function(action, actor) {
+ return this.get_n_current_points() <= 4;
+ },
+
+ vfunc_gesture_begin : function(action, actor) {
+ let nPoints = this.get_n_current_points();
+ let event = this.get_last_event (nPoints - 1);
+
+ if (nPoints == 3)
+ this._longPressStartTime = event.get_time();
+ else if (nPoints == 4) {
+ // Check whether the 4th finger press happens after a 3-finger long press,
+ // this only needs to be checked on the first 4th finger press
+ if (this._longPressStartTime != null &&
+ event.get_time() < this._longPressStartTime + LONG_PRESS_TIMEOUT)
+ this.cancel();
+ else {
+ this._longPressStartTime = null;
+ this.emit('activated');
+ }
+ }
+
+ return this.get_n_current_points() <= 4;
+ },
+
+ vfunc_gesture_progress : function(action, actor) {
+ if (this.get_n_current_points() == 3) {
+ for (let i = 0; i < this.get_n_current_points(); i++) {
+ [startX, startY] = this.get_press_coords(i);
+ [x, y] = this.get_motion_coords(i);
+
+ if (Math.abs(x - startX) > MOTION_THRESHOLD ||
+ Math.abs(y - startY) > MOTION_THRESHOLD)
+ return false;
+ }
+
+ }
+
+ return true;
+ }
+});
+Signals.addSignalMethods(AppSwitchAction.prototype);